Files
UnrealEngineUWP/Engine/Source/Developer/FunctionalTesting/Private/FunctionalUIScreenshotTest.cpp
Luke Thatcher 75cee9fd43 Marked several functions on the immediate RHICmdList that do not use "this" as deprecated.
- Replaced uses of these functions in the engine with the global scope equivalents.
 - Added some missing global scope RHI...() functions.
 - LLM scopes have been moved into the global scope function, where necessary.

Removed several _RenderThread() RHICmdList functions
 - These were previously provided for the automatic RHI thread stall / flush mechanism, but now provide no benefit
 - Removing these also helps prevent use of FRHICommandListExecutor::GetImmediateCommandList() on threads other than the rendering thread.

Remove RHIExecuteCommandList
 - Unused, legacy code path. No platform RHIs provide an implementation. No need to deprecate since there's no way anyone would be calling it already.

#jira none
#rb Zach.Bethel
#preflight 63c86e2dfb1a8cf245f44469

[CL 23771476 by Luke Thatcher in ue5-main branch]
2023-01-19 06:13:08 -05:00

234 lines
7.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "FunctionalUIScreenshotTest.h"
#include "Engine/GameViewportClient.h"
#include "AutomationBlueprintFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Camera/PlayerCameraManager.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/Engine.h"
#include "EngineGlobals.h"
#include "Misc/AutomationTest.h"
#include "Widgets/SViewport.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Slate/SceneViewport.h"
#include "Slate/WidgetRenderer.h"
#include "TextureResource.h"
#include "RenderingThread.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(FunctionalUIScreenshotTest)
AFunctionalUIScreenshotTest::AFunctionalUIScreenshotTest( const FObjectInitializer& ObjectInitializer )
: AScreenshotFunctionalTestBase(ObjectInitializer)
{
WidgetLocation = EWidgetTestAppearLocation::Viewport;
bHideDebugCanvas = true;
}
/**
* Get pixel format and color space of a backbuffer. Do nothing if the viewport doesn't
* render into backbuffer directly
* @InViewport - the viewport to get backbuffer from
* @OutPixelFormat - pixel format of the backbuffer
* @OutIsSRGB - whether the backbuffer stores pixels in sRGB space
*/
void GetBackbufferInfo(const FViewport* InViewport, EPixelFormat* OutPixelFormat, bool* OutIsSRGB)
{
if (!InViewport->GetViewportRHI())
{
return;
}
ENQUEUE_RENDER_COMMAND(GetBackbufferFormatCmd)(
[InViewport, OutPixelFormat, OutIsSRGB](FRHICommandListImmediate& RHICmdList)
{
FViewportRHIRef ViewportRHI = InViewport->GetViewportRHI();
check(ViewportRHI.IsValid());
FTexture2DRHIRef BackbufferTexture = RHIGetViewportBackBuffer(ViewportRHI);
check(BackbufferTexture.IsValid());
*OutPixelFormat = BackbufferTexture->GetFormat();
*OutIsSRGB = (BackbufferTexture->GetFlags() & TexCreate_SRGB) == TexCreate_SRGB;
});
FlushRenderingCommands();
}
void AFunctionalUIScreenshotTest::PrepareTest()
{
// Resize viewport to screenshot size
Super::PrepareTest();
// Hide all debug info
if (bHideDebugCanvas)
{
if (IConsoleVariable* CVarDebugCanvasVisible = IConsoleManager::Get().FindConsoleVariable(TEXT("Slate.GameLayer.DebugCanvasVisible")))
{
PreviousDebugCanvasVisible = CVarDebugCanvasVisible->GetBool();
CVarDebugCanvasVisible->Set(false);
}
}
TSharedPtr<SViewport> GameViewportWidget = GEngine->GameViewport->GetGameViewportWidget();
check(GameViewportWidget.IsValid());
// If render directly to backbuffer, just read from backbuffer
if (!GameViewportWidget->ShouldRenderDirectly())
{
// Resize screenshot render target to have the same size as the game viewport. Also
// make sure they have the same data format (pixel format, color space, etc.) if possible
const FSceneViewport* GameViewport = GEngine->GameViewport->GetGameViewport();
FIntPoint ScreenshotSize = GameViewport->GetSizeXY();
EPixelFormat PixelFormat = PF_A2B10G10R10;
bool bIsSRGB = false;
GetBackbufferInfo(GameViewport, &PixelFormat, &bIsSRGB);
if (!ScreenshotRT)
{
ScreenshotRT = NewObject<UTextureRenderTarget2D>(this);
}
ScreenshotRT->ClearColor = FLinearColor::Transparent;
ScreenshotRT->InitCustomFormat(ScreenshotSize.X, ScreenshotSize.Y, PixelFormat, !bIsSRGB);
}
// Spawn the widget
APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
SpawnedWidget = CreateWidget<UUserWidget>(PlayerController, WidgetClass);
if (SpawnedWidget)
{
if (WidgetLocation == EWidgetTestAppearLocation::Viewport)
{
SpawnedWidget->AddToViewport();
}
else
{
// Add to the game viewport and restrain the widget within
// owning player's sub-rect
SpawnedWidget->AddToPlayerScreen();
}
SpawnedWidget->SetVisibility(ESlateVisibility::HitTestInvisible);
}
UAutomationBlueprintFunctionLibrary::FinishLoadingBeforeScreenshot();
}
void AFunctionalUIScreenshotTest::OnScreenshotTakenAndCompared()
{
if (SpawnedWidget)
{
SpawnedWidget->RemoveFromParent();
}
// Restore viewport size and finish the test
Super::OnScreenshotTakenAndCompared();
// Restore the debug text
if (PreviousDebugCanvasVisible.IsSet())
{
if (IConsoleVariable* CVarDebugCanvasVisible = IConsoleManager::Get().FindConsoleVariable(TEXT("Slate.GameLayer.DebugCanvasVisible")))
{
CVarDebugCanvasVisible->Set(PreviousDebugCanvasVisible.GetValue());
PreviousDebugCanvasVisible.Reset();
}
}
}
void AFunctionalUIScreenshotTest::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
// Restore the debug text
if (PreviousDebugCanvasVisible.IsSet())
{
if (IConsoleVariable* CVarDebugCanvasVisible = IConsoleManager::Get().FindConsoleVariable(TEXT("Slate.GameLayer.DebugCanvasVisible")))
{
CVarDebugCanvasVisible->Set(PreviousDebugCanvasVisible.GetValue());
PreviousDebugCanvasVisible.Reset();
}
}
}
/**
* Read all pixels from backbuffer
* @InViewport - backbuffer comes from this viewport
* @OutPixels
*/
void ReadBackbuffer(const FViewport* InViewport, TArray<FColor>* OutPixels)
{
// Make sure rendering to the viewport backbuffer is finished
FlushRenderingCommands();
ENQUEUE_RENDER_COMMAND(CopyBackbufferCmd)(
[InViewport, OutPixels](FRHICommandListImmediate& RHICmdList)
{
FViewportRHIRef ViewportRHI = InViewport->GetViewportRHI();
FTexture2DRHIRef BackbufferTexture = RHIGetViewportBackBuffer(ViewportRHI);
RHICmdList.ReadSurfaceData(
BackbufferTexture,
FIntRect(0, 0, BackbufferTexture->GetSizeX(), BackbufferTexture->GetSizeY()),
*OutPixels,
FReadSurfaceDataFlags());
});
FlushRenderingCommands();
}
void ReadPixelsFromRT(UTextureRenderTarget2D* InRT, TArray<FColor>* OutPixels)
{
ENQUEUE_RENDER_COMMAND(ReadScreenshotRTCmd)(
[InRT, OutPixels](FRHICommandListImmediate& RHICmdList)
{
FTextureRenderTarget2DResource* RTResource =
static_cast<FTextureRenderTarget2DResource*>(InRT->GetRenderTargetResource());
RHICmdList.ReadSurfaceData(
RTResource->GetTextureRHI(),
FIntRect(0, 0, InRT->SizeX, InRT->SizeY),
*OutPixels,
FReadSurfaceDataFlags());
});
FlushRenderingCommands();
}
void AFunctionalUIScreenshotTest::RequestScreenshot()
{
// Register a handler to UGameViewportClient::OnScreenshotCaptured
Super::RequestScreenshot();
UGameViewportClient* GameViewportClient = GEngine->GameViewport;
TSharedPtr<SViewport> ViewportWidget = GameViewportClient->GetGameViewportWidget();
FIntPoint ScreenshotSize = GameViewportClient->GetGameViewport()->GetSizeXY();
TArray<FColor> OutColorData;
if (ViewportWidget.IsValid())
{
if (ViewportWidget->ShouldRenderDirectly())
{
const FSceneViewport* GameViewport = GameViewportClient->GetGameViewport();
ReadBackbuffer(GameViewport, &OutColorData);
}
else
{
// Draw the game viewport (overlaid with the widget to screenshot) to our ScreenshotRT.
// Need to do this manually because the game viewport doesn't have a valid FViewportRHIRef
// when rendering to a separate render target
FWidgetRenderer* WidgetRenderer = new FWidgetRenderer(true, false);
check(WidgetRenderer);
WidgetRenderer->DrawWidget(ScreenshotRT, ViewportWidget.ToSharedRef(), ViewportWidget->GetCachedGeometry().Scale, ScreenshotSize, 0.f);
FlushRenderingCommands();
BeginCleanup(WidgetRenderer);
ReadPixelsFromRT(ScreenshotRT, &OutColorData);
}
// For UI, we only care about what the final image looks like. So don't compare alpha channel.
for (int32 Idx = 0; Idx < OutColorData.Num(); ++Idx)
{
OutColorData[Idx].A = 0xff;
}
}
check(OutColorData.Num() == ScreenshotSize.X * ScreenshotSize.Y);
GameViewportClient->OnScreenshotCaptured().Broadcast(ScreenshotSize.X, ScreenshotSize.Y, OutColorData);
}