mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-22 05:42:58 +00:00
[host] d12: implement damage aware copy
This commit is contained in:
parent
1098b7e6bd
commit
9de047d9cb
3 changed files with 209 additions and 15 deletions
|
@ -25,6 +25,8 @@
|
|||
#include <d3d12.h>
|
||||
#include "interface/capture.h"
|
||||
|
||||
#define D12_MAX_DIRTY_RECTS 256
|
||||
|
||||
typedef struct D12Backend D12Backend;
|
||||
|
||||
struct D12Backend
|
||||
|
@ -35,6 +37,9 @@ struct D12Backend
|
|||
// internal name
|
||||
const char * codeName;
|
||||
|
||||
// enable damage tracking
|
||||
bool trackDamage;
|
||||
|
||||
// creation/init/free
|
||||
bool (*create)(D12Backend ** instance, unsigned frameBuffers);
|
||||
bool (*init)(
|
||||
|
@ -54,7 +59,9 @@ struct D12Backend
|
|||
ID3D12CommandQueue * commandQueue);
|
||||
|
||||
ID3D12Resource * (*fetch)(D12Backend * instance,
|
||||
unsigned frameBufferIndex);
|
||||
unsigned frameBufferIndex,
|
||||
const RECT * dirtyRects[static D12_MAX_DIRTY_RECTS],
|
||||
unsigned * nbDirtyRects);
|
||||
};
|
||||
|
||||
static inline bool d12_backendCreate(const D12Backend * backend,
|
||||
|
@ -67,8 +74,12 @@ static inline bool d12_backendCreate(const D12Backend * backend,
|
|||
}
|
||||
|
||||
static inline bool d12_backendInit(D12Backend * instance, bool debug,
|
||||
ID3D12Device3 * device, IDXGIAdapter1 * adapter, IDXGIOutput * output)
|
||||
{ return instance->init(instance, debug, device, adapter, output); }
|
||||
ID3D12Device3 * device, IDXGIAdapter1 * adapter, IDXGIOutput * output,
|
||||
bool trackDamage)
|
||||
{
|
||||
instance->trackDamage = trackDamage;
|
||||
return instance->init(instance, debug, device, adapter, output);
|
||||
}
|
||||
|
||||
static inline bool d12_backendDeinit(D12Backend * instance)
|
||||
{ return instance->deinit(instance); }
|
||||
|
@ -85,8 +96,10 @@ static inline CaptureResult d12_backendSync(D12Backend * instance,
|
|||
{ return instance->sync(instance, commandQueue); }
|
||||
|
||||
static inline ID3D12Resource * d12_backendFetch(D12Backend * instance,
|
||||
unsigned frameBufferIndex)
|
||||
{ return instance->fetch(instance, frameBufferIndex); }
|
||||
unsigned frameBufferIndex, const RECT * dirtyRects[static D12_MAX_DIRTY_RECTS],
|
||||
unsigned * nbDirtyRects)
|
||||
{ return instance->fetch(instance, frameBufferIndex, dirtyRects,
|
||||
nbDirtyRects); }
|
||||
|
||||
// Backend defines
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ typedef struct DDCacheInfo
|
|||
ID3D12Fence ** d12Fence;
|
||||
UINT64 fenceValue;
|
||||
bool ready;
|
||||
|
||||
RECT dirtyRects[D12_MAX_DIRTY_RECTS];
|
||||
unsigned nbDirtyRects;
|
||||
}
|
||||
DDCacheInfo;
|
||||
|
||||
|
@ -416,13 +419,17 @@ static CaptureResult d12_dd_sync(D12Backend * instance,
|
|||
}
|
||||
|
||||
static ID3D12Resource * d12_dd_fetch(D12Backend * instance,
|
||||
unsigned frameBufferIndex)
|
||||
unsigned frameBufferIndex, const RECT * dirtyRects[static D12_MAX_DIRTY_RECTS],
|
||||
unsigned * nbDirtyRects)
|
||||
{
|
||||
DDInstance * this = UPCAST(DDInstance, instance);
|
||||
|
||||
if (!this->current)
|
||||
return NULL;
|
||||
|
||||
*dirtyRects = this->current->dirtyRects;
|
||||
*nbDirtyRects = this->current->nbDirtyRects;
|
||||
|
||||
ID3D12Resource_AddRef(*this->current->d12Res);
|
||||
return *this->current->d12Res;
|
||||
}
|
||||
|
@ -478,6 +485,75 @@ static bool d12_dd_handleFrameUpdate(DDInstance * this, IDXGIResource * res)
|
|||
ID3D11DeviceContext4_Signal(
|
||||
*this->context, *this->current->fence, this->current->fenceValue);
|
||||
|
||||
// handle damage tracking
|
||||
this->current->nbDirtyRects = 0;
|
||||
if (this->base.trackDamage)
|
||||
{
|
||||
/* Get the frame damage, if there is too many damage rects, we disable
|
||||
* damage tracking for the frame and assume full frame damage */
|
||||
|
||||
UINT requiredSize;
|
||||
hr = IDXGIOutputDuplication_GetFrameDirtyRects(*this->dup,
|
||||
sizeof(this->current->dirtyRects),
|
||||
this->current->dirtyRects,
|
||||
&requiredSize);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
if (hr != DXGI_ERROR_MORE_DATA)
|
||||
{
|
||||
DEBUG_WINERROR("GetFrameDirtyRects failed", hr);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
this->current->nbDirtyRects =
|
||||
requiredSize / sizeof(*this->current->dirtyRects);
|
||||
|
||||
DXGI_OUTDUPL_MOVE_RECT moveRects[
|
||||
(ARRAY_LENGTH(this->current->dirtyRects) - this->current->nbDirtyRects) / 2
|
||||
];
|
||||
hr = IDXGIOutputDuplication_GetFrameMoveRects(*this->dup,
|
||||
sizeof(moveRects), moveRects, &requiredSize);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
this->current->nbDirtyRects = 0;
|
||||
if (hr != DXGI_ERROR_MORE_DATA)
|
||||
{
|
||||
DEBUG_WINERROR("GetFrameMoveRects failed", hr);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move rects are seemingly not generated on Windows 10, but incase it
|
||||
* becomes a thing in the future we still need to implement this */
|
||||
const unsigned moveRectCount = requiredSize / sizeof(*moveRects);
|
||||
for(DXGI_OUTDUPL_MOVE_RECT *moveRect = moveRects; moveRect < moveRects +
|
||||
moveRectCount; ++moveRect)
|
||||
{
|
||||
/* According to WebRTC source comments, the DirectX capture API may
|
||||
* randomly return unmoved rects, which should be skipped to avoid
|
||||
* unnecessary work */
|
||||
if (moveRect->SourcePoint.x == moveRect->DestinationRect.left &&
|
||||
moveRect->SourcePoint.y == moveRect->DestinationRect.top)
|
||||
continue;
|
||||
|
||||
/* Add the source rect to the dirty array */
|
||||
this->current->dirtyRects[this->current->nbDirtyRects++] = (RECT)
|
||||
{
|
||||
.left = moveRect->SourcePoint.x,
|
||||
.top = moveRect->SourcePoint.y,
|
||||
.right = moveRect->SourcePoint.x +
|
||||
(moveRect->DestinationRect.right - moveRect->DestinationRect.left),
|
||||
.bottom = moveRect->SourcePoint.y +
|
||||
(moveRect->DestinationRect.bottom - moveRect->DestinationRect.top)
|
||||
};
|
||||
|
||||
/* Add the destination rect to the dirty array */
|
||||
this->current->dirtyRects[this->current->nbDirtyRects++] =
|
||||
moveRect->DestinationRect;
|
||||
}
|
||||
}
|
||||
|
||||
result = true;
|
||||
|
||||
exit:
|
||||
|
|
|
@ -65,8 +65,13 @@ struct D12Interface
|
|||
// output format tracking
|
||||
D3D12_RESOURCE_DESC dstFormat;
|
||||
|
||||
// prior frame dirty rects
|
||||
RECT dirtyRects[D12_MAX_DIRTY_RECTS];
|
||||
unsigned nbDirtyRects;
|
||||
|
||||
// options
|
||||
bool debug;
|
||||
bool trackDamage;
|
||||
bool allowRGB24;
|
||||
|
||||
unsigned frameBufferCount;
|
||||
|
@ -131,6 +136,13 @@ static void d12_initOptions(void)
|
|||
.type = OPTION_TYPE_STRING,
|
||||
.value.x_string = NULL
|
||||
},
|
||||
{
|
||||
.module = "d12",
|
||||
.name = "trackDamage",
|
||||
.description = "Perform damage-aware copies (saves bandwidth)",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "d12",
|
||||
.name = "allowRGB24",
|
||||
|
@ -139,6 +151,13 @@ static void d12_initOptions(void)
|
|||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{
|
||||
.module = "d12",
|
||||
.name = "debug",
|
||||
.description = "Enable DirectX12 debugging and validation (SLOW!)",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
@ -158,7 +177,16 @@ static bool d12_create(
|
|||
return false;
|
||||
}
|
||||
|
||||
this->debug = false;
|
||||
this->debug = option_get_bool("d12", "debug" );
|
||||
this->trackDamage = option_get_bool("d12", "trackDamage");
|
||||
this->allowRGB24 = option_get_bool("d12", "allowRGB24" );
|
||||
|
||||
DEBUG_INFO(
|
||||
"debug:%d trackDamage:%d allowRGB24:%d",
|
||||
this->debug,
|
||||
this->trackDamage,
|
||||
this->allowRGB24);
|
||||
|
||||
this->d3d12 = LoadLibrary("d3d12.dll");
|
||||
if (!this->d3d12)
|
||||
{
|
||||
|
@ -190,8 +218,6 @@ static bool d12_create(
|
|||
|
||||
this->frameBufferCount = frameBuffers;
|
||||
|
||||
this->allowRGB24 = option_get_bool("d12", "allowRGB24");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -318,7 +344,8 @@ retryCreateCommandQueue:
|
|||
*alignSize = heapDesc.Alignment;
|
||||
|
||||
// initialize the backend
|
||||
if (!d12_backendInit(this->backend, this->debug, *device, *adapter, *output))
|
||||
if (!d12_backendInit(this->backend, this->debug, *device, *adapter, *output,
|
||||
this->trackDamage))
|
||||
goto exit;
|
||||
|
||||
if (this->allowRGB24)
|
||||
|
@ -396,8 +423,12 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
|
|||
CaptureResult result = CAPTURE_RESULT_ERROR;
|
||||
comRef_scopePush(1);
|
||||
|
||||
const RECT * dirtyRects;
|
||||
unsigned nbDirtyRects;
|
||||
|
||||
comRef_defineLocal(ID3D12Resource, src);
|
||||
*src = d12_backendFetch(this->backend, frameBufferIndex);
|
||||
*src = d12_backendFetch(this->backend, frameBufferIndex,
|
||||
&dirtyRects, &nbDirtyRects);
|
||||
if (!*src)
|
||||
{
|
||||
DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
|
||||
|
@ -457,7 +488,23 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
|
|||
frame->hdr = false;
|
||||
frame->hdrPQ = false;
|
||||
frame->rotation = CAPTURE_ROT_0;
|
||||
frame->damageRectsCount = 0;
|
||||
|
||||
// if there are too many rects
|
||||
if (unlikely(nbDirtyRects > ARRAY_LENGTH(frame->damageRects)))
|
||||
frame->damageRectsCount = 0;
|
||||
else
|
||||
{
|
||||
// send the list of dirty rects for this frame
|
||||
frame->damageRectsCount = nbDirtyRects;
|
||||
for(unsigned i = 0; i < nbDirtyRects; ++i)
|
||||
frame->damageRects[i] = (FrameDamageRect)
|
||||
{
|
||||
.x = dirtyRects[i].left,
|
||||
.y = dirtyRects[i].top,
|
||||
.width = dirtyRects[i].right - dirtyRects[i].left,
|
||||
.height = dirtyRects[i].bottom - dirtyRects[i].top
|
||||
};
|
||||
}
|
||||
|
||||
result = CAPTURE_RESULT_OK;
|
||||
|
||||
|
@ -472,8 +519,12 @@ static CaptureResult d12_getFrame(unsigned frameBufferIndex,
|
|||
CaptureResult result = CAPTURE_RESULT_ERROR;
|
||||
comRef_scopePush(3);
|
||||
|
||||
const RECT * dirtyRects;
|
||||
unsigned nbDirtyRects;
|
||||
|
||||
comRef_defineLocal(ID3D12Resource, src);
|
||||
*src = d12_backendFetch(this->backend, frameBufferIndex);
|
||||
*src = d12_backendFetch(this->backend, frameBufferIndex,
|
||||
&dirtyRects, &nbDirtyRects);
|
||||
if (!*src)
|
||||
{
|
||||
DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
|
||||
|
@ -526,8 +577,62 @@ static CaptureResult d12_getFrame(unsigned frameBufferIndex,
|
|||
}
|
||||
};
|
||||
|
||||
ID3D12GraphicsCommandList_CopyTextureRegion(
|
||||
*this->copyCommand.gfxList, &dstLoc, 0, 0, 0, &srcLoc, NULL);
|
||||
// if full frame damage
|
||||
if (nbDirtyRects == 0)
|
||||
{
|
||||
this->nbDirtyRects = 0;
|
||||
ID3D12GraphicsCommandList_CopyTextureRegion(
|
||||
*this->copyCommand.gfxList, &dstLoc, 0, 0, 0, &srcLoc, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we must update the rects that were dirty in the prior frame also,
|
||||
* otherwise the frame in memory will not be consistent when areas need to
|
||||
* be redrawn by the client, such as under the cursor */
|
||||
if (this->nbDirtyRects > 0)
|
||||
{
|
||||
for(const RECT * rect = this->dirtyRects;
|
||||
rect < this->dirtyRects + this->nbDirtyRects; ++rect)
|
||||
{
|
||||
D3D12_BOX box =
|
||||
{
|
||||
.left = rect->left,
|
||||
.top = rect->top,
|
||||
.front = 0,
|
||||
.back = 1,
|
||||
.right = rect->right,
|
||||
.bottom = rect->bottom
|
||||
};
|
||||
|
||||
ID3D12GraphicsCommandList_CopyTextureRegion(
|
||||
*this->copyCommand.gfxList, &dstLoc,
|
||||
box.left, box.top, 0, &srcLoc, &box);
|
||||
}
|
||||
}
|
||||
|
||||
/* update the frame with the new dirty areas */
|
||||
for(const RECT * rect = dirtyRects; rect < dirtyRects + nbDirtyRects; ++rect)
|
||||
{
|
||||
D3D12_BOX box =
|
||||
{
|
||||
.left = rect->left,
|
||||
.top = rect->top,
|
||||
.front = 0,
|
||||
.back = 1,
|
||||
.right = rect->right,
|
||||
.bottom = rect->bottom
|
||||
};
|
||||
|
||||
ID3D12GraphicsCommandList_CopyTextureRegion(
|
||||
*this->copyCommand.gfxList, &dstLoc,
|
||||
box.left, box.top, 0, &srcLoc, &box);
|
||||
}
|
||||
|
||||
/* store the dirty rects for the next frame */
|
||||
memcpy(this->dirtyRects, dirtyRects,
|
||||
nbDirtyRects * sizeof(*this->dirtyRects));
|
||||
this->nbDirtyRects = nbDirtyRects;
|
||||
}
|
||||
|
||||
// execute the compute commands
|
||||
if (this->allowRGB24)
|
||||
|
|
Loading…
Reference in a new issue