diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningCS.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningCS.azsl index fc5684fe7f..99d38463c7 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningCS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningCS.azsl @@ -14,6 +14,15 @@ option enum class SkinningMethod { LinearSkinning, DualQuaternion } o_skinningMe option bool o_applyMorphTargets = false; option bool o_applyColorMorphTargets = false; +float3 ReadFloat3FromFloatBuffer(Buffer buffer, uint index) +{ + float3 result; + result.x = buffer[index * 3]; + result.y = buffer[index * 3 + 1]; + result.z = buffer[index * 3 + 2]; + return result; +} + // Apply a morph target delta with three components void ApplyMorphTargetDelta(uint streamOffset, uint vertexIndex, inout float3 modifiedValue) { @@ -162,10 +171,10 @@ void MainCS(uint3 thread_id: SV_DispatchThreadID) return; } - float3 position = InstanceSrg::m_sourcePositions[i]; - float3 normal = InstanceSrg::m_sourceNormals[i]; - float4 tangent = InstanceSrg::m_sourceTangents[i]; - float3 bitangent = InstanceSrg::m_sourceBiTangents[i]; + float3 position = ReadFloat3FromFloatBuffer(InstanceSrg::m_sourcePositions, i); + float3 normal = ReadFloat3FromFloatBuffer(InstanceSrg::m_sourceNormals, i); + float4 tangent = InstanceSrg::m_sourceTangents[i]; + float3 bitangent = ReadFloat3FromFloatBuffer(InstanceSrg::m_sourceBiTangents, i); // Four indices, 16-bits each, stored in 2 32-bit uints uint2 rawIndices = InstanceSrg::m_sourceBlendIndices.Load2(i * 8); diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli index 95802ddbb7..066cefdc37 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli @@ -20,10 +20,12 @@ ShaderResourceGroup InstanceSrg : SRG_PerDraw uint m_totalNumberOfThreadsX; // Per-model input - Buffer m_sourcePositions; // POSITION 0 - Buffer m_sourceNormals; // NORMAL 0 + // Positions, normals, and bitangents are all 3-component per-vertex buffers, + // but Metal doesn't support float3 buffers so Buffer is used instead + Buffer m_sourcePositions; // POSITION 0 + Buffer m_sourceNormals; // NORMAL 0 Buffer m_sourceTangents; // TANGENT 0 - Buffer m_sourceBiTangents; // BITANGENT 0 + Buffer m_sourceBiTangents; // BITANGENT 0 ByteAddressBuffer m_sourceBlendIndices; // BLENDINDICES 0 Buffer m_sourceBlendWeights; // BLENDWEIGHTS 0 diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp index 75d079c937..8ef82dfac4 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshInputBuffers.cpp @@ -118,7 +118,29 @@ namespace AZ } m_inputBufferAssets[static_cast(inputStream)] = bufferAsset; - m_inputBuffers[static_cast(inputStream)] = RPI::Buffer::FindOrCreate(bufferAsset); + Data::Instance buffer = RPI::Buffer::FindOrCreate(bufferAsset); + m_inputBuffers[static_cast(inputStream)] = buffer; + + // Create a buffer view to use as input to the skinning shader + AZ::RHI::Ptr bufferView = RHI::Factory::Get().CreateBufferView(); + bufferView->SetName(Name{ AZStd::string(buffer->GetBufferView()->GetName().GetStringView()) + "_SkinningInputBufferView" }); + RHI::BufferViewDescriptor bufferViewDescriptor = bufferAsset->GetBufferViewDescriptor(); + + // 3-component float buffers are not supported on metal for non-input assembly buffer views, so use a float view instead + if (bufferViewDescriptor.m_elementFormat == RHI::Format::R32G32B32_FLOAT) + { + // Use one float per element, with 3x as many elements + bufferViewDescriptor = RHI::BufferViewDescriptor::CreateTyped( + bufferViewDescriptor.m_elementOffset * 3, bufferViewDescriptor.m_elementCount * 3, RHI::Format::R32_FLOAT); + } + + [[maybe_unused]] RHI::ResultCode resultCode = + bufferView->Init(*buffer->GetRHIBuffer(), bufferViewDescriptor); + AZ_Error( + "SkinnedMeshInputBuffers", resultCode == RHI::ResultCode::Success, + "Failed to initialize buffer view for skinned mesh input."); + + m_bufferViews[static_cast(inputStream)] = bufferView; } void SkinnedMeshInputLod::SetStaticBufferAsset(const Data::Asset bufferAsset, SkinnedMeshStaticVertexStreams staticStream) @@ -315,7 +337,7 @@ namespace AZ [[maybe_unused]] bool success = false; if (m_lods[lodIndex].m_inputBuffers[inputStream]) { - success = perInstanceSRG->SetBufferView(srgIndex, m_lods[lodIndex].m_inputBuffers[inputStream]->GetBufferView()); + success = perInstanceSRG->SetBufferView(srgIndex, m_lods[lodIndex].m_bufferViews[inputStream].get()); } AZ_Error("SkinnedMeshInputBuffers", success, "Failed to bind buffer view for %s", streamInfo.m_bufferName.GetCStr());