mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-25 14:57:20 +00:00
[host] d12: implement damage aware copy
This commit is contained in:
parent
1098b7e6bd
commit
9de047d9cb
@ -25,6 +25,8 @@
|
|||||||
#include <d3d12.h>
|
#include <d3d12.h>
|
||||||
#include "interface/capture.h"
|
#include "interface/capture.h"
|
||||||
|
|
||||||
|
#define D12_MAX_DIRTY_RECTS 256
|
||||||
|
|
||||||
typedef struct D12Backend D12Backend;
|
typedef struct D12Backend D12Backend;
|
||||||
|
|
||||||
struct D12Backend
|
struct D12Backend
|
||||||
@ -35,6 +37,9 @@ struct D12Backend
|
|||||||
// internal name
|
// internal name
|
||||||
const char * codeName;
|
const char * codeName;
|
||||||
|
|
||||||
|
// enable damage tracking
|
||||||
|
bool trackDamage;
|
||||||
|
|
||||||
// creation/init/free
|
// creation/init/free
|
||||||
bool (*create)(D12Backend ** instance, unsigned frameBuffers);
|
bool (*create)(D12Backend ** instance, unsigned frameBuffers);
|
||||||
bool (*init)(
|
bool (*init)(
|
||||||
@ -54,7 +59,9 @@ struct D12Backend
|
|||||||
ID3D12CommandQueue * commandQueue);
|
ID3D12CommandQueue * commandQueue);
|
||||||
|
|
||||||
ID3D12Resource * (*fetch)(D12Backend * instance,
|
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,
|
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,
|
static inline bool d12_backendInit(D12Backend * instance, bool debug,
|
||||||
ID3D12Device3 * device, IDXGIAdapter1 * adapter, IDXGIOutput * output)
|
ID3D12Device3 * device, IDXGIAdapter1 * adapter, IDXGIOutput * output,
|
||||||
{ return instance->init(instance, debug, device, adapter, output); }
|
bool trackDamage)
|
||||||
|
{
|
||||||
|
instance->trackDamage = trackDamage;
|
||||||
|
return instance->init(instance, debug, device, adapter, output);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool d12_backendDeinit(D12Backend * instance)
|
static inline bool d12_backendDeinit(D12Backend * instance)
|
||||||
{ return instance->deinit(instance); }
|
{ return instance->deinit(instance); }
|
||||||
@ -85,8 +96,10 @@ static inline CaptureResult d12_backendSync(D12Backend * instance,
|
|||||||
{ return instance->sync(instance, commandQueue); }
|
{ return instance->sync(instance, commandQueue); }
|
||||||
|
|
||||||
static inline ID3D12Resource * d12_backendFetch(D12Backend * instance,
|
static inline ID3D12Resource * d12_backendFetch(D12Backend * instance,
|
||||||
unsigned frameBufferIndex)
|
unsigned frameBufferIndex, const RECT * dirtyRects[static D12_MAX_DIRTY_RECTS],
|
||||||
{ return instance->fetch(instance, frameBufferIndex); }
|
unsigned * nbDirtyRects)
|
||||||
|
{ return instance->fetch(instance, frameBufferIndex, dirtyRects,
|
||||||
|
nbDirtyRects); }
|
||||||
|
|
||||||
// Backend defines
|
// Backend defines
|
||||||
|
|
||||||
|
@ -51,6 +51,9 @@ typedef struct DDCacheInfo
|
|||||||
ID3D12Fence ** d12Fence;
|
ID3D12Fence ** d12Fence;
|
||||||
UINT64 fenceValue;
|
UINT64 fenceValue;
|
||||||
bool ready;
|
bool ready;
|
||||||
|
|
||||||
|
RECT dirtyRects[D12_MAX_DIRTY_RECTS];
|
||||||
|
unsigned nbDirtyRects;
|
||||||
}
|
}
|
||||||
DDCacheInfo;
|
DDCacheInfo;
|
||||||
|
|
||||||
@ -416,13 +419,17 @@ static CaptureResult d12_dd_sync(D12Backend * instance,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ID3D12Resource * d12_dd_fetch(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);
|
DDInstance * this = UPCAST(DDInstance, instance);
|
||||||
|
|
||||||
if (!this->current)
|
if (!this->current)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
*dirtyRects = this->current->dirtyRects;
|
||||||
|
*nbDirtyRects = this->current->nbDirtyRects;
|
||||||
|
|
||||||
ID3D12Resource_AddRef(*this->current->d12Res);
|
ID3D12Resource_AddRef(*this->current->d12Res);
|
||||||
return *this->current->d12Res;
|
return *this->current->d12Res;
|
||||||
}
|
}
|
||||||
@ -478,6 +485,75 @@ static bool d12_dd_handleFrameUpdate(DDInstance * this, IDXGIResource * res)
|
|||||||
ID3D11DeviceContext4_Signal(
|
ID3D11DeviceContext4_Signal(
|
||||||
*this->context, *this->current->fence, this->current->fenceValue);
|
*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;
|
result = true;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
@ -65,8 +65,13 @@ struct D12Interface
|
|||||||
// output format tracking
|
// output format tracking
|
||||||
D3D12_RESOURCE_DESC dstFormat;
|
D3D12_RESOURCE_DESC dstFormat;
|
||||||
|
|
||||||
|
// prior frame dirty rects
|
||||||
|
RECT dirtyRects[D12_MAX_DIRTY_RECTS];
|
||||||
|
unsigned nbDirtyRects;
|
||||||
|
|
||||||
// options
|
// options
|
||||||
bool debug;
|
bool debug;
|
||||||
|
bool trackDamage;
|
||||||
bool allowRGB24;
|
bool allowRGB24;
|
||||||
|
|
||||||
unsigned frameBufferCount;
|
unsigned frameBufferCount;
|
||||||
@ -131,6 +136,13 @@ static void d12_initOptions(void)
|
|||||||
.type = OPTION_TYPE_STRING,
|
.type = OPTION_TYPE_STRING,
|
||||||
.value.x_string = NULL
|
.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",
|
.module = "d12",
|
||||||
.name = "allowRGB24",
|
.name = "allowRGB24",
|
||||||
@ -139,6 +151,13 @@ static void d12_initOptions(void)
|
|||||||
.type = OPTION_TYPE_BOOL,
|
.type = OPTION_TYPE_BOOL,
|
||||||
.value.x_bool = false
|
.value.x_bool = false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.module = "d12",
|
||||||
|
.name = "debug",
|
||||||
|
.description = "Enable DirectX12 debugging and validation (SLOW!)",
|
||||||
|
.type = OPTION_TYPE_BOOL,
|
||||||
|
.value.x_bool = false
|
||||||
|
},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -158,7 +177,16 @@ static bool d12_create(
|
|||||||
return false;
|
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");
|
this->d3d12 = LoadLibrary("d3d12.dll");
|
||||||
if (!this->d3d12)
|
if (!this->d3d12)
|
||||||
{
|
{
|
||||||
@ -190,8 +218,6 @@ static bool d12_create(
|
|||||||
|
|
||||||
this->frameBufferCount = frameBuffers;
|
this->frameBufferCount = frameBuffers;
|
||||||
|
|
||||||
this->allowRGB24 = option_get_bool("d12", "allowRGB24");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +344,8 @@ retryCreateCommandQueue:
|
|||||||
*alignSize = heapDesc.Alignment;
|
*alignSize = heapDesc.Alignment;
|
||||||
|
|
||||||
// initialize the backend
|
// 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;
|
goto exit;
|
||||||
|
|
||||||
if (this->allowRGB24)
|
if (this->allowRGB24)
|
||||||
@ -396,8 +423,12 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
|
|||||||
CaptureResult result = CAPTURE_RESULT_ERROR;
|
CaptureResult result = CAPTURE_RESULT_ERROR;
|
||||||
comRef_scopePush(1);
|
comRef_scopePush(1);
|
||||||
|
|
||||||
|
const RECT * dirtyRects;
|
||||||
|
unsigned nbDirtyRects;
|
||||||
|
|
||||||
comRef_defineLocal(ID3D12Resource, src);
|
comRef_defineLocal(ID3D12Resource, src);
|
||||||
*src = d12_backendFetch(this->backend, frameBufferIndex);
|
*src = d12_backendFetch(this->backend, frameBufferIndex,
|
||||||
|
&dirtyRects, &nbDirtyRects);
|
||||||
if (!*src)
|
if (!*src)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
|
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->hdr = false;
|
||||||
frame->hdrPQ = false;
|
frame->hdrPQ = false;
|
||||||
frame->rotation = CAPTURE_ROT_0;
|
frame->rotation = CAPTURE_ROT_0;
|
||||||
|
|
||||||
|
// if there are too many rects
|
||||||
|
if (unlikely(nbDirtyRects > ARRAY_LENGTH(frame->damageRects)))
|
||||||
frame->damageRectsCount = 0;
|
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;
|
result = CAPTURE_RESULT_OK;
|
||||||
|
|
||||||
@ -472,8 +519,12 @@ static CaptureResult d12_getFrame(unsigned frameBufferIndex,
|
|||||||
CaptureResult result = CAPTURE_RESULT_ERROR;
|
CaptureResult result = CAPTURE_RESULT_ERROR;
|
||||||
comRef_scopePush(3);
|
comRef_scopePush(3);
|
||||||
|
|
||||||
|
const RECT * dirtyRects;
|
||||||
|
unsigned nbDirtyRects;
|
||||||
|
|
||||||
comRef_defineLocal(ID3D12Resource, src);
|
comRef_defineLocal(ID3D12Resource, src);
|
||||||
*src = d12_backendFetch(this->backend, frameBufferIndex);
|
*src = d12_backendFetch(this->backend, frameBufferIndex,
|
||||||
|
&dirtyRects, &nbDirtyRects);
|
||||||
if (!*src)
|
if (!*src)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
|
DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
|
||||||
@ -526,8 +577,62 @@ static CaptureResult d12_getFrame(unsigned frameBufferIndex,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// if full frame damage
|
||||||
|
if (nbDirtyRects == 0)
|
||||||
|
{
|
||||||
|
this->nbDirtyRects = 0;
|
||||||
ID3D12GraphicsCommandList_CopyTextureRegion(
|
ID3D12GraphicsCommandList_CopyTextureRegion(
|
||||||
*this->copyCommand.gfxList, &dstLoc, 0, 0, 0, &srcLoc, NULL);
|
*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
|
// execute the compute commands
|
||||||
if (this->allowRGB24)
|
if (this->allowRGB24)
|
||||||
|
Loading…
Reference in New Issue
Block a user