Garbage Collection of UMaterialInstance objects stored in TMap

I’m having issues storing a TMap (called cachedMaterials) with keys of type class UMaterialInstance*. I need to ensure they’re not GCed, but can’t use TMaps (since UPROPERTY() doesn’t work for them), and the alternative of using material->AddToRoot() / setting flags to RF_RootSet causes a crash in editor when ending play, presumably because the materials are not removed from the root (commenting out the offending root-setting line prevents the crash).

I’ve tried using material->RemoveFromRoot() during BeginDestroy(), but I’m still crashing when exiting play mode.

The thought crossed my mind to store those materials in a UPROPERTY() TArray arrayJustToPreventGC;, but that seems hackish…

The cachedMaterials TMap is used to map each StaticMeshActor to copied instances of their TArray of materials in a for loop, using something resembling this:

TArray dynamicMaterials;
UPrimitiveComponent* pc = someStaticMeshActor->GetRootPrimitiveComponent();
for (for (int i=0; iGetNumMaterials(); i++)
{
    UMaterialInstanceDynamic* tempMaterial = pc->CreateAndSetMaterialInstanceDynamicFromMaterial( i, pc->GetMaterial(i) )
    tempMaterial->AddToRoot(); //or tempMaterial->SetFlags(RF_RootSet);
    dynamicMaterials.Add( tempMaterial );
}
cachedMaterials.Add( someStaticMeshActor, dynamicMaterials );

Since TMaps can’t be used with UPROPERTY(), I can’t use that pattern to prevent the material instances created at runtime from being GCed. I’ve also tried using class UMaterialInstanceDynamic* as the TMap’s value type, but the issues remain. I’m presuming this issue applies to UObjects generally, since they’re subject to garbage collection unless referenced / part of the root set?

Any help would be greatly appreciated!

Cheers,
Steve

Just a note for anyone currently running into this issue before TMaps become compatible with UPROPERTY() - I ended up preventing the crash by also storing materials created at runtime in a TArray, declared as such:

//Not able to use UPROPERTY() to prevent the UMaterialInterface objects
//created at runtime from being garbage collected, and using AddToRoot()
//or SetFlags(RF_RootSet) was causing crashes after ending play in editor.
TMap> cachedMaterials; //map of static mesh actors, each element with an array of materials

//An array whose elements are safe from garbage collection since it's a UPROPERTY().
UPROPERTY()
TArray HackToStopEditorCrash;

Then, when when creating a new dynamic material instance, I did this:

//the "a" variable is a pointer to a StaticMeshActor
TArray dynamicMaterials;
UPrimitiveComponent* pc = a->GetRootPrimitiveComponent();
for (int i=0; iGetNumMaterials(); i++)
{
    //create dynamic instance of the PrimitiveComponent's i-th material
    UMaterialInstanceDynamic* tempMaterial = pc->CreateDynamicMaterialInstance(i);

    //HACK: add dynamic material to a UPROPERTY()-ed TArray to prevent GC and gracefully end PIE
    HackToStopEditorCrash.Add(tempMaterial);

    //add tempMaterial to dynamicMaterials, to be stored in the cachedMaterials TMap
    dynamicMaterials.Add(tempMaterial);
}
cachedMaterials.Add(a,dynamicMaterials);

Thanks for the help, guys. This workaround seems like the best solution for now, and should be no problem to remove once TMaps work with UPROPERTY(), since my code makes no other use of the HackToStopEditorCrash array and any lines referencing it can then safely be deleted.

#Why Need TMap?

Why do you need a TMap instead of just using a TArray and setting up all the proper accessors?

I am quite curious,

I am not presuming I know one way better than another, just want more info as to why TMap is your preferred design choice

Rama

If I had to guess it’s because TMap is Key-Value storage, while TArray is just Value (object). And he needs that key for something.

Lukasz is correct - I want to map each StaticMeshActor in the game to a TArray
of materials copied from its array of materials element by element.

Even if this isn’t the best way to go about doing that, I’d still be interested to know how to solve this issue with TMaps. I could use a TMap with a TArray> to map StaticMeshActors to indices in an array of arrays of materials, but I’ve got a feeling it can be done with some GC control per copied material.

I think AddToRoot() could be the way to go, but I need to call RemoveFromRoot() before play in editor ends. I tried calling it in BeginDestroy(), but the crash still occurs.

Hmm my post is missing some template arguments - not sure where they’re getting consumed, but I’m guessing it’s because it’s similar to html tags… Hopefully you can understand I meant a TMap(StaticMeshActor*,int) and TArray(TArray(UMaterialInstance*)), with angle brackets.

#Unresolved

“Even if this isn’t the best way to go about doing that, I’d still be interested to know how to solve this issue with TMaps.”

Well then I cant help you much, since TMaps are not working with UProperty(),

good luck with this!

Hope Epic has way to address TMaps + UProperty()

Rama

I needed to use TMap because I’m using sparse array.