Removing the PixelReader2
This commit is contained in:
parent
a9a8e88ed9
commit
2ff36dbfda
|
@ -8,6 +8,7 @@
|
|||
#include "Carla/Sensor/PixelReader.h"
|
||||
|
||||
#include "Engine/TextureRenderTarget2D.h"
|
||||
#include "Async/Async.h"
|
||||
#include "HighResScreenshot.h"
|
||||
#include "Runtime/ImageWriteQueue/Public/ImageWriteQueue.h"
|
||||
|
||||
|
@ -36,18 +37,15 @@ struct LockTexture
|
|||
// -- Static local functions ---------------------------------------------------
|
||||
// =============================================================================
|
||||
|
||||
// Temporal; this avoid allocating the array each time and also avoids checking
|
||||
// for a bigger texture, ReadSurfaceData will allocate the space needed.
|
||||
TArray<FColor> gPixels;
|
||||
|
||||
static void WritePixelsToBuffer_Vulkan(
|
||||
const UTextureRenderTarget2D &RenderTarget,
|
||||
carla::Buffer &Buffer,
|
||||
uint32 Offset,
|
||||
FRHICommandListImmediate &InRHICmdList)
|
||||
FRHICommandListImmediate &RHICmdList,
|
||||
FPixelReader::Payload FuncForSending)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("WritePixelsToBuffer_Vulkan");
|
||||
check(IsInRenderingThread());
|
||||
|
||||
auto RenderResource =
|
||||
static_cast<const FTextureRenderTarget2DResource *>(RenderTarget.Resource);
|
||||
FTexture2DRHIRef Texture = RenderResource->GetRenderTargetTexture();
|
||||
|
@ -56,21 +54,46 @@ static void WritePixelsToBuffer_Vulkan(
|
|||
return;
|
||||
}
|
||||
|
||||
FIntPoint Rect = RenderResource->GetSizeXY();
|
||||
auto BackBufferReadback = std::make_unique<FRHIGPUTextureReadback>(TEXT("CameraBufferReadback"));
|
||||
FIntPoint BackBufferSize = Texture->GetSizeXY();
|
||||
EPixelFormat BackBufferPixelFormat = Texture->GetFormat();
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("EnqueueCopy");
|
||||
BackBufferReadback->EnqueueCopy(RHICmdList, Texture, FResolveRect(0, 0, BackBufferSize.X, BackBufferSize.Y));
|
||||
}
|
||||
|
||||
// NS: Extra copy here, don't know how to avoid it.
|
||||
// workaround to force RHI with Vulkan to refresh the fences state in the middle of frame
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Read Surface");
|
||||
InRHICmdList.ReadSurfaceData(
|
||||
Texture,
|
||||
FIntRect(0, 0, Rect.X, Rect.Y),
|
||||
gPixels,
|
||||
FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX));
|
||||
FRenderQueryRHIRef Query = RHICreateRenderQuery(RQT_AbsoluteTime);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("create query");
|
||||
RHICmdList.EndRenderQuery(Query);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Flush");
|
||||
RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("query result");
|
||||
uint64 OldAbsTime = 0;
|
||||
RHICmdList.GetRenderQueryResult(Query, OldAbsTime, true);
|
||||
}
|
||||
|
||||
AsyncTask(ENamedThreads::AnyNormalThreadNormalTask, [=, Readback=std::move(BackBufferReadback)]() mutable {
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Buffer Copy");
|
||||
Buffer.copy_from(Offset, gPixels);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Wait GPU transfer");
|
||||
while (!Readback->IsReady())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
FPixelFormatInfo PixelFormat = GPixelFormats[BackBufferPixelFormat];
|
||||
int32 Count = (BackBufferSize.Y * (PixelFormat.BlockBytes * BackBufferSize.X));
|
||||
void* LockedData = Readback->Lock(Count);
|
||||
if (LockedData)
|
||||
{
|
||||
FuncForSending(LockedData, Count, Offset);
|
||||
}
|
||||
Readback->Unlock();
|
||||
Readback.reset();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Temporal; this avoid allocating the array each time
|
||||
|
@ -78,12 +101,13 @@ TArray<FFloat16Color> gFloatPixels;
|
|||
|
||||
static void WriteFloatPixelsToBuffer_Vulkan(
|
||||
const UTextureRenderTarget2D &RenderTarget,
|
||||
carla::Buffer &Buffer,
|
||||
uint32 Offset,
|
||||
FRHICommandListImmediate &InRHICmdList)
|
||||
FRHICommandListImmediate &RHICmdList,
|
||||
FPixelReader::Payload FuncForSending)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("WritePixelsToBuffer_Vulkan");
|
||||
check(IsInRenderingThread());
|
||||
gFloatPixels.Empty();
|
||||
|
||||
auto RenderResource =
|
||||
static_cast<const FTextureRenderTarget2DResource *>(RenderTarget.Resource);
|
||||
FTexture2DRHIRef Texture = RenderResource->GetRenderTargetTexture();
|
||||
|
@ -92,25 +116,58 @@ static void WriteFloatPixelsToBuffer_Vulkan(
|
|||
return;
|
||||
}
|
||||
|
||||
FIntPoint Rect = RenderResource->GetSizeXY();
|
||||
auto BackBufferReadback = std::make_unique<FRHIGPUTextureReadback>(TEXT("CameraBufferReadback"));
|
||||
FIntPoint BackBufferSize = Texture->GetSizeXY();
|
||||
EPixelFormat BackBufferPixelFormat = Texture->GetFormat();
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("EnqueueCopy");
|
||||
BackBufferReadback->EnqueueCopy(RHICmdList, Texture, FResolveRect(0, 0, BackBufferSize.X, BackBufferSize.Y));
|
||||
}
|
||||
|
||||
// NS: Extra copy here, don't know how to avoid it.
|
||||
InRHICmdList.ReadSurfaceFloatData(
|
||||
Texture,
|
||||
FIntRect(0, 0, Rect.X, Rect.Y),
|
||||
gFloatPixels,
|
||||
CubeFace_PosX,0,0);
|
||||
// workaround to force RHI with Vulkan to refresh the fences state in the middle of frame
|
||||
{
|
||||
FRenderQueryRHIRef Query = RHICreateRenderQuery(RQT_AbsoluteTime);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("create query");
|
||||
RHICmdList.EndRenderQuery(Query);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Flush");
|
||||
RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("query result");
|
||||
uint64 OldAbsTime = 0;
|
||||
RHICmdList.GetRenderQueryResult(Query, OldAbsTime, true);
|
||||
}
|
||||
|
||||
AsyncTask(ENamedThreads::AnyNormalThreadNormalTask, [=, Readback=std::move(BackBufferReadback)]() mutable {
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Wait GPU transfer");
|
||||
while (!Readback->IsReady())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
FPixelFormatInfo PixelFormat = GPixelFormats[BackBufferPixelFormat];
|
||||
int32 Count = (BackBufferSize.Y * (PixelFormat.BlockBytes * BackBufferSize.X));
|
||||
int32 TotalPixels = (BackBufferSize.Y * BackBufferSize.X);
|
||||
void* LockedData = Readback->Lock(Count);
|
||||
if (LockedData)
|
||||
{
|
||||
TArray<float> IntermediateBuffer;
|
||||
IntermediateBuffer.Reserve(gFloatPixels.Num() * 2);
|
||||
for (FFloat16Color& color : gFloatPixels) {
|
||||
float x = (color.R.GetFloat() - 0.5f)*4.f;
|
||||
float y = (color.G.GetFloat() - 0.5f)*4.f;
|
||||
FFloat16Color *Data = reinterpret_cast<FFloat16Color *>(LockedData);
|
||||
IntermediateBuffer.Reserve(TotalPixels * 2);
|
||||
for (int i=0; i<TotalPixels; ++i)
|
||||
{
|
||||
float x = (Data->R.GetFloat() - 0.5f) * 4.f;
|
||||
float y = (Data->G.GetFloat() - 0.5f) * 4.f;
|
||||
IntermediateBuffer.Add(x);
|
||||
IntermediateBuffer.Add(y);
|
||||
++Data;
|
||||
}
|
||||
FuncForSending(reinterpret_cast<void *>(IntermediateBuffer.GetData()), TotalPixels * sizeof(float) * 2 , Offset);
|
||||
}
|
||||
Readback->Unlock();
|
||||
Readback.reset();
|
||||
});
|
||||
}
|
||||
Buffer.copy_from(Offset, IntermediateBuffer);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// -- FPixelReader -------------------------------------------------------------
|
||||
|
@ -171,11 +228,10 @@ TFuture<bool> FPixelReader::SavePixelsToDisk(
|
|||
|
||||
void FPixelReader::WritePixelsToBuffer(
|
||||
UTextureRenderTarget2D &RenderTarget,
|
||||
carla::Buffer &Buffer,
|
||||
uint32 Offset,
|
||||
FRHICommandListImmediate &InRHICmdList,
|
||||
bool use16BitFormat
|
||||
)
|
||||
FPixelReader::Payload FuncForSending,
|
||||
bool use16BitFormat)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("WritePixelsToBuffer");
|
||||
check(IsInRenderingThread());
|
||||
|
@ -184,15 +240,15 @@ void FPixelReader::WritePixelsToBuffer(
|
|||
{
|
||||
if (use16BitFormat)
|
||||
{
|
||||
WriteFloatPixelsToBuffer_Vulkan(RenderTarget, Buffer, Offset, InRHICmdList);
|
||||
WriteFloatPixelsToBuffer_Vulkan(RenderTarget, Offset, InRHICmdList, std::move(FuncForSending));
|
||||
}
|
||||
else
|
||||
{
|
||||
WritePixelsToBuffer_Vulkan(RenderTarget, Buffer, Offset, InRHICmdList);
|
||||
WritePixelsToBuffer_Vulkan(RenderTarget, Offset, InRHICmdList, std::move(FuncForSending));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
FTextureRenderTargetResource* RenderTargetResource = RenderTarget.GetRenderTargetResource();
|
||||
if(!RenderTargetResource)
|
||||
{
|
||||
|
@ -235,4 +291,5 @@ void FPixelReader::WritePixelsToBuffer(
|
|||
Buffer.copy_from(Offset, Source, ExpectedStride * Height);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Runtime/ImageWriteQueue/Public/ImagePixelData.h"
|
||||
|
||||
#include <compiler/disable-ue4-macros.h>
|
||||
#include <carla/Logging.h>
|
||||
#include <carla/Buffer.h>
|
||||
#include <carla/sensor/SensorRegistry.h>
|
||||
#include <compiler/enable-ue4-macros.h>
|
||||
|
@ -26,6 +27,8 @@ class FPixelReader
|
|||
{
|
||||
public:
|
||||
|
||||
using Payload = std::function<void(void *, uint32, uint32)>;
|
||||
|
||||
/// Copy the pixels in @a RenderTarget into @a BitMap.
|
||||
///
|
||||
/// @pre To be called from game-thread.
|
||||
|
@ -71,9 +74,9 @@ private:
|
|||
/// @pre To be called from render-thread.
|
||||
static void WritePixelsToBuffer(
|
||||
UTextureRenderTarget2D &RenderTarget,
|
||||
carla::Buffer &Buffer,
|
||||
uint32 Offset,
|
||||
FRHICommandListImmediate &InRHICmdList,
|
||||
FPixelReader::Payload FuncForSending,
|
||||
bool use16BitFormat = false);
|
||||
|
||||
};
|
||||
|
@ -101,20 +104,28 @@ void FPixelReader::SendPixelsInRenderThread(TSensor &Sensor, bool use16BitFormat
|
|||
// game-thread.
|
||||
ENQUEUE_RENDER_COMMAND(FWritePixels_SendPixelsInRenderThread)
|
||||
(
|
||||
[&Sensor, Stream=Sensor.GetDataStream(Sensor), use16BitFormat](auto &InRHICmdList) mutable
|
||||
[&Sensor, use16BitFormat](auto &InRHICmdList) mutable
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("FWritePixels_SendPixelsInRenderThread");
|
||||
|
||||
/// @todo Can we make sure the sensor is not going to be destroyed?
|
||||
if (!Sensor.IsPendingKill())
|
||||
{
|
||||
auto Buffer = Stream.PopBufferFromPool();
|
||||
WritePixelsToBuffer(
|
||||
*Sensor.CaptureRenderTarget,
|
||||
Buffer,
|
||||
carla::sensor::SensorRegistry::get<TSensor *>::type::header_offset,
|
||||
InRHICmdList, use16BitFormat);
|
||||
FPixelReader::Payload FuncForSending = [&Sensor, Frame = FCarlaEngine::GetFrameCounter()](void *LockedData, uint32 Count, uint32 Offset)
|
||||
{
|
||||
if (Sensor.IsPendingKill()) return;
|
||||
|
||||
auto Stream = Sensor.GetDataStream(Sensor);
|
||||
// Stream.SetFrameNumber(Frame);
|
||||
auto Buffer = Stream.PopBufferFromPool();
|
||||
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Buffer Copy");
|
||||
Buffer.copy_from(Offset, boost::asio::buffer(LockedData, Count));
|
||||
}
|
||||
{
|
||||
// send
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Sending buffer");
|
||||
if(Buffer.data())
|
||||
{
|
||||
SCOPE_CYCLE_COUNTER(STAT_CarlaSensorStreamSend);
|
||||
|
@ -122,6 +133,15 @@ void FPixelReader::SendPixelsInRenderThread(TSensor &Sensor, bool use16BitFormat
|
|||
Stream.Send(Sensor, std::move(Buffer));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WritePixelsToBuffer(
|
||||
*Sensor.CaptureRenderTarget,
|
||||
carla::sensor::SensorRegistry::get<TSensor *>::type::header_offset,
|
||||
InRHICmdList,
|
||||
std::move(FuncForSending),
|
||||
use16BitFormat);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -1,220 +0,0 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB).
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include "Carla.h"
|
||||
#include "Carla/Sensor/PixelReader2.h"
|
||||
|
||||
#include "Engine/TextureRenderTarget2D.h"
|
||||
#include "Async/Async.h"
|
||||
#include "HighResScreenshot.h"
|
||||
#include "Runtime/ImageWriteQueue/Public/ImageWriteQueue.h"
|
||||
|
||||
// =============================================================================
|
||||
// -- Local variables and types ------------------------------------------------
|
||||
// =============================================================================
|
||||
|
||||
struct LockTexture2
|
||||
{
|
||||
LockTexture2(FRHITexture2D *InTexture, uint32 &Stride)
|
||||
: Texture(InTexture),
|
||||
Source(reinterpret_cast<const uint8 *>(
|
||||
RHILockTexture2D(Texture, 0, RLM_ReadOnly, Stride, false))) {}
|
||||
|
||||
~LockTexture2()
|
||||
{
|
||||
RHIUnlockTexture2D(Texture, 0, false);
|
||||
}
|
||||
|
||||
FRHITexture2D *Texture;
|
||||
|
||||
const uint8 *Source;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// -- Static local functions ---------------------------------------------------
|
||||
// =============================================================================
|
||||
|
||||
// Temporal; this avoid allocating the array each time and also avoids checking
|
||||
// for a bigger texture, ReadSurfaceData will allocate the space needed.
|
||||
TArray<FColor> gPixels2;
|
||||
|
||||
static void WritePixelsToBuffer_Vulkan2(
|
||||
const UTextureRenderTarget2D &RenderTarget,
|
||||
uint32 Offset,
|
||||
FRHICommandListImmediate &RHICmdList,
|
||||
std::function<void(void *, uint32, uint32)> FuncForSending)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__);
|
||||
check(IsInRenderingThread());
|
||||
|
||||
auto RenderResource = static_cast<const FTextureRenderTarget2DResource *>(RenderTarget.Resource);
|
||||
FTexture2DRHIRef Texture = RenderResource->GetRenderTargetTexture();
|
||||
if (!Texture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto BackBufferReadback = std::make_unique<FRHIGPUTextureReadback>(TEXT("CameraBufferReadback"));
|
||||
FIntPoint BackBufferSize = Texture->GetSizeXY();
|
||||
EPixelFormat BackBufferPixelFormat = Texture->GetFormat();
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("EnqueueCopy");
|
||||
BackBufferReadback->EnqueueCopy(RHICmdList, Texture, FResolveRect(0, 0, BackBufferSize.X, BackBufferSize.Y));
|
||||
}
|
||||
|
||||
// workaround to force RHI with Vulkan to refresh the fences state in the middle of frame
|
||||
{
|
||||
FRenderQueryRHIRef Query = RHICreateRenderQuery(RQT_AbsoluteTime);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("create query");
|
||||
RHICmdList.EndRenderQuery(Query);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Flush");
|
||||
RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("query result");
|
||||
uint64 OldAbsTime = 0;
|
||||
RHICmdList.GetRenderQueryResult(Query, OldAbsTime, true);
|
||||
}
|
||||
|
||||
AsyncTask(ENamedThreads::AnyNormalThreadNormalTask, [=, Readback=std::move(BackBufferReadback)]() mutable {
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Wait GPU transfer");
|
||||
while (!Readback->IsReady())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
FPixelFormatInfo PixelFormat = GPixelFormats[BackBufferPixelFormat];
|
||||
int32 Count = (BackBufferSize.Y * (PixelFormat.BlockBytes * BackBufferSize.X));
|
||||
void* LockedData = Readback->Lock(Count);
|
||||
if (LockedData)
|
||||
{
|
||||
FuncForSending(LockedData, Count, Offset);
|
||||
}
|
||||
Readback->Unlock();
|
||||
Readback.reset();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Temporal; this avoid allocating the array each time
|
||||
TArray<FFloat16Color> gFloatPixels2;
|
||||
|
||||
static void WriteFloatPixelsToBuffer_Vulkan2(
|
||||
const UTextureRenderTarget2D &RenderTarget,
|
||||
carla::Buffer &Buffer,
|
||||
uint32 Offset,
|
||||
FRHICommandListImmediate &InRHICmdList)
|
||||
|
||||
{
|
||||
check(IsInRenderingThread());
|
||||
gFloatPixels2.Empty();
|
||||
auto RenderResource =
|
||||
static_cast<const FTextureRenderTarget2DResource *>(RenderTarget.Resource);
|
||||
FTexture2DRHIRef Texture = RenderResource->GetRenderTargetTexture();
|
||||
if (!Texture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FIntPoint Rect = RenderResource->GetSizeXY();
|
||||
|
||||
// NS: Extra copy here, don't know how to avoid it.
|
||||
InRHICmdList.ReadSurfaceFloatData(
|
||||
Texture,
|
||||
FIntRect(0, 0, Rect.X, Rect.Y),
|
||||
gFloatPixels2,
|
||||
CubeFace_PosX,0,0);
|
||||
|
||||
TArray<float> IntermediateBuffer;
|
||||
IntermediateBuffer.Reserve(gFloatPixels2.Num() * 2);
|
||||
for (FFloat16Color& color : gFloatPixels2) {
|
||||
float x = (color.R.GetFloat() - 0.5f)*4.f;
|
||||
float y = (color.G.GetFloat() - 0.5f)*4.f;
|
||||
IntermediateBuffer.Add(x);
|
||||
IntermediateBuffer.Add(y);
|
||||
}
|
||||
Buffer.copy_from(Offset, IntermediateBuffer);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// -- FPixelReader -------------------------------------------------------------
|
||||
// =============================================================================
|
||||
|
||||
bool FPixelReader2::WritePixelsToArray(
|
||||
UTextureRenderTarget2D &RenderTarget,
|
||||
TArray<FColor> &BitMap)
|
||||
{
|
||||
check(IsInGameThread());
|
||||
FTextureRenderTargetResource *RTResource =
|
||||
RenderTarget.GameThread_GetRenderTargetResource();
|
||||
if (RTResource == nullptr)
|
||||
{
|
||||
UE_LOG(LogCarla, Error, TEXT("FPixelReader2: UTextureRenderTarget2D missing render target"));
|
||||
return false;
|
||||
}
|
||||
FReadSurfaceDataFlags ReadPixelFlags(RCM_UNorm);
|
||||
ReadPixelFlags.SetLinearToGamma(true);
|
||||
return RTResource->ReadPixels(BitMap, ReadPixelFlags);
|
||||
}
|
||||
|
||||
TUniquePtr<TImagePixelData<FColor>> FPixelReader2::DumpPixels(
|
||||
UTextureRenderTarget2D &RenderTarget)
|
||||
{
|
||||
const FIntPoint DestSize(RenderTarget.GetSurfaceWidth(), RenderTarget.GetSurfaceHeight());
|
||||
TUniquePtr<TImagePixelData<FColor>> PixelData = MakeUnique<TImagePixelData<FColor>>(DestSize);
|
||||
TArray<FColor> Pixels(PixelData->Pixels.GetData(), PixelData->Pixels.Num());
|
||||
if (!WritePixelsToArray(RenderTarget, Pixels))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return PixelData;
|
||||
}
|
||||
|
||||
TFuture<bool> FPixelReader2::SavePixelsToDisk(
|
||||
UTextureRenderTarget2D &RenderTarget,
|
||||
const FString &FilePath)
|
||||
{
|
||||
return SavePixelsToDisk(DumpPixels(RenderTarget), FilePath);
|
||||
}
|
||||
|
||||
TFuture<bool> FPixelReader2::SavePixelsToDisk(
|
||||
TUniquePtr<TImagePixelData<FColor>> PixelData,
|
||||
const FString &FilePath)
|
||||
{
|
||||
TUniquePtr<FImageWriteTask> ImageTask = MakeUnique<FImageWriteTask>();
|
||||
ImageTask->PixelData = MoveTemp(PixelData);
|
||||
ImageTask->Filename = FilePath;
|
||||
ImageTask->Format = EImageFormat::PNG;
|
||||
ImageTask->CompressionQuality = (int32) EImageCompressionQuality::Default;
|
||||
ImageTask->bOverwriteFile = true;
|
||||
ImageTask->PixelPreProcessors.Add(TAsyncAlphaWrite<FColor>(255));
|
||||
|
||||
FHighResScreenshotConfig &HighResScreenshotConfig = GetHighResScreenshotConfig();
|
||||
return HighResScreenshotConfig.ImageWriteQueue->Enqueue(MoveTemp(ImageTask));
|
||||
}
|
||||
|
||||
void FPixelReader2::WritePixelsToBuffer(
|
||||
UTextureRenderTarget2D &RenderTarget,
|
||||
uint32 Offset,
|
||||
FRHICommandListImmediate &InRHICmdList,
|
||||
std::function<void(void *, uint32, uint32)> FuncForSending,
|
||||
bool use16BitFormat)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__);
|
||||
check(IsInRenderingThread());
|
||||
|
||||
if (IsVulkanPlatform(GMaxRHIShaderPlatform) || IsD3DPlatform(GMaxRHIShaderPlatform, false))
|
||||
{
|
||||
if (use16BitFormat)
|
||||
{
|
||||
// WriteFloatPixelsToBuffer_Vulkan2(RenderTarget, Buffer, Offset, InRHICmdList);
|
||||
}
|
||||
else
|
||||
{
|
||||
WritePixelsToBuffer_Vulkan2(RenderTarget, Offset, InRHICmdList, std::move(FuncForSending));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB).
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreGlobals.h"
|
||||
#include "Engine/TextureRenderTarget2D.h"
|
||||
#include "Runtime/ImageWriteQueue/Public/ImagePixelData.h"
|
||||
|
||||
#include <compiler/disable-ue4-macros.h>
|
||||
#include <carla/Logging.h>
|
||||
#include <carla/Buffer.h>
|
||||
#include <carla/sensor/SensorRegistry.h>
|
||||
#include <compiler/enable-ue4-macros.h>
|
||||
|
||||
// =============================================================================
|
||||
// -- FPixelReader -------------------------------------------------------------
|
||||
// =============================================================================
|
||||
|
||||
/// Utils for reading pixels from UTextureRenderTarget2D.
|
||||
///
|
||||
/// @todo This class only supports PF_R8G8B8A8 format.
|
||||
class FPixelReader2
|
||||
{
|
||||
public:
|
||||
|
||||
/// Copy the pixels in @a RenderTarget into @a BitMap.
|
||||
///
|
||||
/// @pre To be called from game-thread.
|
||||
static bool WritePixelsToArray(
|
||||
UTextureRenderTarget2D &RenderTarget,
|
||||
TArray<FColor> &BitMap);
|
||||
|
||||
/// Dump the pixels in @a RenderTarget.
|
||||
///
|
||||
/// @pre To be called from game-thread.
|
||||
static TUniquePtr<TImagePixelData<FColor>> DumpPixels(
|
||||
UTextureRenderTarget2D &RenderTarget);
|
||||
|
||||
/// Asynchronously save the pixels in @a RenderTarget to disk.
|
||||
///
|
||||
/// @pre To be called from game-thread.
|
||||
static TFuture<bool> SavePixelsToDisk(
|
||||
UTextureRenderTarget2D &RenderTarget,
|
||||
const FString &FilePath);
|
||||
|
||||
/// Asynchronously save the pixels in @a PixelData to disk.
|
||||
///
|
||||
/// @pre To be called from game-thread.
|
||||
static TFuture<bool> SavePixelsToDisk(
|
||||
TUniquePtr<TImagePixelData<FColor>> PixelData,
|
||||
const FString &FilePath);
|
||||
|
||||
/// Convenience function to enqueue a render command that sends the pixels
|
||||
/// down the @a Sensor's data stream. It expects a sensor derived from
|
||||
/// ASceneCaptureSensor or compatible.
|
||||
///
|
||||
/// Note that the serializer needs to define a "header_offset" that it's
|
||||
/// allocated in front of the buffer.
|
||||
///
|
||||
/// @pre To be called from game-thread.
|
||||
template <typename TSensor>
|
||||
static void SendPixelsInRenderThread(TSensor &Sensor, bool use16BitFormat = false);
|
||||
|
||||
private:
|
||||
|
||||
/// Copy the pixels in @a RenderTarget into @a Buffer.
|
||||
///
|
||||
/// @pre To be called from render-thread.
|
||||
static void WritePixelsToBuffer(
|
||||
UTextureRenderTarget2D &RenderTarget,
|
||||
uint32 Offset,
|
||||
FRHICommandListImmediate &InRHICmdList,
|
||||
std::function<void(void *, uint32, uint32)> FuncForSending,
|
||||
bool use16BitFormat = false);
|
||||
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// -- FPixelReader::SendPixelsInRenderThread -----------------------------------
|
||||
// =============================================================================
|
||||
|
||||
template <typename TSensor>
|
||||
void FPixelReader2::SendPixelsInRenderThread(TSensor &Sensor, bool use16BitFormat)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(FPixelReader2::SendPixelsInRenderThread);
|
||||
check(Sensor.CaptureRenderTarget != nullptr);
|
||||
|
||||
if (!Sensor.HasActorBegunPlay() || Sensor.IsPendingKill())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/// Blocks until the render thread has finished all it's tasks.
|
||||
Sensor.EnqueueRenderSceneImmediate();
|
||||
|
||||
// Enqueue a command in the render-thread that will write the image buffer to
|
||||
// the data stream. The stream is created in the capture thus executed in the
|
||||
// game-thread.
|
||||
ENQUEUE_RENDER_COMMAND(FWritePixels2_SendPixelsInRenderThread)
|
||||
(
|
||||
[&Sensor, use16BitFormat](auto &InRHICmdList) mutable
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("FWritePixels2_SendPixelsInRenderThread");
|
||||
|
||||
/// @todo Can we make sure the sensor is not going to be destroyed?
|
||||
if (!Sensor.IsPendingKill())
|
||||
{
|
||||
|
||||
std::function<void(void *, uint32, uint32)> FuncForSending = [&Sensor, Frame = FCarlaEngine::GetFrameCounter()](void *LockedData, uint32 Count, uint32 Offset)
|
||||
{
|
||||
if (Sensor.IsPendingKill()) return;
|
||||
|
||||
auto Stream = Sensor.GetDataStream(Sensor);
|
||||
// Stream.SetFrameNumber(Frame);
|
||||
auto Buffer = Stream.PopBufferFromPool();
|
||||
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Buffer Copy");
|
||||
Buffer.copy_from(Offset, boost::asio::buffer(LockedData, Count));
|
||||
}
|
||||
{
|
||||
// send
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Sending buffer");
|
||||
if(Buffer.data())
|
||||
{
|
||||
SCOPE_CYCLE_COUNTER(STAT_CarlaSensorStreamSend);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR("Stream Send");
|
||||
Stream.Send(Sensor, std::move(Buffer));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WritePixelsToBuffer(
|
||||
*Sensor.CaptureRenderTarget,
|
||||
carla::sensor::SensorRegistry::get<TSensor *>::type::header_offset,
|
||||
InRHICmdList,
|
||||
std::move(FuncForSending),
|
||||
use16BitFormat);
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Blocks until the render thread has finished all it's tasks
|
||||
Sensor.WaitForRenderThreadToFinish();
|
||||
}
|
|
@ -60,5 +60,5 @@ void ASceneCaptureCamera::PostPhysTick(UWorld *World, ELevelTick TickType, float
|
|||
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*ProfilerText);
|
||||
}
|
||||
);
|
||||
FPixelReader2::SendPixelsInRenderThread(*this);
|
||||
FPixelReader::SendPixelsInRenderThread(*this);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ class CARLA_API ASceneCaptureSensor : public ASensor
|
|||
|
||||
friend class ACarlaGameModeBase;
|
||||
friend class FPixelReader;
|
||||
friend class FPixelReader2;
|
||||
|
||||
public:
|
||||
|
||||
|
|
Loading…
Reference in New Issue