Swapping texture in material instance crashes

I’m trying to write an actor component that will dynamically swap the texture that’s being applied to an actor. When I run my project, however, it immediately crashes with this error:

Unhandled exception at 0x000007FED8FF8600 (UE4Editor-Engine.dll) in UE4Editor.exe: 0xC0000005: Access violation writing location 0x000000000000015B.

Some background:

  • My component has a parameter called BaseMaterial, and the material I’m pointing it to has a TextureSampleParameter called “texture”
  • My component has a hidden parameter called MtlInst of type UMaterialInstanceDynamic*
  • If it matters, the component is being applied to a DecalActor to try to change the decal material that’s being used.

Here are the applicable bits from my ActorComponent code:

UTexture2D* LoadTextureRefFromPath(const FString& path)
{
	if (path.IsEmpty())
		return NULL;

	return Cast<UTexture2D>(StaticLoadObject(UTexture2D::StaticClass(), NULL, *(path)));
}

void UMyActorComponent::BeginPlay()
{
	Super::BeginPlay();

	// ...
	if (!MtlInst)
	{
		MtlInst = ((UStaticMeshComponent*)GetOwner())->CreateAndSetMaterialInstanceDynamicFromMaterial(0, BaseMaterial);
	}
}

void UMyActorComponent::SwapTexture()
{
        FString texPath = "Texture2D'/Game/Textures/test_texture.test_texture'";
        UTexture2D* tex = LoadTextureRefFromPath(texPath);
	if (tex)
	{
		if (!MtlInst)
		{
			UE_LOG(LogTemp, Warning, TEXT("MtlInst is undefined!"));
		}
		else
		{
			MtlInst->SetTextureParameterValue(FName("texture"), tex);
		}
	}
	else
	{
		FString msg = "Unable to find texture: " + texPath;
		UE_LOG(LogTemp, Warning, TEXT("%s"), *msg);
	}
	
}

The crash occurs when it hits the SetTextureParameterValue call. Does anyone see anything that might cause a crash in there?

Here’s the stack trace, if it helps:

UE4Editor_Engine!FMaterialRenderProxy::InvalidateUniformExpressionCache() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\engine\private\materials\materialshared.cpp:1652]
UE4Editor_Engine!FMaterialInstanceResource::RenderThread_UpdateParameter<UTexture const * __ptr64>() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\engine\private\materials\materialinstancesupport.h:118]
UE4Editor_Engine!EURCMacro_SetMIParameterValue<FTextureParameterValue>::DoTask() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\engine\private\materials\materialinstance.cpp:322]
UE4Editor_Engine!TGraphTask<EURCMacro_SetMIParameterValue<FTextureParameterValue> >::ExecuteTask() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\public\async\taskgraphinterfaces.h:779]
UE4Editor_Core!FTaskThread::ProcessTasks() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\async\taskgraph.cpp:539]
UE4Editor_Core!FTaskThread::ProcessTasksUntilQuit() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\async\taskgraph.cpp:340]
UE4Editor_RenderCore!RenderingThreadMain() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\rendercore\private\renderingthread.cpp:310]
UE4Editor_RenderCore!FRenderingThread::Run() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\rendercore\private\renderingthread.cpp:411]
UE4Editor_Core!FRunnableThreadWin::Run() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\windows\windowsrunnablethread.cpp:74]

Here’s the stacktrace, if it helps:

UE4Editor_Engine!FMaterialRenderProxy::InvalidateUniformExpressionCache() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\engine\private\materials\materialshared.cpp:1652]
 UE4Editor_Engine!FMaterialInstanceResource::RenderThread_UpdateParameter<UTexture const * __ptr64>() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\engine\private\materials\materialinstancesupport.h:118]
 UE4Editor_Engine!EURCMacro_SetMIParameterValue<FTextureParameterValue>::DoTask() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\engine\private\materials\materialinstance.cpp:322]
 UE4Editor_Engine!TGraphTask<EURCMacro_SetMIParameterValue<FTextureParameterValue> >::ExecuteTask() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\public\async\taskgraphinterfaces.h:779]
 UE4Editor_Core!FTaskThread::ProcessTasks() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\async\taskgraph.cpp:539]
 UE4Editor_Core!FTaskThread::ProcessTasksUntilQuit() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\async\taskgraph.cpp:340]
 UE4Editor_RenderCore!RenderingThreadMain() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\rendercore\private\renderingthread.cpp:310]
 UE4Editor_RenderCore!FRenderingThread::Run() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\rendercore\private\renderingthread.cpp:411]
 UE4Editor_Core!FRunnableThreadWin::Run() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.9\engine\source\runtime\core\private\windows\windowsrunnablethread.cpp:74]

Seems like decal actors may be unique enough to not work with the generic casting/assigning that I was trying to do. I switched the BeginPlay() code to this:

	if (!MtlInst)
	{
		UDecalComponent* decal = Cast<ADecalActor>(GetOwner())->GetDecal();
		MtlInst = UMaterialInstanceDynamic::Create(decal->GetDecalMaterial(), this);
		if (!MtlInst)
		{
			UE_LOG(LogTemp, Warning, TEXT("MtlInst wasn't initialized!!!!"));
			return;
		}
		decal->SetDecalMaterial(MtlInst);
	}

And it seems to be much happier - no crashes and the texture swaps out correctly!