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.
This commit is contained in:
yuriy0
2021-06-21 18:19:23 -04:00
committed by GitHub
parent b032177418
commit 704c127e34
@@ -526,6 +526,20 @@ namespace AZ
}
}
template<class X>
void swizzle_unique(AZStd::vector<X>& values, const AZStd::vector<size_t>& indices)
{
AZStd::vector<X> 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<size_t> 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