mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] project: organize driver sources by responsibility
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.
This commit is contained in:
484
idd/LGIdd/postprocess/CPostProcessor.cpp
Normal file
484
idd/LGIdd/postprocess/CPostProcessor.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
154
idd/LGIdd/postprocess/CPostProcessor.h
Normal file
154
idd/LGIdd/postprocess/CPostProcessor.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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 <Windows.h>
|
||||
#include <wrl/client.h>
|
||||
#include <d3d12.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "postprocess/D12FrameFormat.h"
|
||||
|
||||
struct CD3D12Device;
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
enum class PostProcessStatus
|
||||
{
|
||||
SUCCESS,
|
||||
BYPASS_EFFECT,
|
||||
FAILED
|
||||
};
|
||||
|
||||
class CPostProcessEffect
|
||||
{
|
||||
public:
|
||||
virtual ~CPostProcessEffect() {}
|
||||
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,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst) = 0;
|
||||
virtual void AdjustDamage(RECT dirtyRects[], unsigned * nbDirtyRects) { UNREFERENCED_PARAMETER(dirtyRects); UNREFERENCED_PARAMETER(nbDirtyRects); }
|
||||
virtual ComPtr<ID3D12Resource> Run(const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects) = 0;
|
||||
bool Enabled = false;
|
||||
};
|
||||
|
||||
class CPostProcessor
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<CD3D12Device> m_dx12Device;
|
||||
ComPtr<ID3D12Device3> m_device;
|
||||
std::vector<std::unique_ptr<CPostProcessEffect>> m_effects;
|
||||
D12FrameFormat m_srcFormat = {};
|
||||
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_configured = false;
|
||||
|
||||
public:
|
||||
bool Init(std::shared_ptr<CD3D12Device> dx12Device,
|
||||
bool enableEffects);
|
||||
void Reset();
|
||||
|
||||
bool HasSameEffectChain(const CPostProcessor& other) const;
|
||||
bool ShareEffectState(const CPostProcessor& other);
|
||||
void Update(const D12FrameFormat& srcFormat);
|
||||
bool NeedsReconfigure(const D12FrameFormat& srcFormat) const;
|
||||
bool RequiresFullDamage() const;
|
||||
bool Configure(const D12FrameFormat& srcFormat, bool * formatChanged);
|
||||
void AdjustFrameDamage(RECT dirtyRects[], unsigned * nbDirtyRects);
|
||||
ComPtr<ID3D12Resource> Run(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects);
|
||||
|
||||
const D12FrameFormat& GetOutputFormat() const { return m_dstFormat; }
|
||||
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 CopyToFrameBuffer(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
ID3D12Resource * dst, ID3D12Resource * src,
|
||||
const RECT dirtyRects[], unsigned nbDirtyRects, bool fullCopy) const;
|
||||
void CopyToCandidate(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
ID3D12Resource * dst, ID3D12Resource * src) const;
|
||||
void CopyFromCandidate(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
ID3D12Resource * dst, ID3D12Resource * src,
|
||||
const RECT dirtyRects[], unsigned nbDirtyRects, bool fullCopy) const;
|
||||
};
|
||||
70
idd/LGIdd/postprocess/D12FrameFormat.h
Normal file
70
idd/LGIdd/postprocess/D12FrameFormat.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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 <Windows.h>
|
||||
#include <d3d12.h>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
#include "common/types.h"
|
||||
}
|
||||
|
||||
struct D12ColorTransform
|
||||
{
|
||||
bool matrixEnabled = false;
|
||||
float matrix[3][4] = {};
|
||||
float scalar = 1.0f;
|
||||
bool lutEnabled = false;
|
||||
float lut[4096][4] = {};
|
||||
};
|
||||
|
||||
bool IsIdentityColorTransform(const D12ColorTransform& transform);
|
||||
|
||||
struct D12FrameFormat
|
||||
{
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
unsigned dataWidth = 0;
|
||||
unsigned dataHeight = 0;
|
||||
unsigned pitch = 0;
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
FrameType format = FRAME_TYPE_INVALID;
|
||||
bool hdr = false;
|
||||
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)
|
||||
// Display color primaries in 0.00002 units (xy coordinates)
|
||||
uint16_t displayPrimary[3][2];
|
||||
// White point in 0.00002 units
|
||||
uint16_t whitePoint[2];
|
||||
// Max mastering display luminance in whole cd/m²
|
||||
uint32_t maxDisplayLuminance;
|
||||
// Min mastering display luminance in 0.0001 cd/m² units
|
||||
uint32_t minDisplayLuminance;
|
||||
// MaxCLL and MaxFALL in cd/m²
|
||||
uint32_t maxContentLightLevel;
|
||||
uint32_t maxFrameAverageLightLevel;
|
||||
};
|
||||
337
idd/LGIdd/postprocess/effect/CColorTransformEffect.cpp
Normal file
337
idd/LGIdd/postprocess/effect/CColorTransformEffect.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
/**
|
||||
* 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 "CColorTransformEffect.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace PostProcessUtil;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum TransferFunction : UINT
|
||||
{
|
||||
TRANSFER_LINEAR,
|
||||
TRANSFER_SRGB,
|
||||
TRANSFER_PQ,
|
||||
};
|
||||
|
||||
bool CreateUploadBuffer(const ComPtr<ID3D12Device3>& device, size_t size,
|
||||
ComPtr<ID3D12Resource>& resource)
|
||||
{
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
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;
|
||||
|
||||
const HRESULT hr = device->CreateCommittedResource(&heapProps,
|
||||
D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr, IID_PPV_ARGS(&resource));
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
bool Upload(const ComPtr<ID3D12Resource>& resource,
|
||||
const void * data, size_t size)
|
||||
{
|
||||
void * dst = nullptr;
|
||||
const D3D12_RANGE readRange = { 0, 0 };
|
||||
if (FAILED(resource->Map(0, &readRange, &dst)))
|
||||
return false;
|
||||
std::memcpy(dst, data, size);
|
||||
resource->Unmap(0, nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CColorTransformEffect::Init(const ComPtr<ID3D12Device3>& device)
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE ranges[4] = {};
|
||||
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
|
||||
ranges[0].NumDescriptors = 1;
|
||||
ranges[0].BaseShaderRegister = 0;
|
||||
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
ranges[1].NumDescriptors = 1;
|
||||
ranges[1].BaseShaderRegister = 0;
|
||||
ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
ranges[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
ranges[2].NumDescriptors = 1;
|
||||
ranges[2].BaseShaderRegister = 1;
|
||||
ranges[2].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
ranges[3].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
ranges[3].NumDescriptors = 1;
|
||||
ranges[3].BaseShaderRegister = 0;
|
||||
ranges[3].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
|
||||
const char * shader =
|
||||
"cbuffer Constants : register(b0)\n"
|
||||
"{\n"
|
||||
" float4 ColorMatrix[3];\n"
|
||||
" float Scalar;\n"
|
||||
" uint MatrixEnabled;\n"
|
||||
" uint LutEnabled;\n"
|
||||
" uint InputTransfer;\n"
|
||||
" uint OutputTransfer;\n"
|
||||
"};\n"
|
||||
"Texture2D<float4> src : register(t0);\n"
|
||||
"Buffer<float4> lut : register(t1);\n"
|
||||
"RWTexture2D<float4> dst : register(u0);\n"
|
||||
"static const float PQ_m1 = 0.1593017578125;\n"
|
||||
"static const float PQ_m2 = 78.84375;\n"
|
||||
"static const float PQ_c1 = 0.8359375;\n"
|
||||
"static const float PQ_c2 = 18.8515625;\n"
|
||||
"static const float PQ_c3 = 18.6875;\n"
|
||||
"float3 decode(float3 value)\n"
|
||||
"{\n"
|
||||
" if (InputTransfer == 1)\n"
|
||||
" return lerp(value / 12.92, pow((value + 0.055) / 1.055, 2.4),\n"
|
||||
" step(0.04045, value));\n"
|
||||
" if (InputTransfer == 2)\n"
|
||||
" {\n"
|
||||
" float3 p = pow(max(value, 0.0), 1.0 / PQ_m2);\n"
|
||||
" return pow(max(p - PQ_c1, 0.0) / max(PQ_c2 - PQ_c3 * p, 1e-6),\n"
|
||||
" 1.0 / PQ_m1);\n"
|
||||
" }\n"
|
||||
" return value;\n"
|
||||
"}\n"
|
||||
"float3 encode(float3 value)\n"
|
||||
"{\n"
|
||||
" if (OutputTransfer == 1)\n"
|
||||
" return lerp(value * 12.92, 1.055 * pow(max(value, 0.0), 1.0 / 2.4) - 0.055,\n"
|
||||
" step(0.0031308, value));\n"
|
||||
" if (OutputTransfer == 2)\n"
|
||||
" {\n"
|
||||
" float3 p = pow(max(value, 0.0), PQ_m1);\n"
|
||||
" return pow((PQ_c1 + PQ_c2 * p) / (1.0 + PQ_c3 * p), PQ_m2);\n"
|
||||
" }\n"
|
||||
" return value;\n"
|
||||
"}\n"
|
||||
"float3 rgbToXYZ(float3 rgb)\n"
|
||||
"{\n"
|
||||
" if (InputTransfer == 2)\n"
|
||||
" return float3(\n"
|
||||
" dot(rgb, float3(0.6369580, 0.1446169, 0.1688810)),\n"
|
||||
" dot(rgb, float3(0.2627002, 0.6779981, 0.0593017)),\n"
|
||||
" dot(rgb, float3(0.0000000, 0.0280727, 1.0609851)));\n"
|
||||
" return float3(\n"
|
||||
" dot(rgb, float3(0.4123908, 0.3575843, 0.1804808)),\n"
|
||||
" dot(rgb, float3(0.2126390, 0.7151687, 0.0721923)),\n"
|
||||
" dot(rgb, float3(0.0193308, 0.1191948, 0.9505322)));\n"
|
||||
"}\n"
|
||||
"float3 xyzToRGB(float3 xyz)\n"
|
||||
"{\n"
|
||||
" if (OutputTransfer == 2)\n"
|
||||
" return float3(\n"
|
||||
" dot(xyz, float3( 1.7166512, -0.3556708, -0.2533663)),\n"
|
||||
" dot(xyz, float3(-0.6666844, 1.6164812, 0.0157685)),\n"
|
||||
" dot(xyz, float3( 0.0176399, -0.0427706, 0.9421031)));\n"
|
||||
" return float3(\n"
|
||||
" dot(xyz, float3( 3.2409699, -1.5373832, -0.4986108)),\n"
|
||||
" dot(xyz, float3(-0.9692436, 1.8759675, 0.0415551)),\n"
|
||||
" dot(xyz, float3( 0.0556301, -0.2039770, 1.0569715)));\n"
|
||||
"}\n"
|
||||
"float3 applyLut(float3 value)\n"
|
||||
"{\n"
|
||||
" float3 pos = saturate(value) * 4095.0;\n"
|
||||
" uint3 lo = (uint3)floor(pos);\n"
|
||||
" uint3 hi = min(lo + 1, 4095);\n"
|
||||
" float3 f = frac(pos);\n"
|
||||
" return float3(\n"
|
||||
" lerp(lut[lo.r].r, lut[hi.r].r, f.r),\n"
|
||||
" lerp(lut[lo.g].g, lut[hi.g].g, f.g),\n"
|
||||
" lerp(lut[lo.b].b, lut[hi.b].b, f.b));\n"
|
||||
"}\n"
|
||||
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
||||
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
||||
"{\n"
|
||||
" float4 pixel = src[dt.xy];\n"
|
||||
" float3 value = decode(pixel.rgb);\n"
|
||||
" if (MatrixEnabled != 0 || InputTransfer != OutputTransfer)\n"
|
||||
" {\n"
|
||||
" float3 xyz = rgbToXYZ(value);\n"
|
||||
" if (MatrixEnabled != 0)\n"
|
||||
" xyz = float3(dot(float4(xyz, 1.0), ColorMatrix[0]),\n"
|
||||
" dot(float4(xyz, 1.0), ColorMatrix[1]),\n"
|
||||
" dot(float4(xyz, 1.0), ColorMatrix[2])) * Scalar;\n"
|
||||
" value = xyzToRGB(xyz);\n"
|
||||
" }\n"
|
||||
" if (InputTransfer == 0 && OutputTransfer == 2)\n"
|
||||
" value *= 80.0 / 10000.0;\n"
|
||||
" value = encode(value);\n"
|
||||
" if (LutEnabled != 0)\n"
|
||||
" value = applyLut(value);\n"
|
||||
" dst[dt.xy] = float4(value, pixel.a);\n"
|
||||
"}\n";
|
||||
|
||||
if (!InitCompute(device, ranges, ARRAYSIZE(ranges), nullptr, 0, shader))
|
||||
return false;
|
||||
|
||||
const size_t constSize = AlignTo(sizeof(m_consts),
|
||||
(size_t)D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
||||
if (!CreateUploadBuffer(device, constSize, m_constBuffer) ||
|
||||
!CreateUploadBuffer(device, sizeof(float) * 4096 * 4, m_lutBuffer))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create color transform buffers");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PostProcessStatus CColorTransformEffect::SetFormat(
|
||||
const ComPtr<ID3D12Device3>& device,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst)
|
||||
{
|
||||
if (!src.colorTransform || IsIdentityColorTransform(*src.colorTransform) ||
|
||||
(!src.colorTransform->matrixEnabled && !src.colorTransform->lutEnabled))
|
||||
return PostProcessStatus::BYPASS_EFFECT;
|
||||
|
||||
DXGI_FORMAT dstFormat;
|
||||
FrameType frameType;
|
||||
switch (src.desc.Format)
|
||||
{
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||
dstFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
frameType = FRAME_TYPE_RGBA;
|
||||
break;
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM:
|
||||
dstFormat = src.desc.Format;
|
||||
frameType = src.format;
|
||||
break;
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
||||
// The client wire format is HDR10. Perform the XYZ adjustment before
|
||||
// the BT.2020 rotation, and its LUT after PQ encoding, in one pass.
|
||||
dstFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
|
||||
frameType = FRAME_TYPE_RGBA10;
|
||||
break;
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported color transform source format %u", src.desc.Format);
|
||||
return PostProcessStatus::FAILED;
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_DESC desc = src.desc;
|
||||
desc.Format = dstFormat;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
if (!m_dst || m_dst->GetDesc().Width != desc.Width ||
|
||||
m_dst->GetDesc().Height != desc.Height ||
|
||||
m_dst->GetDesc().Format != desc.Format)
|
||||
{
|
||||
if (!CreateDefaultTexture(device, desc, m_dst))
|
||||
return PostProcessStatus::FAILED;
|
||||
}
|
||||
|
||||
std::memcpy(m_consts.matrix, src.colorTransform->matrix,
|
||||
sizeof(m_consts.matrix));
|
||||
m_consts.scalar = src.colorTransform->scalar;
|
||||
m_consts.matrixEnabled = src.colorTransform->matrixEnabled;
|
||||
m_consts.lutEnabled = src.colorTransform->lutEnabled;
|
||||
m_consts.inputTransfer = src.hdrPQ ? TRANSFER_PQ :
|
||||
(src.hdr ? TRANSFER_LINEAR : TRANSFER_SRGB);
|
||||
m_consts.outputTransfer = src.hdr ? TRANSFER_PQ : TRANSFER_SRGB;
|
||||
|
||||
std::memcpy(m_lut, src.colorTransform->lut, sizeof(m_lut));
|
||||
m_uploadPending = true;
|
||||
|
||||
m_srcFormat = src.desc.Format;
|
||||
m_dstFormat = dstFormat;
|
||||
m_threadsX = ((unsigned)desc.Width + (Threads - 1)) / Threads;
|
||||
m_threadsY = ((unsigned)desc.Height + (Threads - 1)) / Threads;
|
||||
|
||||
dst.desc = desc;
|
||||
dst.format = frameType;
|
||||
if (src.hdr)
|
||||
dst.hdrPQ = true;
|
||||
return PostProcessStatus::SUCCESS;
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> CColorTransformEffect::Run(
|
||||
const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(dirtyRects);
|
||||
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||
|
||||
// The framebuffer-indexed compute slot waits for this chain's previous
|
||||
// submission before Run is called, so this is the first point where its
|
||||
// upload buffers are guaranteed not to be in use by the GPU.
|
||||
if (m_uploadPending)
|
||||
{
|
||||
if (!Upload(m_constBuffer, &m_consts, sizeof(m_consts)) ||
|
||||
!Upload(m_lutBuffer, m_lut, sizeof(m_lut)))
|
||||
DEBUG_ERROR("Failed to upload display color transform");
|
||||
else
|
||||
m_uploadPending = false;
|
||||
}
|
||||
|
||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_COMMON,
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||
m_descHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
const UINT inc = device->GetDescriptorHandleIncrementSize(
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
||||
|
||||
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
|
||||
cbvDesc.BufferLocation = m_constBuffer->GetGPUVirtualAddress();
|
||||
cbvDesc.SizeInBytes = (UINT)AlignTo(sizeof(m_consts),
|
||||
(size_t)D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
||||
device->CreateConstantBufferView(&cbvDesc, handle);
|
||||
handle.ptr += inc;
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||
srvDesc.Format = m_srcFormat;
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
device->CreateShaderResourceView(src.Get(), &srvDesc, handle);
|
||||
handle.ptr += inc;
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC lutDesc = {};
|
||||
lutDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
lutDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
|
||||
lutDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
lutDesc.Buffer.NumElements = 4096;
|
||||
device->CreateShaderResourceView(m_lutBuffer.Get(), &lutDesc, handle);
|
||||
handle.ptr += inc;
|
||||
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
||||
uavDesc.Format = m_dstFormat;
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||
device->CreateUnorderedAccessView(m_dst.Get(), nullptr, &uavDesc, handle);
|
||||
|
||||
Bind(commandList);
|
||||
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
||||
|
||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||
D3D12_RESOURCE_STATE_COMMON);
|
||||
return m_dst;
|
||||
}
|
||||
57
idd/LGIdd/postprocess/effect/CColorTransformEffect.h
Normal file
57
idd/LGIdd/postprocess/effect/CColorTransformEffect.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 "CComputeEffect.h"
|
||||
|
||||
class CColorTransformEffect : public CComputeEffect
|
||||
{
|
||||
private:
|
||||
struct Consts
|
||||
{
|
||||
float matrix[3][4];
|
||||
float scalar;
|
||||
UINT matrixEnabled;
|
||||
UINT lutEnabled;
|
||||
UINT inputTransfer;
|
||||
UINT outputTransfer;
|
||||
} m_consts = {};
|
||||
float m_lut[4096][4] = {};
|
||||
bool m_uploadPending = false;
|
||||
|
||||
ComPtr<ID3D12Resource> m_constBuffer;
|
||||
ComPtr<ID3D12Resource> m_lutBuffer;
|
||||
DXGI_FORMAT m_srcFormat = DXGI_FORMAT_UNKNOWN;
|
||||
DXGI_FORMAT m_dstFormat = DXGI_FORMAT_UNKNOWN;
|
||||
|
||||
public:
|
||||
const char * GetName() const override { return "ColorTransform"; }
|
||||
|
||||
bool Init(const ComPtr<ID3D12Device3>& device);
|
||||
|
||||
PostProcessStatus SetFormat(const ComPtr<ID3D12Device3>& device,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst) override;
|
||||
|
||||
ComPtr<ID3D12Resource> Run(const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects) override;
|
||||
};
|
||||
195
idd/LGIdd/postprocess/effect/CComputeEffect.cpp
Normal file
195
idd/LGIdd/postprocess/effect/CComputeEffect.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* 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 "CComputeEffect.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
|
||||
#include <d3dcompiler.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace PostProcessUtil
|
||||
{
|
||||
static bool CreateDefaultResource(const ComPtr<ID3D12Device3>& device,
|
||||
const D3D12_RESOURCE_DESC& desc, ComPtr<ID3D12Resource>& resource)
|
||||
{
|
||||
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;
|
||||
|
||||
HRESULT hr = device->CreateCommittedResource(
|
||||
&heapProps,
|
||||
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED,
|
||||
&desc,
|
||||
D3D12_RESOURCE_STATE_COMMON,
|
||||
nullptr,
|
||||
IID_PPV_ARGS(&resource));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create post-processing destination resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
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,
|
||||
const D3D12_DESCRIPTOR_RANGE * ranges, UINT rangeCount,
|
||||
const D3D12_STATIC_SAMPLER_DESC * samplers, UINT samplerCount,
|
||||
const char * shader)
|
||||
{
|
||||
D3D12_ROOT_PARAMETER rootParam = {};
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.DescriptorTable.NumDescriptorRanges = rangeCount;
|
||||
rootParam.DescriptorTable.pDescriptorRanges = ranges;
|
||||
|
||||
D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
|
||||
rootSignatureDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1;
|
||||
rootSignatureDesc.Desc_1_0.NumParameters = 1;
|
||||
rootSignatureDesc.Desc_1_0.pParameters = &rootParam;
|
||||
rootSignatureDesc.Desc_1_0.NumStaticSamplers = samplerCount;
|
||||
rootSignatureDesc.Desc_1_0.pStaticSamplers = samplers;
|
||||
rootSignatureDesc.Desc_1_0.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
|
||||
|
||||
ComPtr<ID3DBlob> blob;
|
||||
ComPtr<ID3DBlob> error;
|
||||
HRESULT hr = D3D12SerializeVersionedRootSignature(
|
||||
&rootSignatureDesc, &blob, &error);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to serialize post-processing root signature");
|
||||
if (error)
|
||||
DEBUG_ERROR("%s", (const char *)error->GetBufferPointer());
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = device->CreateRootSignature(
|
||||
0,
|
||||
blob->GetBufferPointer(),
|
||||
blob->GetBufferSize(),
|
||||
IID_PPV_ARGS(&m_rootSignature));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create post-processing root signature");
|
||||
return false;
|
||||
}
|
||||
|
||||
blob.Reset();
|
||||
error.Reset();
|
||||
hr = D3DCompile(
|
||||
shader,
|
||||
std::strlen(shader),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"main",
|
||||
"cs_5_0",
|
||||
0,
|
||||
0,
|
||||
&blob,
|
||||
&error);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to compile post-processing shader");
|
||||
if (error)
|
||||
DEBUG_ERROR("%s", (const char *)error->GetBufferPointer());
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
|
||||
psoDesc.pRootSignature = m_rootSignature.Get();
|
||||
psoDesc.CS.pShaderBytecode = blob->GetBufferPointer();
|
||||
psoDesc.CS.BytecodeLength = blob->GetBufferSize();
|
||||
|
||||
hr = device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&m_pso));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create post-processing PSO");
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT descriptorCount = 0;
|
||||
for (UINT i = 0; i < rangeCount; ++i)
|
||||
descriptorCount += ranges[i].NumDescriptors;
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
|
||||
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
|
||||
heapDesc.NumDescriptors = descriptorCount;
|
||||
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||
|
||||
hr = device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_descHeap));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create post-processing descriptor heap");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CComputeEffect::Bind(const ComPtr<ID3D12GraphicsCommandList>& commandList)
|
||||
{
|
||||
ID3D12DescriptorHeap * heaps[] = { m_descHeap.Get() };
|
||||
commandList->SetDescriptorHeaps(1, heaps);
|
||||
commandList->SetPipelineState(m_pso.Get());
|
||||
commandList->SetComputeRootSignature(m_rootSignature.Get());
|
||||
commandList->SetComputeRootDescriptorTable(
|
||||
0, m_descHeap->GetGPUDescriptorHandleForHeapStart());
|
||||
}
|
||||
|
||||
void CComputeEffect::TransitionDst(
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after)
|
||||
{
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = m_dst.Get();
|
||||
barrier.Transition.StateBefore = before;
|
||||
barrier.Transition.StateAfter = after;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
commandList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
63
idd/LGIdd/postprocess/effect/CComputeEffect.h
Normal file
63
idd/LGIdd/postprocess/effect/CComputeEffect.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 "postprocess/CPostProcessor.h"
|
||||
|
||||
|
||||
#define POST_PROCESS_THREADS_STR "8"
|
||||
|
||||
namespace PostProcessUtil
|
||||
{
|
||||
static constexpr unsigned Threads = 8;
|
||||
|
||||
template<typename T>
|
||||
static constexpr T AlignTo(T value, T align)
|
||||
{
|
||||
return (value + (align - 1)) & ~(align - 1);
|
||||
}
|
||||
|
||||
bool CreateDefaultTexture(const ComPtr<ID3D12Device3>& device,
|
||||
const D3D12_RESOURCE_DESC& desc, ComPtr<ID3D12Resource>& resource);
|
||||
bool CreateDefaultBuffer(const ComPtr<ID3D12Device3>& device,
|
||||
UINT64 size, ComPtr<ID3D12Resource>& resource);
|
||||
}
|
||||
|
||||
class CComputeEffect : public CPostProcessEffect
|
||||
{
|
||||
protected:
|
||||
ComPtr<ID3D12RootSignature> m_rootSignature;
|
||||
ComPtr<ID3D12PipelineState> m_pso;
|
||||
ComPtr<ID3D12DescriptorHeap> m_descHeap;
|
||||
ComPtr<ID3D12Resource> m_dst;
|
||||
unsigned m_threadsX = 0;
|
||||
unsigned m_threadsY = 0;
|
||||
|
||||
bool InitCompute(const ComPtr<ID3D12Device3>& device,
|
||||
const D3D12_DESCRIPTOR_RANGE * ranges, UINT rangeCount,
|
||||
const D3D12_STATIC_SAMPLER_DESC * samplers, UINT samplerCount,
|
||||
const char * shader);
|
||||
|
||||
void Bind(const ComPtr<ID3D12GraphicsCommandList>& commandList);
|
||||
|
||||
void TransitionDst(const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after);
|
||||
};
|
||||
278
idd/LGIdd/postprocess/effect/CDownsampleEffect.cpp
Normal file
278
idd/LGIdd/postprocess/effect/CDownsampleEffect.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* 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 "CDownsampleEffect.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "config/CSettings.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cwchar>
|
||||
#include <cwctype>
|
||||
#include <cstring>
|
||||
|
||||
using namespace PostProcessUtil;
|
||||
|
||||
bool CDownsampleEffect::ParseRules(const std::wstring& value)
|
||||
{
|
||||
m_rules.clear();
|
||||
if (value.empty())
|
||||
return false;
|
||||
|
||||
size_t pos = 0;
|
||||
while (pos < value.size())
|
||||
{
|
||||
size_t comma = value.find(L',', pos);
|
||||
std::wstring token = value.substr(pos,
|
||||
comma == std::wstring::npos ? std::wstring::npos : comma - pos);
|
||||
|
||||
while (!token.empty() && std::iswspace(token.front()))
|
||||
token.erase(token.begin());
|
||||
while (!token.empty() && std::iswspace(token.back()))
|
||||
token.pop_back();
|
||||
|
||||
if (!token.empty())
|
||||
{
|
||||
Rule rule = {};
|
||||
const wchar_t * start = token.c_str();
|
||||
if (*start == L'>')
|
||||
{
|
||||
rule.greater = true;
|
||||
++start;
|
||||
}
|
||||
|
||||
if (swscanf_s(start, L"%ux%u:%ux%u",
|
||||
&rule.x, &rule.y, &rule.targetX, &rule.targetY) != 4)
|
||||
{
|
||||
DEBUG_ERROR("Unable to parse IDD downsample rule");
|
||||
m_rules.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_INFO("idd:downsample rule: %ux%u -> %ux%u%s",
|
||||
rule.x, rule.y, rule.targetX, rule.targetY,
|
||||
rule.greater ? " (greater-than)" : "");
|
||||
m_rules.push_back(rule);
|
||||
}
|
||||
|
||||
if (comma == std::wstring::npos)
|
||||
break;
|
||||
pos = comma + 1;
|
||||
}
|
||||
|
||||
return !m_rules.empty();
|
||||
}
|
||||
|
||||
const CDownsampleEffect::Rule * CDownsampleEffect::MatchRule(
|
||||
unsigned width, unsigned height) const
|
||||
{
|
||||
const Rule * match = nullptr;
|
||||
for (const auto& rule : m_rules)
|
||||
if (( rule.greater && (width > rule.x || height > rule.y)) ||
|
||||
(!rule.greater && (width == rule.x && height == rule.y)))
|
||||
match = &rule;
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
bool CDownsampleEffect::Init(const ComPtr<ID3D12Device3>& device)
|
||||
{
|
||||
if (!ParseRules(g_settings.ReadStringValue(L"Downsample")))
|
||||
return false;
|
||||
|
||||
D3D12_STATIC_SAMPLER_DESC sampler = {};
|
||||
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
|
||||
sampler.MaxLOD = D3D12_FLOAT32_MAX;
|
||||
sampler.ShaderRegister = 0;
|
||||
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
|
||||
D3D12_DESCRIPTOR_RANGE ranges[3] = {};
|
||||
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
|
||||
ranges[0].NumDescriptors = 1;
|
||||
ranges[0].BaseShaderRegister = 0;
|
||||
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
ranges[1].NumDescriptors = 1;
|
||||
ranges[1].BaseShaderRegister = 0;
|
||||
ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
ranges[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
ranges[2].NumDescriptors = 1;
|
||||
ranges[2].BaseShaderRegister = 0;
|
||||
ranges[2].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
|
||||
const char * shader =
|
||||
"cbuffer Constants : register(b0)\n"
|
||||
"{\n"
|
||||
" float Width;\n"
|
||||
" float Height;\n"
|
||||
"};\n"
|
||||
"Texture2D <float4> src : register(t0);\n"
|
||||
"RWTexture2D<float4> dst : register(u0);\n"
|
||||
"SamplerState ss : register(s0);\n"
|
||||
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
||||
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
||||
"{\n"
|
||||
" dst[dt.xy] = src.SampleLevel(ss,\n"
|
||||
" float2(\n"
|
||||
" (float(dt.x) + 0.5f) / Width,\n"
|
||||
" (float(dt.y) + 0.5f) / Height),\n"
|
||||
" 0);\n"
|
||||
"}\n";
|
||||
|
||||
if (!InitCompute(device, ranges, ARRAYSIZE(ranges), &sampler, 1, shader))
|
||||
return false;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Width = AlignTo(sizeof(m_consts),
|
||||
(size_t)D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
|
||||
HRESULT hr = device->CreateCommittedResource(&heapProps,
|
||||
D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr, IID_PPV_ARGS(&m_constBuffer));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create Downsample constant buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PostProcessStatus CDownsampleEffect::SetFormat(
|
||||
const ComPtr<ID3D12Device3>& device,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst)
|
||||
{
|
||||
const Rule * rule = MatchRule((unsigned)src.desc.Width, src.desc.Height);
|
||||
if (!rule ||
|
||||
(rule->targetX == src.desc.Width && rule->targetY == src.desc.Height))
|
||||
return PostProcessStatus::BYPASS_EFFECT;
|
||||
|
||||
D3D12_RESOURCE_DESC desc = src.desc;
|
||||
desc.Width = rule->targetX;
|
||||
desc.Height = rule->targetY;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
|
||||
if (!CreateDefaultTexture(device, desc, m_dst))
|
||||
return PostProcessStatus::FAILED;
|
||||
|
||||
m_consts.width = (float)rule->targetX;
|
||||
m_consts.height = (float)rule->targetY;
|
||||
|
||||
void * data = nullptr;
|
||||
D3D12_RANGE readRange = { 0, 0 };
|
||||
HRESULT hr = m_constBuffer->Map(0, &readRange, &data);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to map Downsample constant buffer");
|
||||
return PostProcessStatus::FAILED;
|
||||
}
|
||||
std::memcpy(data, &m_consts, sizeof(m_consts));
|
||||
m_constBuffer->Unmap(0, nullptr);
|
||||
|
||||
m_threadsX = ((unsigned)desc.Width + (Threads - 1)) / Threads;
|
||||
m_threadsY = ((unsigned)desc.Height + (Threads - 1)) / Threads;
|
||||
m_format = src.desc.Format;
|
||||
m_scaleX = (double)desc.Width / src.desc.Width;
|
||||
m_scaleY = (double)desc.Height / src.desc.Height;
|
||||
m_width = (unsigned)desc.Width;
|
||||
m_height = desc.Height;
|
||||
|
||||
dst.desc = desc;
|
||||
dst.width = (unsigned)desc.Width;
|
||||
dst.height = desc.Height;
|
||||
return PostProcessStatus::SUCCESS;
|
||||
}
|
||||
|
||||
void CDownsampleEffect::AdjustDamage(RECT dirtyRects[], unsigned * nbDirtyRects)
|
||||
{
|
||||
for (RECT * rect = dirtyRects; rect < dirtyRects + *nbDirtyRects; ++rect)
|
||||
{
|
||||
unsigned width = (unsigned)std::ceil((double)(rect->right - rect->left) * m_scaleX);
|
||||
unsigned height = (unsigned)std::ceil((double)(rect->bottom - rect->top ) * m_scaleY);
|
||||
rect->left = (LONG)max(0.0, std::floor((double)rect->left * m_scaleX));
|
||||
rect->right = (LONG)min((double)m_width , (double)rect->left + width);
|
||||
rect->top = (LONG)max(0.0, std::floor((double)rect->top * m_scaleY));
|
||||
rect->bottom = (LONG)min((double)m_height, (double)rect->top + height);
|
||||
|
||||
if (rect->left > 0 ) rect->left -= 1;
|
||||
if (rect->top > 0 ) rect->top -= 1;
|
||||
if (rect->right < (LONG)m_width ) rect->right += 1;
|
||||
if (rect->bottom < (LONG)m_height ) rect->bottom += 1;
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> CDownsampleEffect::Run(
|
||||
const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(dirtyRects);
|
||||
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||
|
||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_COMMON,
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||
m_descHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
const UINT inc = device->GetDescriptorHandleIncrementSize(
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
||||
|
||||
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
|
||||
cbvDesc.BufferLocation = m_constBuffer->GetGPUVirtualAddress();
|
||||
cbvDesc.SizeInBytes = (UINT)AlignTo(sizeof(m_consts),
|
||||
(size_t)D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
||||
device->CreateConstantBufferView(&cbvDesc, handle);
|
||||
handle.ptr += inc;
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||
srvDesc.Format = m_format;
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
device->CreateShaderResourceView(src.Get(), &srvDesc, handle);
|
||||
handle.ptr += inc;
|
||||
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
||||
uavDesc.Format = m_format;
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||
device->CreateUnorderedAccessView(m_dst.Get(), nullptr, &uavDesc, handle);
|
||||
|
||||
Bind(commandList);
|
||||
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
||||
|
||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||
D3D12_RESOURCE_STATE_COMMON);
|
||||
return m_dst;
|
||||
}
|
||||
71
idd/LGIdd/postprocess/effect/CDownsampleEffect.h
Normal file
71
idd/LGIdd/postprocess/effect/CDownsampleEffect.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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 "CComputeEffect.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class CDownsampleEffect : public CComputeEffect
|
||||
{
|
||||
private:
|
||||
struct Rule
|
||||
{
|
||||
bool greater = false;
|
||||
unsigned x = 0;
|
||||
unsigned y = 0;
|
||||
unsigned targetX = 0;
|
||||
unsigned targetY = 0;
|
||||
};
|
||||
|
||||
struct Consts
|
||||
{
|
||||
float width;
|
||||
float height;
|
||||
} m_consts = {};
|
||||
|
||||
std::vector<Rule> m_rules;
|
||||
ComPtr<ID3D12Resource> m_constBuffer;
|
||||
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
|
||||
double m_scaleX = 1.0;
|
||||
double m_scaleY = 1.0;
|
||||
unsigned m_width = 0;
|
||||
unsigned m_height = 0;
|
||||
|
||||
bool ParseRules(const std::wstring& value);
|
||||
const Rule * MatchRule(unsigned width, unsigned height) const;
|
||||
|
||||
public:
|
||||
const char * GetName() const override { return "Downsample"; }
|
||||
|
||||
bool Init(const ComPtr<ID3D12Device3>& device);
|
||||
|
||||
PostProcessStatus SetFormat(const ComPtr<ID3D12Device3>& device,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst) override;
|
||||
|
||||
void AdjustDamage(RECT dirtyRects[], unsigned * nbDirtyRects) override;
|
||||
|
||||
ComPtr<ID3D12Resource> Run(const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects) override;
|
||||
};
|
||||
192
idd/LGIdd/postprocess/effect/CHDR16to10Effect.cpp
Normal file
192
idd/LGIdd/postprocess/effect/CHDR16to10Effect.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* 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 "CHDR16to10Effect.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace PostProcessUtil;
|
||||
|
||||
bool CHDR16to10Effect::Init(const ComPtr<ID3D12Device3>& device)
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE ranges[3] = {};
|
||||
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
|
||||
ranges[0].NumDescriptors = 1;
|
||||
ranges[0].BaseShaderRegister = 0;
|
||||
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
ranges[1].NumDescriptors = 1;
|
||||
ranges[1].BaseShaderRegister = 0;
|
||||
ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
ranges[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
ranges[2].NumDescriptors = 1;
|
||||
ranges[2].BaseShaderRegister = 0;
|
||||
ranges[2].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
|
||||
const char * shader =
|
||||
"cbuffer Constants : register(b0)\n"
|
||||
"{\n"
|
||||
" float ReferenceWhiteNits;\n"
|
||||
"};\n"
|
||||
"Texture2D<float4> src : register(t0);\n"
|
||||
"RWTexture2D<float4> dst : register(u0);\n"
|
||||
"static const float PQ_m1 = 0.1593017578125;\n"
|
||||
"static const float PQ_m2 = 78.84375;\n"
|
||||
"static const float PQ_c1 = 0.8359375;\n"
|
||||
"static const float PQ_c2 = 18.8515625;\n"
|
||||
"static const float PQ_c3 = 18.6875;\n"
|
||||
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
||||
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
||||
"{\n"
|
||||
" float3 linearValue = src[dt.xy].rgb * ReferenceWhiteNits;\n"
|
||||
" // scRGB uses BT.709 primaries whereas HDR10/PQ output uses BT.2020.\n"
|
||||
" // Rotate the gamut in linear light (BT.2087) BEFORE applying the PQ\n"
|
||||
" // curve, otherwise the BT.709 values are later reinterpreted as BT.2020\n"
|
||||
" // and saturated colours (most visibly red) are pushed outside their\n"
|
||||
" // intended gamut.\n"
|
||||
" float3 rec2020 = float3(\n"
|
||||
" dot(linearValue, float3(0.6274039, 0.3292830, 0.0433131)),\n"
|
||||
" dot(linearValue, float3(0.0690973, 0.9195404, 0.0113623)),\n"
|
||||
" dot(linearValue, float3(0.0163914, 0.0880133, 0.8955953)));\n"
|
||||
" // scRGB to PQ (ST.2084)\n"
|
||||
" float3 Y = rec2020 / 10000.0;\n"
|
||||
" float3 Ym1 = pow(max(Y, 0.0), PQ_m1);\n"
|
||||
" float3 pq = pow((PQ_c1 + PQ_c2 * Ym1) / (1.0 + PQ_c3 * Ym1), PQ_m2);\n"
|
||||
" dst[dt.xy] = float4(pq, src[dt.xy].a);\n"
|
||||
"}\n";
|
||||
|
||||
if (!InitCompute(device, ranges, ARRAYSIZE(ranges), nullptr, 0, shader))
|
||||
return false;
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Width = AlignTo(sizeof(m_consts),
|
||||
(size_t)D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
|
||||
HRESULT hr = device->CreateCommittedResource(&heapProps,
|
||||
D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr, IID_PPV_ARGS(&m_constBuffer));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create HDR16to10 constant buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
void * data = nullptr;
|
||||
D3D12_RANGE readRange = { 0, 0 };
|
||||
hr = m_constBuffer->Map(0, &readRange, &data);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
std::memcpy(data, &m_consts, sizeof(m_consts));
|
||||
m_constBuffer->Unmap(0, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PostProcessStatus CHDR16to10Effect::SetFormat(
|
||||
const ComPtr<ID3D12Device3>& device,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst)
|
||||
{
|
||||
if (src.desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT || !src.hdr)
|
||||
return PostProcessStatus::BYPASS_EFFECT;
|
||||
|
||||
D3D12_RESOURCE_DESC desc = src.desc;
|
||||
desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
|
||||
if (!CreateDefaultTexture(device, desc, m_dst))
|
||||
return PostProcessStatus::FAILED;
|
||||
|
||||
m_threadsX = ((unsigned)desc.Width + (Threads - 1)) / Threads;
|
||||
m_threadsY = ((unsigned)desc.Height + (Threads - 1)) / Threads;
|
||||
|
||||
dst.desc = desc;
|
||||
dst.format = FRAME_TYPE_RGBA10;
|
||||
dst.hdr = true;
|
||||
dst.hdrPQ = true;
|
||||
|
||||
// Gamut conversion changes the signal's container primaries to BT.2020, but
|
||||
// does not change the mastering display chromaticities described by ST 2086.
|
||||
dst.hdrMetadata = src.hdrMetadata;
|
||||
memcpy(dst.displayPrimary, src.displayPrimary, sizeof(dst.displayPrimary));
|
||||
memcpy(dst.whitePoint , src.whitePoint , sizeof(dst.whitePoint ));
|
||||
dst.maxDisplayLuminance = src.maxDisplayLuminance;
|
||||
dst.minDisplayLuminance = src.minDisplayLuminance;
|
||||
dst.maxContentLightLevel = src.maxContentLightLevel;
|
||||
dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel;
|
||||
dst.sdrWhiteLevel = src.sdrWhiteLevel;
|
||||
|
||||
return PostProcessStatus::SUCCESS;
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> CHDR16to10Effect::Run(
|
||||
const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(dirtyRects);
|
||||
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||
|
||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_COMMON,
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||
m_descHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
const UINT inc = device->GetDescriptorHandleIncrementSize(
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
||||
|
||||
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
|
||||
cbvDesc.BufferLocation = m_constBuffer->GetGPUVirtualAddress();
|
||||
cbvDesc.SizeInBytes = (UINT)AlignTo(sizeof(m_consts),
|
||||
(size_t)D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
|
||||
device->CreateConstantBufferView(&cbvDesc, handle);
|
||||
handle.ptr += inc;
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||
srvDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
device->CreateShaderResourceView(src.Get(), &srvDesc, handle);
|
||||
handle.ptr += inc;
|
||||
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
||||
uavDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||
device->CreateUnorderedAccessView(m_dst.Get(), nullptr, &uavDesc, handle);
|
||||
|
||||
Bind(commandList);
|
||||
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
||||
|
||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||
D3D12_RESOURCE_STATE_COMMON);
|
||||
return m_dst;
|
||||
}
|
||||
46
idd/LGIdd/postprocess/effect/CHDR16to10Effect.h
Normal file
46
idd/LGIdd/postprocess/effect/CHDR16to10Effect.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 "CComputeEffect.h"
|
||||
|
||||
class CHDR16to10Effect : public CComputeEffect
|
||||
{
|
||||
private:
|
||||
struct Consts
|
||||
{
|
||||
float ReferenceWhiteNits; // scRGB reference white in nits (typically 80)
|
||||
} m_consts = { 80.0f };
|
||||
ComPtr<ID3D12Resource> m_constBuffer;
|
||||
|
||||
public:
|
||||
const char * GetName() const override { return "HDR16to10"; }
|
||||
|
||||
bool Init(const ComPtr<ID3D12Device3>& device);
|
||||
|
||||
PostProcessStatus SetFormat(const ComPtr<ID3D12Device3>& device,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst) override;
|
||||
|
||||
ComPtr<ID3D12Resource> Run(const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects) override;
|
||||
};
|
||||
577
idd/LGIdd/postprocess/effect/CRGB24Effect.cpp
Normal file
577
idd/LGIdd/postprocess/effect/CRGB24Effect.cpp
Normal file
@@ -0,0 +1,577 @@
|
||||
/**
|
||||
* 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 "CRGB24Effect.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "config/CSettings.h"
|
||||
#include "common/LGMPConfig.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
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)
|
||||
{
|
||||
if (!g_settings.ReadBoolValue(L"AllowRGB24", true))
|
||||
return false;
|
||||
|
||||
D3D12_DESCRIPTOR_RANGE ranges[2] = {};
|
||||
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
ranges[0].NumDescriptors = 1;
|
||||
ranges[0].BaseShaderRegister = 0;
|
||||
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
ranges[1].NumDescriptors = 1;
|
||||
ranges[1].BaseShaderRegister = 0;
|
||||
ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
|
||||
const char * shader =
|
||||
"Texture2D<float4> src : register(t0);\n"
|
||||
"RWByteAddressBuffer dst : register(u0);\n"
|
||||
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
||||
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
||||
"{\n"
|
||||
" uint width, height;\n"
|
||||
" src.GetDimensions(width, height);\n"
|
||||
" uint rowBytes = width * 3;\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"
|
||||
" float4 color1 = xmod3 <= 1 ? color0 : color3;\n"
|
||||
" float4 color2 = xmod3 == 0 ? color0 : color3;\n"
|
||||
" float4 packed = float4(\n"
|
||||
" color0.bgr[xmod3], color1.grb[xmod3],\n"
|
||||
" color2.rbg[xmod3], color3.bgr[xmod3]);\n"
|
||||
" uint4 bytes = (uint4)(saturate(packed) * 255.0f + 0.5f);\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";
|
||||
|
||||
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,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst)
|
||||
{
|
||||
if (!m_state->WantsPacked())
|
||||
{
|
||||
m_dst.Reset();
|
||||
return PostProcessStatus::BYPASS_EFFECT;
|
||||
}
|
||||
|
||||
if (src.desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
|
||||
src.desc.Format != DXGI_FORMAT_B8G8R8A8_UNORM ||
|
||||
src.hdr)
|
||||
{
|
||||
DEBUG_WARN("RGB24 packing is unavailable for the current format");
|
||||
m_state->Reject();
|
||||
m_dst.Reset();
|
||||
return PostProcessStatus::BYPASS_EFFECT;
|
||||
}
|
||||
|
||||
if (src.desc.Width >
|
||||
(UINT64_MAX - (D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1)) / 3)
|
||||
{
|
||||
m_state->Reject();
|
||||
m_dst.Reset();
|
||||
return PostProcessStatus::BYPASS_EFFECT;
|
||||
}
|
||||
|
||||
const UINT64 packedPitch = AlignTo<UINT64>(
|
||||
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;
|
||||
}
|
||||
|
||||
const UINT64 bufferSize = packedPitch * src.desc.Height;
|
||||
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;
|
||||
}
|
||||
|
||||
ComPtr<ID3D12Resource> CRGB24Effect::Run(const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(dirtyRects);
|
||||
UNREFERENCED_PARAMETER(nbDirtyRects);
|
||||
|
||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_COMMON,
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||
m_descHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
const UINT inc = device->GetDescriptorHandleIncrementSize(
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||
srvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
device->CreateShaderResourceView(src.Get(), &srvDesc, handle);
|
||||
handle.ptr += inc;
|
||||
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
|
||||
uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
|
||||
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);
|
||||
|
||||
Bind(commandList);
|
||||
commandList->Dispatch(m_threadsX, m_threadsY, 1);
|
||||
|
||||
TransitionDst(commandList, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||
D3D12_RESOURCE_STATE_COMMON);
|
||||
|
||||
return m_dst;
|
||||
}
|
||||
62
idd/LGIdd/postprocess/effect/CRGB24Effect.h
Normal file
62
idd/LGIdd/postprocess/effect/CRGB24Effect.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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 "CComputeEffect.h"
|
||||
|
||||
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:
|
||||
const char * GetName() const override { return "RGB24"; }
|
||||
|
||||
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,
|
||||
const D12FrameFormat& src, D12FrameFormat& dst) override;
|
||||
|
||||
ComPtr<ID3D12Resource> Run(const ComPtr<ID3D12Device3>& device,
|
||||
const ComPtr<ID3D12GraphicsCommandList>& commandList,
|
||||
const ComPtr<ID3D12Resource>& src, RECT dirtyRects[],
|
||||
unsigned * nbDirtyRects) override;
|
||||
};
|
||||
Reference in New Issue
Block a user