Pause rendering and continually display the last-rendered frame

Out of pure interest I was trying to make it work by actually getting last rendered frame, and found a function GetViewportScreenShot on viewport, it does just that.

However, I then wasn’t able to correctly determine how to use that data. The only thing I managed to achieve is copying that data onto provided TextureRenderTarget2D. That works just fine (resulting quality isn’t great, but good enough). However, the drawing part turned out to cause a notable freeze, so I ended up not using it.

Maybe someone else can figure it out better? I’m pretty sure this entire approach isn’t right.
Please note that I had absolutely ZERO idea what am I doing here.

#include "Kismet/KismetRenderingLibrary.h"
#include "Engine/GameViewportClient.h"
#include "Engine/Canvas.h"
#include "CanvasItem.h"
#include "Slate/SceneViewport.h"

#include "ViewportFrameCapture.generated.h"

UCLASS()
class UViewportFrameCapture : public UObject
{
	GENERATED_BODY()

	UFUNCTION(BlueprintCallable)
	static bool GetLastRenderedFrame(UObject* WorldContext, UTextureRenderTarget2D*& OutTexture)
	{
		if (!OutTexture)
		{
			return false;
		}

		TArray<FColor> BitMap;
		FVector2D ViewportSize;
		bool bReceivedColorBuffer = false;
		if (GEngine && GEngine->GameViewport)
		{
			if (FSceneViewport* Viewport = GEngine->GameViewport->GetGameViewport())
			{
				// read last rendered frame
				bReceivedColorBuffer = GetViewportScreenShot(Viewport, BitMap, FIntRect(0, 0, 0, 0));
				// find viewport size
				GEngine->GameViewport->GetViewportSize(ViewportSize);
			}
		}

		if (!bReceivedColorBuffer)
		{
			return false;
		}

		// set up pixel format for render target
		const EPixelFormat RequestedFormat = FSlateApplication::Get().GetRenderer()->GetSlateRecommendedColorFormat();
		OutTexture->InitCustomFormat(ViewportSize.X, ViewportSize.Y, RequestedFormat, true);
		OutTexture->UpdateResourceImmediate(true);

		// create canvas object to draw on
		UCanvas* Canvas;
		FVector2D CanvasSize;
		FDrawToRenderTargetContext Context;
		UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(WorldContext, OutTexture, Canvas, CanvasSize, Context);

		// this partially repeats BeginDrawCanvasToRenderTarget, except we create canvas in DeferDrawing mode,
		// otherwise rendering thread will freeze for several seconds in CreateRHIBuffer
		UWorld* World = GEngine->GetWorldFromContextObject(WorldContext, EGetWorldErrorMode::LogAndReturnNull);
		FTextureRenderTargetResource* RenderTargetResource = OutTexture->GameThread_GetRenderTargetResource();
		FCanvas* ParamsCanvas = new FCanvas(
			RenderTargetResource,
			nullptr,
			World,
			World->FeatureLevel,
			FCanvas::CDM_DeferDrawing);
		Canvas->Init(OutTexture->SizeX, OutTexture->SizeY, nullptr, ParamsCanvas);

		// TODO: this is unoptimzed, causes about 0.5s freeze. There must be a better way to copy bitmap onto canvas, but I wasnt able to find it
		for (int i = 0; i < BitMap.Num(); i++)
		{
			const FVector2D Pos(i % (int)ViewportSize.X, i / (int)ViewportSize.X);
			FCanvasLineItem Item(Pos, Pos);
			Item.LineThickness = 1.0f;
			Item.SetColor(BitMap[i]);
			Canvas->DrawItem(Item);
		}

		UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(WorldContext, Context);
		return true;
	}
};