Adding minimal emitter to Virtualized Niagara system asset can crash editor

If your project has asset virtualization setup, adding minimal emitter to a virtualized Niagara system asset with an emitter that has black image texture as thumbnail can make the editor crash.

I confirmed that this bug occurs in the latest 5.6.1 and 5.5.

The crash occurs if any of the emitter within your virtualized Niagara system asset contains a black image texture as thumbnail.

You can view an emitter’s thumbnail by clicking the triangle show in the red box of above image.

The Minimal Emitter from the default template has black image.

Many other default templates have null thumbnails as shown in above image.

You can generate thumbnails by clicking the left down picture icon. This will set the emitter’s thumbnail to the image shown at preview panel.

However, I couldn’t find any way to make the emitter thumbnail to null.

Once an emitter’s thumbnail is set, it can’t go back to null thumbnail.

Steps to reproduce crash.

  1. Create an Unreal Engine Project with asset virtualization setup.
  2. Open the project with Unreal Engine Editor.
  3. Add a Niagara System asset from some default engine template.
  4. Open the asset and within Niagara System Overview tab, right click the mouse and click “add minimal emitter”
  5. Save the Niagara System asset. Now the asset has an emitter with black thumb nail image.
  6. Submit the asset file to Perforce Depot. The asset should be virtualized.
  7. Close the editor and re-open the project with the editor.
  8. Open the Niagara System asset again.
  9. Add another minimal emitter as you did in step 4.
  10. The editor crashes

This is a bug in the Unreal Engine.

After step 5, both default Minimal Emitter template asset, and the virtualized Niagar system asset in the project contains black thumbnail image.

If you run DumpPackagePayloadInfo [file path] console command on both assets, you con confirm that the same payload id(payload id of the black thumbnail texture’s bulkdata) are shown on both command outputs.

The command output of Minimal Emitter template asset tells that the payload is local.

The Command output of the virtualized asset tells that the payload is virtualized.

Adding minimal emitter within editor executes function StaticDuplicateObjectEx(FObjectDuplicationParameters &) UObjectGlobals.cpp:3151

It tries to copy the minimal emitter instance from the template asset, which is not virtualized, to the project’s Niagara system asset, which is virtualized.

within the function a serializer is reading from a UObject binary data that is created without any virtualization, but when it loads the thumbnail texture UObject, it treats the bulkdata payload of the thumbnail texture as virtualized, resulting wrong binary data offset value within the serializer.

This causes loading wrong flag value in the texture object, which causes execution of minmap creation of the thumbnail texture, which then crashes the editor.

The problem is that StaticDuplicateObjectEx decides if the payload is virtualized or not by checking the payload content id table of the destination asset package, not the table from the source asset package.

The binary data is from non-virtualized source asset, but the function considers the payload is virtualized as the payload id is registered as a virtualized payload in the destination asset package.

To avoid the crash, you can just generate some non-black thumbnail to every emitter that contains the black thumbnail before saving the system.

I have only found the case involving Niagara system assets, but there could be more cases of side effects due to this bug.

Bellow is the callstack just before the crash

UTexture2D::Serialize(FArchive &) Texture2D.cpp:486
StaticDuplicateObjectEx(FObjectDuplicationParameters &) UObjectGlobals.cpp:3151
StaticDuplicateObject(const UObject *, UObject *, FName, EObjectFlags, UClass *, Type, EInternalObjectFlags) UObjectGlobals.cpp:3015
UNiagaraEmitter::CreateWithParentAndOwner(FVersionedNiagaraEmitter, UObject *, FName, EObjectFlags) NiagaraEmitter.cpp:1101
UNiagaraSystem::AddEmitterHandle(UNiagaraEmitter &, FName, FGuid) NiagaraSystem.cpp:2774
FNiagaraEditorUtilities::AddEmitterToSystem(UNiagaraSystem &, UNiagaraEmitter &, FGuid, bool) NiagaraEditorUtilities.cpp:2110
FNiagaraSystemViewModel::AddEmitter(UNiagaraEmitter &, FGuid) NiagaraSystemViewModel.cpp:549
FNiagaraSystemViewModel::AddEmitterFromAssetData(const FAssetData &) NiagaraSystemViewModel.cpp:531
FNiagaraSystemViewModel::AddMinimalEmitter() NiagaraSystemViewModel.cpp:700
SNiagaraOverviewGraph::OnCreateEmptyEmitter() SNiagaraOverviewGraph.cpp:286
TBaseSPMethodDelegateInstance::ExecuteIfSafe() DelegateInstancesImpl.h:299
TDelegate::ExecuteIfBound<…>() DelegateSignatureImpl.inl:634
SMenuEntryBlock::OnClicked(bool) SMenuEntryBlock.cpp:1175
SMenuEntryBlock::OnMenuItemButtonClicked() SMenuEntryBlock.cpp:1129
UE::Core::Private::Tuple::TTupleBase::ApplyAfter<…>(FReply (SMenuEntryBlock::*&)(), SMenuEntryBlock *&) Tuple.h:317
TBaseSPMethodDelegateInstance::Execute() DelegateInstancesImpl.h:282
SButton::ExecuteOnClick() SButton.cpp:467
SButton::OnMouseButtonUp(const FGeometry &, const FPointerEvent &) SButton.cpp:392
SMenuEntryButton::OnMouseButtonUp(const FGeometry &, const FPointerEvent &) SMenuEntryBlock.cpp:443
`FSlateApplication::RoutePointerUpEvent'::`8'::<lambda_2>::operator()(const FArrangedWidget &,const FPointerEvent &) SlateApplication.cpp:5346
FEventRouter::Route<FReply,FEventRouter::FToLeafmostPolicy,FPointerEvent,`FSlateApplication::RoutePointerUpEvent'::`8'::<lambda_2> >(FSlateApplication *,FToLeafmostPolicy,FPointerEvent,const <lambda_2> &,ESlateDebuggingInputEvent) SlateApplication.cpp:456
FSlateApplication::RoutePointerUpEvent(const FWidgetPath &, const FPointerEvent &) SlateApplication.cpp:5332
FSlateApplication::ProcessMouseButtonUpEvent(const FPointerEvent &) SlateApplication.cpp:5917
FSlateApplication::OnMouseUp(Type, TVector2<…>) SlateApplication.cpp:5873
FWindowsApplication::ProcessDeferredMessage(const FDeferredWindowsMessage &) WindowsApplication.cpp:2271
FWindowsApplication::DeferMessage(TSharedPtr<…> &, HWND__ *, unsigned int, unsigned long long, long long, int, int, unsigned int) WindowsApplication.cpp:2783
FWindowsApplication::ProcessMessage(HWND__ *, unsigned int, unsigned long long, long long) WindowsApplication.cpp:1122
WindowsApplication_WndProc(HWND__ *, unsigned int, unsigned long long, long long) WindowsApplication.cpp:939
FWindowsApplication::AppWndProc(HWND__ *, unsigned int, unsigned long long, long long) WindowsApplication.cpp:944
WinPumpMessages() WindowsPlatformApplicationMisc.cpp:116
FWindowsPlatformApplicationMisc::PumpMessages(bool) WindowsPlatformApplicationMisc.cpp:145
FEngineLoop::Tick() LaunchEngineLoop.cpp:5800
EngineTick() Launch.cpp:69
GuardedMain(const wchar_t *) Launch.cpp:190
LaunchWindowsStartup(HINSTANCE__ *, HINSTANCE__ *, char *, int, const wchar_t *) LaunchWindows.cpp:266
WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) LaunchWindows.cpp:317

This is the part of the code that overwrites flag variable used by serializer that decides whether the payload is virtual.

It is using FPackageTrailer instance of the destination asset package, when it is supposed to use the instance of the source asset package.

So in conclusion,

If you are using Virtual Asset in your project. Do not leave black thumbnail image in your Niagara emitters, just replace those with some other images.

Also, there could be some unexpected side effects on any process that copies UObject instances from a non-virtualized asset to a virtualized asset.

I trtied to report this issue on https://unrealbugsubmissions.epicgames.com/ this page as the official guide line tells, but the page is not working now. Clkicking submit button takes forever to load.