Is there a way to figure out when scene capture have performed scene capture?

Hello! I am trying to implement a procedural runtime masking via runtime unwrapping and render target capture like this https://www.raywenderlich.com/6817-d…nreal-engine-4 in C++. The issue arises when I try to switch materials after performing scene capture. From debugging, I believe that occasionally material swap is performed while the scene capture is happening (or some other related rendering process) and that causes something to become nulltptr and crash. Hence I am trying to find a solution for controlling when the material swap should happen after the scene capture. Doing timed delay does not really work and stopping game thread while waiting for render thread to catch up does not sound like a good idea either, So, my question is there an option to “listen” for when the scene capture happens and then perform the material swap. Here is code in the function responsible for it (if needed):



m_pMesh->SetMaterial(0, m_pUnwrapMaterialInstance);

m_pUnwrapMaterialInstance->SetVectorParameterValue("HitLocation", FLinearColor(hitLoc));
m_pUnwrapMaterialInstance->SetVectorParameterValue("UnwrapLocation", FLinearColor(GetActorLocation() + FVector(0.f, 0.f, 1000.f)));
m_pUnwrapMaterialInstance->SetScalarParameterValue("BrushRadius", 5.f);

m_pSceneCapture2D->SetRelativeRotation(FRotator::MakeFromEuler(FVector(0.f, -90.f, -90.f)));
m_pSceneCapture2D->SetRelativeLocation(FVector(0.f, 0.f, 1200.f));

m_pSceneCapture2D->CaptureScene();

m_pMesh->SetMaterial(0, m_pEnemyMaterialInstance); <-- this seems to be the issue, as without this reset - crash does not occur


I was also looking into trying to wrap this code with ENQUEUE_UNIQUE_RENDER_COMMAND_XXXPARAMETER but underlying functions in CaptureScene() are required to be called from game thread and I assume this means that this macro would not work and from testing it did not solve the issue either (only increased nr of issues).

The crash specifically occurs in void FMaterialRenderProxy::InvalidateUniformExpressionCache(bool bRecreateUniformBuffer), on line
if (HasVirtualTextureCallbacks). (Render thread), while Main/Game thread is executing FMaterialRelevance UMaterialInterface::GetRelevance_Concurrent(ERHIFeatureLevel::Type InFeatureLevel) const, line const UMaterial* Material = GetMaterial_Concurrent();

Update: It seems like I am an idiot and misdiagnosed the issue. So, the materials seem to get destroyed as they get marked for GC in SetMaterial function. Creating new instances in before assigning them seemed to fix the issue but it looks like a huge performance killer, so I guess I need to figure out a way to prevent the material instance from being destroyed? I am not 100% sure this is the solution but stuff did not seem to crash with the constant instance creation. Given that I am dealing with rendering and two threads interacting, maybe I just got lucky with avoiding the crashes.

1 Like

Someone reprimand me if “necroing” threads is frowned upon, but I just thought I’d add my experience for anyone else who comes across this issue.

In my case at least, my UMaterialInstanceDynamic was getting garbage collected. I don’t really know enough about UE scope and how they store UObjects, but I think my problem was that I use a TArray<FSkeletalMaterial> to change materials, so that was stopping the engine from storing the materials.

My solution was probably primitive, but I just now store the TArray<FSkeletalMaterial> as a member variable for the owning actor so it’s scope stays with the actor.

EDIT: This advice is trash, please disregard. Bug still active. Will report back.

EDIT2: I figured out what my problem was, I was using a TMap of pointers to the dynamic materials that I had set up, because I thought it would be faster than re-creating the dynamic materials and setting the scalar parameters.
Grabbing those dynamic materials from the map seemed to work most of the time, but that was how I was ending up getting the random access violations.
I ditched the TMap and now there’s no problems and it actually runs faster.