LYN-2227 : Improve mesh initialization performance by not creating draw packet more than once (#1476)

* Move SetUseForwardPassIblSpecular to happen when acquiring a mesh, instead of immediately after so that we don't build the drawpacket twice for every mesh

* Update the MeshFeatureProcessor to use the booleans from the descriptor directly instead of having loose booleans in the MeshDataInstance

* m_excludeFromReflectionCubeMaps is not (and does not need to be) part of the descriptor, since setting is cheap and doesn't duplicate work that is done when acquiring the mesh
This commit is contained in:
Tommy Walton
2021-06-22 12:59:27 -07:00
committed by GitHub
parent 6fe448d991
commit be0fbaaddc
12 changed files with 70 additions and 70 deletions
@@ -82,11 +82,11 @@ namespace AZ
RPI::Cullable m_cullable;
MaterialAssignmentMap m_materialAssignments;
MeshHandleDescriptor m_descriptor;
Data::Instance<RPI::Model> m_model;
//! A reference to the original model asset in case it got cloned before creating the model instance.
Data::Asset<RPI::ModelAsset> m_originalModelAsset;
MeshFeatureProcessorInterface::RequiresCloneCallback m_requiresCloningCallback;
Data::Instance<RPI::ShaderResourceGroup> m_shaderResourceGroup;
AZStd::unique_ptr<MeshLoader> m_meshLoader;
@@ -99,10 +99,7 @@ namespace AZ
bool m_cullableNeedsRebuild = false;
bool m_objectSrgNeedsUpdate = true;
bool m_excludeFromReflectionCubeMaps = false;
bool m_skinnedMeshWithMotion = false;
bool m_rayTracingEnabled = true;
bool m_visible = true;
bool m_useForwardPassIblSpecular = false;
bool m_hasForwardPassIblSpecularMaterial = false;
};
@@ -132,17 +129,11 @@ namespace AZ
void OnEndPrepareRender() override;
MeshHandle AcquireMesh(
const Data::Asset<RPI::ModelAsset>& modelAsset,
const MaterialAssignmentMap& materials = {},
bool skinnedMeshWithMotion = false,
bool rayTracingEnabled = true,
RequiresCloneCallback requiresCloneCallback = {}) override;
const MeshHandleDescriptor& descriptor,
const MaterialAssignmentMap& materials = {}) override;
MeshHandle AcquireMesh(
const Data::Asset<RPI::ModelAsset> &modelAsset,
const Data::Instance<RPI::Material>& material,
bool skinnedMeshWithMotion = false,
bool rayTracingEnabled = true,
RequiresCloneCallback requiresCloneCallback = {}) override;
const MeshHandleDescriptor& descriptor,
const Data::Instance<RPI::Material>& material) override;
bool ReleaseMesh(MeshHandle& meshHandle) override;
MeshHandle CloneMesh(const MeshHandle& meshHandle) override;
@@ -26,6 +26,18 @@ namespace AZ
{
class MeshDataInstance;
//! Settings to apply to a mesh handle when acquiring it for the first time
struct MeshHandleDescriptor
{
using RequiresCloneCallback = AZStd::function<bool(const Data::Asset<RPI::ModelAsset>& modelAsset)>;
Data::Asset<RPI::ModelAsset> m_modelAsset;
bool m_isSkinnedMeshWithMotion = false;
bool m_isRayTracingEnabled = true;
bool m_useForwardPassIblSpecular = false;
RequiresCloneCallback m_requiresCloneCallback = {};
};
//! MeshFeatureProcessorInterface provides an interface to acquire and release a MeshHandle from the underlying MeshFeatureProcessor
class MeshFeatureProcessorInterface
: public RPI::FeatureProcessor
@@ -35,23 +47,16 @@ namespace AZ
using MeshHandle = StableDynamicArrayHandle<MeshDataInstance>;
using ModelChangedEvent = Event<const Data::Instance<RPI::Model>>;
using RequiresCloneCallback = AZStd::function<bool(const Data::Asset<RPI::ModelAsset>& modelAsset)>;
//! Acquires a model with an optional collection of material assignments.
//! @param requiresCloneCallback The callback indicates whether cloning is required for a given model asset.
virtual MeshHandle AcquireMesh(
const Data::Asset<RPI::ModelAsset>& modelAsset,
const MaterialAssignmentMap& materials = {},
bool skinnedMeshWithMotion = false,
bool rayTracingEnabled = true,
RequiresCloneCallback requiresCloneCallback = {}) = 0;
const MeshHandleDescriptor& descriptor,
const MaterialAssignmentMap& materials = {}) = 0;
//! Acquires a model with a single material applied to all its meshes.
virtual MeshHandle AcquireMesh(
const Data::Asset<RPI::ModelAsset>& modelAsset,
const Data::Instance<RPI::Material>& material,
bool skinnedMeshWithMotion = false,
bool rayTracingEnabled = true,
RequiresCloneCallback requiresCloneCallback = {}) = 0;
const MeshHandleDescriptor& descriptor,
const Data::Instance<RPI::Material>& material) = 0;
//! Releases the mesh handle
virtual bool ReleaseMesh(MeshHandle& meshHandle) = 0;
//! Creates a new instance and handle of a mesh using an existing MeshId. Currently, this will reset the new mesh to default materials.
@@ -37,8 +37,8 @@ namespace UnitTest
MOCK_METHOD1(GetSortKey, AZ::RHI::DrawItemSortKey(const MeshHandle&));
MOCK_METHOD2(SetLodOverride, void(const MeshHandle&, AZ::RPI::Cullable::LodOverride));
MOCK_METHOD1(GetLodOverride, AZ::RPI::Cullable::LodOverride(const MeshHandle&));
MOCK_METHOD5(AcquireMesh, MeshHandle (const AZ::Data::Asset<AZ::RPI::ModelAsset>&, const AZ::Render::MaterialAssignmentMap&, bool, bool, AZ::Render::MeshFeatureProcessorInterface::RequiresCloneCallback));
MOCK_METHOD5(AcquireMesh, MeshHandle (const AZ::Data::Asset<AZ::RPI::ModelAsset>&, const AZ::Data::Instance<AZ::RPI::Material>&, bool, bool, AZ::Render::MeshFeatureProcessorInterface::RequiresCloneCallback));
MOCK_METHOD2(AcquireMesh, MeshHandle (const AZ::Render::MeshHandleDescriptor&, const AZ::Render::MaterialAssignmentMap&));
MOCK_METHOD2(AcquireMesh, MeshHandle (const AZ::Render::MeshHandleDescriptor&, const AZ::Data::Instance<AZ::RPI::Material>&));
MOCK_METHOD2(SetRayTracingEnabled, void (const MeshHandle&, bool));
MOCK_METHOD2(SetVisible, void (const MeshHandle&, bool));
MOCK_METHOD2(SetUseForwardPassIblSpecular, void (const MeshHandle&, bool));
@@ -149,46 +149,38 @@ namespace AZ
}
MeshFeatureProcessor::MeshHandle MeshFeatureProcessor::AcquireMesh(
const Data::Asset<RPI::ModelAsset>& modelAsset,
const MaterialAssignmentMap& materials,
bool skinnedMeshWithMotion,
bool rayTracingEnabled,
RequiresCloneCallback requiresCloneCallback)
const MeshHandleDescriptor& descriptor,
const MaterialAssignmentMap& materials)
{
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
// don't need to check the concurrency during emplace() because the StableDynamicArray won't move the other elements during insertion
MeshHandle meshDataHandle = m_meshData.emplace();
// Mark skinned meshes to enable special processes to generate motion vector
meshDataHandle->m_skinnedMeshWithMotion = skinnedMeshWithMotion;
meshDataHandle->m_descriptor = descriptor;
// set ray tracing flag, but always disable on skinned meshes
// Always disable ray tracing flag on skinned meshes
// [GFX TODO][ATOM-13067] Enable raytracing on skinned meshes
meshDataHandle->m_rayTracingEnabled = rayTracingEnabled && (skinnedMeshWithMotion == false);
meshDataHandle->m_descriptor.m_isRayTracingEnabled &= !descriptor.m_isSkinnedMeshWithMotion;
meshDataHandle->m_scene = GetParentScene();
meshDataHandle->m_materialAssignments = materials;
meshDataHandle->m_objectId = m_transformService->ReserveObjectId();
meshDataHandle->m_originalModelAsset = modelAsset;
meshDataHandle->m_requiresCloningCallback = requiresCloneCallback;
meshDataHandle->m_meshLoader = AZStd::make_unique<MeshDataInstance::MeshLoader>(modelAsset, &*meshDataHandle);
meshDataHandle->m_originalModelAsset = descriptor.m_modelAsset;
meshDataHandle->m_meshLoader = AZStd::make_unique<MeshDataInstance::MeshLoader>(descriptor.m_modelAsset, &*meshDataHandle);
return meshDataHandle;
}
MeshFeatureProcessor::MeshHandle MeshFeatureProcessor::AcquireMesh(
const Data::Asset<RPI::ModelAsset>& modelAsset,
const Data::Instance<RPI::Material>& material,
bool skinnedMeshWithMotion,
bool rayTracingEnabled,
RequiresCloneCallback requiresCloneCallback)
const MeshHandleDescriptor& descriptor,
const Data::Instance<RPI::Material>& material)
{
Render::MaterialAssignmentMap materials;
Render::MaterialAssignment& defaultMaterial = materials[AZ::Render::DefaultMaterialAssignmentId];
defaultMaterial.m_materialInstance = material;
return AcquireMesh(modelAsset, materials, skinnedMeshWithMotion, rayTracingEnabled, requiresCloneCallback);
return AcquireMesh(descriptor, materials);
}
bool MeshFeatureProcessor::ReleaseMesh(MeshHandle& meshHandle)
@@ -210,7 +202,7 @@ namespace AZ
{
if (meshHandle.IsValid())
{
MeshHandle clone = AcquireMesh(meshHandle->m_originalModelAsset, meshHandle->m_materialAssignments);
MeshHandle clone = AcquireMesh(meshHandle->m_descriptor, meshHandle->m_materialAssignments);
return clone;
}
return MeshFeatureProcessor::MeshHandle();
@@ -377,6 +369,14 @@ namespace AZ
if (meshHandle.IsValid())
{
meshHandle->m_excludeFromReflectionCubeMaps = excludeFromReflectionCubeMaps;
if (excludeFromReflectionCubeMaps)
{
meshHandle->m_cullable.m_cullData.m_hideFlags |= RPI::View::UsageReflectiveCubeMap;
}
else
{
meshHandle->m_cullable.m_cullData.m_hideFlags &= ~RPI::View::UsageReflectiveCubeMap;
}
}
}
@@ -385,12 +385,12 @@ namespace AZ
if (meshHandle.IsValid())
{
// update the ray tracing data based on the current state and the new state
if (rayTracingEnabled && !meshHandle->m_rayTracingEnabled)
if (rayTracingEnabled && !meshHandle->m_descriptor.m_isRayTracingEnabled)
{
// add to ray tracing
meshHandle->SetRayTracingData();
}
else if (!rayTracingEnabled && meshHandle->m_rayTracingEnabled)
else if (!rayTracingEnabled && meshHandle->m_descriptor.m_isRayTracingEnabled)
{
// remove from ray tracing
if (m_rayTracingFeatureProcessor)
@@ -400,7 +400,7 @@ namespace AZ
}
// set new state
meshHandle->m_rayTracingEnabled = rayTracingEnabled;
meshHandle->m_descriptor.m_isRayTracingEnabled = rayTracingEnabled;
}
}
@@ -416,7 +416,7 @@ namespace AZ
{
if (meshHandle.IsValid())
{
meshHandle->m_useForwardPassIblSpecular = useForwardPassIblSpecular;
meshHandle->m_descriptor.m_useForwardPassIblSpecular = useForwardPassIblSpecular;
meshHandle->m_objectSrgNeedsUpdate = true;
if (meshHandle->m_model)
@@ -450,7 +450,7 @@ namespace AZ
// we need to rebuild the Srg for any meshes that are using the forward pass IBL specular option
for (auto& meshInstance : m_meshData)
{
if (meshInstance.m_useForwardPassIblSpecular)
if (meshInstance.m_descriptor.m_useForwardPassIblSpecular)
{
meshInstance.m_objectSrgNeedsUpdate = true;
}
@@ -507,8 +507,8 @@ namespace AZ
Data::Instance<RPI::Model> model;
// Check if a requires cloning callback got set and if so check if cloning the model asset is requested.
if (m_parent->m_requiresCloningCallback &&
m_parent->m_requiresCloningCallback(modelAsset))
if (m_parent->m_descriptor.m_requiresCloneCallback &&
m_parent->m_descriptor.m_requiresCloneCallback(modelAsset))
{
// Clone the model asset to force create another model instance.
AZ::Data::AssetId newId(AZ::Uuid::CreateRandom(), /*subId=*/0);
@@ -598,7 +598,7 @@ namespace AZ
objectIdIndex.AssertValid();
}
if (m_rayTracingEnabled)
if (m_descriptor.m_isRayTracingEnabled)
{
SetRayTracingData();
}
@@ -671,7 +671,7 @@ namespace AZ
RPI::MeshDrawPacket drawPacket(modelLod, meshIndex, material, m_shaderResourceGroup, materialAssignment.m_matModUvOverrides);
// set the shader option to select forward pass IBL specular if necessary
if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ m_useForwardPassIblSpecular }))
if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ m_descriptor.m_useForwardPassIblSpecular }))
{
AZ_Warning("MeshDrawPacket", false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet");
}
@@ -682,7 +682,7 @@ namespace AZ
m_hasForwardPassIblSpecularMaterial |= materialRequiresForwardPassIblSpecular;
// stencil bits
uint8_t stencilRef = m_useForwardPassIblSpecular || materialRequiresForwardPassIblSpecular ? Render::StencilRefs::None : Render::StencilRefs::UseIBLSpecularPass;
uint8_t stencilRef = m_descriptor.m_useForwardPassIblSpecular || materialRequiresForwardPassIblSpecular ? Render::StencilRefs::None : Render::StencilRefs::UseIBLSpecularPass;
stencilRef |= Render::StencilRefs::UseDiffuseGIPass;
drawPacket.SetStencilRef(stencilRef);
@@ -1102,12 +1102,12 @@ namespace AZ
//[GFX TODO][ATOM-4726] Replace this with a "isSkinnedMesh" external material property and a functor that enables/disables the appropriate shader
for (auto& shaderItem : material->GetShaderCollection())
{
if (shaderItem.GetShaderAsset()->GetName() == Name{ "StaticMeshMotionVector" } && m_skinnedMeshWithMotion)
if (shaderItem.GetShaderAsset()->GetName() == Name{ "StaticMeshMotionVector" } && m_descriptor.m_isSkinnedMeshWithMotion)
{
shaderItem.SetEnabled(false);
}
if (shaderItem.GetShaderAsset()->GetName() == Name{ "SkinnedMeshMotionVector" } && (!m_skinnedMeshWithMotion))
if (shaderItem.GetShaderAsset()->GetName() == Name{ "SkinnedMeshMotionVector" } && (!m_descriptor.m_isSkinnedMeshWithMotion))
{
shaderItem.SetEnabled(false);
}
@@ -1123,7 +1123,7 @@ namespace AZ
ReflectionProbeFeatureProcessor* reflectionProbeFeatureProcessor = m_scene->GetFeatureProcessor<ReflectionProbeFeatureProcessor>();
if (reflectionProbeFeatureProcessor && (m_useForwardPassIblSpecular || m_hasForwardPassIblSpecularMaterial))
if (reflectionProbeFeatureProcessor && (m_descriptor.m_useForwardPassIblSpecular || m_hasForwardPassIblSpecularMaterial))
{
// retrieve probe constant indices
AZ::RHI::ShaderInputConstantIndex posConstantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_reflectionProbeData.m_aabbPos"));
@@ -39,7 +39,7 @@ namespace AZ
"Models/OcclusionCullingPlane.azmodel",
AZ::RPI::AssetUtils::TraceLevel::Assert);
m_visualizationMeshHandle = m_meshFeatureProcessor->AcquireMesh(m_visualizationModelAsset);
m_visualizationMeshHandle = m_meshFeatureProcessor->AcquireMesh(MeshHandleDescriptor{ m_visualizationModelAsset });
m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_visualizationMeshHandle, true);
m_meshFeatureProcessor->SetRayTracingEnabled(m_visualizationMeshHandle, false);
m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, AZ::Transform::CreateIdentity());
@@ -67,7 +67,7 @@ namespace AZ
"Models/ReflectionProbeSphere.azmodel",
AZ::RPI::AssetUtils::TraceLevel::Assert);
m_visualizationMeshHandle = m_meshFeatureProcessor->AcquireMesh(m_visualizationModelAsset);
m_visualizationMeshHandle = m_meshFeatureProcessor->AcquireMesh(MeshHandleDescriptor{ m_visualizationModelAsset });
m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_visualizationMeshHandle, true);
m_meshFeatureProcessor->SetRayTracingEnabled(m_visualizationMeshHandle, false);
m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, AZ::Transform::CreateIdentity());
@@ -317,8 +317,11 @@ namespace AZ
MaterialComponentRequestBus::EventResult(materials, entityId, &MaterialComponentRequests::GetMaterialOverrides);
m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
m_meshHandle = m_meshFeatureProcessor->AcquireMesh(m_configuration.m_modelAsset, materials,
/*skinnedMeshWithMotion=*/false, /*rayTracingEnabled=*/true, RequiresCloning);
MeshHandleDescriptor meshDescriptor;
meshDescriptor.m_modelAsset = m_configuration.m_modelAsset;
meshDescriptor.m_useForwardPassIblSpecular = m_configuration.m_useForwardPassIblSpecular;
meshDescriptor.m_requiresCloneCallback = RequiresCloning;
m_meshHandle = m_meshFeatureProcessor->AcquireMesh(meshDescriptor, materials);
m_meshFeatureProcessor->ConnectModelChangeEventHandler(m_meshHandle, m_changeEventHandler);
const AZ::Transform& transform = m_transformInterface ? m_transformInterface->GetWorldTM() : AZ::Transform::CreateIdentity();
@@ -327,7 +330,6 @@ namespace AZ
m_meshFeatureProcessor->SetSortKey(m_meshHandle, m_configuration.m_sortKey);
m_meshFeatureProcessor->SetLodOverride(m_meshHandle, m_configuration.m_lodOverride);
m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_meshHandle, m_configuration.m_excludeFromReflectionCubeMaps);
m_meshFeatureProcessor->SetUseForwardPassIblSpecular(m_meshHandle, m_configuration.m_useForwardPassIblSpecular);
// [GFX TODO] This should happen automatically. m_changeEventHandler should be passed to AcquireMesh
// If the model instance or asset already exists, announce a model change to let others know it's loaded.
@@ -626,9 +626,11 @@ namespace AZ
AZ_Error("ActorComponentController", meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId.");
if (meshFeatureProcessor)
{
// Last boolean parameter indicates if motion vector is enabled
MeshHandleDescriptor meshDescriptor;
meshDescriptor.m_modelAsset = m_skinnedMeshInstance->m_model->GetModelAsset();
meshDescriptor.m_isSkinnedMeshWithMotion = true;
m_meshHandle = AZStd::make_shared<MeshFeatureProcessorInterface::MeshHandle>(
m_meshFeatureProcessor->AcquireMesh(m_skinnedMeshInstance->m_model->GetModelAsset(), materials, /*skinnedMeshWithMotion=*/true));
m_meshFeatureProcessor->AcquireMesh(meshDescriptor, materials));
}
// If render proxies already exist, they will be auto-freed
@@ -187,7 +187,7 @@ namespace Blast
materials, GetEntityId(), &AZ::Render::MaterialComponentRequests::GetMaterialOverrides);
m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
m_meshHandle = m_meshFeatureProcessor->AcquireMesh(m_meshAssets[0], materials);
m_meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_meshAssets[0] }, materials);
m_meshFeatureProcessor->ConnectModelChangeEventHandler(m_meshHandle, m_changeEventHandler);
HandleModelChange(m_meshFeatureProcessor->GetModel(m_meshHandle));
@@ -47,7 +47,7 @@ namespace Blast
{
m_chunkActors[chunkId] = &actor;
m_chunkMeshHandles[chunkId] =
m_meshFeatureProcessor->AcquireMesh(m_meshData->GetMeshAsset(chunkId), m_materialMap);
m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_meshData->GetMeshAsset(chunkId) }, m_materialMap);
}
}
@@ -98,7 +98,7 @@ namespace Blast
// ActorRenderManager::OnActorCreated
{
EXPECT_CALL(
*m_mockMeshFeatureProcessor, AcquireMesh(_, testing::A<const AZ::Render::MaterialAssignmentMap&>(), _, _, _))
*m_mockMeshFeatureProcessor, AcquireMesh(_, testing::A<const AZ::Render::MaterialAssignmentMap&>()))
.Times(aznumeric_cast<int>(m_actorFactory->m_mockActors[0]->GetChunkIndices().size()))
.WillOnce(Return(testing::ByMove(AZ::Render::MeshFeatureProcessorInterface::MeshHandle())))
.WillOnce(Return(testing::ByMove(AZ::Render::MeshFeatureProcessorInterface::MeshHandle())));
@@ -178,7 +178,7 @@ namespace WhiteBox
}
m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
m_meshHandle = m_meshFeatureProcessor->AcquireMesh(m_modelAsset);
m_meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_modelAsset });
return true;
}