SkinnedMeshDispatchItem.cpp 15 KB

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