ResetOwnedComponents causes components to be dropped from actor

Hello - I’m running into an issue with several actors in our game failing to register components properly. I’ve tracked the issue back to AActor::ResetOwnedComponents() dropping elements from the OwnedComponents list. This seems to be due to the components being marked with EInternalObjectFlags::AsyncLoadingPhase1 when ResetOwnedComponents() is called, which means they get filtered out from the lambda that adds the components back into the actor’s OwnedComponents list. We are only seeing this on console builds - Windows builds appear to load everything just fine.

For the callstack I’ve provided - the actor in question is an ACineCameraActor owned by a level sequence package, but I’ve seen the problem with other actors which ends up crashing later in the ability system. In the callstack above - the engine is loading the game instance and a bunch of dependencies of it. If I run in full debug - I hit this checkSlow in RegisterComponentsWithWorld()

checkSlow(MyOwner == nullptr || MyOwner->OwnsComponent(this));

Steps to Reproduce

Hi there,

Is the ACineCameraActor a blueprint of a ACineCameraActor,

Is the UCineCameraComponent within it being registered?

Are there any additional componets other than UCineCameraComponent that are not registering on that actor?

Regards

Keegan GIbson

Hi,

I haven’t been able to reproduce this on console or on windows.

I tested in a simple scene, one with a level sequence in the world set to auto play and one where it was being created from the game instance and played.

I suspect what might be the cause is when the object is being requested to load.

How are the Game instance and this Level Seqauence related to one another, is the level instance being referenced by the game instance?

From looking at the codebase, and your call stack, it appears it trying to load the level sequence during LoadObject/Static LOad Object (line 1142) on my tests it was being created from Initialise Standalone.

GameEngine.cpp:1138

`// Create game instance. For GameEngine, this should be the only GameInstance that ever gets created.
{
TRACE_CPUPROFILER_EVENT_SCOPE(InitGameInstance);
FSoftClassPath GameInstanceClassName = GetDefault()->GameInstanceClass;
UClass* GameInstanceClass = (GameInstanceClassName.IsValid() ? LoadObject(NULL, *GameInstanceClassName.ToString()) : UGameInstance::StaticClass());

if (GameInstanceClass == nullptr)
{
UE_LOG(LogEngine, Error, TEXT(“Unable to load GameInstance Class ‘%s’. Falling back to generic UGameInstance.”), *GameInstanceClassName.ToString());
GameInstanceClass = UGameInstance::StaticClass();
}

GameInstance = NewObject(this, GameInstanceClass);

GameInstance->InitializeStandalone();
}`

If you could provide me a little more context on how the level sequence is related to the game instance and being referenced during its static load of the asset, or a minimal sample project that reproduces the error, I could debug further on my end.

----

Some information about the changes to the Async Loading in 5.5.

When FindObject is called outside of PostLoad functions, all objects that still have the EInternalObjectFlags::AsyncLoading are filtered out so you should not be able to get a pointer to an object that still has a RF_NeedPostLoad flag.

When you resolve a TSoftObjectPtr or call FindObject from within a PostLoad call, you may end up with objects that are not fully loaded yet because there is a scope in the loader that is active during PostLoads to give you access to objects that are being loaded.

This is actually a feature to give PostLoad access to other objects it might want to interact with. The idea in those cases is that you are responsible for calling ConditionalPostLoad on other objects you interact with, since by definition, during PostLoad you are part of the loader.

5.5 improved object visibility in zenloader (the default loader used when the game is packaged) by dividing it into 2 different visibility phases, when inside a PostLoad, FindObject will not be able to see objects still being loaded on the async loading thread (i.e. being serialized) to avoid potential race conditions.

In 5.5, objects being serialized on the async loading thread are in EInternalObjectFlags::AsyncLoadingPhase1.

When they are ready to be accessed by the game thread, we switch them to EInternalObjectFlags::AsyncLoadingPhase2.

Kind Regards

Keegan Gibson

Hello,

Thank you for the extra information, I have been able to reproduce the bug on a windows and a console build, with a similiar setup and seeing the same result with the components not being registered. You mentioned you noticed this on other objects awell, are they also referenced via a data table or a similliar chain?

I’ll submit a bug report, as it seems to be its related to the hard references through that particular chain, being created when the game instance asset is loaded.

If this is shared global data to be referenced during the game, you may wish to consider moving it to a Primary Data asset or assets that contain shared game data and utalising soft object and class pointers will help break the hard reference chain.

You may also want to consider depending on the type of data, moving the entries out of the data asset into data assets.

Documentation Links:

https://dev.epicgames.com/documentation/en\-us/unreal\-engine/asset\-management\-in\-unreal\-engine

I’ll provide a bug tracking link once one becomes available.

Kind Regards

Keegan GIbson

H ithere,

The bug ticket was marked as not an engine bug, referencing assets this way is unintended.

Breaking the hard reference chain, and moving the shared game data to a primary asset should fix your issues.

The lyra sample project has an example of storing game data.

See ULyraGameData and ULyraAssetManager for examples of this pattern.

I will go ahead and close this case now. Feel free to open another if you have any further questions

Kind Regards

Keegan Gibson

Hello!

No this is not a blueprint as far as I can tell - the UObject’s ClassPrivate points to a class that is marked as CLASS_Native.

There’s two components on the actor that are not getting registered (which is all of them as far as I can tell). There’s a SceneObject root component and a CameraComponent. Both are marked as AsyncLoadingPhase1 so the function never re-adds them back into OwnedComponents.

Having not looked into the AsyncLoading2 code too terribly much - is there a way I can why the AsyncLoadingPhase1 flag isn’t cleared before ConditionalPostLoad is called? I assume there’s something that checks if subobjects have been fully loaded before doing this?

Hello thank you for the info!

It looks like there’s just a straight ref chain from the game instance to this level sequence by way of a data table -> hard ref to a weapon -> hard ref to animation bp -> level sequence. I’ve run some tests with setting a breakpoint in StaticLoadObjectInternal, and we don’t have any cases of attempting to load from a PostLoad() call. After calling load on the game instance, there are just a few instances of loading from constructor statics until I hit the error. I can try to disentangle this ref chain so it’s not loading in so much with the game instance. However, this still might indicate an issue with the loader if it’s possible for PostLoad to get called without subobjects having been fully processed.

Is there anything else I can check for to test if this is the case?

Ah cool I’m glad you were able to get a repro of the issue, and it’s good to know that just fixing up the reference chain should resolve the issue.