Files
LookingGlass/idd/LGIdd/CFrameBufferResource.cpp
Geoffrey McRae 3ddc199bec [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.
2026-08-07 13:34:40 +10:00

229 lines
6.8 KiB
C++

/**
* 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 "CFrameBufferResource.h"
#include "CFrameProcessorUtil.h"
#include "CD3D12Device.h"
#include "CIndirectDeviceContext.h"
#include "CDebug.h"
#include <cstring>
bool CFrameBufferResource::Init(CIndirectDeviceContext * device,
CD3D12Device * dx12, unsigned frameIndex, uint8_t * base, size_t size,
const D3D12_RESOURCE_DESC * textureDesc)
{
m_frameIndex = frameIndex;
if (size > device->GetMaxFrameSize())
{
DEBUG_ERROR("Frame size of %llu is too large to fit in shared ram",
(unsigned long long)size);
return false;
}
const bool indirect = dx12->IsIndirectCopy();
const ResourceType type = textureDesc ?
RESOURCE_TEXTURE : RESOURCE_BUFFER;
D3D12_RESOURCE_DESC desc = {};
if (textureDesc)
{
if (indirect || !dx12->CanUseIVSHMEMTexture())
return false;
desc = *textureDesc;
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
!desc.Width ||
!desc.Height ||
desc.DepthOrArraySize != 1 ||
desc.MipLevels != 1 ||
desc.SampleDesc.Count != 1 ||
desc.SampleDesc.Quality ||
desc.Format == DXGI_FORMAT_UNKNOWN ||
desc.Layout != D3D12_TEXTURE_LAYOUT_ROW_MAJOR ||
desc.Flags != D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER)
return false;
}
else
{
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = size;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
if (!indirect)
{
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
}
}
// Nothing to do if the resource already represents this allocation.
if (m_base == base && m_type == type &&
((type == RESOURCE_BUFFER && m_size >= size) ||
(type == RESOURCE_TEXTURE &&
CFrameProcessorUtil::ResourceDescMatches(m_desc, desc))))
{
m_frameSize = size;
return true;
}
Reset();
HRESULT hr;
const WCHAR * resName;
UINT64 allocationSize = size;
if (indirect)
{
DEBUG_TRACE("Creating standard resource for %p", base);
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = 1;
heapProps.VisibleNodeMask = 1;
hr = dx12->GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
NULL,
IID_PPV_ARGS(&m_res)
);
resName = L"STAGING";
if (SUCCEEDED(hr))
{
D3D12_RANGE range = {0, 0};
hr = m_res->Map(0, &range, &m_map);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to map the resource");
return false;
}
}
}
else
{
const UINT64 heapOffset =
(uintptr_t)base -
(uintptr_t)device->GetIVSHMEM().GetMem();
const D3D12_RESOURCE_ALLOCATION_INFO allocation =
dx12->GetDevice()->GetResourceAllocationInfo(0, 1, &desc);
allocationSize = allocation.SizeInBytes;
const D3D12_HEAP_DESC heapDesc = dx12->GetHeap()->GetDesc();
if (!allocation.Alignment ||
heapOffset % allocation.Alignment ||
allocation.SizeInBytes > device->GetMaxFrameSize() ||
heapOffset > heapDesc.SizeInBytes ||
allocation.SizeInBytes > heapDesc.SizeInBytes - heapOffset)
{
DEBUG_ERROR("IVSHMEM resource does not fit its framebuffer allocation");
return false;
}
if (type == RESOURCE_TEXTURE)
{
D3D12_FEATURE_DATA_FORMAT_SUPPORT support = {};
support.Format = desc.Format;
hr = dx12->GetDevice()->CheckFeatureSupport(
D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support));
if (FAILED(hr) ||
!(support.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D))
{
DEBUG_ERROR("IVSHMEM texture format is unsupported");
return false;
}
DEBUG_TRACE("Creating IVSHMEM texture for %p", base);
resName = L"IVSHMEM Texture";
}
else
{
DEBUG_TRACE("Creating IVSHMEM buffer for %p", base);
resName = L"IVSHMEM";
}
hr = dx12->GetDevice()->CreatePlacedResource(
dx12->GetHeap().Get(),
heapOffset,
&desc,
D3D12_RESOURCE_STATE_COMMON,
NULL,
IID_PPV_ARGS(&m_res)
);
}
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the FrameBuffer ID3D12Resource");
return false;
}
m_res->SetName(resName);
m_base = base;
m_size = type == RESOURCE_TEXTURE ?
(size_t)allocationSize : size;
m_frameSize = size;
m_type = type;
m_desc = desc;
return true;
}
void CFrameBufferResource::Reset()
{
if (m_map)
{
m_res->Unmap(0, NULL);
m_map = NULL;
}
m_base = nullptr;
m_size = 0;
m_frameSize = 0;
m_type = RESOURCE_NONE;
m_desc = {};
m_fullCopy = false;
m_nbCopyDirtyRects = 0;
m_copyPitch = 0;
m_copyBytesPerPixel = 0;
m_res.Reset();
}
void CFrameBufferResource::SetCopyDamage(const RECT dirtyRects[],
unsigned nbDirtyRects, bool fullCopy, unsigned pitch,
unsigned bytesPerPixel)
{
m_fullCopy = fullCopy;
m_nbCopyDirtyRects = fullCopy ? 0 : nbDirtyRects;
m_copyPitch = pitch;
m_copyBytesPerPixel = bytesPerPixel;
if (m_nbCopyDirtyRects)
memcpy(m_copyDirtyRects, dirtyRects,
m_nbCopyDirtyRects * sizeof(*m_copyDirtyRects));
}