mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] capture: split software and hardware frame processors
Move cadence-aware hardware capture and immediate software capture behind a common frame processor interface. Keep shared damage tracking and frame-buffer ownership in the base processor, and move stateless format, resource, and rectangle helpers into CFrameProcessorUtil. Decouple frame-buffer resources from CSwapChainProcessor by passing the device dependencies they use directly.
This commit is contained in:
270
idd/LGIdd/CFrameProcessorUtil.cpp
Normal file
270
idd/LGIdd/CFrameProcessorUtil.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "CFrameProcessorUtil.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
bool CFrameProcessorUtil::FrameMetadataChanged(
|
||||
const D12FrameFormat& previous, const D12FrameFormat& current)
|
||||
{
|
||||
return
|
||||
previous.hdrMetadata != current.hdrMetadata ||
|
||||
previous.sdrWhiteLevel != current.sdrWhiteLevel ||
|
||||
(current.hdrMetadata &&
|
||||
(memcmp(previous.displayPrimary, current.displayPrimary,
|
||||
sizeof(current.displayPrimary)) != 0 ||
|
||||
memcmp(previous.whitePoint, current.whitePoint,
|
||||
sizeof(current.whitePoint)) != 0 ||
|
||||
previous.maxDisplayLuminance != current.maxDisplayLuminance ||
|
||||
previous.minDisplayLuminance != current.minDisplayLuminance ||
|
||||
previous.maxContentLightLevel != current.maxContentLightLevel ||
|
||||
previous.maxFrameAverageLightLevel != current.maxFrameAverageLightLevel));
|
||||
}
|
||||
|
||||
FrameType CFrameProcessorUtil::GetFrameType(DXGI_FORMAT format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM : return FRAME_TYPE_BGRA;
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM : return FRAME_TYPE_RGBA;
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM : return FRAME_TYPE_RGBA10;
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT: return FRAME_TYPE_RGBA16F;
|
||||
default : return FRAME_TYPE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
bool CFrameProcessorUtil::ResourceDescMatches(
|
||||
const D3D12_RESOURCE_DESC& left, const D3D12_RESOURCE_DESC& right,
|
||||
bool compareAlignment)
|
||||
{
|
||||
// GetDesc may report a resolved alignment when resource creation requested
|
||||
// automatic alignment, so callers comparing creation descriptors can omit
|
||||
// this allocation metadata.
|
||||
return
|
||||
left.Dimension == right.Dimension &&
|
||||
(!compareAlignment || left.Alignment == right.Alignment) &&
|
||||
left.Width == right.Width &&
|
||||
left.Height == right.Height &&
|
||||
left.DepthOrArraySize == right.DepthOrArraySize &&
|
||||
left.MipLevels == right.MipLevels &&
|
||||
left.Format == right.Format &&
|
||||
left.SampleDesc.Count == right.SampleDesc.Count &&
|
||||
left.SampleDesc.Quality == right.SampleDesc.Quality &&
|
||||
left.Layout == right.Layout &&
|
||||
left.Flags == right.Flags;
|
||||
}
|
||||
|
||||
static bool IsFullDamage(const RECT * dirtyRects, unsigned nbDirtyRects,
|
||||
unsigned width, unsigned height)
|
||||
{
|
||||
for (const RECT * rect = dirtyRects;
|
||||
rect < dirtyRects + nbDirtyRects; ++rect)
|
||||
if (rect->left == 0 &&
|
||||
rect->top == 0 &&
|
||||
rect->right == (LONG)width &&
|
||||
rect->bottom == (LONG)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);
|
||||
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, unsigned width, unsigned height)
|
||||
{
|
||||
const uint64_t frameArea = (uint64_t)width * 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 bool ClipDirtyRect(RECT& rect, unsigned width, unsigned height)
|
||||
{
|
||||
const LONG maxRight = (LONG)width;
|
||||
const LONG maxBottom = (LONG)height;
|
||||
|
||||
if (rect.left < 0 ) rect.left = 0;
|
||||
if (rect.top < 0 ) rect.top = 0;
|
||||
if (rect.right > maxRight ) rect.right = maxRight;
|
||||
if (rect.bottom > maxBottom) rect.bottom = maxBottom;
|
||||
|
||||
return rect.left < rect.right && rect.top < rect.bottom;
|
||||
}
|
||||
|
||||
void CFrameProcessorUtil::ClipDirtyRects(
|
||||
RECT dirtyRects[], unsigned * nbDirtyRects,
|
||||
unsigned width, unsigned height)
|
||||
{
|
||||
unsigned out = 0;
|
||||
for (unsigned i = 0; i < *nbDirtyRects; ++i)
|
||||
{
|
||||
RECT rect = dirtyRects[i];
|
||||
if (ClipDirtyRect(rect, width, height))
|
||||
dirtyRects[out++] = rect;
|
||||
}
|
||||
*nbDirtyRects = out;
|
||||
}
|
||||
|
||||
bool CFrameProcessorUtil::BuildCopyDamage(
|
||||
const CPostProcessor& postProcessor, bool destinationNeedsFullCopy,
|
||||
const RECT previousDirtyRects[], unsigned nbPreviousDirtyRects,
|
||||
const RECT currentDirtyRects[], unsigned nbCurrentDirtyRects,
|
||||
unsigned width, unsigned height,
|
||||
RECT copyDirtyRects[], unsigned * nbCopyDirtyRects)
|
||||
{
|
||||
*nbCopyDirtyRects = 0;
|
||||
bool fullCopy = destinationNeedsFullCopy ||
|
||||
nbCurrentDirtyRects == 0 || nbPreviousDirtyRects == 0;
|
||||
|
||||
if (fullCopy)
|
||||
return true;
|
||||
|
||||
for (const RECT * rect = previousDirtyRects;
|
||||
rect < previousDirtyRects + nbPreviousDirtyRects && !fullCopy;
|
||||
++rect)
|
||||
{
|
||||
RECT clipped = *rect;
|
||||
if (ClipDirtyRect(clipped, width, height) &&
|
||||
!AddCopyDirtyRect(copyDirtyRects, LG_MAX_DIRTY_RECTS * 2,
|
||||
nbCopyDirtyRects, clipped))
|
||||
fullCopy = true;
|
||||
}
|
||||
|
||||
for (const RECT * rect = currentDirtyRects;
|
||||
rect < currentDirtyRects + nbCurrentDirtyRects && !fullCopy;
|
||||
++rect)
|
||||
if (!AddCopyDirtyRect(copyDirtyRects, LG_MAX_DIRTY_RECTS * 2,
|
||||
nbCopyDirtyRects, *rect))
|
||||
fullCopy = true;
|
||||
|
||||
if (!fullCopy)
|
||||
fullCopy = IsFullDamage(copyDirtyRects, *nbCopyDirtyRects,
|
||||
width, height) ||
|
||||
CopyAreaCoversFrame(copyDirtyRects, *nbCopyDirtyRects,
|
||||
width, height);
|
||||
|
||||
if (!fullCopy)
|
||||
fullCopy = postProcessor.ShouldCopyFully(
|
||||
copyDirtyRects, *nbCopyDirtyRects);
|
||||
|
||||
return fullCopy;
|
||||
}
|
||||
|
||||
void CFrameProcessorUtil::AccumulateDamage(
|
||||
RECT pendingDirtyRects[], unsigned * nbPendingDirtyRects,
|
||||
bool * hasPendingDamage, const RECT dirtyRects[],
|
||||
unsigned nbDirtyRects)
|
||||
{
|
||||
if (nbDirtyRects > LG_MAX_DIRTY_RECTS)
|
||||
nbDirtyRects = 0;
|
||||
|
||||
if (!*hasPendingDamage)
|
||||
{
|
||||
*hasPendingDamage = true;
|
||||
*nbPendingDirtyRects = nbDirtyRects;
|
||||
if (nbDirtyRects)
|
||||
memcpy(pendingDirtyRects, dirtyRects,
|
||||
nbDirtyRects * sizeof(*pendingDirtyRects));
|
||||
return;
|
||||
}
|
||||
|
||||
if (*nbPendingDirtyRects == 0 || nbDirtyRects == 0 ||
|
||||
*nbPendingDirtyRects + nbDirtyRects > LG_MAX_DIRTY_RECTS)
|
||||
{
|
||||
*nbPendingDirtyRects = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(pendingDirtyRects + *nbPendingDirtyRects, dirtyRects,
|
||||
nbDirtyRects * sizeof(*pendingDirtyRects));
|
||||
*nbPendingDirtyRects += nbDirtyRects;
|
||||
}
|
||||
Reference in New Issue
Block a user