Skip to content

Instantly share code, notes, and snippets.

@dilne
Created February 24, 2022 20:35
Show Gist options
  • Save dilne/715162238d4c3487ababc991f75fe483 to your computer and use it in GitHub Desktop.
Save dilne/715162238d4c3487ababc991f75fe483 to your computer and use it in GitHub Desktop.
// Fill out your copyright notice in the Description page of Project Settings.
#include "CaptureManager.h"
#include "Runtime/Engine/Classes/Components/SceneCaptureComponent2D.h"
#include "Runtime/Engine/Classes/Engine/TextureRenderTarget2D.h"
#include "Engine.h"
#include <Runtime/Engine/Classes/Kismet/GameplayStatics.h>
#include <Runtime/Engine/Public/ShowFlags.h>
#include "RHICommandList.h"
#include "IImageWrapper.h"
#include "IImageWrapperModule.h"
#include "ImageUtils.h"
// Static ImageWrapperModule to prevent reloading -> this thing does not like to be reloaded..
static IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
void ACaptureManager::CaptureColorNonBlocking(ASceneCapture2D* CaptureComponent, bool IsSegmentation) {
// Get RenderContext
FTextureRenderTargetResource* renderTargetResource = CaptureComponent->GetCaptureComponent2D()->TextureTarget->GameThread_GetRenderTargetResource();
struct FReadSurfaceContext {
FRenderTarget* SrcRenderTarget;
TArray<FColor>* OutData;
FIntRect Rect;
FReadSurfaceDataFlags Flags;
};
// Init new RenderRequest
FRenderRequest* renderRequest = new FRenderRequest();
renderRequest->isPNG = IsSegmentation;
// Setup GPU command
FReadSurfaceContext readSurfaceContext = {
renderTargetResource,
&(renderRequest->Image),
FIntRect(0,0,renderTargetResource->GetSizeXY().X, renderTargetResource->GetSizeXY().Y),
FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX)
};
// Send command to GPU
/* Up to version 4.22 use this
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
SceneDrawCompletion,//ReadSurfaceCommand,
FReadSurfaceContext, Context, readSurfaceContext,
{
RHICmdList.ReadSurfaceData(
Context.SrcRenderTarget->GetRenderTargetTexture(),
Context.Rect,
*Context.OutData,
Context.Flags
);
});
*/
// Above 4.22 use this
ENQUEUE_RENDER_COMMAND(SceneDrawCompletion)(
[readSurfaceContext](FRHICommandListImmediate& RHICmdList) {
RHICmdList.ReadSurfaceData(
readSurfaceContext.SrcRenderTarget->GetRenderTargetTexture(),
readSurfaceContext.Rect,
*readSurfaceContext.OutData,
readSurfaceContext.Flags
);
});
// Notify new task in RenderQueue
RenderRequestQueue.Enqueue(renderRequest);
// Set RenderCommandFence
renderRequest->RenderFence.BeginFence();
}
void ACaptureManager::SetupColorCaptureComponents(ASceneCapture2D* captureComponent) {
// Create RenderTargets
UTextureRenderTarget2D* renderTarget2D = NewObject<UTextureRenderTarget2D>();
// Set FrameWidth and FrameHeight
renderTarget2D->TargetGamma = 1.2f;// for Vulkan //GEngine->GetDisplayGamma(); // for DX11/12
// Setup the RenderTarget capture format
renderTarget2D->InitAutoFormat(256, 256); // some random format, got crashing otherwise
int32 frameWidth = 640;
int32 frameHeight = 480;
renderTarget2D->InitCustomFormat(frameWidth, frameHeight, PF_B8G8R8A8, true); // PF_B8G8R8A8 disables HDR which will boost storing to disk due to less image information
renderTarget2D->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA8;
renderTarget2D->bGPUSharedFlag = true; // demand buffer on GPU
// Assign RenderTarget
captureComponent->GetCaptureComponent2D()->TextureTarget = renderTarget2D;
// Set Camera Properties
captureComponent->GetCaptureComponent2D()->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
captureComponent->GetCaptureComponent2D()->ShowFlags.SetTemporalAA(true);
// lookup more showflags in the UE4 documentation..
}
// Sets default values
ACaptureManager::ACaptureManager()
{
// 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;
}
// Called when the game starts or when spawned
void ACaptureManager::BeginPlay()
{
Super::BeginPlay();
// Setup CaptureComponents
SetupColorCaptureComponents(ColorCaptureComponents);
}
// Called every frame
void ACaptureManager::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
AsyncSaveImageToDiskTask::AsyncSaveImageToDiskTask(TArray<uint8> Image, FString ImageName) {
ImageCopy = Image;
FileName = ImageName;
}
AsyncSaveImageToDiskTask::~AsyncSaveImageToDiskTask() {
//UE_LOG(LogTemp, Warning, TEXT("AsyncTaskDone"));
}
void AsyncSaveImageToDiskTask::DoWork() {
FFileHelper::SaveArrayToFile(ImageCopy, *FileName);
UE_LOG(LogTemp, Log, TEXT("Stored Image: %s"), *FileName);
}
void ACaptureManager::RunAsyncImageSaveTask(TArray<uint8> Image, FString ImageName) {
(new FAutoDeleteAsyncTask<AsyncSaveImageToDiskTask>(Image, ImageName))->StartBackgroundTask();
}
// Called every frame
void ACaptureManager::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Read pixels once RenderFence is completed
if (!RenderRequestQueue.IsEmpty()) {
// Peek the next RenderRequest from queue
FRenderRequest* nextRenderRequest = nullptr;
RenderRequestQueue.Peek(nextRenderRequest);
int32 frameWidth = 640;
int32 frameHeight = 480;
if (nextRenderRequest) { //nullptr check
if (nextRenderRequest->RenderFence.IsFenceComplete()) { // Check if rendering is done, indicated by RenderFence
// Decide storing of data, either jpeg or png
if (nextRenderRequest->isPNG) {
//Generate image name
FString fileName = FPaths::ProjectSavedDir();
fileName += ".png"; // Add file ending
// Prepare data to be written to disk
static TSharedPtr<IImageWrapper> imageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); //EImageFormat::PNG //EImageFormat::JPEG
imageWrapper->SetRaw(nextRenderRequest->Image.GetData(), nextRenderRequest->Image.GetAllocatedSize(), frameWidth, frameHeight, ERGBFormat::BGRA, 8);
const TArray<uint8>& ImgData = imageWrapper->GetCompressed(5);
RunAsyncImageSaveTask(ImgData, fileName);
}
else {
UE_LOG(LogTemp, Log, TEXT("Started Saving Color Image"));
// Generate image name
FString fileName = FPaths::ProjectSavedDir();
fileName += ".jpeg"; // Add file ending
// Prepare data to be written to disk
static TSharedPtr<IImageWrapper> imageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG); //EImageFormat::PNG //EImageFormat::JPEG
imageWrapper->SetRaw(nextRenderRequest->Image.GetData(), nextRenderRequest->Image.GetAllocatedSize(), frameWidth, frameHeight, ERGBFormat::BGRA, 8);
const TArray<uint8>& ImgData = imageWrapper->GetCompressed(0);
RunAsyncImageSaveTask(ImgData, fileName);
}
// Delete the first element from RenderQueue
RenderRequestQueue.Pop();
delete nextRenderRequest;
UE_LOG(LogTemp, Log, TEXT("Done..."));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment