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:
@@ -36,7 +36,7 @@ private:
|
|||||||
ComPtr<IDXGIAdapter1 > m_adapter;
|
ComPtr<IDXGIAdapter1 > m_adapter;
|
||||||
ComPtr<ID3D11Device5 > m_device;
|
ComPtr<ID3D11Device5 > m_device;
|
||||||
ComPtr<ID3D11DeviceContext4> m_context;
|
ComPtr<ID3D11DeviceContext4> m_context;
|
||||||
bool m_isSoftware;
|
bool m_isSoftware = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CD3D11Device(LUID adapterLuid) :
|
CD3D11Device(LUID adapterLuid) :
|
||||||
@@ -54,4 +54,4 @@ public:
|
|||||||
ComPtr<ID3D11DeviceContext4> GetContext() { return m_context; }
|
ComPtr<ID3D11DeviceContext4> GetContext() { return m_context; }
|
||||||
|
|
||||||
bool IsSoftware() { return m_isSoftware; }
|
bool IsSoftware() { return m_isSoftware; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,13 +19,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CFrameBufferPool.h"
|
#include "CFrameBufferPool.h"
|
||||||
#include "CSwapChainProcessor.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void CFrameBufferPool::Init(CSwapChainProcessor * swapChain)
|
void CFrameBufferPool::Init(
|
||||||
|
CIndirectDeviceContext * device, CD3D12Device * dx12)
|
||||||
{
|
{
|
||||||
m_swapChain = swapChain;
|
m_device = device;
|
||||||
|
m_dx12 = dx12;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrameBufferPool::Reset()
|
void CFrameBufferPool::Reset()
|
||||||
@@ -42,7 +43,7 @@ CFrameBufferResource * CFrameBufferPool::Get(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
CFrameBufferResource * fbr = &m_buffers[buffer.frameIndex];
|
CFrameBufferResource * fbr = &m_buffers[buffer.frameIndex];
|
||||||
if (!fbr->Init(m_swapChain, buffer.frameIndex, buffer.mem,
|
if (!fbr->Init(m_device, m_dx12, buffer.frameIndex, buffer.mem,
|
||||||
minSize, textureDesc))
|
minSize, textureDesc))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|||||||
@@ -24,19 +24,20 @@
|
|||||||
#include "CIndirectDeviceContext.h"
|
#include "CIndirectDeviceContext.h"
|
||||||
#include "common/KVMFR.h"
|
#include "common/KVMFR.h"
|
||||||
|
|
||||||
//class CSwapChainProcessor;
|
struct CD3D12Device;
|
||||||
|
|
||||||
class CFrameBufferPool
|
class CFrameBufferPool
|
||||||
{
|
{
|
||||||
CSwapChainProcessor * m_swapChain;
|
CIndirectDeviceContext * m_device = nullptr;
|
||||||
|
CD3D12Device * m_dx12 = nullptr;
|
||||||
|
|
||||||
CFrameBufferResource m_buffers[LGMP_Q_FRAME_BUFFER_LEN];
|
CFrameBufferResource m_buffers[LGMP_Q_FRAME_BUFFER_LEN];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void Init(CSwapChainProcessor * swapChain);
|
void Init(CIndirectDeviceContext * device, CD3D12Device * dx12);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
CFrameBufferResource* CFrameBufferPool::Get(
|
CFrameBufferResource * Get(
|
||||||
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
|
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
|
||||||
size_t minSize,
|
size_t minSize,
|
||||||
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
|
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
|
||||||
|
|||||||
@@ -19,42 +19,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CFrameBufferResource.h"
|
#include "CFrameBufferResource.h"
|
||||||
#include "CSwapChainProcessor.h"
|
#include "CFrameProcessorUtil.h"
|
||||||
|
#include "CD3D12Device.h"
|
||||||
|
#include "CIndirectDeviceContext.h"
|
||||||
#include "CDebug.h"
|
#include "CDebug.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
static bool ResourceDescMatches(
|
bool CFrameBufferResource::Init(CIndirectDeviceContext * device,
|
||||||
const D3D12_RESOURCE_DESC& left, const D3D12_RESOURCE_DESC& right)
|
CD3D12Device * dx12, unsigned frameIndex, uint8_t * base, size_t size,
|
||||||
{
|
|
||||||
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)
|
const D3D12_RESOURCE_DESC * textureDesc)
|
||||||
{
|
{
|
||||||
m_frameIndex = frameIndex;
|
m_frameIndex = frameIndex;
|
||||||
|
|
||||||
if (size > swapChain->GetDevice()->GetMaxFrameSize())
|
if (size > device->GetMaxFrameSize())
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Frame size of %llu is too large to fit in shared ram",
|
DEBUG_ERROR("Frame size of %llu is too large to fit in shared ram",
|
||||||
(unsigned long long)size);
|
(unsigned long long)size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto dx12 = swapChain->GetD3D12Device();
|
|
||||||
const bool indirect = dx12->IsIndirectCopy();
|
const bool indirect = dx12->IsIndirectCopy();
|
||||||
const ResourceType type = textureDesc ?
|
const ResourceType type = textureDesc ?
|
||||||
RESOURCE_TEXTURE : RESOURCE_BUFFER;
|
RESOURCE_TEXTURE : RESOURCE_BUFFER;
|
||||||
@@ -99,7 +83,8 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain,
|
|||||||
// Nothing to do if the resource already represents this allocation.
|
// Nothing to do if the resource already represents this allocation.
|
||||||
if (m_base == base && m_type == type &&
|
if (m_base == base && m_type == type &&
|
||||||
((type == RESOURCE_BUFFER && m_size >= size) ||
|
((type == RESOURCE_BUFFER && m_size >= size) ||
|
||||||
(type == RESOURCE_TEXTURE && ResourceDescMatches(m_desc, desc))))
|
(type == RESOURCE_TEXTURE &&
|
||||||
|
CFrameProcessorUtil::ResourceDescMatches(m_desc, desc))))
|
||||||
{
|
{
|
||||||
m_frameSize = size;
|
m_frameSize = size;
|
||||||
return true;
|
return true;
|
||||||
@@ -146,14 +131,14 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain,
|
|||||||
{
|
{
|
||||||
const UINT64 heapOffset =
|
const UINT64 heapOffset =
|
||||||
(uintptr_t)base -
|
(uintptr_t)base -
|
||||||
(uintptr_t)swapChain->GetDevice()->GetIVSHMEM().GetMem();
|
(uintptr_t)device->GetIVSHMEM().GetMem();
|
||||||
const D3D12_RESOURCE_ALLOCATION_INFO allocation =
|
const D3D12_RESOURCE_ALLOCATION_INFO allocation =
|
||||||
dx12->GetDevice()->GetResourceAllocationInfo(0, 1, &desc);
|
dx12->GetDevice()->GetResourceAllocationInfo(0, 1, &desc);
|
||||||
allocationSize = allocation.SizeInBytes;
|
allocationSize = allocation.SizeInBytes;
|
||||||
const D3D12_HEAP_DESC heapDesc = dx12->GetHeap()->GetDesc();
|
const D3D12_HEAP_DESC heapDesc = dx12->GetHeap()->GetDesc();
|
||||||
if (!allocation.Alignment ||
|
if (!allocation.Alignment ||
|
||||||
heapOffset % allocation.Alignment ||
|
heapOffset % allocation.Alignment ||
|
||||||
allocation.SizeInBytes > swapChain->GetDevice()->GetMaxFrameSize() ||
|
allocation.SizeInBytes > device->GetMaxFrameSize() ||
|
||||||
heapOffset > heapDesc.SizeInBytes ||
|
heapOffset > heapDesc.SizeInBytes ||
|
||||||
allocation.SizeInBytes > heapDesc.SizeInBytes - heapOffset)
|
allocation.SizeInBytes > heapDesc.SizeInBytes - heapOffset)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
#include "CFrameScheduler.h"
|
#include "CFrameScheduler.h"
|
||||||
#include "CInteropResource.h"
|
#include "CInteropResource.h"
|
||||||
|
|
||||||
class CSwapChainProcessor;
|
struct CD3D12Device;
|
||||||
|
class CIndirectDeviceContext;
|
||||||
|
|
||||||
using namespace Microsoft::WRL;
|
using namespace Microsoft::WRL;
|
||||||
|
|
||||||
@@ -69,8 +70,8 @@ class CFrameBufferResource
|
|||||||
void * m_map = nullptr;
|
void * m_map = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex,
|
bool Init(CIndirectDeviceContext * device, CD3D12Device * dx12,
|
||||||
uint8_t * base, size_t size,
|
unsigned frameIndex, uint8_t * base, size_t size,
|
||||||
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
|
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
|||||||
175
idd/LGIdd/CFrameProcessor.cpp
Normal file
175
idd/LGIdd/CFrameProcessor.cpp
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* 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 "CFrameProcessor.h"
|
||||||
|
#include "CFrameProcessorUtil.h"
|
||||||
|
#include "CHardwareFrameProcessor.h"
|
||||||
|
#include "CSoftwareFrameProcessor.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <new>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
CFrameProcessor::CFrameProcessor(CIndirectDeviceContext * device,
|
||||||
|
std::shared_ptr<CD3D12Device> dx12,
|
||||||
|
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
|
||||||
|
SRWLOCK * pipelineLock, HANDLE terminateEvent) :
|
||||||
|
m_device(device),
|
||||||
|
m_dx12(std::move(dx12)),
|
||||||
|
m_postProcessors(postProcessors),
|
||||||
|
m_pipelineLock(pipelineLock),
|
||||||
|
m_terminateEvent(terminateEvent)
|
||||||
|
{
|
||||||
|
m_readyEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||||
|
m_frameBuffers.Init(m_device, m_dx12.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFrameProcessor::IsValid() const
|
||||||
|
{
|
||||||
|
return m_readyEvent.Get() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::Reset()
|
||||||
|
{
|
||||||
|
m_frameBuffers.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::Invalidate()
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
m_previousDamageCount = 0;
|
||||||
|
m_hasPendingDamage = true;
|
||||||
|
m_pendingDamageCount = 0;
|
||||||
|
SetFullDamageLocked();
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::ResetPipeline()
|
||||||
|
{
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::AccumulateDamage(
|
||||||
|
const RECT dirtyRects[], unsigned count)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
CFrameProcessorUtil::AccumulateDamage(
|
||||||
|
m_pendingDamage, &m_pendingDamageCount, &m_hasPendingDamage,
|
||||||
|
dirtyRects, count);
|
||||||
|
AccumulateDamageLocked(dirtyRects, count);
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::SetFullDamage()
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
m_hasPendingDamage = true;
|
||||||
|
m_pendingDamageCount = 0;
|
||||||
|
SetFullDamageLocked();
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::AccumulateDamageLocked(
|
||||||
|
const RECT[], unsigned)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::SetFullDamageLocked()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFrameProcessor::HasPendingDamage() const
|
||||||
|
{
|
||||||
|
AcquireSRWLockShared(&m_damageLock);
|
||||||
|
const bool result = m_hasPendingDamage;
|
||||||
|
ReleaseSRWLockShared(&m_damageLock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFrameProcessor::TakePendingDamage(
|
||||||
|
RECT dirtyRects[], unsigned * count)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
const bool hasDamage = m_hasPendingDamage;
|
||||||
|
*count = hasDamage ? m_pendingDamageCount : 0;
|
||||||
|
if (*count)
|
||||||
|
memcpy(dirtyRects, m_pendingDamage,
|
||||||
|
*count * sizeof(*dirtyRects));
|
||||||
|
m_hasPendingDamage = false;
|
||||||
|
m_pendingDamageCount = 0;
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
return hasDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::RestorePendingDamage(
|
||||||
|
const RECT dirtyRects[], unsigned count, bool hasDamage)
|
||||||
|
{
|
||||||
|
if (!hasDamage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
CFrameProcessorUtil::AccumulateDamage(
|
||||||
|
m_pendingDamage, &m_pendingDamageCount, &m_hasPendingDamage,
|
||||||
|
dirtyRects, count);
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::CommitDamage(
|
||||||
|
const RECT dirtyRects[], unsigned count)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
m_previousDamageCount = count;
|
||||||
|
if (count)
|
||||||
|
memcpy(m_previousDamage, dirtyRects,
|
||||||
|
count * sizeof(*m_previousDamage));
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFrameProcessor::GetPreviousDamage(
|
||||||
|
RECT dirtyRects[], unsigned * count) const
|
||||||
|
{
|
||||||
|
AcquireSRWLockShared(&m_damageLock);
|
||||||
|
*count = m_previousDamageCount;
|
||||||
|
if (*count)
|
||||||
|
memcpy(dirtyRects, m_previousDamage,
|
||||||
|
*count * sizeof(*dirtyRects));
|
||||||
|
ReleaseSRWLockShared(&m_damageLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CFrameProcessor> CreateFrameProcessor(
|
||||||
|
bool software, CIndirectDeviceContext * device,
|
||||||
|
std::shared_ptr<CD3D12Device> dx12,
|
||||||
|
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
|
||||||
|
SRWLOCK * pipelineLock, HANDLE terminateEvent)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CFrameProcessor> processor;
|
||||||
|
if (software)
|
||||||
|
processor.reset(new (std::nothrow) CSoftwareFrameProcessor(
|
||||||
|
device, std::move(dx12), postProcessors,
|
||||||
|
pipelineLock, terminateEvent));
|
||||||
|
else
|
||||||
|
processor.reset(new (std::nothrow) CHardwareFrameProcessor(
|
||||||
|
device, std::move(dx12), postProcessors,
|
||||||
|
pipelineLock, terminateEvent));
|
||||||
|
|
||||||
|
if (!processor || !processor->IsValid())
|
||||||
|
return nullptr;
|
||||||
|
return processor;
|
||||||
|
}
|
||||||
99
idd/LGIdd/CFrameProcessor.h
Normal file
99
idd/LGIdd/CFrameProcessor.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CD3D12Device.h"
|
||||||
|
#include "CFrameBufferPool.h"
|
||||||
|
#include "CIndirectDeviceContext.h"
|
||||||
|
#include "CInteropResource.h"
|
||||||
|
#include "CPostProcessor.h"
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace Microsoft::WRL;
|
||||||
|
|
||||||
|
struct FrameSubmission
|
||||||
|
{
|
||||||
|
CInteropResource * source;
|
||||||
|
D12FrameFormat sourceFormat;
|
||||||
|
uint64_t captureTime;
|
||||||
|
uint64_t postProcessStart;
|
||||||
|
unsigned timingEffectIndex;
|
||||||
|
uint64_t timingToken;
|
||||||
|
bool noImageUpdate;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CFrameProcessor
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
CIndirectDeviceContext * m_device;
|
||||||
|
std::shared_ptr<CD3D12Device> m_dx12;
|
||||||
|
CPostProcessor * m_postProcessors;
|
||||||
|
SRWLOCK * m_pipelineLock;
|
||||||
|
HANDLE m_terminateEvent;
|
||||||
|
CFrameBufferPool m_frameBuffers;
|
||||||
|
Wrappers::Event m_readyEvent;
|
||||||
|
|
||||||
|
mutable SRWLOCK m_damageLock = SRWLOCK_INIT;
|
||||||
|
RECT m_previousDamage[LG_MAX_DIRTY_RECTS] = {};
|
||||||
|
unsigned m_previousDamageCount = 0;
|
||||||
|
RECT m_pendingDamage[LG_MAX_DIRTY_RECTS] = {};
|
||||||
|
unsigned m_pendingDamageCount = 0;
|
||||||
|
bool m_hasPendingDamage = true;
|
||||||
|
|
||||||
|
bool HasPendingDamage() const;
|
||||||
|
bool TakePendingDamage(RECT dirtyRects[], unsigned * count);
|
||||||
|
void RestorePendingDamage(
|
||||||
|
const RECT dirtyRects[], unsigned count, bool hasDamage);
|
||||||
|
void CommitDamage(const RECT dirtyRects[], unsigned count);
|
||||||
|
void GetPreviousDamage(RECT dirtyRects[], unsigned * count) const;
|
||||||
|
virtual void AccumulateDamageLocked(
|
||||||
|
const RECT dirtyRects[], unsigned count);
|
||||||
|
virtual void SetFullDamageLocked();
|
||||||
|
|
||||||
|
public:
|
||||||
|
CFrameProcessor(CIndirectDeviceContext * device,
|
||||||
|
std::shared_ptr<CD3D12Device> dx12,
|
||||||
|
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
|
||||||
|
SRWLOCK * pipelineLock, HANDLE terminateEvent);
|
||||||
|
virtual ~CFrameProcessor() = default;
|
||||||
|
|
||||||
|
virtual bool IsValid() const;
|
||||||
|
virtual bool Submit(const FrameSubmission& submission) = 0;
|
||||||
|
virtual bool HasReadyFrame() const = 0;
|
||||||
|
virtual bool Publish(const CFrameScheduler::Schedule& schedule,
|
||||||
|
bool periodic, uint64_t publishStart) = 0;
|
||||||
|
virtual bool UsesCadence() const = 0;
|
||||||
|
virtual void Reset();
|
||||||
|
virtual void Invalidate();
|
||||||
|
virtual void ResetPipeline();
|
||||||
|
void AccumulateDamage(const RECT dirtyRects[], unsigned count);
|
||||||
|
void SetFullDamage();
|
||||||
|
|
||||||
|
HANDLE GetReadyEvent() const { return m_readyEvent.Get(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<CFrameProcessor> CreateFrameProcessor(
|
||||||
|
bool software, CIndirectDeviceContext * device,
|
||||||
|
std::shared_ptr<CD3D12Device> dx12,
|
||||||
|
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
|
||||||
|
SRWLOCK * pipelineLock, HANDLE terminateEvent);
|
||||||
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;
|
||||||
|
}
|
||||||
63
idd/LGIdd/CFrameProcessorUtil.h
Normal file
63
idd/LGIdd/CFrameProcessorUtil.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CInteropResource.h"
|
||||||
|
#include "CPostProcessor.h"
|
||||||
|
|
||||||
|
class CFrameProcessorSharedLock
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SRWLOCK * m_lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CFrameProcessorSharedLock(SRWLOCK * lock) : m_lock(lock)
|
||||||
|
{
|
||||||
|
AcquireSRWLockShared(m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CFrameProcessorSharedLock()
|
||||||
|
{
|
||||||
|
ReleaseSRWLockShared(m_lock);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CFrameProcessorUtil
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool FrameMetadataChanged(const D12FrameFormat& previous,
|
||||||
|
const D12FrameFormat& current);
|
||||||
|
static FrameType GetFrameType(DXGI_FORMAT format);
|
||||||
|
static bool ResourceDescMatches(const D3D12_RESOURCE_DESC& left,
|
||||||
|
const D3D12_RESOURCE_DESC& right, bool compareAlignment = true);
|
||||||
|
static void ClipDirtyRects(RECT dirtyRects[], unsigned * nbDirtyRects,
|
||||||
|
unsigned width, unsigned height);
|
||||||
|
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);
|
||||||
|
static void AccumulateDamage(
|
||||||
|
RECT pendingDirtyRects[], unsigned * nbPendingDirtyRects,
|
||||||
|
bool * hasPendingDamage, const RECT dirtyRects[],
|
||||||
|
unsigned nbDirtyRects);
|
||||||
|
};
|
||||||
821
idd/LGIdd/CHardwareFrameProcessor.cpp
Normal file
821
idd/LGIdd/CHardwareFrameProcessor.cpp
Normal file
@@ -0,0 +1,821 @@
|
|||||||
|
/**
|
||||||
|
* 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 "CHardwareFrameProcessor.h"
|
||||||
|
#include "CFrameProcessorUtil.h"
|
||||||
|
#include "CDebug.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace Microsoft::WRL;
|
||||||
|
|
||||||
|
static_assert(LGMP_Q_FRAME_LEN == 2,
|
||||||
|
"IDD candidate pipeline assumes two slots");
|
||||||
|
|
||||||
|
class CPublishPending
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SRWLOCK * m_lock;
|
||||||
|
bool * m_pending;
|
||||||
|
HANDLE m_event;
|
||||||
|
bool m_active = true;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CPublishPending(SRWLOCK * lock, bool * pending, HANDLE event) :
|
||||||
|
m_lock(lock),
|
||||||
|
m_pending(pending),
|
||||||
|
m_event(event)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(m_lock);
|
||||||
|
*m_pending = true;
|
||||||
|
ResetEvent(m_event);
|
||||||
|
ReleaseSRWLockExclusive(m_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CPublishPending()
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
if (!m_active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(m_lock);
|
||||||
|
*m_pending = false;
|
||||||
|
SetEvent(m_event);
|
||||||
|
ReleaseSRWLockExclusive(m_lock);
|
||||||
|
m_active = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CHardwareFrameProcessor::CHardwareFrameProcessor(
|
||||||
|
CIndirectDeviceContext * device, std::shared_ptr<CD3D12Device> dx12,
|
||||||
|
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
|
||||||
|
SRWLOCK * pipelineLock, HANDLE terminateEvent) :
|
||||||
|
CFrameProcessor(device, std::move(dx12), postProcessors,
|
||||||
|
pipelineLock, terminateEvent)
|
||||||
|
{
|
||||||
|
m_candidateAvailableEvent.Attach(
|
||||||
|
CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||||
|
m_copySubmitEvent.Attach(CreateEvent(nullptr, TRUE, TRUE, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CHardwareFrameProcessor::IsValid() const
|
||||||
|
{
|
||||||
|
return CFrameProcessor::IsValid() &&
|
||||||
|
m_candidateAvailableEvent.Get() && m_copySubmitEvent.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::SignalCandidateState()
|
||||||
|
{
|
||||||
|
SetEvent(m_readyEvent.Get());
|
||||||
|
SetEvent(m_candidateAvailableEvent.Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::SetFullDamageLocked()
|
||||||
|
{
|
||||||
|
for (CandidateDamageTail& tail : m_candidateDamageTail)
|
||||||
|
if (tail.active)
|
||||||
|
{
|
||||||
|
tail.hasDamage = true;
|
||||||
|
tail.nbDirtyRects = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::AccumulateDamageLocked(
|
||||||
|
const RECT dirtyRects[], unsigned count)
|
||||||
|
{
|
||||||
|
for (CandidateDamageTail& tail : m_candidateDamageTail)
|
||||||
|
if (tail.active)
|
||||||
|
CFrameProcessorUtil::AccumulateDamage(
|
||||||
|
tail.dirtyRects, &tail.nbDirtyRects, &tail.hasDamage,
|
||||||
|
dirtyRects, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::ResetCandidates()
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_candidateLock);
|
||||||
|
for (FrameCandidate& candidate : m_candidates)
|
||||||
|
candidate = {};
|
||||||
|
ReleaseSRWLockExclusive(&m_candidateLock);
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
for (CandidateDamageTail& tail : m_candidateDamageTail)
|
||||||
|
tail = {};
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
SignalCandidateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::Reset()
|
||||||
|
{
|
||||||
|
ResetCandidates();
|
||||||
|
CFrameProcessor::Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::ResetPipeline()
|
||||||
|
{
|
||||||
|
ResetCandidates();
|
||||||
|
CFrameProcessor::Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CHardwareFrameProcessor::HasReadyFrame() const
|
||||||
|
{
|
||||||
|
bool ready = false;
|
||||||
|
AcquireSRWLockShared(&m_candidateLock);
|
||||||
|
for (const FrameCandidate& candidate : m_candidates)
|
||||||
|
if (candidate.state == CANDIDATE_READY)
|
||||||
|
{
|
||||||
|
ready = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockShared(&m_candidateLock);
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CHardwareFrameProcessor::AcquireCandidate(
|
||||||
|
bool exclusiveSample, bool allowSupersede)
|
||||||
|
{
|
||||||
|
int selected = -1;
|
||||||
|
uint64_t oldest = UINT64_MAX;
|
||||||
|
bool superseded = false;
|
||||||
|
bool idle = true;
|
||||||
|
bool publishing = false;
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&m_candidateLock);
|
||||||
|
for (unsigned i = 0; i < ARRAYSIZE(m_candidates); ++i)
|
||||||
|
{
|
||||||
|
if (m_candidates[i].state != CANDIDATE_FREE)
|
||||||
|
{
|
||||||
|
idle = false;
|
||||||
|
if (m_candidates[i].state == CANDIDATE_PUBLISHING)
|
||||||
|
publishing = true;
|
||||||
|
}
|
||||||
|
else if (selected < 0)
|
||||||
|
selected = static_cast<int>(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exclusiveSample && !idle)
|
||||||
|
selected = -1;
|
||||||
|
|
||||||
|
unsigned readyCount = 0;
|
||||||
|
for (const FrameCandidate& candidate : m_candidates)
|
||||||
|
if (candidate.state == CANDIDATE_READY)
|
||||||
|
++readyCount;
|
||||||
|
|
||||||
|
if (allowSupersede && !exclusiveSample && selected < 0 &&
|
||||||
|
readyCount > (publishing ? 0U : 1U))
|
||||||
|
for (unsigned i = 0; i < ARRAYSIZE(m_candidates); ++i)
|
||||||
|
if (m_candidates[i].state == CANDIDATE_READY &&
|
||||||
|
m_candidates[i].sequence < oldest)
|
||||||
|
{
|
||||||
|
selected = static_cast<int>(i);
|
||||||
|
oldest = m_candidates[i].sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected >= 0)
|
||||||
|
{
|
||||||
|
FrameCandidate& candidate =
|
||||||
|
m_candidates[static_cast<unsigned>(selected)];
|
||||||
|
superseded = candidate.state == CANDIDATE_READY;
|
||||||
|
candidate.state = CANDIDATE_PREPARING;
|
||||||
|
candidate.sequence = ++m_candidateSequence;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&m_candidateLock);
|
||||||
|
|
||||||
|
if (superseded)
|
||||||
|
m_device->FrameSuperseded();
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::ReleaseCandidate(unsigned candidateIndex)
|
||||||
|
{
|
||||||
|
if (candidateIndex >= ARRAYSIZE(m_candidates))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&m_candidateLock);
|
||||||
|
m_candidates[candidateIndex].state = CANDIDATE_FREE;
|
||||||
|
ReleaseSRWLockExclusive(&m_candidateLock);
|
||||||
|
SignalCandidateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CHardwareFrameProcessor::EnsureCandidateResource(
|
||||||
|
unsigned candidateIndex, size_t frameSize)
|
||||||
|
{
|
||||||
|
FrameCandidate& candidate = m_candidates[candidateIndex];
|
||||||
|
|
||||||
|
D3D12_RESOURCE_DESC desc = {};
|
||||||
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||||
|
desc.Width = frameSize;
|
||||||
|
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 (candidate.resource &&
|
||||||
|
CFrameProcessorUtil::ResourceDescMatches(
|
||||||
|
candidate.resource->GetDesc(), desc, false))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
candidate.resource.Reset();
|
||||||
|
|
||||||
|
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||||
|
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||||
|
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||||
|
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||||
|
heapProps.CreationNodeMask = 1;
|
||||||
|
heapProps.VisibleNodeMask = 1;
|
||||||
|
|
||||||
|
const HRESULT hr = m_dx12->GetDevice()->CreateCommittedResource(
|
||||||
|
&heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON,
|
||||||
|
nullptr, IID_PPV_ARGS(&candidate.resource));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(hr, "Failed to create retained frame candidate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const WCHAR * names[] =
|
||||||
|
{
|
||||||
|
L"Frame Candidate 0",
|
||||||
|
L"Frame Candidate 1",
|
||||||
|
};
|
||||||
|
candidate.resource->SetName(names[candidateIndex]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CHardwareFrameProcessor::ExecuteCandidateCopy(
|
||||||
|
CD3D12CommandSlot * copySlot)
|
||||||
|
{
|
||||||
|
HANDLE waitHandles[] =
|
||||||
|
{
|
||||||
|
m_terminateEvent,
|
||||||
|
m_copySubmitEvent.Get(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_copySubmitLock);
|
||||||
|
if (!m_publishPending)
|
||||||
|
{
|
||||||
|
const bool result = copySlot->Execute();
|
||||||
|
ReleaseSRWLockExclusive(&m_copySubmitLock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&m_copySubmitLock);
|
||||||
|
|
||||||
|
const DWORD result = WaitForMultipleObjects(
|
||||||
|
ARRAYSIZE(waitHandles), waitHandles, FALSE, INFINITE);
|
||||||
|
if (result == WAIT_OBJECT_0 + 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
copySlot->Cancel();
|
||||||
|
if (result != WAIT_OBJECT_0)
|
||||||
|
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(GetLastError()),
|
||||||
|
"Failed while waiting to submit a frame candidate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::CandidateCompletionFunction(
|
||||||
|
CD3D12CommandSlot * slot, bool result, void * param1, void * param2)
|
||||||
|
{
|
||||||
|
auto processor = static_cast<CHardwareFrameProcessor *>(param1);
|
||||||
|
auto candidate = static_cast<FrameCandidate *>(param2);
|
||||||
|
|
||||||
|
uint64_t gpuStart = 0;
|
||||||
|
uint64_t gpuEnd = 0;
|
||||||
|
const bool timingValid = result && slot->GetGPUTimes(gpuStart, gpuEnd);
|
||||||
|
|
||||||
|
bool forceFrame = false;
|
||||||
|
AcquireSRWLockExclusive(&processor->m_candidateLock);
|
||||||
|
if (candidate->state == CANDIDATE_PREPARING)
|
||||||
|
{
|
||||||
|
candidate->prepareReady = CFrameScheduler::Nanotime();
|
||||||
|
candidate->prepareGPUStart = gpuStart;
|
||||||
|
candidate->prepareGPUEnd = gpuEnd;
|
||||||
|
candidate->prepareTimingValid = timingValid;
|
||||||
|
candidate->state =
|
||||||
|
result ? CANDIDATE_READY : CANDIDATE_FREE;
|
||||||
|
forceFrame = result && candidate->timingToken != 0;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&processor->m_candidateLock);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
processor->SetFullDamage();
|
||||||
|
processor->m_device->ForceFrame();
|
||||||
|
}
|
||||||
|
else if (forceFrame)
|
||||||
|
processor->m_device->ForceFrame();
|
||||||
|
processor->SignalCandidateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHardwareFrameProcessor::CompletionFunction(
|
||||||
|
CD3D12CommandSlot * slot, bool result, void * param1, void * param2)
|
||||||
|
{
|
||||||
|
auto processor = static_cast<CHardwareFrameProcessor *>(param1);
|
||||||
|
auto fbRes = static_cast<CFrameBufferResource *>(param2);
|
||||||
|
const unsigned candidateIndex = fbRes->GetCandidateIndex();
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
processor->m_device->FailFrameBuffer(fbRes->GetFrameIndex());
|
||||||
|
processor->SetFullDamage();
|
||||||
|
processor->m_device->ForceFrame();
|
||||||
|
processor->ReleaseCandidate(candidateIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t prepareCopyStart;
|
||||||
|
uint64_t prepareReady;
|
||||||
|
uint64_t prepareGPUStart;
|
||||||
|
uint64_t prepareGPUEnd;
|
||||||
|
uint64_t timingStart;
|
||||||
|
bool prepareTimingValid;
|
||||||
|
AcquireSRWLockShared(&processor->m_candidateLock);
|
||||||
|
const FrameCandidate& candidate =
|
||||||
|
processor->m_candidates[candidateIndex];
|
||||||
|
prepareCopyStart = candidate.prepareCopyStart;
|
||||||
|
prepareReady = candidate.prepareReady;
|
||||||
|
prepareGPUStart = candidate.prepareGPUStart;
|
||||||
|
prepareGPUEnd = candidate.prepareGPUEnd;
|
||||||
|
timingStart = candidate.timingStart;
|
||||||
|
prepareTimingValid = candidate.prepareTimingValid;
|
||||||
|
ReleaseSRWLockShared(&processor->m_candidateLock);
|
||||||
|
|
||||||
|
const uint64_t publishStart = fbRes->GetCopyStart();
|
||||||
|
uint64_t gpuCopyStart = 0;
|
||||||
|
uint64_t gpuCopyEnd = 0;
|
||||||
|
uint64_t indirectCopyTime = 0;
|
||||||
|
if (processor->m_dx12->IsIndirectCopy())
|
||||||
|
{
|
||||||
|
const uint64_t indirectCopyStart = CFrameScheduler::Nanotime();
|
||||||
|
processor->m_device->WriteFrameBuffer(
|
||||||
|
fbRes->GetFrameIndex(), fbRes->GetMap(), 0,
|
||||||
|
fbRes->GetFrameSize(), false);
|
||||||
|
indirectCopyTime = CFrameScheduler::Nanotime() - indirectCopyStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool gpuTimingValid =
|
||||||
|
slot->GetGPUTimes(gpuCopyStart, gpuCopyEnd);
|
||||||
|
const uint64_t copyReady = CFrameScheduler::Nanotime();
|
||||||
|
|
||||||
|
const uint64_t postProcessStart = fbRes->GetPostProcessStart();
|
||||||
|
uint64_t postProcessTime = prepareCopyStart - postProcessStart;
|
||||||
|
uint64_t prepareCopyTime = prepareReady - prepareCopyStart;
|
||||||
|
if (prepareTimingValid && prepareGPUStart >= postProcessStart &&
|
||||||
|
prepareGPUEnd >= prepareGPUStart && prepareGPUEnd <= prepareReady)
|
||||||
|
{
|
||||||
|
postProcessTime = prepareGPUStart - postProcessStart;
|
||||||
|
prepareCopyTime = prepareGPUEnd - prepareGPUStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t publishCopyTime = copyReady - publishStart;
|
||||||
|
if (gpuTimingValid && gpuCopyStart >= publishStart &&
|
||||||
|
gpuCopyEnd >= gpuCopyStart && gpuCopyEnd <= copyReady)
|
||||||
|
publishCopyTime = gpuCopyEnd - gpuCopyStart + indirectCopyTime;
|
||||||
|
|
||||||
|
const uint64_t copyTime = prepareCopyTime + publishCopyTime;
|
||||||
|
|
||||||
|
processor->m_device->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||||
|
const uint64_t publishedAt = CFrameScheduler::Nanotime();
|
||||||
|
const uint64_t prepareElapsed = prepareReady >= postProcessStart ?
|
||||||
|
prepareReady - postProcessStart : 0;
|
||||||
|
const uint64_t prepareMeasured = postProcessTime + prepareCopyTime;
|
||||||
|
const uint64_t prepareReadyTime = prepareElapsed > prepareMeasured ?
|
||||||
|
prepareElapsed - prepareMeasured : 0;
|
||||||
|
const uint64_t publishElapsed = publishedAt >= publishStart ?
|
||||||
|
publishedAt - publishStart : 0;
|
||||||
|
const uint64_t publishReadyTime = publishElapsed > publishCopyTime ?
|
||||||
|
publishElapsed - publishCopyTime : 0;
|
||||||
|
const uint64_t readyTime = prepareReadyTime + publishReadyTime;
|
||||||
|
const uint64_t holdTime = publishStart >= prepareReady ?
|
||||||
|
publishStart - prepareReady : 0;
|
||||||
|
|
||||||
|
processor->m_device->SetFrameTiming(fbRes->GetFrameIndex(),
|
||||||
|
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime, holdTime,
|
||||||
|
fbRes->GetSchedule(), publishedAt);
|
||||||
|
processor->m_device->TryRecordFrameTiming(publishedAt - publishStart);
|
||||||
|
|
||||||
|
const uint64_t timingToken = fbRes->GetTimingToken();
|
||||||
|
if (timingToken && timingStart && prepareReady >= timingStart &&
|
||||||
|
copyReady >= publishStart)
|
||||||
|
{
|
||||||
|
const uint64_t totalTime =
|
||||||
|
(prepareReady - timingStart) + (copyReady - publishStart);
|
||||||
|
processor->m_postProcessors[candidateIndex].RecordTiming(
|
||||||
|
fbRes->GetTimingEffectIndex(), timingToken,
|
||||||
|
fbRes->IsFullCopy(), totalTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
processor->m_device->CompleteFrameBuffer(fbRes->GetFrameIndex(), true);
|
||||||
|
processor->ReleaseCandidate(candidateIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CHardwareFrameProcessor::Publish(
|
||||||
|
const CFrameScheduler::Schedule& schedule, bool periodic,
|
||||||
|
uint64_t publishStart)
|
||||||
|
{
|
||||||
|
CPublishPending publishPending(
|
||||||
|
&m_copySubmitLock, &m_publishPending, m_copySubmitEvent.Get());
|
||||||
|
CFrameProcessorSharedLock pipelineLock(m_pipelineLock);
|
||||||
|
|
||||||
|
int selectedCandidate = -1;
|
||||||
|
uint64_t newestSequence = 0;
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&m_candidateLock);
|
||||||
|
for (unsigned i = 0; i < ARRAYSIZE(m_candidates); ++i)
|
||||||
|
if (m_candidates[i].state == CANDIDATE_READY &&
|
||||||
|
(selectedCandidate < 0 ||
|
||||||
|
m_candidates[i].sequence > newestSequence))
|
||||||
|
{
|
||||||
|
selectedCandidate = static_cast<int>(i);
|
||||||
|
newestSequence = m_candidates[i].sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedCandidate >= 0)
|
||||||
|
m_candidates[static_cast<unsigned>(selectedCandidate)].state =
|
||||||
|
CANDIDATE_PUBLISHING;
|
||||||
|
ReleaseSRWLockExclusive(&m_candidateLock);
|
||||||
|
|
||||||
|
if (selectedCandidate < 0)
|
||||||
|
return false;
|
||||||
|
const unsigned candidateIndex =
|
||||||
|
static_cast<unsigned>(selectedCandidate);
|
||||||
|
|
||||||
|
const auto restoreCandidate = [this, candidateIndex]()
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_candidateLock);
|
||||||
|
if (m_candidates[candidateIndex].state == CANDIDATE_PUBLISHING)
|
||||||
|
m_candidates[candidateIndex].state = CANDIDATE_READY;
|
||||||
|
ReleaseSRWLockExclusive(&m_candidateLock);
|
||||||
|
SignalCandidateState();
|
||||||
|
};
|
||||||
|
|
||||||
|
AcquireSRWLockShared(&m_candidateLock);
|
||||||
|
const bool candidateValid =
|
||||||
|
m_candidates[candidateIndex].state == CANDIDATE_PUBLISHING &&
|
||||||
|
m_candidates[candidateIndex].resource.Get();
|
||||||
|
ReleaseSRWLockShared(&m_candidateLock);
|
||||||
|
if (!candidateValid)
|
||||||
|
{
|
||||||
|
restoreCandidate();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameCandidate& candidate = m_candidates[candidateIndex];
|
||||||
|
CPostProcessor& postProcessor = m_postProcessors[candidateIndex];
|
||||||
|
const uint64_t candidateSequence = candidate.sequence;
|
||||||
|
|
||||||
|
auto buffer = m_device->PrepareFrameBuffer(
|
||||||
|
candidate.pitch, candidate.srcFormat, candidate.dstFormat,
|
||||||
|
candidate.dirtyRects, candidate.nbDirtyRects, schedule);
|
||||||
|
if (!buffer.mem)
|
||||||
|
{
|
||||||
|
restoreCandidate();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFrameBufferResource * fbRes =
|
||||||
|
m_frameBuffers.Get(buffer, candidate.frameSize);
|
||||||
|
if (!fbRes)
|
||||||
|
{
|
||||||
|
m_device->AbortFrameBuffer(buffer.frameIndex);
|
||||||
|
restoreCandidate();
|
||||||
|
DEBUG_ERROR("Failed to get a CFrameBufferResource from the pool");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CD3D12CommandSlot * copySlot = m_dx12->GetCopySlot(candidateIndex);
|
||||||
|
if (!copySlot)
|
||||||
|
{
|
||||||
|
m_device->AbortFrameBuffer(buffer.frameIndex);
|
||||||
|
restoreCandidate();
|
||||||
|
DEBUG_ERROR("Failed to get a copy CommandSlot for publication");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT previousDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||||
|
unsigned nbPreviousDirtyRects = 0;
|
||||||
|
GetPreviousDamage(previousDirtyRects, &nbPreviousDirtyRects);
|
||||||
|
|
||||||
|
RECT copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {};
|
||||||
|
unsigned nbCopyDirtyRects = 0;
|
||||||
|
const bool fullCopy = CFrameProcessorUtil::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);
|
||||||
|
fbRes->SetCandidateIndex(candidateIndex);
|
||||||
|
fbRes->SetPostProcessSample(
|
||||||
|
candidate.timingEffectIndex, candidate.timingToken, fullCopy);
|
||||||
|
copySlot->SetCompletionCallback(&CompletionFunction, this, fbRes);
|
||||||
|
|
||||||
|
copySlot->BeginTiming();
|
||||||
|
postProcessor.CopyFromCandidate(
|
||||||
|
copySlot->GetGfxList(), fbRes->Get().Get(), candidate.resource.Get(),
|
||||||
|
copyDirtyRects, nbCopyDirtyRects, fullCopy);
|
||||||
|
copySlot->EndTiming();
|
||||||
|
|
||||||
|
bool deliveredToOwner;
|
||||||
|
if (!m_device->PublishFrameBuffer(
|
||||||
|
buffer.frameIndex, schedule, deliveredToOwner))
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
m_device->AbortFrameBuffer(buffer.frameIndex);
|
||||||
|
restoreCandidate();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CFrameScheduler::Schedule frameSchedule = schedule;
|
||||||
|
if (!deliveredToOwner ||
|
||||||
|
!m_device->TryFrameSubmitted(buffer.frameIndex, schedule))
|
||||||
|
frameSchedule.phaseEligible = false;
|
||||||
|
fbRes->SetSchedule(frameSchedule);
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
if (candidate.nbDirtyRects)
|
||||||
|
memcpy(m_previousDamage, candidate.dirtyRects,
|
||||||
|
candidate.nbDirtyRects * sizeof(*m_previousDamage));
|
||||||
|
m_previousDamageCount = candidate.nbDirtyRects;
|
||||||
|
CandidateDamageTail& tail = m_candidateDamageTail[candidateIndex];
|
||||||
|
if (tail.active && tail.ownerSequence == candidateSequence)
|
||||||
|
{
|
||||||
|
m_hasPendingDamage = tail.hasDamage;
|
||||||
|
m_pendingDamageCount = tail.nbDirtyRects;
|
||||||
|
if (tail.hasDamage && tail.nbDirtyRects)
|
||||||
|
memcpy(m_pendingDamage, tail.dirtyRects,
|
||||||
|
tail.nbDirtyRects * sizeof(*m_pendingDamage));
|
||||||
|
tail.ownerSequence = 0;
|
||||||
|
tail.active = false;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
|
||||||
|
const bool submitted = copySlot->Execute();
|
||||||
|
publishPending.Clear();
|
||||||
|
if (!submitted)
|
||||||
|
{
|
||||||
|
SetFullDamage();
|
||||||
|
AcquireSRWLockShared(&m_candidateLock);
|
||||||
|
const bool callbackPending =
|
||||||
|
candidate.state == CANDIDATE_PUBLISHING;
|
||||||
|
ReleaseSRWLockShared(&m_candidateLock);
|
||||||
|
if (callbackPending && !copySlot->HasSubmittedWork())
|
||||||
|
{
|
||||||
|
m_device->FailFrameBuffer(buffer.frameIndex);
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
}
|
||||||
|
m_device->ForceFrame();
|
||||||
|
SignalCandidateState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_device->CommitFrameBuffer(
|
||||||
|
buffer.frameIndex, schedule, periodic, deliveredToOwner);
|
||||||
|
|
||||||
|
unsigned superseded = 0;
|
||||||
|
AcquireSRWLockExclusive(&m_candidateLock);
|
||||||
|
for (FrameCandidate& ready : m_candidates)
|
||||||
|
if (ready.state == CANDIDATE_READY &&
|
||||||
|
ready.sequence < candidateSequence)
|
||||||
|
{
|
||||||
|
ready.state = CANDIDATE_FREE;
|
||||||
|
++superseded;
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&m_candidateLock);
|
||||||
|
for (unsigned i = 0; i < superseded; ++i)
|
||||||
|
m_device->FrameSuperseded();
|
||||||
|
SignalCandidateState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CHardwareFrameProcessor::Submit(const FrameSubmission& submission)
|
||||||
|
{
|
||||||
|
int selectedCandidate = AcquireCandidate(
|
||||||
|
submission.timingToken != 0, !submission.noImageUpdate);
|
||||||
|
while (selectedCandidate < 0 && submission.noImageUpdate)
|
||||||
|
{
|
||||||
|
HANDLE waitHandles[] =
|
||||||
|
{
|
||||||
|
m_terminateEvent,
|
||||||
|
m_candidateAvailableEvent.Get(),
|
||||||
|
};
|
||||||
|
const DWORD waitResult = WaitForMultipleObjects(
|
||||||
|
ARRAYSIZE(waitHandles), waitHandles, FALSE, INFINITE);
|
||||||
|
if (waitResult == WAIT_OBJECT_0)
|
||||||
|
return true;
|
||||||
|
if (waitResult != WAIT_OBJECT_0 + 1)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(GetLastError()),
|
||||||
|
"Failed while waiting for a frame candidate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedCandidate = AcquireCandidate(
|
||||||
|
submission.timingToken != 0, false);
|
||||||
|
}
|
||||||
|
if (selectedCandidate < 0)
|
||||||
|
{
|
||||||
|
m_device->FrameSuperseded();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const unsigned candidateIndex =
|
||||||
|
static_cast<unsigned>(selectedCandidate);
|
||||||
|
FrameCandidate& candidate = m_candidates[candidateIndex];
|
||||||
|
|
||||||
|
CFrameProcessorSharedLock pipelineLock(m_pipelineLock);
|
||||||
|
CPostProcessor& postProcessor = m_postProcessors[candidateIndex];
|
||||||
|
const D12FrameFormat& dstFormat = postProcessor.GetOutputFormat();
|
||||||
|
|
||||||
|
RECT currentDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||||
|
unsigned nbDirtyRects = 0;
|
||||||
|
AcquireSRWLockExclusive(&m_damageLock);
|
||||||
|
if (m_hasPendingDamage)
|
||||||
|
{
|
||||||
|
nbDirtyRects = m_pendingDamageCount;
|
||||||
|
if (nbDirtyRects)
|
||||||
|
memcpy(currentDirtyRects, m_pendingDamage,
|
||||||
|
nbDirtyRects * sizeof(*currentDirtyRects));
|
||||||
|
}
|
||||||
|
CandidateDamageTail& tail = m_candidateDamageTail[candidateIndex];
|
||||||
|
tail.ownerSequence = candidate.sequence;
|
||||||
|
tail.nbDirtyRects = 0;
|
||||||
|
tail.hasDamage = false;
|
||||||
|
tail.active = true;
|
||||||
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
|
|
||||||
|
CD3D12CommandSlot * copySlot = m_dx12->GetCopySlot(candidateIndex);
|
||||||
|
if (!copySlot)
|
||||||
|
{
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
DEBUG_ERROR("Failed to get a copy CommandSlot");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const uint64_t timingStart = submission.timingToken ?
|
||||||
|
CFrameScheduler::Nanotime() : 0;
|
||||||
|
|
||||||
|
ComPtr<ID3D12Resource> copySrcResource =
|
||||||
|
submission.source->GetRes();
|
||||||
|
CD3D12CommandSlot * computeSlot = nullptr;
|
||||||
|
if (postProcessor.HasActiveEffects())
|
||||||
|
{
|
||||||
|
computeSlot = m_dx12->GetComputeSlot(candidateIndex);
|
||||||
|
if (!computeSlot)
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
DEBUG_ERROR("Failed to get a compute CommandSlot");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!submission.source->Signal())
|
||||||
|
{
|
||||||
|
if (computeSlot)
|
||||||
|
computeSlot->Cancel();
|
||||||
|
copySlot->Cancel();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (computeSlot)
|
||||||
|
{
|
||||||
|
if (!submission.source->Sync(*computeSlot))
|
||||||
|
{
|
||||||
|
computeSlot->Cancel();
|
||||||
|
copySlot->Cancel();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
copySrcResource = postProcessor.Run(
|
||||||
|
computeSlot->GetGfxList(), copySrcResource,
|
||||||
|
currentDirtyRects, &nbDirtyRects);
|
||||||
|
if (!copySrcResource)
|
||||||
|
{
|
||||||
|
computeSlot->Cancel();
|
||||||
|
copySlot->Cancel();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
DEBUG_ERROR("Post processor returned no output resource");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!computeSlot->Execute())
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
m_dx12->WaitForIdle();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copySlot->WaitFor(*computeSlot))
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
m_dx12->WaitForIdle();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
DEBUG_ERROR("Failed to queue compute synchronization");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!submission.source->Sync(*copySlot))
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
DEBUG_ERROR("Failed to queue source synchronization");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFrameProcessorUtil::ClipDirtyRects(
|
||||||
|
currentDirtyRects, &nbDirtyRects,
|
||||||
|
dstFormat.width, dstFormat.height);
|
||||||
|
|
||||||
|
const size_t frameSize = postProcessor.GetOutputSize();
|
||||||
|
if (!EnsureCandidateResource(candidateIndex, frameSize))
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
if (computeSlot)
|
||||||
|
m_dx12->WaitForIdle();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate.srcFormat = submission.sourceFormat;
|
||||||
|
candidate.dstFormat = dstFormat;
|
||||||
|
candidate.nbDirtyRects = nbDirtyRects;
|
||||||
|
candidate.pitch = postProcessor.GetOutputPitch();
|
||||||
|
candidate.frameSize = frameSize;
|
||||||
|
candidate.captureTime = submission.captureTime;
|
||||||
|
candidate.postProcessStart = submission.postProcessStart;
|
||||||
|
candidate.prepareCopyStart = CFrameScheduler::Nanotime();
|
||||||
|
candidate.prepareReady = 0;
|
||||||
|
candidate.prepareGPUStart = 0;
|
||||||
|
candidate.prepareGPUEnd = 0;
|
||||||
|
candidate.timingStart = timingStart;
|
||||||
|
candidate.prepareTimingValid = false;
|
||||||
|
if (nbDirtyRects)
|
||||||
|
memcpy(candidate.dirtyRects, currentDirtyRects,
|
||||||
|
nbDirtyRects * sizeof(*candidate.dirtyRects));
|
||||||
|
candidate.timingEffectIndex = submission.timingEffectIndex;
|
||||||
|
candidate.timingToken = submission.timingToken;
|
||||||
|
|
||||||
|
copySlot->SetCompletionCallback(
|
||||||
|
&CandidateCompletionFunction, this, &candidate);
|
||||||
|
copySlot->BeginTiming();
|
||||||
|
postProcessor.CopyToCandidate(
|
||||||
|
copySlot->GetGfxList(), candidate.resource.Get(),
|
||||||
|
copySrcResource.Get());
|
||||||
|
copySlot->EndTiming();
|
||||||
|
|
||||||
|
if (!ExecuteCandidateCopy(copySlot))
|
||||||
|
{
|
||||||
|
if (!copySlot->HasSubmittedWork())
|
||||||
|
{
|
||||||
|
if (computeSlot)
|
||||||
|
m_dx12->WaitForIdle();
|
||||||
|
ReleaseCandidate(candidateIndex);
|
||||||
|
}
|
||||||
|
SetFullDamage();
|
||||||
|
m_device->ForceFrame();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
105
idd/LGIdd/CHardwareFrameProcessor.h
Normal file
105
idd/LGIdd/CHardwareFrameProcessor.h
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CFrameProcessor.h"
|
||||||
|
|
||||||
|
class CHardwareFrameProcessor final : public CFrameProcessor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum CandidateState
|
||||||
|
{
|
||||||
|
CANDIDATE_FREE,
|
||||||
|
CANDIDATE_PREPARING,
|
||||||
|
CANDIDATE_READY,
|
||||||
|
CANDIDATE_PUBLISHING,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FrameCandidate
|
||||||
|
{
|
||||||
|
CandidateState state = CANDIDATE_FREE;
|
||||||
|
ComPtr<ID3D12Resource> resource;
|
||||||
|
D12FrameFormat srcFormat = {};
|
||||||
|
D12FrameFormat dstFormat = {};
|
||||||
|
RECT dirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||||
|
unsigned nbDirtyRects = 0;
|
||||||
|
unsigned pitch = 0;
|
||||||
|
size_t frameSize = 0;
|
||||||
|
uint64_t sequence = 0;
|
||||||
|
uint64_t captureTime = 0;
|
||||||
|
uint64_t postProcessStart = 0;
|
||||||
|
uint64_t prepareCopyStart = 0;
|
||||||
|
uint64_t prepareReady = 0;
|
||||||
|
uint64_t prepareGPUStart = 0;
|
||||||
|
uint64_t prepareGPUEnd = 0;
|
||||||
|
uint64_t timingStart = 0;
|
||||||
|
unsigned timingEffectIndex = 0;
|
||||||
|
uint64_t timingToken = 0;
|
||||||
|
bool prepareTimingValid = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CandidateDamageTail
|
||||||
|
{
|
||||||
|
uint64_t ownerSequence = 0;
|
||||||
|
RECT dirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||||
|
unsigned nbDirtyRects = 0;
|
||||||
|
bool hasDamage = false;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
FrameCandidate m_candidates[LGMP_Q_FRAME_LEN];
|
||||||
|
CandidateDamageTail m_candidateDamageTail[LGMP_Q_FRAME_LEN];
|
||||||
|
mutable SRWLOCK m_candidateLock = SRWLOCK_INIT;
|
||||||
|
SRWLOCK m_copySubmitLock = SRWLOCK_INIT;
|
||||||
|
uint64_t m_candidateSequence = 0;
|
||||||
|
bool m_publishPending = false;
|
||||||
|
Wrappers::Event m_candidateAvailableEvent;
|
||||||
|
Wrappers::Event m_copySubmitEvent;
|
||||||
|
|
||||||
|
static void CandidateCompletionFunction(
|
||||||
|
CD3D12CommandSlot * slot, bool result, void * param1, void * param2);
|
||||||
|
static void CompletionFunction(
|
||||||
|
CD3D12CommandSlot * slot, bool result, void * param1, void * param2);
|
||||||
|
int AcquireCandidate(bool exclusiveSample, bool allowSupersede);
|
||||||
|
void ReleaseCandidate(unsigned candidateIndex);
|
||||||
|
bool EnsureCandidateResource(unsigned candidateIndex, size_t frameSize);
|
||||||
|
void ResetCandidates();
|
||||||
|
void SignalCandidateState();
|
||||||
|
bool ExecuteCandidateCopy(CD3D12CommandSlot * copySlot);
|
||||||
|
void AccumulateDamageLocked(
|
||||||
|
const RECT dirtyRects[], unsigned count) override;
|
||||||
|
void SetFullDamageLocked() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CHardwareFrameProcessor(CIndirectDeviceContext * device,
|
||||||
|
std::shared_ptr<CD3D12Device> dx12,
|
||||||
|
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
|
||||||
|
SRWLOCK * pipelineLock, HANDLE terminateEvent);
|
||||||
|
|
||||||
|
bool IsValid() const override;
|
||||||
|
bool Submit(const FrameSubmission& submission) override;
|
||||||
|
bool HasReadyFrame() const override;
|
||||||
|
bool Publish(const CFrameScheduler::Schedule& schedule,
|
||||||
|
bool periodic, uint64_t publishStart) override;
|
||||||
|
bool UsesCadence() const override { return true; }
|
||||||
|
void Reset() override;
|
||||||
|
void ResetPipeline() override;
|
||||||
|
};
|
||||||
@@ -2562,7 +2562,7 @@ std::shared_ptr<const D12ColorTransform>
|
|||||||
CIndirectDeviceContext::GetColorTransform() const
|
CIndirectDeviceContext::GetColorTransform() const
|
||||||
{
|
{
|
||||||
AcquireSRWLockShared(&m_colorTransformLock);
|
AcquireSRWLockShared(&m_colorTransformLock);
|
||||||
auto transform = m_colorTransform;
|
std::shared_ptr<const D12ColorTransform> transform = m_colorTransform;
|
||||||
ReleaseSRWLockShared(&m_colorTransformLock);
|
ReleaseSRWLockShared(&m_colorTransformLock);
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|||||||
364
idd/LGIdd/CSoftwareFrameProcessor.cpp
Normal file
364
idd/LGIdd/CSoftwareFrameProcessor.cpp
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
/**
|
||||||
|
* 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 "CSoftwareFrameProcessor.h"
|
||||||
|
#include "CFrameProcessorUtil.h"
|
||||||
|
#include "CDebug.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
CSoftwareFrameProcessor::CSoftwareFrameProcessor(
|
||||||
|
CIndirectDeviceContext * device, std::shared_ptr<CD3D12Device> dx12,
|
||||||
|
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
|
||||||
|
SRWLOCK * pipelineLock, HANDLE terminateEvent) :
|
||||||
|
CFrameProcessor(device, std::move(dx12), postProcessors,
|
||||||
|
pipelineLock, terminateEvent),
|
||||||
|
m_directTexture(
|
||||||
|
!m_dx12->IsIndirectCopy() && m_dx12->CanUseIVSHMEMTexture())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSoftwareFrameProcessor::CompletionFunction(
|
||||||
|
CD3D12CommandSlot * slot, bool result, void * param1, void * param2)
|
||||||
|
{
|
||||||
|
auto processor = static_cast<CSoftwareFrameProcessor *>(param1);
|
||||||
|
auto fbRes = static_cast<CFrameBufferResource *>(param2);
|
||||||
|
fbRes->MarkCompletion();
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
processor->m_device->FailFrameBuffer(fbRes->GetFrameIndex());
|
||||||
|
processor->SetFullDamage();
|
||||||
|
processor->m_device->ForceFrame();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t indirectCopyTime = 0;
|
||||||
|
if (processor->m_dx12->IsIndirectCopy())
|
||||||
|
{
|
||||||
|
const uint64_t indirectCopyStart = CFrameScheduler::Nanotime();
|
||||||
|
if (fbRes->IsFullCopy())
|
||||||
|
processor->m_device->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;
|
||||||
|
processor->m_device->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);
|
||||||
|
|
||||||
|
processor->m_device->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;
|
||||||
|
|
||||||
|
processor->m_device->SetFrameTiming(fbRes->GetFrameIndex(),
|
||||||
|
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime, 0,
|
||||||
|
fbRes->GetSchedule(), publishedAt);
|
||||||
|
processor->m_device->CompleteFrameBuffer(fbRes->GetFrameIndex(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
|
||||||
|
{
|
||||||
|
CFrameProcessorSharedLock 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_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_device->GetMaxFrameSize() / texturePitch)
|
||||||
|
{
|
||||||
|
pitch = texturePitch;
|
||||||
|
frameSize = (size_t)pitch * textureDesc.Height;
|
||||||
|
textureDescPtr = &textureDesc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_directTexture = false;
|
||||||
|
DEBUG_WARN("IVSHMEM texture layout does not fit the framebuffer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_directTexture)
|
||||||
|
{
|
||||||
|
m_directTexture = false;
|
||||||
|
DEBUG_WARN("Post-processor output cannot use an IVSHMEM texture");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pitch || !frameSize || frameSize > m_device->GetMaxFrameSize())
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Software frame does not fit in shared memory");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submission.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_device->GetPublishTarget(CFrameScheduler::Nanotime(),
|
||||||
|
ignoredTarget, commitSchedule, ignoredPeriodic, ignoredRepublish);
|
||||||
|
deliverySchedule = commitSchedule;
|
||||||
|
deliverySchedule.deliveryDeadlineSerial = 0;
|
||||||
|
deliverySchedule.phaseEligible = false;
|
||||||
|
|
||||||
|
m_device->ProcessFrameQueue();
|
||||||
|
if (!m_device->FrameBufferAvailable(
|
||||||
|
deliverySchedule, submission.noImageUpdate))
|
||||||
|
{
|
||||||
|
if (!submission.noImageUpdate)
|
||||||
|
{
|
||||||
|
m_device->FrameSuperseded();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WaitForSingleObject(m_terminateEvent, 1) == WAIT_OBJECT_0)
|
||||||
|
return true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
copySlot = m_dx12->GetCopySlot();
|
||||||
|
if (!copySlot)
|
||||||
|
{
|
||||||
|
if (!submission.noImageUpdate)
|
||||||
|
{
|
||||||
|
m_device->FrameSuperseded();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WaitForSingleObject(m_terminateEvent, 1) == WAIT_OBJECT_0)
|
||||||
|
return true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasDamage = TakePendingDamage(currentDirtyRects, &nbDirtyRects);
|
||||||
|
CFrameProcessorUtil::ClipDirtyRects(
|
||||||
|
currentDirtyRects, &nbDirtyRects,
|
||||||
|
dstFormat.width, dstFormat.height);
|
||||||
|
buffer = m_device->PrepareFrameBuffer(
|
||||||
|
pitch, submission.sourceFormat, dstFormat,
|
||||||
|
currentDirtyRects, nbDirtyRects, deliverySchedule,
|
||||||
|
submission.noImageUpdate);
|
||||||
|
if (!buffer.mem)
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
RestorePendingDamage(
|
||||||
|
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||||
|
if (!submission.noImageUpdate)
|
||||||
|
{
|
||||||
|
m_device->FrameSuperseded();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WaitForSingleObject(m_terminateEvent, 1) == WAIT_OBJECT_0)
|
||||||
|
return true;
|
||||||
|
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_device->AbortFrameBuffer(buffer.frameIndex);
|
||||||
|
RestorePendingDamage(
|
||||||
|
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||||
|
DEBUG_ERROR_HR(deviceStatus,
|
||||||
|
"D3D12 device removed while creating an IVSHMEM texture");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_directTexture = false;
|
||||||
|
textureDescPtr = nullptr;
|
||||||
|
DEBUG_WARN(
|
||||||
|
"IVSHMEM textures unavailable; using a direct buffer copy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fbRes)
|
||||||
|
fbRes = m_frameBuffers.Get(buffer, frameSize);
|
||||||
|
if (!fbRes)
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
m_device->AbortFrameBuffer(buffer.frameIndex);
|
||||||
|
RestorePendingDamage(
|
||||||
|
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||||
|
DEBUG_ERROR("Failed to get a framebuffer for software capture");
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!submission.source->Signal() ||
|
||||||
|
!submission.source->Sync(*copySlot))
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
m_device->AbortFrameBuffer(buffer.frameIndex);
|
||||||
|
RestorePendingDamage(
|
||||||
|
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||||
|
SetFullDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT previousDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||||
|
unsigned nbPreviousDirtyRects = 0;
|
||||||
|
GetPreviousDamage(previousDirtyRects, &nbPreviousDirtyRects);
|
||||||
|
|
||||||
|
RECT copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {};
|
||||||
|
unsigned nbCopyDirtyRects = 0;
|
||||||
|
const bool fullCopy = CFrameProcessorUtil::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(submission.captureTime,
|
||||||
|
submission.postProcessStart, copyStart);
|
||||||
|
fbRes->SetSchedule(deliverySchedule);
|
||||||
|
fbRes->SetCopyDamage(copyDirtyRects, nbCopyDirtyRects,
|
||||||
|
fullCopy, pitch, bytesPerPixel);
|
||||||
|
fbRes->ResetCompletion();
|
||||||
|
copySlot->SetCompletionCallback(
|
||||||
|
&CompletionFunction, this, fbRes);
|
||||||
|
copySlot->BeginTiming();
|
||||||
|
postProcessor.CopyToFrameBuffer(copySlot->GetGfxList(),
|
||||||
|
fbRes->Get().Get(), submission.source->GetRes().Get(),
|
||||||
|
copyDirtyRects, nbCopyDirtyRects, fullCopy);
|
||||||
|
copySlot->EndTiming();
|
||||||
|
|
||||||
|
bool deliveredToOwner;
|
||||||
|
if (!m_device->PublishFrameBuffer(
|
||||||
|
buffer.frameIndex, deliverySchedule, deliveredToOwner))
|
||||||
|
{
|
||||||
|
copySlot->Cancel();
|
||||||
|
m_device->AbortFrameBuffer(buffer.frameIndex);
|
||||||
|
RestorePendingDamage(
|
||||||
|
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||||
|
if (!submission.noImageUpdate)
|
||||||
|
{
|
||||||
|
m_device->FrameSuperseded();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WaitForSingleObject(m_terminateEvent, 1) == WAIT_OBJECT_0)
|
||||||
|
return true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommitDamage(currentDirtyRects, nbDirtyRects);
|
||||||
|
if (!copySlot->Execute())
|
||||||
|
{
|
||||||
|
const bool submittedWork = copySlot->HasSubmittedWork();
|
||||||
|
const bool completionHandled = fbRes->CompletionHandled();
|
||||||
|
if (!submittedWork && !completionHandled)
|
||||||
|
m_device->FailFrameBuffer(buffer.frameIndex);
|
||||||
|
RestorePendingDamage(
|
||||||
|
currentDirtyRects, nbDirtyRects, hasDamage);
|
||||||
|
if (!submittedWork && !completionHandled)
|
||||||
|
{
|
||||||
|
SetFullDamage();
|
||||||
|
m_device->ForceFrame();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_device->CommitFrameBuffer(
|
||||||
|
buffer.frameIndex, commitSchedule, false, deliveredToOwner);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
idd/LGIdd/CSoftwareFrameProcessor.h
Normal file
46
idd/LGIdd/CSoftwareFrameProcessor.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CFrameProcessor.h"
|
||||||
|
|
||||||
|
class CSoftwareFrameProcessor final : public CFrameProcessor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool m_directTexture;
|
||||||
|
|
||||||
|
static void CompletionFunction(
|
||||||
|
CD3D12CommandSlot * slot, bool result, void * param1, void * param2);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CSoftwareFrameProcessor(CIndirectDeviceContext * device,
|
||||||
|
std::shared_ptr<CD3D12Device> dx12,
|
||||||
|
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
|
||||||
|
SRWLOCK * pipelineLock, HANDLE terminateEvent);
|
||||||
|
|
||||||
|
bool Submit(const FrameSubmission& submission) override;
|
||||||
|
bool HasReadyFrame() const override { return false; }
|
||||||
|
bool Publish(const CFrameScheduler::Schedule&, bool, uint64_t) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool UsesCadence() const override { return false; }
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@
|
|||||||
#include "CD3D12Device.h"
|
#include "CD3D12Device.h"
|
||||||
#include "CIndirectDeviceContext.h"
|
#include "CIndirectDeviceContext.h"
|
||||||
#include "CInteropResourcePool.h"
|
#include "CInteropResourcePool.h"
|
||||||
#include "CFrameBufferPool.h"
|
#include "CFrameProcessor.h"
|
||||||
#include "CPostProcessor.h"
|
#include "CPostProcessor.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@@ -35,8 +35,6 @@
|
|||||||
|
|
||||||
using namespace Microsoft::WRL;
|
using namespace Microsoft::WRL;
|
||||||
|
|
||||||
#define STAGING_TEXTURES 3
|
|
||||||
|
|
||||||
class CIndirectMonitorContext;
|
class CIndirectMonitorContext;
|
||||||
|
|
||||||
class CSwapChainProcessor
|
class CSwapChainProcessor
|
||||||
@@ -52,69 +50,14 @@ private:
|
|||||||
std::shared_ptr<CD3D12Device> m_dx12Device;
|
std::shared_ptr<CD3D12Device> m_dx12Device;
|
||||||
HANDLE m_newFrameEvent;
|
HANDLE m_newFrameEvent;
|
||||||
|
|
||||||
CInteropResourcePool m_resPool;
|
CInteropResourcePool m_resPool;
|
||||||
CFrameBufferPool m_fbPool;
|
CPostProcessor m_postProcessors[LGMP_Q_FRAME_LEN];
|
||||||
CPostProcessor m_postProcessors[LGMP_Q_FRAME_LEN];
|
std::unique_ptr<CFrameProcessor> m_frameProcessor;
|
||||||
|
|
||||||
enum CandidateState
|
|
||||||
{
|
|
||||||
CANDIDATE_FREE,
|
|
||||||
CANDIDATE_PREPARING,
|
|
||||||
CANDIDATE_READY,
|
|
||||||
CANDIDATE_PUBLISHING,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FrameCandidate
|
|
||||||
{
|
|
||||||
CandidateState state = CANDIDATE_FREE;
|
|
||||||
ComPtr<ID3D12Resource> resource;
|
|
||||||
D12FrameFormat srcFormat = {};
|
|
||||||
D12FrameFormat dstFormat = {};
|
|
||||||
RECT dirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
|
||||||
unsigned nbDirtyRects = 0;
|
|
||||||
unsigned pitch = 0;
|
|
||||||
size_t frameSize = 0;
|
|
||||||
uint64_t sequence = 0;
|
|
||||||
uint64_t captureTime = 0;
|
|
||||||
uint64_t postProcessStart = 0;
|
|
||||||
uint64_t prepareCopyStart = 0;
|
|
||||||
uint64_t prepareReady = 0;
|
|
||||||
uint64_t prepareGPUStart = 0;
|
|
||||||
uint64_t prepareGPUEnd = 0;
|
|
||||||
uint64_t timingStart = 0;
|
|
||||||
unsigned timingEffectIndex = 0;
|
|
||||||
uint64_t timingToken = 0;
|
|
||||||
bool prepareTimingValid = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CandidateDamageTail
|
|
||||||
{
|
|
||||||
uint64_t ownerSequence = 0;
|
|
||||||
RECT dirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
|
||||||
unsigned nbDirtyRects = 0;
|
|
||||||
bool hasDamage = false;
|
|
||||||
bool active = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// An active tail records only damage received after its candidate snapshot.
|
|
||||||
FrameCandidate m_candidates[LGMP_Q_FRAME_LEN];
|
|
||||||
CandidateDamageTail m_candidateDamageTail[LGMP_Q_FRAME_LEN];
|
|
||||||
SRWLOCK m_candidateLock = SRWLOCK_INIT;
|
|
||||||
SRWLOCK m_damageLock = SRWLOCK_INIT;
|
|
||||||
// Reconfiguration is exclusive while per-candidate recording is shared.
|
// Reconfiguration is exclusive while per-candidate recording is shared.
|
||||||
SRWLOCK m_pipelineLock = SRWLOCK_INIT;
|
SRWLOCK m_pipelineLock = SRWLOCK_INIT;
|
||||||
// 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;
|
|
||||||
bool m_directSoftwareTexture = false;
|
|
||||||
|
|
||||||
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[3];
|
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[3];
|
||||||
Wrappers::Event m_terminateEvent;
|
Wrappers::Event m_terminateEvent;
|
||||||
Wrappers::Event m_candidateEvent;
|
|
||||||
Wrappers::Event m_candidateAvailableEvent;
|
|
||||||
Wrappers::Event m_copySubmitEvent;
|
|
||||||
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_publishTimer;
|
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_publishTimer;
|
||||||
|
|
||||||
Wrappers::Event m_cursorDataEvent;
|
Wrappers::Event m_cursorDataEvent;
|
||||||
@@ -122,20 +65,6 @@ private:
|
|||||||
DWORD m_lastShapeId = 0;
|
DWORD m_lastShapeId = 0;
|
||||||
std::atomic<UINT> m_sdrWhiteLevel { KVMFR_SDR_WHITE_LEVEL_DEFAULT };
|
std::atomic<UINT> m_sdrWhiteLevel { KVMFR_SDR_WHITE_LEVEL_DEFAULT };
|
||||||
|
|
||||||
// Logical output-space damage from the previous published frame. The
|
|
||||||
// shared-memory frame buffers alternate, so this must be copied along with
|
|
||||||
// the current damage to bring the older target buffer up to date.
|
|
||||||
RECT m_dirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
|
||||||
unsigned m_nbDirtyRects = 0;
|
|
||||||
|
|
||||||
// Source-space damage accumulated since the last published frame. Frames
|
|
||||||
// can be dropped while the LGMP queue is full, but their damage must be
|
|
||||||
// included in the next frame sent to the client. A count of zero represents
|
|
||||||
// full-frame damage when m_hasPendingDamage is set.
|
|
||||||
RECT m_pendingDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
|
||||||
unsigned m_nbPendingDirtyRects = 0;
|
|
||||||
bool m_hasPendingDamage = true;
|
|
||||||
|
|
||||||
#ifdef HAS_IDDCX_110
|
#ifdef HAS_IDDCX_110
|
||||||
// The per-frame metadata stream can select the monitor default, provide a
|
// The per-frame metadata stream can select the monitor default, provide a
|
||||||
// replacement block, or retain the selection from the previous frame.
|
// replacement block, or retain the selection from the previous frame.
|
||||||
@@ -151,43 +80,14 @@ private:
|
|||||||
|
|
||||||
static DWORD CALLBACK _PublisherThread(LPVOID arg);
|
static DWORD CALLBACK _PublisherThread(LPVOID arg);
|
||||||
void PublisherThread();
|
void PublisherThread();
|
||||||
bool PublishNewestCandidate(
|
|
||||||
const CFrameScheduler::Schedule& schedule, bool periodic,
|
|
||||||
uint64_t publishStart);
|
|
||||||
bool HasReadyCandidate();
|
|
||||||
int AcquireCandidate(bool exclusiveSample, bool allowSupersede);
|
|
||||||
void ReleaseCandidate(unsigned candidateIndex);
|
|
||||||
bool EnsureCandidateResource(
|
|
||||||
unsigned candidateIndex, size_t frameSize);
|
|
||||||
void ResetCandidates();
|
|
||||||
void SignalCandidateState();
|
|
||||||
bool ExecuteCandidateCopy(CD3D12CommandSlot * copySlot);
|
|
||||||
|
|
||||||
static DWORD CALLBACK _CursorThread(LPVOID arg);
|
static DWORD CALLBACK _CursorThread(LPVOID arg);
|
||||||
bool QueryHWCursor();
|
bool QueryHWCursor();
|
||||||
void CursorThread();
|
void CursorThread();
|
||||||
|
|
||||||
static void CompletionFunction(
|
|
||||||
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
|
#ifdef HAS_IDDCX_110
|
||||||
void UpdateHDRMetadata(const IDDCX_METADATA2& metadata);
|
void UpdateHDRMetadata(const IDDCX_METADATA2& metadata);
|
||||||
#endif
|
#endif
|
||||||
bool GetContentHDRMetadata(D12FrameFormat& format) const;
|
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,
|
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer,
|
||||||
unsigned dirtyRectCount, unsigned moveRegionCount,
|
unsigned dirtyRectCount, unsigned moveRegionCount,
|
||||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel,
|
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel,
|
||||||
@@ -200,7 +100,4 @@ public:
|
|||||||
HANDLE newFrameEvent);
|
HANDLE newFrameEvent);
|
||||||
~CSwapChainProcessor();
|
~CSwapChainProcessor();
|
||||||
bool Start();
|
bool Start();
|
||||||
|
|
||||||
CIndirectDeviceContext * GetDevice() { return m_devContext; }
|
|
||||||
std::shared_ptr<CD3D12Device> GetD3D12Device() { return m_dx12Device; }
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,7 +29,10 @@
|
|||||||
<ClCompile Include="CEdid.cpp" />
|
<ClCompile Include="CEdid.cpp" />
|
||||||
<ClCompile Include="CFrameBufferPool.cpp" />
|
<ClCompile Include="CFrameBufferPool.cpp" />
|
||||||
<ClCompile Include="CFrameBufferResource.cpp" />
|
<ClCompile Include="CFrameBufferResource.cpp" />
|
||||||
|
<ClCompile Include="CFrameProcessor.cpp" />
|
||||||
|
<ClCompile Include="CFrameProcessorUtil.cpp" />
|
||||||
<ClCompile Include="CFrameScheduler.cpp" />
|
<ClCompile Include="CFrameScheduler.cpp" />
|
||||||
|
<ClCompile Include="CHardwareFrameProcessor.cpp" />
|
||||||
<ClCompile Include="CIndirectDeviceContext.cpp" />
|
<ClCompile Include="CIndirectDeviceContext.cpp" />
|
||||||
<ClCompile Include="CIndirectMonitorContext.cpp" />
|
<ClCompile Include="CIndirectMonitorContext.cpp" />
|
||||||
<ClCompile Include="CInteropResourcePool.cpp" />
|
<ClCompile Include="CInteropResourcePool.cpp" />
|
||||||
@@ -44,6 +47,7 @@
|
|||||||
<ClCompile Include="effect\CHDR16to10Effect.cpp" />
|
<ClCompile Include="effect\CHDR16to10Effect.cpp" />
|
||||||
<ClCompile Include="effect\CRGB24Effect.cpp" />
|
<ClCompile Include="effect\CRGB24Effect.cpp" />
|
||||||
<ClCompile Include="CSettings.cpp" />
|
<ClCompile Include="CSettings.cpp" />
|
||||||
|
<ClCompile Include="CSoftwareFrameProcessor.cpp" />
|
||||||
<ClCompile Include="CSwapChainProcessor.cpp" />
|
<ClCompile Include="CSwapChainProcessor.cpp" />
|
||||||
<ClCompile Include="CD3D12Device.cpp" />
|
<ClCompile Include="CD3D12Device.cpp" />
|
||||||
<ClCompile Include="Device.cpp" />
|
<ClCompile Include="Device.cpp" />
|
||||||
@@ -56,7 +60,10 @@
|
|||||||
<ClInclude Include="CEdid.h" />
|
<ClInclude Include="CEdid.h" />
|
||||||
<ClInclude Include="CFrameBufferPool.h" />
|
<ClInclude Include="CFrameBufferPool.h" />
|
||||||
<ClInclude Include="CFrameBufferResource.h" />
|
<ClInclude Include="CFrameBufferResource.h" />
|
||||||
|
<ClInclude Include="CFrameProcessor.h" />
|
||||||
|
<ClInclude Include="CFrameProcessorUtil.h" />
|
||||||
<ClInclude Include="CFrameScheduler.h" />
|
<ClInclude Include="CFrameScheduler.h" />
|
||||||
|
<ClInclude Include="CHardwareFrameProcessor.h" />
|
||||||
<ClInclude Include="CIndirectMonitorContext.h" />
|
<ClInclude Include="CIndirectMonitorContext.h" />
|
||||||
<ClInclude Include="CInteropResourcePool.h" />
|
<ClInclude Include="CInteropResourcePool.h" />
|
||||||
<ClInclude Include="CInteropResource.h" />
|
<ClInclude Include="CInteropResource.h" />
|
||||||
@@ -70,6 +77,7 @@
|
|||||||
<ClInclude Include="effect\CHDR16to10Effect.h" />
|
<ClInclude Include="effect\CHDR16to10Effect.h" />
|
||||||
<ClInclude Include="effect\CRGB24Effect.h" />
|
<ClInclude Include="effect\CRGB24Effect.h" />
|
||||||
<ClInclude Include="CSettings.h" />
|
<ClInclude Include="CSettings.h" />
|
||||||
|
<ClInclude Include="CSoftwareFrameProcessor.h" />
|
||||||
<ClInclude Include="CSwapChainProcessor.h" />
|
<ClInclude Include="CSwapChainProcessor.h" />
|
||||||
<ClInclude Include="CD3D12Device.h" />
|
<ClInclude Include="CD3D12Device.h" />
|
||||||
<ClInclude Include="Device.h" />
|
<ClInclude Include="Device.h" />
|
||||||
|
|||||||
@@ -82,6 +82,15 @@
|
|||||||
<ClInclude Include="CFrameBufferPool.h">
|
<ClInclude Include="CFrameBufferPool.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="CFrameProcessor.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="CFrameProcessorUtil.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="CHardwareFrameProcessor.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="CFrameScheduler.h">
|
<ClInclude Include="CFrameScheduler.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -97,6 +106,9 @@
|
|||||||
<ClInclude Include="CPostProcessor.h">
|
<ClInclude Include="CPostProcessor.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="CSoftwareFrameProcessor.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="effect\CColorTransformEffect.h">
|
<ClInclude Include="effect\CColorTransformEffect.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -161,6 +173,15 @@
|
|||||||
<ClCompile Include="CFrameBufferPool.cpp">
|
<ClCompile Include="CFrameBufferPool.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="CFrameProcessor.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="CFrameProcessorUtil.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="CHardwareFrameProcessor.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
|
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
|
||||||
<ClCompile Include="CPipeServer.cpp">
|
<ClCompile Include="CPipeServer.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
@@ -172,6 +193,9 @@
|
|||||||
<ClCompile Include="CPostProcessor.cpp">
|
<ClCompile Include="CPostProcessor.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="CSoftwareFrameProcessor.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="effect\CColorTransformEffect.cpp">
|
<ClCompile Include="effect\CColorTransformEffect.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
Reference in New Issue
Block a user