PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
VideoFrameTransfer.cpp
Go to the documentation of this file.
1 /* -LICENSE-START-
2  ** Copyright (c) 2012 Blackmagic Design
3  **
4  ** Permission is hereby granted, free of charge, to any person or organization
5  ** obtaining a copy of the software and accompanying documentation covered by
6  ** this license (the "Software") to use, reproduce, display, distribute,
7  ** execute, and transmit the Software, and to prepare derivative works of the
8  ** Software, and to permit third-parties to whom the Software is furnished to
9  ** do so, all subject to the following:
10  **
11  ** The copyright notices in the Software and this entire statement, including
12  ** the above license grant, this restriction and the following disclaimer,
13  ** must be included in all copies of the Software, in whole or in part, and
14  ** all derivative works of the Software, unless such copies or derivative
15  ** works are solely in the form of machine-executable object code generated by
16  ** a source language processor.
17  **
18  ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
21  ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
22  ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
23  ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  ** DEALINGS IN THE SOFTWARE.
25  ** -LICENSE-END-
26  */
27 
28 #include "VideoFrameTransfer.h"
29 
30 
31 #define DVP_CHECK(cmd) { \
32  DVPStatus hr = (cmd); \
33  if (DVP_STATUS_OK != hr) { \
34  OutputDebugStringA( #cmd " failed\n" ); \
35  ExitProcess(hr); \
36  } \
37 }
38 
39 
40 // Initialise static members
41 bool VideoFrameTransfer::mInitialized = false;
42 bool VideoFrameTransfer::mUseDvp = false;
43 unsigned VideoFrameTransfer::mWidth = 0;
44 unsigned VideoFrameTransfer::mHeight = 0;
45 GLuint VideoFrameTransfer::mCaptureTexture = 0;
46 
47 // NVIDIA specific static members
48 DVPBufferHandle VideoFrameTransfer::mDvpCaptureTextureHandle = 0;
49 DVPBufferHandle VideoFrameTransfer::mDvpPlaybackTextureHandle = 0;
50 uint32_t VideoFrameTransfer::mBufferAddrAlignment = 0;
51 uint32_t VideoFrameTransfer::mBufferGpuStrideAlignment = 0;
52 uint32_t VideoFrameTransfer::mSemaphoreAddrAlignment = 0;
53 uint32_t VideoFrameTransfer::mSemaphoreAllocSize = 0;
54 uint32_t VideoFrameTransfer::mSemaphorePayloadOffset = 0;
55 uint32_t VideoFrameTransfer::mSemaphorePayloadSize = 0;
56 
57 
58 bool VideoFrameTransfer::isNvidiaDvpAvailable()
59 {
60  // Look for supported graphics boards
61  const GLubyte* renderer = glGetString(GL_RENDERER);
62  bool hasDvp = (strstr((char*)renderer, "Quadro") != NULL);
63  return hasDvp;
64 }
65 
66 bool VideoFrameTransfer::isAMDPinnedMemoryAvailable()
67 {
68  // GL_AMD_pinned_memory presence indicates GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD buffer target is supported
69  const GLubyte* strExt = glGetString(GL_EXTENSIONS);
70  bool hasAMDPinned = (strstr((char*)strExt, "GL_AMD_pinned_memory") != NULL);
71  return hasAMDPinned;
72 }
73 
75 {
76  return (isNvidiaDvpAvailable() || isAMDPinnedMemoryAvailable());
77 }
78 
79 bool VideoFrameTransfer::initialize(unsigned width, unsigned height, GLuint captureTexture, GLuint playbackTexture)
80 {
81  if (mInitialized)
82  return false;
83 
84  bool hasDvp = isNvidiaDvpAvailable();
85  bool hasAMDPinned = isAMDPinnedMemoryAvailable();
86 
87  if (!hasDvp && !hasAMDPinned)
88  return false;
89 
90  mUseDvp = hasDvp;
91  mWidth = width;
92  mHeight = height;
93  mCaptureTexture = captureTexture;
94 
95  if (! initializeMemoryLocking(mWidth * mHeight * 4)) // BGRA uses 4 bytes per pixel
96  return false;
97 
98  if (mUseDvp)
99  {
100  // DVP initialisation
102  DVP_CHECK(dvpGetRequiredConstantsGLCtx( &mBufferAddrAlignment, &mBufferGpuStrideAlignment,
103  &mSemaphoreAddrAlignment, &mSemaphoreAllocSize,
104  &mSemaphorePayloadOffset, &mSemaphorePayloadSize));
105 
106  // Register textures with DVP
107  DVP_CHECK(dvpCreateGPUTextureGL(captureTexture, &mDvpCaptureTextureHandle));
108  DVP_CHECK(dvpCreateGPUTextureGL(playbackTexture, &mDvpPlaybackTextureHandle));
109  }
110 
111  mInitialized = true;
112 
113  return true;
114 }
115 
116 bool VideoFrameTransfer::initializeMemoryLocking(unsigned memSize)
117 {
118  // Increase the process working set size to allow pinning of memory.
119  static SIZE_T dwMin = 0, dwMax = 0;
120  HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_SET_QUOTA, FALSE, GetCurrentProcessId());
121  if (!hProcess)
122  return false;
123 
124  // Retrieve the working set size of the process.
125  if (!dwMin && !GetProcessWorkingSetSize(hProcess, &dwMin, &dwMax))
126  return false;
127 
128  // Allow for 80 frames to be locked
129  BOOL res = SetProcessWorkingSetSize(hProcess, memSize * 80 + dwMin, memSize * 80 + (dwMax-dwMin));
130  if (!res)
131  return false;
132 
133  CloseHandle(hProcess);
134  return true;
135 }
136 
137 // SyncInfo sets up a semaphore which is shared between the GPU and CPU and used to
138 // synchronise access to DVP buffers.
139 struct SyncInfo
140 {
141  SyncInfo(uint32_t semaphoreAllocSize, uint32_t semaphoreAddrAlignment);
142  ~SyncInfo();
143 
144  volatile uint32_t* mSem;
145  volatile uint32_t mReleaseValue;
146  volatile uint32_t mAcquireValue;
147  DVPSyncObjectHandle mDvpSync;
148 };
149 
150 SyncInfo::SyncInfo(uint32_t semaphoreAllocSize, uint32_t semaphoreAddrAlignment)
151 {
152  mSem = (uint32_t*)_aligned_malloc(semaphoreAllocSize, semaphoreAddrAlignment);
153 
154  // Initialise
155  mSem[0] = 0;
156  mReleaseValue = 0;
157  mAcquireValue = 0;
158 
159  // Setup DVP sync object and import it
160  DVPSyncObjectDesc syncObjectDesc;
161  syncObjectDesc.externalClientWaitFunc = NULL;
162  syncObjectDesc.sem = (uint32_t*)mSem;
163 
164  DVP_CHECK(dvpImportSyncObject(&syncObjectDesc, &mDvpSync));
165 }
166 
167 SyncInfo::~SyncInfo()
168 {
169  DVP_CHECK(dvpFreeSyncObject(mDvpSync));
170  _aligned_free((void*)mSem);
171 }
172 
173 VideoFrameTransfer::VideoFrameTransfer(unsigned long memSize, void* address, Direction direction) :
174  mBuffer(address),
175  mMemSize(memSize),
176  mDirection(direction),
177  mExtSync(NULL),
178  mGpuSync(NULL),
179  mDvpSysMemHandle(0),
180  mBufferHandle(0)
181 {
182  if (mUseDvp)
183  {
184  // Pin the memory
185  if (! VirtualLock(mBuffer, mMemSize))
186  throw std::runtime_error("Error pinning memory with VirtualLock");
187 
188  // Create necessary sysmem and gpu sync objects
189  mExtSync = new SyncInfo(mSemaphoreAllocSize, mSemaphoreAddrAlignment);
190  mGpuSync = new SyncInfo(mSemaphoreAllocSize, mSemaphoreAddrAlignment);
191 
192  // Register system memory buffers with DVP
193  DVPSysmemBufferDesc sysMemBuffersDesc;
194  sysMemBuffersDesc.width = mWidth;
195  sysMemBuffersDesc.height = mHeight;
196  sysMemBuffersDesc.stride = mWidth * 4;
197  sysMemBuffersDesc.format = DVP_BGRA;
198  sysMemBuffersDesc.type = DVP_UNSIGNED_BYTE;
199  sysMemBuffersDesc.size = mMemSize;
200  sysMemBuffersDesc.bufAddr = mBuffer;
201 
202  if (mDirection == CPUtoGPU)
203  {
204  // A UYVY 4:2:2 frame is transferred to the GPU, rather than RGB 4:4:4, so width is halved
205  sysMemBuffersDesc.width /= 2;
206  sysMemBuffersDesc.stride /= 2;
207  }
208 
209  DVP_CHECK(dvpCreateBuffer(&sysMemBuffersDesc, &mDvpSysMemHandle));
210  DVP_CHECK(dvpBindToGLCtx(mDvpSysMemHandle));
211  }
212  else
213  {
214  // Create an OpenGL buffer handle to use for pinned memory
215  GLuint bufferHandle;
216  glGenBuffers(1, &bufferHandle);
217 
218  // Pin memory by binding buffer to special AMD target.
219  glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, bufferHandle);
220 
221  // glBufferData() sets up the address so any OpenGL operation on this buffer will use system memory directly
222  // (assumes address is aligned to 4k boundary).
223  glBufferData(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, mMemSize, address, GL_STREAM_DRAW);
224  GLenum result = glGetError();
225  if (result != GL_NO_ERROR)
226  {
227  throw std::runtime_error("Error pinning memory with glBufferData(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, ...)");
228  }
229  glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0); // Unbind buffer to target
230 
231  mBufferHandle = bufferHandle;
232  }
233 }
234 
236 {
237  if (mUseDvp)
238  {
239  DVP_CHECK(dvpUnbindFromGLCtx(mDvpSysMemHandle));
240  DVP_CHECK(dvpDestroyBuffer(mDvpSysMemHandle));
241 
242  delete mExtSync;
243  delete mGpuSync;
244 
245  VirtualUnlock(mBuffer, mMemSize);
246  }
247  else
248  {
249  // The buffer is un-pinned by the GPU when the buffer is deleted
250  glDeleteBuffers(1, &mBufferHandle);
251  }
252 }
253 
255 {
256  if (mUseDvp)
257  {
258  // NVIDIA DVP transfers
259  DVPStatus status;
260 
261  mGpuSync->mReleaseValue++;
262 
263  dvpBegin();
264  if (mDirection == CPUtoGPU)
265  {
266  // Copy from system memory to GPU texture
267  dvpMapBufferWaitDVP(mDvpCaptureTextureHandle);
268  status = dvpMemcpyLined( mDvpSysMemHandle, mExtSync->mDvpSync, mExtSync->mAcquireValue, DVP_TIMEOUT_IGNORED,
269  mDvpCaptureTextureHandle, mGpuSync->mDvpSync, mGpuSync->mReleaseValue, 0, mHeight);
270  dvpMapBufferEndDVP(mDvpCaptureTextureHandle);
271  }
272  else
273  {
274  // Copy from GPU texture to system memory
275  dvpMapBufferWaitDVP(mDvpPlaybackTextureHandle);
276  status = dvpMemcpyLined( mDvpPlaybackTextureHandle, mExtSync->mDvpSync, mExtSync->mReleaseValue, DVP_TIMEOUT_IGNORED,
277  mDvpSysMemHandle, mGpuSync->mDvpSync, mGpuSync->mReleaseValue, 0, mHeight);
278  dvpMapBufferEndDVP(mDvpPlaybackTextureHandle);
279  }
280  dvpEnd();
281 
282  return (status == DVP_STATUS_OK);
283  }
284  else
285  {
286  // AMD pinned memory transfers
287  if (mDirection == CPUtoGPU)
288  {
289  glEnable(GL_TEXTURE_2D);
290 
291  // Use a pinned buffer for the GL_PIXEL_UNPACK_BUFFER target
292  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBufferHandle);
293  glBindTexture(GL_TEXTURE_2D, mCaptureTexture);
294 
295  // NULL for last arg indicates use current GL_PIXEL_UNPACK_BUFFER target as texture data
296  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mWidth/2, mHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
297 
298  // Ensure pinned texture has been transferred to GPU before we draw with it
299  GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
300  glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 40 * 1000 * 1000); // timeout in nanosec
301  glDeleteSync(fence);
302 
303  glBindTexture(GL_TEXTURE_2D, 0);
304  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
305  glDisable(GL_TEXTURE_2D);
306  }
307  else
308  {
309  // Use a PIXEL PACK BUFFER to read back pixels
310  glBindBuffer(GL_PIXEL_PACK_BUFFER, mBufferHandle);
311  glReadPixels(0, 0, mWidth, mHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
312 
313  // Ensure GPU has processed all commands in the pipeline up to this point, before memory is read by the CPU
314  GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
315  glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 40 * 1000 * 1000); // timeout in nanosec
316  glDeleteSync(fence);
317  }
318 
319  return (glGetError() == GL_NO_ERROR);
320  }
321 }
322 
324 {
325  if (!mUseDvp)
326  return;
327 
328  // Block until buffer has completely transferred from GPU to CPU buffer
329  if (mDirection == GPUtoCPU)
330  {
331  dvpBegin();
333  dvpEnd();
334  }
335 }
336 
338 {
339  if (!mUseDvp)
340  return;
341 
342  if (direction == CPUtoGPU)
343  dvpMapBufferWaitAPI(mDvpCaptureTextureHandle);
344  else
345  dvpMapBufferWaitAPI(mDvpPlaybackTextureHandle);
346 }
347 
349 {
350  if (!mUseDvp)
351  return;
352 
353  if (direction == CPUtoGPU)
354  dvpMapBufferEndAPI(mDvpCaptureTextureHandle);
355  else
356  dvpMapBufferEndAPI(mDvpPlaybackTextureHandle);
357 }
DVPAPI_INTERFACE dvpMapBufferEndDVP(DVPBufferHandle gpuBufferHandle)
DVPAPI_INTERFACE dvpGetRequiredConstantsGLCtx(uint32_t *bufferAddrAlignment, uint32_t *bufferGPUStrideAlignment, uint32_t *semaphoreAddrAlignment, uint32_t *semaphoreAllocSize, uint32_t *semaphorePayloadOffset, uint32_t *semaphorePayloadSize)
PFNGLFENCESYNCPROC glFenceSync
ext function
VideoFrameTransfer(unsigned long memSize, void *address, Direction direction)
uint64_t DVPSyncObjectHandle
Definition: DVPAPI.h:81
DVPAPI_INTERFACE dvpImportSyncObject(DVPSyncObjectDesc *desc, DVPSyncObjectHandle *syncObject)
DVPAPI_INTERFACE dvpFreeSyncObject(DVPSyncObjectHandle syncObject)
#define DVP_TIMEOUT_IGNORED
Definition: DVPAPI.h:212
DVPAPI_INTERFACE dvpMapBufferWaitAPI(DVPBufferHandle gpuBufferHandle)
DVPAPI_INTERFACE dvpMemcpyLined(DVPBufferHandle srcBuffer, DVPSyncObjectHandle srcSync, uint32_t srcAcquireValue, uint64_t timeout, DVPBufferHandle dstBuffer, DVPSyncObjectHandle dstSync, uint32_t dstReleaseValue, uint32_t startingLine, uint32_t numberOfLines)
DVPAPI_INTERFACE dvpMapBufferEndAPI(DVPBufferHandle gpuBufferHandle)
DVPAPI_INTERFACE dvpBegin()
#define FALSE
Definition: ATC3DGm.h:220
DVPStatus
Definition: DVPAPI.h:83
DVPAPI_INTERFACE dvpDestroyBuffer(DVPBufferHandle hBuf)
PFNGLBUFFERDATAARBPROC glBufferData
uint32_t * sem
Definition: DVPAPI.h:193
const char * address
Definition: phidget22.h:2552
DVPAPI_INTERFACE dvpSyncObjClientWaitComplete(DVPSyncObjectHandle syncObject, uint64_t timeout)
PhidgetLCD_Font int * width
Definition: phidget22.h:4275
DVPBufferFormats format
Definition: DVPAPI.h:177
PFNGLCLIENTWAITSYNCPROC glClientWaitSync
ext function
DVPAPI_INTERFACE dvpCreateGPUTextureGL(GLuint texID, DVPBufferHandle *bufferHandle)
PFNGLDELETEBUFFERSARBPROC glDeleteBuffers
#define DVP_DEVICE_FLAGS_SHARE_APP_CONTEXT
Definition: DVPAPI.h:224
DVPBufferTypes type
Definition: DVPAPI.h:178
DVPStatus(* externalClientWaitFunc)(DVPSyncObjectHandle sync, uint32_t value, bool GEQ, uint64_t timeout)
Definition: DVPAPI.h:195
uint64_t DVPBufferHandle
Definition: DVPAPI.h:80
PhidgetLCD_Font int int * height
Definition: phidget22.h:4275
static void beginTextureInUse(Direction direction)
static bool checkFastMemoryTransferAvailable()
static bool initialize(unsigned width, unsigned height, GLuint captureTexture, GLuint playbackTexture)
DVPAPI_INTERFACE dvpMapBufferWaitDVP(DVPBufferHandle gpuBufferHandle)
DVPAPI_INTERFACE dvpInitGLContext(uint32_t flags)
DVPAPI_INTERFACE dvpBindToGLCtx(DVPBufferHandle hBuf)
DVPAPI_INTERFACE dvpCreateBuffer(DVPSysmemBufferDesc *desc, DVPBufferHandle *hBuf)
static void endTextureInUse(Direction direction)
#define DVP_CHECK(cmd)
int BOOL
Definition: ATC3DGm.h:446
PFNGLDELETESYNCPROC glDeleteSync
ext function
PFNGLBINDBUFFERARBPROC glBindBuffer
PFNGLGENBUFFERSARBPROC glGenBuffers
DVPAPI_INTERFACE dvpEnd()
DVPAPI_INTERFACE dvpUnbindFromGLCtx(DVPBufferHandle hBuf)