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.
238 lines
8.6 KiB
C++
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();
|
|
}
|
|
}
|
|
}
|