[c-host] dxgi: reworked for better pipelining

This commit is contained in:
Geoffrey McRae 2019-03-04 16:56:45 +11:00
parent 935eb0651d
commit 8120913acb
4 changed files with 140 additions and 74 deletions

View File

@ -97,11 +97,7 @@ static int frameThread(void * opaque)
// wait for the client to finish with the previous frame // wait for the client to finish with the previous frame
while(fi->flags & KVMFR_FRAME_FLAG_UPDATE && app.running) while(fi->flags & KVMFR_FRAME_FLAG_UPDATE && app.running)
{
DEBUG_WARN("Waiting for the client");
// this generally never happens
usleep(1000); usleep(1000);
}
switch(frame.format) switch(frame.format)
{ {

View File

@ -40,10 +40,12 @@ bool os_joinThread (osThreadHandle * handle, int * resultCode);
// os specific event functions // os specific event functions
#define TIMEOUT_INFINITE ((unsigned int)~0)
typedef struct osEventHandle osEventHandle; typedef struct osEventHandle osEventHandle;
osEventHandle * os_createEvent(bool autoReset); osEventHandle * os_createEvent(bool autoReset);
void os_freeEvent (osEventHandle * handle); void os_freeEvent (osEventHandle * handle);
bool os_waitEvent (osEventHandle * handle); bool os_waitEvent (osEventHandle * handle, unsigned int timeout);
bool os_signalEvent(osEventHandle * handle); bool os_signalEvent(osEventHandle * handle);
bool os_resetEvent (osEventHandle * handle); bool os_resetEvent (osEventHandle * handle);

View File

@ -29,10 +29,21 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "dxgi_extra.h" #include "dxgi_extra.h"
#define MAX_TEXTURES 2
typedef struct Texture
{
ID3D11Texture2D * tex;
D3D11_MAPPED_SUBRESOURCE map;
osEventHandle * evt;
}
Texture;
// locals // locals
struct iface struct iface
{ {
bool initialized; bool initialized;
bool reinit;
IDXGIFactory1 * factory; IDXGIFactory1 * factory;
IDXGIAdapter1 * adapter; IDXGIAdapter1 * adapter;
IDXGIOutput * output; IDXGIOutput * output;
@ -40,10 +51,10 @@ struct iface
ID3D11DeviceContext * deviceContext; ID3D11DeviceContext * deviceContext;
D3D_FEATURE_LEVEL featureLevel; D3D_FEATURE_LEVEL featureLevel;
IDXGIOutputDuplication * dup; IDXGIOutputDuplication * dup;
ID3D11Texture2D * texture; Texture texture[MAX_TEXTURES];
D3D11_MAPPED_SUBRESOURCE mapping; int texRIndex;
int texWIndex;
bool needsRelease; bool needsRelease;
osEventHandle * copyEvent;
osEventHandle * frameEvent; osEventHandle * frameEvent;
osEventHandle * pointerEvent; osEventHandle * pointerEvent;
@ -79,19 +90,10 @@ static bool dxgi_create()
return false; return false;
} }
this->copyEvent = os_createEvent(true);
if (!this->copyEvent)
{
DEBUG_ERROR("failed to create the copy event");
free(this);
return false;
}
this->frameEvent = os_createEvent(true); this->frameEvent = os_createEvent(true);
if (!this->frameEvent) if (!this->frameEvent)
{ {
DEBUG_ERROR("failed to create the frame event"); DEBUG_ERROR("failed to create the frame event");
os_freeEvent(this->copyEvent);
free(this); free(this);
return false; return false;
} }
@ -100,7 +102,6 @@ static bool dxgi_create()
if (!this->pointerEvent) if (!this->pointerEvent)
{ {
DEBUG_ERROR("failed to create the pointer event"); DEBUG_ERROR("failed to create the pointer event");
os_freeEvent(this->copyEvent );
os_freeEvent(this->frameEvent); os_freeEvent(this->frameEvent);
free(this); free(this);
return false; return false;
@ -129,12 +130,14 @@ static bool dxgi_init()
dpiDone = true; dpiDone = true;
} }
// pre-signal the copy event
os_signalEvent(this->copyEvent);
HRESULT status; HRESULT status;
DXGI_OUTPUT_DESC outputDesc; DXGI_OUTPUT_DESC outputDesc;
this->reinit = false;
this->texRIndex = 0;
this->texWIndex = 0;
os_resetEvent(this->frameEvent);
status = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&this->factory); status = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&this->factory);
if (FAILED(status)) if (FAILED(status))
{ {
@ -334,16 +337,29 @@ static bool dxgi_init()
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
texDesc.MiscFlags = 0; texDesc.MiscFlags = 0;
status = ID3D11Device_CreateTexture2D(this->device, &texDesc, NULL, &this->texture); for(int i = 0; i < MAX_TEXTURES; ++i)
if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to create texture", status); status = ID3D11Device_CreateTexture2D(this->device, &texDesc, NULL, &this->texture[i].tex);
goto fail; if (FAILED(status))
{
DEBUG_WINERROR("Failed to create texture", status);
goto fail;
}
this->texture[i].evt = os_createEvent(true);
if (!this->texture[i].evt)
{
DEBUG_ERROR("Failed to create the texture event");
goto fail;
}
// pre-signal the events to flag as unused
os_signalEvent(this->texture[i].evt);
} }
// map the texture simply to get the pitch and stride // map the texture simply to get the pitch and stride
D3D11_MAPPED_SUBRESOURCE mapping; D3D11_MAPPED_SUBRESOURCE mapping;
status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource *)this->texture, 0, D3D11_MAP_READ, 0, &mapping); status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource *)this->texture[0].tex, 0, D3D11_MAP_READ, 0, &mapping);
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to map the texture", status); DEBUG_WINERROR("Failed to map the texture", status);
@ -351,7 +367,7 @@ static bool dxgi_init()
} }
this->pitch = mapping.RowPitch; this->pitch = mapping.RowPitch;
this->stride = mapping.RowPitch / 4; this->stride = mapping.RowPitch / 4;
ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource *)this->texture, 0); ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource *)this->texture[0].tex, 0);
this->initialized = true; this->initialized = true;
return true; return true;
@ -365,16 +381,26 @@ static bool dxgi_deinit()
{ {
assert(this); assert(this);
if (this->mapping.pData) for(int i = 0; i < MAX_TEXTURES; ++i)
{ {
ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource*)this->texture, 0); if (this->texture[i].map.pData)
this->mapping.pData = NULL; {
} ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource*)this->texture[i].tex, 0);
this->texture[i].map.pData = NULL;
}
if (this->texture) if (this->texture[i].tex)
{ {
ID3D11Texture2D_Release(this->texture); ID3D11Texture2D_Release(this->texture[i].tex);
this->texture = NULL; this->texture[i].tex = NULL;
}
if (this->texture[i].evt)
{
os_signalEvent(this->texture[i].evt);
os_freeEvent(this->texture[i].evt);
this->texture[i].evt = NULL;
}
} }
if (this->dup) if (this->dup)
@ -431,7 +457,6 @@ static void dxgi_free()
if (this->initialized) if (this->initialized)
dxgi_deinit(); dxgi_deinit();
os_freeEvent(this->copyEvent );
os_freeEvent(this->frameEvent ); os_freeEvent(this->frameEvent );
os_freeEvent(this->pointerEvent); os_freeEvent(this->pointerEvent);
@ -447,7 +472,7 @@ static unsigned int dxgi_getMaxFrameSize()
return this->height * this->pitch; return this->height * this->pitch;
} }
static CaptureResult dxgi_capture(bool * hasFrameUpdate, bool * hasPointerUpdate) inline static CaptureResult dxgi_capture_int(bool * hasFrameUpdate, bool * hasPointerUpdate)
{ {
assert(this); assert(this);
assert(this->initialized); assert(this->initialized);
@ -482,41 +507,54 @@ static CaptureResult dxgi_capture(bool * hasFrameUpdate, bool * hasPointerUpdate
if (frameInfo.LastPresentTime.QuadPart != 0) if (frameInfo.LastPresentTime.QuadPart != 0)
{ {
ID3D11Texture2D * src; Texture * tex = &this->texture[this->texWIndex];
status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src);
if (FAILED(status)) // check if the texture is free, if not skip the frame to keep up
if (!os_waitEvent(tex->evt, 0))
{ {
DEBUG_WINERROR("Failed to get the texture from the dxgi resource", status); DEBUG_WARN("Frame skipped");
IDXGIResource_Release(res);
return CAPTURE_RESULT_ERROR;
} }
else
// wait for the prior copy to finish in getFrame and unmap
os_waitEvent(this->copyEvent);
if (this->mapping.pData)
{ {
ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource*)this->texture, 0); ID3D11Texture2D * src;
this->mapping.pData = NULL; status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to get the texture from the dxgi resource", status);
IDXGIResource_Release(res);
return CAPTURE_RESULT_ERROR;
}
// if the texture was mapped, unmap it
if (tex->map.pData)
{
ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource*)tex->tex, 0);
tex->map.pData = NULL;
}
// issue the copy from GPU to CPU RAM
ID3D11DeviceContext_CopyResource(this->deviceContext,
(ID3D11Resource *)tex->tex, (ID3D11Resource *)src);
// map the resource (this must be done here as ID3D11DeviceContext is not thread safe)
status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)tex->tex, 0, D3D11_MAP_READ, 0, &tex->map);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to map the texture", status);
IDXGIResource_Release(res);
return CAPTURE_RESULT_ERROR;
}
// signal that a frame is available
os_signalEvent(this->frameEvent);
// advance our write pointer
if (++this->texWIndex == MAX_TEXTURES)
this->texWIndex = 0;
ID3D11Texture2D_Release(src);
*hasFrameUpdate = true;
} }
// issue the copy from GPU to CPU RAM
ID3D11DeviceContext_CopyResource(this->deviceContext,
(ID3D11Resource *)this->texture, (ID3D11Resource *)src);
// map the resource (this must be done here as ID3D11DeviceContext is not thread safe)
status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)this->texture, 0, D3D11_MAP_READ, 0, &this->mapping);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to map the texture", status);
IDXGIResource_Release(res);
return CAPTURE_RESULT_ERROR;
}
// signal that a frame is available
os_signalEvent(this->frameEvent);
ID3D11Texture2D_Release(src);
*hasFrameUpdate = true;
} }
if (frameInfo.PointerShapeBufferSize > 0) if (frameInfo.PointerShapeBufferSize > 0)
@ -529,27 +567,51 @@ static CaptureResult dxgi_capture(bool * hasFrameUpdate, bool * hasPointerUpdate
return CAPTURE_RESULT_OK; return CAPTURE_RESULT_OK;
} }
static CaptureResult dxgi_capture(bool * hasFrameUpdate, bool * hasPointerUpdate)
{
CaptureResult result = dxgi_capture_int(hasFrameUpdate, hasPointerUpdate);
// signal pending events if the result was any form of failure or reinit
if (result != CAPTURE_RESULT_OK && result != CAPTURE_RESULT_TIMEOUT)
{
this->reinit = true;
os_signalEvent(this->frameEvent);
}
return result;
}
static bool dxgi_getFrame(CaptureFrame * frame) static bool dxgi_getFrame(CaptureFrame * frame)
{ {
assert(this); assert(this);
assert(this->initialized); assert(this->initialized);
if (!os_waitEvent(this->frameEvent)) if (this->reinit)
return true;
if (!os_waitEvent(this->frameEvent, TIMEOUT_INFINITE))
{ {
DEBUG_ERROR("Failed to wait on the frame event"); DEBUG_ERROR("Failed to wait on the frame event");
return false; return false;
} }
if (this->reinit)
return true;
Texture * tex = &this->texture[this->texRIndex];
frame->width = this->width; frame->width = this->width;
frame->height = this->height; frame->height = this->height;
frame->pitch = this->pitch; frame->pitch = this->pitch;
frame->stride = this->stride; frame->stride = this->stride;
frame->format = this->format; frame->format = this->format;
memcpy(frame->data, tex->map.pData, this->pitch * this->height);
os_signalEvent(tex->evt);
memcpy(frame->data, this->mapping.pData, this->pitch * this->height); if (++this->texRIndex == MAX_TEXTURES)
this->texRIndex = 0;
os_signalEvent(this->copyEvent);
return true; return true;
} }

View File

@ -317,19 +317,25 @@ void os_freeEvent(osEventHandle * handle)
CloseHandle((HANDLE)handle); CloseHandle((HANDLE)handle);
} }
bool os_waitEvent(osEventHandle * handle) bool os_waitEvent(osEventHandle * handle, unsigned int timeout)
{ {
const DWORD to = (timeout == TIMEOUT_INFINITE) ? INFINITE : (DWORD)timeout;
while(true) while(true)
{ {
switch(WaitForSingleObject((HANDLE)handle, INFINITE)) switch(WaitForSingleObject((HANDLE)handle, to))
{ {
case WAIT_OBJECT_0: case WAIT_OBJECT_0:
return true; return true;
case WAIT_ABANDONED: case WAIT_ABANDONED:
case WAIT_TIMEOUT:
continue; continue;
case WAIT_TIMEOUT:
if (timeout == TIMEOUT_INFINITE)
continue;
return false;
case WAIT_FAILED: case WAIT_FAILED:
DEBUG_WINERROR("Wait for event failed", GetLastError()); DEBUG_WINERROR("Wait for event failed", GetLastError());
return false; return false;