From 704c127e34fb67273d9bc9b953b21c2e13b85921 Mon Sep 17 00:00:00 2001 From: yuriy0 Date: Mon, 21 Jun 2021 18:19:23 -0400 Subject: [PATCH] If there are too many wrinkle masks, discard the lowest weight ones (#988) Since the shader supports a fixed maximum number of active wrinkle masks, if the number of active wrinkle masks is larger than that, it will crash where wrinkleMaskObjectSrg->SetImageArray is called with an array_view which is larger than the target image array. We need a sensible method of truncating the number of masks. One such method is to discard the lowest weight masks. --- .../Code/Source/AtomActorInstance.cpp | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index 0c331740e6..8486a46cd0 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -526,6 +526,20 @@ namespace AZ } } + template + void swizzle_unique(AZStd::vector& values, const AZStd::vector& indices) + { + AZStd::vector out; + out.reserve(indices.size()); + + for (size_t i : indices) + { + out.push_back(AZStd::move(values[i])); + } + + values = AZStd::move(out); + } + void AtomActorInstance::OnUpdateSkinningMatrices() { if (m_skinnedMeshRenderProxy.IsValid()) @@ -584,6 +598,30 @@ namespace AZ } } } + + AZ_Assert(m_wrinkleMasks.size() == m_wrinkleMaskWeights.size(), "Must have equal # of masks and weights"); + + // If there's too many masks, truncate + if (m_wrinkleMasks.size() > s_maxActiveWrinkleMasks) + { + // Build a remapping of indices (because we want to sort two vectors) + AZStd::vector remapped; + remapped.resize_no_construct(m_wrinkleMasks.size()); + std::iota(remapped.begin(), remapped.end(), 0); + + // Sort index remapping by weight (highest first) + std::sort(remapped.begin(), remapped.end(), [&](size_t ia, size_t ib) { + return m_wrinkleMaskWeights[ia] > m_wrinkleMaskWeights[ib]; + }); + + // Truncate indices list + remapped.resize(s_maxActiveWrinkleMasks); + + // Remap wrinkle masks list and weights list + swizzle_unique(m_wrinkleMasks, remapped); + swizzle_unique(m_wrinkleMaskWeights, remapped); + } + m_skinnedMeshRenderProxy->SetMorphTargetWeights(lodIndex, m_morphTargetWeights); // Until EMotionFX and Atom lods are synchronized [ATOM-13564] we don't know which EMotionFX lod to pull the weights from