Help with multithreading and accessing render target images

I am working on an application where I need to record images seen from an in-game camera. Initially, the method was to set up a TextureRenderTargetResource from the in-game capture component, and subsequently call ReadPixels to access the frame data and save that data as an image. (code flow below)


 bool saveImage() {
      USceneCaptureComponent2D* capture = getCaptureComponent(camera_type, true);
      FTextureRenderTargetResource* RenderResource = capture->TextureTarget->GameThread_GetRenderTargetResource();
      width = capture->TextureTarget->GetSurfaceWidth();
	  height = capture->TextureTarget->GetSurfaceHeight();

	  TArray<FColor> imageColor;
	  imageColor.AddUninitialized(width * height);
	  RenderResource->ReadPixels(bmp);
    }

This approach was slowing the game thread down significantly, so I am trying to move the screenshot saving procedure to its own thread. My approach now is inspired by the article at A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine Forums. I access the *AActor object from this separate thread and subsequently its camera, and then I call the saveImage() function from the thread: but this is resulting in a crash with no additional information (just “UE4Editor.exe has triggered an exception”) at the call to ReadPixels(). So essentially, calling saveImage() and thereby ReadPixels() from the game thread works perfectly, and it fails when I call it from a different thread. Am I doing something wrong in my process here? Looking for some suggestions as to how to better debug this.

EDIT: Visual Studio’s output log says :

“Assertion failed: IsInGameThread() [File: D:\Build++UE4+Release-4.15+Compile\Sync\Engine\Source\Runtime\RenderCore\Private\RenderingThread.cpp]”

So it looks like it doesn’t like the fact that it’s being called from a non-game thread… is there a way to fix this?

Are you sure RenderResource isn’t null when you call it from that other thread? Right now you’re just blindly accessing it.

Yes, there are checks that return false is RenderResource or capture is Null. Sorry I just didn’t include them in my snippet because I was just trying to indicate the important steps.

Figured out the problem here: The default ReadPixels() implementation has a function call to FlushRenderingCommands(), which should be called only from a game thread. A rewrite of ReadPixels() in my own implementation was needed to fix this.

@sahiven - I’m wanting to move screenshot saving to another thread as well- we’re rendering out 8K 360 equirectangular images and when user takes screenshot game will freeze for a few seconds. We’re doing a VR game too so the freeze is extra horrible. Would you mind posting or messaging your implementation of ReadPixels ?

@saihv I am also interested in knowing what was your ReadPixels() implementation if you have some time to post it. I am currently trying the same thing in a Tick method and it is really slowing down the game. Obviously I am doing something wrong here.