CARLA
Loading...
Searching...
No Matches
CarlaLidarSubsystem.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 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
8#include "Carla.h"
13
14#include <util/ue-header-guard-begin.h>
15#include "Async/ParallelFor.h"
16#include "PhysicsEngine/PhysicsObjectExternalInterface.h"
17#include <util/ue-header-guard-end.h>
18
19static TAutoConsoleVariable<int32> CVarLidarBatch(
20 TEXT("carla.Lidar.Batch"), 1,
21 TEXT("Simulate multiple LiDARs in parallel (1) or one after another (0)."));
22
24{
25 if (LidarSimulationTask.IsValid())
26 {
27 FTaskGraphInterface::Get().WaitUntilTaskCompletes(LidarSimulationTask);
28 LidarSimulationTask = nullptr;
29 }
30 LidarsToSimulate.Empty();
31 ActiveLidars.Empty();
32 Super::Deinitialize();
33}
34
36{
37 if (Lidar != nullptr)
38 {
39 ActiveLidars.AddUnique(Lidar);
40
41 // Remove from FSensorManager so this subsystem is the sole manager of LiDAR ticking
42 UCarlaEpisode* CurrentEpisode = UCarlaStatics::GetCurrentEpisode(GetWorld());
43 if (CurrentEpisode != nullptr)
44 {
45 CurrentEpisode->GetSensorManager().DeRegisterSensor(Lidar);
46 }
47 }
48}
49
51{
52 ActiveLidars.Remove(Lidar);
53 ActiveLidars.RemoveAll([](const TWeakObjectPtr<ARayCastSemanticLidar>& Ptr) { return !Ptr.IsValid(); });
54}
55
56void UCarlaLidarSubsystem::StartLidarSimulations(UWorld *World, [[maybe_unused]] float DeltaSeconds)
57{
58 TRACE_CPUPROFILER_EVENT_SCOPE(UCarlaLidarSubsystem::StartLidarSimulations);
59
60 if (World == nullptr || World->GetPhysicsScene() == nullptr || !HasActiveLidars())
61 {
62 return;
63 }
64
65 // Snapshot on the game thread; the task must not read ActiveLidars while the game thread can modify it.
66 LidarsToSimulate.Reset();
67 for (const TWeakObjectPtr<ARayCastSemanticLidar> &WeakLidar : ActiveLidars)
68 {
69 ARayCastSemanticLidar *Lidar = WeakLidar.Get();
70 if (Lidar != nullptr && Lidar->IsReadyToTick())
71 {
72 LidarsToSimulate.Add(Lidar);
73 }
74 }
75
76 if (LidarsToSimulate.Num() == 0)
77 {
78 return;
79 }
80
81 const bool bBatch = (CVarLidarBatch.GetValueOnGameThread() != 0) && (LidarsToSimulate.Num() > 1);
82
83 // Dispatch raycast simulation as a high-priority Task Graph job on worker threads.
84 // Each LiDAR uses its own tick-interval delta, not the physics substep delta.
85 LidarSimulationTask = FFunctionGraphTask::CreateAndDispatchWhenReady(
86 [this, World, bBatch]()
87 {
88 TRACE_CPUPROFILER_EVENT_SCOPE(TaskGraphLidarSimulations);
89 auto LockedPhysObject = FPhysicsObjectExternalInterface::LockRead(World->GetPhysicsScene());
90
91 if (bBatch)
92 {
93 ParallelFor(LidarsToSimulate.Num(), [this](int32 Index)
94 {
95 ARayCastSemanticLidar *Lidar = LidarsToSimulate[Index];
96 Lidar->SimulateLidar(Lidar->GetReadyToTickDeltaSeconds(), /*bLockPhysics=*/false);
97 });
98 }
99 else
100 {
102 {
103 Lidar->SimulateLidar(Lidar->GetReadyToTickDeltaSeconds(), /*bLockPhysics=*/false);
104 }
105 }
106
107 LockedPhysObject.Release();
108 },
109 TStatId(),
110 nullptr,
111 ENamedThreads::AnyBackgroundHiPriTask
112 );
113}
114
115void UCarlaLidarSubsystem::FinishLidarSimulations(UWorld *World, [[maybe_unused]] float DeltaSeconds)
116{
117 TRACE_CPUPROFILER_EVENT_SCOPE(UCarlaLidarSubsystem::FinishLidarSimulations);
118
119 if (LidarSimulationTask.IsValid())
120 {
121 FTaskGraphInterface::Get().WaitUntilTaskCompletes(LidarSimulationTask);
122 LidarSimulationTask = nullptr;
123 }
124
125 // Streaming and ROS2 publishing stay on the game thread.
126 {
127 TRACE_CPUPROFILER_EVENT_SCOPE(LidarSendData);
129 {
130 if (IsValid(Lidar))
131 {
132 Lidar->SendData(Lidar->GetReadyToTickDeltaSeconds());
133 Lidar->ClearReadyToTick();
134 }
135 }
136 }
137 LidarsToSimulate.Reset();
138}
static TAutoConsoleVariable< int32 > CVarLidarBatch(TEXT("carla.Lidar.Batch"), 1, TEXT("Simulate multiple LiDARs in parallel (1) or one after another (0)."))
static bool IsValid(const ACarlaWheeledVehicle *Vehicle)
A ray-cast based Lidar sensor.
void DeRegisterSensor(ASensor *Sensor)
A simulation episode.
FSensorManager & GetSensorManager()
void RegisterLidar(ARayCastSemanticLidar *Lidar)
virtual void Deinitialize() override
void FinishLidarSimulations(UWorld *World, float DeltaSeconds)
void StartLidarSimulations(UWorld *World, float DeltaSeconds)
TArray< TObjectPtr< ARayCastSemanticLidar > > LidarsToSimulate
LiDARs due this frame, snapshotted on the game thread for the simulation task.
FGraphEventRef LidarSimulationTask
void UnregisterLidar(ARayCastSemanticLidar *Lidar)
TArray< TWeakObjectPtr< ARayCastSemanticLidar > > ActiveLidars
static UCarlaEpisode * GetCurrentEpisode(const UObject *WorldContextObject)