Let's Talk Spectator Screen UI

Hey guys I’ve been trying to crack this nut for a while and appreciate any advice/feedback you guys can give. I’ve got a VR game where I use the unreal spectator screen and all I want to do is add a UMG widget to it. I’ve seen some posts out there of people that in theory have ways to make it work, but I haven’t found anyone who’s actually written a step by step. All the advice I’ve seen seems to point to go something like this.

  1. Create a custom render target to write to.
  2. Use a scene capture 2d component to capture the gameplay and write it to the render target
  3. Incorporate the custom render target in a UI material
  4. Make a custom UMG widget that uses the UI material from step 3 as the background.
  5. Using c++ render the widget from step 4 to a second render target and use that as the texture input for the spectator screen

So I’ve implemented these steps but I’ve got a a problem. after just a few seconds of running my ram usage jumps exponentially till it hits 100%. My c++ is real weak so I’m not sure what I’m doing wrong. Here is the class responsible for rendering the widget to a render target.

SpectatorCamera.H


 
  #pragma once    #include "CoreMinimal.h"  #include "GameFramework/Actor.h"  #include "Kismet/BlueprintFunctionLibrary.h"  #include "UMG.h"  #include "SlateBasics.h"  #include "SlateCore.h"  #include "SWidget.h"  #include "WidgetRenderer.h"  #include "SpectatorCamera.generated.h"    UCLASS(Blueprintable)  class UFO_RODEO_API ASpectatorCamera : public AActor  {      GENERATED_BODY()        public:          // Sets default values for this actor's properties      ASpectatorCamera();          protected:      // Called when the game starts or when spawned      virtual void BeginPlay() override;    public:          // Called every frame      virtual void Tick(float DeltaTime) override;        UFUNCTION(Category = "SpectatorCamera", BlueprintCallable)      void RenderWidgetToTargetEnhanced(bool UseGamma, TextureFilter Filter, UUserWidget * WidgetToRender, FVector2D DrawSize, float DeltaTime, UPARAM(ref) UTextureRenderTarget2D * Target);            FWidgetRenderer * renderer;        };  

SpectatorCamera.cpp


 
 #include "SpectatorCamera.h"      // Sets default values  ASpectatorCamera::ASpectatorCamera()  {       // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.      PrimaryActorTick.bCanEverTick = true;      renderer = new FWidgetRenderer(false);  }    // Called when the game starts or when spawned  void ASpectatorCamera::BeginPlay()  {      Super::BeginPlay();        }    // Called every frame  void ASpectatorCamera::Tick(float DeltaTime)  {      Super::Tick(DeltaTime);    }    void ASpectatorCamera::RenderWidgetToTargetEnhanced(bool UseGamma, TextureFilter Filter, UUserWidget * WidgetToRender, FVector2D DrawSize, float DeltaTime, UPARAM(ref) UTextureRenderTarget2D * Target)  {      if (!WidgetToRender) return;      if (DrawSize == FVector2D(0, 0)) return;      if (!Target) return;            TSharedRef<SWidget> ref = WidgetToRender->TakeWidget();      renderer->DrawWidget(Target, ref, DrawSize, DeltaTime);  } 

I’m calling RenderWidgetToTargetEnhanced once a tick from a blueprint that inherits from this class.

I’ve also noticed that if in step 4, I just create a widget with no references to the first render target I don’t get the massive memory leak. I also don’t get my spectator camera in that case though so it doesn’t really help, but it does make me wonder if the problem is really my c++ code or something else.

Anyone out there either have a solution to my memory leak problem or a better alternative on how to add widgets to a spectator screen?

Also is there a way to post the code better here? It’s reducing everything to 1 line.

You can override a URetainerBox which is a widget that already renders to a rendertarget for a delayed presentation, then you can use that render target for the spectator screen custom texture in the Eye + Texture spectator mode.

Using the widget renderer itself is also fine, but you can still just use the Eye + Texture spectator mode, skip the scene capture 2D unless you are specifically trying to have a second custom view.

I didn’t look too much at your code since it is obvious not formatted correctly…there shouldn’t be a memory leak normally unless you aren’t clearing memory you allocate though.



 renderer = new FWidgetRenderer(false); 

Couldn’t find where you release this again in trying to scroll through.

Thanks for the reply! If I override the retainer box and expose the render it uses will the widget its on update if it’s not attached to anything? Like if I just create a reference to a widget with that contains that retainer box but I never add that to a viewport will it still tick and render itself to the render target?

In my case I do need the SceneCapture2d because I’m trying to use a separate spectator camera, not just a copy of the eye.

Is there a way to format the code properly on this forum? No matter what I tried it kept pushing it all to 1 line. Here it is in pastebin links if that helps:
SpectatorCamera.h
SpectatorCamera.cpp

Your right I’m not really cleaning up that renderer, but there’s only one instance of this class and from my understanding it only creates that renderer once. Would that really balloon my ram usage like what I’m seeing?

You can still attach it to a viewport and just not let it draw out its contents, but manually rendering it like you are attempting to do is likely better in the end.

Also no, I had missed that you were doing that in the constructor since everything was in one line. Your issue would have to be outside of that class, I don’t see anything wrong with it.