You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Atom/RPI/Code/Source/RPI.Public/PipelineState.cpp

238 lines
8.6 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Atom/RPI.Public/PipelineState.h>
#include <Atom/RPI.Public/Scene.h>
namespace AZ
{
namespace RPI
{
PipelineStateForDraw::PipelineStateForDraw()
: m_dirty(false)
, m_initDataFromShader(false)
, m_hasOutputData(false)
, m_isShaderVariantReady(false)
, m_useRenderStatesOverlay(false)
{
m_renderStatesOverlay = RHI::GetInvalidRenderStates();
}
PipelineStateForDraw::PipelineStateForDraw(const PipelineStateForDraw& right)
{
m_descriptor = right.m_descriptor;
m_shader = right.m_shader;
m_pipelineState = right.m_pipelineState;
m_initDataFromShader = right.m_initDataFromShader;
m_hasOutputData = right.m_hasOutputData;
m_dirty = right.m_dirty;
m_shaderVariantId = right.m_shaderVariantId;
m_isShaderVariantReady = right.m_isShaderVariantReady;
m_useRenderStatesOverlay = right.m_useRenderStatesOverlay;
m_renderStatesOverlay = right.m_renderStatesOverlay;
if (m_shader)
{
ShaderReloadNotificationBus::MultiHandler::BusConnect(m_shader->GetAsset().GetId());
}
}
PipelineStateForDraw::~PipelineStateForDraw()
{
Shutdown();
}
void PipelineStateForDraw::Init(const Data::Instance<RPI::Shader>& shader, const ShaderOptionList* optionAndValues)
{
// Reset some variables
m_pipelineState = nullptr;
m_shaderVariantId = ShaderVariantId{};
// Reset some flags
m_dirty = true;
m_isShaderVariantReady = true;
// Get shader variant from the shader
auto shaderVariant = shader->GetRootVariant();
if (optionAndValues)
{
RPI::ShaderOptionGroup shaderOptionGroup = shader->CreateShaderOptionGroup();
shaderOptionGroup.SetUnspecifiedToDefaultValues();
for (const auto& optionAndValue : *optionAndValues)
{
shaderOptionGroup.SetValue(optionAndValue.first, optionAndValue.second);
}
m_shaderVariantId = shaderOptionGroup.GetShaderVariantId();
shaderVariant = shader->GetVariant(m_shaderVariantId);
m_isShaderVariantReady = shaderVariant.IsFullyBaked();
}
// Fill the descriptor with data from shader variant
shaderVariant.ConfigurePipelineState(m_descriptor);
// Connect to shader reload notification bus to rebuilt pipeline state when shader or shader variant changed.
ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
ShaderReloadNotificationBus::MultiHandler::BusConnect(shader->GetAsset().GetId());
m_initDataFromShader = true;
// Cache shader so it can be used for create RHI::PipelineState later
m_shader = shader;
}
void PipelineStateForDraw::RefreshShaderVariant()
{
auto shaderVariant = m_shader->GetVariant(m_shaderVariantId);
m_isShaderVariantReady = shaderVariant.IsFullyBaked();
auto multisampleState = m_descriptor.m_renderStates.m_multisampleState;
shaderVariant.ConfigurePipelineState(m_descriptor);
// Recover multisampleState if it was set from output data
if (m_hasOutputData)
{
m_descriptor.m_renderStates.m_multisampleState = multisampleState;
}
// re-finalize if the pipeline state wasn't finalized.
if (!m_dirty)
{
m_dirty = true;
Finalize();
}
}
void PipelineStateForDraw::OnShaderReinitialized([[maybe_unused]] const AZ::RPI::Shader& shader)
{
RefreshShaderVariant();
}
void PipelineStateForDraw::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset<ShaderAsset>& shaderAsset)
{
RefreshShaderVariant();
}
void PipelineStateForDraw::OnShaderVariantReinitialized(const ShaderVariant& shaderVariant)
{
if(shaderVariant.GetShaderVariantId() == m_shaderVariantId)
{
RefreshShaderVariant();
}
}
void PipelineStateForDraw::SetOutputFromScene(const Scene* scene, RHI::DrawListTag overrideDrawListTag)
{
// Use overrideDrawListTag if it's valid. otherwise get DrawListTag from the shader
RHI::DrawListTag drawListTag = overrideDrawListTag;
if (!drawListTag.IsValid())
{
drawListTag = m_shader->GetDrawListTag();
}
// The scene may or may not have the output data for this PipelineState
// For example, if there is no render pipeline in the scene, or there is no pass in the scene with matching draw list tag
m_hasOutputData = scene->ConfigurePipelineState(drawListTag, m_descriptor);
m_dirty = true;
}
void PipelineStateForDraw::SetOutputFromPass(RenderPass* renderPass)
{
m_hasOutputData = true;
m_dirty = true;
m_descriptor.m_renderAttachmentConfiguration = renderPass->GetRenderAttachmentConfiguration();
m_descriptor.m_renderStates.m_multisampleState = renderPass->GetMultisampleState();
}
void PipelineStateForDraw::SetInputStreamLayout(const RHI::InputStreamLayout& inputStreamLayout)
{
m_descriptor.m_inputStreamLayout = inputStreamLayout;
}
const RHI::PipelineState* PipelineStateForDraw::Finalize()
{
if (m_dirty)
{
AZ_Assert(m_initDataFromShader, "PipelineStateForDraw::Init() need to be called once before Finalize()");
m_pipelineState = nullptr;
if (m_hasOutputData && m_initDataFromShader)
{
RHI::PipelineStateDescriptorForDraw descriptor = m_descriptor;
if (m_useRenderStatesOverlay)
{
RHI::MergeStateInto(m_renderStatesOverlay, descriptor.m_renderStates);
}
m_pipelineState = m_shader->AcquirePipelineState(descriptor);
}
m_dirty = false;
}
return m_pipelineState;
}
const RHI::PipelineState* PipelineStateForDraw::GetRHIPipelineState() const
{
AZ_Assert(false == m_dirty, "The descriptor has been modified and Finalize() need to be called before get a proper PipelineState");
return m_pipelineState;
}
RHI::RenderStates& PipelineStateForDraw::RenderStatesOverlay()
{
// Assume the descriptor will be changed if user tries to get reference but not a constant reference
m_dirty = true;
m_useRenderStatesOverlay = true;
return m_renderStatesOverlay;
}
RHI::InputStreamLayout& PipelineStateForDraw::InputStreamLayout()
{
m_dirty = true;
return m_descriptor.m_inputStreamLayout;
}
const RHI::PipelineStateDescriptorForDraw& PipelineStateForDraw::ConstDescriptor() const
{
return m_descriptor;
}
const Data::Instance<Shader>& PipelineStateForDraw::GetShader() const
{
return m_shader;
}
bool PipelineStateForDraw::UpdateSrgVariantFallback(Data::Instance<ShaderResourceGroup>& srg) const
{
if (m_isShaderVariantReady)
{
return false;
}
srg->SetShaderVariantKeyFallbackValue(m_shaderVariantId.m_key);
return true;
}
void PipelineStateForDraw::Shutdown()
{
m_descriptor = RHI::PipelineStateDescriptorForDraw{};
m_shader = nullptr;
m_pipelineState = nullptr;
m_shaderVariantId = ShaderVariantId{};
m_initDataFromShader = false;
m_hasOutputData = false;
m_dirty = false;
m_isShaderVariantReady = false;
ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
}
}
}