539 lines
24 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 "spdrendermodule.h"
#include "core/backend_interface.h"
#include "core/contentmanager.h"
#include "core/framework.h"
#include "core/loaders/textureloader.h"
#include "core/uimanager.h"
#include "render/device.h"
#include "render/parameterset.h"
#include "render/pipelineobject.h"
#include "render/profiler.h"
#include "render/rasterview.h"
#include "render/renderdefines.h"
// Pull in common structures for SPD
#include "shaders/spd_common.h"
#include <limits>
using namespace cauldron;
using namespace std::experimental;
void SPDRenderModule::Init(const json& initData)
{
// Fetch needed resource
m_pColorTarget = GetFramework()->GetColorTargetForCallback(GetName());
m_pColorRasterView = GetRasterViewAllocator()->RequestRasterView(m_pColorTarget, ViewDimension::Texture2D);
// Register UI for SPD
UISection* uiSection = GetUIManager()->RegisterUIElements("Downsampler", UISectionType::Sample);
const char* downsamplers[] = { "Multipass PS", "Multipass CS", "SPD CS" };
const char* loadOptions[] = { "Load", "Linear Sampler" };
const char* waveOptions[] = { "LocalDataShare", "WaveOps" };
const char* mathOptions[] = { "Non-Packed", "Packed" };
const char* sliceOptions[] = { "0", "1", "2", "3", "4", "5" };
std::vector<const char*> comboOptions;
// Add down sampler combo
comboOptions.assign(downsamplers, downsamplers + _countof(downsamplers));
uiSection->RegisterUIElement<UICombo>(
"Downsampler options",
m_DownsamplerUsed,
std::move(comboOptions),
[this](int32_t cur, int32_t old) {
if (cur != old)
{
UpdateSPDContext(false);
UpdateSPDContext(true);
}
}
);
// Use the same callback for all option changes, which will always destroy/create the context
std::function<void(int32_t, int32_t)> optionChangeCallback = [this](int32_t, int32_t) {
if (m_ContextCreated)
{
// Refresh
UpdateSPDContext(false);
UpdateSPDContext(true);
}
};
// Add load/linear combo
comboOptions.assign(loadOptions, loadOptions + _countof(loadOptions));
uiSection->RegisterUIElement<UICombo>("SPD Load / Linear", m_SPDLoadLinear, std::move(comboOptions), optionChangeCallback);
// Add wave op combo
comboOptions.assign(waveOptions, waveOptions + _countof(waveOptions));
uiSection->RegisterUIElement<UICombo>("SPD Wave Interop", m_SPDWaveInterop, std::move(comboOptions), optionChangeCallback);
// Add math combo
comboOptions.assign(mathOptions, mathOptions + _countof(mathOptions));
uiSection->RegisterUIElement<UICombo>("SPD Math", m_SPDMath, std::move(comboOptions), optionChangeCallback);
// Add a combo for the slice to view (assumes a cubemap, if ever we are viewing a 2d texture, disable UI)
comboOptions.assign(sliceOptions, sliceOptions + _countof(sliceOptions));
uiSection->RegisterUIElement<UICombo>("Slice to View", (int32_t&)m_ViewSlice, std::move(comboOptions));
GetFramework()->ConfigureRuntimeShaderRecompiler(
[this](void) { DestroyFfxContext(); }, [this](void) { InitFfxContext(); });
// Initialize common resources that aren't pipeline dependent
m_LinearSamplerDesc.Filter = FilterFunc::MinMagLinearMipPoint;
m_LinearSamplerDesc.MaxLOD = std::numeric_limits<float>::max();
m_LinearSamplerDesc.MaxAnisotropy = 1;
// Pipelines will be initialized after the texture is loaded
// Using AllowRenderTarget + AllowUnorderedAccess on the same resource is usually
// frowned upon for performance reasons, but we are doing it here in the interest of re-using a resource
TextureLoadCompletionCallbackFn completionCallback =
[this](const std::vector<const Texture*>& textures, void* additionalParams = nullptr) {
this->TextureLoadComplete(textures, additionalParams);
};
filesystem::path texturePath = L"..\\media\\Textures\\SPD\\spd_cubemap.dds";
GetContentManager()->LoadTexture(TextureLoadInfo(texturePath, true, 1.f,
ResourceFlags::AllowRenderTarget | ResourceFlags::AllowUnorderedAccess),
completionCallback);
}
SPDRenderModule::~SPDRenderModule()
{
DestroyFfxContext();
// Delete pipeline objects and resources
for (int32_t i = 0; i < static_cast<int32_t>(DownsampleTechnique::Count); ++i)
{
for (auto paramIter = m_PipelineSets[i].ParameterSets.begin(); paramIter != m_PipelineSets[i].ParameterSets.end(); ++paramIter)
delete (*paramIter);
m_PipelineSets[i].ParameterSets.clear();
delete m_PipelineSets[i].pPipelineObj;
delete m_PipelineSets[i].pRootSignature;
}
// Delete verification pipeline set
for (ParameterSet* pParamSet : m_VerificationSet.ParameterSets)
delete pParamSet;
m_VerificationSet.ParameterSets.clear();
delete m_VerificationSet.pPipelineObj;
delete m_VerificationSet.pRootSignature;
m_pColorRasterView = nullptr; // Don't own this memory
// Clear out our raster views
m_RasterViews.clear();
// Don't own this memory
m_pCubeTexture = nullptr;
}
void SPDRenderModule::DestroyFfxContext()
{
// Tear down SPD context
UpdateSPDContext(false);
// Destroy the FidelityFX interface memory
if (m_InitializationParameters.backendInterface.scratchBuffer != nullptr)
{
free(m_InitializationParameters.backendInterface.scratchBuffer);
m_InitializationParameters.backendInterface.scratchBuffer = nullptr;
}
}
void SPDRenderModule::InitTraditionalDSPipeline(bool computeDownsample)
{
ShaderBindStage shaderStage = (computeDownsample) ? ShaderBindStage::Compute : ShaderBindStage::Pixel;
int32_t pipelineID = (computeDownsample) ? static_cast<int32_t>(DownsampleTechnique::CSDownsample) : static_cast<int32_t>(DownsampleTechnique::PSDownsample);
// Create root signature
RootSignatureDesc signatureDesc;
signatureDesc.AddConstantBufferView(0, shaderStage, 1);
signatureDesc.AddTextureSRVSet(0, shaderStage, 1);
signatureDesc.AddStaticSamplers(0, shaderStage, 1, &m_LinearSamplerDesc);
std::wstring rootName;
if (computeDownsample)
{
signatureDesc.AddTextureUAVSet(0, shaderStage, 1);
rootName = L"SPD_DownsampleCS_RootSignature";
}
else
rootName = L"SPD_DownsamplePS_RootSignature";
m_PipelineSets[pipelineID].pRootSignature = RootSignature::CreateRootSignature(rootName.c_str(), signatureDesc);
// Setup the pipeline object
std::wstring pipelineName;
PipelineDesc psoDesc;
psoDesc.SetRootSignature(m_PipelineSets[pipelineID].pRootSignature);
// Setup the shaders to build on the pipeline object
if (computeDownsample)
{
pipelineName = L"SPD_DownsampleCS_PipelineObj";
psoDesc.AddShaderDesc(ShaderBuildDesc::Compute(L"spd_cs_downsampler.hlsl", L"mainCS", ShaderModel::SM6_0, nullptr));
}
else
{
pipelineName = L"SPD_DownsamplePS_PipelineObj";
psoDesc.AddShaderDesc(ShaderBuildDesc::Vertex(L"fullscreen.hlsl", L"FullscreenVS", ShaderModel::SM6_0, nullptr));
psoDesc.AddShaderDesc(ShaderBuildDesc::Pixel(L"spd_ps_downsampler.hlsl", L"mainPS", ShaderModel::SM6_0, nullptr));
// Setup remaining information and build
psoDesc.AddPrimitiveTopology(PrimitiveTopologyType::Triangle);
psoDesc.AddRasterFormats(m_pCubeTexture->GetFormat()); // Use the first raster set, as we just want the format and they are all the same
}
m_PipelineSets[pipelineID].pPipelineObj = PipelineObject::CreatePipelineObject(pipelineName.c_str(), psoDesc);
// Setup parameter sets
// Now that the texture is loaded, create raster passes, and initialize the different down sample techniques
const TextureDesc& desc = m_pCubeTexture->GetDesc();
uint32_t paramSetCount = (computeDownsample) ? desc.MipLevels - 1 : desc.DepthOrArraySize * (desc.MipLevels - 1);
m_PipelineSets[pipelineID].ParameterSets.clear();
m_PipelineSets[pipelineID].ParameterSets.resize(paramSetCount);
for (uint32_t slice = 0; slice < desc.DepthOrArraySize; ++slice)
{
for (uint32_t mip = 0; mip < desc.MipLevels - 1; ++mip)
{
uint32_t offset = slice * (desc.MipLevels - 1) + mip;
if (!slice || !computeDownsample)
{
ParameterSet* pParamSet = ParameterSet::CreateParameterSet(m_PipelineSets[pipelineID].pRootSignature);
pParamSet->SetRootConstantBufferResource(GetDynamicBufferPool()->GetResource(), sizeof(SPDDownsampleInfo), 0);
// If this is the CS version we also need to setup the UAV for writes to the same parameter set
if (!computeDownsample)
pParamSet->SetTextureSRV(m_pCubeTexture, ViewDimension::Texture2DArray, 0, mip, 1, slice);
else
{
pParamSet->SetTextureSRV(m_pCubeTexture, ViewDimension::Texture2DArray, 0, mip, desc.DepthOrArraySize, 0);
pParamSet->SetTextureUAV(m_pCubeTexture, ViewDimension::Texture2DArray, 0, mip + 1, desc.DepthOrArraySize, 0);
}
m_PipelineSets[pipelineID].ParameterSets[offset] = pParamSet;
}
}
}
}
void SPDRenderModule::InitVerificationPipeline()
{
// Initialize the verification pipeline set
RootSignatureDesc verificationSignatureDesc;
verificationSignatureDesc.AddConstantBufferView(0, ShaderBindStage::VertexAndPixel, 1);
verificationSignatureDesc.AddTextureSRVSet(0, ShaderBindStage::Pixel, 1);
verificationSignatureDesc.AddStaticSamplers(0, ShaderBindStage::Pixel, 1, &m_LinearSamplerDesc);
m_VerificationSet.pRootSignature = RootSignature::CreateRootSignature(L"SPD_VerificationSignature", verificationSignatureDesc);
PipelineDesc psoDesc;
psoDesc.SetRootSignature(m_VerificationSet.pRootSignature);
psoDesc.AddShaderDesc(ShaderBuildDesc::Vertex(L"spd_verify_results.hlsl", L"MainVS", ShaderModel::SM6_0, nullptr));
psoDesc.AddShaderDesc(ShaderBuildDesc::Pixel(L"spd_verify_results.hlsl", L"MainPS", ShaderModel::SM6_0, nullptr));
// Setup remaining information and build
psoDesc.AddPrimitiveTopology(PrimitiveTopologyType::Triangle);
psoDesc.AddRasterFormats(m_pColorTarget->GetFormat());
m_VerificationSet.pPipelineObj = PipelineObject::CreatePipelineObject(L"SPD_VerificationPipeline", psoDesc);
// Setup parameter sets
const TextureDesc& desc = m_pCubeTexture->GetDesc();
m_VerificationSet.ParameterSets.clear();
m_VerificationSet.ParameterSets.resize(1);
ParameterSet* pParamSet = ParameterSet::CreateParameterSet(m_VerificationSet.pRootSignature);
pParamSet->SetRootConstantBufferResource(GetDynamicBufferPool()->GetResource(), sizeof(SPDVerifyConstants), 0);
pParamSet->SetTextureSRV(m_pCubeTexture, ViewDimension::Texture2DArray, 0);
m_VerificationSet.ParameterSets[0] = pParamSet;
}
void SPDRenderModule::UpdateSPDContext(bool enabled)
{
if (enabled && !m_ContextCreated)
{
// Setup all the parameters for this SPD run
m_InitializationParameters.flags = 0; // Reset
m_InitializationParameters.flags |= m_SPDLoadLinear ? FFX_SPD_SAMPLER_LINEAR : FFX_SPD_SAMPLER_LOAD;
m_InitializationParameters.flags |= m_SPDWaveInterop ? FFX_SPD_WAVE_INTEROP_WAVE_OPS : FFX_SPD_WAVE_INTEROP_LDS;
m_InitializationParameters.flags |= m_SPDMath ? FFX_SPD_MATH_PACKED : FFX_SPD_MATH_NONPACKED;
ffxSpdContextCreate(&m_Context, &m_InitializationParameters);
m_ContextCreated = true;
}
else if (!enabled && m_ContextCreated)
{
// Flush anything out of the pipes before destroying the context
GetDevice()->FlushAllCommandQueues();
ffxSpdContextDestroy(&m_Context);
m_ContextCreated = false;
}
}
void SPDRenderModule::TextureLoadComplete(const std::vector<const Texture*>& textureList, void*)
{
// Cube map for SPD
m_pCubeTexture = textureList[0];
// Will need to reference texture information to create appropriate views
const TextureDesc& desc = m_pCubeTexture->GetDesc();
int32_t psID = static_cast<int32_t>(DownsampleTechnique::PSDownsample);
// Now that the texture is loaded, create raster passes, and initialize the different down sample techniques
m_RasterViews.resize(desc.DepthOrArraySize * (desc.MipLevels - 1));
for (uint32_t slice = 0; slice < desc.DepthOrArraySize; ++slice)
{
for (uint32_t mip = 0; mip < desc.MipLevels - 1; ++mip)
{
// Setup raster sets for the pixel shader down sample
int32_t resourceOffset = slice * (desc.MipLevels - 1) + mip;
m_RasterViews[resourceOffset] = GetRasterViewAllocator()->RequestRasterView(m_pCubeTexture, ViewDimension::Texture2DArray, mip + 1, 1, slice);
}
}
// Init all pipelines for the various down sample modes
InitTraditionalDSPipeline(false); // PS version
InitTraditionalDSPipeline(true); // CS version
InitVerificationPipeline(); // Result verification pipeline set
InitFfxContext();
// We are now ready for use
SetModuleReady(true);
}
void SPDRenderModule::InitFfxContext()
{
// Initialize the FFX backend
const size_t scratchBufferSize = SDKWrapper::ffxGetScratchMemorySize(FFX_SPD_CONTEXT_COUNT);
void* scratchBuffer = calloc(scratchBufferSize, 1u);
FfxErrorCode errorCode = SDKWrapper::ffxGetInterface(&m_InitializationParameters.backendInterface, GetDevice(), scratchBuffer, scratchBufferSize, FFX_SPD_CONTEXT_COUNT);
CAULDRON_ASSERT(errorCode == FFX_OK);
CauldronAssert(ASSERT_CRITICAL, m_InitializationParameters.backendInterface.fpGetSDKVersion(&m_InitializationParameters.backendInterface) == FFX_SDK_MAKE_VERSION(1, 1, 2),
L"FidelityFX SPD 2.1 sample requires linking with a 1.1.2 version SDK backend");
CauldronAssert(ASSERT_CRITICAL, ffxSpdGetEffectVersion() == FFX_SDK_MAKE_VERSION(2, 2, 0),
L"FidelityFX SPD 2.1 sample requires linking with a 2.2 version FidelityFX SPD library");
m_InitializationParameters.backendInterface.fpRegisterConstantBufferAllocator(&m_InitializationParameters.backendInterface, SDKWrapper::ffxAllocateConstantBuffer);
// Init SPD
UpdateSPDContext(m_DownsamplerUsed == static_cast<int32_t>(DownsampleTechnique::SPDDownsample));
}
void SPDRenderModule::Execute(double deltaTime, CommandList* pCmdList)
{
GPUScopedProfileCapture sampleMarker(pCmdList, L"FFX SPD");
// Pick the right application based on down sampling technique
DownsampleTechnique technique = static_cast<DownsampleTechnique>(m_DownsamplerUsed);
switch (technique)
{
case DownsampleTechnique::PSDownsample:
ExecutePSDownsample(deltaTime, pCmdList);
break;
case DownsampleTechnique::CSDownsample:
ExecuteCSDownsample(deltaTime, pCmdList);
break;
case DownsampleTechnique::SPDDownsample:
default:
ExecuteSPDDownsample(deltaTime, pCmdList);
break;
}
// Render the verification quads
ExecuteVerificationQuads(deltaTime, pCmdList);
}
void SPDRenderModule::OnResize(const ResolutionInfo& resInfo)
{
if (!ModuleEnabled())
return;
// Refresh
UpdateSPDContext(false);
UpdateSPDContext(true);
}
void SPDRenderModule::ExecuteVerificationQuads(double deltaTime, CommandList* pCmdList)
{
SetPrimitiveTopology(pCmdList, PrimitiveTopology::TriangleList);
// Barrier the color target to render
Barrier rtBarrier = Barrier::Transition(m_pColorTarget->GetResource(),
ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource,
ResourceState::RenderTargetResource);
ResourceBarrier(pCmdList, 1, &rtBarrier);
// Begin raster into cube map mip face
BeginRaster(pCmdList, 1, &m_pColorRasterView);
// Allocate a dynamic constant buffer and set
SPDVerifyConstants verityConst = { (uint32_t)m_VerificationSet.ParameterSets.size(), m_ViewSlice, 1.f / GetFramework()->GetAspectRatio(), 0 };
BufferAddressInfo bufferInfo = GetDynamicBufferPool()->AllocConstantBuffer(sizeof(SPDVerifyConstants), &verityConst);
m_VerificationSet.ParameterSets[0]->UpdateRootConstantBuffer(&bufferInfo, 0);
// Bind all parameters
m_VerificationSet.ParameterSets[0]->Bind(pCmdList, m_VerificationSet.pPipelineObj);
// Set pipeline and draw
const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo();
Viewport vp = { 0.f, 0.f, resInfo.fDisplayWidth(), resInfo.fDisplayHeight(), 0.f, 1.f };
SetViewport(pCmdList, &vp);
Rect scissorRect = { 0, 0, resInfo.RenderWidth, resInfo.RenderHeight };
SetScissorRects(pCmdList, 1, &scissorRect);
SetPipelineState(pCmdList, m_VerificationSet.pPipelineObj);
CauldronAssert(ASSERT_CRITICAL, m_pCubeTexture->GetDesc().MipLevels < SPD_MAX_MIP_LEVELS, L"SPD Shader only can't represent mip. Please grow SPD_MAX_MIP_LEVELS");
DrawInstanced(pCmdList, 6, m_pCubeTexture->GetDesc().MipLevels); // Each mip will represent another quad instance
// End raster into cube map mip face
EndRaster(pCmdList);
rtBarrier = Barrier::Transition(m_pColorTarget->GetResource(),
ResourceState::RenderTargetResource,
ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource);
ResourceBarrier(pCmdList, 1, &rtBarrier);
}
void SPDRenderModule::ExecutePSDownsample(double deltaTime, cauldron::CommandList* pCmdList)
{
PipelineSet& pipeline = m_PipelineSets[static_cast<size_t>(DownsampleTechnique::PSDownsample)];
// Applies to all
SetPrimitiveTopology(pCmdList, PrimitiveTopology::TriangleList);
// Down sample each face/mip individually
const TextureDesc& desc = m_pCubeTexture->GetDesc();
for (uint32_t slice = 0; slice < desc.DepthOrArraySize; ++slice)
{
for (uint32_t mip = 0; mip < desc.MipLevels - 1; ++mip)
{
int32_t resourceOffset = slice * (desc.MipLevels - 1) + mip;
// Barrier the face in/out
Barrier rtBarrier = Barrier::Transition(m_pCubeTexture->GetResource(),
ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource,
ResourceState::RenderTargetResource,
slice * desc.MipLevels + mip + 1);
ResourceBarrier(pCmdList, 1, &rtBarrier);
// Begin raster into cube map mip face
BeginRaster(pCmdList, 1, &m_RasterViews[resourceOffset]);
// Allocate a dynamic constant buffer and set
SPDDownsampleInfo constants = { desc.Width >> (mip +1), desc.Height >> (mip + 1), // OutSize
1.f / static_cast<float>(desc.Width >> mip), 1.f / static_cast<float>(desc.Height >> mip), // InvSize
0, 0, 0, 0 }; // Padding
BufferAddressInfo bufferInfo = GetDynamicBufferPool()->AllocConstantBuffer(sizeof(SPDDownsampleInfo), &constants);
pipeline.ParameterSets[resourceOffset]->UpdateRootConstantBuffer(&bufferInfo, 0);
// Bind all parameters
pipeline.ParameterSets[resourceOffset]->Bind(pCmdList, pipeline.pPipelineObj);
// Set pipeline and draw
SetViewportScissorRect(pCmdList, 0, 0, desc.Width >> (mip + 1), desc.Height >> (mip + 1), 0.f, 1.f);
SetPipelineState(pCmdList, pipeline.pPipelineObj);
DrawInstanced(pCmdList, 3);
// End raster into cube map mip face
EndRaster(pCmdList);
rtBarrier = Barrier::Transition(m_pCubeTexture->GetResource(),
ResourceState::RenderTargetResource,
ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource,
slice * desc.MipLevels + mip + 1);
ResourceBarrier(pCmdList, 1, &rtBarrier);
}
}
}
void SPDRenderModule::ExecuteCSDownsample(double deltaTime, cauldron::CommandList* pCmdList)
{
PipelineSet& pipeline = m_PipelineSets[static_cast<size_t>(DownsampleTechnique::CSDownsample)];
// Down sample each face/mip individually
const TextureDesc& desc = m_pCubeTexture->GetDesc();
for (int32_t slice = 0; slice < (int32_t)desc.DepthOrArraySize ; ++slice)
{
for (uint32_t mip = 0; mip < desc.MipLevels - 1; ++mip)
{
// Barrier the face in/out
Barrier rtBarrier = Barrier::Transition(m_pCubeTexture->GetResource(),
ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource,
ResourceState::UnorderedAccess, mip + 1);
ResourceBarrier(pCmdList, 1, &rtBarrier);
// Allocate a dynamic constant buffer and set
SPDDownsampleInfo constants = { desc.Width >> (mip + 1), desc.Height >> (mip + 1), // OutSize
1.f / static_cast<float>(desc.Width >> mip), 1.f / static_cast<float>(desc.Height >> mip), // InvSize
slice, 0, 0, 0 }; // Slice + Padding
BufferAddressInfo bufferInfo = GetDynamicBufferPool()->AllocConstantBuffer(sizeof(SPDDownsampleInfo), &constants);
pipeline.ParameterSets[mip]->UpdateRootConstantBuffer(&bufferInfo, 0);
// Bind all parameters
pipeline.ParameterSets[mip]->Bind(pCmdList, pipeline.pPipelineObj);
// Set pipeline and dispatch
SetPipelineState(pCmdList, pipeline.pPipelineObj);
uint32_t dispatchX = DivideRoundingUp(desc.Width >> (mip + 1), 8);
uint32_t dispatchY = DivideRoundingUp(desc.Height >> (mip + 1), 8);
uint32_t dispatchZ = 1;
Dispatch(pCmdList, dispatchX, dispatchY, dispatchZ);
rtBarrier = Barrier::Transition(m_pCubeTexture->GetResource(),
ResourceState::UnorderedAccess,
ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource,
mip + 1);
ResourceBarrier(pCmdList, 1, &rtBarrier);
}
}
}
void SPDRenderModule::ExecuteSPDDownsample(double deltaTime, cauldron::CommandList* pCmdList)
{
GPUScopedProfileCapture sampleMarker(pCmdList, L"SPD");
FfxSpdDispatchDescription dispatchParameters = {};
dispatchParameters.commandList = SDKWrapper::ffxGetCommandList(pCmdList);
dispatchParameters.resource = SDKWrapper::ffxGetResource(m_pCubeTexture->GetResource(), L"SPD_Downsample_Resource", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ, FFX_RESOURCE_USAGE_ARRAYVIEW);
FfxErrorCode errorCode = ffxSpdContextDispatch(&m_Context, &dispatchParameters);
CAULDRON_ASSERT(errorCode == FFX_OK);
// FidelityFX contexts modify the set resource view heaps, so set the cauldron one back
SetAllResourceViewHeaps(pCmdList);
}