[UE5.1.1] Need Help Tracking Down Engine Crash Bug

I’m creating a photography game. When the player raises the camera, they can take a picture and the last picture taken is displayed in a widget in the corner of the screen. Sometimes the game/editor will crash when raising the camera after at least one picture has been taken. Here is the crash report:

Unhandled Exception: EXCEPTION_ACCESS_VIOLATION 0x0000000000000000

UnrealEditor_Engine!FUniformExpressionSet::FillUniformBuffer() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\Materials\MaterialUniformExpressions.cpp:936]
UnrealEditor_Engine!<lambda_25add83bda8d5688509793822c85efe4>::operator()() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\Materials\MaterialShared.cpp:3865]
UnrealEditor_Engine!TGraphTask<TFunctionGraphTaskImpl<void __cdecl(void),0> >::ExecuteTask() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Async\TaskGraphInterfaces.h:1348]
UnrealEditor_Core!<lambda_13c427d0bfcf321a066cb5a2badfbc27>::operator()() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:2051]
UnrealEditor_Core!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<<lambda_17c904c32264d0348d15245fba0e1bff>,0>::CallAndMove() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Async\Fundamental\TaskDelegate.h:171]
UnrealEditor_Core!LowLevelTasks::FTask::ExecuteTask() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Async\Fundamental\Task.h:656]
UnrealEditor_Core!LowLevelTasks::FScheduler::ExecuteTask() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Async\Fundamental\Scheduler.cpp:162]
UnrealEditor_Core!LowLevelTasks::FScheduler::TryExecuteTaskFrom<&LowLevelTasks::TLocalQueueRegistry<1024>::TLocalQueue::DequeueGlobal,0>() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Async\Fundamental\Scheduler.cpp:361]
UnrealEditor_Core!LowLevelTasks::FScheduler::WorkerMain() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Async\Fundamental\Scheduler.cpp:402]
UnrealEditor_Core!UE::Core::Private::Function::TFunctionRefCaller<<lambda_0a548c7e497de3cc77a9e48080e1524f>,void __cdecl(void)>::Call() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Templates\Function.h:475]
UnrealEditor_Core!FThreadImpl::Run() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\HAL\Thread.cpp:67]
UnrealEditor_Core!FRunnableThreadWin::Run() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Windows\WindowsRunnableThread.cpp:149]

None of my code is shown in the crash log, but I believe I know the function that is leading to the crash. Here is the engine code where the crash is occurring (I’ve marked the precise line with “@CRASH”):

//...
const UTexture* Value = nullptr;
GetTextureValue(EMaterialTextureParameterType::Standard2D, ExpressionIndex, MaterialRenderContext,MaterialRenderContext.Material,Value);
if (Value)
{
    // Pre-application validity checks (explicit ensures to avoid needless string allocation)
    //const FMaterialUniformExpressionTextureParameter* TextureParameter = (Uniform2DTextureExpressions[ExpressionIndex]->GetType() == &FMaterialUniformExpressionTextureParameter::StaticType) ?
    //	&static_cast<const FMaterialUniformExpressionTextureParameter&>(*Uniform2DTextureExpressions[ExpressionIndex]) : nullptr;

    // gmartin: Trying to locate UE-23902
    if (!Value->IsValidLowLevel())
    {
        ensureMsgf(false, TEXT("Texture not valid! UE-23902! Parameter (%s)"), *Parameter.ParameterInfo.Name.ToString());
    }

    // Trying to track down a dangling pointer bug.
    checkf(
        Value->IsA<UTexture>(),
        TEXT("Expecting a UTexture! Name(%s), Type(%s), TextureParameter(%s), Expression(%d), Material(%s)"),
        *Value->GetName(), *Value->GetClass()->GetName(),
        *Parameter.ParameterInfo.Name.ToString(),
        ExpressionIndex,
        *MaterialRenderContext.Material.GetFriendlyName());

    // Do not allow external textures to be applied to normal texture samplers
    if (Value->GetMaterialType() == MCT_TextureExternal)  // @CRASH
    {
        FText MessageText = FText::Format(
            NSLOCTEXT("MaterialExpressions", "IncompatibleExternalTexture", " applied to a non-external Texture2D sampler. This may work by chance on some platforms but is not portable. Please change sampler type to 'External'. Parameter '{0}' (slot {1}) in material '{2}'"),
            FText::FromName(Parameter.ParameterInfo.GetName()),
            ExpressionIndex,
            FText::FromString(*MaterialRenderContext.Material.GetFriendlyName()));

        GLog->Logf(ELogVerbosity::Warning, TEXT("%s"), *MessageText.ToString());
    }
}
//...

I looked up the issue tracker tag referenced there (UE-23902), but it seems to be related to a crash caused by the CreateUniformBuffer method in the same file and is marked as fixed.

Here is the relevant code from my end that I believe is leading to the crash:

// Called with argument true each time the camera is raised, which is when the crash occurs:
void APhotoCameraEquipment::DisplayCameraHUD(bool bDisplay)
{
	if (bDisplay)
	{
		UGameInstance* GameInstance = GetGameInstance();
		CameraHUD = CreateWidget<UUserWidget>(GameInstance, CameraHUDClass, TEXT("CameraHUD"));
		if (CameraHUD != nullptr && !CameraHUD->IsInViewport())
		{
			CameraHUD->AddToViewport();
			LastPhotoImageWidget = CameraHUD->WidgetTree->FindWidget<UImage>(TEXT("LastPhotoImage"));
			DisplayLastPhoto();
		}
	}
	else
	{
		if (CameraHUD != nullptr && CameraHUD->IsInViewport()) {CameraHUD->RemoveFromParent();}
	}
}

void APhotoCameraEquipment::DisplayLastPhoto()
{
	FPhotoData LastPhoto = GetLastPhoto();
	UTextureRenderTarget2D* PhotoImage = LastPhoto.Image;

	if (LastPhotoImageWidget == nullptr) {return;}
	UMaterialInstanceDynamic* PhotoRenderMat = LastPhotoImageWidget->GetDynamicMaterial();

	if (PhotoRenderMat == nullptr || PhotoImage == nullptr) {return;}	
	PhotoRenderMat->SetTextureParameterValue(TEXT("PhotoRender"), PhotoImage);

	FVector2D DisplaySize = FVector2D(PhotoImage->SizeX/10, PhotoImage->SizeY/10);
	LastPhotoImageWidget->SetDesiredSizeOverride(DisplaySize);
	LastPhotoImageWidget->SetVisibility(ESlateVisibility::Visible);

	LogPhotoData(LastPhoto);
}

FPhotoData APhotoCameraEquipment::GetLastPhoto()
{
	if (Photos.IsEmpty()) {return FPhotoData();}
	return Photos[Photos.Num() - 1];
}

void APhotoCameraEquipment::LogPhotoData(FPhotoData Photo)
{
	FString PhotoString = FString::Printf(TEXT("Photo taken at %s:\nSubjects: "), *(Photo.TimeTaken.ToString()));
	float Score = 0;
	for (FPhotoSubjectData Subject : Photo.Subjects)
	{
		PhotoString.Append(Subject.Name.ToString());
		PhotoString.Append(FString(" | "));
		
		for (FPhotoSubjectPointOfInterest PointOfInterest : Subject.PointsOfInterest)
		{
			if (PointOfInterest.IsVisible()) {Score += PointOfInterest.ScoreValue;}
		}
	}
	PhotoString.Append(FString::Printf(TEXT("\nTotal Score: %f"), Score));
	UE_LOG(LogTemp, Warning, TEXT("%s"), *PhotoString);
}

I don’t exactly know if the fault for the crash lies with my code or the engine, but the fact that it seems fairly random, paired with all of the references to threads and async tasks in the crash report makes me think it’s due to some race condition in the engine itself. I’m not sure if I should spend time trying to track it down, or if I should just update the engine and hope that A, the bug is fixed, and B, updating doesn’t break anything else in my project.

1 Like

If you want to rule out an Engine Bug (they do happen, sometimes), then you should migrate the project into a fresh project.

If the problem persists, then it’s likely something you need to fix.

At the very least, migrating it will rule out 50% of the possibilities.
At the very best, it’ll fix your problem.

Hi Leomerya,

I’m not sure I follow what you’re saying. If I migrate everything into a fresh project on the same version of the engine, then it would be running the same code as the original project. Any bug present in either the engine code or my own in the original project would still be present in the migrated project, so I don’t see how the issue persisting would indicate much of anything in that case.

Sorry if I’m not understanding you properly!

Hello,

This is not necessarily so. There could be a broken reference.

I’ve done a little more digging into this. I was able to stop the editor from crashing by adding a check in DisplayLastPhoto() for PhotoImage->IsValidLowLevel(). Here is the new code:

void APhotoCameraEquipment::DisplayLastPhoto()
{
	if (LastPhotoImageWidget == nullptr)
	{
		UE_LOG(LogEquipment, Error, TEXT("PhotoCameraEquipment:: No LastPhotoImageWidget set!"));
		return;
	}

	FPhotoData LastPhoto = GetLastPhoto();
	UTextureRenderTarget2D* PhotoImage = LastPhoto.Image;
	if (PhotoImage == nullptr)
	{
		UE_LOG(LogEquipment, Error, TEXT("PhotoCameraEquipment:: LastPhoto has no image!"));
		return;
	}
	PhotoImage->UpdateResourceImmediate(false);

	if (!PhotoImage->IsValidLowLevel())
	{
		UE_LOG(LogEquipment, Error, TEXT("PhotoCameraEquipment:: PhotoImage is invalid!"));
		return;
	}

	UMaterialInstanceDynamic* PhotoRenderMat = LastPhotoImageWidget->GetDynamicMaterial();
	if (PhotoRenderMat == nullptr)
	{
		UE_LOG(LogEquipment, Error, TEXT("PhotoCameraEquipment:: LastPhotoImageWidget has no DynamicMaterial!"));
		return;
	}
	PhotoRenderMat->SetTextureParameterValue(TEXT("PhotoRender"), PhotoImage);

	FVector2D DisplaySize = FVector2D(PhotoImage->SizeX/10, PhotoImage->SizeY/10);
	LastPhotoImageWidget->SetDesiredSizeOverride(DisplaySize);
	LastPhotoImageWidget->SetVisibility(ESlateVisibility::Visible);

	LogPhotoData(LastPhoto);
}

I’ve now seen that when PhotoImage->IsValidLowLevel() is false, I also get a warning in the log that reads “LogUObjectArray: Warning: Empty slot” or “LogUObjectArray: Warning: Other object in slot” right before my error message that says “PhotoCameraEquipment:: PhotoImage is invalid!”. Those warnings are coming from this function in UObjectArray.cpp:

bool FUObjectArray::IsValid(const UObjectBase* Object) const 
{ 
	int32 Index = Object->InternalIndex;
	if( Index == INDEX_NONE )
	{
		UE_LOG(LogUObjectArray, Warning, TEXT("Object is not in global object array") );
		return false;
	}
	if( !ObjObjects.IsValidIndex(Index))
	{
		UE_LOG(LogUObjectArray, Warning, TEXT("Invalid object index %i"), Index );
		return false;
	}
	const FUObjectItem& Slot = ObjObjects[Index];
	if( Slot.Object == NULL )
	{
		UE_LOG(LogUObjectArray, Warning, TEXT("Empty slot") );
		return false;
	}
	if( Slot.Object != Object )
	{
		UE_LOG(LogUObjectArray, Warning, TEXT("Other object in slot") );
		return false;
	}
	return true;
}

I’m not sure what is causing this behavior, but after making the Image property in FPhotoData into a UPROPERTY set to VisibleAnywhere, I can no longer replicate it, even when playing as a standalone game. Somehow that seems to be preventing whatever was causing the UTextureRenderTarget2D to be removed from the UObjectArray.

If anyone with deeper engine knowledge can provide further insight, I’d really appreciate it. I’m not crashing anymore, but there’s still a bug lurking somewhere in there.