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.
311 lines
13 KiB
C++
311 lines
13 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/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;
|
|
}
|
|
|
|
Data::Instance<RPI::Shader> HairGeometryRasterPass::GetShader()
|
|
{
|
|
if (!m_initialized || !m_shader)
|
|
{
|
|
AZ_Error("Hair Gem", LoadShaderAndPipelineState(), "HairGeometryRasterPass could not initialize pipeline or shader");
|
|
}
|
|
return m_shader;
|
|
}
|
|
|
|
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->BuildDrawPacket(m_shader.get(), 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->GetGeometrylDrawPacket(m_shader.get());
|
|
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
|