Issues with inheritance of Material Layer overrides

Hi,

We are seeing issues where edits to layer setups in parent Material Instances are not propagating correctly to child instances. For example, adding a new layer. The issue seems to be that the editor is not invoking some code it needs to like UMaterialInstance::UpdateParameterNames() and IMaterialEditor::NotifyExternalMaterialChange on dependent child instances. This mostly works since for parameter overrides only overridden values are stored on the MI, the others are read directly from the parent, but for layers states they are not and FMaterialLayersFunctions contains the full set. In this case the FMaterialLayersFunctions::ResolveParent codepath is not triggered until the material is reloaded so the child material uses out of date layers.

Also, related to this, we have noticed that even for layers with EMaterialLayerLinkState::LinkedToParent, the layer visibility state never inherits unless the layer is newly added, FMaterialLayersFunctions::ResolveParent never considers the parent linked layer visibility when updating an existing layer. This surpised us since were hoping to be able to disable and enable layers on the parent material instances and have them also disabled/enabled on child instances. Is this behavior something that is intentional? It seems to go against the general functionality of the LinkedToParent mode.

Thanks,

Lucas

[Attachment Removed]

Steps to Reproduce

In the attached sample project, add a new instance of /Game/ML_BaseColor to /Game/MP_Test_Inst while /Game/MP_Test_Inst_Inst is loaded in memory or open in the asset editor. See that the new layer is not added to MP_Test_Inst_Inst until MP_Test_Inst_Inst is reloaded via the Content Browser.

[Attachment Removed]

Hi Lucas,

What you mention is a known limitation of the way material layers are currently implemented. The truth is that the way Material Layers are currently implemented is only a thin UI layer of abstraction over material functions and material function instances: you can achieve the same results by using those directly (without the issues).

Due to the way material layers store parameter data it can lead to stale data when the parent parameter assignments are changed but the child material isn’t bumped, as you mentioned. Generally, in the engine we don’t have a dependency invalidation mechanism when a base asset notifies other dependant assets that it has changed: the only type of “update” we do is within the editor itself if these assets are opened in the editor. For instance changing an open material function will trigger changes to any material also opened that calls that function. Is this the type of dependency tracking you think is missing?

Regarding EMaterialLayerLinkState::LinkedToParent you’re correct. Currently, visibility is only inherited when a layer entry is newly added. I understand this may cause confusion, but it is the way it was implemented. It’s also difficult to change these behaviours now as other existing content may depend on it working this way. We can put this on our backlog of freature requests, but we currently can’t commit to when we would be able to do it.

Kind regards,

Massimo

[Attachment Removed]

Hi Massimo,

Thanks for the context here. Even though there isn’t a fix, we’re glad you guys are at least aware of these issues and it’s not something on our end.

Due to the way material layers store parameter data it can lead to stale data when the parent parameter assignments are changed but the child material isn’t bumped, as you mentioned. Generally, in the engine we don’t have a dependency invalidation mechanism when a base asset notifies other dependant assets that it has changed: the only type of “update” we do is within the editor itself if these assets are opened in the editor. For instance changing an open material function will trigger changes to any material also opened that calls that function. Is this the type of dependency tracking you think is missing?

Yeah, we have noticed even more issues than these when editing materials programmatically like via UMaterialEditingLibrary since as you say, some of the update logic is unfortunately coupled to the actual Material Editor UI itself, however in this case these issues are from normal interactive edits via the FMaterialInstanceEditor UI. It seems like layered materials need to do more propagation since unlike parameter values, of which child MIs don’t store inherited values even in memory, the layer values are copied to child instances.

That said, in our case at least, the Epic out-of-the-box propagation logic does work correctly, however it’s only triggered in the UMaterialInstance:::PostLoad() so you need to restart the Editor or reload the assets to see the updates. It seems like missing part is that the Parent Material needs to find all the dependent materials in memory (maybe via FObjectCacheContext::GetMaterialsAffectedByMaterials?) sort them by inheritance depth and call UpdateParameters() working from the immediate children down to the leaf children. This would run the codepath which calls FMaterialLayersFunctions::ResolveParent and update their in-memory FMaterialLayersFunctions for their parent’s data instead of leaving the in-memory material out of date until next time it’s loaded. The semantics of PostLoad maintain this ordering normally since the parent material’s package always loads before the child material since it imports it.

I think really only the part inside the if (StaticParametersRuntime.bHasMaterialLayers && Parent) block matters since the UpdateParameterSet calls aren’t needed in this case (likely why the current logic which maybe predates the layer code doesn’t do this dependent material search) but they are also harmless to call in this situation AFAIK.

Regarding EMaterialLayerLinkState::LinkedToParent you’re correct. Currently, visibility is only inherited when a layer entry is newly added. I understand this may cause confusion, but it is the way it was implemented. It’s also difficult to change these behaviours now as other existing content may depend on it working this way. We can put this on our backlog of freature requests, but we currently can’t commit to when we would be able to do it.

Thanks for the confirmation. Yeah, we have similar concerns with changing the semantics ourselves due to existing content, even if the current behavior is resulting in authoring mistakes for us. One idea we had was creating a new EMaterialLayerLinkState enum like LinkedToParentWithVisibility or adding another TArray<bool> LayerInheritVisibilityStates to FMaterialLayersFunctionsEditorOnlyData so that the new behavior it would be opt-in (and likely a project setting to control the default for new materials)

Thanks,

Lucas

[Attachment Removed]