MorphTargetDispatchItem.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <MorphTargets/MorphTargetDispatchItem.h>
  9. #include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
  10. #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
  11. #include <Atom/RPI.Public/Shader/Shader.h>
  12. #include <Atom/RPI.Public/Model/ModelLod.h>
  13. #include <Atom/RPI.Public/Buffer/Buffer.h>
  14. #include <Atom/RPI.Public/RPIUtils.h>
  15. #include <Atom/RHI/Factory.h>
  16. #include <Atom/RHI/BufferView.h>
  17. #include <limits>
  18. namespace AZ
  19. {
  20. namespace Render
  21. {
  22. MorphTargetDispatchItem::MorphTargetDispatchItem(
  23. const AZStd::intrusive_ptr<MorphTargetInputBuffers> inputBuffers,
  24. const MorphTargetComputeMetaData& morphTargetComputeMetaData,
  25. SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
  26. MorphTargetInstanceMetaData morphInstanceMetaData,
  27. float morphDeltaIntegerEncoding)
  28. : m_inputBuffers(inputBuffers)
  29. , m_morphTargetComputeMetaData(morphTargetComputeMetaData)
  30. , m_morphInstanceMetaData(morphInstanceMetaData)
  31. , m_accumulatedDeltaIntegerEncoding(morphDeltaIntegerEncoding)
  32. {
  33. m_morphTargetShader = skinnedMeshFeatureProcessor->GetMorphTargetShader();
  34. RPI::ShaderReloadNotificationBus::Handler::BusConnect(m_morphTargetShader->GetAssetId());
  35. }
  36. MorphTargetDispatchItem::~MorphTargetDispatchItem()
  37. {
  38. RPI::ShaderReloadNotificationBus::Handler::BusDisconnect();
  39. }
  40. bool MorphTargetDispatchItem::Init()
  41. {
  42. if (!m_morphTargetShader)
  43. {
  44. AZ_Error("MorphTargetDispatchItem", false, "Cannot initialize a MorphTargetDispatchItem with a null shader");
  45. return false;
  46. }
  47. AZ::RPI::ShaderOptionGroup shaderOptionGroup = m_morphTargetShader->CreateShaderOptionGroup();
  48. // In case there are several options you don't care about, it's good practice to initialize them with default values.
  49. shaderOptionGroup.SetUnspecifiedToDefaultValues();
  50. // Get the shader variant and instance SRG
  51. RPI::ShaderReloadNotificationBus::Handler::BusConnect(m_morphTargetShader->GetAssetId());
  52. const RPI::ShaderVariant& shaderVariant = m_morphTargetShader->GetVariant(shaderOptionGroup.GetShaderVariantId());
  53. if (!InitPerInstanceSRG())
  54. {
  55. return false;
  56. }
  57. if (!shaderVariant.IsFullyBaked() && m_instanceSrg->HasShaderVariantKeyFallbackEntry())
  58. {
  59. m_instanceSrg->SetShaderVariantKeyFallbackValue(shaderOptionGroup.GetShaderVariantKeyFallbackValue());
  60. }
  61. RHI::PipelineStateDescriptorForDispatch pipelineStateDescriptor;
  62. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  63. InitRootConstants(pipelineStateDescriptor.m_pipelineLayoutDescriptor->GetRootConstantsLayout());
  64. m_dispatchItem.m_pipelineState = m_morphTargetShader->AcquirePipelineState(pipelineStateDescriptor);
  65. // Get the threads-per-group values from the compute shader [numthreads(x,y,z)]
  66. auto& arguments = m_dispatchItem.m_arguments.m_direct;
  67. const auto outcome = RPI::GetComputeShaderNumThreads(m_morphTargetShader->GetAsset(), arguments);
  68. if (!outcome.IsSuccess())
  69. {
  70. AZ_Error("MorphTargetDispatchItem", false, outcome.GetError().c_str());
  71. }
  72. arguments.m_totalNumberOfThreadsX = m_morphTargetComputeMetaData.m_vertexCount;
  73. arguments.m_totalNumberOfThreadsY = 1;
  74. arguments.m_totalNumberOfThreadsZ = 1;
  75. return true;
  76. }
  77. bool MorphTargetDispatchItem::InitPerInstanceSRG()
  78. {
  79. auto perInstanceSrgLayout = m_morphTargetShader->FindShaderResourceGroupLayout(AZ::Name{ "MorphTargetInstanceSrg" });
  80. if (!perInstanceSrgLayout)
  81. {
  82. AZ_Error("MorphTargetDispatchItem", false, "Failed to get shader resource group layout");
  83. return false;
  84. }
  85. m_instanceSrg = RPI::ShaderResourceGroup::Create(m_morphTargetShader->GetAsset(), m_morphTargetShader->GetSupervariantIndex(), perInstanceSrgLayout->GetName());
  86. if (!m_instanceSrg)
  87. {
  88. AZ_Error("MorphTargetDispatchItem", false, "Failed to create shader resource group for morph target");
  89. return false;
  90. }
  91. m_inputBuffers->SetBufferViewsOnShaderResourceGroup(m_instanceSrg);
  92. m_instanceSrg->Compile();
  93. m_dispatchItem.m_uniqueShaderResourceGroup = m_instanceSrg->GetRHIShaderResourceGroup();
  94. return true;
  95. }
  96. void MorphTargetDispatchItem::InitRootConstants(const RHI::ConstantsLayout* rootConstantsLayout)
  97. {
  98. auto vertexCountIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_vertexCount" });
  99. AZ_Error("MorphTargetDispatchItem", vertexCountIndex.IsValid(), "Could not find root constant 's_vertexCount' in the shader");
  100. auto positionOffsetIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_targetPositionOffset" });
  101. AZ_Error("MorphTargetDispatchItem", positionOffsetIndex.IsValid(), "Could not find root constant 's_targetPositionOffset' in the shader");
  102. auto normalOffsetIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_targetNormalOffset" });
  103. AZ_Error("MorphTargetDispatchItem", normalOffsetIndex.IsValid(), "Could not find root constant 's_targetNormalOffset' in the shader");
  104. auto tangentOffsetIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_targetTangentOffset" });
  105. AZ_Error("MorphTargetDispatchItem", tangentOffsetIndex.IsValid(), "Could not find root constant 's_targetTangentOffset' in the shader");
  106. auto bitangentOffsetIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_targetBitangentOffset" });
  107. AZ_Error("MorphTargetDispatchItem", bitangentOffsetIndex.IsValid(), "Could not find root constant 's_targetBitangentOffset' in the shader");
  108. auto minIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_min" });
  109. AZ_Error("MorphTargetDispatchItem", minIndex.IsValid(), "Could not find root constant 's_min' in the shader");
  110. auto maxIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_max" });
  111. AZ_Error("MorphTargetDispatchItem", maxIndex.IsValid(), "Could not find root constant 's_max' in the shader");
  112. auto morphDeltaIntegerEncodingIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_accumulatedDeltaIntegerEncoding" });
  113. AZ_Error("MorphTargetDispatchItem", morphDeltaIntegerEncodingIndex.IsValid(), "Could not find root constant 's_accumulatedDeltaIntegerEncoding' in the shader");
  114. m_weightIndex = rootConstantsLayout->FindShaderInputIndex(AZ::Name{ "s_weight" });
  115. AZ_Error("MorphTargetDispatchItem", m_weightIndex.IsValid(), "Could not find root constant 's_weight' in the shader");
  116. m_rootConstantData = AZ::RHI::ConstantsData(rootConstantsLayout);
  117. m_rootConstantData.SetConstant(minIndex, m_morphTargetComputeMetaData.m_minDelta);
  118. m_rootConstantData.SetConstant(maxIndex, m_morphTargetComputeMetaData.m_maxDelta);
  119. m_rootConstantData.SetConstant(morphDeltaIntegerEncodingIndex, m_accumulatedDeltaIntegerEncoding);
  120. m_rootConstantData.SetConstant(m_weightIndex, 0.0f);
  121. m_rootConstantData.SetConstant(vertexCountIndex, m_morphTargetComputeMetaData.m_vertexCount);
  122. // The buffer is using 32-bit integers, so divide the offset by 4 here so it doesn't have to be done in the shader
  123. m_rootConstantData.SetConstant(positionOffsetIndex, m_morphInstanceMetaData.m_accumulatedPositionDeltaOffsetInBytes / 4);
  124. m_rootConstantData.SetConstant(normalOffsetIndex, m_morphInstanceMetaData.m_accumulatedNormalDeltaOffsetInBytes / 4);
  125. m_rootConstantData.SetConstant(tangentOffsetIndex, m_morphInstanceMetaData.m_accumulatedTangentDeltaOffsetInBytes / 4);
  126. m_rootConstantData.SetConstant(bitangentOffsetIndex, m_morphInstanceMetaData.m_accumulatedBitangentDeltaOffsetInBytes / 4);
  127. m_dispatchItem.m_rootConstantSize = static_cast<uint8_t>(m_rootConstantData.GetConstantData().size());
  128. m_dispatchItem.m_rootConstants = m_rootConstantData.GetConstantData().data();
  129. }
  130. void MorphTargetDispatchItem::SetWeight(float weight)
  131. {
  132. m_rootConstantData.SetConstant(m_weightIndex, weight);
  133. m_dispatchItem.m_rootConstants = m_rootConstantData.GetConstantData().data();
  134. }
  135. float MorphTargetDispatchItem::GetWeight() const
  136. {
  137. return m_rootConstantData.GetConstant<float>(m_weightIndex);
  138. }
  139. const RHI::DispatchItem& MorphTargetDispatchItem::GetRHIDispatchItem() const
  140. {
  141. return m_dispatchItem;
  142. }
  143. void MorphTargetDispatchItem::OnShaderReinitialized([[maybe_unused]] const RPI::Shader& shader)
  144. {
  145. if (!Init())
  146. {
  147. AZ_Error("MorphTargetDispatchItem", false, "Failed to re-initialize after the shader was re-loaded.");
  148. }
  149. }
  150. void MorphTargetDispatchItem::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset<RPI::ShaderAsset>& shaderAsset)
  151. {
  152. if (!Init())
  153. {
  154. AZ_Error("MorphTargetDispatchItem", false, "Failed to re-initialize after the shader asset was re-loaded.");
  155. }
  156. }
  157. void MorphTargetDispatchItem::OnShaderVariantReinitialized(const RPI::ShaderVariant&)
  158. {
  159. if (!Init())
  160. {
  161. AZ_Error("MorphTargetDispatchItem", false, "Failed to re-initialize after the shader variant was loaded.");
  162. }
  163. }
  164. } // namespace Render
  165. } // namespace AZ