UMaterialInstance from UMaterialInstanceDynamic deleted by the garbage collector

I’m facing a problem with the UMaterialInstanceDynamic in a Widget.

I have a widget taking a UMaterialInstance* as an argument and create a UMaterialInstanceDynamic in the Construct.
After a period of time, the referenced ParentMaterial is apparently deleted by the garbage collector.

The class :

class KSGM_API SKBatteryWidget : public SImage
{
public:
	SLATE_BEGIN_ARGS(SKBatteryWidget)
		:
..........
	{}

		/** @brief The HUD used to create the dynamic material instance. */
		SLATE_ARGUMENT(AKPlayerHUD*, HUD)
		/** @brief The fill material instance. */
		SLATE_ARGUMENT(UMaterialInstance*, FillMatInst)
...
        UMaterialInstanceDynamic* FillMatInstDyn;
....
}

In the construct :

void SKBatteryWidget::Construct(const FArguments& InArgs)
{
	// Create the dynamic material instance
	if (InArgs._HUD != nullptr && InArgs._FillMatInst != nullptr)
		FillMatInstDyn = UMaterialInstanceDynamic::Create(InArgs._FillMatInst, InArgs._HUD);
.....
	SImage::Construct(SImage::FArguments()
					  .Image(InArgs._BatteryBrush)
					  );
}

The InArgs._FillMatInst is a pointer coming from a widget style

USTRUCT()
struct KSGM_API FKActionBarStyle : public FSlateWidgetStyle
{
	GENERATED_USTRUCT_BODY()
.....
	/** @brief The energy fill material instance. */
	UPROPERTY(EditAnywhere, Category = "Appearance")
	UMaterialInstance* EnergyFillMaterial;
	FKActionBarStyle& SetEnergyFillMaterial(UMaterialInstance* InEnergyFillMaterial) { EnergyFillMaterial = InEnergyFillMaterial; return *this; }

.....
}

The problem is in fact the UMaterialInstanceDynamic::Create is setting the pointer using MID->SetParentInternal
Which means, if the UMaterialInstance is destroyed, it becomes invalid.

  •   UMaterialInstance	(Name=Invalid)	UMaterialInstance
    
  •   UMaterialInterface	(Name=Invalid)	UMaterialInterface
    
  •   PhysMaterial	0xdddddddddddddddd (Name={ComparisonIndex=??? DisplayIndex=??? Number=??? })	UPhysicalMaterial *
    
  •   Parent	0xdddddddddddddddd (Name={ComparisonIndex=??? DisplayIndex=??? Number=??? })	UMaterialInterface *
    
    
      UMaterialInstanceDynamic* UMaterialInstanceDynamic::Create(UMaterialInterface* ParentMaterial, UObject* InOuter)
      {
      	UObject* Outer = InOuter ? InOuter : GetTransientPackage();
      	UMaterialInstanceDynamic* MID = NewObject<UMaterialInstanceDynamic>(Outer);
      	MID->SetParentInternal(ParentMaterial, false);
      	return MID;
      }
    

First, I don’t know why the UMaterialInstance coming from the widget style is destroyed after a period of time ?

Second, do I have to make a copy of the UMaterialInstance and using the copy in UMaterialInstanceDynamic::Create ?

Third, How to avoid the UMaterialInstance, coming from the widget style, to be destroyed ?

Txs for your help,
D.

Hello, domzorg!
I think i face analogue situation here…
I have blueprint that has UMaterial* property, and after that in runtime i create MaterialInstanceDynamic (not in constructor, but on game event). And after some time material became corrupted.

What i’ve done to that is instead of
UMaterialInstanceDynamic::Create(ParentMaterial, this);
i’ve used in costructor:
static ConstructorHelpers::FObjectFinder MaterialOb(TEXT("/Game/UI/HUD/ParentMaterial"));
ParentMaterial = ObjectiveMarkerMaterialOb.Object;

and after that
UMaterialInstanceDynamic::Create(ParentMaterial, this);

Of course it has direct reference to asset in code and maybe it will not work for you, but as an idea.

Another helpfull idea is to not compare you dynamic material with null but use IsValidLowLevel method for checking validity. If you use this method you will get helpfull messages in log about validity of you object (if it not valid any more) and of course your game will stop crashing.

Txs for your answer.
I’ll try in the future since I’ve stopped using dynamic material instance in this widget.

D.