[host] dxgi: rework locking and retry logic for lower latency

This commit is contained in:
Geoffrey McRae 2020-08-15 20:49:49 +10:00
parent cdc3384883
commit 1c7961daeb

View File

@ -27,6 +27,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <assert.h> #include <assert.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <unistd.h>
#include <dxgi.h> #include <dxgi.h>
#include <d3d11.h> #include <d3d11.h>
#include <d3dcommon.h> #include <d3dcommon.h>
@ -56,7 +57,7 @@ enum TextureState
typedef struct Texture typedef struct Texture
{ {
enum TextureState state; volatile enum TextureState state;
ID3D11Texture2D * tex; ID3D11Texture2D * tex;
D3D11_MAPPED_SUBRESOURCE map; D3D11_MAPPED_SUBRESOURCE map;
} }
@ -706,6 +707,15 @@ static CaptureResult dxgi_capture()
DXGI_OUTDUPL_FRAME_INFO frameInfo; DXGI_OUTDUPL_FRAME_INFO frameInfo;
IDXGIResource * res; IDXGIResource * res;
bool copyFrame = false;
bool copyPointer = false;
ID3D11Texture2D * src;
bool postPointer = false;
CapturePointer pointer = { 0 };
void * pointerShape = NULL;
UINT pointerShapeSize = 0;
// release the prior frame // release the prior frame
result = dxgi_releaseFrame(); result = dxgi_releaseFrame();
if (result != CAPTURE_RESULT_OK) if (result != CAPTURE_RESULT_OK)
@ -737,7 +747,7 @@ static CaptureResult dxgi_capture()
// check if the texture is free, if not skip the frame to keep up // check if the texture is free, if not skip the frame to keep up
if (tex->state == TEXTURE_STATE_UNUSED) if (tex->state == TEXTURE_STATE_UNUSED)
{ {
ID3D11Texture2D * src; copyFrame = true;
status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src); status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src);
if (FAILED(status)) if (FAILED(status))
{ {
@ -745,13 +755,43 @@ static CaptureResult dxgi_capture()
IDXGIResource_Release(res); IDXGIResource_Release(res);
return CAPTURE_RESULT_ERROR; return CAPTURE_RESULT_ERROR;
} }
}
}
LOCKED({ // if the pointer shape has changed
// issue the copy from GPU to CPU RAM and release the src uint32_t bufferSize;
if (frameInfo.PointerShapeBufferSize > 0)
{
if(!this->getPointerBufferFn(&pointerShape, &bufferSize))
DEBUG_WARN("Failed to obtain a buffer for the pointer shape");
else
copyPointer = true;
}
if (copyFrame || copyPointer)
{
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
LOCKED(
{
if (copyFrame)
{
// issue the copy from GPU to CPU RAM
ID3D11DeviceContext_CopyResource(this->deviceContext, ID3D11DeviceContext_CopyResource(this->deviceContext,
(ID3D11Resource *)tex->tex, (ID3D11Resource *)src); (ID3D11Resource *)tex->tex, (ID3D11Resource *)src);
}); }
if (copyPointer)
{
// grab the pointer shape
status = IDXGIOutputDuplication_GetFramePointerShape(
this->dup, bufferSize, pointerShape, &pointerShapeSize, &shapeInfo);
}
ID3D11DeviceContext_Flush(this->deviceContext);
});
if (copyFrame)
{
ID3D11Texture2D_Release(src); ID3D11Texture2D_Release(src);
// set the state, and signal // set the state, and signal
@ -766,51 +806,9 @@ static CaptureResult dxgi_capture()
// update the last frame time // update the last frame time
this->frameTime.QuadPart = frameInfo.LastPresentTime.QuadPart; this->frameTime.QuadPart = frameInfo.LastPresentTime.QuadPart;
} }
}
IDXGIResource_Release(res); if (copyPointer)
// if the pointer has moved or changed state
bool postPointer = false;
CapturePointer pointer = { 0 };
void * pointerShape = NULL;
UINT pointerShapeSize = 0;
if (frameInfo.LastMouseUpdateTime.QuadPart)
{
/* the pointer position is only valid if the pointer is visible */
if (frameInfo.PointerPosition.Visible &&
(frameInfo.PointerPosition.Position.x != this->lastPointerX ||
frameInfo.PointerPosition.Position.y != this->lastPointerY))
{ {
pointer.positionUpdate = true;
pointer.x =
this->lastPointerX =
frameInfo.PointerPosition.Position.x;
pointer.y =
this->lastPointerY =
frameInfo.PointerPosition.Position.y;
postPointer = true;
}
if (this->lastPointerVisible != frameInfo.PointerPosition.Visible)
{
this->lastPointerVisible = frameInfo.PointerPosition.Visible;
postPointer = true;
}
}
// if the pointer shape has changed
if (frameInfo.PointerShapeBufferSize > 0)
{
uint32_t bufferSize;
if(!this->getPointerBufferFn(&pointerShape, &bufferSize))
DEBUG_WARN("Failed to obtain a buffer for the pointer shape");
else
{
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
LOCKED({status = IDXGIOutputDuplication_GetFramePointerShape(this->dup, bufferSize, pointerShape, &pointerShapeSize, &shapeInfo);});
result = dxgi_hResultToCaptureResult(status); result = dxgi_hResultToCaptureResult(status);
if (result != CAPTURE_RESULT_OK) if (result != CAPTURE_RESULT_OK)
{ {
@ -837,6 +835,32 @@ static CaptureResult dxgi_capture()
} }
} }
IDXGIResource_Release(res);
if (frameInfo.LastMouseUpdateTime.QuadPart)
{
/* the pointer position is only valid if the pointer is visible */
if (frameInfo.PointerPosition.Visible &&
(frameInfo.PointerPosition.Position.x != this->lastPointerX ||
frameInfo.PointerPosition.Position.y != this->lastPointerY))
{
pointer.positionUpdate = true;
pointer.x =
this->lastPointerX =
frameInfo.PointerPosition.Position.x;
pointer.y =
this->lastPointerY =
frameInfo.PointerPosition.Position.y;
postPointer = true;
}
if (this->lastPointerVisible != frameInfo.PointerPosition.Visible)
{
this->lastPointerVisible = frameInfo.PointerPosition.Visible;
postPointer = true;
}
}
// post back the pointer information // post back the pointer information
if (postPointer) if (postPointer)
{ {
@ -866,15 +890,26 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
Texture * tex = &this->texture[this->texRIndex]; Texture * tex = &this->texture[this->texRIndex];
// try to map the resource, but don't wait for it // try to map the resource, but don't wait for it
HRESULT status; for (int i = 0; ; ++i)
LOCKED({status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)tex->tex, 0, D3D11_MAP_READ, 0x100000L, &tex->map);});
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
return CAPTURE_RESULT_TIMEOUT;
if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to map the texture", status); HRESULT status;
return CAPTURE_RESULT_ERROR; LOCKED({status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)tex->tex, 0, D3D11_MAP_READ, 0x100000L, &tex->map);});
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
{
if (i == 100)
return CAPTURE_RESULT_TIMEOUT;
usleep(1);
continue;
}
if (FAILED(status))
{
DEBUG_WINERROR("Failed to map the texture", status);
return CAPTURE_RESULT_ERROR;
}
break;
} }
tex->state = TEXTURE_STATE_MAPPED; tex->state = TEXTURE_STATE_MAPPED;