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:
Adi Bar-Lev
2021-10-13 09:37:47 -04:00
committed by GitHub
parent 5bf7330f35
commit 3af07e5117
5 changed files with 434 additions and 324 deletions
@@ -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
+6
View File
@@ -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
#)