Hair - creating parent class for raster pass as prep for ShortCut technique (#4560)
Signed-off-by: Adi-Amazon <barlev@amazon.com>
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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/RHI/CommandList.h>
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
#include <Atom/RHI/DrawPacketBuilder.h>
|
||||
#include <Atom/RHI/PipelineState.h>
|
||||
|
||||
#include <Atom/RPI.Public/View.h>
|
||||
#include <Atom/RPI.Public/RPIUtils.h>
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/PassUtils.h>
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
#include <Atom/RPI.Reflect/Pass/RasterPassData.h>
|
||||
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
|
||||
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
|
||||
|
||||
#include <Passes/HairGeometryRasterPass.h>
|
||||
#include <Rendering/HairRenderObject.h>
|
||||
#include <Rendering/HairFeatureProcessor.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
namespace Hair
|
||||
{
|
||||
|
||||
// --- Creation & Initialization ---
|
||||
RPI::Ptr<HairGeometryRasterPass> HairGeometryRasterPass::Create(const RPI::PassDescriptor& descriptor)
|
||||
{
|
||||
RPI::Ptr<HairGeometryRasterPass> pass = aznew HairGeometryRasterPass(descriptor);
|
||||
return pass;
|
||||
}
|
||||
|
||||
HairGeometryRasterPass::HairGeometryRasterPass(const RPI::PassDescriptor& descriptor)
|
||||
: RasterPass(descriptor),
|
||||
m_passDescriptor(descriptor)
|
||||
{
|
||||
// For inherited classes, override this method and set the proper path.
|
||||
// Example: "Shaders/hairrenderingfillppll.azshader"
|
||||
SetShaderPath("dummyShaderPath");
|
||||
}
|
||||
|
||||
bool HairGeometryRasterPass::AcquireFeatureProcessor()
|
||||
{
|
||||
if (m_featureProcessor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
RPI::Scene* scene = GetScene();
|
||||
if (scene)
|
||||
{
|
||||
m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_featureProcessor)
|
||||
{
|
||||
AZ_Warning("Hair Gem", false,
|
||||
"HairGeometryRasterPass [%s] - Failed to retrieve Hair feature processor from the scene",
|
||||
GetName().GetCStr());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HairGeometryRasterPass::InitializeInternal()
|
||||
{
|
||||
if (GetScene())
|
||||
{
|
||||
RasterPass::InitializeInternal();
|
||||
}
|
||||
}
|
||||
|
||||
bool HairGeometryRasterPass::IsEnabled() const
|
||||
{
|
||||
return (RPI::RasterPass::IsEnabled() && m_initialized) ? true : false;
|
||||
}
|
||||
|
||||
bool HairGeometryRasterPass::LoadShaderAndPipelineState()
|
||||
{
|
||||
RPI::ShaderReloadNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
const RPI::RasterPassData* passData = RPI::PassUtils::GetPassData<RPI::RasterPassData>(m_passDescriptor);
|
||||
|
||||
// If we successfully retrieved our custom data, use it to set the DrawListTag
|
||||
if (!passData)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Missing pass raster data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load Shader
|
||||
const char* shaderFilePath = m_shaderPath.c_str();
|
||||
Data::Asset<RPI::ShaderAsset> shaderAsset =
|
||||
RPI::AssetUtils::LoadAssetByProductPath<RPI::ShaderAsset>(shaderFilePath, RPI::AssetUtils::TraceLevel::Error);
|
||||
|
||||
if (!shaderAsset.GetId().IsValid())
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Invalid shader asset for shader '%s'!", shaderFilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_shader = RPI::Shader::FindOrCreate(shaderAsset);
|
||||
if (m_shader == nullptr)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Pass failed to load shader '%s'!", shaderFilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Per Pass Srg
|
||||
{
|
||||
// Using 'PerPass' naming since currently RasterPass assumes that the pass Srg is always named 'PassSrg'
|
||||
// [To Do] - RasterPass should use srg slot index and not name - currently this will
|
||||
// result in a crash in one of the Atom existing MSAA passes that requires further dive.
|
||||
// m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "HairPerPassSrg", "Hair Gem");
|
||||
m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "PassSrg", "Hair Gem");
|
||||
if (!m_shaderResourceGroup)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Failed to create the per pass srg");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const RPI::ShaderVariant& shaderVariant = m_shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
|
||||
RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
|
||||
shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
|
||||
|
||||
RPI::Scene* scene = GetScene();
|
||||
if (!scene)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Scene could not be acquired" );
|
||||
return false;
|
||||
}
|
||||
RHI::DrawListTag drawListTag = m_shader->GetDrawListTag();
|
||||
scene->ConfigurePipelineState(drawListTag, pipelineStateDescriptor);
|
||||
|
||||
pipelineStateDescriptor.m_renderAttachmentConfiguration = GetRenderAttachmentConfiguration();
|
||||
pipelineStateDescriptor.m_inputStreamLayout.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList);
|
||||
pipelineStateDescriptor.m_inputStreamLayout.Finalize();
|
||||
|
||||
m_pipelineState = m_shader->AcquirePipelineState(pipelineStateDescriptor);
|
||||
if (!m_pipelineState)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Pipeline state could not be acquired");
|
||||
return false;
|
||||
}
|
||||
|
||||
RPI::ShaderReloadNotificationBus::Handler::BusConnect(shaderAsset.GetId());
|
||||
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HairGeometryRasterPass::SchedulePacketBuild(HairRenderObject* hairObject)
|
||||
{
|
||||
m_newRenderObjects.insert(hairObject);
|
||||
}
|
||||
|
||||
bool HairGeometryRasterPass::BuildDrawPacket(HairRenderObject* hairObject)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::DrawPacketBuilder::DrawRequest drawRequest;
|
||||
drawRequest.m_listTag = m_drawListTag;
|
||||
drawRequest.m_pipelineState = m_pipelineState;
|
||||
// drawRequest.m_streamBufferViews = // no explicit vertex buffer. shader is using the srg buffers
|
||||
drawRequest.m_stencilRef = 0;
|
||||
drawRequest.m_sortKey = 0;
|
||||
|
||||
// Seems that the PerView and PerScene are gathered through RenderPass::CollectSrgs()
|
||||
// The PerPass is gathered through the RasterPass::m_shaderResourceGroup
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
|
||||
return hairObject->BuildPPLLDrawPacket(drawRequest);
|
||||
}
|
||||
|
||||
bool HairGeometryRasterPass::AddDrawPackets(AZStd::list<Data::Instance<HairRenderObject>>& hairRenderObjects)
|
||||
{
|
||||
bool overallSuccess = true;
|
||||
|
||||
if (!m_currentView &&
|
||||
(!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag)))
|
||||
{
|
||||
m_currentView = nullptr; // set it to nullptr to prevent further attempts this frame
|
||||
AZ_Warning("Hair Gem", false, "AddDrawPackets: failed to acquire or match the DrawListTag - check that your pass and shader tag name match");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& renderObject : hairRenderObjects)
|
||||
{
|
||||
const RHI::DrawPacket* drawPacket = renderObject->GetFillDrawPacket();
|
||||
if (!drawPacket)
|
||||
{ // might not be an error - the object might have just been added and the DrawPacket is
|
||||
// scheduled to be built when the render frame begins
|
||||
AZ_Warning("Hair Gem", !m_newRenderObjects.empty(), "HairGeometryRasterPass - DrawPacket wasn't built");
|
||||
overallSuccess = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_currentView->AddDrawPacket(drawPacket);
|
||||
}
|
||||
return overallSuccess;
|
||||
}
|
||||
|
||||
void HairGeometryRasterPass::FrameBeginInternal(FramePrepareParams params)
|
||||
{
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
if (!m_initialized && AcquireFeatureProcessor())
|
||||
{
|
||||
LoadShaderAndPipelineState();
|
||||
m_featureProcessor->ForceRebuildRenderData();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind the Per Object resources and trigger the RHI validation that will use attachment
|
||||
// for its validation. The attachments are invalidated outside the render begin/end frame.
|
||||
for (HairRenderObject* newObject : m_newRenderObjects)
|
||||
{
|
||||
newObject->BindPerObjectSrgForRaster();
|
||||
BuildDrawPacket(newObject);
|
||||
}
|
||||
|
||||
// Clear the new added objects - BuildDrawPacket should only be carried out once per
|
||||
// object/shader lifetime
|
||||
m_newRenderObjects.clear();
|
||||
|
||||
// Refresh current view every frame
|
||||
if (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag))
|
||||
{
|
||||
m_currentView = nullptr; // set it to null if view exists but no tag match
|
||||
AZ_Warning("Hair Gem", false, "FrameBeginInternal: failed to acquire or match the DrawListTag - check that your pass and shader tag name match");
|
||||
return;
|
||||
}
|
||||
|
||||
RPI::RasterPass::FrameBeginInternal(params);
|
||||
}
|
||||
|
||||
void HairGeometryRasterPass::CompileResources(const RHI::FrameGraphCompileContext& context)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzRender);
|
||||
|
||||
if (!m_featureProcessor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Compilation of remaining srgs will be done by the parent class
|
||||
RPI::RasterPass::CompileResources(context);
|
||||
}
|
||||
|
||||
void HairGeometryRasterPass::BuildShaderAndRenderData()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
m_initialized = false; // make sure we initialize it even if not in this frame
|
||||
if (AcquireFeatureProcessor())
|
||||
{
|
||||
LoadShaderAndPipelineState();
|
||||
m_featureProcessor->ForceRebuildRenderData();
|
||||
}
|
||||
}
|
||||
|
||||
void HairGeometryRasterPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader & shader)
|
||||
{
|
||||
BuildShaderAndRenderData();
|
||||
}
|
||||
|
||||
void HairGeometryRasterPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset<RPI::ShaderAsset>& shaderAsset)
|
||||
{
|
||||
BuildShaderAndRenderData();
|
||||
}
|
||||
|
||||
void HairGeometryRasterPass::OnShaderVariantReinitialized([[maybe_unused]] const AZ::RPI::ShaderVariant& shaderVariant)
|
||||
{
|
||||
BuildShaderAndRenderData();
|
||||
}
|
||||
} // namespace Hair
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/Size.h>
|
||||
|
||||
#include <Atom/RPI.Public/Pass/RasterPass.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadNotificationBus.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
{
|
||||
struct DrawItem;
|
||||
}
|
||||
|
||||
namespace Render
|
||||
{
|
||||
namespace Hair
|
||||
{
|
||||
class HairRenderObject;
|
||||
class HairFeatureProcessor;
|
||||
|
||||
//! A HairGeometryRasterPass is used for the render of the hair geometries. This is the base
|
||||
//! class that can be inherited - for example by the HiarPPLLRasterPass and have only the specific
|
||||
//! class data handling added on top.
|
||||
class HairGeometryRasterPass
|
||||
: public RPI::RasterPass
|
||||
, private RPI::ShaderReloadNotificationBus::Handler
|
||||
{
|
||||
AZ_RPI_PASS(HairGeometryRasterPass);
|
||||
|
||||
public:
|
||||
AZ_RTTI(HairGeometryRasterPass, "{0F07360A-A286-4060-8C62-137AFFA50561}", RasterPass);
|
||||
AZ_CLASS_ALLOCATOR(HairGeometryRasterPass, SystemAllocator, 0);
|
||||
|
||||
//! Creates a HairGeometryRasterPass
|
||||
static RPI::Ptr<HairGeometryRasterPass> Create(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
bool AddDrawPackets(AZStd::list<Data::Instance<HairRenderObject>>& hairObjects);
|
||||
|
||||
//! The following will be called when an object was added or shader has been compiled
|
||||
void SchedulePacketBuild(HairRenderObject* hairObject);
|
||||
|
||||
Data::Instance<RPI::Shader> GetShader() { return m_shader; }
|
||||
|
||||
void SetFeatureProcessor(HairFeatureProcessor* featureProcessor)
|
||||
{
|
||||
m_featureProcessor = featureProcessor;
|
||||
}
|
||||
|
||||
virtual bool IsEnabled() const override;
|
||||
|
||||
protected:
|
||||
explicit HairGeometryRasterPass(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
// ShaderReloadNotificationBus::Handler overrides...
|
||||
void OnShaderReinitialized(const RPI::Shader& shader) override;
|
||||
void OnShaderAssetReinitialized(const Data::Asset<RPI::ShaderAsset>& shaderAsset) override;
|
||||
void OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant) override;
|
||||
|
||||
void SetShaderPath(const char* shaderPath) { m_shaderPath = shaderPath; }
|
||||
bool LoadShaderAndPipelineState();
|
||||
bool AcquireFeatureProcessor();
|
||||
void BuildShaderAndRenderData();
|
||||
bool BuildDrawPacket(HairRenderObject* hairObject);
|
||||
|
||||
// Pass behavior overrides
|
||||
void InitializeInternal() override;
|
||||
// void BuildInternal() override;
|
||||
void FrameBeginInternal(FramePrepareParams params) override;
|
||||
|
||||
// Scope producer functions...
|
||||
void CompileResources(const RHI::FrameGraphCompileContext& context) override;
|
||||
|
||||
protected:
|
||||
HairFeatureProcessor* m_featureProcessor = nullptr;
|
||||
|
||||
// The shader that will be used by the pass
|
||||
Data::Instance<RPI::Shader> m_shader = nullptr;
|
||||
|
||||
// Override the following in the inherited class
|
||||
AZStd::string m_shaderPath = "dummyShaderPath";
|
||||
|
||||
// To help create the pipeline state
|
||||
RPI::PassDescriptor m_passDescriptor;
|
||||
|
||||
const RHI::PipelineState* m_pipelineState = nullptr;
|
||||
RPI::ViewPtr m_currentView = nullptr;
|
||||
|
||||
AZStd::mutex m_mutex;
|
||||
|
||||
//! List of new render objects introduced this frame so that their in order to identify
|
||||
//! that their PerObject (dynamic) Srg needs binding to the resources.
|
||||
//! Done once per every new object introduced / requires update.
|
||||
AZStd::unordered_set<HairRenderObject*> m_newRenderObjects;
|
||||
|
||||
bool m_initialized = false;
|
||||
};
|
||||
|
||||
} // namespace Hair
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -34,61 +34,25 @@ namespace AZ
|
||||
namespace Hair
|
||||
{
|
||||
|
||||
// --- Creation & Initialization ---
|
||||
HairPPLLRasterPass::HairPPLLRasterPass(const RPI::PassDescriptor& descriptor)
|
||||
: HairGeometryRasterPass(descriptor)
|
||||
{
|
||||
SetShaderPath("Shaders/hairrenderingfillppll.azshader");
|
||||
}
|
||||
|
||||
RPI::Ptr<HairPPLLRasterPass> HairPPLLRasterPass::Create(const RPI::PassDescriptor& descriptor)
|
||||
{
|
||||
RPI::Ptr<HairPPLLRasterPass> pass = aznew HairPPLLRasterPass(descriptor);
|
||||
return pass;
|
||||
}
|
||||
|
||||
HairPPLLRasterPass::HairPPLLRasterPass(const RPI::PassDescriptor& descriptor)
|
||||
: RasterPass(descriptor),
|
||||
m_passDescriptor(descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
HairPPLLRasterPass::~HairPPLLRasterPass()
|
||||
{
|
||||
}
|
||||
|
||||
bool HairPPLLRasterPass::AcquireFeatureProcessor()
|
||||
{
|
||||
if (m_featureProcessor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
RPI::Scene* scene = GetScene();
|
||||
if (scene)
|
||||
{
|
||||
m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_featureProcessor)
|
||||
{
|
||||
AZ_Warning("Hair Gem", false,
|
||||
"HairPPLLRasterPass [%s] - Failed to retrieve Hair feature processor from the scene",
|
||||
GetName().GetCStr());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HairPPLLRasterPass::InitializeInternal()
|
||||
{
|
||||
if (GetScene())
|
||||
{
|
||||
RasterPass::InitializeInternal();
|
||||
}
|
||||
}
|
||||
|
||||
//! This method is used for attaching the PPLL data buffer which is a transient buffer.
|
||||
//! It is done this ways because Atom doesn't support transient structured buffers declaration
|
||||
//! via Pass yet.
|
||||
//! Once supported, this will be done via data driven code and the method can be removed.
|
||||
void HairPPLLRasterPass::BuildInternal()
|
||||
{
|
||||
RasterPass::BuildInternal();
|
||||
RasterPass::BuildInternal(); // change this to call parent if the method exists
|
||||
|
||||
if (!AcquireFeatureProcessor())
|
||||
{
|
||||
@@ -104,217 +68,6 @@ namespace AZ
|
||||
AttachBufferToSlot(Name{ "PerPixelLinkedList" }, m_featureProcessor->GetPerPixelListBuffer());
|
||||
}
|
||||
|
||||
bool HairPPLLRasterPass::IsEnabled() const
|
||||
{
|
||||
return (RPI::RasterPass::IsEnabled() && m_initialized) ? true : false;
|
||||
}
|
||||
|
||||
bool HairPPLLRasterPass::LoadShaderAndPipelineState()
|
||||
{
|
||||
RPI::ShaderReloadNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
const RPI::RasterPassData* passData = RPI::PassUtils::GetPassData<RPI::RasterPassData>(m_passDescriptor);
|
||||
|
||||
// If we successfully retrieved our custom data, use it to set the DrawListTag
|
||||
if (!passData)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Missing pass raster data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load Shader
|
||||
const char* shaderFilePath = "Shaders/hairrenderingfillppll.azshader";
|
||||
Data::Asset<RPI::ShaderAsset> shaderAsset =
|
||||
RPI::AssetUtils::LoadAssetByProductPath<RPI::ShaderAsset>(shaderFilePath, RPI::AssetUtils::TraceLevel::Error);
|
||||
|
||||
if (!shaderAsset.GetId().IsValid())
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Invalid shader asset for shader '%s'!", shaderFilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_shader = RPI::Shader::FindOrCreate(shaderAsset);
|
||||
if (m_shader == nullptr)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Pass failed to load shader '%s'!", shaderFilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Per Pass Srg
|
||||
{
|
||||
// Using 'PerPass' naming since currently RasterPass assumes that the pass Srg is always named 'PassSrg'
|
||||
// [To Do] - RasterPass should use srg slot index and not name - currently this will
|
||||
// result in a crash in one of the Atom existing MSAA passes that requires further dive.
|
||||
// m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "HairPerPassSrg", "Hair Gem");
|
||||
m_shaderResourceGroup = UtilityClass::CreateShaderResourceGroup(m_shader, "PassSrg", "Hair Gem");
|
||||
if (!m_shaderResourceGroup)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Failed to create the per pass srg");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const RPI::ShaderVariant& shaderVariant = m_shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
|
||||
RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
|
||||
shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
|
||||
|
||||
RPI::Scene* scene = GetScene();
|
||||
if (!scene)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Scene could not be acquired" );
|
||||
return false;
|
||||
}
|
||||
RHI::DrawListTag drawListTag = m_shader->GetDrawListTag();
|
||||
scene->ConfigurePipelineState(drawListTag, pipelineStateDescriptor);
|
||||
|
||||
pipelineStateDescriptor.m_renderAttachmentConfiguration = GetRenderAttachmentConfiguration();
|
||||
pipelineStateDescriptor.m_inputStreamLayout.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList);
|
||||
pipelineStateDescriptor.m_inputStreamLayout.Finalize();
|
||||
|
||||
m_pipelineState = m_shader->AcquirePipelineState(pipelineStateDescriptor);
|
||||
if (!m_pipelineState)
|
||||
{
|
||||
AZ_Error("Hair Gem", false, "Pipeline state could not be acquired");
|
||||
return false;
|
||||
}
|
||||
|
||||
RPI::ShaderReloadNotificationBus::Handler::BusConnect(shaderAsset.GetId());
|
||||
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HairPPLLRasterPass::SchedulePacketBuild(HairRenderObject* hairObject)
|
||||
{
|
||||
m_newRenderObjects.insert(hairObject);
|
||||
}
|
||||
|
||||
bool HairPPLLRasterPass::BuildDrawPacket(HairRenderObject* hairObject)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::DrawPacketBuilder::DrawRequest drawRequest;
|
||||
drawRequest.m_listTag = m_drawListTag;
|
||||
drawRequest.m_pipelineState = m_pipelineState;
|
||||
// drawRequest.m_streamBufferViews = // no explicit vertex buffer. shader is using the srg buffers
|
||||
drawRequest.m_stencilRef = 0;
|
||||
drawRequest.m_sortKey = 0;
|
||||
|
||||
// Seems that the PerView and PerScene are gathered through RenderPass::CollectSrgs()
|
||||
// The PerPass is gathered through the RasterPass::m_shaderResourceGroup
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
|
||||
return hairObject->BuildPPLLDrawPacket(drawRequest);
|
||||
}
|
||||
|
||||
bool HairPPLLRasterPass::AddDrawPackets(AZStd::list<Data::Instance<HairRenderObject>>& hairRenderObjects)
|
||||
{
|
||||
bool overallSuccess = true;
|
||||
|
||||
if (!m_currentView &&
|
||||
(!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag)))
|
||||
{
|
||||
m_currentView = nullptr; // set it to nullptr to prevent further attempts this frame
|
||||
AZ_Warning("Hair Gem", false, "HairPPLLRasterPass failed to acquire or match the DrawListTag - check that your pass and shader tag name match");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& renderObject : hairRenderObjects)
|
||||
{
|
||||
const RHI::DrawPacket* drawPacket = renderObject->GetFillDrawPacket();
|
||||
if (!drawPacket)
|
||||
{ // might not be an error - the object might have just been added and the DrawPacket is
|
||||
// scheduled to be built when the render frame begins
|
||||
AZ_Warning("Hair Gem", !m_newRenderObjects.empty(), "HairPPLLRasterPass - DrawPacket wasn't built");
|
||||
overallSuccess = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
m_currentView->AddDrawPacket(drawPacket);
|
||||
}
|
||||
return overallSuccess;
|
||||
}
|
||||
|
||||
void HairPPLLRasterPass::FrameBeginInternal(FramePrepareParams params)
|
||||
{
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
if (!m_initialized && AcquireFeatureProcessor())
|
||||
{
|
||||
LoadShaderAndPipelineState();
|
||||
m_featureProcessor->ForceRebuildRenderData();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind the Per Object resources and trigger the RHI validation that will use attachment
|
||||
// for its validation. The attachments are invalidated outside the render begin/end frame.
|
||||
for (HairRenderObject* newObject : m_newRenderObjects)
|
||||
{
|
||||
newObject->BindPerObjectSrgForRaster();
|
||||
BuildDrawPacket(newObject);
|
||||
}
|
||||
|
||||
// Clear the new added objects - BuildDrawPacket should only be carried out once per
|
||||
// object/shader lifetime
|
||||
m_newRenderObjects.clear();
|
||||
|
||||
// Refresh current view every frame
|
||||
if (!(m_currentView = GetView()) || !m_currentView->HasDrawListTag(m_drawListTag))
|
||||
{
|
||||
m_currentView = nullptr; // set it to null if view exists but no tag match
|
||||
AZ_Warning("Hair Gem", false, "HairPPLLRasterPass failed to acquire or match the DrawListTag - check that your pass and shader tag name match");
|
||||
return;
|
||||
}
|
||||
|
||||
RPI::RasterPass::FrameBeginInternal(params);
|
||||
}
|
||||
|
||||
void HairPPLLRasterPass::CompileResources(const RHI::FrameGraphCompileContext& context)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AzRender);
|
||||
|
||||
if (!m_featureProcessor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Compilation of remaining srgs will be done by the parent class
|
||||
RPI::RasterPass::CompileResources(context);
|
||||
}
|
||||
|
||||
void HairPPLLRasterPass::BuildShaderAndRenderData()
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
m_initialized = false; // make sure we initialize it even if not in this frame
|
||||
if (AcquireFeatureProcessor())
|
||||
{
|
||||
LoadShaderAndPipelineState();
|
||||
m_featureProcessor->ForceRebuildRenderData();
|
||||
}
|
||||
}
|
||||
|
||||
void HairPPLLRasterPass::OnShaderReinitialized([[maybe_unused]] const RPI::Shader & shader)
|
||||
{
|
||||
BuildShaderAndRenderData();
|
||||
}
|
||||
|
||||
void HairPPLLRasterPass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset<RPI::ShaderAsset>& shaderAsset)
|
||||
{
|
||||
BuildShaderAndRenderData();
|
||||
}
|
||||
|
||||
void HairPPLLRasterPass::OnShaderVariantReinitialized([[maybe_unused]] const AZ::RPI::ShaderVariant& shaderVariant)
|
||||
{
|
||||
BuildShaderAndRenderData();
|
||||
}
|
||||
} // namespace Hair
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -7,14 +7,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <Atom/RHI.Reflect/Size.h>
|
||||
|
||||
#include <Atom/RPI.Public/Pass/RasterPass.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderReloadNotificationBus.h>
|
||||
#include <Passes/HairGeometryRasterPass.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -27,11 +20,8 @@ namespace AZ
|
||||
{
|
||||
namespace Hair
|
||||
{
|
||||
class HairRenderObject;
|
||||
class HairFeatureProcessor;
|
||||
|
||||
//! A HairPPLLRasterPass is used for the hair fragments fill render after the data
|
||||
//! went through the skinning and simulation passes.
|
||||
//! went through the skinning and simulation passes.
|
||||
//! The output of this pass is the general list of fragment data that can now be
|
||||
//! traversed for depth resolve and lighting.
|
||||
//! The Fill pass uses the following Srgs:
|
||||
@@ -41,74 +31,22 @@ namespace AZ
|
||||
//! - HairDynamicDataSrg (PerObjectSrg) - shared buffers views for this hair object only.
|
||||
//! - PerViewSrg and PerSceneSrg - as per the data from Atom.
|
||||
class HairPPLLRasterPass
|
||||
: public RPI::RasterPass
|
||||
, private RPI::ShaderReloadNotificationBus::Handler
|
||||
: public HairGeometryRasterPass
|
||||
{
|
||||
AZ_RPI_PASS(HairPPLLRasterPass);
|
||||
|
||||
public:
|
||||
AZ_RTTI(HairPPLLRasterPass, "{6614D7DD-24EE-4A2B-B314-7C035E2FB3C4}", RasterPass);
|
||||
AZ_RTTI(HairPPLLRasterPass, "{6614D7DD-24EE-4A2B-B314-7C035E2FB3C4}", HairGeometryRasterPass);
|
||||
AZ_CLASS_ALLOCATOR(HairPPLLRasterPass, SystemAllocator, 0);
|
||||
virtual ~HairPPLLRasterPass();
|
||||
|
||||
//! Creates a HairPPLLRasterPass
|
||||
static RPI::Ptr<HairPPLLRasterPass> Create(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
bool AddDrawPackets(AZStd::list<Data::Instance<HairRenderObject>>& hairObjects);
|
||||
|
||||
//! The following will be called when an object was added or shader has been compiled
|
||||
void SchedulePacketBuild(HairRenderObject* hairObject);
|
||||
|
||||
Data::Instance<RPI::Shader> GetShader() { return m_shader; }
|
||||
|
||||
void SetFeatureProcessor(HairFeatureProcessor* featureProcessor)
|
||||
{
|
||||
m_featureProcessor = featureProcessor;
|
||||
}
|
||||
|
||||
virtual bool IsEnabled() const override;
|
||||
protected:
|
||||
// ShaderReloadNotificationBus::Handler overrides...
|
||||
void OnShaderReinitialized(const RPI::Shader& shader) override;
|
||||
void OnShaderAssetReinitialized(const Data::Asset<RPI::ShaderAsset>& shaderAsset) override;
|
||||
void OnShaderVariantReinitialized(const AZ::RPI::ShaderVariant& shaderVariant) override;
|
||||
|
||||
private:
|
||||
explicit HairPPLLRasterPass(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
bool LoadShaderAndPipelineState();
|
||||
bool AcquireFeatureProcessor();
|
||||
void BuildShaderAndRenderData();
|
||||
bool BuildDrawPacket(HairRenderObject* hairObject);
|
||||
|
||||
// Pass behavior overrides
|
||||
void InitializeInternal() override;
|
||||
void BuildInternal() override;
|
||||
void FrameBeginInternal(FramePrepareParams params) override;
|
||||
|
||||
// Scope producer functions...
|
||||
void CompileResources(const RHI::FrameGraphCompileContext& context) override;
|
||||
|
||||
private:
|
||||
HairFeatureProcessor* m_featureProcessor = nullptr;
|
||||
|
||||
// The shader that will be used by the pass
|
||||
Data::Instance<RPI::Shader> m_shader = nullptr;
|
||||
|
||||
// To help create the pipeline state
|
||||
RPI::PassDescriptor m_passDescriptor;
|
||||
|
||||
const RHI::PipelineState* m_pipelineState = nullptr;
|
||||
RPI::ViewPtr m_currentView = nullptr;
|
||||
|
||||
AZStd::mutex m_mutex;
|
||||
|
||||
//! List of new render objects introduced this frame so that their
|
||||
//! Per Object (dynamic) Srg should be bound to the resources.
|
||||
//! Done once per every new object introduced / requires update.
|
||||
AZStd::unordered_set<HairRenderObject*> m_newRenderObjects;
|
||||
|
||||
bool m_initialized = false;
|
||||
};
|
||||
|
||||
} // namespace Hair
|
||||
|
||||
@@ -61,10 +61,16 @@ set(FILES
|
||||
#)
|
||||
#
|
||||
#set(atom_hair_passes
|
||||
# The simulation pass class shared by all simulation / skinning compute passes
|
||||
Code/Passes/HairSkinningComputePass.h
|
||||
Code/Passes/HairSkinningComputePass.cpp
|
||||
# Base class of all geometry raster passes
|
||||
Code/Passes/HairGeometryRasterPass.h
|
||||
Code/Passes/HairGeometryRasterPass.cpp
|
||||
# PPLL rendering technique - geometry raster pass
|
||||
Code/Passes/HairPPLLRasterPass.h
|
||||
Code/Passes/HairPPLLRasterPass.cpp
|
||||
# PP full screen resolve pass
|
||||
Code/Passes/HairPPLLResolvePass.h
|
||||
Code/Passes/HairPPLLResolvePass.cpp
|
||||
#)
|
||||
|
||||
Reference in New Issue
Block a user