mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-04-26 16:46:28 +00:00
[c-host] dxgi: reworked for better pipelining
This commit is contained in:
parent
935eb0651d
commit
8120913acb
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user