mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
Group the IDD sources and Visual Studio filters by subsystem. Split the device and swap-chain implementations into focused units, rename the context classes, and reduce header coupling.
485 lines
14 KiB
C++
485 lines
14 KiB
C++
/**
|
|
* Looking Glass
|
|
* Copyright © 2017-2026 The Looking Glass Authors
|
|
* https://looking-glass.io
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "postprocess/CPostProcessor.h"
|
|
|
|
#include "d3d/CD3D12Device.h"
|
|
#include "CDebug.h"
|
|
#include "postprocess/effect/CColorTransformEffect.h"
|
|
#include "postprocess/effect/CDownsampleEffect.h"
|
|
#include "postprocess/effect/CHDR16to10Effect.h"
|
|
#include "postprocess/effect/CRGB24Effect.h"
|
|
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <utility>
|
|
|
|
namespace
|
|
{
|
|
bool NearlyEqual(float a, float b, float tolerance)
|
|
{
|
|
const float delta = a - b;
|
|
return delta >= -tolerance && delta <= tolerance;
|
|
}
|
|
}
|
|
|
|
bool IsIdentityColorTransform(const D12ColorTransform& transform)
|
|
{
|
|
static const float matrixTolerance = 1.0f / 1048576.0f;
|
|
static const float lutTolerance = 1.0f / 65535.0f;
|
|
|
|
if (transform.matrixEnabled)
|
|
{
|
|
for (unsigned row = 0; row < 3; ++row)
|
|
for (unsigned column = 0; column < 4; ++column)
|
|
{
|
|
const float expected = row == column ? 1.0f : 0.0f;
|
|
const float effective =
|
|
transform.matrix[row][column] * transform.scalar;
|
|
if (!NearlyEqual(effective, expected, matrixTolerance))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (transform.lutEnabled)
|
|
for (unsigned i = 0; i < 4096; ++i)
|
|
{
|
|
const float expected = (float)i / 4095.0f;
|
|
if (!NearlyEqual(transform.lut[i][0], expected, lutTolerance) ||
|
|
!NearlyEqual(transform.lut[i][1], expected, lutTolerance) ||
|
|
!NearlyEqual(transform.lut[i][2], expected, lutTolerance))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void CopyHDRMetadata(D12FrameFormat& dst, const D12FrameFormat& src)
|
|
{
|
|
dst.hdrMetadata = src.hdrMetadata;
|
|
dst.sdrWhiteLevel = src.sdrWhiteLevel;
|
|
std::memcpy(dst.displayPrimary, src.displayPrimary, sizeof(dst.displayPrimary));
|
|
std::memcpy(dst.whitePoint, src.whitePoint, sizeof(dst.whitePoint));
|
|
dst.maxDisplayLuminance = src.maxDisplayLuminance;
|
|
dst.minDisplayLuminance = src.minDisplayLuminance;
|
|
dst.maxContentLightLevel = src.maxContentLightLevel;
|
|
dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel;
|
|
}
|
|
|
|
bool CPostProcessor::Init(std::shared_ptr<CD3D12Device> dx12Device,
|
|
bool enableEffects)
|
|
{
|
|
m_dx12Device = dx12Device;
|
|
m_device = dx12Device->GetDevice();
|
|
m_effects.clear();
|
|
|
|
if (!enableEffects)
|
|
return true;
|
|
|
|
std::unique_ptr<CColorTransformEffect> colorTransform(new CColorTransformEffect());
|
|
if (colorTransform->Init(m_device))
|
|
{
|
|
DEBUG_INFO("Created post-processing effect: %s", colorTransform->GetName());
|
|
m_effects.push_back(std::move(colorTransform));
|
|
}
|
|
else
|
|
return false;
|
|
|
|
std::unique_ptr<CDownsampleEffect> downsample(new CDownsampleEffect());
|
|
if (downsample->Init(m_device))
|
|
{
|
|
DEBUG_INFO("Created post-processing effect: %s", downsample->GetName());
|
|
m_effects.push_back(std::move(downsample));
|
|
}
|
|
|
|
std::unique_ptr<CHDR16to10Effect> hdr16to10(new CHDR16to10Effect());
|
|
if (hdr16to10->Init(m_device))
|
|
{
|
|
DEBUG_INFO("Created post-processing effect: %s", hdr16to10->GetName());
|
|
m_effects.push_back(std::move(hdr16to10));
|
|
}
|
|
else
|
|
return false;
|
|
|
|
std::unique_ptr<CRGB24Effect> rgb24(new CRGB24Effect());
|
|
if (rgb24->Init(m_device))
|
|
{
|
|
DEBUG_INFO("Created post-processing effect: %s", rgb24->GetName());
|
|
m_effects.push_back(std::move(rgb24));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CPostProcessor::Reset()
|
|
{
|
|
m_effects.clear();
|
|
m_dx12Device.reset();
|
|
m_device.Reset();
|
|
m_srcFormat = {};
|
|
m_dstFormat = {};
|
|
m_copyLayout = {};
|
|
m_copyEffect = nullptr;
|
|
m_frameSize = 0;
|
|
m_pitch = 0;
|
|
m_effectsActive = false;
|
|
m_configured = false;
|
|
}
|
|
|
|
bool CPostProcessor::HasSameEffectChain(const CPostProcessor& other) const
|
|
{
|
|
if (m_effects.size() != other.m_effects.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < m_effects.size(); ++i)
|
|
if (std::strcmp(m_effects[i]->GetName(),
|
|
other.m_effects[i]->GetName()) != 0)
|
|
return false;
|
|
|
|
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
|
|
{
|
|
if (!m_configured ||
|
|
srcFormat.desc.Width != m_srcFormat.desc.Width ||
|
|
srcFormat.desc.Height != m_srcFormat.desc.Height ||
|
|
srcFormat.desc.Format != m_srcFormat.desc.Format ||
|
|
srcFormat.format != m_srcFormat.format ||
|
|
srcFormat.width != m_srcFormat.width ||
|
|
srcFormat.height != m_srcFormat.height ||
|
|
srcFormat.hdr != m_srcFormat.hdr ||
|
|
srcFormat.hdrPQ != m_srcFormat.hdrPQ ||
|
|
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 * formatChanged)
|
|
{
|
|
if (formatChanged)
|
|
*formatChanged = false;
|
|
|
|
if (!NeedsReconfigure(srcFormat))
|
|
{
|
|
// Static HDR metadata may change independently of the resource format.
|
|
// Propagate it without recreating resources or post-processing state.
|
|
CopyHDRMetadata(m_srcFormat, srcFormat);
|
|
CopyHDRMetadata(m_dstFormat, srcFormat);
|
|
return true;
|
|
}
|
|
|
|
D12FrameFormat oldDst = m_dstFormat;
|
|
D12FrameFormat cur = srcFormat;
|
|
CPostProcessEffect * outputEffect = nullptr;
|
|
bool effectsActive = false;
|
|
|
|
for (const auto& effect : m_effects)
|
|
{
|
|
D12FrameFormat dst = cur;
|
|
switch (effect->SetFormat(m_device, cur, dst))
|
|
{
|
|
case PostProcessStatus::SUCCESS:
|
|
effect->Enabled = true;
|
|
effectsActive = true;
|
|
cur = dst;
|
|
outputEffect = effect.get();
|
|
DEBUG_INFO("Post-processing effect active: %s", effect->GetName());
|
|
break;
|
|
|
|
case PostProcessStatus::BYPASS_EFFECT:
|
|
effect->Enabled = false;
|
|
break;
|
|
|
|
case PostProcessStatus::FAILED:
|
|
DEBUG_ERROR("Failed to configure post-processing effect: %s", effect->GetName());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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_dstFormat = cur;
|
|
m_copyLayout = copyLayout;
|
|
m_copyEffect = copyEffect;
|
|
m_frameSize = frameSize;
|
|
m_pitch = pitch;
|
|
m_effectsActive = effectsActive;
|
|
m_configured = true;
|
|
if (formatChanged)
|
|
*formatChanged =
|
|
oldDst.desc.Width != m_dstFormat.desc.Width ||
|
|
oldDst.desc.Height != m_dstFormat.desc.Height ||
|
|
oldDst.desc.Format != m_dstFormat.desc.Format ||
|
|
oldDst.dataWidth != m_dstFormat.dataWidth ||
|
|
oldDst.dataHeight != m_dstFormat.dataHeight ||
|
|
oldDst.pitch != m_dstFormat.pitch ||
|
|
oldDst.format != m_dstFormat.format ||
|
|
oldDst.width != m_dstFormat.width ||
|
|
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;
|
|
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::CopyToFrameBuffer(
|
|
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;
|
|
if (dst->GetDesc().Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D)
|
|
{
|
|
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
|
dstLoc.SubresourceIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
dstLoc.PlacedFootprint = m_copyLayout;
|
|
}
|
|
|
|
if (fullCopy)
|
|
{
|
|
commandList->CopyTextureRegion(
|
|
&dstLoc, 0, 0, 0, &srcLoc, nullptr);
|
|
return;
|
|
}
|
|
|
|
for (const RECT * rect = dirtyRects;
|
|
rect < dirtyRects + nbDirtyRects; ++rect)
|
|
{
|
|
D3D12_BOX box = {};
|
|
box.left = rect->left;
|
|
box.top = rect->top;
|
|
box.front = 0;
|
|
box.right = rect->right;
|
|
box.bottom = rect->bottom;
|
|
box.back = 1;
|
|
|
|
commandList->CopyTextureRegion(
|
|
&dstLoc, box.left, box.top, 0, &srcLoc, &box);
|
|
}
|
|
}
|
|
|
|
void CPostProcessor::CopyToCandidate(
|
|
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
|
ID3D12Resource * dst, ID3D12Resource * src) const
|
|
{
|
|
CopyToFrameBuffer(
|
|
commandList, dst, src, nullptr, 0, true);
|
|
}
|
|
|
|
void CPostProcessor::CopyFromCandidate(
|
|
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_PLACED_FOOTPRINT;
|
|
srcLoc.PlacedFootprint = m_copyLayout;
|
|
|
|
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
|
|
dstLoc.pResource = dst;
|
|
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
|
dstLoc.PlacedFootprint = m_copyLayout;
|
|
|
|
if (fullCopy)
|
|
{
|
|
commandList->CopyBufferRegion(
|
|
dst, 0, src, 0, m_frameSize);
|
|
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)
|
|
{
|
|
for (const auto& effect : m_effects)
|
|
if (effect->Enabled)
|
|
effect->AdjustDamage(dirtyRects, nbDirtyRects);
|
|
}
|
|
|
|
ComPtr<ID3D12Resource> CPostProcessor::Run(
|
|
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
|
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
|
unsigned * nbDirtyRects)
|
|
{
|
|
ComPtr<ID3D12Resource> next = src;
|
|
for (const auto& effect : m_effects)
|
|
{
|
|
if (!effect->Enabled)
|
|
continue;
|
|
|
|
//DEBUG_TRACE("Run post-processing effect: %s", effect->GetName());
|
|
effect->AdjustDamage(dirtyRects, nbDirtyRects);
|
|
next = effect->Run(m_device, commandList, next, dirtyRects, nbDirtyRects);
|
|
}
|
|
|
|
return next;
|
|
}
|