mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] capture: copy software frames directly to IVSHMEM
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
build / client-tests (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client-tests (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client-tests (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client-tests (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client-tests (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client-tests (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client-tests (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client-tests (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
build / client-tests (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client-tests (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client-tests (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client-tests (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client-tests (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client-tests (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client-tests (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client-tests (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
Bypass cadence retention when the software render adapter is active. Publish each available source frame immediately with one damage-aware D3D copy into its final IVSHMEM resource. Keep only one software copy in flight so newer frames are dropped instead of queued behind stale work. Retain accumulated damage for the next frame and skip static re-encodes when no image update is pending. Use a row-major IVSHMEM texture when the shared heap supports it and fall back to a direct IVSHMEM buffer copy otherwise. In indirect mode, copy only damaged rows from readback memory into IVSHMEM.
This commit is contained in:
@@ -538,7 +538,22 @@ CD3D12CommandSlot * CD3D12CommandQueue::Acquire(UINT slotIndex)
|
||||
|
||||
CD3D12CommandSlot * CD3D12CommandQueue::Acquire()
|
||||
{
|
||||
return Acquire(0);
|
||||
if (!m_slotCount)
|
||||
return nullptr;
|
||||
|
||||
// The unindexed path is used for immediate software publication. Keep at
|
||||
// most one copy in flight so a newer frame is dropped instead of queued
|
||||
// behind bandwidth-bound work which is already stale.
|
||||
CD3D12CommandSlot& slot = m_slots[0];
|
||||
if (slot.Acquire())
|
||||
return &slot;
|
||||
|
||||
// Complete already-fenced work without waiting for callback dispatch.
|
||||
slot.OnCompletion(false);
|
||||
if (slot.Acquire())
|
||||
return &slot;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CD3D12CommandQueue::WaitForIdle()
|
||||
|
||||
@@ -116,6 +116,9 @@ CD3D12Device::InitResult CD3D12Device::Init(CIVSHMEM &ivshmem,
|
||||
|
||||
D3D12_HEAP_DESC heapDesc = m_ivshmemHeap->GetDesc();
|
||||
alignSize = heapDesc.Alignment;
|
||||
m_ivshmemTextureSupported =
|
||||
(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())
|
||||
@@ -128,6 +131,8 @@ CD3D12Device::InitResult CD3D12Device::Init(CIVSHMEM &ivshmem,
|
||||
}
|
||||
|
||||
DEBUG_INFO("Using IVSHMEM as a D3D12Heap");
|
||||
if (!m_ivshmemTextureSupported)
|
||||
DEBUG_WARN("IVSHMEM heap does not support placed textures");
|
||||
}
|
||||
|
||||
if (!m_copyQueue.Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COPY,
|
||||
@@ -207,6 +212,11 @@ CD3D12CommandSlot * CD3D12Device::GetCopySlot(unsigned frameIndex)
|
||||
return m_copyQueue.Acquire(frameIndex);
|
||||
}
|
||||
|
||||
CD3D12CommandSlot * CD3D12Device::GetCopySlot()
|
||||
{
|
||||
return m_copyQueue.Acquire();
|
||||
}
|
||||
|
||||
CD3D12CommandSlot * CD3D12Device::GetComputeSlot(unsigned frameIndex)
|
||||
{
|
||||
if (!m_computeEnabled)
|
||||
|
||||
@@ -51,7 +51,8 @@ struct CD3D12Device
|
||||
|
||||
CD3D12CommandQueue m_copyQueue;
|
||||
CD3D12CommandQueue m_computeQueue;
|
||||
bool m_computeEnabled = false;
|
||||
bool m_computeEnabled = false;
|
||||
bool m_ivshmemTextureSupported = false;
|
||||
|
||||
bool HeapTest();
|
||||
|
||||
@@ -78,7 +79,9 @@ struct CD3D12Device
|
||||
ComPtr<ID3D12Device3> GetDevice() { return m_device; }
|
||||
ComPtr<ID3D12Heap > GetHeap() { return m_ivshmemHeap; }
|
||||
bool IsIndirectCopy() { return m_indirectCopy; }
|
||||
bool CanUseIVSHMEMTexture() { return m_ivshmemTextureSupported; }
|
||||
|
||||
CD3D12CommandSlot * GetCopySlot ();
|
||||
CD3D12CommandSlot * GetCopySlot (unsigned frameIndex);
|
||||
CD3D12CommandSlot * GetComputeSlot(unsigned frameIndex);
|
||||
};
|
||||
|
||||
@@ -36,13 +36,14 @@ void CFrameBufferPool::Reset()
|
||||
|
||||
CFrameBufferResource * CFrameBufferPool::Get(
|
||||
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
|
||||
size_t minSize)
|
||||
size_t minSize, const D3D12_RESOURCE_DESC * textureDesc)
|
||||
{
|
||||
if (buffer.frameIndex > ARRAYSIZE(m_buffers) - 1)
|
||||
return nullptr;
|
||||
|
||||
CFrameBufferResource * fbr = &m_buffers[buffer.frameIndex];
|
||||
if (!fbr->Init(m_swapChain, buffer.frameIndex, buffer.mem, minSize))
|
||||
if (!fbr->Init(m_swapChain, buffer.frameIndex, buffer.mem,
|
||||
minSize, textureDesc))
|
||||
return nullptr;
|
||||
|
||||
return fbr;
|
||||
|
||||
@@ -38,5 +38,6 @@ class CFrameBufferPool
|
||||
|
||||
CFrameBufferResource* CFrameBufferPool::Get(
|
||||
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
|
||||
size_t minSize);
|
||||
size_t minSize,
|
||||
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
|
||||
};
|
||||
|
||||
@@ -22,7 +22,28 @@
|
||||
#include "CSwapChainProcessor.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size)
|
||||
#include <cstring>
|
||||
|
||||
static bool ResourceDescMatches(
|
||||
const D3D12_RESOURCE_DESC& left, const D3D12_RESOURCE_DESC& right)
|
||||
{
|
||||
return
|
||||
left.Dimension == right.Dimension &&
|
||||
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;
|
||||
}
|
||||
|
||||
bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain,
|
||||
unsigned frameIndex, uint8_t * base, size_t size,
|
||||
const D3D12_RESOURCE_DESC * textureDesc)
|
||||
{
|
||||
m_frameIndex = frameIndex;
|
||||
|
||||
@@ -33,8 +54,52 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameI
|
||||
return false;
|
||||
}
|
||||
|
||||
// nothing to do if the resource already exists and is large enough
|
||||
if (m_base == base && m_size >= size)
|
||||
const auto dx12 = swapChain->GetD3D12Device();
|
||||
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 && ResourceDescMatches(m_desc, desc))))
|
||||
{
|
||||
m_frameSize = size;
|
||||
return true;
|
||||
@@ -42,22 +107,11 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameI
|
||||
|
||||
Reset();
|
||||
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
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;
|
||||
|
||||
HRESULT hr;
|
||||
HRESULT hr;
|
||||
const WCHAR * resName;
|
||||
UINT64 allocationSize = size;
|
||||
|
||||
if (swapChain->GetD3D12Device()->IsIndirectCopy())
|
||||
if (indirect)
|
||||
{
|
||||
DEBUG_TRACE("Creating standard resource for %p", base);
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
@@ -67,7 +121,7 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameI
|
||||
heapProps.CreationNodeMask = 1;
|
||||
heapProps.VisibleNodeMask = 1;
|
||||
|
||||
hr = swapChain->GetD3D12Device()->GetDevice()->CreateCommittedResource(
|
||||
hr = dx12->GetDevice()->CreateCommittedResource(
|
||||
&heapProps,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&desc,
|
||||
@@ -90,19 +144,52 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameI
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_TRACE("Creating ivshmem resource for %p", base);
|
||||
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
|
||||
const UINT64 heapOffset =
|
||||
(uintptr_t)base -
|
||||
(uintptr_t)swapChain->GetDevice()->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 > swapChain->GetDevice()->GetMaxFrameSize() ||
|
||||
heapOffset > heapDesc.SizeInBytes ||
|
||||
allocation.SizeInBytes > heapDesc.SizeInBytes - heapOffset)
|
||||
{
|
||||
DEBUG_ERROR("IVSHMEM resource does not fit its framebuffer allocation");
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = swapChain->GetD3D12Device()->GetDevice()->CreatePlacedResource(
|
||||
swapChain->GetD3D12Device()->GetHeap().Get(),
|
||||
(uintptr_t)base - (uintptr_t)swapChain->GetDevice()->GetIVSHMEM().GetMem(),
|
||||
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)
|
||||
);
|
||||
resName = L"IVSHMEM";
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
@@ -114,8 +201,11 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameI
|
||||
m_res->SetName(resName);
|
||||
|
||||
m_base = base;
|
||||
m_size = size;
|
||||
m_size = type == RESOURCE_TEXTURE ?
|
||||
(size_t)allocationSize : size;
|
||||
m_frameSize = size;
|
||||
m_type = type;
|
||||
m_desc = desc;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -127,8 +217,27 @@ void CFrameBufferResource::Reset()
|
||||
m_map = NULL;
|
||||
}
|
||||
|
||||
m_base = nullptr;
|
||||
m_size = 0;
|
||||
m_frameSize = 0;
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -24,9 +24,11 @@
|
||||
#include <wdf.h>
|
||||
#include <wrl.h>
|
||||
#include <d3d12.h>
|
||||
#include <atomic>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "CFrameScheduler.h"
|
||||
#include "CInteropResource.h"
|
||||
|
||||
class CSwapChainProcessor;
|
||||
|
||||
@@ -35,6 +37,13 @@ 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;
|
||||
@@ -48,12 +57,21 @@ class CFrameBufferResource
|
||||
unsigned m_timingEffectIndex = 0;
|
||||
uint64_t m_timingToken = 0;
|
||||
bool m_fullCopy = false;
|
||||
RECT m_copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {};
|
||||
unsigned m_nbCopyDirtyRects = 0;
|
||||
unsigned m_copyPitch = 0;
|
||||
unsigned m_copyBytesPerPixel = 0;
|
||||
unsigned m_candidateIndex = 0;
|
||||
ResourceType m_type = RESOURCE_NONE;
|
||||
D3D12_RESOURCE_DESC m_desc = {};
|
||||
std::atomic<bool> m_completionHandled = false;
|
||||
ComPtr<ID3D12Resource> m_res;
|
||||
void * m_map = nullptr;
|
||||
|
||||
public:
|
||||
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size);
|
||||
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex,
|
||||
uint8_t * base, size_t size,
|
||||
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
|
||||
void Reset();
|
||||
|
||||
unsigned GetFrameIndex() { return m_frameIndex; }
|
||||
@@ -89,6 +107,27 @@ class CFrameBufferResource
|
||||
unsigned GetTimingEffectIndex() const { return m_timingEffectIndex; }
|
||||
uint64_t GetTimingToken () const { return m_timingToken; }
|
||||
bool IsFullCopy () const { return m_fullCopy; }
|
||||
void SetCopyDamage(const RECT dirtyRects[], unsigned nbDirtyRects,
|
||||
bool fullCopy, unsigned pitch, unsigned bytesPerPixel);
|
||||
const RECT * GetCopyDirtyRects() const { return m_copyDirtyRects; }
|
||||
unsigned GetCopyDirtyRectCount() const { return m_nbCopyDirtyRects; }
|
||||
unsigned GetCopyPitch () const { return m_copyPitch; }
|
||||
unsigned GetCopyBytesPerPixel () const
|
||||
{
|
||||
return m_copyBytesPerPixel;
|
||||
}
|
||||
void ResetCompletion()
|
||||
{
|
||||
m_completionHandled.store(false, std::memory_order_release);
|
||||
}
|
||||
void MarkCompletion()
|
||||
{
|
||||
m_completionHandled.store(true, std::memory_order_release);
|
||||
}
|
||||
bool CompletionHandled() const
|
||||
{
|
||||
return m_completionHandled.load(std::memory_order_acquire);
|
||||
}
|
||||
void SetCandidateIndex(unsigned index) { m_candidateIndex = index; }
|
||||
unsigned GetCandidateIndex() const { return m_candidateIndex; }
|
||||
|
||||
|
||||
@@ -1804,7 +1804,8 @@ int CIndirectDeviceContext::FindNewestCompletedFrame(
|
||||
}
|
||||
|
||||
bool CIndirectDeviceContext::FrameBufferAvailable(
|
||||
const CFrameScheduler::Schedule& schedule)
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement)
|
||||
{
|
||||
if (!m_lgmp || !m_frameQueue)
|
||||
return false;
|
||||
@@ -1823,7 +1824,8 @@ bool CIndirectDeviceContext::FrameBufferAvailable(
|
||||
const bool ownerBlocked =
|
||||
CountOwnerDeliveries(schedule.clientID) >= LGMP_Q_FRAME_LEN;
|
||||
const bool ownerQueuesBlocked = FindAvailableOwnerQueue(0) < 0;
|
||||
allowReady = ownerBlocked || ownerQueuesBlocked;
|
||||
allowReady = allowReadyReplacement &&
|
||||
(ownerBlocked || ownerQueuesBlocked);
|
||||
}
|
||||
else if (lgmpHostQueuePending(m_frameQueue) != 0)
|
||||
{
|
||||
@@ -1911,7 +1913,8 @@ bool CIndirectDeviceContext::ReplaySharedFrame(uint64_t now, bool& retry)
|
||||
CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrameBuffer(
|
||||
unsigned pitch, const D12FrameFormat& srcFormat,
|
||||
const D12FrameFormat& dstFormat, const RECT * dirtyRects,
|
||||
unsigned nbDirtyRects, const CFrameScheduler::Schedule& schedule)
|
||||
unsigned nbDirtyRects, const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement)
|
||||
{
|
||||
PreparedFrameBuffer result = {};
|
||||
|
||||
@@ -1929,8 +1932,9 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
AcquireSRWLockExclusive(&m_framePublishLock);
|
||||
const bool ownerBlocked = schedule.clientID &&
|
||||
CountOwnerDeliveries(schedule.clientID) >= LGMP_Q_FRAME_LEN;
|
||||
const bool allowReady = ownerBlocked ||
|
||||
(schedule.clientID && FindAvailableOwnerQueue(0) < 0);
|
||||
const bool allowReady = allowReadyReplacement &&
|
||||
(ownerBlocked ||
|
||||
(schedule.clientID && FindAvailableOwnerQueue(0) < 0));
|
||||
const int availableFrameIndex =
|
||||
FindAvailableFrameBuffer(allowReady);
|
||||
bool expected = false;
|
||||
@@ -2450,6 +2454,21 @@ void CIndirectDeviceContext::WriteFrameBuffer(unsigned frameIndex, void* src, si
|
||||
fb->wp = (uint32_t)(offset + len);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::WriteFrameBufferRows(unsigned frameIndex,
|
||||
void * src, size_t offset, size_t rowBytes, size_t pitch,
|
||||
unsigned rows) const
|
||||
{
|
||||
FrameBuffer * fb = m_frameBuffer[frameIndex];
|
||||
uint8_t * dst = fb->data + offset;
|
||||
uint8_t * source = static_cast<uint8_t *>(src) + offset;
|
||||
for (unsigned row = 0; row < rows; ++row)
|
||||
{
|
||||
memcpy(dst, source, rowBytes);
|
||||
dst += pitch;
|
||||
source += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::FinalizeFrameBuffer(unsigned frameIndex) const
|
||||
{
|
||||
const KVMFRFrame * frame = m_frame[frameIndex];
|
||||
|
||||
@@ -280,7 +280,8 @@ public:
|
||||
bool fullCopy;
|
||||
};
|
||||
|
||||
bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule);
|
||||
bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement = true);
|
||||
bool HasPublishedFrame() const
|
||||
{
|
||||
return m_readyFrameIndex.load(std::memory_order_acquire) >= 0;
|
||||
@@ -291,7 +292,8 @@ public:
|
||||
PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch,
|
||||
const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat,
|
||||
const RECT * dirtyRects, unsigned nbDirtyRects,
|
||||
const CFrameScheduler::Schedule& schedule);
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement = true);
|
||||
bool PublishFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner);
|
||||
bool RepublishFrameBuffer(const CFrameScheduler::Schedule& schedule);
|
||||
@@ -308,6 +310,8 @@ public:
|
||||
uint64_t holdTime, const CFrameScheduler::Schedule& schedule,
|
||||
uint64_t completedAt);
|
||||
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const;
|
||||
void WriteFrameBufferRows(unsigned frameIndex, void * src,
|
||||
size_t offset, size_t rowBytes, size_t pitch, unsigned rows) const;
|
||||
void FinalizeFrameBuffer(unsigned frameIndex) const;
|
||||
|
||||
void ObserveFrame(uint64_t now);
|
||||
|
||||
@@ -351,14 +351,15 @@ bool CPostProcessor::ShouldCopyFully(
|
||||
m_copyEffect->ShouldCopyFully(dirtyRects, nbDirtyRects);
|
||||
}
|
||||
|
||||
void CPostProcessor::CopyToCandidate(
|
||||
void CPostProcessor::CopyToFrameBuffer(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
ID3D12Resource * dst, ID3D12Resource * src) const
|
||||
ID3D12Resource * dst, ID3D12Resource * src,
|
||||
const RECT dirtyRects[], unsigned nbDirtyRects, bool fullCopy) const
|
||||
{
|
||||
if (m_copyEffect)
|
||||
{
|
||||
m_copyEffect->CopyFrame(
|
||||
commandList, dst, src, nullptr, 0, true);
|
||||
commandList, dst, src, dirtyRects, nbDirtyRects, fullCopy);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -368,12 +369,47 @@ void CPostProcessor::CopyToCandidate(
|
||||
srcLoc.SubresourceIndex = 0;
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
|
||||
dstLoc.pResource = dst;
|
||||
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
dstLoc.PlacedFootprint = m_copyLayout;
|
||||
dstLoc.pResource = dst;
|
||||
if (dst->GetDesc().Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D)
|
||||
{
|
||||
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dstLoc.SubresourceIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
dstLoc.PlacedFootprint = m_copyLayout;
|
||||
}
|
||||
|
||||
commandList->CopyTextureRegion(
|
||||
&dstLoc, 0, 0, 0, &srcLoc, nullptr);
|
||||
if (fullCopy)
|
||||
{
|
||||
commandList->CopyTextureRegion(
|
||||
&dstLoc, 0, 0, 0, &srcLoc, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const RECT * rect = dirtyRects;
|
||||
rect < dirtyRects + nbDirtyRects; ++rect)
|
||||
{
|
||||
D3D12_BOX box = {};
|
||||
box.left = rect->left;
|
||||
box.top = rect->top;
|
||||
box.front = 0;
|
||||
box.right = rect->right;
|
||||
box.bottom = rect->bottom;
|
||||
box.back = 1;
|
||||
|
||||
commandList->CopyTextureRegion(
|
||||
&dstLoc, box.left, box.top, 0, &srcLoc, &box);
|
||||
}
|
||||
}
|
||||
|
||||
void CPostProcessor::CopyToCandidate(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
ID3D12Resource * dst, ID3D12Resource * src) const
|
||||
{
|
||||
CopyToFrameBuffer(
|
||||
commandList, dst, src, nullptr, 0, true);
|
||||
}
|
||||
|
||||
void CPostProcessor::CopyFromCandidate(
|
||||
|
||||
@@ -183,6 +183,10 @@ public:
|
||||
size_t GetOutputSize () const { return m_frameSize; }
|
||||
bool ShouldCopyFully(
|
||||
const RECT dirtyRects[], unsigned nbDirtyRects) const;
|
||||
void CopyToFrameBuffer(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
ID3D12Resource * dst, ID3D12Resource * src,
|
||||
const RECT dirtyRects[], unsigned nbDirtyRects, bool fullCopy) const;
|
||||
void CopyToCandidate(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
ID3D12Resource * dst, ID3D12Resource * src) const;
|
||||
|
||||
@@ -212,6 +212,10 @@ bool CSwapChainProcessor::InitializePipeline()
|
||||
}
|
||||
|
||||
m_dx12Device = std::move(dx12Device);
|
||||
m_directSoftwareTexture =
|
||||
m_dx11Device->IsSoftware() &&
|
||||
!m_dx12Device->IsIndirectCopy() &&
|
||||
m_dx12Device->CanUseIVSHMEMTexture();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -355,6 +359,10 @@ void CSwapChainProcessor::PublisherThread()
|
||||
scheduleEvent,
|
||||
m_publishTimer.Get(),
|
||||
};
|
||||
// Software capture publishes source frames immediately. Keep this thread
|
||||
// available for transport delivery and reconnects, but do not wake it for
|
||||
// deadlines the software path cannot reliably meet.
|
||||
const bool cadenceEnabled = !m_dx11Device->IsSoftware();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@@ -375,7 +383,7 @@ void CSwapChainProcessor::PublisherThread()
|
||||
|
||||
uint64_t current = CFrameScheduler::Nanotime();
|
||||
uint64_t cadenceTarget = 0;
|
||||
if (schedule.deliveryDeadlineSerial && periodic)
|
||||
if (cadenceEnabled && schedule.deliveryDeadlineSerial && periodic)
|
||||
{
|
||||
if (schedule.deadline <= current)
|
||||
{
|
||||
@@ -791,6 +799,82 @@ void CSwapChainProcessor::CandidateCompletionFunction(
|
||||
sc->SignalCandidateState();
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::SoftwareCompletionFunction(
|
||||
CD3D12CommandSlot * slot, bool result, void * param1, void * param2)
|
||||
{
|
||||
auto sc = static_cast<CSwapChainProcessor *>(param1);
|
||||
auto fbRes = static_cast<CFrameBufferResource *>(param2);
|
||||
fbRes->MarkCompletion();
|
||||
|
||||
if (!result)
|
||||
{
|
||||
sc->m_devContext->FailFrameBuffer(fbRes->GetFrameIndex());
|
||||
sc->SetFullPendingDamage();
|
||||
sc->m_devContext->ForceFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t indirectCopyTime = 0;
|
||||
if (sc->m_dx12Device->IsIndirectCopy())
|
||||
{
|
||||
const uint64_t indirectCopyStart = CFrameScheduler::Nanotime();
|
||||
if (fbRes->IsFullCopy())
|
||||
sc->m_devContext->WriteFrameBuffer(fbRes->GetFrameIndex(),
|
||||
fbRes->GetMap(), 0, fbRes->GetFrameSize(), false);
|
||||
else
|
||||
{
|
||||
const unsigned pitch = fbRes->GetCopyPitch();
|
||||
const unsigned bytesPerPixel = fbRes->GetCopyBytesPerPixel();
|
||||
const RECT * dirtyRects = fbRes->GetCopyDirtyRects();
|
||||
const unsigned count = fbRes->GetCopyDirtyRectCount();
|
||||
for (const RECT * rect = dirtyRects; rect < dirtyRects + count; ++rect)
|
||||
{
|
||||
const size_t rowOffset =
|
||||
(size_t)rect->top * pitch +
|
||||
(size_t)rect->left * bytesPerPixel;
|
||||
const size_t rowBytes =
|
||||
(size_t)(rect->right - rect->left) * bytesPerPixel;
|
||||
sc->m_devContext->WriteFrameBufferRows(fbRes->GetFrameIndex(),
|
||||
fbRes->GetMap(), rowOffset, rowBytes, pitch,
|
||||
(unsigned)(rect->bottom - rect->top));
|
||||
}
|
||||
}
|
||||
indirectCopyTime = CFrameScheduler::Nanotime() - indirectCopyStart;
|
||||
}
|
||||
|
||||
uint64_t gpuStart = 0;
|
||||
uint64_t gpuEnd = 0;
|
||||
const uint64_t copyReady = CFrameScheduler::Nanotime();
|
||||
const bool gpuTimingValid =
|
||||
slot->GetGPUTimes(gpuStart, gpuEnd);
|
||||
|
||||
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||
const uint64_t publishedAt = CFrameScheduler::Nanotime();
|
||||
const uint64_t postProcessStart = fbRes->GetPostProcessStart();
|
||||
const uint64_t copyStart = fbRes->GetCopyStart();
|
||||
uint64_t postProcessTime = copyStart >= postProcessStart ?
|
||||
copyStart - postProcessStart : 0;
|
||||
uint64_t copyTime = copyReady >= copyStart ?
|
||||
copyReady - copyStart : 0;
|
||||
if (gpuTimingValid && gpuStart >= postProcessStart &&
|
||||
gpuEnd >= gpuStart && gpuEnd <= copyReady)
|
||||
{
|
||||
postProcessTime = gpuStart - postProcessStart;
|
||||
copyTime = gpuEnd - gpuStart + indirectCopyTime;
|
||||
}
|
||||
|
||||
const uint64_t elapsed = publishedAt >= postProcessStart ?
|
||||
publishedAt - postProcessStart : 0;
|
||||
const uint64_t measured = postProcessTime + copyTime;
|
||||
const uint64_t readyTime = elapsed > measured ?
|
||||
elapsed - measured : 0;
|
||||
|
||||
sc->m_devContext->SetFrameTiming(fbRes->GetFrameIndex(),
|
||||
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime, 0,
|
||||
fbRes->GetSchedule(), publishedAt);
|
||||
sc->m_devContext->CompleteFrameBuffer(fbRes->GetFrameIndex(), true);
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::CompletionFunction(
|
||||
CD3D12CommandSlot * slot, bool result, void * param1, void * param2)
|
||||
{
|
||||
@@ -1028,6 +1112,51 @@ static void ClipDirtyRects(RECT dirtyRects[], unsigned * nbDirtyRects,
|
||||
*nbDirtyRects = out;
|
||||
}
|
||||
|
||||
static bool 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;
|
||||
}
|
||||
|
||||
static FrameType GetFrameType(DXGI_FORMAT format)
|
||||
{
|
||||
switch (format)
|
||||
@@ -1100,6 +1229,53 @@ void CSwapChainProcessor::AccumulateFrameDamage(
|
||||
ReleaseSRWLockExclusive(&m_damageLock);
|
||||
}
|
||||
|
||||
bool CSwapChainProcessor::HasPendingDamage()
|
||||
{
|
||||
AcquireSRWLockShared(&m_damageLock);
|
||||
const bool result = m_hasPendingDamage;
|
||||
ReleaseSRWLockShared(&m_damageLock);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CSwapChainProcessor::TakePendingDamage(
|
||||
RECT dirtyRects[], unsigned * nbDirtyRects)
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_damageLock);
|
||||
const bool hasDamage = m_hasPendingDamage;
|
||||
*nbDirtyRects = hasDamage ? m_nbPendingDirtyRects : 0;
|
||||
if (*nbDirtyRects)
|
||||
memcpy(dirtyRects, m_pendingDirtyRects,
|
||||
*nbDirtyRects * sizeof(*dirtyRects));
|
||||
m_hasPendingDamage = false;
|
||||
m_nbPendingDirtyRects = 0;
|
||||
ReleaseSRWLockExclusive(&m_damageLock);
|
||||
return hasDamage;
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::RestorePendingDamage(
|
||||
const RECT dirtyRects[], unsigned nbDirtyRects, bool hasDamage)
|
||||
{
|
||||
if (!hasDamage)
|
||||
return;
|
||||
|
||||
AcquireSRWLockExclusive(&m_damageLock);
|
||||
AccumulatePendingDamage(
|
||||
m_pendingDirtyRects, &m_nbPendingDirtyRects, &m_hasPendingDamage,
|
||||
dirtyRects, nbDirtyRects);
|
||||
ReleaseSRWLockExclusive(&m_damageLock);
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::CommitFrameDamage(
|
||||
const RECT dirtyRects[], unsigned nbDirtyRects)
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_damageLock);
|
||||
m_nbDirtyRects = nbDirtyRects;
|
||||
if (nbDirtyRects)
|
||||
memcpy(m_dirtyRects, dirtyRects,
|
||||
nbDirtyRects * sizeof(*m_dirtyRects));
|
||||
ReleaseSRWLockExclusive(&m_damageLock);
|
||||
}
|
||||
|
||||
int CSwapChainProcessor::AcquireCandidate(
|
||||
bool exclusiveSample, bool allowSupersede)
|
||||
{
|
||||
@@ -1396,42 +1572,11 @@ bool CSwapChainProcessor::PublishNewestCandidate(
|
||||
|
||||
RECT copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {};
|
||||
unsigned nbCopyDirtyRects = 0;
|
||||
bool fullCopy = buffer.fullCopy ||
|
||||
candidate.nbDirtyRects == 0 || nbPreviousDirtyRects == 0;
|
||||
|
||||
if (!fullCopy)
|
||||
{
|
||||
for (const RECT * rect = previousDirtyRects;
|
||||
rect < previousDirtyRects + nbPreviousDirtyRects && !fullCopy;
|
||||
++rect)
|
||||
{
|
||||
RECT clipped = *rect;
|
||||
if (ClipDirtyRect(clipped,
|
||||
candidate.dstFormat.width, candidate.dstFormat.height) &&
|
||||
!AddCopyDirtyRect(copyDirtyRects, ARRAYSIZE(copyDirtyRects),
|
||||
&nbCopyDirtyRects, clipped))
|
||||
fullCopy = true;
|
||||
}
|
||||
|
||||
for (const RECT * rect = candidate.dirtyRects;
|
||||
rect < candidate.dirtyRects + candidate.nbDirtyRects && !fullCopy;
|
||||
++rect)
|
||||
if (!AddCopyDirtyRect(copyDirtyRects, ARRAYSIZE(copyDirtyRects),
|
||||
&nbCopyDirtyRects, *rect))
|
||||
fullCopy = true;
|
||||
|
||||
if (!fullCopy)
|
||||
fullCopy = IsFullDamage(
|
||||
copyDirtyRects, nbCopyDirtyRects,
|
||||
candidate.dstFormat.width, candidate.dstFormat.height) ||
|
||||
CopyAreaCoversFrame(
|
||||
copyDirtyRects, nbCopyDirtyRects,
|
||||
candidate.dstFormat.width, candidate.dstFormat.height);
|
||||
|
||||
if (!fullCopy)
|
||||
fullCopy = postProcessor.ShouldCopyFully(
|
||||
copyDirtyRects, nbCopyDirtyRects);
|
||||
}
|
||||
const bool fullCopy = BuildCopyDamage(postProcessor, buffer.fullCopy,
|
||||
previousDirtyRects, nbPreviousDirtyRects,
|
||||
candidate.dirtyRects, candidate.nbDirtyRects,
|
||||
candidate.dstFormat.width, candidate.dstFormat.height,
|
||||
copyDirtyRects, &nbCopyDirtyRects);
|
||||
|
||||
fbRes->SetTiming(
|
||||
candidate.captureTime, candidate.postProcessStart, publishStart);
|
||||
@@ -1592,6 +1737,271 @@ bool CSwapChainProcessor::GetContentHDRMetadata(D12FrameFormat& format) const
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CSwapChainProcessor::PublishSoftwareFrame(CInteropResource * srcRes,
|
||||
const D12FrameFormat& srcFormat, uint64_t captureTime,
|
||||
uint64_t postProcessStart, bool noImageUpdate)
|
||||
{
|
||||
CSRWSharedLock pipelineLock(&m_pipelineLock);
|
||||
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_directSoftwareTexture &&
|
||||
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_dx12Device->GetDevice()->GetCopyableFootprints(
|
||||
&textureDesc, 0, 1, 0, &layout, nullptr, nullptr, nullptr);
|
||||
const unsigned texturePitch = layout.Footprint.RowPitch;
|
||||
if (texturePitch && textureDesc.Height <=
|
||||
m_devContext->GetMaxFrameSize() / texturePitch)
|
||||
{
|
||||
pitch = texturePitch;
|
||||
frameSize = (size_t)pitch * textureDesc.Height;
|
||||
textureDescPtr = &textureDesc;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_directSoftwareTexture = false;
|
||||
DEBUG_WARN("IVSHMEM texture layout does not fit the framebuffer");
|
||||
}
|
||||
}
|
||||
else if (m_directSoftwareTexture)
|
||||
{
|
||||
m_directSoftwareTexture = false;
|
||||
DEBUG_WARN("Post-processor output cannot use an IVSHMEM texture");
|
||||
}
|
||||
|
||||
if (!pitch || !frameSize || frameSize > m_devContext->GetMaxFrameSize())
|
||||
{
|
||||
DEBUG_ERROR("Software frame does not fit in shared memory");
|
||||
SetFullPendingDamage();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Static-desktop re-encodes carry no new image. The retained frame can be
|
||||
// republished without spending GPU or memory bandwidth on another copy. A
|
||||
// pending full-damage request still passes through so startup, format
|
||||
// changes, and failed copies can establish a valid replacement.
|
||||
if (noImageUpdate && !HasPendingDamage())
|
||||
return true;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CFrameScheduler::Schedule commitSchedule = {};
|
||||
CFrameScheduler::Schedule deliverySchedule = {};
|
||||
CIndirectDeviceContext::PreparedFrameBuffer buffer = {};
|
||||
CD3D12CommandSlot * copySlot = nullptr;
|
||||
RECT currentDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||
unsigned nbDirtyRects = 0;
|
||||
bool hasDamage = false;
|
||||
|
||||
uint64_t ignoredTarget = 0;
|
||||
bool ignoredPeriodic = false;
|
||||
bool ignoredRepublish = false;
|
||||
m_devContext->GetPublishTarget(CFrameScheduler::Nanotime(),
|
||||
ignoredTarget, commitSchedule, ignoredPeriodic, ignoredRepublish);
|
||||
deliverySchedule = commitSchedule;
|
||||
deliverySchedule.deliveryDeadlineSerial = 0;
|
||||
deliverySchedule.phaseEligible = false;
|
||||
|
||||
m_devContext->ProcessFrameQueue();
|
||||
// Ordinary frames never displace the retained fallback. A must-not-drop
|
||||
// static replacement may reuse it once every transport reference is gone.
|
||||
if (!m_devContext->FrameBufferAvailable(
|
||||
deliverySchedule, noImageUpdate))
|
||||
{
|
||||
if (!noImageUpdate)
|
||||
{
|
||||
m_devContext->FrameSuperseded();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(m_terminateEvent.Get(), 1) == WAIT_OBJECT_0)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
copySlot = m_dx12Device->GetCopySlot();
|
||||
if (!copySlot)
|
||||
{
|
||||
if (!noImageUpdate)
|
||||
{
|
||||
m_devContext->FrameSuperseded();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(m_terminateEvent.Get(), 1) == WAIT_OBJECT_0)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
hasDamage = TakePendingDamage(
|
||||
currentDirtyRects, &nbDirtyRects);
|
||||
ClipDirtyRects(currentDirtyRects, &nbDirtyRects,
|
||||
dstFormat.width, dstFormat.height);
|
||||
buffer = m_devContext->PrepareFrameBuffer(
|
||||
pitch, srcFormat, dstFormat,
|
||||
currentDirtyRects, nbDirtyRects, deliverySchedule,
|
||||
noImageUpdate);
|
||||
if (!buffer.mem)
|
||||
{
|
||||
copySlot->Cancel();
|
||||
RestorePendingDamage(
|
||||
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||
if (!noImageUpdate)
|
||||
{
|
||||
m_devContext->FrameSuperseded();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(m_terminateEvent.Get(), 1) == WAIT_OBJECT_0)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
CFrameBufferResource * fbRes = nullptr;
|
||||
if (textureDescPtr)
|
||||
{
|
||||
fbRes = m_fbPool.Get(buffer, frameSize, textureDescPtr);
|
||||
if (!fbRes)
|
||||
{
|
||||
const HRESULT deviceStatus =
|
||||
m_dx12Device->GetDevice()->GetDeviceRemovedReason();
|
||||
if (FAILED(deviceStatus))
|
||||
{
|
||||
copySlot->Cancel();
|
||||
m_devContext->AbortFrameBuffer(buffer.frameIndex);
|
||||
RestorePendingDamage(
|
||||
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||
DEBUG_ERROR_HR(deviceStatus,
|
||||
"D3D12 device removed while creating an IVSHMEM texture");
|
||||
SetFullPendingDamage();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_directSoftwareTexture = false;
|
||||
textureDescPtr = nullptr;
|
||||
DEBUG_WARN(
|
||||
"IVSHMEM textures unavailable; using a direct buffer copy");
|
||||
}
|
||||
}
|
||||
|
||||
if (!fbRes)
|
||||
fbRes = m_fbPool.Get(buffer, frameSize);
|
||||
if (!fbRes)
|
||||
{
|
||||
copySlot->Cancel();
|
||||
m_devContext->AbortFrameBuffer(buffer.frameIndex);
|
||||
RestorePendingDamage(
|
||||
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||
DEBUG_ERROR("Failed to get a framebuffer for software capture");
|
||||
SetFullPendingDamage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!srcRes->Signal() || !srcRes->Sync(*copySlot))
|
||||
{
|
||||
copySlot->Cancel();
|
||||
m_devContext->AbortFrameBuffer(buffer.frameIndex);
|
||||
RestorePendingDamage(
|
||||
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||
SetFullPendingDamage();
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT previousDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||
unsigned nbPreviousDirtyRects = 0;
|
||||
AcquireSRWLockShared(&m_damageLock);
|
||||
nbPreviousDirtyRects = m_nbDirtyRects;
|
||||
if (nbPreviousDirtyRects)
|
||||
memcpy(previousDirtyRects, m_dirtyRects,
|
||||
nbPreviousDirtyRects * sizeof(*previousDirtyRects));
|
||||
ReleaseSRWLockShared(&m_damageLock);
|
||||
|
||||
RECT copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {};
|
||||
unsigned nbCopyDirtyRects = 0;
|
||||
const bool fullCopy = BuildCopyDamage(
|
||||
postProcessor, buffer.fullCopy,
|
||||
previousDirtyRects, nbPreviousDirtyRects,
|
||||
currentDirtyRects, nbDirtyRects,
|
||||
dstFormat.width, dstFormat.height,
|
||||
copyDirtyRects, &nbCopyDirtyRects);
|
||||
|
||||
const unsigned bytesPerPixel =
|
||||
dstFormat.format == FRAME_TYPE_RGBA16F ? 8 : 4;
|
||||
const uint64_t copyStart = CFrameScheduler::Nanotime();
|
||||
fbRes->SetTiming(captureTime, postProcessStart, copyStart);
|
||||
fbRes->SetSchedule(deliverySchedule);
|
||||
fbRes->SetCopyDamage(copyDirtyRects, nbCopyDirtyRects,
|
||||
fullCopy, pitch, bytesPerPixel);
|
||||
fbRes->ResetCompletion();
|
||||
copySlot->SetCompletionCallback(
|
||||
&SoftwareCompletionFunction, this, fbRes);
|
||||
copySlot->BeginTiming();
|
||||
postProcessor.CopyToFrameBuffer(copySlot->GetGfxList(),
|
||||
fbRes->Get().Get(), srcRes->GetRes().Get(),
|
||||
copyDirtyRects, nbCopyDirtyRects, fullCopy);
|
||||
copySlot->EndTiming();
|
||||
|
||||
bool deliveredToOwner;
|
||||
if (!m_devContext->PublishFrameBuffer(
|
||||
buffer.frameIndex, deliverySchedule, deliveredToOwner))
|
||||
{
|
||||
copySlot->Cancel();
|
||||
m_devContext->AbortFrameBuffer(buffer.frameIndex);
|
||||
RestorePendingDamage(
|
||||
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||
if (!noImageUpdate)
|
||||
{
|
||||
m_devContext->FrameSuperseded();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(m_terminateEvent.Get(), 1) == WAIT_OBJECT_0)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
CommitFrameDamage(currentDirtyRects, nbDirtyRects);
|
||||
if (!copySlot->Execute())
|
||||
{
|
||||
const bool submittedWork = copySlot->HasSubmittedWork();
|
||||
const bool completionHandled = fbRes->CompletionHandled();
|
||||
if (!submittedWork && !completionHandled)
|
||||
m_devContext->FailFrameBuffer(buffer.frameIndex);
|
||||
RestorePendingDamage(
|
||||
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||
if (!submittedWork && !completionHandled)
|
||||
{
|
||||
SetFullPendingDamage();
|
||||
m_devContext->ForceFrame();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_devContext->CommitFrameBuffer(
|
||||
buffer.frameIndex, commitSchedule, false, deliveredToOwner);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer,
|
||||
unsigned dirtyRectCount, unsigned moveRegionCount,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel,
|
||||
@@ -1838,6 +2248,10 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
||||
if (needsReconfigure || postProcessFormatChanged || frameMetadataChanged)
|
||||
m_devContext->ForceFrame();
|
||||
|
||||
if (m_dx11Device->IsSoftware())
|
||||
return PublishSoftwareFrame(srcRes, srcFormat,
|
||||
captureTime, postProcessStart, noImageUpdate);
|
||||
|
||||
// Always prepare the requested static-desktop re-encode. An older
|
||||
// publication can still fail after this frame is acquired, so deciding
|
||||
// solely from the current pending-damage state can lose the final update.
|
||||
|
||||
@@ -106,8 +106,9 @@ private:
|
||||
// Capture holds this only across submission; the publisher uses it to
|
||||
// close the gate before recording deadline work.
|
||||
SRWLOCK m_copySubmitLock = SRWLOCK_INIT;
|
||||
uint64_t m_candidateSequence = 0;
|
||||
bool m_publishPending = false;
|
||||
uint64_t m_candidateSequence = 0;
|
||||
bool m_publishPending = false;
|
||||
bool m_directSoftwareTexture = false;
|
||||
|
||||
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[3];
|
||||
Wrappers::Event m_terminateEvent;
|
||||
@@ -170,12 +171,23 @@ private:
|
||||
CD3D12CommandSlot * slot, bool result, void * param1, void * param2);
|
||||
static void CandidateCompletionFunction(
|
||||
CD3D12CommandSlot * slot, bool result, void * param1, void * param2);
|
||||
static void SoftwareCompletionFunction(
|
||||
CD3D12CommandSlot * slot, bool result, void * param1, void * param2);
|
||||
void AccumulateFrameDamage(const RECT * dirtyRects, unsigned nbDirtyRects);
|
||||
bool HasPendingDamage();
|
||||
bool TakePendingDamage(RECT dirtyRects[], unsigned * nbDirtyRects);
|
||||
void RestorePendingDamage(const RECT dirtyRects[],
|
||||
unsigned nbDirtyRects, bool hasDamage);
|
||||
void CommitFrameDamage(
|
||||
const RECT dirtyRects[], unsigned nbDirtyRects);
|
||||
void SetFullPendingDamage();
|
||||
#ifdef HAS_IDDCX_110
|
||||
void UpdateHDRMetadata(const IDDCX_METADATA2& metadata);
|
||||
#endif
|
||||
bool GetContentHDRMetadata(D12FrameFormat& format) const;
|
||||
bool PublishSoftwareFrame(CInteropResource * srcRes,
|
||||
const D12FrameFormat& srcFormat, uint64_t captureTime,
|
||||
uint64_t postProcessStart, bool noImageUpdate);
|
||||
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer,
|
||||
unsigned dirtyRectCount, unsigned moveRegionCount,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel,
|
||||
|
||||
Reference in New Issue
Block a user