mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-04 06:12:04 +00:00
[idd] coalesce framebuffer copy damage
Combine the previous and current frame damage before recording copy commands. Remove contained regions, merge beneficial overlaps, and use one full-frame copy when partial work reaches the full surface size. This prevents a previous full-frame update from being copied again alongside the current damage.
This commit is contained in:
@@ -28,6 +28,9 @@
|
|||||||
static const uint32_t HDR_PQ_MIN_LUMINANCE = 50;
|
static const uint32_t HDR_PQ_MIN_LUMINANCE = 50;
|
||||||
static const uint32_t HDR_PQ_MAX_LUMINANCE = 10000;
|
static const uint32_t HDR_PQ_MAX_LUMINANCE = 10000;
|
||||||
|
|
||||||
|
static_assert(LGMP_Q_FRAME_LEN == 2,
|
||||||
|
"IDD damage repair assumes two alternating frame buffers");
|
||||||
|
|
||||||
static uint64_t Nanotime()
|
static uint64_t Nanotime()
|
||||||
{
|
{
|
||||||
static const uint64_t frequency = []()
|
static const uint64_t frequency = []()
|
||||||
@@ -356,12 +359,98 @@ void CSwapChainProcessor::CompletionFunction(
|
|||||||
static bool IsFullDamage(const RECT * dirtyRects, unsigned nbDirtyRects,
|
static bool IsFullDamage(const RECT * dirtyRects, unsigned nbDirtyRects,
|
||||||
const D3D12_RESOURCE_DESC& desc)
|
const D3D12_RESOURCE_DESC& desc)
|
||||||
{
|
{
|
||||||
return nbDirtyRects == 0 ||
|
for (const RECT * rect = dirtyRects;
|
||||||
(nbDirtyRects == 1 &&
|
rect < dirtyRects + nbDirtyRects; ++rect)
|
||||||
dirtyRects[0].left == 0 &&
|
if (rect->left == 0 &&
|
||||||
dirtyRects[0].top == 0 &&
|
rect->top == 0 &&
|
||||||
dirtyRects[0].right == (LONG)desc.Width &&
|
rect->right == (LONG)desc.Width &&
|
||||||
dirtyRects[0].bottom == (LONG)desc.Height);
|
rect->bottom == (LONG)desc.Height)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DirtyRectContains(const RECT& outer, const RECT& inner)
|
||||||
|
{
|
||||||
|
return outer.left <= inner.left &&
|
||||||
|
outer.top <= inner.top &&
|
||||||
|
outer.right >= inner.right &&
|
||||||
|
outer.bottom >= inner.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DirtyRectsTouchOrIntersect(const RECT& a, const RECT& b)
|
||||||
|
{
|
||||||
|
return a.left <= b.right && a.right >= b.left &&
|
||||||
|
a.top <= b.bottom && a.bottom >= b.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RECT MergeDirtyRects(const RECT& a, const RECT& b)
|
||||||
|
{
|
||||||
|
RECT result;
|
||||||
|
result.left = min(a.left , b.left );
|
||||||
|
result.top = min(a.top , b.top );
|
||||||
|
result.right = max(a.right , b.right );
|
||||||
|
result.bottom = max(a.bottom, b.bottom);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t DirtyRectArea(const RECT& rect)
|
||||||
|
{
|
||||||
|
const uint64_t width = (uint64_t)((int64_t)rect.right - rect.left);
|
||||||
|
const uint64_t height = (uint64_t)((int64_t)rect.bottom - rect.top );
|
||||||
|
return width * height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AddCopyDirtyRect(RECT dirtyRects[], unsigned capacity,
|
||||||
|
unsigned * nbDirtyRects, const RECT& dirtyRect)
|
||||||
|
{
|
||||||
|
RECT candidate = dirtyRect;
|
||||||
|
for (unsigned i = 0; i < *nbDirtyRects;)
|
||||||
|
{
|
||||||
|
if (DirtyRectContains(dirtyRects[i], candidate))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const RECT merged = MergeDirtyRects(dirtyRects[i], candidate);
|
||||||
|
// Reduce command and overlap cost without copying more pixels than the
|
||||||
|
// two original rectangles would have copied.
|
||||||
|
if (DirtyRectContains(candidate, dirtyRects[i]) ||
|
||||||
|
(DirtyRectsTouchOrIntersect(dirtyRects[i], candidate) &&
|
||||||
|
DirtyRectArea(merged) <=
|
||||||
|
DirtyRectArea(dirtyRects[i]) + DirtyRectArea(candidate)))
|
||||||
|
{
|
||||||
|
candidate = merged;
|
||||||
|
--(*nbDirtyRects);
|
||||||
|
dirtyRects[i] = dirtyRects[*nbDirtyRects];
|
||||||
|
i = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*nbDirtyRects >= capacity)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dirtyRects[(*nbDirtyRects)++] = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CopyAreaCoversFrame(const RECT * dirtyRects,
|
||||||
|
unsigned nbDirtyRects, const D3D12_RESOURCE_DESC& desc)
|
||||||
|
{
|
||||||
|
const uint64_t frameArea = (uint64_t)desc.Width * desc.Height;
|
||||||
|
uint64_t copyArea = 0;
|
||||||
|
|
||||||
|
for (const RECT * rect = dirtyRects;
|
||||||
|
rect < dirtyRects + nbDirtyRects; ++rect)
|
||||||
|
{
|
||||||
|
const uint64_t area = DirtyRectArea(*rect);
|
||||||
|
if (area >= frameArea - copyArea)
|
||||||
|
return true;
|
||||||
|
copyArea += area;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CopyDirtyRect(ComPtr<ID3D12GraphicsCommandList> list,
|
static void CopyDirtyRect(ComPtr<ID3D12GraphicsCommandList> list,
|
||||||
@@ -802,30 +891,51 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||||
dstLoc.PlacedFootprint = layout;
|
dstLoc.PlacedFootprint = layout;
|
||||||
|
|
||||||
|
/* Each destination is reused every other frame, so repair both the prior
|
||||||
|
* and current damage. Coalesce them first to avoid copying overlapping
|
||||||
|
* regions, especially a prior full frame, more than once. */
|
||||||
|
RECT copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {};
|
||||||
|
unsigned nbCopyDirtyRects = 0;
|
||||||
|
bool fullCopy =
|
||||||
|
nbDirtyRects == 0 || m_nbDirtyRects == 0;
|
||||||
|
|
||||||
|
if (!fullCopy)
|
||||||
|
{
|
||||||
|
for (const RECT * rect = m_dirtyRects;
|
||||||
|
rect < m_dirtyRects + m_nbDirtyRects && !fullCopy; ++rect)
|
||||||
|
{
|
||||||
|
RECT clipped = *rect;
|
||||||
|
if (ClipDirtyRect(clipped, dstFormat.desc) &&
|
||||||
|
!AddCopyDirtyRect(copyDirtyRects, ARRAYSIZE(copyDirtyRects),
|
||||||
|
&nbCopyDirtyRects, clipped))
|
||||||
|
fullCopy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const RECT * rect = currentDirtyRects;
|
||||||
|
rect < currentDirtyRects + nbDirtyRects && !fullCopy; ++rect)
|
||||||
|
if (!AddCopyDirtyRect(copyDirtyRects, ARRAYSIZE(copyDirtyRects),
|
||||||
|
&nbCopyDirtyRects, *rect))
|
||||||
|
fullCopy = true;
|
||||||
|
|
||||||
|
if (!fullCopy)
|
||||||
|
fullCopy = IsFullDamage(
|
||||||
|
copyDirtyRects, nbCopyDirtyRects, dstFormat.desc) ||
|
||||||
|
CopyAreaCoversFrame(
|
||||||
|
copyDirtyRects, nbCopyDirtyRects, dstFormat.desc);
|
||||||
|
}
|
||||||
|
|
||||||
// The source/compute waits were inserted directly on the queue above. The
|
// The source/compute waits were inserted directly on the queue above. The
|
||||||
// command-list timestamp therefore marks the first actual copy operation.
|
// command-list timestamp therefore marks the first actual copy operation.
|
||||||
copyQueue->BeginTiming();
|
copyQueue->BeginTiming();
|
||||||
if (IsFullDamage(currentDirtyRects, nbDirtyRects, dstFormat.desc) ||
|
if (fullCopy)
|
||||||
nbDirtyRects > KVMFR_MAX_DAMAGE_RECTS || m_nbDirtyRects == 0)
|
|
||||||
{
|
|
||||||
copyQueue->GetGfxList()->CopyTextureRegion(
|
|
||||||
&dstLoc, 0, 0, 0, &srcLoc, NULL);
|
|
||||||
}
|
|
||||||
else if (m_nbDirtyRects + nbDirtyRects > LG_MAX_DIRTY_RECTS)
|
|
||||||
{
|
{
|
||||||
copyQueue->GetGfxList()->CopyTextureRegion(
|
copyQueue->GetGfxList()->CopyTextureRegion(
|
||||||
&dstLoc, 0, 0, 0, &srcLoc, NULL);
|
&dstLoc, 0, 0, 0, &srcLoc, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const RECT * rect = m_dirtyRects; rect < m_dirtyRects + m_nbDirtyRects; ++rect)
|
for (const RECT * rect = copyDirtyRects;
|
||||||
{
|
rect < copyDirtyRects + nbCopyDirtyRects; ++rect)
|
||||||
RECT clipped = *rect;
|
|
||||||
if (ClipDirtyRect(clipped, dstFormat.desc))
|
|
||||||
CopyDirtyRect(copyQueue->GetGfxList(), &dstLoc, &srcLoc, clipped);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const RECT * rect = currentDirtyRects; rect < currentDirtyRects + nbDirtyRects; ++rect)
|
|
||||||
CopyDirtyRect(copyQueue->GetGfxList(), &dstLoc, &srcLoc, *rect);
|
CopyDirtyRect(copyQueue->GetGfxList(), &dstLoc, &srcLoc, *rect);
|
||||||
}
|
}
|
||||||
copyQueue->EndTiming();
|
copyQueue->EndTiming();
|
||||||
|
|||||||
Reference in New Issue
Block a user