From e470f9d8ae8d8c0b88569883a69a5ee925d3cdbf Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 7 Aug 2026 21:10:37 +1000 Subject: [PATCH] [idd] capture: preserve software copy row pitch Use an explicit placed buffer for software frame copies so the D3D copy uses the same row pitch published through KVMFR. GetCopyableFootprints describes buffer copies and cannot be used to infer the physical layout of a placed row-major texture. This caused padded resolutions to use the wrong row starts in every consumer. Keep the copy direct to IVSHMEM without a staging or CPU copy. --- idd/LGIdd/capture/CFrameBufferPool.cpp | 6 +- idd/LGIdd/capture/CFrameBufferPool.h | 5 +- idd/LGIdd/capture/CFrameBufferResource.cpp | 89 ++++--------------- idd/LGIdd/capture/CFrameBufferResource.h | 12 +-- idd/LGIdd/capture/CSoftwareFrameProcessor.cpp | 82 ++--------------- idd/LGIdd/capture/CSoftwareFrameProcessor.h | 2 - idd/LGIdd/d3d/CD3D12Device.cpp | 5 -- idd/LGIdd/d3d/CD3D12Device.h | 6 +- 8 files changed, 34 insertions(+), 173 deletions(-) diff --git a/idd/LGIdd/capture/CFrameBufferPool.cpp b/idd/LGIdd/capture/CFrameBufferPool.cpp index b5318656..12d48570 100644 --- a/idd/LGIdd/capture/CFrameBufferPool.cpp +++ b/idd/LGIdd/capture/CFrameBufferPool.cpp @@ -37,16 +37,14 @@ void CFrameBufferPool::Reset() } CFrameBufferResource * CFrameBufferPool::Get( - const PreparedFrameBuffer& buffer, - size_t minSize, const D3D12_RESOURCE_DESC * textureDesc) + const PreparedFrameBuffer& buffer, size_t minSize) { if (buffer.frameIndex > ARRAYSIZE(m_buffers) - 1) return nullptr; CFrameBufferResource * fbr = &m_buffers[buffer.frameIndex]; if (!fbr->Init(m_dx12, buffer.frameIndex, buffer.mem, - buffer.heapOffset, minSize, m_transport->GetMaxFrameSize(), - textureDesc)) + buffer.heapOffset, minSize, m_transport->GetMaxFrameSize())) return nullptr; return fbr; diff --git a/idd/LGIdd/capture/CFrameBufferPool.h b/idd/LGIdd/capture/CFrameBufferPool.h index dc7bc7fb..d481e7eb 100644 --- a/idd/LGIdd/capture/CFrameBufferPool.h +++ b/idd/LGIdd/capture/CFrameBufferPool.h @@ -39,7 +39,6 @@ public: void Init(IFrameTransport * transport, CD3D12Device * dx12); void Reset(); - CFrameBufferResource * Get(const PreparedFrameBuffer& buffer, - size_t minSize, - const D3D12_RESOURCE_DESC * textureDesc = nullptr); + CFrameBufferResource * Get( + const PreparedFrameBuffer& buffer, size_t minSize); }; diff --git a/idd/LGIdd/capture/CFrameBufferResource.cpp b/idd/LGIdd/capture/CFrameBufferResource.cpp index f27585af..db6c6b79 100644 --- a/idd/LGIdd/capture/CFrameBufferResource.cpp +++ b/idd/LGIdd/capture/CFrameBufferResource.cpp @@ -19,7 +19,6 @@ */ #include "capture/CFrameBufferResource.h" -#include "capture/CFrameProcessorUtil.h" #include "d3d/CD3D12Device.h" #include "CDebug.h" @@ -27,7 +26,7 @@ bool CFrameBufferResource::Init(CD3D12Device * dx12, unsigned frameIndex, uint8_t * base, uint64_t heapOffset, size_t size, - size_t maxFrameSize, const D3D12_RESOURCE_DESC * textureDesc) + size_t maxFrameSize) { m_frameIndex = frameIndex; @@ -38,52 +37,27 @@ bool CFrameBufferResource::Init(CD3D12Device * dx12, return false; } - const bool indirect = dx12->IsIndirectCopy(); - const ResourceType type = textureDesc ? - RESOURCE_TEXTURE : RESOURCE_BUFFER; + const bool indirect = dx12->IsIndirectCopy(); D3D12_RESOURCE_DESC desc = {}; - if (textureDesc) + 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) { - if (indirect || !dx12->CanUseDirectTexture()) - 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; - } + 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)))) + if (m_base == base && m_size >= size) { m_frameSize = size; return true; @@ -93,7 +67,6 @@ bool CFrameBufferResource::Init(CD3D12Device * dx12, HRESULT hr; const WCHAR * resName; - UINT64 allocationSize = size; if (indirect) { @@ -130,7 +103,6 @@ bool CFrameBufferResource::Init(CD3D12Device * dx12, { const D3D12_RESOURCE_ALLOCATION_INFO allocation = dx12->GetDevice()->GetResourceAllocationInfo(0, 1, &desc); - allocationSize = allocation.SizeInBytes; const D3D12_HEAP_DESC heapDesc = dx12->GetTransportHeap()->GetDesc(); if (!allocation.Alignment || @@ -144,26 +116,8 @@ bool CFrameBufferResource::Init(CD3D12Device * dx12, 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("Transport texture format is unsupported"); - return false; - } - DEBUG_TRACE("Creating transport texture for %p", base); - resName = L"Transport Texture"; - } - else - { - DEBUG_TRACE("Creating transport buffer for %p", base); - resName = L"Transport Buffer"; - } + DEBUG_TRACE("Creating transport buffer for %p", base); + resName = L"Transport Buffer"; hr = dx12->GetDevice()->CreatePlacedResource( dx12->GetTransportHeap().Get(), @@ -184,11 +138,8 @@ bool CFrameBufferResource::Init(CD3D12Device * dx12, m_res->SetName(resName); m_base = base; - m_size = type == RESOURCE_TEXTURE ? - (size_t)allocationSize : size; + m_size = size; m_frameSize = size; - m_type = type; - m_desc = desc; return true; } @@ -203,8 +154,6 @@ void CFrameBufferResource::Reset() m_base = nullptr; m_size = 0; m_frameSize = 0; - m_type = RESOURCE_NONE; - m_desc = {}; m_fullCopy = false; m_nbCopyDirtyRects = 0; m_copyPitch = 0; diff --git a/idd/LGIdd/capture/CFrameBufferResource.h b/idd/LGIdd/capture/CFrameBufferResource.h index 8863cad1..5bb2880b 100644 --- a/idd/LGIdd/capture/CFrameBufferResource.h +++ b/idd/LGIdd/capture/CFrameBufferResource.h @@ -37,13 +37,6 @@ using namespace Microsoft::WRL; class CFrameBufferResource { private: - enum ResourceType - { - RESOURCE_NONE, - RESOURCE_BUFFER, - RESOURCE_TEXTURE, - }; - unsigned m_frameIndex = 0; uint8_t * m_base = nullptr; size_t m_size = 0; @@ -62,16 +55,13 @@ class CFrameBufferResource unsigned m_copyPitch = 0; unsigned m_copyBytesPerPixel = 0; unsigned m_candidateIndex = 0; - ResourceType m_type = RESOURCE_NONE; - D3D12_RESOURCE_DESC m_desc = {}; std::atomic m_completionHandled = false; ComPtr m_res; void * m_map = nullptr; public: bool Init(CD3D12Device * dx12, unsigned frameIndex, uint8_t * base, - uint64_t heapOffset, size_t size, size_t maxFrameSize, - const D3D12_RESOURCE_DESC * textureDesc = nullptr); + uint64_t heapOffset, size_t size, size_t maxFrameSize); void Reset(); unsigned GetFrameIndex() { return m_frameIndex; } diff --git a/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp b/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp index 1153a644..4699d610 100644 --- a/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp +++ b/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp @@ -32,9 +32,7 @@ CSoftwareFrameProcessor::CSoftwareFrameProcessor( CPostProcessor postProcessors[CAPTURE_PIPELINE_SLOTS], SRWLOCK * pipelineLock, HANDLE terminateEvent) : CFrameProcessor(transport, std::move(dx12), postProcessors, - pipelineLock, terminateEvent), - m_directTexture( - !m_dx12->IsIndirectCopy() && m_dx12->CanUseDirectTexture()) + pipelineLock, terminateEvent) { } @@ -118,48 +116,11 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission) CPostProcessor& postProcessor = m_postProcessors[0]; const D12FrameFormat& dstFormat = postProcessor.GetOutputFormat(); - D3D12_RESOURCE_DESC textureDesc = {}; - const D3D12_RESOURCE_DESC * textureDescPtr = nullptr; - unsigned pitch = postProcessor.GetOutputPitch(); - size_t frameSize = postProcessor.GetOutputSize(); - if (m_directTexture && - dstFormat.desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && - dstFormat.desc.Width && dstFormat.desc.Height && - dstFormat.desc.Format != DXGI_FORMAT_UNKNOWN) - { - textureDesc = dstFormat.desc; - textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - textureDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; - textureDesc.DepthOrArraySize = 1; - textureDesc.MipLevels = 1; - textureDesc.SampleDesc.Count = 1; - textureDesc.SampleDesc.Quality = 0; - textureDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - textureDesc.Flags = - D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER; - - D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {}; - m_dx12->GetDevice()->GetCopyableFootprints( - &textureDesc, 0, 1, 0, &layout, nullptr, nullptr, nullptr); - const unsigned texturePitch = layout.Footprint.RowPitch; - if (texturePitch && textureDesc.Height <= - m_transport->GetMaxFrameSize() / texturePitch) - { - pitch = texturePitch; - frameSize = (size_t)pitch * textureDesc.Height; - textureDescPtr = &textureDesc; - } - else - { - m_directTexture = false; - DEBUG_WARN("Transport texture layout does not fit the framebuffer"); - } - } - else if (m_directTexture) - { - m_directTexture = false; - DEBUG_WARN("Post-processor output cannot use a transport texture"); - } + // A copyable footprint describes a buffer layout, not the physical layout + // of a row-major texture. Use that explicit buffer layout so the pitch sent + // through KVMFR always matches the bytes written into transport memory. + const unsigned pitch = postProcessor.GetOutputPitch(); + const size_t frameSize = postProcessor.GetOutputSize(); if (!pitch || !frameSize || frameSize > m_transport->GetMaxFrameSize()) { @@ -243,35 +204,8 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission) continue; } - CFrameBufferResource * fbRes = nullptr; - if (textureDescPtr) - { - fbRes = m_frameBuffers.Get(buffer, frameSize, textureDescPtr); - if (!fbRes) - { - const HRESULT deviceStatus = - m_dx12->GetDevice()->GetDeviceRemovedReason(); - if (FAILED(deviceStatus)) - { - copySlot->Cancel(); - m_transport->AbortFrameBuffer(buffer.frameIndex); - RestorePendingDamage( - currentDirtyRects, nbDirtyRects, hasDamage); - DEBUG_ERROR_HR(deviceStatus, - "D3D12 device removed while creating a transport texture"); - SetFullDamage(); - return false; - } - - m_directTexture = false; - textureDescPtr = nullptr; - DEBUG_WARN( - "Transport textures unavailable; using a direct buffer copy"); - } - } - - if (!fbRes) - fbRes = m_frameBuffers.Get(buffer, frameSize); + CFrameBufferResource * fbRes = + m_frameBuffers.Get(buffer, frameSize); if (!fbRes) { copySlot->Cancel(); diff --git a/idd/LGIdd/capture/CSoftwareFrameProcessor.h b/idd/LGIdd/capture/CSoftwareFrameProcessor.h index 85aa48fb..4c8dbbbc 100644 --- a/idd/LGIdd/capture/CSoftwareFrameProcessor.h +++ b/idd/LGIdd/capture/CSoftwareFrameProcessor.h @@ -25,8 +25,6 @@ class CSoftwareFrameProcessor final : public CFrameProcessor { private: - bool m_directTexture; - static void CompletionFunction( CD3D12CommandSlot * slot, bool result, void * param1, void * param2); diff --git a/idd/LGIdd/d3d/CD3D12Device.cpp b/idd/LGIdd/d3d/CD3D12Device.cpp index fcc4704d..02f9e828 100644 --- a/idd/LGIdd/d3d/CD3D12Device.cpp +++ b/idd/LGIdd/d3d/CD3D12Device.cpp @@ -120,9 +120,6 @@ CD3D12Device::InitResult CD3D12Device::Init( D3D12_HEAP_DESC heapDesc = m_transportHeap->GetDesc(); alignSize = heapDesc.Alignment; - m_directTextureSupported = - (heapDesc.Flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER) && - !(heapDesc.Flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES); // test that the heap is usable if (!HeapTest()) @@ -136,8 +133,6 @@ CD3D12Device::InitResult CD3D12Device::Init( } DEBUG_INFO("Using transport memory as a D3D12 heap"); - if (!m_directTextureSupported) - DEBUG_WARN("Transport memory does not support placed textures"); } if (!m_copyQueue.Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COPY, diff --git a/idd/LGIdd/d3d/CD3D12Device.h b/idd/LGIdd/d3d/CD3D12Device.h index 816ec876..e23a936d 100644 --- a/idd/LGIdd/d3d/CD3D12Device.h +++ b/idd/LGIdd/d3d/CD3D12Device.h @@ -52,9 +52,8 @@ struct CD3D12Device CD3D12CommandQueue m_copyQueue; CD3D12CommandQueue m_computeQueue; - bool m_computeEnabled = false; - bool m_indirectCopy = true; - bool m_directTextureSupported = false; + bool m_computeEnabled = false; + bool m_indirectCopy = true; bool HeapTest(); @@ -81,7 +80,6 @@ struct CD3D12Device ComPtr GetDevice() { return m_device; } ComPtr GetTransportHeap() { return m_transportHeap; } bool IsIndirectCopy() const { return m_indirectCopy; } - bool CanUseDirectTexture() const { return m_directTextureSupported; } CD3D12CommandSlot * GetCopySlot (); CD3D12CommandSlot * GetCopySlot (unsigned frameIndex);