Files
o3de/Gems/Atom/RPI/Code/Source/RPI.Public/PipelineState.cpp
T
Chris Santora c158ca178f Added new shader reinitialization signaling.
This was done while working on "ATOM-15728 Shader Hot Reload Fails in Debug Build", but it turned out these changes did not actually fix the issue (or any other known hot-reload issue). Still, these improvements are appropriate as they correct logical oversights.

ShaderVariant was not listening to asset reloads. It needs to know when the ShaderVariantAsset reload happens so it can reinitialize it's members as well as propagate reinitialization messages. I added a member for the ShaderAsset as the class needs this to reinitialize itself. So now the class listens for reloads of both the ShaderVariantAsset and the ShaderAsset.

Shader was not listening for ShaderAsset reinitialization events.

Updated the API for ShaderReloadNotificationBus's OnShaderVariantReinitialized to include the ShaderVariant which is the most relevant information (the other information wasn't really being used anyway).
2021-06-15 19:26:11 -07:00

242 lines
8.9 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#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();
}
}
}