mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-04 06:12:04 +00:00
[idd] select linear RGB24 packing automatically
Replace the texture-shaped RGB24 output with a linear raw buffer and keep its packing, damage translation, and buffer copies inside the RGB24 effect. Benchmark full-frame native and packed processing and retain the faster path for each source format. Preserve logical damage rectangles for client updates and alternating framebuffer repair. Return compute outputs to COMMON for COPY queue handoff and refresh cached framebuffer sizes when switching packed and native layouts.
This commit is contained in:
@@ -41,13 +41,9 @@ CFrameBufferResource * CFrameBufferPool::Get(
|
|||||||
if (buffer.frameIndex > ARRAYSIZE(m_buffers) - 1)
|
if (buffer.frameIndex > ARRAYSIZE(m_buffers) - 1)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
CFrameBufferResource* fbr = &m_buffers[buffer.frameIndex];
|
CFrameBufferResource * fbr = &m_buffers[buffer.frameIndex];
|
||||||
if (!fbr->IsValid() || fbr->GetBase() != buffer.mem || fbr->GetSize() < minSize)
|
if (!fbr->Init(m_swapChain, buffer.frameIndex, buffer.mem, minSize))
|
||||||
{
|
return nullptr;
|
||||||
fbr->Reset();
|
|
||||||
if (!fbr->Init(m_swapChain, buffer.frameIndex, buffer.mem, minSize))
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fbr;
|
return fbr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameI
|
|||||||
|
|
||||||
if (size > swapChain->GetDevice()->GetMaxFrameSize())
|
if (size > swapChain->GetDevice()->GetMaxFrameSize())
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Frame size of %lu is too large to fit in available shared ram");
|
DEBUG_ERROR("Frame size of %llu is too large to fit in shared ram",
|
||||||
|
(unsigned long long)size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameI
|
|||||||
swapChain->GetD3D12Device()->GetHeap().Get(),
|
swapChain->GetD3D12Device()->GetHeap().Get(),
|
||||||
(uintptr_t)base - (uintptr_t)swapChain->GetDevice()->GetIVSHMEM().GetMem(),
|
(uintptr_t)base - (uintptr_t)swapChain->GetDevice()->GetIVSHMEM().GetMem(),
|
||||||
&desc,
|
&desc,
|
||||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
D3D12_RESOURCE_STATE_COMMON,
|
||||||
NULL,
|
NULL,
|
||||||
IID_PPV_ARGS(&m_res)
|
IID_PPV_ARGS(&m_res)
|
||||||
);
|
);
|
||||||
@@ -115,7 +116,6 @@ bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameI
|
|||||||
m_base = base;
|
m_base = base;
|
||||||
m_size = size;
|
m_size = size;
|
||||||
m_frameSize = size;
|
m_frameSize = size;
|
||||||
m_valid = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,5 +131,4 @@ void CFrameBufferResource::Reset()
|
|||||||
m_size = 0;
|
m_size = 0;
|
||||||
m_frameSize = 0;
|
m_frameSize = 0;
|
||||||
m_res.Reset();
|
m_res.Reset();
|
||||||
m_valid = false;
|
|
||||||
}
|
}
|
||||||
@@ -33,25 +33,24 @@ using namespace Microsoft::WRL;
|
|||||||
class CFrameBufferResource
|
class CFrameBufferResource
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
bool m_valid = false;
|
unsigned m_frameIndex = 0;
|
||||||
unsigned m_frameIndex = 0;
|
uint8_t * m_base = nullptr;
|
||||||
uint8_t * m_base = nullptr;
|
size_t m_size = 0;
|
||||||
size_t m_size = 0;
|
size_t m_frameSize = 0;
|
||||||
size_t m_frameSize = 0;
|
uint64_t m_captureTime = 0;
|
||||||
uint64_t m_captureTime = 0;
|
uint64_t m_postProcessStart = 0;
|
||||||
uint64_t m_postProcessStart = 0;
|
uint64_t m_copyStart = 0;
|
||||||
uint64_t m_copyStart = 0;
|
unsigned m_timingEffectIndex = 0;
|
||||||
|
uint64_t m_timingToken = 0;
|
||||||
|
bool m_fullCopy = false;
|
||||||
ComPtr<ID3D12Resource> m_res;
|
ComPtr<ID3D12Resource> m_res;
|
||||||
void * m_map = nullptr;
|
void * m_map = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size);
|
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
bool IsValid() { return m_valid; }
|
|
||||||
unsigned GetFrameIndex() { return m_frameIndex; }
|
unsigned GetFrameIndex() { return m_frameIndex; }
|
||||||
uint8_t * GetBase() { return m_base; }
|
|
||||||
size_t GetSize() { return m_size; }
|
|
||||||
size_t GetFrameSize() { return m_frameSize; }
|
size_t GetFrameSize() { return m_frameSize; }
|
||||||
void * GetMap() { return m_map; }
|
void * GetMap() { return m_map; }
|
||||||
|
|
||||||
@@ -66,5 +65,16 @@ class CFrameBufferResource
|
|||||||
uint64_t GetPostProcessStart() const { return m_postProcessStart; }
|
uint64_t GetPostProcessStart() const { return m_postProcessStart; }
|
||||||
uint64_t GetCopyStart () const { return m_copyStart; }
|
uint64_t GetCopyStart () const { return m_copyStart; }
|
||||||
|
|
||||||
|
void SetPostProcessSample(
|
||||||
|
unsigned effectIndex, uint64_t token, bool fullCopy)
|
||||||
|
{
|
||||||
|
m_timingEffectIndex = effectIndex;
|
||||||
|
m_timingToken = token;
|
||||||
|
m_fullCopy = fullCopy;
|
||||||
|
}
|
||||||
|
unsigned GetTimingEffectIndex() const { return m_timingEffectIndex; }
|
||||||
|
uint64_t GetTimingToken () const { return m_timingToken; }
|
||||||
|
bool IsFullCopy () const { return m_fullCopy; }
|
||||||
|
|
||||||
ComPtr<ID3D12Resource> Get() { return m_res; }
|
ComPtr<ID3D12Resource> Get() { return m_res; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1249,6 +1249,11 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
|||||||
{
|
{
|
||||||
PreparedFrameBuffer result = {};
|
PreparedFrameBuffer result = {};
|
||||||
|
|
||||||
|
const unsigned dataWidth = dstFormat.dataWidth ?
|
||||||
|
dstFormat.dataWidth : (unsigned)dstFormat.desc.Width;
|
||||||
|
const unsigned dataHeight = dstFormat.dataHeight ?
|
||||||
|
dstFormat.dataHeight : dstFormat.desc.Height;
|
||||||
|
|
||||||
if (!FrameBufferAvailable())
|
if (!FrameBufferAvailable())
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@@ -1268,17 +1273,21 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
|||||||
expected, true, std::memory_order_acq_rel))
|
expected, true, std::memory_order_acq_rel))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
if (m_width != dstFormat.desc.Width ||
|
if (m_width != dataWidth ||
|
||||||
m_height != dstFormat.desc.Height ||
|
m_height != dataHeight ||
|
||||||
m_pitch != pitch ||
|
m_frameWidth != dstFormat.width ||
|
||||||
m_format != dstFormat.desc.Format ||
|
m_frameHeight != dstFormat.height ||
|
||||||
m_frameType != dstFormat.format)
|
m_pitch != pitch ||
|
||||||
|
m_format != dstFormat.desc.Format ||
|
||||||
|
m_frameType != dstFormat.format)
|
||||||
{
|
{
|
||||||
m_width = (unsigned)dstFormat.desc.Width;
|
m_width = dataWidth;
|
||||||
m_height = dstFormat.desc.Height;
|
m_height = dataHeight;
|
||||||
m_format = dstFormat.desc.Format;
|
m_frameWidth = dstFormat.width;
|
||||||
m_frameType = dstFormat.format;
|
m_frameHeight = dstFormat.height;
|
||||||
m_pitch = pitch;
|
m_pitch = pitch;
|
||||||
|
m_format = dstFormat.desc.Format;
|
||||||
|
m_frameType = dstFormat.format;
|
||||||
++m_formatVer;
|
++m_formatVer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1326,15 +1335,15 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
|||||||
(dstFormat.hdrPQ ? FRAME_FLAG_HDR_PQ : 0) |
|
(dstFormat.hdrPQ ? FRAME_FLAG_HDR_PQ : 0) |
|
||||||
(dstFormat.hdrMetadata ? FRAME_FLAG_HDR_METADATA : 0);
|
(dstFormat.hdrMetadata ? FRAME_FLAG_HDR_METADATA : 0);
|
||||||
|
|
||||||
if (maxRows < dstFormat.desc.Height)
|
if (maxRows < dataHeight)
|
||||||
flags |= FRAME_FLAG_TRUNCATED;
|
flags |= FRAME_FLAG_TRUNCATED;
|
||||||
|
|
||||||
fi->formatVer = m_formatVer;
|
fi->formatVer = m_formatVer;
|
||||||
fi->frameSerial = m_frameSerial++;
|
fi->frameSerial = m_frameSerial++;
|
||||||
fi->screenWidth = srcFormat.width;
|
fi->screenWidth = srcFormat.width;
|
||||||
fi->screenHeight = srcFormat.height;
|
fi->screenHeight = srcFormat.height;
|
||||||
fi->dataWidth = (unsigned)dstFormat.desc.Width;
|
fi->dataWidth = dataWidth;
|
||||||
fi->dataHeight = min(maxRows, dstFormat.desc.Height);
|
fi->dataHeight = min(maxRows, dataHeight);
|
||||||
fi->frameWidth = dstFormat.width;
|
fi->frameWidth = dstFormat.width;
|
||||||
fi->frameHeight = dstFormat.height;
|
fi->frameHeight = dstFormat.height;
|
||||||
fi->stride = pitch / bpp;
|
fi->stride = pitch / bpp;
|
||||||
|
|||||||
@@ -113,15 +113,18 @@ private:
|
|||||||
KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {};
|
KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {};
|
||||||
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {};
|
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {};
|
||||||
|
|
||||||
unsigned m_width = 0;
|
unsigned m_width = 0;
|
||||||
unsigned m_height = 0;
|
unsigned m_height = 0;
|
||||||
unsigned m_pitch = 0;
|
unsigned m_frameWidth = 0;
|
||||||
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
|
unsigned m_frameHeight = 0;
|
||||||
FrameType m_frameType = FRAME_TYPE_INVALID;
|
unsigned m_pitch = 0;
|
||||||
UINT m_iddCxVersion = 0;
|
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
|
||||||
bool m_hasIddCx110DDIs = false;
|
FrameType m_frameType = FRAME_TYPE_INVALID;
|
||||||
bool m_canProcessFP16 = false;
|
|
||||||
bool m_softwareMode = true;
|
UINT m_iddCxVersion = 0;
|
||||||
|
bool m_hasIddCx110DDIs = false;
|
||||||
|
bool m_canProcessFP16 = false;
|
||||||
|
bool m_softwareMode = true;
|
||||||
|
|
||||||
// Previous HDR metadata used to detect changes for formatVer bumps
|
// Previous HDR metadata used to detect changes for formatVer bumps
|
||||||
uint16_t m_lastHDRDisplayPrimary[3][2] = {};
|
uint16_t m_lastHDRDisplayPrimary[3][2] = {};
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "effect/CRGB24Effect.h"
|
#include "effect/CRGB24Effect.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -82,12 +83,16 @@ static void CopyHDRMetadata(D12FrameFormat& dst, const D12FrameFormat& src)
|
|||||||
dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel;
|
dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPostProcessor::Init(std::shared_ptr<CD3D12Device> dx12Device)
|
bool CPostProcessor::Init(std::shared_ptr<CD3D12Device> dx12Device,
|
||||||
|
bool enableEffects)
|
||||||
{
|
{
|
||||||
m_dx12Device = dx12Device;
|
m_dx12Device = dx12Device;
|
||||||
m_device = dx12Device->GetDevice();
|
m_device = dx12Device->GetDevice();
|
||||||
m_effects.clear();
|
m_effects.clear();
|
||||||
|
|
||||||
|
if (!enableEffects)
|
||||||
|
return true;
|
||||||
|
|
||||||
std::unique_ptr<CColorTransformEffect> colorTransform(new CColorTransformEffect());
|
std::unique_ptr<CColorTransformEffect> colorTransform(new CColorTransformEffect());
|
||||||
if (colorTransform->Init(m_device))
|
if (colorTransform->Init(m_device))
|
||||||
{
|
{
|
||||||
@@ -130,6 +135,10 @@ void CPostProcessor::Reset()
|
|||||||
m_device.Reset();
|
m_device.Reset();
|
||||||
m_srcFormat = {};
|
m_srcFormat = {};
|
||||||
m_dstFormat = {};
|
m_dstFormat = {};
|
||||||
|
m_copyLayout = {};
|
||||||
|
m_copyEffect = nullptr;
|
||||||
|
m_frameSize = 0;
|
||||||
|
m_pitch = 0;
|
||||||
m_effectsActive = false;
|
m_effectsActive = false;
|
||||||
m_configured = false;
|
m_configured = false;
|
||||||
}
|
}
|
||||||
@@ -147,9 +156,26 @@ bool CPostProcessor::HasSameEffectChain(const CPostProcessor& other) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CPostProcessor::ShareEffectState(const CPostProcessor& other)
|
||||||
|
{
|
||||||
|
if (!HasSameEffectChain(other))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_effects.size(); ++i)
|
||||||
|
m_effects[i]->ShareState(*other.m_effects[i]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPostProcessor::Update(const D12FrameFormat& srcFormat)
|
||||||
|
{
|
||||||
|
for (const auto& effect : m_effects)
|
||||||
|
effect->Update(srcFormat);
|
||||||
|
}
|
||||||
|
|
||||||
bool CPostProcessor::NeedsReconfigure(const D12FrameFormat& srcFormat) const
|
bool CPostProcessor::NeedsReconfigure(const D12FrameFormat& srcFormat) const
|
||||||
{
|
{
|
||||||
return !m_configured ||
|
if (!m_configured ||
|
||||||
srcFormat.desc.Width != m_srcFormat.desc.Width ||
|
srcFormat.desc.Width != m_srcFormat.desc.Width ||
|
||||||
srcFormat.desc.Height != m_srcFormat.desc.Height ||
|
srcFormat.desc.Height != m_srcFormat.desc.Height ||
|
||||||
srcFormat.desc.Format != m_srcFormat.desc.Format ||
|
srcFormat.desc.Format != m_srcFormat.desc.Format ||
|
||||||
@@ -158,7 +184,23 @@ bool CPostProcessor::NeedsReconfigure(const D12FrameFormat& srcFormat) const
|
|||||||
srcFormat.height != m_srcFormat.height ||
|
srcFormat.height != m_srcFormat.height ||
|
||||||
srcFormat.hdr != m_srcFormat.hdr ||
|
srcFormat.hdr != m_srcFormat.hdr ||
|
||||||
srcFormat.hdrPQ != m_srcFormat.hdrPQ ||
|
srcFormat.hdrPQ != m_srcFormat.hdrPQ ||
|
||||||
srcFormat.colorTransform != m_srcFormat.colorTransform;
|
srcFormat.colorTransform != m_srcFormat.colorTransform)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (const auto& effect : m_effects)
|
||||||
|
if (effect->NeedsReconfigure())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPostProcessor::RequiresFullDamage() const
|
||||||
|
{
|
||||||
|
for (const auto& effect : m_effects)
|
||||||
|
if (effect->RequiresFullDamage())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPostProcessor::Configure(const D12FrameFormat& srcFormat,
|
bool CPostProcessor::Configure(const D12FrameFormat& srcFormat,
|
||||||
@@ -170,15 +212,16 @@ bool CPostProcessor::Configure(const D12FrameFormat& srcFormat,
|
|||||||
if (!NeedsReconfigure(srcFormat))
|
if (!NeedsReconfigure(srcFormat))
|
||||||
{
|
{
|
||||||
// Static HDR metadata may change independently of the resource format.
|
// Static HDR metadata may change independently of the resource format.
|
||||||
// Propagate it without recreating textures or post-processing state.
|
// Propagate it without recreating resources or post-processing state.
|
||||||
CopyHDRMetadata(m_srcFormat, srcFormat);
|
CopyHDRMetadata(m_srcFormat, srcFormat);
|
||||||
CopyHDRMetadata(m_dstFormat, srcFormat);
|
CopyHDRMetadata(m_dstFormat, srcFormat);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
D12FrameFormat oldDst = m_dstFormat;
|
D12FrameFormat oldDst = m_dstFormat;
|
||||||
D12FrameFormat cur = srcFormat;
|
D12FrameFormat cur = srcFormat;
|
||||||
bool effectsActive = false;
|
CPostProcessEffect * outputEffect = nullptr;
|
||||||
|
bool effectsActive = false;
|
||||||
|
|
||||||
for (const auto& effect : m_effects)
|
for (const auto& effect : m_effects)
|
||||||
{
|
{
|
||||||
@@ -189,6 +232,7 @@ bool CPostProcessor::Configure(const D12FrameFormat& srcFormat,
|
|||||||
effect->Enabled = true;
|
effect->Enabled = true;
|
||||||
effectsActive = true;
|
effectsActive = true;
|
||||||
cur = dst;
|
cur = dst;
|
||||||
|
outputEffect = effect.get();
|
||||||
DEBUG_INFO("Post-processing effect active: %s", effect->GetName());
|
DEBUG_INFO("Post-processing effect active: %s", effect->GetName());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -202,25 +246,156 @@ bool CPostProcessor::Configure(const D12FrameFormat& srcFormat,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT copyLayout = {};
|
||||||
|
CPostProcessEffect * copyEffect = nullptr;
|
||||||
|
unsigned pitch = 0;
|
||||||
|
unsigned dataHeight = 0;
|
||||||
|
if (outputEffect && outputEffect->GetCopyLayout(&pitch, &dataHeight))
|
||||||
|
copyEffect = outputEffect;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cur.desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Post-processing output has no copy implementation");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_device->GetCopyableFootprints(
|
||||||
|
&cur.desc,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
©Layout,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
nullptr);
|
||||||
|
pitch = copyLayout.Footprint.RowPitch;
|
||||||
|
dataHeight = cur.desc.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pitch || !dataHeight ||
|
||||||
|
pitch > (std::numeric_limits<size_t>::max)() / dataHeight)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Invalid post-processing output layout");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t frameSize = (size_t)pitch * dataHeight;
|
||||||
|
if (copyEffect && cur.desc.Width < frameSize)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Post-processing output buffer is too small");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_srcFormat = srcFormat;
|
m_srcFormat = srcFormat;
|
||||||
m_dstFormat = cur;
|
m_dstFormat = cur;
|
||||||
|
m_copyLayout = copyLayout;
|
||||||
|
m_copyEffect = copyEffect;
|
||||||
|
m_frameSize = frameSize;
|
||||||
|
m_pitch = pitch;
|
||||||
m_effectsActive = effectsActive;
|
m_effectsActive = effectsActive;
|
||||||
m_configured = true;
|
m_configured = true;
|
||||||
if (formatChanged)
|
if (formatChanged)
|
||||||
*formatChanged =
|
*formatChanged =
|
||||||
oldDst.desc.Width != m_dstFormat.desc.Width ||
|
oldDst.desc.Width != m_dstFormat.desc.Width ||
|
||||||
oldDst.desc.Height != m_dstFormat.desc.Height ||
|
oldDst.desc.Height != m_dstFormat.desc.Height ||
|
||||||
oldDst.desc.Format != m_dstFormat.desc.Format ||
|
oldDst.desc.Format != m_dstFormat.desc.Format ||
|
||||||
oldDst.format != m_dstFormat.format ||
|
oldDst.dataWidth != m_dstFormat.dataWidth ||
|
||||||
oldDst.width != m_dstFormat.width ||
|
oldDst.dataHeight != m_dstFormat.dataHeight ||
|
||||||
oldDst.height != m_dstFormat.height ||
|
oldDst.pitch != m_dstFormat.pitch ||
|
||||||
oldDst.hdr != m_dstFormat.hdr ||
|
oldDst.format != m_dstFormat.format ||
|
||||||
oldDst.hdrPQ != m_dstFormat.hdrPQ ||
|
oldDst.width != m_dstFormat.width ||
|
||||||
oldDst.sdrWhiteLevel != m_dstFormat.sdrWhiteLevel ||
|
oldDst.height != m_dstFormat.height ||
|
||||||
|
oldDst.hdr != m_dstFormat.hdr ||
|
||||||
|
oldDst.hdrPQ != m_dstFormat.hdrPQ ||
|
||||||
|
oldDst.sdrWhiteLevel != m_dstFormat.sdrWhiteLevel ||
|
||||||
oldDst.colorTransform != m_dstFormat.colorTransform;
|
oldDst.colorTransform != m_dstFormat.colorTransform;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPostProcessor::GetTimingToken(
|
||||||
|
unsigned * effectIndex, uint64_t * token) const
|
||||||
|
{
|
||||||
|
if (effectIndex)
|
||||||
|
*effectIndex = 0;
|
||||||
|
if (token)
|
||||||
|
*token = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_effects.size(); ++i)
|
||||||
|
{
|
||||||
|
const uint64_t value = m_effects[i]->GetTimingToken();
|
||||||
|
if (!value)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (effectIndex)
|
||||||
|
*effectIndex = (unsigned)i;
|
||||||
|
if (token)
|
||||||
|
*token = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPostProcessor::RecordTiming(
|
||||||
|
unsigned effectIndex, uint64_t token, bool fullCopy, uint64_t totalTime)
|
||||||
|
{
|
||||||
|
if (!token || effectIndex >= m_effects.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_effects[effectIndex]->RecordTiming(token, fullCopy, totalTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPostProcessor::ShouldCopyFully(
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects) const
|
||||||
|
{
|
||||||
|
return m_copyEffect &&
|
||||||
|
m_copyEffect->ShouldCopyFully(dirtyRects, nbDirtyRects);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPostProcessor::CopyFrame(
|
||||||
|
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||||
|
ID3D12Resource * dst, ID3D12Resource * src,
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects, bool fullCopy) const
|
||||||
|
{
|
||||||
|
if (m_copyEffect)
|
||||||
|
{
|
||||||
|
m_copyEffect->CopyFrame(
|
||||||
|
commandList, dst, src, dirtyRects, nbDirtyRects, fullCopy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
|
||||||
|
srcLoc.pResource = src;
|
||||||
|
srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||||
|
srcLoc.SubresourceIndex = 0;
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
|
||||||
|
dstLoc.pResource = dst;
|
||||||
|
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||||
|
dstLoc.PlacedFootprint = m_copyLayout;
|
||||||
|
|
||||||
|
if (fullCopy)
|
||||||
|
{
|
||||||
|
commandList->CopyTextureRegion(
|
||||||
|
&dstLoc, 0, 0, 0, &srcLoc, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const RECT * rect = dirtyRects;
|
||||||
|
rect < dirtyRects + nbDirtyRects; ++rect)
|
||||||
|
{
|
||||||
|
D3D12_BOX box = {};
|
||||||
|
box.left = rect->left;
|
||||||
|
box.top = rect->top;
|
||||||
|
box.front = 0;
|
||||||
|
box.right = rect->right;
|
||||||
|
box.bottom = rect->bottom;
|
||||||
|
box.back = 1;
|
||||||
|
|
||||||
|
commandList->CopyTextureRegion(
|
||||||
|
&dstLoc, box.left, box.top, 0, &srcLoc, &box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CPostProcessor::AdjustFrameDamage(RECT dirtyRects[], unsigned * nbDirtyRects)
|
void CPostProcessor::AdjustFrameDamage(RECT dirtyRects[], unsigned * nbDirtyRects)
|
||||||
{
|
{
|
||||||
for (const auto& effect : m_effects)
|
for (const auto& effect : m_effects)
|
||||||
|
|||||||
@@ -55,15 +55,18 @@ bool IsIdentityColorTransform(const D12ColorTransform& transform);
|
|||||||
|
|
||||||
struct D12FrameFormat
|
struct D12FrameFormat
|
||||||
{
|
{
|
||||||
D3D12_RESOURCE_DESC desc = {};
|
D3D12_RESOURCE_DESC desc = {};
|
||||||
unsigned width = 0;
|
unsigned dataWidth = 0;
|
||||||
unsigned height = 0;
|
unsigned dataHeight = 0;
|
||||||
FrameType format = FRAME_TYPE_INVALID;
|
unsigned pitch = 0;
|
||||||
bool hdr = false;
|
unsigned width = 0;
|
||||||
bool hdrPQ = false;
|
unsigned height = 0;
|
||||||
bool hdrMetadata = false;
|
FrameType format = FRAME_TYPE_INVALID;
|
||||||
uint32_t sdrWhiteLevel = LG_SDR_WHITE_LEVEL_DEFAULT;
|
bool hdr = false;
|
||||||
std::shared_ptr<const D12ColorTransform> colorTransform;
|
bool hdrPQ = false;
|
||||||
|
bool hdrMetadata = false;
|
||||||
|
uint32_t sdrWhiteLevel = LG_SDR_WHITE_LEVEL_DEFAULT;
|
||||||
|
std::shared_ptr<const D12ColorTransform> colorTransform;
|
||||||
|
|
||||||
// HDR static metadata (SMPTE ST 2086)
|
// HDR static metadata (SMPTE ST 2086)
|
||||||
// Display color primaries in 0.00002 units (xy coordinates)
|
// Display color primaries in 0.00002 units (xy coordinates)
|
||||||
@@ -84,6 +87,51 @@ class CPostProcessEffect
|
|||||||
public:
|
public:
|
||||||
virtual ~CPostProcessEffect() {}
|
virtual ~CPostProcessEffect() {}
|
||||||
virtual const char * GetName() const = 0;
|
virtual const char * GetName() const = 0;
|
||||||
|
virtual void ShareState(const CPostProcessEffect& other)
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(other);
|
||||||
|
}
|
||||||
|
virtual void Update(const D12FrameFormat& format)
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(format);
|
||||||
|
}
|
||||||
|
virtual bool NeedsReconfigure() const { return false; }
|
||||||
|
virtual bool RequiresFullDamage() const { return false; }
|
||||||
|
virtual uint64_t GetTimingToken() const { return 0; }
|
||||||
|
virtual void RecordTiming(
|
||||||
|
uint64_t token, bool fullCopy, uint64_t totalTime)
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(token);
|
||||||
|
UNREFERENCED_PARAMETER(fullCopy);
|
||||||
|
UNREFERENCED_PARAMETER(totalTime);
|
||||||
|
}
|
||||||
|
// A final effect with a non-texture output owns its framebuffer copy.
|
||||||
|
virtual bool GetCopyLayout(
|
||||||
|
unsigned * pitch, unsigned * dataHeight) const
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(pitch);
|
||||||
|
UNREFERENCED_PARAMETER(dataHeight);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual bool ShouldCopyFully(
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects) const
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(dirtyRects);
|
||||||
|
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual void CopyFrame(
|
||||||
|
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||||
|
ID3D12Resource * dst, ID3D12Resource * src,
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects, bool fullCopy) const
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(commandList);
|
||||||
|
UNREFERENCED_PARAMETER(dst);
|
||||||
|
UNREFERENCED_PARAMETER(src);
|
||||||
|
UNREFERENCED_PARAMETER(dirtyRects);
|
||||||
|
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||||
|
UNREFERENCED_PARAMETER(fullCopy);
|
||||||
|
}
|
||||||
virtual PostProcessStatus SetFormat(const ComPtr<ID3D12Device3>& device,
|
virtual PostProcessStatus SetFormat(const ComPtr<ID3D12Device3>& device,
|
||||||
const D12FrameFormat& src, D12FrameFormat& dst) = 0;
|
const D12FrameFormat& src, D12FrameFormat& dst) = 0;
|
||||||
virtual void AdjustDamage(RECT dirtyRects[], unsigned * nbDirtyRects) { UNREFERENCED_PARAMETER(dirtyRects); UNREFERENCED_PARAMETER(nbDirtyRects); }
|
virtual void AdjustDamage(RECT dirtyRects[], unsigned * nbDirtyRects) { UNREFERENCED_PARAMETER(dirtyRects); UNREFERENCED_PARAMETER(nbDirtyRects); }
|
||||||
@@ -102,15 +150,23 @@ private:
|
|||||||
std::vector<std::unique_ptr<CPostProcessEffect>> m_effects;
|
std::vector<std::unique_ptr<CPostProcessEffect>> m_effects;
|
||||||
D12FrameFormat m_srcFormat = {};
|
D12FrameFormat m_srcFormat = {};
|
||||||
D12FrameFormat m_dstFormat = {};
|
D12FrameFormat m_dstFormat = {};
|
||||||
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT m_copyLayout = {};
|
||||||
|
CPostProcessEffect * m_copyEffect = nullptr;
|
||||||
|
size_t m_frameSize = 0;
|
||||||
|
unsigned m_pitch = 0;
|
||||||
bool m_effectsActive = false;
|
bool m_effectsActive = false;
|
||||||
bool m_configured = false;
|
bool m_configured = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Init(std::shared_ptr<CD3D12Device> dx12Device);
|
bool Init(std::shared_ptr<CD3D12Device> dx12Device,
|
||||||
|
bool enableEffects);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
bool HasSameEffectChain(const CPostProcessor& other) const;
|
bool HasSameEffectChain(const CPostProcessor& other) const;
|
||||||
|
bool ShareEffectState(const CPostProcessor& other);
|
||||||
|
void Update(const D12FrameFormat& srcFormat);
|
||||||
bool NeedsReconfigure(const D12FrameFormat& srcFormat) const;
|
bool NeedsReconfigure(const D12FrameFormat& srcFormat) const;
|
||||||
|
bool RequiresFullDamage() const;
|
||||||
bool Configure(const D12FrameFormat& srcFormat, bool * formatChanged);
|
bool Configure(const D12FrameFormat& srcFormat, bool * formatChanged);
|
||||||
void AdjustFrameDamage(RECT dirtyRects[], unsigned * nbDirtyRects);
|
void AdjustFrameDamage(RECT dirtyRects[], unsigned * nbDirtyRects);
|
||||||
ComPtr<ID3D12Resource> Run(
|
ComPtr<ID3D12Resource> Run(
|
||||||
@@ -120,4 +176,15 @@ public:
|
|||||||
|
|
||||||
const D12FrameFormat& GetOutputFormat() const { return m_dstFormat; }
|
const D12FrameFormat& GetOutputFormat() const { return m_dstFormat; }
|
||||||
bool HasActiveEffects() const { return m_effectsActive; }
|
bool HasActiveEffects() const { return m_effectsActive; }
|
||||||
|
void GetTimingToken(unsigned * effectIndex, uint64_t * token) const;
|
||||||
|
void RecordTiming(unsigned effectIndex, uint64_t token,
|
||||||
|
bool fullCopy, uint64_t totalTime);
|
||||||
|
unsigned GetOutputPitch() const { return m_pitch; }
|
||||||
|
size_t GetOutputSize () const { return m_frameSize; }
|
||||||
|
bool ShouldCopyFully(
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects) const;
|
||||||
|
void CopyFrame(
|
||||||
|
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||||
|
ID3D12Resource * dst, ID3D12Resource * src,
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects, bool fullCopy) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -79,33 +79,37 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex
|
|||||||
{
|
{
|
||||||
m_resPool.Init(dx11Device, dx12Device);
|
m_resPool.Init(dx11Device, dx12Device);
|
||||||
m_fbPool.Init(this);
|
m_fbPool.Init(this);
|
||||||
if (m_dx11Device->IsSoftware())
|
const bool enableEffects = !m_dx11Device->IsSoftware();
|
||||||
|
if (!enableEffects)
|
||||||
DEBUG_INFO("Software render adapter: post-processing disabled");
|
DEBUG_INFO("Software render adapter: post-processing disabled");
|
||||||
else
|
|
||||||
{
|
bool initialized = true;
|
||||||
bool initialized = true;
|
for (CPostProcessor& postProcessor : m_postProcessors)
|
||||||
for (CPostProcessor& postProcessor : m_postProcessors)
|
if (!postProcessor.Init(dx12Device, enableEffects))
|
||||||
if (!postProcessor.Init(dx12Device))
|
{
|
||||||
|
initialized = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initialized)
|
||||||
|
for (unsigned i = 1; i < ARRAYSIZE(m_postProcessors); ++i)
|
||||||
|
if (!m_postProcessors[i].ShareEffectState(m_postProcessors[0]))
|
||||||
{
|
{
|
||||||
|
DEBUG_ERROR("Post processor effect chains do not match");
|
||||||
initialized = false;
|
initialized = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialized)
|
if (!initialized)
|
||||||
for (unsigned i = 1; i < ARRAYSIZE(m_postProcessors); ++i)
|
{
|
||||||
if (!m_postProcessors[0].HasSameEffectChain(m_postProcessors[i]))
|
for (CPostProcessor& postProcessor : m_postProcessors)
|
||||||
{
|
|
||||||
DEBUG_ERROR("Post processor effect chains do not match");
|
|
||||||
initialized = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!initialized)
|
|
||||||
{
|
{
|
||||||
for (CPostProcessor& postProcessor : m_postProcessors)
|
postProcessor.Reset();
|
||||||
postProcessor.Reset();
|
if (!postProcessor.Init(dx12Device, false))
|
||||||
DEBUG_ERROR("Failed to initialize post processors");
|
DEBUG_ERROR("Failed to initialize post processor copy support");
|
||||||
}
|
}
|
||||||
|
DEBUG_WARN(
|
||||||
|
"Failed to initialize post-processing effects; effects disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manual-reset: both worker threads wait on this, so it must stay signalled
|
// Manual-reset: both worker threads wait on this, so it must stay signalled
|
||||||
@@ -363,7 +367,7 @@ void CSwapChainProcessor::CompletionFunction(
|
|||||||
fbRes->GetFrameIndex(), fbRes->GetMap(), 0, fbRes->GetFrameSize(), false);
|
fbRes->GetFrameIndex(), fbRes->GetMap(), 0, fbRes->GetFrameSize(), false);
|
||||||
|
|
||||||
// Queue waits execute before the start timestamp. The end timestamp follows
|
// Queue waits execute before the start timestamp. The end timestamp follows
|
||||||
// the last CopyTextureRegion, separating GPU work from readiness dispatch.
|
// the last copy command, separating GPU work from readiness dispatch.
|
||||||
const bool gpuTimingValid =
|
const bool gpuTimingValid =
|
||||||
slot->GetGPUTimes(gpuCopyStart, gpuCopyEnd);
|
slot->GetGPUTimes(gpuCopyStart, gpuCopyEnd);
|
||||||
|
|
||||||
@@ -384,6 +388,9 @@ void CSwapChainProcessor::CompletionFunction(
|
|||||||
readyTime = readyEnd - gpuCopyEnd;
|
readyTime = readyEnd - gpuCopyEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sc->m_postProcessors[fbRes->GetFrameIndex()].RecordTiming(
|
||||||
|
fbRes->GetTimingEffectIndex(), fbRes->GetTimingToken(),
|
||||||
|
fbRes->IsFullCopy(), postProcessTime + copyTime + readyTime);
|
||||||
sc->m_devContext->SetFrameTiming(fbRes->GetFrameIndex(),
|
sc->m_devContext->SetFrameTiming(fbRes->GetFrameIndex(),
|
||||||
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime);
|
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime);
|
||||||
sc->m_devContext->CompleteFrameBuffer(fbRes->GetFrameIndex());
|
sc->m_devContext->CompleteFrameBuffer(fbRes->GetFrameIndex());
|
||||||
@@ -391,14 +398,14 @@ void CSwapChainProcessor::CompletionFunction(
|
|||||||
|
|
||||||
|
|
||||||
static bool IsFullDamage(const RECT * dirtyRects, unsigned nbDirtyRects,
|
static bool IsFullDamage(const RECT * dirtyRects, unsigned nbDirtyRects,
|
||||||
const D3D12_RESOURCE_DESC& desc)
|
unsigned width, unsigned height)
|
||||||
{
|
{
|
||||||
for (const RECT * rect = dirtyRects;
|
for (const RECT * rect = dirtyRects;
|
||||||
rect < dirtyRects + nbDirtyRects; ++rect)
|
rect < dirtyRects + nbDirtyRects; ++rect)
|
||||||
if (rect->left == 0 &&
|
if (rect->left == 0 &&
|
||||||
rect->top == 0 &&
|
rect->top == 0 &&
|
||||||
rect->right == (LONG)desc.Width &&
|
rect->right == (LONG)width &&
|
||||||
rect->bottom == (LONG)desc.Height)
|
rect->bottom == (LONG)height)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -470,9 +477,9 @@ static bool AddCopyDirtyRect(RECT dirtyRects[], unsigned capacity,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool CopyAreaCoversFrame(const RECT * dirtyRects,
|
static bool CopyAreaCoversFrame(const RECT * dirtyRects,
|
||||||
unsigned nbDirtyRects, const D3D12_RESOURCE_DESC& desc)
|
unsigned nbDirtyRects, unsigned width, unsigned height)
|
||||||
{
|
{
|
||||||
const uint64_t frameArea = (uint64_t)desc.Width * desc.Height;
|
const uint64_t frameArea = (uint64_t)width * height;
|
||||||
uint64_t copyArea = 0;
|
uint64_t copyArea = 0;
|
||||||
|
|
||||||
for (const RECT * rect = dirtyRects;
|
for (const RECT * rect = dirtyRects;
|
||||||
@@ -487,26 +494,10 @@ static bool CopyAreaCoversFrame(const RECT * dirtyRects,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CopyDirtyRect(ComPtr<ID3D12GraphicsCommandList> list,
|
static bool ClipDirtyRect(RECT& rect, unsigned width, unsigned height)
|
||||||
D3D12_TEXTURE_COPY_LOCATION * dstLoc,
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION * srcLoc,
|
|
||||||
const RECT& rect)
|
|
||||||
{
|
{
|
||||||
D3D12_BOX box = {};
|
const LONG maxRight = (LONG)width;
|
||||||
box.left = rect.left;
|
const LONG maxBottom = (LONG)height;
|
||||||
box.top = rect.top;
|
|
||||||
box.front = 0;
|
|
||||||
box.right = rect.right;
|
|
||||||
box.bottom = rect.bottom;
|
|
||||||
box.back = 1;
|
|
||||||
|
|
||||||
list->CopyTextureRegion(dstLoc, box.left, box.top, 0, srcLoc, &box);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ClipDirtyRect(RECT& rect, const D3D12_RESOURCE_DESC& desc)
|
|
||||||
{
|
|
||||||
const LONG maxRight = (LONG)desc.Width;
|
|
||||||
const LONG maxBottom = (LONG)desc.Height;
|
|
||||||
|
|
||||||
if (rect.left < 0 ) rect.left = 0;
|
if (rect.left < 0 ) rect.left = 0;
|
||||||
if (rect.top < 0 ) rect.top = 0;
|
if (rect.top < 0 ) rect.top = 0;
|
||||||
@@ -517,13 +508,13 @@ static bool ClipDirtyRect(RECT& rect, const D3D12_RESOURCE_DESC& desc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void ClipDirtyRects(RECT dirtyRects[], unsigned * nbDirtyRects,
|
static void ClipDirtyRects(RECT dirtyRects[], unsigned * nbDirtyRects,
|
||||||
const D3D12_RESOURCE_DESC& desc)
|
unsigned width, unsigned height)
|
||||||
{
|
{
|
||||||
unsigned out = 0;
|
unsigned out = 0;
|
||||||
for (unsigned i = 0; i < *nbDirtyRects; ++i)
|
for (unsigned i = 0; i < *nbDirtyRects; ++i)
|
||||||
{
|
{
|
||||||
RECT rect = dirtyRects[i];
|
RECT rect = dirtyRects[i];
|
||||||
if (ClipDirtyRect(rect, desc))
|
if (ClipDirtyRect(rect, width, height))
|
||||||
dirtyRects[out++] = rect;
|
dirtyRects[out++] = rect;
|
||||||
}
|
}
|
||||||
*nbDirtyRects = out;
|
*nbDirtyRects = out;
|
||||||
@@ -816,6 +807,8 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_postProcessors[0].Update(srcFormat);
|
||||||
|
|
||||||
const bool frameMetadataChanged = noImageUpdate &&
|
const bool frameMetadataChanged = noImageUpdate &&
|
||||||
FrameMetadataChanged(m_postProcessors[0].GetOutputFormat(), srcFormat);
|
FrameMetadataChanged(m_postProcessors[0].GetOutputFormat(), srcFormat);
|
||||||
|
|
||||||
@@ -836,18 +829,40 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
m_dx12Device->WaitForIdle();
|
m_dx12Device->WaitForIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An optional adaptive effect can reject its candidate while configuring
|
||||||
|
// either slot. Both queues are already idle, so allow one convergence pass
|
||||||
|
// to return both effect chains to the same mode.
|
||||||
bool postProcessFormatChanged = false;
|
bool postProcessFormatChanged = false;
|
||||||
for (unsigned i = 0; i < ARRAYSIZE(m_postProcessors); ++i)
|
bool configurationStable = false;
|
||||||
|
for (unsigned pass = 0; pass < 2 && !configurationStable; ++pass)
|
||||||
{
|
{
|
||||||
bool formatChanged = false;
|
for (unsigned i = 0; i < ARRAYSIZE(m_postProcessors); ++i)
|
||||||
if (!m_postProcessors[i].Configure(srcFormat, &formatChanged))
|
|
||||||
{
|
{
|
||||||
SetFullPendingDamage();
|
bool formatChanged = false;
|
||||||
return false;
|
if (!m_postProcessors[i].Configure(srcFormat, &formatChanged))
|
||||||
|
{
|
||||||
|
SetFullPendingDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
postProcessFormatChanged |= formatChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 0)
|
configurationStable = true;
|
||||||
postProcessFormatChanged = formatChanged;
|
for (const CPostProcessor& postProcessor : m_postProcessors)
|
||||||
|
if (postProcessor.NeedsReconfigure(srcFormat))
|
||||||
|
{
|
||||||
|
configurationStable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!configurationStable)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Post processor configuration did not stabilize");
|
||||||
|
SetFullPendingDamage();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postProcessFormatChanged)
|
if (postProcessFormatChanged)
|
||||||
@@ -858,37 +873,34 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
else if (frameMetadataChanged)
|
else if (frameMetadataChanged)
|
||||||
SetFullPendingDamage();
|
SetFullPendingDamage();
|
||||||
|
|
||||||
|
// Adaptive effects need comparable full-frame samples until they lock.
|
||||||
|
if (m_postProcessors[0].RequiresFullDamage())
|
||||||
|
SetFullPendingDamage();
|
||||||
|
|
||||||
if (noImageUpdate && !m_hasPendingDamage)
|
if (noImageUpdate && !m_hasPendingDamage)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const D12FrameFormat& dstFormat =
|
const D12FrameFormat& dstFormat =
|
||||||
m_postProcessors[0].GetOutputFormat();
|
m_postProcessors[0].GetOutputFormat();
|
||||||
|
const unsigned pitch = m_postProcessors[0].GetOutputPitch();
|
||||||
|
const size_t frameSize = m_postProcessors[0].GetOutputSize();
|
||||||
|
|
||||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
|
RECT currentDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||||
m_dx12Device->GetDevice()->GetCopyableFootprints(
|
RECT frameDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||||
&dstFormat.desc,
|
unsigned nbDirtyRects = m_nbPendingDirtyRects;
|
||||||
0,
|
unsigned frameDirtyRectCount = nbDirtyRects;
|
||||||
1,
|
|
||||||
0,
|
|
||||||
&layout,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
RECT currentDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
|
||||||
RECT frameDirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
|
||||||
unsigned nbDirtyRects = m_nbPendingDirtyRects;
|
|
||||||
if (nbDirtyRects)
|
if (nbDirtyRects)
|
||||||
{
|
{
|
||||||
memcpy(currentDirtyRects, m_pendingDirtyRects, nbDirtyRects * sizeof(*currentDirtyRects));
|
memcpy(currentDirtyRects, m_pendingDirtyRects,
|
||||||
memcpy(frameDirtyRects, currentDirtyRects, nbDirtyRects * sizeof(*frameDirtyRects));
|
nbDirtyRects * sizeof(*currentDirtyRects));
|
||||||
|
memcpy(frameDirtyRects, currentDirtyRects,
|
||||||
|
nbDirtyRects * sizeof(*frameDirtyRects));
|
||||||
}
|
}
|
||||||
unsigned frameDirtyRectCount = nbDirtyRects;
|
|
||||||
m_postProcessors[0].AdjustFrameDamage(
|
m_postProcessors[0].AdjustFrameDamage(
|
||||||
frameDirtyRects, &frameDirtyRectCount);
|
frameDirtyRects, &frameDirtyRectCount);
|
||||||
|
|
||||||
auto buffer = m_devContext->PrepareFrameBuffer(
|
auto buffer = m_devContext->PrepareFrameBuffer(
|
||||||
(unsigned)layout.Footprint.RowPitch,
|
pitch,
|
||||||
srcFormat,
|
srcFormat,
|
||||||
dstFormat,
|
dstFormat,
|
||||||
frameDirtyRects,
|
frameDirtyRects,
|
||||||
@@ -899,8 +911,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
if (!buffer.mem)
|
if (!buffer.mem)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
CFrameBufferResource * fbRes = m_fbPool.Get(buffer,
|
CFrameBufferResource * fbRes = m_fbPool.Get(buffer, frameSize);
|
||||||
(size_t)layout.Footprint.RowPitch * dstFormat.desc.Height);
|
|
||||||
|
|
||||||
if (!fbRes)
|
if (!fbRes)
|
||||||
{
|
{
|
||||||
@@ -986,23 +997,14 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClipDirtyRects(currentDirtyRects, &nbDirtyRects, dstFormat.desc);
|
ClipDirtyRects(currentDirtyRects, &nbDirtyRects,
|
||||||
|
dstFormat.width, dstFormat.height);
|
||||||
|
|
||||||
const uint64_t copyStart = Nanotime();
|
const uint64_t copyStart = Nanotime();
|
||||||
fbRes->SetTiming(captureTime, postProcessStart, copyStart);
|
fbRes->SetTiming(captureTime, postProcessStart, copyStart);
|
||||||
|
|
||||||
copySlot->SetCompletionCallback(&CompletionFunction, this, fbRes);
|
copySlot->SetCompletionCallback(&CompletionFunction, this, fbRes);
|
||||||
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
|
|
||||||
srcLoc.pResource = copySrcResource.Get();
|
|
||||||
srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
||||||
srcLoc.SubresourceIndex = 0;
|
|
||||||
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
|
|
||||||
dstLoc.pResource = fbRes->Get().Get();
|
|
||||||
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
||||||
dstLoc.PlacedFootprint = layout;
|
|
||||||
|
|
||||||
/* Each destination is reused every other frame, so repair both the prior
|
/* Each destination is reused every other frame, so repair both the prior
|
||||||
* and current damage. Coalesce them first to avoid copying overlapping
|
* and current damage. Coalesce them first to avoid copying overlapping
|
||||||
* regions, especially a prior full frame, more than once. */
|
* regions, especially a prior full frame, more than once. */
|
||||||
@@ -1017,7 +1019,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
rect < m_dirtyRects + m_nbDirtyRects && !fullCopy; ++rect)
|
rect < m_dirtyRects + m_nbDirtyRects && !fullCopy; ++rect)
|
||||||
{
|
{
|
||||||
RECT clipped = *rect;
|
RECT clipped = *rect;
|
||||||
if (ClipDirtyRect(clipped, dstFormat.desc) &&
|
if (ClipDirtyRect(clipped, dstFormat.width, dstFormat.height) &&
|
||||||
!AddCopyDirtyRect(copyDirtyRects, ARRAYSIZE(copyDirtyRects),
|
!AddCopyDirtyRect(copyDirtyRects, ARRAYSIZE(copyDirtyRects),
|
||||||
&nbCopyDirtyRects, clipped))
|
&nbCopyDirtyRects, clipped))
|
||||||
fullCopy = true;
|
fullCopy = true;
|
||||||
@@ -1031,25 +1033,29 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
|
|
||||||
if (!fullCopy)
|
if (!fullCopy)
|
||||||
fullCopy = IsFullDamage(
|
fullCopy = IsFullDamage(
|
||||||
copyDirtyRects, nbCopyDirtyRects, dstFormat.desc) ||
|
copyDirtyRects, nbCopyDirtyRects,
|
||||||
|
dstFormat.width, dstFormat.height) ||
|
||||||
CopyAreaCoversFrame(
|
CopyAreaCoversFrame(
|
||||||
copyDirtyRects, nbCopyDirtyRects, dstFormat.desc);
|
copyDirtyRects, nbCopyDirtyRects,
|
||||||
|
dstFormat.width, dstFormat.height);
|
||||||
|
|
||||||
|
if (!fullCopy)
|
||||||
|
fullCopy = postProcessor.ShouldCopyFully(
|
||||||
|
copyDirtyRects, nbCopyDirtyRects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned timingEffectIndex = 0;
|
||||||
|
uint64_t timingToken = 0;
|
||||||
|
postProcessor.GetTimingToken(&timingEffectIndex, &timingToken);
|
||||||
|
fbRes->SetPostProcessSample(
|
||||||
|
timingEffectIndex, timingToken, fullCopy);
|
||||||
|
|
||||||
// Source/compute waits are submitted immediately before this command list.
|
// Source/compute waits are submitted immediately before this command list.
|
||||||
// The timestamp therefore marks the first actual copy operation.
|
// The timestamp therefore marks the first actual copy operation.
|
||||||
copySlot->BeginTiming();
|
copySlot->BeginTiming();
|
||||||
if (fullCopy)
|
postProcessor.CopyFrame(
|
||||||
{
|
copySlot->GetGfxList(), fbRes->Get().Get(), copySrcResource.Get(),
|
||||||
copySlot->GetGfxList()->CopyTextureRegion(
|
copyDirtyRects, nbCopyDirtyRects, fullCopy);
|
||||||
&dstLoc, 0, 0, 0, &srcLoc, NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const RECT * rect = copyDirtyRects;
|
|
||||||
rect < copyDirtyRects + nbCopyDirtyRects; ++rect)
|
|
||||||
CopyDirtyRect(copySlot->GetGfxList(), &dstLoc, &srcLoc, *rect);
|
|
||||||
}
|
|
||||||
copySlot->EndTiming();
|
copySlot->EndTiming();
|
||||||
|
|
||||||
if (!copySlot->Execute())
|
if (!copySlot->Execute())
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ 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 };
|
||||||
|
|
||||||
// Output-space damage from the previous published frame. The shared-memory
|
// Logical output-space damage from the previous published frame. The
|
||||||
// frame buffers alternate, so this must be copied along with the current
|
// shared-memory frame buffers alternate, so this must be copied along with
|
||||||
// damage to bring the older target buffer up to date.
|
// the current damage to bring the older target buffer up to date.
|
||||||
RECT m_dirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
RECT m_dirtyRects[LG_MAX_DIRTY_RECTS] = {};
|
||||||
unsigned m_nbDirtyRects = 0;
|
unsigned m_nbDirtyRects = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ ComPtr<ID3D12Resource> CColorTransformEffect::Run(
|
|||||||
m_uploadPending = false;
|
m_uploadPending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_COPY_SOURCE,
|
TransitionDst(commandList, D3D12_RESOURCE_STATE_COMMON,
|
||||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||||
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||||
@@ -332,6 +332,6 @@ ComPtr<ID3D12Resource> CColorTransformEffect::Run(
|
|||||||
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
||||||
|
|
||||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||||
D3D12_RESOURCE_STATE_COPY_SOURCE);
|
D3D12_RESOURCE_STATE_COMMON);
|
||||||
return m_dst;
|
return m_dst;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
namespace PostProcessUtil
|
namespace PostProcessUtil
|
||||||
{
|
{
|
||||||
bool CreateDefaultTexture(const ComPtr<ID3D12Device3>& device,
|
static bool CreateDefaultResource(const ComPtr<ID3D12Device3>& device,
|
||||||
const D3D12_RESOURCE_DESC& desc, ComPtr<ID3D12Resource>& resource)
|
const D3D12_RESOURCE_DESC& desc, ComPtr<ID3D12Resource>& resource)
|
||||||
{
|
{
|
||||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||||
@@ -41,17 +41,38 @@ namespace PostProcessUtil
|
|||||||
&heapProps,
|
&heapProps,
|
||||||
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
|
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
|
||||||
&desc,
|
&desc,
|
||||||
D3D12_RESOURCE_STATE_COPY_SOURCE,
|
D3D12_RESOURCE_STATE_COMMON,
|
||||||
nullptr,
|
nullptr,
|
||||||
IID_PPV_ARGS(&resource));
|
IID_PPV_ARGS(&resource));
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR_HR(hr, "Failed to create post-processing destination texture");
|
DEBUG_ERROR_HR(hr, "Failed to create post-processing destination resource");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CreateDefaultTexture(const ComPtr<ID3D12Device3>& device,
|
||||||
|
const D3D12_RESOURCE_DESC& desc, ComPtr<ID3D12Resource>& resource)
|
||||||
|
{
|
||||||
|
return CreateDefaultResource(device, desc, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateDefaultBuffer(const ComPtr<ID3D12Device3>& device,
|
||||||
|
UINT64 size, ComPtr<ID3D12Resource>& resource)
|
||||||
|
{
|
||||||
|
D3D12_RESOURCE_DESC desc = {};
|
||||||
|
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||||
|
desc.Width = size;
|
||||||
|
desc.Height = 1;
|
||||||
|
desc.DepthOrArraySize = 1;
|
||||||
|
desc.MipLevels = 1;
|
||||||
|
desc.SampleDesc.Count = 1;
|
||||||
|
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||||
|
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||||
|
return CreateDefaultResource(device, desc, resource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CComputeEffect::InitCompute(const ComPtr<ID3D12Device3>& device,
|
bool CComputeEffect::InitCompute(const ComPtr<ID3D12Device3>& device,
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ namespace PostProcessUtil
|
|||||||
|
|
||||||
bool CreateDefaultTexture(const ComPtr<ID3D12Device3>& device,
|
bool CreateDefaultTexture(const ComPtr<ID3D12Device3>& device,
|
||||||
const D3D12_RESOURCE_DESC& desc, ComPtr<ID3D12Resource>& resource);
|
const D3D12_RESOURCE_DESC& desc, ComPtr<ID3D12Resource>& resource);
|
||||||
|
bool CreateDefaultBuffer(const ComPtr<ID3D12Device3>& device,
|
||||||
|
UINT64 size, ComPtr<ID3D12Resource>& resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CComputeEffect : public CPostProcessEffect
|
class CComputeEffect : public CPostProcessEffect
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ ComPtr<ID3D12Resource> CDownsampleEffect::Run(
|
|||||||
UNREFERENCED_PARAMETER(dirtyRects);
|
UNREFERENCED_PARAMETER(dirtyRects);
|
||||||
UNREFERENCED_PARAMETER(nbDirtyRects);
|
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||||
|
|
||||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_COPY_SOURCE,
|
TransitionDst(commandList, D3D12_RESOURCE_STATE_COMMON,
|
||||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||||
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||||
@@ -273,6 +273,6 @@ ComPtr<ID3D12Resource> CDownsampleEffect::Run(
|
|||||||
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
||||||
|
|
||||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||||
D3D12_RESOURCE_STATE_COPY_SOURCE);
|
D3D12_RESOURCE_STATE_COMMON);
|
||||||
return m_dst;
|
return m_dst;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ ComPtr<ID3D12Resource> CHDR16to10Effect::Run(
|
|||||||
UNREFERENCED_PARAMETER(dirtyRects);
|
UNREFERENCED_PARAMETER(dirtyRects);
|
||||||
UNREFERENCED_PARAMETER(nbDirtyRects);
|
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||||
|
|
||||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_COPY_SOURCE,
|
TransitionDst(commandList, D3D12_RESOURCE_STATE_COMMON,
|
||||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||||
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||||
@@ -187,6 +187,6 @@ ComPtr<ID3D12Resource> CHDR16to10Effect::Run(
|
|||||||
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
||||||
|
|
||||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||||
D3D12_RESOURCE_STATE_COPY_SOURCE);
|
D3D12_RESOURCE_STATE_COMMON);
|
||||||
return m_dst;
|
return m_dst;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,69 +20,517 @@
|
|||||||
|
|
||||||
#include "CRGB24Effect.h"
|
#include "CRGB24Effect.h"
|
||||||
|
|
||||||
|
#include "CDebug.h"
|
||||||
#include "../CSettings.h"
|
#include "../CSettings.h"
|
||||||
|
#include "common/LGMPConfig.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
using namespace PostProcessUtil;
|
using namespace PostProcessUtil;
|
||||||
|
|
||||||
|
static_assert(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT == 256,
|
||||||
|
"RGB24 shader row alignment must match D3D12");
|
||||||
|
|
||||||
|
struct CRGB24Effect::State
|
||||||
|
{
|
||||||
|
enum class Phase
|
||||||
|
{
|
||||||
|
DISABLED,
|
||||||
|
NATIVE_WARMUP,
|
||||||
|
NATIVE_SAMPLE,
|
||||||
|
PACKED_WARMUP,
|
||||||
|
PACKED_SAMPLE,
|
||||||
|
LOCKED_NATIVE,
|
||||||
|
LOCKED_PACKED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FormatKey
|
||||||
|
{
|
||||||
|
D3D12_RESOURCE_DIMENSION resourceDimension = D3D12_RESOURCE_DIMENSION_UNKNOWN;
|
||||||
|
UINT64 resourceWidth = 0;
|
||||||
|
UINT resourceHeight = 0;
|
||||||
|
DXGI_FORMAT resourceFormat = DXGI_FORMAT_UNKNOWN;
|
||||||
|
unsigned width = 0;
|
||||||
|
unsigned height = 0;
|
||||||
|
FrameType format = FRAME_TYPE_INVALID;
|
||||||
|
bool hdr = false;
|
||||||
|
bool hdrPQ = false;
|
||||||
|
std::shared_ptr<const D12ColorTransform> colorTransform;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned WarmupCount = LGMP_Q_FRAME_LEN;
|
||||||
|
static const unsigned SampleCount = 64;
|
||||||
|
static const unsigned TrimCount = SampleCount / 8;
|
||||||
|
|
||||||
|
SRWLOCK lock = SRWLOCK_INIT;
|
||||||
|
Phase phase = Phase::DISABLED;
|
||||||
|
FormatKey format = {};
|
||||||
|
bool formatValid = false;
|
||||||
|
uint64_t generation = 0;
|
||||||
|
unsigned warmups = 0;
|
||||||
|
unsigned sampleCount = 0;
|
||||||
|
uint64_t nativeMean = 0;
|
||||||
|
uint64_t samples[SampleCount] = {};
|
||||||
|
|
||||||
|
static bool IsEligible(const D12FrameFormat& format)
|
||||||
|
{
|
||||||
|
if (format.hdr ||
|
||||||
|
format.desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
|
||||||
|
format.desc.Format != DXGI_FORMAT_B8G8R8A8_UNORM)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!format.colorTransform ||
|
||||||
|
(!format.colorTransform->matrixEnabled &&
|
||||||
|
!format.colorTransform->lutEnabled))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return IsIdentityColorTransform(*format.colorTransform);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WantsPackedLocked() const
|
||||||
|
{
|
||||||
|
return phase == Phase::PACKED_WARMUP ||
|
||||||
|
phase == Phase::PACKED_SAMPLE ||
|
||||||
|
phase == Phase::LOCKED_PACKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBenchmarkingLocked() const
|
||||||
|
{
|
||||||
|
return phase == Phase::NATIVE_WARMUP ||
|
||||||
|
phase == Phase::NATIVE_SAMPLE ||
|
||||||
|
phase == Phase::PACKED_WARMUP ||
|
||||||
|
phase == Phase::PACKED_SAMPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetStageLocked()
|
||||||
|
{
|
||||||
|
warmups = 0;
|
||||||
|
sampleCount = 0;
|
||||||
|
std::memset(samples, 0, sizeof(samples));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t TrimmedMeanLocked()
|
||||||
|
{
|
||||||
|
std::sort(samples, samples + SampleCount);
|
||||||
|
|
||||||
|
uint64_t total = 0;
|
||||||
|
for (unsigned i = TrimCount; i < SampleCount - TrimCount; ++i)
|
||||||
|
total += samples[i];
|
||||||
|
|
||||||
|
return total / (SampleCount - TrimCount * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(const D12FrameFormat& next)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&lock);
|
||||||
|
|
||||||
|
const bool formatChanged = !formatValid ||
|
||||||
|
format.resourceDimension != next.desc.Dimension ||
|
||||||
|
format.resourceWidth != next.desc.Width ||
|
||||||
|
format.resourceHeight != next.desc.Height ||
|
||||||
|
format.resourceFormat != next.desc.Format ||
|
||||||
|
format.width != next.width ||
|
||||||
|
format.height != next.height ||
|
||||||
|
format.format != next.format ||
|
||||||
|
format.hdr != next.hdr ||
|
||||||
|
format.hdrPQ != next.hdrPQ ||
|
||||||
|
format.colorTransform != next.colorTransform;
|
||||||
|
|
||||||
|
if (formatChanged)
|
||||||
|
{
|
||||||
|
format.resourceDimension = next.desc.Dimension;
|
||||||
|
format.resourceWidth = next.desc.Width;
|
||||||
|
format.resourceHeight = next.desc.Height;
|
||||||
|
format.resourceFormat = next.desc.Format;
|
||||||
|
format.width = next.width;
|
||||||
|
format.height = next.height;
|
||||||
|
format.format = next.format;
|
||||||
|
format.hdr = next.hdr;
|
||||||
|
format.hdrPQ = next.hdrPQ;
|
||||||
|
format.colorTransform = next.colorTransform;
|
||||||
|
formatValid = true;
|
||||||
|
|
||||||
|
phase = IsEligible(next) ?
|
||||||
|
Phase::NATIVE_WARMUP : Phase::DISABLED;
|
||||||
|
nativeMean = 0;
|
||||||
|
++generation;
|
||||||
|
ResetStageLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (phase)
|
||||||
|
{
|
||||||
|
case Phase::NATIVE_WARMUP:
|
||||||
|
if (warmups >= WarmupCount)
|
||||||
|
{
|
||||||
|
phase = Phase::NATIVE_SAMPLE;
|
||||||
|
++generation;
|
||||||
|
ResetStageLocked();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Phase::NATIVE_SAMPLE:
|
||||||
|
if (sampleCount >= SampleCount)
|
||||||
|
{
|
||||||
|
nativeMean = TrimmedMeanLocked();
|
||||||
|
phase = Phase::PACKED_WARMUP;
|
||||||
|
++generation;
|
||||||
|
ResetStageLocked();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Phase::PACKED_WARMUP:
|
||||||
|
if (warmups >= WarmupCount)
|
||||||
|
{
|
||||||
|
phase = Phase::PACKED_SAMPLE;
|
||||||
|
++generation;
|
||||||
|
ResetStageLocked();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Phase::PACKED_SAMPLE:
|
||||||
|
if (sampleCount >= SampleCount)
|
||||||
|
{
|
||||||
|
const uint64_t packedMean = TrimmedMeanLocked();
|
||||||
|
const uint64_t relativeThreshold = nativeMean / 20;
|
||||||
|
const uint64_t threshold = relativeThreshold > 50000ULL ?
|
||||||
|
relativeThreshold : 50000ULL;
|
||||||
|
// Prefer the bandwidth saving unless native is meaningfully faster.
|
||||||
|
const bool usePacked = packedMean <= nativeMean ||
|
||||||
|
packedMean - nativeMean <= threshold;
|
||||||
|
|
||||||
|
DEBUG_INFO(
|
||||||
|
"RGB24 benchmark: native=%llu us, packed=%llu us, selected=%s",
|
||||||
|
(unsigned long long)(nativeMean / 1000),
|
||||||
|
(unsigned long long)(packedMean / 1000),
|
||||||
|
usePacked ? "packed" : "native");
|
||||||
|
|
||||||
|
phase = usePacked ?
|
||||||
|
Phase::LOCKED_PACKED : Phase::LOCKED_NATIVE;
|
||||||
|
++generation;
|
||||||
|
ResetStageLocked();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseSRWLockExclusive(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WantsPacked()
|
||||||
|
{
|
||||||
|
AcquireSRWLockShared(&lock);
|
||||||
|
const bool result = WantsPackedLocked();
|
||||||
|
ReleaseSRWLockShared(&lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBenchmarking()
|
||||||
|
{
|
||||||
|
AcquireSRWLockShared(&lock);
|
||||||
|
const bool result = IsBenchmarkingLocked();
|
||||||
|
ReleaseSRWLockShared(&lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetTimingToken(bool packed)
|
||||||
|
{
|
||||||
|
AcquireSRWLockShared(&lock);
|
||||||
|
|
||||||
|
const uint64_t result = IsBenchmarkingLocked() &&
|
||||||
|
packed == WantsPackedLocked() ? generation : 0;
|
||||||
|
|
||||||
|
ReleaseSRWLockShared(&lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reject()
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&lock);
|
||||||
|
if (WantsPackedLocked())
|
||||||
|
{
|
||||||
|
phase = Phase::LOCKED_NATIVE;
|
||||||
|
++generation;
|
||||||
|
ResetStageLocked();
|
||||||
|
}
|
||||||
|
ReleaseSRWLockExclusive(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordTiming(uint64_t token, bool fullCopy, uint64_t totalTime)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&lock);
|
||||||
|
|
||||||
|
if (token == generation && fullCopy)
|
||||||
|
switch (phase)
|
||||||
|
{
|
||||||
|
case Phase::NATIVE_WARMUP:
|
||||||
|
case Phase::PACKED_WARMUP:
|
||||||
|
++warmups;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Phase::NATIVE_SAMPLE:
|
||||||
|
case Phase::PACKED_SAMPLE:
|
||||||
|
if (sampleCount < SampleCount)
|
||||||
|
samples[sampleCount++] = totalTime;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseSRWLockExclusive(&lock);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool CRGB24Effect::Init(const ComPtr<ID3D12Device3>& device)
|
bool CRGB24Effect::Init(const ComPtr<ID3D12Device3>& device)
|
||||||
{
|
{
|
||||||
if (!g_settings.ReadBoolValue(L"AllowRGB24", false))
|
if (!g_settings.ReadBoolValue(L"AllowRGB24", true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
D3D12_DESCRIPTOR_RANGE ranges[2] = {};
|
D3D12_DESCRIPTOR_RANGE ranges[2] = {};
|
||||||
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||||
ranges[0].NumDescriptors = 1;
|
ranges[0].NumDescriptors = 1;
|
||||||
ranges[0].BaseShaderRegister = 0;
|
ranges[0].BaseShaderRegister = 0;
|
||||||
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||||
ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||||
ranges[1].NumDescriptors = 1;
|
ranges[1].NumDescriptors = 1;
|
||||||
ranges[1].BaseShaderRegister = 0;
|
ranges[1].BaseShaderRegister = 0;
|
||||||
ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||||
|
|
||||||
const char * shader =
|
const char * shader =
|
||||||
"Texture2D <float4> src : register(t0);\n"
|
"Texture2D<float4> src : register(t0);\n"
|
||||||
"RWTexture2D<float4> dst : register(u0);\n"
|
"RWByteAddressBuffer dst : register(u0);\n"
|
||||||
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
||||||
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" uint fstInputX = (dt.x * 4) / 3;\n"
|
" uint width, height;\n"
|
||||||
" float4 color0 = src[uint2(fstInputX, dt.y)];\n"
|
" src.GetDimensions(width, height);\n"
|
||||||
" uint sndInputX = fstInputX + 1;\n"
|
" uint rowBytes = width * 3;\n"
|
||||||
" float4 color3 = src[uint2(sndInputX, dt.y)];\n"
|
" uint dataWidth = ((rowBytes + 255) & ~255u) / 4;\n"
|
||||||
|
" if (dt.x >= dataWidth || dt.y >= height)\n"
|
||||||
|
" return;\n"
|
||||||
|
" uint rowOffset = dt.x * 4;\n"
|
||||||
|
" if (rowOffset >= rowBytes)\n"
|
||||||
|
" {\n"
|
||||||
|
" dst.Store((dt.y * dataWidth + dt.x) * 4, 0);\n"
|
||||||
|
" return;\n"
|
||||||
|
" }\n"
|
||||||
|
" uint firstX = (dt.x * 4) / 3;\n"
|
||||||
|
" uint secondX = firstX + 1;\n"
|
||||||
|
" float4 color0 = src[uint2(firstX, dt.y)];\n"
|
||||||
|
" float4 color3 = secondX < width ?\n"
|
||||||
|
" src[uint2(secondX, dt.y)] : 0.0f;\n"
|
||||||
" uint xmod3 = dt.x % 3;\n"
|
" uint xmod3 = dt.x % 3;\n"
|
||||||
" float4 color1 = xmod3 <= 1 ? color0 : color3;\n"
|
" float4 color1 = xmod3 <= 1 ? color0 : color3;\n"
|
||||||
" float4 color2 = xmod3 == 0 ? color0 : color3;\n"
|
" float4 color2 = xmod3 == 0 ? color0 : color3;\n"
|
||||||
" float b = color0.bgr[xmod3];\n"
|
" float4 packed = float4(\n"
|
||||||
" float g = color1.grb[xmod3];\n"
|
" color0.bgr[xmod3], color1.grb[xmod3],\n"
|
||||||
" float r = color2.rbg[xmod3];\n"
|
" color2.rbg[xmod3], color3.bgr[xmod3]);\n"
|
||||||
" float a = color3.bgr[xmod3];\n"
|
" uint4 bytes = (uint4)(saturate(packed) * 255.0f + 0.5f);\n"
|
||||||
" dst[dt.xy] = float4(r, g, b, a);\n"
|
" uint value = bytes.x | (bytes.y << 8) |\n"
|
||||||
|
" (bytes.z << 16) | (bytes.w << 24);\n"
|
||||||
|
" dst.Store((dt.y * dataWidth + dt.x) * 4, value);\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
return InitCompute(device, ranges, ARRAYSIZE(ranges), nullptr, 0, shader);
|
if (!InitCompute(device, ranges, ARRAYSIZE(ranges), nullptr, 0, shader))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_state = std::make_shared<State>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRGB24Effect::ShareState(const CPostProcessEffect& other)
|
||||||
|
{
|
||||||
|
m_state = static_cast<const CRGB24Effect&>(other).m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRGB24Effect::Update(const D12FrameFormat& format)
|
||||||
|
{
|
||||||
|
m_state->Update(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRGB24Effect::NeedsReconfigure() const
|
||||||
|
{
|
||||||
|
return m_state->WantsPacked() != Enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRGB24Effect::RequiresFullDamage() const
|
||||||
|
{
|
||||||
|
return m_state->IsBenchmarking();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t CRGB24Effect::GetTimingToken() const
|
||||||
|
{
|
||||||
|
return m_state->GetTimingToken(Enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRGB24Effect::RecordTiming(
|
||||||
|
uint64_t token, bool fullCopy, uint64_t totalTime)
|
||||||
|
{
|
||||||
|
m_state->RecordTiming(token, fullCopy, totalTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRGB24Effect::GetCopyLayout(
|
||||||
|
unsigned * pitch, unsigned * dataHeight) const
|
||||||
|
{
|
||||||
|
*pitch = m_pitch;
|
||||||
|
*dataHeight = m_height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetCopySpan(const RECT& rect, unsigned width,
|
||||||
|
unsigned pitch, UINT64 * left, UINT64 * right)
|
||||||
|
{
|
||||||
|
// Damage stays in logical pixels until the packed copy is recorded.
|
||||||
|
*left = ((UINT64)rect.left * 3) & ~3ULL;
|
||||||
|
if (rect.left == 0 && rect.right == (LONG)width)
|
||||||
|
*right = pitch;
|
||||||
|
else
|
||||||
|
*right = ((UINT64)rect.right * 3 + 3) & ~3ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRGB24Effect::ShouldCopyFully(
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects) const
|
||||||
|
{
|
||||||
|
static const unsigned commandLimit = 256;
|
||||||
|
|
||||||
|
const uint64_t frameSize = (uint64_t)m_pitch * m_height;
|
||||||
|
uint64_t copiedBytes = 0;
|
||||||
|
unsigned commands = 0;
|
||||||
|
for (const RECT * rect = dirtyRects;
|
||||||
|
rect < dirtyRects + nbDirtyRects; ++rect)
|
||||||
|
{
|
||||||
|
UINT64 left;
|
||||||
|
UINT64 right;
|
||||||
|
GetCopySpan(*rect, m_width, m_pitch, &left, &right);
|
||||||
|
|
||||||
|
const unsigned rows = (unsigned)(rect->bottom - rect->top);
|
||||||
|
if (left == 0 && right == m_pitch)
|
||||||
|
{
|
||||||
|
++commands;
|
||||||
|
copiedBytes += (uint64_t)rows * m_pitch;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
commands += rows;
|
||||||
|
copiedBytes += (right - left) * rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commands > commandLimit || copiedBytes >= frameSize)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRGB24Effect::CopyFrame(
|
||||||
|
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||||
|
ID3D12Resource * dst, ID3D12Resource * src,
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects, bool fullCopy) const
|
||||||
|
{
|
||||||
|
if (fullCopy)
|
||||||
|
{
|
||||||
|
commandList->CopyBufferRegion(
|
||||||
|
dst, 0, src, 0, (UINT64)m_pitch * m_height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const RECT * rect = dirtyRects;
|
||||||
|
rect < dirtyRects + nbDirtyRects; ++rect)
|
||||||
|
{
|
||||||
|
UINT64 left;
|
||||||
|
UINT64 right;
|
||||||
|
GetCopySpan(*rect, m_width, m_pitch, &left, &right);
|
||||||
|
|
||||||
|
if (left == 0 && right == m_pitch)
|
||||||
|
{
|
||||||
|
const UINT64 offset = (UINT64)rect->top * m_pitch;
|
||||||
|
const UINT64 size =
|
||||||
|
(UINT64)(rect->bottom - rect->top) * m_pitch;
|
||||||
|
commandList->CopyBufferRegion(dst, offset, src, offset, size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (LONG y = rect->top; y < rect->bottom; ++y)
|
||||||
|
{
|
||||||
|
const UINT64 offset = (UINT64)y * m_pitch + left;
|
||||||
|
commandList->CopyBufferRegion(
|
||||||
|
dst, offset, src, offset, right - left);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PostProcessStatus CRGB24Effect::SetFormat(const ComPtr<ID3D12Device3>& device,
|
PostProcessStatus CRGB24Effect::SetFormat(const ComPtr<ID3D12Device3>& device,
|
||||||
const D12FrameFormat& src, D12FrameFormat& dst)
|
const D12FrameFormat& src, D12FrameFormat& dst)
|
||||||
{
|
{
|
||||||
if (src.desc.Format != DXGI_FORMAT_B8G8R8A8_UNORM)
|
if (!m_state->WantsPacked())
|
||||||
|
{
|
||||||
|
m_dst.Reset();
|
||||||
return PostProcessStatus::BYPASS_EFFECT;
|
return PostProcessStatus::BYPASS_EFFECT;
|
||||||
|
}
|
||||||
|
|
||||||
const unsigned packedPitch = AlignTo((unsigned)src.desc.Width * 3, 4u);
|
if (src.desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
|
||||||
D3D12_RESOURCE_DESC desc = src.desc;
|
src.desc.Format != DXGI_FORMAT_B8G8R8A8_UNORM ||
|
||||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
src.hdr)
|
||||||
desc.Width = AlignTo(packedPitch / 4, 64u);
|
{
|
||||||
desc.Height = ((unsigned)src.desc.Width * src.desc.Height) / (packedPitch / 3);
|
DEBUG_WARN("RGB24 packing is unavailable for the current format");
|
||||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
m_state->Reject();
|
||||||
|
m_dst.Reset();
|
||||||
|
return PostProcessStatus::BYPASS_EFFECT;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CreateDefaultTexture(device, desc, m_dst))
|
if (src.desc.Width >
|
||||||
return PostProcessStatus::FAILED;
|
(UINT64_MAX - (D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1)) / 3)
|
||||||
|
{
|
||||||
|
m_state->Reject();
|
||||||
|
m_dst.Reset();
|
||||||
|
return PostProcessStatus::BYPASS_EFFECT;
|
||||||
|
}
|
||||||
|
|
||||||
m_threadsX = ((unsigned)desc.Width + (Threads - 1)) / Threads;
|
const UINT64 packedPitch = AlignTo<UINT64>(
|
||||||
m_threadsY = ((unsigned)desc.Height + (Threads - 1)) / Threads;
|
src.desc.Width * 3, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||||
|
if (!src.desc.Height || packedPitch > LONG_MAX ||
|
||||||
|
packedPitch > UINT64_MAX / src.desc.Height)
|
||||||
|
{
|
||||||
|
m_state->Reject();
|
||||||
|
m_dst.Reset();
|
||||||
|
return PostProcessStatus::BYPASS_EFFECT;
|
||||||
|
}
|
||||||
|
|
||||||
dst.desc = desc;
|
const UINT64 bufferSize = packedPitch * src.desc.Height;
|
||||||
dst.format = FRAME_TYPE_BGR_32;
|
const UINT64 maxUAVSize =
|
||||||
|
(1ULL << D3D12_REQ_BUFFER_RESOURCE_TEXEL_COUNT_2_TO_EXP) *
|
||||||
|
sizeof(uint32_t);
|
||||||
|
if (bufferSize > UINT32_MAX || bufferSize > maxUAVSize)
|
||||||
|
{
|
||||||
|
m_state->Reject();
|
||||||
|
m_dst.Reset();
|
||||||
|
return PostProcessStatus::BYPASS_EFFECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateDefaultBuffer(device, bufferSize, m_dst))
|
||||||
|
{
|
||||||
|
m_state->Reject();
|
||||||
|
m_dst.Reset();
|
||||||
|
return PostProcessStatus::BYPASS_EFFECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned dataWidth = (unsigned)(packedPitch / 4);
|
||||||
|
m_threadsX = (dataWidth + (Threads - 1)) / Threads;
|
||||||
|
m_threadsY = (src.desc.Height + (Threads - 1)) / Threads;
|
||||||
|
m_width = (unsigned)src.desc.Width;
|
||||||
|
m_height = src.desc.Height;
|
||||||
|
m_pitch = (unsigned)packedPitch;
|
||||||
|
|
||||||
|
dst.desc = m_dst->GetDesc();
|
||||||
|
dst.dataWidth = dataWidth;
|
||||||
|
dst.dataHeight = src.desc.Height;
|
||||||
|
dst.pitch = m_pitch;
|
||||||
|
dst.format = FRAME_TYPE_BGR_32;
|
||||||
return PostProcessStatus::SUCCESS;
|
return PostProcessStatus::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +542,7 @@ ComPtr<ID3D12Resource> CRGB24Effect::Run(const ComPtr<ID3D12Device3>& device,
|
|||||||
UNREFERENCED_PARAMETER(dirtyRects);
|
UNREFERENCED_PARAMETER(dirtyRects);
|
||||||
UNREFERENCED_PARAMETER(nbDirtyRects);
|
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||||
|
|
||||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_COPY_SOURCE,
|
TransitionDst(commandList, D3D12_RESOURCE_STATE_COMMON,
|
||||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||||
|
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||||
@@ -103,31 +551,27 @@ ComPtr<ID3D12Resource> CRGB24Effect::Run(const ComPtr<ID3D12Device3>& device,
|
|||||||
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
||||||
|
|
||||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||||
srvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
srvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||||
srvDesc.Texture2D.MipLevels = 1;
|
srvDesc.Texture2D.MipLevels = 1;
|
||||||
device->CreateShaderResourceView(src.Get(), &srvDesc, handle);
|
device->CreateShaderResourceView(src.Get(), &srvDesc, handle);
|
||||||
handle.ptr += inc;
|
handle.ptr += inc;
|
||||||
|
|
||||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
||||||
uavDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
|
||||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
|
||||||
|
uavDesc.Buffer.NumElements = (UINT)(m_dst->GetDesc().Width / 4);
|
||||||
|
uavDesc.Buffer.StructureByteStride = 0;
|
||||||
|
uavDesc.Buffer.CounterOffsetInBytes = 0;
|
||||||
|
uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
|
||||||
device->CreateUnorderedAccessView(m_dst.Get(), nullptr, &uavDesc, handle);
|
device->CreateUnorderedAccessView(m_dst.Get(), nullptr, &uavDesc, handle);
|
||||||
|
|
||||||
Bind(commandList);
|
Bind(commandList);
|
||||||
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
||||||
|
|
||||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||||
D3D12_RESOURCE_STATE_COPY_SOURCE);
|
D3D12_RESOURCE_STATE_COMMON);
|
||||||
|
|
||||||
for (RECT * rect = dirtyRects; rect < dirtyRects + *nbDirtyRects; ++rect)
|
|
||||||
{
|
|
||||||
const LONG left = rect->left;
|
|
||||||
const LONG right = rect->right;
|
|
||||||
rect->left = (left * 3) / 4;
|
|
||||||
rect->right = (right * 3 + 3) / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_dst;
|
return m_dst;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,33 @@
|
|||||||
|
|
||||||
class CRGB24Effect : public CComputeEffect
|
class CRGB24Effect : public CComputeEffect
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
struct State;
|
||||||
|
std::shared_ptr<State> m_state;
|
||||||
|
unsigned m_width = 0;
|
||||||
|
unsigned m_height = 0;
|
||||||
|
unsigned m_pitch = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char * GetName() const override { return "RGB24"; }
|
const char * GetName() const override { return "RGB24"; }
|
||||||
|
|
||||||
bool Init(const ComPtr<ID3D12Device3>& device);
|
bool Init(const ComPtr<ID3D12Device3>& device);
|
||||||
|
void ShareState(const CPostProcessEffect& other) override;
|
||||||
|
void Update(const D12FrameFormat& format) override;
|
||||||
|
bool NeedsReconfigure() const override;
|
||||||
|
bool RequiresFullDamage() const override;
|
||||||
|
uint64_t GetTimingToken() const override;
|
||||||
|
void RecordTiming(
|
||||||
|
uint64_t token, bool fullCopy, uint64_t totalTime) override;
|
||||||
|
bool GetCopyLayout(
|
||||||
|
unsigned * pitch, unsigned * dataHeight) const override;
|
||||||
|
bool ShouldCopyFully(
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects) const override;
|
||||||
|
void CopyFrame(
|
||||||
|
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||||
|
ID3D12Resource * dst, ID3D12Resource * src,
|
||||||
|
const RECT dirtyRects[], unsigned nbDirtyRects,
|
||||||
|
bool fullCopy) const override;
|
||||||
|
|
||||||
PostProcessStatus SetFormat(const ComPtr<ID3D12Device3>& device,
|
PostProcessStatus SetFormat(const ComPtr<ID3D12Device3>& device,
|
||||||
const D12FrameFormat& src, D12FrameFormat& dst) override;
|
const D12FrameFormat& src, D12FrameFormat& dst) override;
|
||||||
|
|||||||
Reference in New Issue
Block a user