824 lines
41 KiB
C++
824 lines
41 KiB
C++
|
// This file is part of the FidelityFX SDK.
|
||
|
//
|
||
|
// Copyright (C) 2024 Advanced Micro Devices, Inc.
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files(the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions :
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
// THE SOFTWARE.
|
||
|
|
||
|
#include "hybridshadowsrendermodule.h"
|
||
|
|
||
|
#include "core/backend_interface.h"
|
||
|
#include "core/framework.h"
|
||
|
#include "core/uimanager.h"
|
||
|
#include "render/parameterset.h"
|
||
|
#include "render/pipelineobject.h"
|
||
|
#include "render/renderdefines.h"
|
||
|
#include "../thirdparty/samplercpp/samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp.cpp"
|
||
|
|
||
|
#include "core/loaders/textureloader.h"
|
||
|
#include "render/dynamicresourcepool.h"
|
||
|
#include "render/profiler.h"
|
||
|
#include "render/indirectworkload.h"
|
||
|
|
||
|
#include "core/scene.h"
|
||
|
#include "core/contentmanager.h"
|
||
|
|
||
|
using namespace std;
|
||
|
using namespace cauldron;
|
||
|
|
||
|
static const Texture* CreateBlueNoiseTexture()
|
||
|
{
|
||
|
byte blueNoise[128][128][4] = {};
|
||
|
|
||
|
for (int x = 0; x < 128; ++x)
|
||
|
{
|
||
|
for (int y = 0; y < 128; ++y)
|
||
|
{
|
||
|
float const f0 = samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp(x, y, 0, 0);
|
||
|
float const f1 = samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp(x, y, 0, 1);
|
||
|
float const f2 = samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp(x, y, 0, 2);
|
||
|
float const f3 = samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d_1spp(x, y, 0, 3);
|
||
|
|
||
|
blueNoise[x][y][0] = static_cast<byte>(f0 * UCHAR_MAX);
|
||
|
blueNoise[x][y][1] = static_cast<byte>(f1 * UCHAR_MAX);
|
||
|
blueNoise[x][y][2] = static_cast<byte>(f2 * UCHAR_MAX);
|
||
|
blueNoise[x][y][3] = static_cast<byte>(f3 * UCHAR_MAX);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const TextureDesc texDesc = TextureDesc::Tex2D(L"BlueNoise Texture", ResourceFormat::RGBA8_UNORM, 128, 128, 1, 1);
|
||
|
|
||
|
MemTextureDataBlock* pDataBlock = new MemTextureDataBlock(reinterpret_cast<char*>(blueNoise));
|
||
|
|
||
|
Texture* pBlueNoiseTexture = Texture::CreateContentTexture(&texDesc);
|
||
|
CauldronAssert(ASSERT_CRITICAL, pBlueNoiseTexture != nullptr, L"Could not create the texture %ls", texDesc.Name.c_str());
|
||
|
|
||
|
pBlueNoiseTexture->CopyData(pDataBlock);
|
||
|
|
||
|
return std::move(pBlueNoiseTexture);
|
||
|
}
|
||
|
static Vec3 CreateTangentVector(Vec3 normal)
|
||
|
{
|
||
|
Vec3 up = abs(normal.getZ()) < 0.99999f ? Vec3(0.0, 0.0, 1.0) : Vec3(1.0, 0.0, 0.0);
|
||
|
|
||
|
return normalize(cross(up, normal));
|
||
|
}
|
||
|
static float ComputeSunSizeLightSpace(const Vec3& lightDirection, float sunSize, const Mat4& lightViewMatrix)
|
||
|
{
|
||
|
const Vec3 coneVec = normalize(lightDirection) + CreateTangentVector(lightDirection) * sunSize;
|
||
|
const Vec3 lightSpaceConeVec = (lightViewMatrix * Vec4(coneVec, 0)).getXYZ();
|
||
|
|
||
|
float length = math::length(Vec2(lightSpaceConeVec.getX(), lightSpaceConeVec.getY())) / lightSpaceConeVec.getZ();
|
||
|
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
// Tile size is (8x4)
|
||
|
constexpr uint32_t k_tileSizeX = 8;
|
||
|
constexpr uint32_t k_tileSizeY = 4;
|
||
|
static constexpr float AMD_PI = 3.1415926535897932384626433832795f;
|
||
|
static constexpr float GOLDEN_RATIO = 1.6180339887f;
|
||
|
|
||
|
void HybridShadowsRenderModule::CreateCopyDepthPipeline()
|
||
|
{
|
||
|
const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo();
|
||
|
|
||
|
// Create resource
|
||
|
TextureDesc copyDepthTextureDesc =
|
||
|
TextureDesc::Tex2D(L"FidelityFXShadowDenoiser_CopyDepth", ResourceFormat::R32_FLOAT, resInfo.RenderWidth, resInfo.RenderHeight, 1, 1, ResourceFlags::AllowUnorderedAccess);
|
||
|
m_pCopyDepth = GetDynamicResourcePool()->CreateTexture(©DepthTextureDesc, ResourceState::PixelShaderResource | ResourceState::NonPixelShaderResource,
|
||
|
[](TextureDesc& desc, uint32_t displayWidth, uint32_t displayHeight, uint32_t renderingWidth, uint32_t renderingHeight) {
|
||
|
desc.Width = renderingWidth;
|
||
|
desc.Height = renderingHeight;
|
||
|
});
|
||
|
|
||
|
|
||
|
// Root signature
|
||
|
RootSignatureDesc signatureDesc;
|
||
|
signatureDesc.AddTextureSRVSet(0, ShaderBindStage::Compute, 1);
|
||
|
signatureDesc.AddTextureUAVSet(0, ShaderBindStage::Compute, 1);
|
||
|
|
||
|
m_pCopyDepthRootSignature = RootSignature::CreateRootSignature(L"CopyDepth_RootSignature", signatureDesc);
|
||
|
|
||
|
// Setup the pipeline object
|
||
|
PipelineDesc psoDesc;
|
||
|
psoDesc.SetRootSignature(m_pCopyDepthRootSignature);
|
||
|
|
||
|
// Setup the shaders to build on the pipeline object
|
||
|
psoDesc.AddShaderDesc(ShaderBuildDesc::Compute(L"copydepth.hlsl", L"main", ShaderModel::SM6_0, nullptr));
|
||
|
|
||
|
m_pCopyDepthPipelineObj = PipelineObject::CreatePipelineObject(L"CopyDepth_PipelineObj", psoDesc);
|
||
|
|
||
|
// Create parameter set to bind constant buffer and texture
|
||
|
m_pCopyDepthParameters = ParameterSet::CreateParameterSet(m_pCopyDepthRootSignature);
|
||
|
m_pCopyDepthParameters->SetTextureSRV(m_pDepthTarget, ViewDimension::Texture2D, 0);
|
||
|
m_pCopyDepthParameters->SetTextureUAV(m_pCopyDepth, ViewDimension::Texture2D, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
void HybridShadowsRenderModule::CreateResources()
|
||
|
{
|
||
|
const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo();
|
||
|
const uint32_t xTiles = DivideRoundingUp(resInfo.RenderWidth, k_tileSizeX);
|
||
|
const uint32_t yTiles = DivideRoundingUp(resInfo.RenderHeight, k_tileSizeY);
|
||
|
const uint32_t tileCount = xTiles * yTiles;
|
||
|
const size_t tileSize = sizeof(uint32_t) * 4;
|
||
|
|
||
|
BufferDesc workQueueCountDesc{};
|
||
|
workQueueCountDesc.Type = BufferType::Data;
|
||
|
workQueueCountDesc.Flags = static_cast<ResourceFlags>(ResourceFlags::AllowUnorderedAccess | ResourceFlags::AllowIndirect);
|
||
|
workQueueCountDesc.Size = sizeof(uint32_t) * 3;
|
||
|
workQueueCountDesc.Stride = sizeof(uint32_t);
|
||
|
workQueueCountDesc.Name = L"FidelityFXClassifier_WorkQueueCount";
|
||
|
m_pWorkQueueCount = GetDynamicResourcePool()->CreateBuffer(&workQueueCountDesc, ResourceState::CopyDest);
|
||
|
|
||
|
BufferDesc workQueueDesc{};
|
||
|
workQueueDesc.Type = BufferType::Data;
|
||
|
workQueueDesc.Flags = ResourceFlags::AllowUnorderedAccess;
|
||
|
workQueueDesc.Size = tileSize * tileCount;
|
||
|
workQueueDesc.Stride = tileSize;
|
||
|
workQueueDesc.Name = L"FidelityFXClassifier_WorkQueue";
|
||
|
m_pWorkQueue = GetDynamicResourcePool()->CreateBuffer(
|
||
|
&workQueueDesc,
|
||
|
ResourceState::UnorderedAccess,
|
||
|
[](BufferDesc& desc, uint32_t displayWidth, uint32_t displayHeight, uint32_t renderingWidth, uint32_t renderingHeight) {
|
||
|
const uint32_t xTiles = DivideRoundingUp(renderingWidth, k_tileSizeX);
|
||
|
const uint32_t yTiles = DivideRoundingUp(renderingHeight, k_tileSizeY);
|
||
|
const uint32_t tileCount = xTiles * yTiles;
|
||
|
const size_t tileSize = sizeof(uint32_t) * 4;
|
||
|
|
||
|
desc.Size = tileSize * tileCount;
|
||
|
});
|
||
|
|
||
|
// RayHitTexture
|
||
|
TextureDesc rayHitTextureDesc =
|
||
|
TextureDesc::Tex2D(
|
||
|
L"FidelityFXClassifier_RayHitTexture",
|
||
|
ResourceFormat::R32_UINT,
|
||
|
xTiles,
|
||
|
yTiles,
|
||
|
1, 1, ResourceFlags::AllowUnorderedAccess);
|
||
|
|
||
|
m_pRayHitTexture = GetDynamicResourcePool()->CreateTexture(
|
||
|
&rayHitTextureDesc,
|
||
|
ResourceState::UnorderedAccess,
|
||
|
[](TextureDesc& desc, uint32_t displayWidth, uint32_t displayHeight, uint32_t renderingWidth, uint32_t renderingHeight) {
|
||
|
desc.Width = DivideRoundingUp(renderingWidth, k_tileSizeX);
|
||
|
desc.Height = DivideRoundingUp(renderingHeight, k_tileSizeY);
|
||
|
});
|
||
|
|
||
|
// ShadowMaskOutput
|
||
|
TextureDesc shadowMaskOutputDesc = TextureDesc::Tex2D(L"FidelityFX_HybridShadows_ShadowMaskOutput",
|
||
|
ResourceFormat::RGBA8_UNORM,
|
||
|
resInfo.RenderWidth,
|
||
|
resInfo.RenderHeight,
|
||
|
1,
|
||
|
1,
|
||
|
ResourceFlags::AllowUnorderedAccess);
|
||
|
m_pShadowMaskOutput = GetDynamicResourcePool()->CreateTexture(
|
||
|
&shadowMaskOutputDesc,
|
||
|
ResourceState::UnorderedAccess,
|
||
|
[](TextureDesc& desc, uint32_t displayWidth, uint32_t displayHeight, uint32_t renderingWidth, uint32_t renderingHeight) {
|
||
|
desc.Width = renderingWidth;
|
||
|
desc.Height = renderingHeight;
|
||
|
});
|
||
|
|
||
|
GetScene()->SetScreenSpaceShadowTexture(m_pShadowMaskOutput);
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::CreateDebugTilesPipeline()
|
||
|
{
|
||
|
// Root Signature
|
||
|
RootSignatureDesc signatureDesc;
|
||
|
signatureDesc.AddConstantBufferView(0, ShaderBindStage::Compute, 1); // CB
|
||
|
signatureDesc.AddBufferSRVSet(0, ShaderBindStage::Compute, 1); // WorkTiles
|
||
|
signatureDesc.AddTextureUAVSet(0, ShaderBindStage::Compute, 1); // ColorOutput
|
||
|
m_pDebugTilesRootSignature = RootSignature::CreateRootSignature(L"FidelityFX_HybridShadows_DebugTilesSignature", signatureDesc);
|
||
|
|
||
|
// Setup the pipeline object
|
||
|
PipelineDesc psoDesc;
|
||
|
psoDesc.SetRootSignature(m_pDebugTilesRootSignature);
|
||
|
std::wstring shaderPath = L"debugtiles.hlsl";
|
||
|
psoDesc.AddShaderDesc(ShaderBuildDesc::Compute(shaderPath.c_str(), L"MainCS", ShaderModel::SM6_5, nullptr));
|
||
|
|
||
|
std::vector<const wchar_t*> pAdditionalParameters;
|
||
|
pAdditionalParameters.push_back(L"-enable-16bit-types");
|
||
|
m_pDebugTilesPipelineObj = PipelineObject::CreatePipelineObject(L"FidelityFX_HybridShadows_DebugTilesPipelineObj", psoDesc, &pAdditionalParameters);
|
||
|
|
||
|
// Create parameter set to bind constant buffer and texture
|
||
|
m_pDebugTilesParameters = ParameterSet::CreateParameterSet(m_pDebugTilesRootSignature);
|
||
|
|
||
|
// Update necessary scene frame information
|
||
|
m_pDebugTilesParameters->SetRootConstantBufferResource(GetDynamicBufferPool()->GetResource(), sizeof(DebugTilesConstantBuffer), 0);
|
||
|
m_pDebugTilesParameters->SetBufferSRV(m_pWorkQueue, 0);
|
||
|
m_pDebugTilesParameters->SetTextureUAV(m_pColorOutput, ViewDimension::Texture2D, 0);
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::CreateRayTracingPipelines()
|
||
|
{
|
||
|
m_pBlueNoise = CreateBlueNoiseTexture();
|
||
|
|
||
|
// Root Signature
|
||
|
RootSignatureDesc signatureDesc;
|
||
|
signatureDesc.AddConstantBufferView(0, ShaderBindStage::Compute, 1); // CB
|
||
|
signatureDesc.AddBufferSRVSet(0, ShaderBindStage::Compute, 1); // WorkTiles
|
||
|
signatureDesc.AddTextureSRVSet(1, ShaderBindStage::Compute, 1); // Depth
|
||
|
signatureDesc.AddTextureSRVSet(2, ShaderBindStage::Compute, 1); // Normals
|
||
|
signatureDesc.AddTextureSRVSet(3, ShaderBindStage::Compute, 1); // BlueNoise
|
||
|
signatureDesc.AddRTAccelerationStructureSet(4, ShaderBindStage::Compute, 1); // AccellerationStructure
|
||
|
signatureDesc.AddTextureUAVSet(0, ShaderBindStage::Compute, 1); // rayHit
|
||
|
m_pRayTracingRootSignature = RootSignature::CreateRootSignature(L"FidelityFX_HybridShadows_RayTracingSignature", signatureDesc);
|
||
|
|
||
|
// Setup the pipeline object
|
||
|
PipelineDesc psoDesc;
|
||
|
psoDesc.SetRootSignature(m_pRayTracingRootSignature);
|
||
|
std::wstring shaderPath = L"traceshadows.hlsl";
|
||
|
psoDesc.AddShaderDesc(ShaderBuildDesc::Compute(shaderPath.c_str(), L"TraceOpaqueOnly", ShaderModel::SM6_5, nullptr));
|
||
|
|
||
|
std::vector<const wchar_t*> pAdditionalParameters;
|
||
|
pAdditionalParameters.push_back(L"-enable-16bit-types");
|
||
|
m_pRayTracingPipelineObj = PipelineObject::CreatePipelineObject(L"FidelityFX_HybridShadows_RayTracingPipelineObj", psoDesc, &pAdditionalParameters);
|
||
|
|
||
|
// Create parameter set to bind constant buffer and texture
|
||
|
m_pRayTracingParameters = ParameterSet::CreateParameterSet(m_pRayTracingRootSignature);
|
||
|
|
||
|
// Update necessary scene frame information
|
||
|
m_pRayTracingParameters->SetRootConstantBufferResource(GetDynamicBufferPool()->GetResource(), sizeof(RTConstantBuffer), 0);
|
||
|
m_pRayTracingParameters->SetBufferSRV(m_pWorkQueue, 0);
|
||
|
m_pRayTracingParameters->SetTextureSRV(m_pDepthTarget, ViewDimension::Texture2D, 1);
|
||
|
m_pRayTracingParameters->SetTextureSRV(m_pNormalTarget, ViewDimension::Texture2D, 2);
|
||
|
m_pRayTracingParameters->SetTextureSRV(m_pBlueNoise, ViewDimension::Texture2D, 3); // BlueNoise
|
||
|
m_pRayTracingParameters->SetTextureUAV(m_pRayHitTexture, ViewDimension::Texture2D, 0);
|
||
|
|
||
|
// Resolve raytracing pass
|
||
|
{
|
||
|
RootSignatureDesc signatureDesc;
|
||
|
signatureDesc.AddTextureSRVSet(0, ShaderBindStage::Compute, 1); // rayHit
|
||
|
signatureDesc.AddTextureUAVSet(0, ShaderBindStage::Compute, 1); // ColorOutput
|
||
|
m_pResolveRayTracingRootSignature = RootSignature::CreateRootSignature(L"FidelityFX_HybridShadows_ResolveRayTracingSignature", signatureDesc);
|
||
|
|
||
|
PipelineDesc psoDesc;
|
||
|
psoDesc.SetRootSignature(m_pResolveRayTracingRootSignature);
|
||
|
std::wstring shaderPath = L"resolveraytracing.hlsl";
|
||
|
psoDesc.AddShaderDesc(ShaderBuildDesc::Compute(shaderPath.c_str(), L"main", ShaderModel::SM6_5, nullptr));
|
||
|
|
||
|
std::vector<const wchar_t*> pAdditionalParameters;
|
||
|
pAdditionalParameters.push_back(L"-enable-16bit-types");
|
||
|
m_pResolveRayTracingPipelineObj =
|
||
|
PipelineObject::CreatePipelineObject(L"FidelityFX_HybridShadows_ResolveRayTracingPipelineObj", psoDesc, &pAdditionalParameters);
|
||
|
|
||
|
m_pResolveRayTracingParameters = ParameterSet::CreateParameterSet(m_pResolveRayTracingRootSignature);
|
||
|
m_pResolveRayTracingParameters->SetTextureSRV(m_pRayHitTexture, ViewDimension::Texture2D, 0);
|
||
|
m_pResolveRayTracingParameters->SetTextureUAV(m_pShadowMaskOutput, ViewDimension::Texture2D, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::CreateDebugRayTracingPipeline()
|
||
|
{
|
||
|
// Root Signature
|
||
|
RootSignatureDesc signatureDesc;
|
||
|
signatureDesc.AddTextureUAVSet(0, ShaderBindStage::Compute, 1); // screenSpace RT texture
|
||
|
signatureDesc.AddTextureUAVSet(1, ShaderBindStage::Compute, 1); // color
|
||
|
m_pDebugRayTracingRootSignature = RootSignature::CreateRootSignature(L"FidelityFX_HybridShadows_DebugRayTracingSignature", signatureDesc);
|
||
|
|
||
|
PipelineDesc psoDesc;
|
||
|
psoDesc.SetRootSignature(m_pDebugRayTracingRootSignature);
|
||
|
std::wstring shaderPath = L"copydebugraytracing.hlsl";
|
||
|
psoDesc.AddShaderDesc(ShaderBuildDesc::Compute(shaderPath.c_str(), L"main", ShaderModel::SM6_0, nullptr));
|
||
|
|
||
|
m_pDebugRayTracingPipelineObj =
|
||
|
PipelineObject::CreatePipelineObject(L"FidelityFX_HybridShadows_DebugRayTracingPipelineObj", psoDesc, nullptr);
|
||
|
|
||
|
m_pDebugRayTracingParameters = ParameterSet::CreateParameterSet(m_pDebugRayTracingRootSignature);
|
||
|
m_pDebugRayTracingParameters->SetTextureUAV(m_pShadowMaskOutput, ViewDimension::Texture2D, 0);
|
||
|
m_pDebugRayTracingParameters->SetTextureUAV(m_pColorOutput, ViewDimension::Texture2D, 1);
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::Init(const json&)
|
||
|
{
|
||
|
CauldronAssert(ASSERT_CRITICAL, !GetFramework()->GetConfig()->MotionVectorGeneration.empty(), L"Error : HybridShadowsRenderModule requires MotionVectorGeneration be set");
|
||
|
|
||
|
// Config Asserts
|
||
|
CauldronAssert(ASSERT_CRITICAL, GetDevice()->FeatureSupported(DeviceFeature::RT_1_1), L"Error : HybridShadowsRenderModule requires RT1.1");
|
||
|
CauldronAssert(ASSERT_CRITICAL, GetDevice()->FeatureSupported(DeviceFeature::FP16), L"Error : HybridShadowsRenderModule requires FP16");
|
||
|
|
||
|
// Fetch needed resource
|
||
|
m_pDepthTarget = GetFramework()->GetRenderTexture(L"DepthTarget");
|
||
|
m_pNormalTarget = GetFramework()->GetRenderTexture(L"GBufferNormalRT");
|
||
|
m_pColorOutput = GetFramework()->GetColorTargetForCallback(GetName());
|
||
|
m_pMotionVectors = GetFramework()->GetRenderTexture(L"GBufferMotionVectorRT");
|
||
|
|
||
|
// Create Resources for Effect
|
||
|
CreateResources();
|
||
|
|
||
|
// UI elements
|
||
|
UISection* uiSection = GetUIManager()->RegisterUIElements("Hybrid Shadows", UISectionType::Sample);
|
||
|
uiSection->RegisterUIElement<UICheckBox>("Run Hybrid Shadows", m_bRunHybridShadows);
|
||
|
uiSection->RegisterUIElement<UICheckBox>("Use Denoiser", m_bUseDenoiser);
|
||
|
uiSection->RegisterUIElement<UISlider<float>>("Sun solid angle", m_sunSolidAngle, 0.f, 1.f, nullptr, true, false, "%.2f deg");
|
||
|
|
||
|
const static std::vector<const char*> debugOptions = {"Disabled", "Show RayTraced Tiles", "Show Ray minT", "Show Ray maxT", "Show Ray length", "Show RayTracing Texture"};
|
||
|
uiSection->RegisterUIElement<UICombo>("Debug Mode", m_DebugMode, debugOptions);
|
||
|
|
||
|
uiSection->RegisterUIElement<UISlider<int32_t>>("TileCutOff", (int&)m_TileCutoff, 0, 32);
|
||
|
uiSection->RegisterUIElement<UISlider<float>>("PCF offset", m_blockerOffset, 0.0f, 0.008f);
|
||
|
|
||
|
uiSection->RegisterUIElement<UICheckBox>("Reject Lit Pixels for Ray Tracing", m_bRejectLitPixels);
|
||
|
uiSection->RegisterUIElement<UICheckBox>("Use Shadow Maps to determine RayT", m_bUseCascadesForRayT);
|
||
|
|
||
|
m_pIndirectWorkLoad = IndirectWorkload::CreateIndirectWorkload(IndirectCommandType::Dispatch);
|
||
|
CreateCopyDepthPipeline();
|
||
|
CreateRayTracingPipelines();
|
||
|
CreateDebugTilesPipeline();
|
||
|
CreateDebugRayTracingPipeline();
|
||
|
|
||
|
InitEffect();
|
||
|
|
||
|
GetFramework()->ConfigureRuntimeShaderRecompiler(
|
||
|
[this](void) {
|
||
|
DestroyEffect();
|
||
|
},
|
||
|
[this](void) {
|
||
|
InitEffect();
|
||
|
});
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Register additional execution callbacks during the frame
|
||
|
|
||
|
// Register a post-lighting callback for tile debug pass
|
||
|
ExecuteCallback callbackTileDebug = [this](double deltaTime, CommandList* pCmdList) { this->TileDebugCallback(deltaTime, pCmdList); };
|
||
|
ExecutionTuple callbackTileDebugTuple = std::make_pair(L"HybridShadowsRenderModule::TileDebugCallback", std::make_pair(this, callbackTileDebug));
|
||
|
GetFramework()->RegisterExecutionCallback(L"TranslucencyRenderModule", false, callbackTileDebugTuple);
|
||
|
|
||
|
SetModuleReady(true);
|
||
|
SetModuleEnabled(true);
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::TileDebugCallback(double deltaTime, cauldron::CommandList* pCmdList)
|
||
|
{
|
||
|
if (m_DebugMode == 0 || !m_bRunHybridShadows)
|
||
|
return;
|
||
|
|
||
|
const ResolutionInfo& resinfo = GetFramework()->GetResolutionInfo();
|
||
|
|
||
|
if (m_DebugMode == 5)
|
||
|
{
|
||
|
GPUScopedProfileCapture sampleMarker(pCmdList, L"Debug-RayTracing (HybridShadows)");
|
||
|
|
||
|
{
|
||
|
std::vector<Barrier> barriers;
|
||
|
barriers.push_back(Barrier::Transition(
|
||
|
m_pColorOutput->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::UnorderedAccess));
|
||
|
barriers.push_back(Barrier::Transition(m_pWorkQueueCount->GetResource(), ResourceState::CopyDest, ResourceState::IndirectArgument));
|
||
|
ResourceBarrier(pCmdList, static_cast<uint32_t>(barriers.size()), barriers.data());
|
||
|
}
|
||
|
|
||
|
// Bind everything
|
||
|
m_pDebugRayTracingParameters->Bind(pCmdList, m_pDebugRayTracingPipelineObj);
|
||
|
SetPipelineState(pCmdList, m_pDebugRayTracingPipelineObj);
|
||
|
|
||
|
|
||
|
Dispatch(pCmdList, DivideRoundingUp(resinfo.RenderWidth, 8), DivideRoundingUp(resinfo.RenderHeight, 8), 1);
|
||
|
{
|
||
|
std::vector<Barrier> barriers;
|
||
|
barriers.push_back(Barrier::Transition(
|
||
|
m_pColorOutput->GetResource(), ResourceState::UnorderedAccess, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource));
|
||
|
barriers.push_back(Barrier::Transition(m_pWorkQueueCount->GetResource(), ResourceState::IndirectArgument, ResourceState::CopyDest));
|
||
|
ResourceBarrier(pCmdList, static_cast<uint32_t>(barriers.size()), barriers.data());
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
GPUScopedProfileCapture sampleMarker(pCmdList, L"Debug-Tiles (HybridShadows)");
|
||
|
|
||
|
{
|
||
|
std::vector<Barrier> barriers;
|
||
|
barriers.push_back(Barrier::Transition(
|
||
|
m_pColorOutput->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::UnorderedAccess));
|
||
|
barriers.push_back(Barrier::Transition(
|
||
|
m_pWorkQueueCount->GetResource(), ResourceState::CopyDest, ResourceState::IndirectArgument));
|
||
|
ResourceBarrier(pCmdList, static_cast<uint32_t>(barriers.size()), barriers.data());
|
||
|
}
|
||
|
|
||
|
static DebugTilesConstantBuffer dtConstantBuffer{};
|
||
|
dtConstantBuffer.debugMode = m_DebugMode;
|
||
|
|
||
|
BufferAddressInfo cb = GetDynamicBufferPool()->AllocConstantBuffer(sizeof(DebugTilesConstantBuffer), &dtConstantBuffer);
|
||
|
m_pDebugTilesParameters->UpdateRootConstantBuffer(&cb, 0);
|
||
|
|
||
|
// Bind everything
|
||
|
m_pDebugTilesParameters->SetBufferSRV(m_pWorkQueue, 0);
|
||
|
m_pDebugTilesParameters->Bind(pCmdList, m_pDebugTilesPipelineObj);
|
||
|
SetPipelineState(pCmdList, m_pDebugTilesPipelineObj);
|
||
|
|
||
|
ExecuteIndirect(pCmdList, m_pIndirectWorkLoad, m_pWorkQueueCount, 1, 0);
|
||
|
|
||
|
{
|
||
|
std::vector<Barrier> barriers;
|
||
|
barriers.push_back(Barrier::Transition(
|
||
|
m_pColorOutput->GetResource(), ResourceState::UnorderedAccess, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource));
|
||
|
barriers.push_back(Barrier::Transition(m_pWorkQueueCount->GetResource(), ResourceState::IndirectArgument, ResourceState::CopyDest));
|
||
|
ResourceBarrier(pCmdList, static_cast<uint32_t>(barriers.size()), barriers.data());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HybridShadowsRenderModule::~HybridShadowsRenderModule()
|
||
|
{
|
||
|
if (ModuleEnabled())
|
||
|
{
|
||
|
EnableModule(false);
|
||
|
|
||
|
// Destroy the context
|
||
|
UpdateEffectContext(false);
|
||
|
|
||
|
// Destroy the FidelityFX interface memory
|
||
|
free(m_ClassifierCtxDesc.backendInterface.scratchBuffer);
|
||
|
|
||
|
delete m_pDebugRayTracingPipelineObj;
|
||
|
delete m_pDebugRayTracingRootSignature;
|
||
|
delete m_pDebugRayTracingParameters;
|
||
|
delete m_pIndirectWorkLoad;
|
||
|
delete m_pRayTracingRootSignature;
|
||
|
delete m_pRayTracingPipelineObj;
|
||
|
delete m_pRayTracingParameters;
|
||
|
delete m_pResolveRayTracingRootSignature;
|
||
|
delete m_pResolveRayTracingPipelineObj;
|
||
|
delete m_pResolveRayTracingParameters;
|
||
|
delete m_pDebugTilesRootSignature;
|
||
|
delete m_pDebugTilesPipelineObj;
|
||
|
delete m_pDebugTilesParameters;
|
||
|
delete m_pCopyDepthRootSignature;
|
||
|
delete m_pCopyDepthPipelineObj;
|
||
|
delete m_pCopyDepthParameters;
|
||
|
|
||
|
delete m_pBlueNoise;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::InitEffect()
|
||
|
{
|
||
|
// Setup FidelityFX interface.
|
||
|
{
|
||
|
const size_t scratchBufferSize = SDKWrapper::ffxGetScratchMemorySize(FFX_CLASSIFIER_CONTEXT_COUNT + FFX_DENOISER_CONTEXT_COUNT);
|
||
|
void* scratchBuffer = calloc(scratchBufferSize, 1u);
|
||
|
FfxErrorCode errorCode = SDKWrapper::ffxGetInterface(
|
||
|
&m_SDKInterface,
|
||
|
GetDevice(),
|
||
|
scratchBuffer,
|
||
|
scratchBufferSize,
|
||
|
FFX_CLASSIFIER_CONTEXT_COUNT + FFX_DENOISER_CONTEXT_COUNT);
|
||
|
CAULDRON_ASSERT(errorCode == FFX_OK);
|
||
|
CauldronAssert(ASSERT_CRITICAL, m_SDKInterface.fpGetSDKVersion(&m_SDKInterface) == FFX_SDK_MAKE_VERSION(1, 1, 2),
|
||
|
L"FidelityFX HybridShadows 2.1 sample requires linking with a 1.1.2 version SDK backend");
|
||
|
CauldronAssert(ASSERT_CRITICAL, ffxClassifierGetEffectVersion() == FFX_SDK_MAKE_VERSION(1, 3, 0),
|
||
|
L"FidelityFX HybridShadows 2.1 sample requires linking with a 1.3 version FidelityFX Classifier library");
|
||
|
CauldronAssert(ASSERT_CRITICAL, ffxDenoiserGetEffectVersion() == FFX_SDK_MAKE_VERSION(1, 3, 0),
|
||
|
L"FidelityFX HybridShadows 2.1 sample requires linking with a 1.3 version FidelityFX Denoiser library");
|
||
|
|
||
|
m_SDKInterface.fpRegisterConstantBufferAllocator(&m_SDKInterface, SDKWrapper::ffxAllocateConstantBuffer);
|
||
|
}
|
||
|
|
||
|
m_ClassifierCtxDesc.backendInterface = m_SDKInterface;
|
||
|
m_DenoiserCtxDesc.backendInterface = m_SDKInterface;
|
||
|
|
||
|
// Create the context
|
||
|
UpdateEffectContext(true);
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::DestroyEffect()
|
||
|
{
|
||
|
// Destroy the context
|
||
|
UpdateEffectContext(false);
|
||
|
|
||
|
// Destroy the FidelityFX interface memory
|
||
|
if (m_ClassifierCtxDesc.backendInterface.scratchBuffer != nullptr)
|
||
|
{
|
||
|
free(m_ClassifierCtxDesc.backendInterface.scratchBuffer);
|
||
|
m_ClassifierCtxDesc.backendInterface.scratchBuffer = nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::UpdateEffectContext(bool enabled)
|
||
|
{
|
||
|
const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo();
|
||
|
|
||
|
if (enabled)
|
||
|
{
|
||
|
// Classifier
|
||
|
m_ClassifierCtxDesc.flags = 0;
|
||
|
m_ClassifierCtxDesc.flags |= FFX_CLASSIFIER_SHADOW;
|
||
|
|
||
|
switch (m_ClassificationMode)
|
||
|
{
|
||
|
case ClassificationMode::ClassifyByNormals:
|
||
|
m_ClassifierCtxDesc.flags |= FFX_CLASSIFIER_CLASSIFY_BY_NORMALS;
|
||
|
break;
|
||
|
case ClassificationMode::ClassifyByCascades:
|
||
|
m_ClassifierCtxDesc.flags |= FFX_CLASSIFIER_CLASSIFY_BY_CASCADES;
|
||
|
break;
|
||
|
default:
|
||
|
CauldronCritical(L"Invalid Classification Mode");
|
||
|
}
|
||
|
m_ClassifierCtxDesc.flags |= GetConfig()->InvertedDepth ? FFX_CLASSIFIER_ENABLE_DEPTH_INVERTED : 0;
|
||
|
|
||
|
m_ClassifierCtxDesc.resolution.width = resInfo.RenderWidth;
|
||
|
m_ClassifierCtxDesc.resolution.height = resInfo.RenderHeight;
|
||
|
|
||
|
ffxClassifierContextCreate(&m_ClassifierContext, &m_ClassifierCtxDesc);
|
||
|
|
||
|
// Denoiser
|
||
|
m_DenoiserCtxDesc.flags = 0;
|
||
|
m_DenoiserCtxDesc.flags = FFX_DENOISER_SHADOWS;
|
||
|
m_DenoiserCtxDesc.flags |= GetConfig()->InvertedDepth ? FFX_CLASSIFIER_ENABLE_DEPTH_INVERTED : 0;
|
||
|
|
||
|
m_DenoiserCtxDesc.windowSize.width = resInfo.RenderWidth;
|
||
|
m_DenoiserCtxDesc.windowSize.height = resInfo.RenderHeight;
|
||
|
|
||
|
ffxDenoiserContextCreate(&m_DenoiserContext, &m_DenoiserCtxDesc);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Flush anything out of the pipes before destroying the context
|
||
|
GetDevice()->FlushAllCommandQueues();
|
||
|
|
||
|
ffxClassifierContextDestroy(&m_ClassifierContext);
|
||
|
ffxDenoiserContextDestroy(&m_DenoiserContext);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::ResolveRayTracingToShadowTexture(cauldron::CommandList* pCmdList)
|
||
|
{
|
||
|
const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo();
|
||
|
|
||
|
{
|
||
|
std::vector<Barrier> preResolveBarriers;
|
||
|
preResolveBarriers.push_back(Barrier::Transition(
|
||
|
m_pRayHitTexture->GetResource(), ResourceState::UnorderedAccess, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource));
|
||
|
}
|
||
|
|
||
|
m_pResolveRayTracingParameters->Bind(pCmdList, m_pResolveRayTracingPipelineObj);
|
||
|
SetPipelineState(pCmdList, m_pResolveRayTracingPipelineObj);
|
||
|
|
||
|
Dispatch(pCmdList, DivideRoundingUp(resInfo.RenderWidth, k_tileSizeX), DivideRoundingUp(resInfo.RenderHeight, k_tileSizeY), 1);
|
||
|
|
||
|
{
|
||
|
std::vector<Barrier> postResolveBarriers;
|
||
|
postResolveBarriers.push_back(Barrier::Transition(
|
||
|
m_pRayHitTexture->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::UnorderedAccess));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::RunRayTracingShadowDispatch(const FfxClassifierShadowDispatchDescription& shadowClassifierDispatchParams,
|
||
|
const float sunSize,
|
||
|
cauldron::CommandList* pCmdList)
|
||
|
{
|
||
|
GPUScopedProfileCapture sampleMarker(pCmdList, L"Trace Shadow Rays");
|
||
|
{
|
||
|
std::vector<Barrier> preTracebarriers;
|
||
|
preTracebarriers.push_back(Barrier::Transition(m_pWorkQueueCount->GetResource(), ResourceState::UnorderedAccess, ResourceState::IndirectArgument));
|
||
|
ResourceBarrier(pCmdList, (uint32_t)preTracebarriers.size(), preTracebarriers.data());
|
||
|
}
|
||
|
m_pRayTracingParameters->SetAccelerationStructure(GetScene()->GetASManager()->GetTLAS(), 4);
|
||
|
|
||
|
const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo();
|
||
|
|
||
|
m_RTConstantBuffer.textureSize = Vec4((float)resInfo.RenderWidth, (float)resInfo.RenderHeight, 1.f / resInfo.RenderWidth, 1.f / resInfo.RenderHeight);
|
||
|
memcpy(&m_RTConstantBuffer.lightDir, &shadowClassifierDispatchParams.lightDir, sizeof(Vec3));
|
||
|
static uint32_t frame = 0;
|
||
|
|
||
|
m_RTConstantBuffer.pixelThickness_bUseCascadesForRayT_noisePhase_sunSize[0] = 1e-4f;
|
||
|
m_RTConstantBuffer.pixelThickness_bUseCascadesForRayT_noisePhase_sunSize[1] = shadowClassifierDispatchParams.bUseCascadesForRayT;
|
||
|
m_RTConstantBuffer.pixelThickness_bUseCascadesForRayT_noisePhase_sunSize[2] = (frame++ & 0xff) * GOLDEN_RATIO;
|
||
|
m_RTConstantBuffer.pixelThickness_bUseCascadesForRayT_noisePhase_sunSize[3] = sunSize;
|
||
|
|
||
|
memcpy(&m_RTConstantBuffer.viewToWorld, &shadowClassifierDispatchParams.viewToWorld, sizeof(Mat4));
|
||
|
BufferAddressInfo cb = GetDynamicBufferPool()->AllocConstantBuffer(sizeof(RTConstantBuffer), &m_RTConstantBuffer);
|
||
|
m_pRayTracingParameters->UpdateRootConstantBuffer(&cb, 0);
|
||
|
|
||
|
// Bind everything
|
||
|
m_pRayTracingParameters->SetBufferSRV(m_pWorkQueue, 0);
|
||
|
m_pRayTracingParameters->Bind(pCmdList, m_pRayTracingPipelineObj);
|
||
|
SetPipelineState(pCmdList, m_pRayTracingPipelineObj);
|
||
|
|
||
|
ExecuteIndirect(pCmdList, m_pIndirectWorkLoad, m_pWorkQueueCount, 1, 0);
|
||
|
|
||
|
{
|
||
|
std::vector<Barrier> postTracebarriers;
|
||
|
postTracebarriers.push_back(Barrier::Transition(m_pWorkQueueCount->GetResource(), ResourceState::IndirectArgument, ResourceState::CopyDest));
|
||
|
ResourceBarrier(pCmdList, (uint32_t)postTracebarriers.size(), postTracebarriers.data());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::RunCopyDepth(cauldron::CommandList* pCmdList)
|
||
|
{
|
||
|
const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo();
|
||
|
|
||
|
{
|
||
|
std::vector<Barrier> barriers;
|
||
|
barriers.push_back(Barrier::Transition(
|
||
|
m_pCopyDepth->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::UnorderedAccess));
|
||
|
ResourceBarrier(pCmdList, static_cast<uint32_t>(barriers.size()), barriers.data());
|
||
|
}
|
||
|
|
||
|
m_pCopyDepthParameters->Bind(pCmdList, m_pCopyDepthPipelineObj);
|
||
|
|
||
|
SetPipelineState(pCmdList, m_pCopyDepthPipelineObj);
|
||
|
|
||
|
Dispatch(pCmdList, DivideRoundingUp(resInfo.RenderWidth, 64u), DivideRoundingUp(resInfo.RenderHeight, 64u), 1);
|
||
|
|
||
|
{
|
||
|
std::vector<Barrier> barriers;
|
||
|
barriers.push_back(Barrier::Transition(
|
||
|
m_pCopyDepth->GetResource(), ResourceState::UnorderedAccess, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource));
|
||
|
ResourceBarrier(pCmdList, static_cast<uint32_t>(barriers.size()), barriers.data());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::RunFfxShadowDenoiser(cauldron::CommandList* pCmdList)
|
||
|
{
|
||
|
GPUScopedProfileCapture sampleMarker(pCmdList, L"FidelityFX Shadow Denoiser");
|
||
|
|
||
|
static uint32_t frameIndex = 0;
|
||
|
FfxDenoiserShadowsDispatchDescription denoiserDispatchDescription{};
|
||
|
denoiserDispatchDescription.commandList = SDKWrapper::ffxGetCommandList(pCmdList);
|
||
|
denoiserDispatchDescription.hitMaskResults =
|
||
|
SDKWrapper::ffxGetResource(m_pRayHitTexture->GetResource(), L"FidelityFX_ShadowDenoiser_RayHit", FFX_RESOURCE_STATE_GENERIC_READ);
|
||
|
denoiserDispatchDescription.depth =
|
||
|
SDKWrapper::ffxGetResource(m_pCopyDepth->GetResource(), L"FidelityFX_ShadowDenoiser_InputDepth", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
||
|
denoiserDispatchDescription.velocity =
|
||
|
SDKWrapper::ffxGetResource(m_pMotionVectors->GetResource(), L"FidelityFX_ShadowDenoiser_InputVelocity", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
||
|
denoiserDispatchDescription.normal =
|
||
|
SDKWrapper::ffxGetResource(m_pNormalTarget->GetResource(), L"FidelityFX_ShadowDenoiser_InputNormals", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
||
|
denoiserDispatchDescription.shadowMaskOutput =
|
||
|
SDKWrapper::ffxGetResource(m_pShadowMaskOutput->GetResource(), L"FidelityFX_ShadowDenoiser_ShadowMaskOutput", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
|
||
|
|
||
|
// Cauldron's GBuffer stores normals in the [0, 1] range, the FidelityFX Shadow Denoiser expects them in the [-1, 1] range.
|
||
|
denoiserDispatchDescription.normalsUnpackMul = 2.f;
|
||
|
denoiserDispatchDescription.normalsUnpackAdd = -1.f;
|
||
|
|
||
|
const CameraComponent* pCam = GetScene()->GetCurrentCamera();
|
||
|
memcpy(&denoiserDispatchDescription.eye, &pCam->GetCameraPos(), sizeof(Vec3));
|
||
|
denoiserDispatchDescription.frameIndex = frameIndex;
|
||
|
memcpy(&denoiserDispatchDescription.projectionInverse, &pCam->GetInverseProjection(), sizeof(Mat4));
|
||
|
const auto reprojMat = pCam->GetProjection() * (pCam->GetPreviousView() * pCam->GetInverseViewProjection());
|
||
|
memcpy(&denoiserDispatchDescription.reprojectionMatrix, &reprojMat, sizeof(Mat4));
|
||
|
memcpy(&denoiserDispatchDescription.viewProjectionInverse, &pCam->GetInverseViewProjection(), sizeof(Mat4));
|
||
|
denoiserDispatchDescription.depthSimilaritySigma = 1.f;
|
||
|
|
||
|
float mvScale[2] = {1.f, 1.f};
|
||
|
memcpy(denoiserDispatchDescription.motionVectorScale, mvScale, sizeof(mvScale));
|
||
|
|
||
|
CAULDRON_ASSERT(FFX_OK == ffxDenoiserContextDispatchShadows(&m_DenoiserContext, &denoiserDispatchDescription));
|
||
|
// FidelityFX contexts modify the set resource view heaps, so set the cauldron one back
|
||
|
SetAllResourceViewHeaps(pCmdList);
|
||
|
|
||
|
frameIndex++;
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::Execute(double deltaTime, CommandList* pCmdList)
|
||
|
{
|
||
|
if (!m_bRunHybridShadows)
|
||
|
{
|
||
|
GetScene()->SetScreenSpaceShadowTexture(nullptr);
|
||
|
return;
|
||
|
}
|
||
|
GetScene()->SetScreenSpaceShadowTexture(m_pShadowMaskOutput);
|
||
|
|
||
|
static float sunSize;
|
||
|
sunSize = tanf(0.5f * (m_sunSolidAngle * (2 * AMD_PI) / 360.0f));
|
||
|
|
||
|
|
||
|
GPUScopedProfileCapture sampleMarker(pCmdList, L"FidelityFX Hybrid Shadows");
|
||
|
FfxClassifierShadowDispatchDescription shadowClassifierDispatchParams{};
|
||
|
{
|
||
|
GPUScopedProfileCapture sampleMarker(pCmdList, L"FidelityFX Classifier");
|
||
|
const CameraComponent* pCamera = GetScene()->GetCurrentCamera();
|
||
|
|
||
|
// Init WorkQueueCount
|
||
|
constexpr uint32_t offsets[3] = {sizeof(uint32_t) * 0, sizeof(uint32_t) * 1, sizeof(uint32_t) * 2};
|
||
|
constexpr uint32_t values[3] = {0, 1, 1};
|
||
|
WriteBufferImmediate(pCmdList, m_pWorkQueueCount->GetResource(), 3, offsets, values);
|
||
|
|
||
|
Barrier barrier = Barrier::Transition(m_pWorkQueueCount->GetResource(), ResourceState::CopyDest, ResourceState::UnorderedAccess);
|
||
|
ResourceBarrier(pCmdList, 1, &barrier);
|
||
|
|
||
|
shadowClassifierDispatchParams.commandList = SDKWrapper::ffxGetCommandList(pCmdList);
|
||
|
shadowClassifierDispatchParams.depth = SDKWrapper::ffxGetResource(m_pDepthTarget->GetResource(), L"FidelityFXClassifier_InputDepth", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
||
|
shadowClassifierDispatchParams.normals = SDKWrapper::ffxGetResource(m_pNormalTarget->GetResource(), L"FidelityFXClassifier_InputNormals", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
||
|
shadowClassifierDispatchParams.workQueue = SDKWrapper::ffxGetResource(m_pWorkQueue->GetResource(), L"FidelityFXClassifier_WorkQueue", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
|
||
|
shadowClassifierDispatchParams.workQueueCount = SDKWrapper::ffxGetResource(m_pWorkQueueCount->GetResource(), L"FidelityFXClassifier_WorkQueueCount", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
|
||
|
shadowClassifierDispatchParams.rayHitTexture = SDKWrapper::ffxGetResource(m_pRayHitTexture->GetResource(), L"FidelityFXClassifier_RayHit", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
|
||
|
|
||
|
// Cauldron's GBuffer stores normals in the [0, 1] range, the FidelityFX Classifier expects them in the [-1, 1] range.
|
||
|
shadowClassifierDispatchParams.normalsUnPackMul = 2.f;
|
||
|
shadowClassifierDispatchParams.normalsUnPackAdd = -1.f;
|
||
|
|
||
|
shadowClassifierDispatchParams.tileCutOff = m_TileCutoff;
|
||
|
shadowClassifierDispatchParams.bRejectLitPixels = m_bRejectLitPixels;
|
||
|
shadowClassifierDispatchParams.bUseCascadesForRayT = m_bUseCascadesForRayT && m_ClassificationMode == ClassificationMode::ClassifyByCascades;
|
||
|
shadowClassifierDispatchParams.blockerOffset = m_blockerOffset;
|
||
|
|
||
|
memcpy(&shadowClassifierDispatchParams.viewToWorld, &pCamera->GetInverseViewProjection(), sizeof(Mat4));
|
||
|
|
||
|
const std::vector<Component*>& lightingComponents = LightComponentMgr::Get()->GetComponentList();
|
||
|
for (auto* pComp : lightingComponents)
|
||
|
{
|
||
|
// Skip inactive lights
|
||
|
if (!pComp->GetOwner()->IsActive())
|
||
|
continue;
|
||
|
|
||
|
const LightComponent* pLightComp = static_cast<LightComponent*>(pComp);
|
||
|
|
||
|
// Process only a light if it has cascades and is a directional light
|
||
|
if (pLightComp->GetCascadesCount() <= 0 || pLightComp->GetType() != LightType::Directional)
|
||
|
{
|
||
|
Barrier barrier2 = Barrier::Transition(m_pWorkQueueCount->GetResource(), ResourceState::UnorderedAccess, ResourceState::CopyDest);
|
||
|
ResourceBarrier(pCmdList, 1, &barrier2);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
memcpy(&shadowClassifierDispatchParams.lightDir, &-pLightComp->GetDirection(), sizeof(Vec3));
|
||
|
memcpy(&shadowClassifierDispatchParams.lightView, &pLightComp->GetView(), sizeof(Mat4));
|
||
|
memcpy(&shadowClassifierDispatchParams.inverseLightView, &pLightComp->GetInverseView(), sizeof(Mat4));
|
||
|
|
||
|
ShadowMapResourcePool* pShadowMapResourcePool = GetFramework()->GetShadowMapResourcePool();
|
||
|
CauldronAssert(ASSERT_CRITICAL, pLightComp->GetCascadesCount() <= 4, L"HybridShadowsRenderModule does not support lights with more than 4 cascades");
|
||
|
shadowClassifierDispatchParams.cascadeCount = pLightComp->GetCascadesCount();
|
||
|
|
||
|
for (uint32_t cascade = 0; cascade < shadowClassifierDispatchParams.cascadeCount; ++cascade)
|
||
|
{
|
||
|
int shadowMapIndex = pLightComp->GetShadowMapIndex(cascade);
|
||
|
shadowClassifierDispatchParams.shadowMaps[cascade] = SDKWrapper::ffxGetResource(pShadowMapResourcePool->GetRenderTarget(shadowMapIndex)->GetResource(),
|
||
|
L"FidelityFXClassifier_ShadowMap",
|
||
|
FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ);
|
||
|
|
||
|
const Vec4 scaleAndOffset = pShadowMapResourcePool->GetTransformation(pLightComp->GetShadowMapRect(cascade));
|
||
|
const Mat4 matTextureScale = Mat4::scale(Vec3(scaleAndOffset.getX(), scaleAndOffset.getY(), 1.0f));
|
||
|
const Mat4 matTextureTranslation = Mat4::translation(Vec3(scaleAndOffset.getZ(), scaleAndOffset.getW(), 0.f));
|
||
|
const Mat4 mShadowTexture = matTextureTranslation * matTextureScale * pLightComp->GetShadowProjection(cascade);
|
||
|
memcpy(&shadowClassifierDispatchParams.cascadeScale[cascade],
|
||
|
&Vec4(mShadowTexture.getCol0().getX(), mShadowTexture.getCol1().getY(), mShadowTexture.getCol2().getZ(), 1.0f),
|
||
|
sizeof(Vec4));
|
||
|
memcpy(&shadowClassifierDispatchParams.cascadeOffset[cascade],
|
||
|
&Vec4(mShadowTexture.getCol3().getX(), mShadowTexture.getCol3().getY(), mShadowTexture.getCol3().getZ(), 0.0f),
|
||
|
sizeof(Vec4));
|
||
|
}
|
||
|
|
||
|
shadowClassifierDispatchParams.cascadeSize = (float)pLightComp->GetShadowResolution();
|
||
|
shadowClassifierDispatchParams.sunSizeLightSpace = ComputeSunSizeLightSpace(pLightComp->GetDirection(), sunSize, pLightComp->GetView());
|
||
|
|
||
|
// Only process one light
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
CAULDRON_ASSERT(FFX_OK == ffxClassifierContextShadowDispatch(&m_ClassifierContext, &shadowClassifierDispatchParams));
|
||
|
// FidelityFX contexts modify the set resource view heaps, so set the cauldron one back
|
||
|
SetAllResourceViewHeaps(pCmdList);
|
||
|
}
|
||
|
|
||
|
|
||
|
RunRayTracingShadowDispatch(shadowClassifierDispatchParams, sunSize, pCmdList);
|
||
|
|
||
|
if (m_bUseDenoiser)
|
||
|
{
|
||
|
// Copy depth to a R32 texture
|
||
|
RunCopyDepth(pCmdList);
|
||
|
|
||
|
// Denoise RT output and write it to the Shadow Texture
|
||
|
RunFfxShadowDenoiser(pCmdList);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Write RT output to the shadow texture directly
|
||
|
GPUScopedProfileCapture sampleMarker(pCmdList, L"Write to Shadow Texture");
|
||
|
ResolveRayTracingToShadowTexture(pCmdList);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HybridShadowsRenderModule::OnResize(const ResolutionInfo& resInfo)
|
||
|
{
|
||
|
if (!ModuleEnabled())
|
||
|
return;
|
||
|
|
||
|
// Need to recreate the context on resource resize
|
||
|
UpdateEffectContext(false); // Destroy
|
||
|
UpdateEffectContext(true); // Re-create
|
||
|
}
|