// 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 "vrsrendermodule.h" #include "shaders/surfacerendercommon.h" #include "render/device.h" #include "render/dynamicresourcepool.h" #include "render/pipelineobject.h" #include "render/profiler.h" #include "render/rasterview.h" #include "render/uploadheap.h" #include "core/backend_interface.h" #include "core/framework.h" #include "core/components/meshcomponent.h" #include "core/loaders/textureloader.h" #include "core/scene.h" #include "core/uimanager.h" #include "render/parameterset.h" #include "render/shaderbuilderhelper.h" #define FFX_CPP #include using namespace cauldron; struct VRSOverlayInformation { FfxUInt32 width, height; FfxUInt32 tileSize; }; VRSRenderModule::VRSRenderModule() : RenderModule(L"VRSRenderModule") { } VRSRenderModule::~VRSRenderModule() { DestroyFfxContext(); delete m_pOverlayRootSignature; delete m_pOverlayPipelineObj; delete m_pOverlayParameters; GetContentManager()->RemoveContentListener(this); if (m_GenerateMotionVectors) { delete m_pMotionVectorsRootSignature; delete m_pMotionVectorsParameterSet; for (auto& pipeline : m_PipelineHashObjects) { delete pipeline.m_Pipeline; } m_PipelineHashObjects.clear(); m_MotionVectorsRenderSurfaces.clear(); } } void VRSRenderModule::Init(const json& initData) { // VRS Tier support if (GetFramework()->GetDevice()->FeatureSupported(DeviceFeature::VRSTier2)) { m_VRSTierSupported = 2; GetFramework()->GetDevice()->GetFeatureInfo(DeviceFeature::VRSTier2, &m_FeatureInfoVRS); } else if (GetFramework()->GetDevice()->FeatureSupported(DeviceFeature::VRSTier1)) { m_VRSTierSupported = 1; GetFramework()->GetDevice()->GetFeatureInfo(DeviceFeature::VRSTier1, &m_FeatureInfoVRS); } BuildUI(); if (m_VRSTierSupported > 0) { m_pColorTarget = GetFramework()->GetColorTargetForCallback(GetName()); int width = GetFramework()->GetResolutionInfo().RenderWidth; int height = GetFramework()->GetResolutionInfo().RenderHeight; // History color buffer TextureDesc historyColorDesc = m_pColorTarget->GetDesc(); historyColorDesc.Width = width; historyColorDesc.Height = height; historyColorDesc.Name = L"HistoryColorBuffer"; m_pHistoryColorBuffer = GetDynamicResourcePool()->CreateRenderTexture( &historyColorDesc, [](TextureDesc& desc, uint32_t displayWidth, uint32_t displayHeight, uint32_t renderingWidth, uint32_t renderingHeight) { desc.Width = renderingWidth; desc.Height = renderingHeight; }); CauldronAssert(ASSERT_ERROR, m_pHistoryColorBuffer, L"Could not create history color texture"); // VRS Image uint32_t vrsImageWidth, vrsImageHeight; ffxVrsGetImageSizeFromeRenderResolution(&vrsImageWidth, &vrsImageHeight, width, height, m_FeatureInfoVRS.MaxTileSize[0]); TextureDesc vrsImageDesc = TextureDesc::Tex2D( L"VRSImage", ResourceFormat::R8_UINT, vrsImageWidth, vrsImageHeight, 1, 1, ResourceFlags::AllowShadingRate | ResourceFlags::AllowUnorderedAccess); m_pVRSTexture = GetDynamicResourcePool()->CreateTexture( &vrsImageDesc, static_cast(ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource), [](TextureDesc& desc, uint32_t displayWidth, uint32_t displayHeight, uint32_t renderingWidth, uint32_t renderingHeight) { cauldron::FeatureInfo_VRS featureInfoVRS; GetFramework()->GetDevice()->GetFeatureInfo(DeviceFeature::VRSTier2, &featureInfoVRS); uint32_t shadingRateImageTileSize = featureInfoVRS.MaxTileSize[0]; ffxVrsGetImageSizeFromeRenderResolution(&desc.Width, &desc.Height, renderingWidth, renderingHeight, shadingRateImageTileSize); }); CauldronAssert(ASSERT_ERROR, m_pVRSTexture, L"Could not create the VRS image texture"); // Callbacks ExecuteCallback callbackDrawOverlay = [this](double deltaTime, CommandList* pCmdList) { this->DrawOverlayCallback(deltaTime, pCmdList); }; ExecutionTuple callbackDrawOverlayTuple = std::make_pair(L"VRSRenderModule::DrawOverlayCallback", std::make_pair(this, callbackDrawOverlay)); GetFramework()->RegisterExecutionCallback(L"ToneMappingRenderModule", false, callbackDrawOverlayTuple); ExecuteCallback callbackCopyColorBuffer = [this](double deltaTime, CommandList* pCmdList) { this->CopyColorBufferCallback(deltaTime, pCmdList); }; ExecutionTuple callbackCopyColorBufferTuple = std::make_pair(L"VRSRenderModule::CopyColorBufferCallback", std::make_pair(this, callbackCopyColorBuffer)); GetFramework()->RegisterExecutionCallback(L"ToneMappingRenderModule", false, callbackCopyColorBufferTuple); m_GenerateMotionVectors = (GetFramework()->GetConfig()->MotionVectorGeneration == "VRSRenderModule"); if (m_GenerateMotionVectors) { ExecuteCallback callbackGenerateMotionVectors = [this](double deltaTime, CommandList* pCmdList) { this->GenerateMotionVectorsCallback(deltaTime, pCmdList); }; ExecutionTuple callbackGenerateMotionVectorsTuple = std::make_pair(L"VRSRenderModule::GenerateMotionVectorsCallback", std::make_pair(this, callbackGenerateMotionVectors)); GetFramework()->RegisterExecutionCallback(L"VRSRenderModule", true, callbackGenerateMotionVectorsTuple); InitMotionVectors(initData); } InitOverlay(initData); InitFfxBackend(); GetFramework()->ConfigureRuntimeShaderRecompiler( [this]() { DestroyFfxContext(); }, [this](void) { InitFfxContext(); }); } UpdateVRSInfo(); SetModuleReady(true); } void VRSRenderModule::InitFfxBackend() { // Initialize the FFX backend const size_t scratchBufferSize = SDKWrapper::ffxGetScratchMemorySize(FFX_VRS_CONTEXT_COUNT); void* scratchBuffer = calloc(scratchBufferSize, 1u); FfxErrorCode errorCode = SDKWrapper::ffxGetInterface(&m_InitializationParameters.backendInterface, GetDevice(), scratchBuffer, scratchBufferSize, FFX_VRS_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 VRS 2.1 sample requires linking with a 1.1.2 version SDK backend"); CauldronAssert(ASSERT_CRITICAL, ffxVrsGetEffectVersion() == FFX_SDK_MAKE_VERSION(1, 2, 0), L"FidelityFX VRS 2.1 sample requires linking with a 1.2 version FidelityFX VRS library"); m_InitializationParameters.backendInterface.fpRegisterConstantBufferAllocator(&m_InitializationParameters.backendInterface, SDKWrapper::ffxAllocateConstantBuffer); } void VRSRenderModule::InitFfxContext() { InitFfxBackend(); UpdateVRSContext(true); } void VRSRenderModule::DestroyFfxContext() { // Flush anything out of the pipes before destroying the context GetDevice()->FlushAllCommandQueues(); UpdateVRSContext(false); // Destroy the FidelityFX interface memory if (m_InitializationParameters.backendInterface.scratchBuffer != nullptr) { free(m_InitializationParameters.backendInterface.scratchBuffer); m_InitializationParameters.backendInterface.scratchBuffer = nullptr; } } void VRSRenderModule::OnResize(const ResolutionInfo& resInfo) { if (!ModuleEnabled()) { return; } UpdateVRSContext(false); UpdateVRSContext(true); } void VRSRenderModule::Execute(double deltaTime, CommandList* pCmdList) { if (!m_EnableVariableShading) return; if (m_ShadingRateCombinerIndex != 0) { ExecuteVRSImageGen(deltaTime, pCmdList); } } void VRSRenderModule::BuildUI() { // UI UISection* uiSection = GetUIManager()->RegisterUIElements("Variable Shading", UISectionType::Sample); if (m_VRSTierSupported == 0) { uiSection->RegisterUIElement("GPU does not support VRS!"); return; } uiSection->RegisterUIElement( "Enable Variable Shading", m_EnableVariableShading, [this](bool cur, bool old) { ToggleVariableShading(); } ); std::vector comboOptions; const char* shadingRateOptions[] = {"1x1", "1x2", "1x4", "2x1", "2x2", "2x4", "4x1", "4x2", "4x4"}; auto GetFirstSetBitPos = [](int32_t n) { return log2(n & -n) + 1; }; for (uint32_t i = 0; i < (m_FeatureInfoVRS.NumShadingRates); ++i) { ShadingRate shadingRate = m_FeatureInfoVRS.ShadingRates[i]; uint32_t x = (uint32_t)GetFirstSetBitPos(static_cast(shadingRate) >> SHADING_RATE_SHIFT) - 1; uint32_t y = (uint32_t)GetFirstSetBitPos(static_cast(shadingRate) & (uint32_t)(ShadingRate1D::ShadingRate1D_1X | ShadingRate1D::ShadingRate1D_2X | ShadingRate1D::ShadingRate1D_4X)) - 1; comboOptions.push_back(shadingRateOptions[x * SHADING_RATE_SHIFT + y]); } uiSection->RegisterUIElement( "PerDraw VRS", (int32_t&)m_ShadingRateIndex, comboOptions, m_VariableShadingEnabled, [this](int32_t cur, int32_t old) { SelectBaseShadingRate(); } ); if (m_VRSTierSupported == 2) { uiSection->RegisterUIElement( "ShadingRateImage Enabled", m_EnableShadingRateImage, m_VariableShadingEnabled, [this](bool cur, bool old) { ToggleShadingRateImage(); } ); const char* combinerOptions[] = {"Passthrough", "Override", "Min", "Max", "Sum", "Mul"}; comboOptions.clear(); m_AvailableCombiners.clear(); for (int32_t i = 0; i < _countof(combinerOptions); ++i) { if ((uint32_t)m_FeatureInfoVRS.Combiners & (1u << i)) { comboOptions.push_back(combinerOptions[i]); m_AvailableCombiners.push_back(static_cast(1u << i)); } } uiSection->RegisterUIElement( "ShadingRateImage Combiner", (int32_t&)m_ShadingRateCombinerIndex, std::move(comboOptions), m_ShadingRateImageEnabled, [this](int32_t cur, int32_t old) { SelectCombiner(); } ); uiSection->RegisterUIElement>("VRS variance Threshold", m_VRSThreshold, 0.f, 0.1f, m_ShadingRateImageEnabled); uiSection->RegisterUIElement>("VRS Motion Factor", m_VRSMotionFactor, 0.f, 0.1f, m_ShadingRateImageEnabled); uiSection->RegisterUIElement( "ShadingRateImage Overlay", m_DrawOverlay, m_ShadingRateImageEnabled, [this](bool cur, bool old) { ToggleOverlay(); } ); } } void VRSRenderModule::InitOverlay(const json& initData) { // Root signature RootSignatureDesc signatureDesc; signatureDesc.AddConstantBufferView(0, ShaderBindStage::Pixel, 1); signatureDesc.AddTextureSRVSet(0, ShaderBindStage::Pixel, 1); m_pOverlayRootSignature = RootSignature::CreateRootSignature(L"VRSOverlay_RootSignature", signatureDesc); // Fetch needed resources m_pOverlayRenderTarget = GetFramework()->GetRenderTexture(L"SwapChainProxy"); // This occurs after tone mapping, so goes to swapchain proxy CauldronAssert(ASSERT_CRITICAL, m_pOverlayRenderTarget != nullptr, L"Couldn't find or create the render target for VRS Overlay."); m_pOverlayRasterView = GetRasterViewAllocator()->RequestRasterView(m_pOverlayRenderTarget, ViewDimension::Texture2D); // Setup the pipeline object PipelineDesc psoDesc; psoDesc.SetRootSignature(m_pOverlayRootSignature); // Setup the shaders to build on the pipeline object psoDesc.AddShaderDesc(ShaderBuildDesc::Vertex(L"VrsOverlay.hlsl", L"mainVS", ShaderModel::SM6_0, nullptr)); psoDesc.AddShaderDesc(ShaderBuildDesc::Pixel(L"VrsOverlay.hlsl", L"mainPS", ShaderModel::SM6_0, nullptr)); // Setup remaining information and build // Setup blend and depth states BlendDesc blendDesc = { true, Blend::SrcAlpha, Blend::InvSrcAlpha, BlendOp::Add, Blend::One, Blend::InvSrcAlpha, BlendOp::Add, static_cast(ColorWriteMask::All)}; std::vector blends; blends.push_back(blendDesc); psoDesc.AddBlendStates(blends, false, false); DepthDesc depthDesc; psoDesc.AddDepthState(&depthDesc); // Use defaults psoDesc.AddPrimitiveTopology(PrimitiveTopologyType::Triangle); psoDesc.AddRasterFormats(m_pOverlayRenderTarget->GetFormat()); m_pOverlayPipelineObj = PipelineObject::CreatePipelineObject(L"VRSOverlay_PipelineObj", psoDesc); // Create parameter set to bind constant buffer and texture m_pOverlayParameters = ParameterSet::CreateParameterSet(m_pOverlayRootSignature); m_pOverlayParameters->SetRootConstantBufferResource(GetDynamicBufferPool()->GetResource(), sizeof(VRSOverlayInformation), 0); } void VRSRenderModule::InitMotionVectors(const json& initData) { m_pMotionVectors = GetFramework()->GetRenderTexture(L"GBufferMotionVectorRT"); m_pDepthTarget = GetFramework()->GetRenderTexture(L"GBufferDepth"); m_pMotionVectorsRasterView = GetRasterViewAllocator()->RequestRasterView(m_pMotionVectors, ViewDimension::Texture2D); m_pDepthRasterView = GetRasterViewAllocator()->RequestRasterView(m_pDepthTarget, ViewDimension::Texture2D); // Root signature RootSignatureDesc signatureDesc; signatureDesc.AddConstantBufferView(0, ShaderBindStage::VertexAndPixel, 1); // Frame Information signatureDesc.AddConstantBufferView(1, ShaderBindStage::VertexAndPixel, 1); // Instance Information m_pMotionVectorsRootSignature = RootSignature::CreateRootSignature(L"MotionVectorsPass_RootSignature", signatureDesc); // Create ParameterSet and assign the constant buffer parameters // We will add texture views as they are loaded m_pMotionVectorsParameterSet = ParameterSet::CreateParameterSet(m_pMotionVectorsRootSignature); m_pMotionVectorsParameterSet->SetRootConstantBufferResource(GetDynamicBufferPool()->GetResource(), sizeof(SceneInformation), 0); m_pMotionVectorsParameterSet->SetRootConstantBufferResource(GetDynamicBufferPool()->GetResource(), sizeof(InstanceInformation), 1); // Register for content change updates GetContentManager()->AddContentListener(this); } void VRSRenderModule::CopyColorBufferCallback(double deltaTime, cauldron::CommandList* pCmdList) { if (!m_EnableShadingRateImage || m_ShadingRateCombinerIndex == 0) // ShadingRateCombiner_Passthrough return; GPUScopedProfileCapture sampleMarker(pCmdList, L"VRS_CopyColor"); std::vector barriers; barriers.push_back(Barrier::Transition(m_pColorTarget->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::CopySource)); barriers.push_back(Barrier::Transition(m_pHistoryColorBuffer->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::CopyDest)); ResourceBarrier(pCmdList, static_cast(barriers.size()), barriers.data()); // Copy the color render target before we apply translucency TextureCopyDesc copyColor = TextureCopyDesc(m_pColorTarget->GetResource(), m_pHistoryColorBuffer->GetResource()); CopyTextureRegion(pCmdList, ©Color); barriers.clear(); barriers.push_back(Barrier::Transition(m_pColorTarget->GetResource(), ResourceState::CopySource, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource)); barriers.push_back(Barrier::Transition(m_pHistoryColorBuffer->GetResource(), ResourceState::CopyDest, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource)); ResourceBarrier(pCmdList, static_cast(barriers.size()), barriers.data()); } void VRSRenderModule::DrawOverlayCallback(double deltaTime, cauldron::CommandList* pCmdList) { if (!m_DrawOverlay || m_ShadingRateCombinerIndex == 0) // ShadingRateCombiner_Passthrough return; m_pOverlayParameters->SetTextureSRV(m_pVRSTexture, ViewDimension::Texture2D, 0); GPUScopedProfileCapture sampleMarker(pCmdList, L"VRS_DrawOverlay"); // Render modules expect resources coming in/going out to be in a shader read state Barrier rtBarrier = Barrier::Transition(m_pOverlayRenderTarget->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::RenderTargetResource); ResourceBarrier(pCmdList, 1, &rtBarrier); BeginRaster(pCmdList, 1, &m_pOverlayRasterView); const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo(); uint32_t rtWidth, rtHeight; rtWidth = resInfo.DisplayWidth; rtHeight = resInfo.DisplayHeight; VRSOverlayInformation constantData = {rtWidth, rtHeight, m_FeatureInfoVRS.MaxTileSize[0]}; BufferAddressInfo bufferInfo = GetDynamicBufferPool()->AllocConstantBuffer(sizeof(constantData), &constantData); m_pOverlayParameters->UpdateRootConstantBuffer(&bufferInfo, 0); // Bind all parameters m_pOverlayParameters->Bind(pCmdList, m_pOverlayPipelineObj); Viewport vp = {0.f, 0.f, resInfo.fDisplayWidth(), resInfo.fDisplayHeight(), 0.f, 1.f}; SetViewport(pCmdList, &vp); Rect scissorRect = {0, 0, rtWidth, rtHeight}; SetScissorRects(pCmdList, 1, &scissorRect); // Set pipeline and draw SetPrimitiveTopology(pCmdList, PrimitiveTopology::TriangleList); SetPipelineState(pCmdList, m_pOverlayPipelineObj); DrawInstanced(pCmdList, 3, 1, 0, 0); EndRaster(pCmdList); // Render modules expect resources coming in/going out to be in a shader read state rtBarrier = Barrier::Transition(m_pOverlayRenderTarget->GetResource(), ResourceState::RenderTargetResource, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource); ResourceBarrier(pCmdList, 1, &rtBarrier); } void VRSRenderModule::GenerateMotionVectorsCallback(double deltaTime, cauldron::CommandList* pCmdList) { if (!m_GenerateMotionVectors) return; GPUScopedProfileCapture motionVectorsMarker(pCmdList, L"VRS_MotionVectors"); // Render modules expect resources coming in/going out to be in a shader read state std::vector barriers; barriers.push_back(Barrier::Transition( m_pMotionVectors->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::RenderTargetResource)); barriers.push_back(Barrier::Transition( m_pDepthTarget->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::DepthWrite)); ResourceBarrier(pCmdList, static_cast(barriers.size()), barriers.data()); float clearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; ClearRenderTarget(pCmdList, &m_pMotionVectorsRasterView->GetResourceView(), clearColor); ClearDepthStencil(pCmdList, &m_pDepthRasterView->GetResourceView(), 0); // Bind raster resources BeginRaster(pCmdList, 1, &m_pMotionVectorsRasterView, m_pDepthRasterView); // Update necessary scene frame information BufferAddressInfo sceneInfoBufferInfo; sceneInfoBufferInfo = GetDynamicBufferPool()->AllocConstantBuffer(sizeof(SceneInformation), reinterpret_cast(&GetScene()->GetSceneInfo())); m_pMotionVectorsParameterSet->UpdateRootConstantBuffer(&sceneInfoBufferInfo, 0); // Set viewport, scissor, primitive topology once and move on (set based on upscaler state) UpscalerState upscaleState = GetFramework()->GetUpscalingState(); const ResolutionInfo& resInfo = GetFramework()->GetResolutionInfo(); uint32_t width, height; if (upscaleState == UpscalerState::None || upscaleState == UpscalerState::PostUpscale) { width = resInfo.DisplayWidth; height = resInfo.DisplayHeight; } else { width = resInfo.RenderWidth; height = resInfo.RenderHeight; } Viewport vp = {0.f, 0.f, (float)width, (float)height, 0.f, 1.f}; SetViewport(pCmdList, &vp); Rect scissorRect = {0, 0, width, height}; SetScissorRects(pCmdList, 1, &scissorRect); SetPrimitiveTopology(pCmdList, PrimitiveTopology::TriangleList); std::lock_guard pipelineLock(m_CriticalSection); cauldron::PipelineObject* currentPipeline = nullptr; // optimization for (const auto& renderSurface : m_MotionVectorsRenderSurfaces) { if (currentPipeline != renderSurface.m_Pipeline) { SetPipelineState(pCmdList, renderSurface.m_Pipeline); currentPipeline = renderSurface.m_Pipeline; } if (renderSurface.m_RenderSurface.pOwner->IsActive()) { InstanceInformation instanceInfo; instanceInfo.WorldTransform = renderSurface.m_RenderSurface.pOwner->GetTransform(); instanceInfo.PrevWorldTransform = renderSurface.m_RenderSurface.pOwner->GetPrevTransform(); instanceInfo.MaterialInfo.EmissiveFactor = Vec4(0.0f, 0.0f, 0.0f, 0.0f); instanceInfo.MaterialInfo.AlbedoFactor = Vec4(1.0f, 1.0f, 1.0f, 1.0f); instanceInfo.MaterialInfo.PBRParams = Vec4(0.0f, 0.0f, 0.0f, 0.0f); const Surface* pSurface = renderSurface.m_RenderSurface.pSurface; // Update root constants BufferAddressInfo perObjectBufferInfo = GetDynamicBufferPool()->AllocConstantBuffer(sizeof(InstanceInformation), &instanceInfo); m_pMotionVectorsParameterSet->UpdateRootConstantBuffer(&perObjectBufferInfo, 1); // Bind for rendering m_pMotionVectorsParameterSet->Bind(pCmdList, renderSurface.m_Pipeline); std::vector vertexBuffers; uint32_t surfaceAttributes = pSurface->GetVertexAttributes(); // Check if the attribute is present - should always be the case if (surfaceAttributes & VertexAttributeFlag_Position) { vertexBuffers.push_back(pSurface->GetVertexBuffer(VertexAttributeType::Position).pBuffer->GetAddressInfo()); } // Set vertex/index buffers SetVertexBuffers(pCmdList, 0, static_cast(vertexBuffers.size()), vertexBuffers.data()); BufferAddressInfo addressInfo = pSurface->GetIndexBuffer().pBuffer->GetAddressInfo(); SetIndexBuffer(pCmdList, &addressInfo); // And draw DrawIndexedInstanced(pCmdList, pSurface->GetIndexBuffer().Count); } } // Done drawing, unbind EndRaster(pCmdList); // Render modules expect resources coming in/going out to be in a shader read state barriers.clear(); barriers.push_back(Barrier::Transition( m_pMotionVectors->GetResource(), ResourceState::RenderTargetResource, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource)); barriers.push_back(Barrier::Transition( m_pDepthTarget->GetResource(), ResourceState::DepthWrite, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource)); ResourceBarrier(pCmdList, static_cast(barriers.size()), barriers.data()); } void VRSRenderModule::ToggleVariableShading() { if (!m_EnableVariableShading) { m_EnableShadingRateImage = false; m_DrawOverlay = false; m_ShadingRateCombinerIndex = 0; m_ShadingRateIndex = 0; m_VariableShadingEnabled = false; m_ShadingRateImageEnabled = false; } else { m_VariableShadingEnabled = true; } UpdateVRSInfo(); UpdateVRSContext(m_EnableVariableShading); } void VRSRenderModule::ToggleShadingRateImage() { m_ShadingRateImageEnabled = !m_ShadingRateImageEnabled; if (!m_EnableShadingRateImage) m_DrawOverlay = false; m_ShadingRateCombinerIndex = m_EnableShadingRateImage ? 1 : 0; UpdateVRSInfo(); } void VRSRenderModule::ToggleOverlay() { if (!m_EnableShadingRateImage) m_DrawOverlay = false; } void VRSRenderModule::SelectBaseShadingRate() { ShadingRate shadingRate = m_FeatureInfoVRS.ShadingRates[m_ShadingRateIndex]; auto GetFirstSetBitPos = [](int32_t n) { return log2(n & -n) + 1; }; uint32_t x = (uint32_t)GetFirstSetBitPos(static_cast(shadingRate) >> SHADING_RATE_SHIFT) - 1; uint32_t y = (uint32_t)GetFirstSetBitPos(static_cast(shadingRate) & (uint32_t)(ShadingRate1D::ShadingRate1D_1X | ShadingRate1D::ShadingRate1D_2X | ShadingRate1D::ShadingRate1D_4X)) - 1; bool isAdditionalShadingRate = (x + y > 2); if (isAdditionalShadingRate != m_AllowAdditionalShadingRates) { m_AllowAdditionalShadingRates = isAdditionalShadingRate; UpdateVRSContext(false); UpdateVRSContext(true); } UpdateVRSInfo(); } void VRSRenderModule::SelectCombiner() { if (m_ShadingRateCombinerIndex == 0) m_DrawOverlay = false; UpdateVRSInfo(); } void VRSRenderModule::UpdateVRSInfo() { VariableShadingRateInfo info; info.Combiners[0] = ShadingRateCombiner::ShadingRateCombiner_Passthrough; info.Combiners[1] = m_AvailableCombiners.empty() ? ShadingRateCombiner::ShadingRateCombiner_Passthrough : m_AvailableCombiners.at(m_ShadingRateCombinerIndex); info.pShadingRateImage = m_pVRSTexture; info.BaseShadingRate = m_FeatureInfoVRS.ShadingRates[m_ShadingRateIndex]; info.ShadingRateTileWidth = m_FeatureInfoVRS.MaxTileSize[0]; info.ShadingRateTileHeight = m_FeatureInfoVRS.MaxTileSize[1]; info.VariableShadingMode = m_EnableShadingRateImage ? VariableShadingMode::VariableShadingMode_Image : (m_EnableVariableShading ? VariableShadingMode::VariableShadingMode_Per_Draw : VariableShadingMode::VariableShadingMode_None); GetDevice()->SetVRSInfo(info); } void VRSRenderModule::UpdateVRSContext(bool enabled) { if (!enabled && m_ContextCreated) { // Flush anything out of the pipes before destroying the context GetDevice()->FlushAllCommandQueues(); if (m_ContextCreated) ffxVrsContextDestroy(&m_VRSContext); m_ContextCreated = false; } else if (enabled && !m_ContextCreated) { if (m_AllowAdditionalShadingRates) m_InitializationParameters.flags |= FFX_VRS_ALLOW_ADDITIONAL_SHADING_RATES; m_InitializationParameters.shadingRateImageTileSize = m_FeatureInfoVRS.MaxTileSize[0]; ffxVrsContextCreate(&m_VRSContext, &m_InitializationParameters); m_ContextCreated = true; } } void VRSRenderModule::ExecuteVRSImageGen(double deltaTime, cauldron::CommandList* pCmdList) { GPUScopedProfileCapture sampleMarker(pCmdList, L"VRS_ImageGen"); // Render modules expect resources coming in/going out to be in a shader read state Barrier barrier = Barrier::Transition(m_pVRSTexture->GetResource(), ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource, ResourceState::UnorderedAccess); ResourceBarrier(pCmdList, 1, &barrier); uint32_t width = GetFramework()->GetResolutionInfo().RenderWidth; uint32_t height = GetFramework()->GetResolutionInfo().RenderHeight; FfxVrsDispatchDescription dispatchParameters = {}; dispatchParameters.commandList = SDKWrapper::ffxGetCommandList(pCmdList); dispatchParameters.output = SDKWrapper::ffxGetResource(m_pVRSTexture->GetResource(), L"VRSImage", FFX_RESOURCE_STATE_UNORDERED_ACCESS); dispatchParameters.historyColor = SDKWrapper::ffxGetResource(m_pHistoryColorBuffer->GetResource(), L"HistoryColorBuffer", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ); dispatchParameters.motionVectors = SDKWrapper::ffxGetResource(m_pMotionVectors->GetResource(), L"VRSMotionVectorsTarget", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ); dispatchParameters.motionFactor = m_VRSMotionFactor; dispatchParameters.varianceCutoff = m_VRSThreshold; dispatchParameters.tileSize = m_FeatureInfoVRS.MaxTileSize[0]; dispatchParameters.renderSize = {width, height}; dispatchParameters.motionVectorScale.x = -1.f; dispatchParameters.motionVectorScale.y = -1.f; // Disabled until remaining things are fixes FfxErrorCode errorCode = ffxVrsContextDispatch(&m_VRSContext, &dispatchParameters); CAULDRON_ASSERT(errorCode == FFX_OK); // FidelityFX contexts modify the set resource view heaps, so set the cauldron one back SetAllResourceViewHeaps(pCmdList); barrier = Barrier::Transition(m_pVRSTexture->GetResource(), ResourceState::UnorderedAccess, ResourceState::NonPixelShaderResource | ResourceState::PixelShaderResource); ResourceBarrier(pCmdList, 1, &barrier); } void VRSRenderModule::OnNewContentLoaded(ContentBlock* pContentBlock) { MeshComponentMgr* pMeshComponentManager = MeshComponentMgr::Get(); std::lock_guard pipelineLock(m_CriticalSection); // For each new Mesh, create a GBufferComponent that will map mesh/material information for more efficient rendering at run time for (auto* pEntityData : pContentBlock->EntityDataBlocks) { for (auto* pComponent : pEntityData->Components) { if (pComponent->GetManager() == pMeshComponentManager) { const Mesh* pMesh = reinterpret_cast(pComponent)->GetData().pMesh; const size_t numSurfaces = pMesh->GetNumSurfaces(); for (uint32_t i = 0; i < numSurfaces; ++i) { const Surface* pSurface = pMesh->GetSurface(i); const Material* pMaterial = pSurface->GetMaterial(); // Push surface render information PipelineSurfaceRenderInfo surfaceRenderInfo; surfaceRenderInfo.pOwner = pComponent->GetOwner(); surfaceRenderInfo.pSurface = pSurface; // Create pipeline or retrieve already created uint32_t pipeHashIndex = CreatePipelineObject(pSurface); // setup MotionVectorsRenderData MotionVectorsRenderData renderData; renderData.m_Pipeline = m_PipelineHashObjects.at(pipeHashIndex).m_Pipeline; renderData.m_RenderSurface = surfaceRenderInfo; m_MotionVectorsRenderSurfaces.push_back(renderData); } } } } } void VRSRenderModule::OnContentUnloaded(ContentBlock* pContentBlock) { } ////////////////////////////////////////////////////////////////////////// // Content loading helpers uint32_t VRSRenderModule::CreatePipelineObject(const Surface* pSurface) { // VRS shader should be optimized based on what the model provides // It only needs the Position attributes // so we should accept by default all the attributes except the texcoords uint32_t usedAttributes = VertexAttributeFlag_Position; // only keep the available attributes of the surface const uint32_t surfaceAttributes = pSurface->GetVertexAttributes(); usedAttributes = usedAttributes & surfaceAttributes; CauldronAssert(ASSERT_CRITICAL, usedAttributes != 0, L"Encountered a surface that has no position attribute."); DefineList defineList; const Material* pMaterial = pSurface->GetMaterial(); // defines in the shaders // ID_skinningMatrices - todo // ID_normalTexCoord // ID_emissiveTexCoord // ID_occlusionTexCoord // ID_albedoTexCoord // ID_metallicRoughnessTexCoord // ID_normalTexture // ID_emissiveTexture // ID_occlusionTexture // ID_albedoTexture // ID_metallicRoughnessTexture defineList.insert(std::make_pair(L"HAS_MOTION_VECTORS", L"1")); // compute hash uint64_t hash = static_cast(Hash(defineList, usedAttributes, pSurface)); // See if we've already built this pipeline for (uint32_t i = 0; i < m_PipelineHashObjects.size(); ++i) { if (m_PipelineHashObjects[i].m_PipelineHash == hash) return i; } // If we didn't find the pipeline already, create a new one // Setup the pipeline object PipelineDesc psoDesc; psoDesc.SetRootSignature(m_pMotionVectorsRootSignature); // Setup the shaders to build on the pipeline object psoDesc.AddShaderDesc(ShaderBuildDesc::Vertex(L"transformVS.hlsl", L"MainVS", ShaderModel::SM6_0, &defineList)); psoDesc.AddShaderDesc(ShaderBuildDesc::Pixel(L"motionvectorsps.hlsl", L"MainPS", ShaderModel::SM6_0, &defineList)); // Setup remaining information and build psoDesc.AddPrimitiveTopology(PrimitiveTopologyType::Triangle); psoDesc.AddRasterFormats(m_pMotionVectors->GetFormat(), m_pDepthTarget->GetFormat()); std::vector blendDesc; psoDesc.AddBlendStates(blendDesc, false, false); RasterDesc rasterDesc; rasterDesc.CullingMode = CullMode::None; psoDesc.AddRasterStateDescription(&rasterDesc); // Set input layout std::vector vertexAttributes; vertexAttributes.push_back(InputLayoutDesc( VertexAttributeType::Position, pSurface->GetVertexBuffer(VertexAttributeType::Position).ResourceDataFormat, static_cast(vertexAttributes.size()), 0)); psoDesc.AddInputLayout(vertexAttributes); DepthDesc depthDesc; depthDesc.DepthEnable = true; depthDesc.StencilEnable = false; depthDesc.DepthWriteEnable = true; depthDesc.DepthFunc = ComparisonFunc::Less; psoDesc.AddDepthState(&depthDesc); PipelineObject* pPipelineObj = PipelineObject::CreatePipelineObject(L"MotionVectorsRenderPass_PipelineObj", psoDesc); // Ok, this is a new pipeline, add it to the PipelineHashObject vector PipelineHashObject pipelineHashObject; pipelineHashObject.m_Pipeline = pPipelineObj; pipelineHashObject.m_PipelineHash = hash; m_PipelineHashObjects.push_back(pipelineHashObject); return static_cast(m_PipelineHashObjects.size() - 1); }