3
0

SkinnedMeshDispatchItem.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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 <SkinnedMesh/SkinnedMeshDispatchItem.h>
  9. #include <SkinnedMesh/SkinnedMeshOutputStreamManager.h>
  10. #include <SkinnedMesh/SkinnedMeshFeatureProcessor.h>
  11. #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
  12. #include <Atom/RPI.Public/Shader/Shader.h>
  13. #include <Atom/RPI.Public/Model/ModelLod.h>
  14. #include <Atom/RPI.Public/Buffer/Buffer.h>
  15. #include <Atom/RPI.Public/RPIUtils.h>
  16. #include <Atom/RHI/Factory.h>
  17. #include <Atom/RHI/BufferView.h>
  18. #include <limits>
  19. namespace AZ
  20. {
  21. namespace Render
  22. {
  23. SkinnedMeshDispatchItem::SkinnedMeshDispatchItem(
  24. AZStd::intrusive_ptr<SkinnedMeshInputBuffers> inputBuffers,
  25. const SkinnedMeshOutputVertexOffsets& outputBufferOffsetsInBytes,
  26. uint32_t positionHistoryOutputBufferOffsetInBytes,
  27. uint32_t lodIndex,
  28. uint32_t meshIndex,
  29. Data::Instance<RPI::Buffer> boneTransforms,
  30. const SkinnedMeshShaderOptions& shaderOptions,
  31. SkinnedMeshFeatureProcessor* skinnedMeshFeatureProcessor,
  32. MorphTargetInstanceMetaData morphTargetInstanceMetaData,
  33. float morphTargetDeltaIntegerEncoding)
  34. : m_inputBuffers(inputBuffers)
  35. , m_outputBufferOffsetsInBytes(outputBufferOffsetsInBytes)
  36. , m_positionHistoryBufferOffsetInBytes(positionHistoryOutputBufferOffsetInBytes)
  37. , m_lodIndex(lodIndex)
  38. , m_meshIndex(meshIndex)
  39. , m_boneTransforms(AZStd::move(boneTransforms))
  40. , m_shaderOptions(shaderOptions)
  41. , m_morphTargetInstanceMetaData(morphTargetInstanceMetaData)
  42. , m_morphTargetDeltaIntegerEncoding(morphTargetDeltaIntegerEncoding)
  43. {
  44. m_skinningShader = skinnedMeshFeatureProcessor->GetSkinningShader();
  45. // 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
  46. if (m_morphTargetInstanceMetaData.m_accumulatedPositionDeltaOffsetInBytes != MorphTargetConstants::s_invalidDeltaOffset)
  47. {
  48. m_shaderOptions.m_applyMorphTargets = true;
  49. }
  50. // CreateShaderOptionGroup will also connect to the SkinnedMeshShaderOptionNotificationBus
  51. m_shaderOptionGroup = skinnedMeshFeatureProcessor->CreateSkinningShaderOptionGroup(m_shaderOptions, *this);
  52. }
  53. SkinnedMeshDispatchItem::~SkinnedMeshDispatchItem()
  54. {
  55. SkinnedMeshShaderOptionNotificationBus::Handler::BusDisconnect();
  56. }
  57. bool SkinnedMeshDispatchItem::Init()
  58. {
  59. if (!m_skinningShader)
  60. {
  61. AZ_Error("SkinnedMeshDispatchItem", false, "Cannot initialize a SkinnedMeshDispatchItem with a null shader");
  62. return false;
  63. }
  64. // Get the shader variant and instance SRG
  65. m_shaderOptionGroup.SetUnspecifiedToDefaultValues();
  66. const RPI::ShaderVariant& shaderVariant = m_skinningShader->GetVariant(m_shaderOptionGroup.GetShaderVariantId());
  67. RHI::PipelineStateDescriptorForDispatch pipelineStateDescriptor;
  68. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  69. auto perInstanceSrgLayout = m_skinningShader->FindShaderResourceGroupLayout(AZ::Name{ "InstanceSrg" });
  70. if (!perInstanceSrgLayout)
  71. {
  72. AZ_Error("SkinnedMeshDispatchItem", false, "Failed to get shader resource group layout");
  73. return false;
  74. }
  75. m_instanceSrg = RPI::ShaderResourceGroup::Create(m_skinningShader->GetAsset(), m_skinningShader->GetSupervariantIndex(), perInstanceSrgLayout->GetName());
  76. if (!m_instanceSrg)
  77. {
  78. AZ_Error("SkinnedMeshDispatchItem", false, "Failed to create shader resource group for skinned mesh");
  79. return false;
  80. }
  81. // If the shader variation is not fully baked, set the fallback key to use a runtime branch for the shader options
  82. if (!shaderVariant.IsFullyBaked() && m_instanceSrg->HasShaderVariantKeyFallbackEntry())
  83. {
  84. m_instanceSrg->SetShaderVariantKeyFallbackValue(m_shaderOptionGroup.GetShaderVariantKeyFallbackValue());
  85. }
  86. m_inputBuffers->SetBufferViewsOnShaderResourceGroup(m_lodIndex, m_meshIndex, m_instanceSrg);
  87. // Set the SRG indices
  88. RHI::ShaderInputBufferIndex actorInstanceBoneTransformsIndex;
  89. if (m_shaderOptions.m_skinningMethod == SkinningMethod::LinearSkinning)
  90. {
  91. actorInstanceBoneTransformsIndex = m_instanceSrg->FindShaderInputBufferIndex(Name{ "m_boneTransformsLinear" });
  92. if (!actorInstanceBoneTransformsIndex.IsValid())
  93. {
  94. AZ_Error("SkinnedMeshDispatchItem", false, "Failed to find shader input index for m_boneTransformsLinear in the skinning compute shader per-instance SRG.");
  95. return false;
  96. }
  97. }
  98. else if(m_shaderOptions.m_skinningMethod == SkinningMethod::DualQuaternion)
  99. {
  100. actorInstanceBoneTransformsIndex = m_instanceSrg->FindShaderInputBufferIndex(Name{ "m_boneTransformsDualQuaternion" });
  101. if (!actorInstanceBoneTransformsIndex.IsValid())
  102. {
  103. AZ_Error("SkinnedMeshDispatchItem", false, "Failed to find shader input index for m_boneTransformsDualQuaternion in the skinning compute shader per-instance SRG.");
  104. return false;
  105. }
  106. }
  107. else
  108. {
  109. AZ_Assert(false, "Invalid skinning method for SkinnedMeshDispatchItem.");
  110. }
  111. for (uint8_t outputStream = 0; outputStream < static_cast<uint8_t>(SkinnedMeshOutputVertexStreams::NumVertexStreams); outputStream++)
  112. {
  113. // Set the buffer offsets
  114. const SkinnedMeshOutputVertexStreamInfo& outputStreamInfo = SkinnedMeshVertexStreamPropertyInterface::Get()->GetOutputStreamInfo(static_cast<SkinnedMeshOutputVertexStreams>(outputStream));
  115. {
  116. RHI::ShaderInputConstantIndex outputOffsetIndex = m_instanceSrg->FindShaderInputConstantIndex(outputStreamInfo.m_shaderResourceGroupName);
  117. if (!outputOffsetIndex.IsValid())
  118. {
  119. AZ_Error("SkinnedMeshDispatchItem", false, "Failed to find shader input index for %s in the skinning compute shader per-instance SRG.", outputStreamInfo.m_shaderResourceGroupName.GetCStr());
  120. return false;
  121. }
  122. // The shader has a view with 4 bytes per element
  123. // Divide the byte offset here so it doesn't need to be done in the shader
  124. m_instanceSrg->SetConstant(outputOffsetIndex, m_outputBufferOffsetsInBytes[outputStream] / 4);
  125. }
  126. // Set the position history buffer offset
  127. RHI::ShaderInputConstantIndex outputOffsetIndex =
  128. m_instanceSrg->FindShaderInputConstantIndex(Name{ "m_targetPositionHistory" });
  129. if (!outputOffsetIndex.IsValid())
  130. {
  131. AZ_Error(
  132. "SkinnedMeshDispatchItem", false,
  133. "Failed to find shader input index for m_targetPositionHistory in the skinning compute shader per-instance SRG.");
  134. return false;
  135. }
  136. // The shader has a view with 4 bytes per element
  137. // Divide the byte offset here so it doesn't need to be done in the shader
  138. m_instanceSrg->SetConstant(outputOffsetIndex, m_positionHistoryBufferOffsetInBytes / 4);
  139. }
  140. m_instanceSrg->SetBuffer(actorInstanceBoneTransformsIndex, m_boneTransforms);
  141. // Set the morph target related srg constants
  142. RHI::ShaderInputConstantIndex morphPositionOffsetIndex = m_instanceSrg->FindShaderInputConstantIndex(Name{ "m_morphTargetPositionDeltaOffset" });
  143. // 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
  144. m_instanceSrg->SetConstant(morphPositionOffsetIndex, m_morphTargetInstanceMetaData.m_accumulatedPositionDeltaOffsetInBytes / 4);
  145. RHI::ShaderInputConstantIndex morphNormalOffsetIndex = m_instanceSrg->FindShaderInputConstantIndex(Name{ "m_morphTargetNormalDeltaOffset" });
  146. // 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
  147. m_instanceSrg->SetConstant(morphNormalOffsetIndex, m_morphTargetInstanceMetaData.m_accumulatedNormalDeltaOffsetInBytes / 4);
  148. RHI::ShaderInputConstantIndex morphTangentOffsetIndex = m_instanceSrg->FindShaderInputConstantIndex(Name{ "m_morphTargetTangentDeltaOffset" });
  149. // 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
  150. m_instanceSrg->SetConstant(morphTangentOffsetIndex, m_morphTargetInstanceMetaData.m_accumulatedTangentDeltaOffsetInBytes / 4);
  151. RHI::ShaderInputConstantIndex morphBitangentOffsetIndex = m_instanceSrg->FindShaderInputConstantIndex(Name{ "m_morphTargetBitangentDeltaOffset" });
  152. // 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
  153. m_instanceSrg->SetConstant(morphBitangentOffsetIndex, m_morphTargetInstanceMetaData.m_accumulatedBitangentDeltaOffsetInBytes / 4);
  154. RHI::ShaderInputConstantIndex morphDeltaIntegerEncodingIndex = m_instanceSrg->FindShaderInputConstantIndex(Name{ "m_morphTargetDeltaInverseIntegerEncoding" });
  155. m_instanceSrg->SetConstant(morphDeltaIntegerEncodingIndex, 1.0f / m_morphTargetDeltaIntegerEncoding);
  156. // Set the vertex count
  157. const uint32_t vertexCount = GetVertexCount();
  158. RHI::ShaderInputConstantIndex numVerticesIndex = m_instanceSrg->FindShaderInputConstantIndex(Name{ "m_numVertices" });
  159. AZ_Error("SkinnedMeshInputBuffers", numVerticesIndex.IsValid(), "Failed to find shader input index for m_numVerticies in the skinning compute shader per-instance SRG.");
  160. m_instanceSrg->SetConstant(numVerticesIndex, vertexCount);
  161. uint32_t xThreads = 0;
  162. uint32_t yThreads = 0;
  163. CalculateSkinnedMeshTotalThreadsPerDimension(vertexCount, xThreads, yThreads);
  164. // Set the total number of threads in the x dimension, so the shader can calculate the vertex index from the thread ids
  165. RHI::ShaderInputConstantIndex totalNumberOfThreadsXIndex;
  166. totalNumberOfThreadsXIndex = m_instanceSrg->FindShaderInputConstantIndex(Name{ "m_totalNumberOfThreadsX" });
  167. AZ_Error("SkinnedMeshInputBuffers", totalNumberOfThreadsXIndex.IsValid(), "Failed to find shader input index for m_totalNumberOfThreadsX in the skinning compute shader per-instance SRG.");
  168. m_instanceSrg->SetConstant(totalNumberOfThreadsXIndex, xThreads);
  169. m_instanceSrg->Compile();
  170. m_dispatchItem.m_uniqueShaderResourceGroup = m_instanceSrg->GetRHIShaderResourceGroup();
  171. m_dispatchItem.m_pipelineState = m_skinningShader->AcquirePipelineState(pipelineStateDescriptor);
  172. auto& arguments = m_dispatchItem.m_arguments.m_direct;
  173. const auto outcome = RPI::GetComputeShaderNumThreads(m_skinningShader->GetAsset(), arguments);
  174. if (!outcome.IsSuccess())
  175. {
  176. AZ_Error("SkinnedMeshInputBuffers", false, outcome.GetError().c_str());
  177. }
  178. arguments.m_totalNumberOfThreadsX = xThreads;
  179. arguments.m_totalNumberOfThreadsY = yThreads;
  180. arguments.m_totalNumberOfThreadsZ = 1;
  181. return true;
  182. }
  183. const RHI::DispatchItem& SkinnedMeshDispatchItem::GetRHIDispatchItem() const
  184. {
  185. return m_dispatchItem;
  186. }
  187. Data::Instance<RPI::Buffer> SkinnedMeshDispatchItem::GetBoneTransforms() const
  188. {
  189. return m_boneTransforms;
  190. }
  191. uint32_t SkinnedMeshDispatchItem::GetVertexCount() const
  192. {
  193. return m_inputBuffers->GetVertexCount(m_lodIndex, m_meshIndex);
  194. }
  195. void SkinnedMeshDispatchItem::Enable()
  196. {
  197. m_isEnabled = true;
  198. }
  199. void SkinnedMeshDispatchItem::Disable()
  200. {
  201. m_isEnabled = false;
  202. }
  203. bool SkinnedMeshDispatchItem::IsEnabled() const
  204. {
  205. return m_isEnabled;
  206. }
  207. void SkinnedMeshDispatchItem::OnShaderReinitialized(const CachedSkinnedMeshShaderOptions* cachedShaderOptions)
  208. {
  209. m_shaderOptionGroup = cachedShaderOptions->CreateShaderOptionGroup(m_shaderOptions);
  210. if (!Init())
  211. {
  212. AZ_Error("SkinnedMeshDispatchItem", false, "Failed to re-initialize after the shader was re-loaded.");
  213. }
  214. }
  215. void CalculateSkinnedMeshTotalThreadsPerDimension(uint32_t vertexCount, uint32_t& xThreads, uint32_t& yThreads)
  216. {
  217. const uint32_t maxVerticesPerDimension = static_cast<uint32_t>(std::numeric_limits<uint16_t>::max());
  218. if (vertexCount > maxVerticesPerDimension * maxVerticesPerDimension)
  219. {
  220. AZ_Error("CalculateSkinnedMeshTotalThreadsPerDimension", false, "Vertex count '%d' exceeds maximum supported vertices '%d' for skinned meshes. Not all vertices will be rendered.", vertexCount, maxVerticesPerDimension * maxVerticesPerDimension);
  221. xThreads = maxVerticesPerDimension;
  222. yThreads = maxVerticesPerDimension;
  223. return;
  224. }
  225. else if (vertexCount == 0)
  226. {
  227. AZ_Error("CalculateSkinnedMeshTotalThreadsPerDimension", false, "Cannot skin mesh with 0 vertices.");
  228. xThreads = 0;
  229. yThreads = 0;
  230. return;
  231. }
  232. // Get the minimum number of threads in the y dimension needed to cover all the vertices in the mesh
  233. yThreads = vertexCount % maxVerticesPerDimension != 0 ? vertexCount / maxVerticesPerDimension + 1 : vertexCount / maxVerticesPerDimension;
  234. // Divide the total number of threads across y dimensions, rounding the number of xThreads up to cover any remainder
  235. xThreads = 1 + ((vertexCount - 1) / yThreads);
  236. }
  237. } // namespace Render
  238. } // namespace AZ