CARLA
PixelReader.h
Go to the documentation of this file.
1 // Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
2 // de Barcelona (UAB).
3 //
4 // This work is licensed under the terms of the MIT license.
5 // For a copy, see <https://opensource.org/licenses/MIT>.
6 
7 #pragma once
8 
9 #include "CoreGlobals.h"
10 #include "Engine/TextureRenderTarget2D.h"
11 #include "Runtime/ImageWriteQueue/Public/ImagePixelData.h"
12 
13 #ifdef _WIN32
14  #define WIN32_LEAN_AND_MEAN
15  #include <D3d12.h>
16 #endif
17 
18 #include "Carla/Game/CarlaEngine.h"
19 
21 #include <carla/Logging.h>
22 #include <carla/Buffer.h>
23 #include <carla/BufferView.h>
26 
27 // =============================================================================
28 // -- FPixelReader -------------------------------------------------------------
29 // =============================================================================
30 
31 /// Utils for reading pixels from UTextureRenderTarget2D.
32 ///
33 /// @todo This class only supports PF_R8G8B8A8 format.
35 {
36 public:
37 
38  using Payload = std::function<void(void *, uint32, uint32, uint32)>;
39 
40  /// Copy the pixels in @a RenderTarget into @a BitMap.
41  ///
42  /// @pre To be called from game-thread.
43  static bool WritePixelsToArray(
44  UTextureRenderTarget2D &RenderTarget,
45  TArray<FColor> &BitMap);
46 
47  /// Dump the pixels in @a RenderTarget.
48  ///
49  /// @pre To be called from game-thread.
50  static TUniquePtr<TImagePixelData<FColor>> DumpPixels(
51  UTextureRenderTarget2D &RenderTarget);
52 
53  /// Asynchronously save the pixels in @a RenderTarget to disk.
54  ///
55  /// @pre To be called from game-thread.
56  static TFuture<bool> SavePixelsToDisk(
57  UTextureRenderTarget2D &RenderTarget,
58  const FString &FilePath);
59 
60  /// Asynchronously save the pixels in @a PixelData to disk.
61  ///
62  /// @pre To be called from game-thread.
63  static TFuture<bool> SavePixelsToDisk(
64  TUniquePtr<TImagePixelData<FColor>> PixelData,
65  const FString &FilePath);
66 
67  /// Convenience function to enqueue a render command that sends the pixels
68  /// down the @a Sensor's data stream. It expects a sensor derived from
69  /// ASceneCaptureSensor or compatible.
70  ///
71  /// Note that the serializer needs to define a "header_offset" that it's
72  /// allocated in front of the buffer.
73  ///
74  /// @pre To be called from game-thread.
75  template <typename TSensor, typename TPixel>
76  static void SendPixelsInRenderThread(TSensor &Sensor, bool use16BitFormat = false, std::function<TArray<TPixel>(void *, uint32)> Conversor = {});
77 
78  /// Copy the pixels in @a RenderTarget into @a Buffer.
79  ///
80  /// @pre To be called from render-thread.
81  static void WritePixelsToBuffer(
82  const UTextureRenderTarget2D &RenderTarget,
83  uint32 Offset,
84  FRHICommandListImmediate &InRHICmdList,
85  FPixelReader::Payload FuncForSending);
86 
87 };
88 
89 // =============================================================================
90 // -- FPixelReader::SendPixelsInRenderThread -----------------------------------
91 // =============================================================================
92 
93 template <typename TSensor, typename TPixel>
94 void FPixelReader::SendPixelsInRenderThread(TSensor &Sensor, bool use16BitFormat, std::function<TArray<TPixel>(void *, uint32)> Conversor)
95 {
96  TRACE_CPUPROFILER_EVENT_SCOPE(FPixelReader::SendPixelsInRenderThread);
97  check(Sensor.CaptureRenderTarget != nullptr);
98 
99  if (!Sensor.HasActorBegunPlay() || Sensor.IsPendingKill())
100  {
101  return;
102  }
103 
104  /// Blocks until the render thread has finished all it's tasks.
105  Sensor.EnqueueRenderSceneImmediate();
106 
107  // Enqueue a command in the render-thread that will write the image buffer to
108  // the data stream. The stream is created in the capture thus executed in the
109  // game-thread.
110  ENQUEUE_RENDER_COMMAND(FWritePixels_SendPixelsInRenderThread)
111  (
112  [&Sensor, use16BitFormat, Conversor = std::move(Conversor)](auto &InRHICmdList) mutable
113  {
114  TRACE_CPUPROFILER_EVENT_SCOPE_STR("FWritePixels_SendPixelsInRenderThread");
115 
116  /// @todo Can we make sure the sensor is not going to be destroyed?
117  if (!Sensor.IsPendingKill())
118  {
119  FPixelReader::Payload FuncForSending =
120  [&Sensor, Frame = FCarlaEngine::GetFrameCounter(), Conversor = std::move(Conversor)](void *LockedData, uint32 Size, uint32 Offset, uint32 ExpectedRowBytes)
121  {
122  if (Sensor.IsPendingKill()) return;
123 
124  TArray<TPixel> Converted;
125 
126  // optional conversion of data
127  if (Conversor)
128  {
129  TRACE_CPUPROFILER_EVENT_SCOPE_STR("Data conversion");
130  Converted = Conversor(LockedData, Size);
131  LockedData = reinterpret_cast<void *>(Converted.GetData());
132  Size = Converted.Num() * Converted.GetTypeSize();
133  }
134 
135  auto Stream = Sensor.GetDataStream(Sensor);
136  Stream.SetFrameNumber(Frame);
137  auto Buffer = Stream.PopBufferFromPool();
138 
139  uint32 CurrentRowBytes = ExpectedRowBytes;
140 
141 #ifdef _WIN32
142  // DirectX uses additional bytes to align each row to 256 boundry,
143  // so we need to remove that extra data
144  if (IsD3DPlatform(GMaxRHIShaderPlatform, false))
145  {
146  CurrentRowBytes = Align(ExpectedRowBytes, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
147  if (ExpectedRowBytes != CurrentRowBytes)
148  {
149  TRACE_CPUPROFILER_EVENT_SCOPE_STR("Buffer Copy (windows, row by row)");
150  Buffer.reset(Offset + Size);
151  auto DstRow = Buffer.begin() + Offset;
152  const uint8 *SrcRow = reinterpret_cast<uint8 *>(LockedData);
153  uint32 i = 0;
154  while (i < Size)
155  {
156  FMemory::Memcpy(DstRow, SrcRow, ExpectedRowBytes);
157  DstRow += ExpectedRowBytes;
158  SrcRow += CurrentRowBytes;
159  i += ExpectedRowBytes;
160  }
161  }
162  }
163 #endif // _WIN32
164 
165  if (ExpectedRowBytes == CurrentRowBytes)
166  {
167  check(ExpectedRowBytes == CurrentRowBytes);
168  TRACE_CPUPROFILER_EVENT_SCOPE_STR("Buffer Copy");
169  Buffer.copy_from(Offset, boost::asio::buffer(LockedData, Size));
170  }
171 
172  {
173  // send
174  TRACE_CPUPROFILER_EVENT_SCOPE_STR("Sending buffer");
175  if(Buffer.data())
176  {
177  // serialize data
178  carla::Buffer BufferReady(std::move(carla::sensor::SensorRegistry::Serialize(Sensor, std::move(Buffer))));
179  carla::SharedBufferView BufView = carla::BufferView::CreateFrom(std::move(BufferReady));
180 
181  // ROS2
182  #if defined(WITH_ROS2)
183  auto ROS2 = carla::ros2::ROS2::GetInstance();
184  if (ROS2->IsEnabled())
185  {
186  TRACE_CPUPROFILER_EVENT_SCOPE_STR("ROS2 Send PixelReader");
187  auto StreamId = carla::streaming::detail::token_type(Sensor.GetToken()).get_stream_id();
188  auto Res = std::async(std::launch::async, [&Sensor, ROS2, &Stream, StreamId, BufView]()
189  {
190  // get resolution of camera
191  int W = -1, H = -1;
192  float Fov = -1.0f;
193  auto WidthOpt = Sensor.GetAttribute("image_size_x");
194  if (WidthOpt.has_value())
195  W = FCString::Atoi(*WidthOpt->Value);
196  auto HeightOpt = Sensor.GetAttribute("image_size_y");
197  if (HeightOpt.has_value())
198  H = FCString::Atoi(*HeightOpt->Value);
199  auto FovOpt = Sensor.GetAttribute("fov");
200  if (FovOpt.has_value())
201  Fov = FCString::Atof(*FovOpt->Value);
202  // send data to ROS2
203  AActor* ParentActor = Sensor.GetAttachParentActor();
204  if (ParentActor)
205  {
206  FTransform LocalTransformRelativeToParent = Sensor.GetActorTransform().GetRelativeTransform(ParentActor->GetActorTransform());
207  ROS2->ProcessDataFromCamera(Stream.GetSensorType(), StreamId, LocalTransformRelativeToParent, W, H, Fov, BufView, &Sensor);
208  }
209  else
210  {
211  ROS2->ProcessDataFromCamera(Stream.GetSensorType(), StreamId, Stream.GetSensorTransform(), W, H, Fov, BufView, &Sensor);
212  }
213  });
214  }
215  #endif
216 
217  // network
218  SCOPE_CYCLE_COUNTER(STAT_CarlaSensorStreamSend);
219  TRACE_CPUPROFILER_EVENT_SCOPE_STR("Stream Send");
220  Stream.Send(Sensor, BufView);
221  }
222  }
223  };
224 
226  *Sensor.CaptureRenderTarget,
228  InRHICmdList,
229  std::move(FuncForSending));
230  }
231  }
232  );
233 
234  // Blocks until the render thread has finished all it's tasks
235  Sensor.WaitForRenderThreadToFinish();
236 }
static TUniquePtr< TImagePixelData< FColor > > DumpPixels(UTextureRenderTarget2D &RenderTarget)
Dump the pixels in RenderTarget.
static bool WritePixelsToArray(UTextureRenderTarget2D &RenderTarget, TArray< FColor > &BitMap)
Copy the pixels in RenderTarget into BitMap.
Definition: PixelReader.cpp:83
static void SendPixelsInRenderThread(TSensor &Sensor, bool use16BitFormat=false, std::function< TArray< TPixel >(void *, uint32)> Conversor={})
Convenience function to enqueue a render command that sends the pixels down the Sensor&#39;s data stream...
Definition: PixelReader.h:94
typename detail::CompileTimeTypeMapImpl< sizeof...(Items), Items... >::template get< InKey > get
std::deque< std::shared_ptr< SimpleWaypoint > > Buffer
Utils for reading pixels from UTextureRenderTarget2D.
Definition: PixelReader.h:34
static std::shared_ptr< BufferView > CreateFrom(Buffer &&buffer)
Definition: BufferView.h:56
static void WritePixelsToBuffer(const UTextureRenderTarget2D &RenderTarget, uint32 Offset, FRHICommandListImmediate &InRHICmdList, FPixelReader::Payload FuncForSending)
Copy the pixels in RenderTarget into Buffer.
Definition: PixelReader.cpp:19
static uint64_t GetFrameCounter()
Definition: CarlaEngine.h:65
static Buffer Serialize(Sensor &sensor, Args &&... args)
Serialize the arguments provided into a Buffer by calling to the serializer registered for the given ...
detail::Stream< detail::MultiStreamState > Stream
A stream represents an unidirectional channel for sending data from server to client.
Definition: Stream.h:19
A piece of raw data.
Definition: carla/Buffer.h:42
std::shared_ptr< BufferView > SharedBufferView
Definition: BufferView.h:151
static std::shared_ptr< ROS2 > GetInstance()
Definition: ROS2.h:51
std::function< void(void *, uint32, uint32, uint32)> Payload
Definition: PixelReader.h:38
static TFuture< bool > SavePixelsToDisk(UTextureRenderTarget2D &RenderTarget, const FString &FilePath)
Asynchronously save the pixels in RenderTarget to disk.
carla::streaming::detail::token_type token_type