# RigLogic: half-float restore keeps a dead fp32 copy of every FloatArray resident (clear() does not free) 

**URL:** <https://forums.unrealengine.com/t/riglogic-half-float-restore-keeps-a-dead-fp32-copy-of-every-floatarray-resident-clear-does-not-free/2855751>\
**Category:** Character & Animation\
**Tags:** Epic-Pro-Support, question, metahuman\
**Created:** [October 12, 2026, 10:25pm UTC](https://forums.unrealengine.com/t/riglogic-half-float-restore-keeps-a-dead-fp32-copy-of-every-floatarray-resident-clear-does-not-free/2855751 "2026-10-12T22:25:07Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![anonymous-edc](https://d3kjluh73b9h9o.cloudfront.net/original/4X/2/8/5/28502927f1d97ba1051b3b47ccfe17233bf41ef0.png) [@anonymous-edc](https://forums.unrealengine.com/u/anonymous-edc)\
**Post date:** [October 12, 2026, 10:25pm UTC](https://forums.unrealengine.com/t/riglogic-half-float-restore-keeps-a-dead-fp32-copy-of-every-floatarray-resident-clear-does-not-free/2855751/1 "2026-10-12T22:25:07Z")

</div>

FloatArrayDeserializer\<std::uint16\_t\> reads the archived fp32 data, converts it to fp16, then calls instance.fp32.clear(). clear() destroys the elements but keeps the capacity, so every FloatArray restored in half-float mode keeps a dead fp32 buffer twice the size of its live fp16 data. This covers the joint storage values and NeuralNet::flatData.

In our content that is about 3.8 MB per MetaHuman head, which adds up quickly with several heads resident on console. Peak load memory is unaffected; this is purely memory that should have been returned after load.

Still present in //UE5/Main and //UE6/Main (checked against UE6/Main CL 58437171).

Suggested fix (we’ve taken this locally in 5.8). In FloatArrayDeserializer\<std::uint16\_t, TF256, TF128\>::operator():

- instance.fp32.clear();

+ AlignedVector\<float\>{instance.fp32.get\_allocator()}.swap(instance.fp32);

The temporary is built from the same allocator (same MemoryResource and alignment, so the PolyAllocators compare equal), which makes the swap well-defined, and the old buffer is freed when the temporary is destroyed. shrink\_to\_fit() would also work but is non-binding.

FloatArraySerializer\<std::uint16\_t\> has the same clear() after archiving fp32. That only runs on save/dump so it matters less, but the same swap would be consistent there.

The builder paths (BPCMJointsBuilder / QuaternionJointsBuilder) write straight into fp16 and are not affected - only the restore path from cooked data is.

```auto
- instance.fp32.clear();
+ AlignedVector<float>{instance.fp32.get_allocator()}.swap(instance.fp32);

```

[Image Removed]

The temporary is built from the same allocator (same MemoryResource and alignment, so the PolyAllocators compare equal), which makes the swap well-defined, and the old buffer is freed when the temporary is destroyed. shrink\_to\_fit() would also work but is non-binding.

FloatArraySerializer\<std::uint16\_t\> has the same clear() after archiving fp32. That only runs on save/dump so it matters less, but the same swap would be consistent there.

The builder paths (BPCMJointsBuilder / QuaternionJointsBuilder) write straight into fp16 and are not affected - only the restore path from cooked data is.

[Attachment Removed]

---

<div class="post-metadata">

**Author:** ![andrean.franc](https://d3kjluh73b9h9o.cloudfront.net/original/4X/2/8/5/28502927f1d97ba1051b3b47ccfe17233bf41ef0.png) [@andrean.franc](https://forums.unrealengine.com/u/andrean.franc)\
**Post date:** [October 15, 2026, 2:38pm UTC](https://forums.unrealengine.com/t/riglogic-half-float-restore-keeps-a-dead-fp32-copy-of-every-floatarray-resident-clear-does-not-free/2855751/2 "2026-10-15T14:38:31Z")

</div>

Thank you for the report, yes, we confirm this issue is valid. We have applied a fix, which will be released with UE 5.8.5.

Until then, the p4 streams already contain the patch.

[Attachment Removed]
