From 9c5f9906fa83df31cb93884e2aac487bdbcdea8d Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Tue, 17 Dec 2019 14:59:58 +1100 Subject: [PATCH] [c-host] add spinlock support to events and alter dxgi to use them --- c-host/include/interface/platform.h | 2 +- .../platform/Windows/capture/DXGI/src/dxgi.c | 38 +++--- c-host/platform/Windows/src/platform.c | 122 +++++++++++++++--- 3 files changed, 127 insertions(+), 35 deletions(-) diff --git a/c-host/include/interface/platform.h b/c-host/include/interface/platform.h index eede1bb8..646eb52e 100644 --- a/c-host/include/interface/platform.h +++ b/c-host/include/interface/platform.h @@ -46,7 +46,7 @@ bool os_joinThread (osThreadHandle * handle, int * resultCode); typedef struct osEventHandle osEventHandle; -osEventHandle * os_createEvent(bool autoReset); +osEventHandle * os_createEvent(bool autoReset, unsigned int msSpinTime); void os_freeEvent (osEventHandle * handle); bool os_waitEvent (osEventHandle * handle, unsigned int timeout); bool os_waitEvents (osEventHandle * handles[], int count, bool waitAll, unsigned int timeout); diff --git a/c-host/platform/Windows/capture/DXGI/src/dxgi.c b/c-host/platform/Windows/capture/DXGI/src/dxgi.c index 8ea2beae..eab080a0 100644 --- a/c-host/platform/Windows/capture/DXGI/src/dxgi.c +++ b/c-host/platform/Windows/capture/DXGI/src/dxgi.c @@ -46,7 +46,7 @@ typedef struct Texture enum TextureState state; ID3D11Texture2D * tex; D3D11_MAPPED_SUBRESOURCE map; - volatile int free; + osEventHandle * readyEvent; } Texture; @@ -80,7 +80,6 @@ struct iface IDXGIOutputDuplication * dup; int maxTextures; Texture * texture; - volatile int texPending; volatile int texRIndex; int texWIndex; bool needsRelease; @@ -165,7 +164,7 @@ static bool dxgi_create() return false; } - this->pointerEvent = os_createEvent(true); + this->pointerEvent = os_createEvent(true, 10); if (!this->pointerEvent) { DEBUG_ERROR("failed to create the pointer event"); @@ -178,6 +177,20 @@ static bool dxgi_create() this->maxTextures = 1; this->texture = calloc(sizeof(struct Texture), this->maxTextures); + for(int i = 0; i < this->maxTextures; ++i) + { + this->texture[i].readyEvent = os_createEvent(false, 30); + if (!this->texture[i].readyEvent) + { + DEBUG_ERROR("failed to create the texture event"); + + for(int x = 0; x < i; ++x) + os_freeEvent(this->texture[x].readyEvent); + + free(this->texture); + free(this); + } + } this->useAcquireLock = option_get_bool("dxgi", "useAcquireLock"); return true; @@ -211,7 +224,6 @@ static bool dxgi_init(void * pointerShape, const unsigned int pointerSize) this->pointerUsed = 0; this->stop = false; - this->texPending = 0; this->texRIndex = 0; this->texWIndex = 0; @@ -483,8 +495,6 @@ static bool dxgi_init(void * pointerShape, const unsigned int pointerSize) DEBUG_WINERROR("Failed to create texture", status); goto fail; } - - this->texture[i].free = 1; } // map the texture simply to get the pitch and stride @@ -653,7 +663,7 @@ static CaptureResult dxgi_capture() tex = &this->texture[this->texWIndex]; // check if the texture is free, if not skip the frame to keep up - if (INTERLOCKED_GET(&tex->free)) + if (tex->state == TEXTURE_STATE_UNUSED) { ID3D11Texture2D * src; status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src); @@ -672,10 +682,9 @@ static CaptureResult dxgi_capture() ID3D11Texture2D_Release(src); - // set the state + // set the state and signal tex->state = TEXTURE_STATE_PENDING_MAP; - INTERLOCKED_DEC(&tex->free); - INTERLOCKED_INC(&this->texPending); + os_signalEvent(tex->readyEvent); // advance our write pointer if (++this->texWIndex == this->maxTextures) @@ -752,11 +761,8 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame) assert(this->initialized); Texture * tex = &this->texture[INTERLOCKED_GET(&this->texRIndex)]; - if (INTERLOCKED_GET(&tex->free)) - { - Sleep(1); + if (!os_waitEvent(tex->readyEvent, 1000)) return CAPTURE_RESULT_TIMEOUT; - } // try to map the resource, but don't wait for it HRESULT status; @@ -771,7 +777,6 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame) } tex->state = TEXTURE_STATE_MAPPED; - INTERLOCKED_DEC(&this->texPending); frame->width = this->width; frame->height = this->height; @@ -779,6 +784,7 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame) frame->stride = this->stride; frame->format = this->format; + os_resetEvent(tex->readyEvent); return CAPTURE_RESULT_OK; } @@ -791,8 +797,8 @@ static CaptureResult dxgi_getFrame(FrameBuffer frame) framebuffer_write(frame, tex->map.pData, this->pitch * this->height); LOCKED({ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource*)tex->tex, 0);}); + tex->state = TEXTURE_STATE_UNUSED; - INTERLOCKED_INC(&tex->free); INTERLOCKED_INC(&this->texRIndex); INTERLOCKED_CE (&this->texRIndex, this->maxTextures, 0); diff --git a/c-host/platform/Windows/src/platform.c b/c-host/platform/Windows/src/platform.c index 01533caa..bd05db38 100644 --- a/c-host/platform/Windows/src/platform.c +++ b/c-host/platform/Windows/src/platform.c @@ -38,6 +38,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA struct AppState { + LARGE_INTEGER perfFreq; HINSTANCE hInst; int argc; @@ -69,6 +70,15 @@ struct osThreadHandle int resultCode; }; +struct osEventHandle +{ + bool reset; + HANDLE handle; + bool wrapped; + unsigned int msSpinTime; + volatile bool signaled; +}; + // undocumented API to adjust the system timer resolution (yes, its a nasty hack) typedef NTSTATUS (__stdcall *ZwSetTimerResolution_t)(ULONG RequestedResolution, BOOLEAN Set, PULONG ActualResolution); static ZwSetTimerResolution_t ZwSetTimerResolution = NULL; @@ -320,6 +330,9 @@ bool app_init() DEBUG_INFO("System timer resolution: %.2f ns", (float)actualResolution / 100.0f); } + // get the performance frequency for spinlocks + QueryPerformanceFrequency(&app.perfFreq); + HDEVINFO deviceInfoSet; PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL; SP_DEVICE_INTERFACE_DATA deviceInterfaceData; @@ -488,36 +501,98 @@ bool os_joinThread(osThreadHandle * handle, int * resultCode) return false; } -osEventHandle * os_createEvent(bool autoReset) +osEventHandle * os_createEvent(bool autoReset, unsigned int msSpinTime) { - HANDLE event = CreateEvent(NULL, autoReset ? FALSE : TRUE, FALSE, NULL); + osEventHandle * event = (osEventHandle *)malloc(sizeof(osEventHandle)); if (!event) { - DEBUG_WINERROR("Failed to create the event", GetLastError()); + DEBUG_ERROR("out of ram"); return NULL; } - return (osEventHandle*)event; + event->reset = autoReset; + event->wrapped = false; + event->msSpinTime = msSpinTime; + event->handle = CreateEvent(NULL, autoReset ? FALSE : TRUE, FALSE, NULL); + if (!event->handle) + { + DEBUG_WINERROR("Failed to create the event", GetLastError()); + free(event); + return NULL; + } + + return event; } -osEventHandle * os_wrapEvent(HANDLE event) +osEventHandle * os_wrapEvent(HANDLE handle) { - return (osEventHandle*)event; + osEventHandle * event = (osEventHandle *)malloc(sizeof(osEventHandle)); + if (!event) + { + DEBUG_ERROR("out of ram"); + return NULL; + } + + event->reset = false; + event->handle = event; + event->wrapped = true; + event->msSpinTime = 0; + return event; } -void os_freeEvent(osEventHandle * handle) +void os_freeEvent(osEventHandle * event) { - CloseHandle((HANDLE)handle); + CloseHandle(event->handle); } -bool os_waitEvent(osEventHandle * handle, unsigned int timeout) +bool os_waitEvent(osEventHandle * event, unsigned int timeout) { + // wrapped events can't be enahnced + if (!event->wrapped) + { + if (event->signaled) + return true; + + if (timeout == 0) + return event->signaled; + + if (event->msSpinTime) + { + unsigned int spinTime = event->msSpinTime; + if (timeout != TIMEOUT_INFINITE) + { + if (timeout > event->msSpinTime) + timeout -= event->msSpinTime; + else + { + spinTime -= timeout; + timeout = 0; + } + } + + LARGE_INTEGER end, now; + QueryPerformanceCounter(&end); + end.QuadPart += (app.perfFreq.QuadPart / 1000) * spinTime; + while(!event->signaled) + { + QueryPerformanceCounter(&now); + if (now.QuadPart >= end.QuadPart) + break; + } + + if (event->signaled) + return true; + } + } + const DWORD to = (timeout == TIMEOUT_INFINITE) ? INFINITE : (DWORD)timeout; while(true) { - switch(WaitForSingleObject((HANDLE)handle, to)) + switch(WaitForSingleObject(event->handle, to)) { case WAIT_OBJECT_0: + if (!event->reset) + event->signaled = true; return true; case WAIT_ABANDONED: @@ -539,18 +614,24 @@ bool os_waitEvent(osEventHandle * handle, unsigned int timeout) } } -bool os_waitEvents(osEventHandle * handles[], int count, bool waitAll, unsigned int timeout) +bool os_waitEvents(osEventHandle * events[], int count, bool waitAll, unsigned int timeout) { const DWORD to = (timeout == TIMEOUT_INFINITE) ? INFINITE : (DWORD)timeout; + + HANDLE * handles = (HANDLE *)malloc(sizeof(HANDLE) * count); + for(int i = 0; i < count; ++i) + handles[i] = events[i]->handle; + while(true) { - DWORD result = WaitForMultipleObjects(count, (HANDLE*)handles, waitAll, to); + DWORD result = WaitForMultipleObjects(count, handles, waitAll, to); if (result >= WAIT_OBJECT_0 && result < count) { - // null non signalled events from the handle list + // null non signaled events from the handle list for(int i = 0; i < count; ++i) - if (i != result && !os_waitEvent(handles[i], 0)) + if (i != result && !os_waitEvent(events[i], 0)) handles[i] = NULL; + free(handles); return true; } @@ -563,24 +644,29 @@ bool os_waitEvents(osEventHandle * handles[], int count, bool waitAll, unsigned if (timeout == TIMEOUT_INFINITE) continue; + free(handles); return false; case WAIT_FAILED: DEBUG_WINERROR("Wait for event failed", GetLastError()); + free(handles); return false; } DEBUG_ERROR("Unknown wait event return code"); + free(handles); return false; } } -bool os_signalEvent(osEventHandle * handle) +bool os_signalEvent(osEventHandle * event) { - return SetEvent((HANDLE)handle); + event->signaled = true; + return SetEvent(event->handle); } -bool os_resetEvent(osEventHandle * handle) +bool os_resetEvent(osEventHandle * event) { - return ResetEvent((HANDLE)handle); + event->signaled = false; + return ResetEvent(event->handle); } \ No newline at end of file