Fix for ATOM-15488 : Rendering out an animation with shadows crashes the editor (#794)
Previously, the SkinnedMeshFeatureProcessor assumed there would only be one skinning pass. However, that's not always the case. When rendering with track view, the feature processor was getting a pass that only updated once every three frames, which could lead to a condition where a skinned mesh was released, but the pass never submitted and cleared the previously added dispatch items, and one or two frames later it would go to submit after the skinned mesh and all of its resources had already been released. -Modified the skinning and morph target compute passes to pull dispatch items from the feature processor instead of the feature processor pushing them to the passes. -If more than one skinning (or morph target) pass is active in the frame, whichever one is first will submit all the dispatch items, and clear the feature processor's dispatch items before the next one tries to submit anything -Moved the logic for caching shader options from the SkinnedMeshComputePass to the SkinnedMeshFeatureProcessor, since there may be more than one pass but only one feature processor per scene
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
|
||||
#include <MorphTargets/MorphTargetComputePass.h>
|
||||
#include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
|
||||
#include <Atom/Feature/SkinnedMesh/SkinnedMeshOutputStreamManagerInterface.h>
|
||||
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
@@ -38,6 +39,11 @@ namespace AZ
|
||||
return m_shader;
|
||||
}
|
||||
|
||||
void MorphTargetComputePass::SetFeatureProcessor(SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor)
|
||||
{
|
||||
m_skinnedMeshFeatureProcessor = skinnedMeshFeatureProcessor;
|
||||
}
|
||||
|
||||
void MorphTargetComputePass::BuildAttachmentsInternal()
|
||||
{
|
||||
// The same buffer that skinning writes to is used to manage the computed vertex deltas that are passed from the
|
||||
@@ -45,30 +51,16 @@ namespace AZ
|
||||
AttachBufferToSlot(Name{ "MorphTargetDeltaOutput" }, SkinnedMeshOutputStreamManagerInterface::Get()->GetBuffer());
|
||||
}
|
||||
|
||||
void MorphTargetComputePass::AddDispatchItem(const RHI::DispatchItem* dispatchItem)
|
||||
{
|
||||
AZ_Assert(dispatchItem != nullptr, "invalid dispatchItem");
|
||||
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
//using an unordered_set here to prevent redundantly adding the same dispatchItem to the submission queue
|
||||
//(i.e. if the same morph target exists in multiple views, it can call AddDispatchItem multiple times with the same item)
|
||||
m_dispatches.insert(dispatchItem);
|
||||
}
|
||||
|
||||
void MorphTargetComputePass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
|
||||
{
|
||||
RHI::CommandList* commandList = context.GetCommandList();
|
||||
|
||||
SetSrgsForDispatch(commandList);
|
||||
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
for (const RHI::DispatchItem* dispatchItem : m_dispatches)
|
||||
if (m_skinnedMeshFeatureProcessor)
|
||||
{
|
||||
commandList->Submit(*dispatchItem);
|
||||
}
|
||||
RHI::CommandList* commandList = context.GetCommandList();
|
||||
|
||||
// Clear the dispatch items. They will need to be re-populated next frame
|
||||
m_dispatches.clear();
|
||||
SetSrgsForDispatch(commandList);
|
||||
|
||||
m_skinnedMeshFeatureProcessor->SubmitMorphTargetDispatchItems(commandList);
|
||||
}
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
class SkinnedMeshFeatureProcessor;
|
||||
|
||||
//! The morph target compute pass submits dispatch items for morph targets. The dispatch items are cleared every frame, so it needs to be re-populated.
|
||||
class MorphTargetComputePass
|
||||
: public RPI::ComputePass
|
||||
@@ -31,16 +33,14 @@ namespace AZ
|
||||
|
||||
static RPI::Ptr<MorphTargetComputePass> Create(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
//! Thread-safe function for adding a dispatch item to the current frame.
|
||||
void AddDispatchItem(const RHI::DispatchItem* dispatchItem);
|
||||
Data::Instance<RPI::Shader> GetShader() const;
|
||||
|
||||
void SetFeatureProcessor(SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor);
|
||||
private:
|
||||
void BuildAttachmentsInternal() override;
|
||||
void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
|
||||
|
||||
AZStd::mutex m_mutex;
|
||||
AZStd::unordered_set<const RHI::DispatchItem*> m_dispatches;
|
||||
SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <MorphTargets/MorphTargetDispatchItem.h>
|
||||
#include <MorphTargets/MorphTargetComputePass.h>
|
||||
#include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
|
||||
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
@@ -30,7 +30,7 @@ namespace AZ
|
||||
MorphTargetDispatchItem::MorphTargetDispatchItem(
|
||||
const AZStd::intrusive_ptr<MorphTargetInputBuffers> inputBuffers,
|
||||
const MorphTargetMetaData& morphTargetMetaData,
|
||||
RPI::Ptr<MorphTargetComputePass> morphTargetComputePass,
|
||||
SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
|
||||
MorphTargetInstanceMetaData morphInstanceMetaData,
|
||||
float morphDeltaIntegerEncoding)
|
||||
: m_inputBuffers(inputBuffers)
|
||||
@@ -38,7 +38,7 @@ namespace AZ
|
||||
, m_morphInstanceMetaData(morphInstanceMetaData)
|
||||
, m_accumulatedDeltaIntegerEncoding(morphDeltaIntegerEncoding)
|
||||
{
|
||||
m_morphTargetShader = morphTargetComputePass->GetShader();
|
||||
m_morphTargetShader = skinnedMeshFeatureProcessor->GetMorphTargetShader();
|
||||
RPI::ShaderReloadNotificationBus::Handler::BusConnect(m_morphTargetShader->GetAssetId());
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace AZ
|
||||
|
||||
namespace Render
|
||||
{
|
||||
class MorphTargetComputePass;
|
||||
class SkinnedMeshFeatureProcessor;
|
||||
|
||||
//! Holds and manages an RHI DispatchItem for a specific morph target, and the resources that are needed to build and maintain it.
|
||||
class MorphTargetDispatchItem
|
||||
@@ -51,7 +51,7 @@ namespace AZ
|
||||
explicit MorphTargetDispatchItem(
|
||||
const AZStd::intrusive_ptr<MorphTargetInputBuffers> inputBuffers,
|
||||
const MorphTargetMetaData& morphTargetMetaData,
|
||||
RPI::Ptr<MorphTargetComputePass> morphTargetComputePass,
|
||||
SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
|
||||
MorphTargetInstanceMetaData morphInstanceMetaData,
|
||||
float accumulatedDeltaRange
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
|
||||
#include <SkinnedMesh/SkinnedMeshComputePass.h>
|
||||
#include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
|
||||
#include <Atom/Feature/SkinnedMesh/SkinnedMeshOutputStreamManagerInterface.h>
|
||||
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
@@ -22,11 +23,9 @@ namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
|
||||
SkinnedMeshComputePass::SkinnedMeshComputePass(const RPI::PassDescriptor& descriptor)
|
||||
: RPI::ComputePass(descriptor)
|
||||
{
|
||||
m_cachedShaderOptions.SetShader(m_shader);
|
||||
}
|
||||
|
||||
RPI::Ptr<SkinnedMeshComputePass> SkinnedMeshComputePass::Create(const RPI::PassDescriptor& descriptor)
|
||||
@@ -40,42 +39,30 @@ namespace AZ
|
||||
return m_shader;
|
||||
}
|
||||
|
||||
RPI::ShaderOptionGroup SkinnedMeshComputePass::CreateShaderOptionGroup(const SkinnedMeshShaderOptions shaderOptions, SkinnedMeshShaderOptionNotificationBus::Handler& shaderReinitializedHandler)
|
||||
void SkinnedMeshComputePass::SetFeatureProcessor(SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor)
|
||||
{
|
||||
m_cachedShaderOptions.ConnectToShaderReinitializedEvent(shaderReinitializedHandler);
|
||||
return m_cachedShaderOptions.CreateShaderOptionGroup(shaderOptions);
|
||||
}
|
||||
|
||||
void SkinnedMeshComputePass::AddDispatchItem(const RHI::DispatchItem* dispatchItem)
|
||||
{
|
||||
AZ_Assert(dispatchItem != nullptr, "invalid dispatchItem");
|
||||
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
//using an unordered_set here to prevent redundantly adding the same dispatchItem to the submission queue
|
||||
//(i.e. if the same skinnedMesh exists in multiple views, it can call AddDispatchItem multiple times with the same item)
|
||||
m_dispatches.insert(dispatchItem);
|
||||
m_skinnedMeshFeatureProcessor = skinnedMeshFeatureProcessor;
|
||||
}
|
||||
|
||||
void SkinnedMeshComputePass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
|
||||
{
|
||||
RHI::CommandList* commandList = context.GetCommandList();
|
||||
|
||||
SetSrgsForDispatch(commandList);
|
||||
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
|
||||
for (const RHI::DispatchItem* dispatchItem : m_dispatches)
|
||||
if (m_skinnedMeshFeatureProcessor)
|
||||
{
|
||||
commandList->Submit(*dispatchItem);
|
||||
}
|
||||
RHI::CommandList* commandList = context.GetCommandList();
|
||||
|
||||
// Clear the dispatch items. They will need to be re-populated next frame
|
||||
m_dispatches.clear();
|
||||
SetSrgsForDispatch(commandList);
|
||||
|
||||
m_skinnedMeshFeatureProcessor->SubmitSkinningDispatchItems(commandList);
|
||||
}
|
||||
}
|
||||
|
||||
void SkinnedMeshComputePass::OnShaderReinitialized(const RPI::Shader& shader)
|
||||
{
|
||||
ComputePass::OnShaderReinitialized(shader);
|
||||
m_cachedShaderOptions.SetShader(m_shader);
|
||||
if (m_skinnedMeshFeatureProcessor)
|
||||
{
|
||||
m_skinnedMeshFeatureProcessor->OnSkinningShaderReinitialized(m_shader);
|
||||
}
|
||||
}
|
||||
|
||||
void SkinnedMeshComputePass::OnShaderVariantReinitialized(const RPI::Shader& shader, const RPI::ShaderVariantId&, RPI::ShaderVariantStableId)
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
class SkinnedMeshFeatureProcessor;
|
||||
|
||||
//! The skinned mesh compute pass submits dispatch items for skinning. The dispatch items are cleared every frame, so it needs to be re-populated.
|
||||
class SkinnedMeshComputePass
|
||||
: public RPI::ComputePass
|
||||
@@ -33,10 +35,9 @@ namespace AZ
|
||||
|
||||
static RPI::Ptr<SkinnedMeshComputePass> Create(const RPI::PassDescriptor& descriptor);
|
||||
|
||||
//! Thread-safe function for adding a dispatch item to the current frame.
|
||||
void AddDispatchItem(const RHI::DispatchItem* dispatchItem);
|
||||
Data::Instance<RPI::Shader> GetShader() const;
|
||||
RPI::ShaderOptionGroup CreateShaderOptionGroup(const SkinnedMeshShaderOptions shaderOptions, SkinnedMeshShaderOptionNotificationBus::Handler& shaderReinitializedHandler);
|
||||
|
||||
void SetFeatureProcessor(SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor);
|
||||
|
||||
private:
|
||||
void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
|
||||
@@ -45,9 +46,7 @@ namespace AZ
|
||||
void OnShaderReinitialized(const RPI::Shader& shader) override;
|
||||
void OnShaderVariantReinitialized(const RPI::Shader& shader, const RPI::ShaderVariantId& shaderVariantId, RPI::ShaderVariantStableId shaderVariantStableId) override;
|
||||
|
||||
AZStd::mutex m_mutex;
|
||||
AZStd::unordered_set<const RHI::DispatchItem*> m_dispatches;
|
||||
CachedSkinnedMeshShaderOptions m_cachedShaderOptions;
|
||||
SkinnedMeshFeatureProcessor* m_skinnedMeshFeatureProcessor = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <SkinnedMesh/SkinnedMeshDispatchItem.h>
|
||||
#include <SkinnedMesh/SkinnedMeshOutputStreamManager.h>
|
||||
#include <SkinnedMesh/SkinnedMeshComputePass.h>
|
||||
#include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
|
||||
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
@@ -34,7 +34,7 @@ namespace AZ
|
||||
size_t lodIndex,
|
||||
Data::Instance<RPI::Buffer> boneTransforms,
|
||||
const SkinnedMeshShaderOptions& shaderOptions,
|
||||
RPI::Ptr<SkinnedMeshComputePass> skinnedMeshComputePass,
|
||||
SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
|
||||
MorphTargetInstanceMetaData morphTargetInstanceMetaData,
|
||||
float morphTargetDeltaIntegerEncoding)
|
||||
: m_inputBuffers(inputBuffers)
|
||||
@@ -45,7 +45,7 @@ namespace AZ
|
||||
, m_morphTargetInstanceMetaData(morphTargetInstanceMetaData)
|
||||
, m_morphTargetDeltaIntegerEncoding(morphTargetDeltaIntegerEncoding)
|
||||
{
|
||||
m_skinningShader = skinnedMeshComputePass->GetShader();
|
||||
m_skinningShader = skinnedMeshFeatureProcessor->GetSkinningShader();
|
||||
|
||||
// Shader options are generally set per-skinned mesh instance, but morph targets may only exist on some lods. Override the option for applying morph targets here
|
||||
if (m_morphTargetInstanceMetaData.m_accumulatedPositionDeltaOffsetInBytes != MorphTargetConstants::s_invalidDeltaOffset)
|
||||
@@ -58,7 +58,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
// CreateShaderOptionGroup will also connect to the SkinnedMeshShaderOptionNotificationBus
|
||||
m_shaderOptionGroup = skinnedMeshComputePass->CreateShaderOptionGroup(m_shaderOptions, *this);
|
||||
m_shaderOptionGroup = skinnedMeshFeatureProcessor->CreateSkinningShaderOptionGroup(m_shaderOptions, *this);
|
||||
}
|
||||
|
||||
SkinnedMeshDispatchItem::~SkinnedMeshDispatchItem()
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace AZ
|
||||
|
||||
namespace Render
|
||||
{
|
||||
class SkinnedMeshComputePass;
|
||||
class SkinnedMeshFeatureProcessor;
|
||||
|
||||
//! Holds and manages an RHI DispatchItem for a specific skinned mesh, and the resources that are needed to build and maintain it.
|
||||
class SkinnedMeshDispatchItem
|
||||
@@ -55,7 +55,7 @@ namespace AZ
|
||||
size_t lodIndex,
|
||||
Data::Instance<RPI::Buffer> skinningMatrices,
|
||||
const SkinnedMeshShaderOptions& shaderOptions,
|
||||
RPI::Ptr<SkinnedMeshComputePass> skinnedMeshComputePass,
|
||||
SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
|
||||
MorphTargetInstanceMetaData morphTargetInstanceMetaData,
|
||||
float morphTargetDeltaIntegerEncoding
|
||||
);
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
#include <Atom/RPI.Public/RPIUtils.h>
|
||||
#include <Atom/RPI.Public/Shader/Shader.h>
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
|
||||
#include <Atom/RHI/CpuProfiler.h>
|
||||
#include <Atom/RHI/CommandList.h>
|
||||
|
||||
#include <AzCore/Debug/EventTrace.h>
|
||||
#include <AzCore/Jobs/JobCompletion.h>
|
||||
@@ -84,11 +86,6 @@ namespace AZ
|
||||
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
|
||||
AZ_ATOM_PROFILE_FUNCTION("SkinnedMesh", "SkinnedMeshFeatureProcessor: Render");
|
||||
|
||||
if (!m_skinningPass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0 //[GFX_TODO][ATOM-13564] Temporarily disable skinning culling until we figure out how to hook up visibility & lod selection with skinning:
|
||||
//Setup the culling workgroup (it will be re-used for each view)
|
||||
{
|
||||
@@ -132,7 +129,7 @@ namespace AZ
|
||||
//Dispatch the workgroup to each view
|
||||
for (const RPI::ViewPtr& viewPtr : packet.m_views)
|
||||
{
|
||||
Job *processWorkgroupJob = AZ::CreateJobFunction(
|
||||
Job* processWorkgroupJob = AZ::CreateJobFunction(
|
||||
[this, cullingSystem, viewPtr](AZ::Job& thisJob)
|
||||
{
|
||||
AZ_PROFILE_SCOPE_DYNAMIC(Debug::ProfileCategory::AzRender, "skinningMeshFP processWorkgroupJob - View: %s", viewPtr->GetName().GetCStr());
|
||||
@@ -167,7 +164,16 @@ namespace AZ
|
||||
float maxScreenPercentage(lod.m_range.m_max);
|
||||
if (approxScreenPercentage >= minScreenPercentage && approxScreenPercentage <= maxScreenPercentage)
|
||||
{
|
||||
m_skinningPass->AddDispatchItem(&renderProxy->m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
|
||||
AZStd::lock_guard lock(m_dispatchItemMutex);
|
||||
m_skinningDispatches.insert(&renderProxy->m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
|
||||
for (size_t morphTargetIndex = 0; morphTargetIndex < renderProxy->m_morphTargetDispatchItemsByLod[lodIndex].size(); morphTargetIndex++)
|
||||
{
|
||||
const MorphTargetDispatchItem* dispatchItem = renderProxy->m_morphTargetDispatchItemsByLod[lodIndex][morphTargetIndex].get();
|
||||
if (dispatchItem && dispatchItem->GetWeight() > AZ::Constants::FloatEpsilon)
|
||||
{
|
||||
m_morphTargetDispatches.insert(&dispatchItem->GetRHIDispatchItem());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,13 +238,14 @@ namespace AZ
|
||||
//Note that this supports overlapping lod ranges (to support cross-fading lods, for example)
|
||||
if (approxScreenPercentage >= lod.m_screenCoverageMin && approxScreenPercentage <= lod.m_screenCoverageMax)
|
||||
{
|
||||
m_skinningPass->AddDispatchItem(&renderProxy.m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
|
||||
AZStd::lock_guard lock(m_dispatchItemMutex);
|
||||
m_skinningDispatches.insert(&renderProxy.m_dispatchItemsByLod[lodIndex]->GetRHIDispatchItem());
|
||||
for (size_t morphTargetIndex = 0; morphTargetIndex < renderProxy.m_morphTargetDispatchItemsByLod[lodIndex].size(); morphTargetIndex++)
|
||||
{
|
||||
const MorphTargetDispatchItem* dispatchItem = renderProxy.m_morphTargetDispatchItemsByLod[lodIndex][morphTargetIndex].get();
|
||||
if (dispatchItem && dispatchItem->GetWeight() > AZ::Constants::FloatEpsilon)
|
||||
{
|
||||
m_morphTargetPass->AddDispatchItem(&dispatchItem->GetRHIDispatchItem());
|
||||
m_morphTargetDispatches.insert(&dispatchItem->GetRHIDispatchItem());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,19 +255,14 @@ namespace AZ
|
||||
#endif
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] RPI::RenderPipelinePtr pipeline)
|
||||
void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline)
|
||||
{
|
||||
InitSkinningAndMorphPass();
|
||||
InitSkinningAndMorphPass(pipeline->GetRootPass());
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* pipeline)
|
||||
void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline)
|
||||
{
|
||||
InitSkinningAndMorphPass();
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] RPI::RenderPipeline* renderPipeline)
|
||||
{
|
||||
InitSkinningAndMorphPass();
|
||||
InitSkinningAndMorphPass(renderPipeline->GetRootPass());
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::OnBeginPrepareRender()
|
||||
@@ -268,9 +270,15 @@ namespace AZ
|
||||
m_renderProxiesChecker.soft_lock();
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::OnEndPrepareRender()
|
||||
void SkinnedMeshFeatureProcessor::OnRenderEnd()
|
||||
{
|
||||
m_renderProxiesChecker.soft_unlock();
|
||||
|
||||
// Clear any dispatch items that were added but never submitted
|
||||
// in case there were no passes that submitted this frame
|
||||
// because they execute at a lower frequency
|
||||
m_skinningDispatches.clear();
|
||||
m_morphTargetDispatches.clear();
|
||||
}
|
||||
|
||||
SkinnedMeshRenderProxyHandle SkinnedMeshFeatureProcessor::AcquireRenderProxy(const SkinnedMeshRenderProxyDesc& desc)
|
||||
@@ -295,61 +303,73 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass()
|
||||
void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(const RPI::Ptr<RPI::ParentPass> pipelineRootPass)
|
||||
{
|
||||
m_skinningPass = nullptr; //reset it to null, just in case it fails to load the assets properly
|
||||
m_morphTargetPass = nullptr;
|
||||
|
||||
RPI::PassSystemInterface* passSystem = RPI::PassSystemInterface::Get();
|
||||
if (passSystem->HasPassesForTemplateName(AZ::Name{ "SkinningPassTemplate" }))
|
||||
RPI::Ptr<RPI::Pass> skinningPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "SkinningPass" });
|
||||
if (skinningPass)
|
||||
{
|
||||
auto& skinningPasses = passSystem->GetPassesForTemplateName(AZ::Name{ "SkinningPassTemplate" });
|
||||
SkinnedMeshComputePass* skinnedMeshComputePass = azdynamic_cast<SkinnedMeshComputePass*>(skinningPass.get());
|
||||
skinnedMeshComputePass->SetFeatureProcessor(this);
|
||||
|
||||
// For now, assume one skinning pass
|
||||
if (!skinningPasses.empty() && skinningPasses[0])
|
||||
// There may be multiple skinning passes in the scene due to multiple pipelines, but there is only one skinning shader
|
||||
m_skinningShader = skinnedMeshComputePass->GetShader();
|
||||
|
||||
if (!m_skinningShader)
|
||||
{
|
||||
m_skinningPass = static_cast<SkinnedMeshComputePass*>(skinningPasses[0]);
|
||||
const Data::Instance<RPI::Shader> shader = m_skinningPass->GetShader();
|
||||
|
||||
if (!shader)
|
||||
{
|
||||
AZ_Error(s_featureProcessorName, false, "Failed to get skinning pass shader. It may need to finish processing.");
|
||||
}
|
||||
AZ_Error(s_featureProcessorName, false, "Failed to get skinning pass shader. It may need to finish processing.");
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(s_featureProcessorName, false, "\"SkinningPassTemplate\" does not have any valid passes. Check your game project's .pass assets.");
|
||||
m_cachedSkinningShaderOptions.SetShader(m_skinningShader);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(s_featureProcessorName, false, "Failed to find passes for \"SkinningPassTemplate\". Check your game project's .pass assets.");
|
||||
}
|
||||
|
||||
if (passSystem->HasPassesForTemplateName(AZ::Name{ "MorphTargetPassTemplate" }))
|
||||
RPI::Ptr<RPI::Pass> morphTargetPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "MorphTargetPass" });
|
||||
if (morphTargetPass)
|
||||
{
|
||||
auto& morphTargetPasses = passSystem->GetPassesForTemplateName(AZ::Name{ "MorphTargetPassTemplate" });
|
||||
MorphTargetComputePass* morphTargetComputePass = azdynamic_cast<MorphTargetComputePass*>(morphTargetPass.get());
|
||||
morphTargetComputePass->SetFeatureProcessor(this);
|
||||
|
||||
// For now, assume one skinning pass
|
||||
if (!morphTargetPasses.empty() && morphTargetPasses[0])
|
||||
// There may be multiple morph target passes in the scene due to multiple pipelines, but there is only one morph target shader
|
||||
m_morphTargetShader = morphTargetComputePass->GetShader();
|
||||
|
||||
if (!m_morphTargetShader)
|
||||
{
|
||||
m_morphTargetPass = static_cast<MorphTargetComputePass*>(morphTargetPasses[0]);
|
||||
const Data::Instance<RPI::Shader> shader = m_morphTargetPass->GetShader();
|
||||
AZ_Error(s_featureProcessorName, false, "Failed to get morph target pass shader. It may need to finish processing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!shader)
|
||||
{
|
||||
AZ_Error(s_featureProcessorName, false, "Failed to get morph target pass shader. It may need to finish processing.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(s_featureProcessorName, false, "\"MorphTargetPassTemplate\" does not have any valid passes. Check your game project's .pass assets.");
|
||||
}
|
||||
}
|
||||
else
|
||||
RPI::ShaderOptionGroup SkinnedMeshFeatureProcessor::CreateSkinningShaderOptionGroup(const SkinnedMeshShaderOptions shaderOptions, SkinnedMeshShaderOptionNotificationBus::Handler& shaderReinitializedHandler)
|
||||
{
|
||||
m_cachedSkinningShaderOptions.ConnectToShaderReinitializedEvent(shaderReinitializedHandler);
|
||||
return m_cachedSkinningShaderOptions.CreateShaderOptionGroup(shaderOptions);
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::OnSkinningShaderReinitialized(const Data::Instance<RPI::Shader> skinningShader)
|
||||
{
|
||||
m_skinningShader = skinningShader;
|
||||
m_cachedSkinningShaderOptions.SetShader(m_skinningShader);
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::SubmitSkinningDispatchItems(RHI::CommandList* commandList)
|
||||
{
|
||||
AZStd::lock_guard lock(m_dispatchItemMutex);
|
||||
for (const RHI::DispatchItem* dispatchItem : m_skinningDispatches)
|
||||
{
|
||||
AZ_Error(s_featureProcessorName, false, "Failed to find passes for \"MorphTargetPassTemplate\". Check your game project's .pass assets.");
|
||||
commandList->Submit(*dispatchItem);
|
||||
}
|
||||
m_skinningDispatches.clear();
|
||||
}
|
||||
|
||||
void SkinnedMeshFeatureProcessor::SubmitMorphTargetDispatchItems(RHI::CommandList* commandList)
|
||||
{
|
||||
AZStd::lock_guard lock(m_dispatchItemMutex);
|
||||
for (const RHI::DispatchItem* dispatchItem : m_morphTargetDispatches)
|
||||
{
|
||||
commandList->Submit(*dispatchItem);
|
||||
}
|
||||
m_morphTargetDispatches.clear();
|
||||
}
|
||||
|
||||
SkinnedMeshRenderProxyInterfaceHandle SkinnedMeshFeatureProcessor::AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc)
|
||||
@@ -363,14 +383,14 @@ namespace AZ
|
||||
return ReleaseRenderProxy(handle);
|
||||
}
|
||||
|
||||
RPI::Ptr<SkinnedMeshComputePass> SkinnedMeshFeatureProcessor::GetSkinningPass() const
|
||||
Data::Instance<RPI::Shader> SkinnedMeshFeatureProcessor::GetSkinningShader() const
|
||||
{
|
||||
return m_skinningPass;
|
||||
return m_skinningShader;
|
||||
}
|
||||
|
||||
RPI::Ptr<MorphTargetComputePass> SkinnedMeshFeatureProcessor::GetMorphTargetPass() const
|
||||
Data::Instance<RPI::Shader> SkinnedMeshFeatureProcessor::GetMorphTargetShader() const
|
||||
{
|
||||
return m_morphTargetPass;
|
||||
return m_morphTargetShader;
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -51,35 +51,46 @@ namespace AZ
|
||||
void Deactivate() override;
|
||||
void Simulate(const FeatureProcessor::SimulatePacket& packet) override;
|
||||
void Render(const FeatureProcessor::RenderPacket& packet) override;
|
||||
void OnRenderEnd() override;
|
||||
|
||||
// RPI::SceneNotificationBus overrides ...
|
||||
void OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline) override;
|
||||
void OnRenderPipelineRemoved(RPI::RenderPipeline* pipeline) override;
|
||||
void OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline) override;
|
||||
void OnBeginPrepareRender() override;
|
||||
void OnEndPrepareRender() override;
|
||||
|
||||
SkinnedMeshRenderProxyHandle AcquireRenderProxy(const SkinnedMeshRenderProxyDesc& desc);
|
||||
bool ReleaseRenderProxy(SkinnedMeshRenderProxyHandle& handle);
|
||||
|
||||
RPI::Ptr<SkinnedMeshComputePass> GetSkinningPass() const;
|
||||
RPI::Ptr<MorphTargetComputePass> GetMorphTargetPass() const;
|
||||
Data::Instance<RPI::Shader> GetSkinningShader() const;
|
||||
RPI::ShaderOptionGroup CreateSkinningShaderOptionGroup(const SkinnedMeshShaderOptions shaderOptions, SkinnedMeshShaderOptionNotificationBus::Handler& shaderReinitializedHandler);
|
||||
void OnSkinningShaderReinitialized(const Data::Instance<RPI::Shader> skinningShader);
|
||||
void SubmitSkinningDispatchItems(RHI::CommandList* commandList);
|
||||
|
||||
Data::Instance<RPI::Shader> GetMorphTargetShader() const;
|
||||
void SubmitMorphTargetDispatchItems(RHI::CommandList* commandList);
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(SkinnedMeshFeatureProcessor);
|
||||
|
||||
void InitSkinningAndMorphPass();
|
||||
void InitSkinningAndMorphPass(const RPI::Ptr<RPI::ParentPass> pipelineRootPass);
|
||||
|
||||
SkinnedMeshRenderProxyInterfaceHandle AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc) override;
|
||||
bool ReleaseRenderProxyInterface(SkinnedMeshRenderProxyInterfaceHandle& handle) override;
|
||||
|
||||
static const char* s_featureProcessorName;
|
||||
RPI::Ptr<SkinnedMeshComputePass> m_skinningPass;
|
||||
RPI::Ptr<MorphTargetComputePass> m_morphTargetPass;
|
||||
|
||||
Data::Instance<RPI::Shader> m_skinningShader;
|
||||
CachedSkinnedMeshShaderOptions m_cachedSkinningShaderOptions;
|
||||
|
||||
Data::Instance<RPI::Shader> m_morphTargetShader;
|
||||
|
||||
AZStd::concurrency_checker m_renderProxiesChecker;
|
||||
StableDynamicArray<SkinnedMeshRenderProxy> m_renderProxies;
|
||||
AZStd::unique_ptr<SkinnedMeshStatsCollector> m_statsCollector;
|
||||
|
||||
MeshFeatureProcessor* m_meshFeatureProcessor = nullptr;
|
||||
AZStd::unordered_set<const RHI::DispatchItem*> m_skinningDispatches;
|
||||
AZStd::unordered_set<const RHI::DispatchItem*> m_morphTargetDispatches;
|
||||
AZStd::mutex m_dispatchItemMutex;
|
||||
|
||||
};
|
||||
} // namespace Render
|
||||
|
||||
@@ -60,13 +60,7 @@ namespace AZ
|
||||
|
||||
bool SkinnedMeshRenderProxy::BuildDispatchItem([[maybe_unused]] const RPI::Scene& scene, size_t modelLodIndex, [[maybe_unused]] const SkinnedMeshShaderOptions& shaderOptions)
|
||||
{
|
||||
if (!m_featureProcessor->GetSkinningPass())
|
||||
{
|
||||
AZ_Error("Skinned Mesh Feature Processor", false, "Failed to get Skinning Pass. Make sure the project has a skinning pass.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Data::Instance<RPI::Shader> skinningShader = m_featureProcessor->GetSkinningPass()->GetShader();
|
||||
Data::Instance<RPI::Shader> skinningShader = m_featureProcessor->GetSkinningShader();
|
||||
if (!skinningShader)
|
||||
{
|
||||
AZ_Error("Skinned Mesh Feature Processor", false, "Failed to get skinning shader from skinning pass");
|
||||
@@ -89,7 +83,7 @@ namespace AZ
|
||||
m_instance->m_outputStreamOffsetsInBytes[modelLodIndex],
|
||||
modelLodIndex, m_boneTransforms,
|
||||
m_shaderOptions,
|
||||
m_featureProcessor->GetSkinningPass(),
|
||||
m_featureProcessor,
|
||||
m_instance->m_morphTargetInstanceMetaData[modelLodIndex],
|
||||
morphDeltaIntegerEncoding });
|
||||
|
||||
@@ -100,7 +94,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
// Get the data needed to create a morph target dispatch item
|
||||
Data::Instance<RPI::Shader> morphTargetShader = m_featureProcessor->GetMorphTargetPass()->GetShader();
|
||||
Data::Instance<RPI::Shader> morphTargetShader = m_featureProcessor->GetMorphTargetShader();
|
||||
const AZStd::vector<AZStd::intrusive_ptr<MorphTargetInputBuffers>>& morphTargetInputBuffersVector = m_inputBuffers->GetMorphTargetInputBuffers(modelLodIndex);
|
||||
AZ_Assert(morphTargetMetaDatas.size() == morphTargetInputBuffersVector.size(), "Skinned Mesh Feature Processor - Mismatch in morph target metadata count and morph target input buffer count");
|
||||
|
||||
@@ -118,7 +112,7 @@ namespace AZ
|
||||
aznew MorphTargetDispatchItem{
|
||||
morphTargetInputBuffersVector[morphTargetIndex],
|
||||
morphTargetMetaDatas[morphTargetIndex],
|
||||
m_featureProcessor->GetMorphTargetPass(),
|
||||
m_featureProcessor,
|
||||
m_instance->m_morphTargetInstanceMetaData[modelLodIndex],
|
||||
morphDeltaIntegerEncoding });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user