Symptom: Assembling a MetaHuman (or importing a character) with a big level open takes close to an hour and the editor locks up. The log fills with every actor in the level being unregistered and re-registered, over and over (UnregisterComponent ... Not registered. Aborting. for the same actors each round). In an empty level the same Assemble is quick.
Our numbers (same project, same character, whole town open):
| Before | After fix | |
|---|---|---|
| Full-level refresh rounds during Assemble | 174 | 0 |
| Editor lockups | Locked the whole time | 0 |
| Assemble time | close to an hour | about 5 minutes (mostly texture baking) |
Cause (caught with WinDbg, breakpoint on FGlobalComponentReregisterContext’s constructor during an Assemble, 37 hits):
- 35 hits:
TryUnpackObject -> TryMoveObjectToAssetPackage -> UPackageTools::FindOrCreatePackageForAssetType -> UPackageTools::LoadPackage - 1 hit:
WriteActorBlueprintHelper(same path) - 1 hit: the outer context in
UMetaHumanCollection::UnpackAssets
UPackageTools::LoadPackage wraps every call in a FGlobalComponentReregisterContext (detach + reattach every component in every open level) - even when the package is brand new and doesn’t exist on disk yet. The MetaHuman build calls it once per asset it writes. Each round costs as much as your level is big, so a large level turns seconds into most of an hour. Epic’s own comment in MetaHumanCollection.cpp says the inner contexts were meant to be no-ops; in practice they still run.
Fix (no engine rebuild needed):
- Copy the engine plugin
Engine/Plugins/MetaHuman/MetaHumanCharacterinto your project’sPlugins/folder (project plugins override engine plugins). You only needSource,Config,Resourcesand the.uplugin; forContentwe used a folder junction back to the engine copy to save ~6.6 GB. - Add a small helper that does the same find-or-create as
UPackageTools::FindOrCreatePackageForAssetType, but without the re-register. The core of it:
// Already in memory, or on disk: plain load (no global component re-register)
Package = FindPackage(nullptr, *PackageNameAsString);
if (!Package && FPackageName::DoesPackageExist(PackageNameAsString))
{
Package = ::LoadPackage(nullptr, *PackageNameAsString, LOAD_NoWarn);
}
if (!Package)
{
Package = NewObject<UPackage>(nullptr, LongPackageName, RF_Public);
}
(Keep the same asset-registry class check and unique-name fallback the engine version does around this.)
3. Point the 5 call sites in the plugin at your helper instead of UPackageTools::FindOrCreatePackageForAssetType:
MetaHumanCharacterPalette/Private/MetaHumanUnpackUtilities.cpp(1)MetaHumanDefaultEditorPipeline/Private/MetaHumanDefaultEditorPipelineBase.cpp(2)MetaHumanCharacterEditor/Private/Subsystem/MetaHumanCharacterBuild.cpp(2)
- In
MetaHumanCharacterPalette/Private/MetaHumanCollection.cpp, comment out the outerFGlobalComponentReregisterContext ComponentReregisterContext;inUnpackAssets- it only existed to soak up the inner ones. - Close the editor, build your project’s Editor target, reopen.
Undo: delete the project’s Plugins/MetaHumanCharacter folder (remove the junction, don’t delete through it). Re-apply after engine updates.
Epic: the real fix would be for UPackageTools::FindOrCreatePackageForAssetType (or the MetaHuman build) to skip the global re-register when the package isn’t loaded / doesn’t exist yet.