Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting "Creating UObjects while Collecting Garbage is not allowed!" when LoadSkeletalMeshRecursiveAsync is invoked from background thread. #39

Open
peetonn opened this issue Jul 14, 2022 · 1 comment

Comments

@peetonn
Copy link

peetonn commented Jul 14, 2022

I'm getting this random crash when UglTFRuntimeAsset::LoadSkeletalMeshRecursiveAsync() is invoked from a lambda running on a background thread (std::thread):

Assertion failed: !IsGarbageCollecting() [File:E:/UnrealEngine/UE_Main/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp] [Line: 2367] Unable to create new object: AssetImportData /Engine/Transient.SkeletalMesh_0.AssetImportData. Creating UObjects while Collecting Garbage is not allowed!

UE4Editor_Core!AssertFailedImplV() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\Core\Private\Misc\AssertionMacros.cpp:104]
UE4Editor_Core!FDebug::CheckVerifyFailedImpl() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\Core\Private\Misc\AssertionMacros.cpp:461]
UE4Editor_CoreUObject!<lambda_2bcb0bc4dbc924c8a72f269ef2bcdaae>::operator()() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectGlobals.cpp:2366]
UE4Editor_CoreUObject!StaticAllocateObject() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectGlobals.cpp:2366]
UE4Editor_CoreUObject!StaticConstructObject_Internal() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectGlobals.cpp:3229]
UE4Editor_Engine!USkeletalMesh::PostInitProperties() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\Engine\Private\SkeletalMesh.cpp:449]
UE4Editor_CoreUObject!FObjectInitializer::PostConstructInit() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectGlobals.cpp:2891]
UE4Editor_CoreUObject!FObjectInitializer::~FObjectInitializer() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectGlobals.cpp:2744]
UE4Editor_CoreUObject!StaticConstructObject_Internal() [E:\UnrealEngine\UE_Main\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectGlobals.cpp:3235]
UE4Editor_glTFRuntime!FglTFRuntimeSkeletalMeshContext::FglTFRuntimeSkeletalMeshContext() [D:\wkspaces\MyProject\Plugins\MyGame\Social\glTFRuntime\Source\glTFRuntime\Public\glTFRuntimeParser.h:783]
UE4Editor_glTFRuntime!FglTFRuntimeParser::LoadSkeletalMeshRecursiveAsync() [D:\wkspaces\MyProject\Plugins\MyGame\Social\glTFRuntime\Source\glTFRuntime\Private\glTFRuntimeParserSkeletalMeshes.cpp:1210]
UE4Editor_glTFRuntime!UglTFRuntimeAsset::LoadSkeletalMeshRecursiveAsync() [D:\wkspaces\MyProject\Plugins\MyGame\Social\glTFRuntime\Source\glTFRuntime\Private\glTFRuntimeAsset.cpp:306]
UE4Editor_MyGame!<lambda_92d524716833cd50d8d71ef497b5b91a>::operator()() [D:\wkspaces\MyProject\Plugins\MyGame\Social\MyGame\Source\MyGame\CharacterAvatarLoader\CharacterAvatarLoader.cpp:592]
UE4Editor_MyGame!journee::utility::PriorityThreadPool::processJob() [D:\wkspaces\MyProject\Plugins\MyGame\Social\MyGame\Source\MyGame\Utility\PriorityThreadPool.cpp:126]
UE4Editor_MyGame!std::thread::_Invoke<std::tuple<std::_Binder<std::_Unforced,void (__cdecl journee::utility::PriorityThreadPool::*)(unsigned int),journee::utility::PriorityThreadPool *,unsigned __int64 &> >,0>() [C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\INCLUDE\thread:56]
ucrtbase
kernel32
ntdll

I'm not entirely sure how shall I avoid this? Do I need to check every time for garbage collection before lambda is executed? Seems like a lot of additional checking and code (for postponing lambda execution). There must be some other way of doing it in Unreal maybe?

@peetonn peetonn changed the title Getting "" when LoadSkeletalMeshRecursiveAsync invoked from background thread. Getting "Creating UObjects while Collecting Garbage is not allowed!" when LoadSkeletalMeshRecursiveAsync invoked from background thread. Jul 14, 2022
@peetonn peetonn changed the title Getting "Creating UObjects while Collecting Garbage is not allowed!" when LoadSkeletalMeshRecursiveAsync invoked from background thread. Getting "Creating UObjects while Collecting Garbage is not allowed!" when LoadSkeletalMeshRecursiveAsync is invoked from background thread. Jul 14, 2022
@andreas-mantler-splicedinc
Copy link

andreas-mantler-splicedinc commented Jun 27, 2024

Hey. I know this is an old issue, but we also encountered GC problems importing skeletal meshes in our project.
The main problem seems to be that the glTFRuntimeParser gets kicked off to a worker thread when calling the async API, but internally it creates and references various UObjects internally, which is problematic in Unreal. UObjects should only really be handled on the game thread. We worked around this locally in UglTFRuntimeFunctionLibrary::glTFLoadAssetFromFilenameAsync by moving the call to FglTFRuntimeParser::FromFilename back to the game thread, while keeping the task completion async like this:

void UglTFRuntimeFunctionLibrary::glTFLoadAssetFromFilenameAsync(const FString& Filename, const bool bPathRelativeToContent, const FglTFRuntimeConfig& LoaderConfig, const FglTFRuntimeHttpResponse& Completed)
{
	UglTFRuntimeAsset* Asset = NewObject<UglTFRuntimeAsset>();
	if (!Asset)
	{
		Completed.ExecuteIfBound(nullptr);
		return;
	}

	Asset->RuntimeContextObject = LoaderConfig.RuntimeContextObject;
	Asset->RuntimeContextString = LoaderConfig.RuntimeContextString;

	// Annoying copy, but we do not want to remove the const
	FglTFRuntimeConfig OverrideConfig = LoaderConfig;

	if (bPathRelativeToContent)
	{
		OverrideConfig.bSearchContentDir = true;
	}

	// The parser is currently creating and referencing various UObjects, so it may not run on a worker thread,
	// or it will interfere with the GC. See also related open Github issue: https://github.com/rdeioris/glTFRuntime/issues/39
	TSharedPtr<FglTFRuntimeParser> Parser = FglTFRuntimeParser::FromFilename(Filename, OverrideConfig);

	Async(EAsyncExecution::Thread, [Parser, Asset, Completed]()
		{
			FGraphEventRef Task = FFunctionGraphTask::CreateAndDispatchWhenReady([Parser, Asset, Completed]()
				{
					if (Parser.IsValid() && Asset->SetParser(Parser.ToSharedRef()))
					{
						Completed.ExecuteIfBound(Asset);
					}
					else
					{
						Completed.ExecuteIfBound(nullptr);
					}
				}, TStatId(), nullptr, ENamedThreads::GameThread);
			FTaskGraphInterface::Get().WaitUntilTaskCompletes(Task);
		});
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants