ReadPixels slow after deprecation of bHDR flag of TextureRenderTarget2D

I am using a SceneCapture2D and a TextureRenderTarget2D to capture and export image data as it is seen by an ingame camera. Every tick ReadPixels is performed on the render target to obtain the captured images.

This worked fine in engine version 4.16, taking about 20ms per frame. If mutliple SceneCapture2Ds were placed in a level, subsequent calls to ReadPixels seemed to be optimized in some way and were even faster.
But now we are trying to move to the current version 4.19 where ReadPixels takes about 10 times longer than in 4.16. Additionally there is no optimization going on for mutliple SceneCaptures.

I tracked the problem down to the bHDR flag of UTextureRenderTarget2D which has been deprecated in the current version.
In 4.16 turning this flag on leads to the same bad performance as the code has in 4.19 - between 200 to 500ms per read operation and no optimizations for mutliple captures.

In 4.19 this flag has been renamed to bHDR_DEPRECATED and turning it off does not seem to have any effect.
In the documentation I could not find any useful information about this flag except that setting it to true would require more memory - there is no statement about the significantly worse runtime.

Does anyone have an Idea what I can do to make ReadPixels fast even with the deprecation of this flag?
Any help is appreciated.

A minimal code example can be found below:

Header:

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"

#include "Engine/SceneCapture2D.h"
#include "Runtime/Engine/Classes/Engine/TextureRenderTarget2D.h"
#include "Runtime/Engine/Classes/Engine/Texture2D.h"

#include "VirtualCamera.generated.h"

UCLASS()
class VIRTUALCAMERA_TEST_API AVirtualCamera : public ASceneCapture2D
{
	GENERATED_BODY()
public:	
	AVirtualCamera();

	virtual void Tick(float DeltaTime) override;

protected:
	virtual void BeginPlay() override;

private:
	UTextureRenderTarget2D * m_renderTarget;
	TArray<FColor> * m_colorBitmap;
	
	int m_width;
	int m_height;
};

Source:

#include "VirtualCamera.h"

#include "Runtime/Engine/Classes/Components/SceneCaptureComponent2D.h"

#include <chrono>

AVirtualCamera::AVirtualCamera() : ASceneCapture2D(), m_width(800), m_height(600)
{
	PrimaryActorTick.bCanEverTick = true;
}

void AVirtualCamera::BeginPlay()
{
	Super::BeginPlay();

	m_renderTarget = NewObject<UTextureRenderTarget2D>(this);
	m_renderTarget->bHDR_DEPRECATED = 0; // Change this to bHDR in older engine versions
	m_renderTarget->InitAutoFormat(m_width, m_height);

	GetCaptureComponent2D()->TextureTarget = m_renderTarget;
	GetCaptureComponent2D()->DetailMode = EDetailMode::DM_High;
	GetCaptureComponent2D()->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
	GetCaptureComponent2D()->bAllowConcurrentTick = 1;

	m_colorBitmap = new TArray<FColor>();
	m_colorBitmap->SetNum(m_width * m_height, false);

	auto postProcessing = &GetCaptureComponent2D()->PostProcessSettings;

	postProcessing->bOverride_AutoExposureBias = 1;
	postProcessing->bOverride_AutoExposureMaxBrightness = 1;
	postProcessing->bOverride_AutoExposureMinBrightness = 1;
	postProcessing->bOverride_AutoExposureHighPercent = 1;
	postProcessing->bOverride_AutoExposureLowPercent = 1;
	postProcessing->AutoExposureMaxBrightness = 0.0;
	postProcessing->AutoExposureMinBrightness = 0.0;
	postProcessing->AutoExposureBias = -11.56;
}

void AVirtualCamera::Tick(float DeltaTime)
{
	using std::chrono::high_resolution_clock;

	Super::Tick(DeltaTime);
	
	const auto p1 = high_resolution_clock::now();

	auto textureResource = m_renderTarget->GameThread_GetRenderTargetResource();
	if (!textureResource->ReadPixels(*m_colorBitmap))
	{
		UE_LOG(LogTemp, Warning, TEXT("ReadPixels failed"));
		return;
	}

	const auto p2 = high_resolution_clock::now();
	const double elapsedTime = std::chrono::duration<double, std::milli>(p2 - p1).count();

	UE_LOG(LogTemp, Display, TEXT("ReadPixels: %f ms;"), elapsedTime);
}
1 Like

Okay, maybe this has been a stupid question but after looking at the underlying source code in TextureRenderTarget2D for a while I noticed that I can achieve my goal by manually setting the PixelFormat to the type previously derived from bHDR. Now everything functions the way it did before in engine version 4.16.

The changes in line 17 and 18 of the source file are:

//m_renderTarget->bHDR_DEPRECATED = 0;
m_renderTarget->InitCustomFormat(m_width, m_height, PF_B8G8R8A8, true);

Shouldn’t the last argument be ‘false’, if you’re trying to disable HDR?