CARLA
Loading...
Searching...
No Matches
CarlaDeviceProfileSelectorModule.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
9#include "HAL/IConsoleManager.h"
10#include "Misc/CommandLine.h"
11#include "Misc/CoreDelegates.h"
12#include "Misc/Parse.h"
13#include "Scalability.h"
14
15#include <optional>
16#include <span>
17#include <string>
18#include <string_view>
19
21
22namespace {
23
24enum class QualityTier {
25 Low,
26 Medium,
27 High,
28 Epic,
29};
30
31// Tier selected when `-quality-level=` is absent on the command line.
32inline constexpr std::string_view kDefaultQualityLevel{"Epic"};
33
34// Case-sensitive lookup. Valid inputs are exactly the PascalCase strings
35// "Low", "Medium", "High", "Epic". Any other input (different casing,
36// surrounding whitespace, empty, or unknown tier) returns std::nullopt and
37// the caller logs the unrecognised value and applies no tier.
38[[nodiscard]] constexpr std::optional<QualityTier> ResolveQualityLevel(
39 std::string_view value) {
40 if (value == "Low") {
41 return QualityTier::Low;
42 }
43 if (value == "Medium") {
44 return QualityTier::Medium;
45 }
46 if (value == "High") {
47 return QualityTier::High;
48 }
49 if (value == "Epic") {
50 return QualityTier::Epic;
51 }
52 return std::nullopt;
53}
54
55// Per-tier policy for hardware ray tracing on ordinary scene-capture cameras:
56// -1 follows each sensor's bUseRayTracing, 0 forces it off, 1 forces it on.
57// Sensors that cannot render without ray tracing ignore a forced off. Read by
58// the Carla plugin by name; registered here because this module loads first.
59TAutoConsoleVariable<int32> CVarCameraUseRayTracing(
60 TEXT("carla.Camera.UseRayTracing"),
61 -1,
62 TEXT("Per-tier hardware ray tracing policy for scene-capture cameras.\n")
63 TEXT(" -1: Respect the per-sensor bUseRayTracing attribute.\n")
64 TEXT(" 0: Force ray tracing OFF on every camera that does not require it.\n")
65 TEXT(" 1: Force ray tracing ON on every camera."),
66 ECVF_Default);
67
68QualityTier GResolvedTier{QualityTier::Epic};
69FDelegateHandle GPostEngineInitHandle{};
70
71struct CVarOverride {
72 const TCHAR *name;
73 const TCHAR *value;
74};
75
76// Epic is the project default: DefaultEngine.ini carries the Epic values and
77// DefaultScalability.ini carries one bucket per tier. Only the CVars that a
78// project-setting or system-settings pin keeps above scalability priority can
79// be lowered here, at ECVF_SetByDeviceProfile.
80constexpr CVarOverride kLowOverrides[] = {
81 {TEXT("r.DynamicGlobalIlluminationMethod"), TEXT("0")},
82 {TEXT("r.ReflectionMethod"), TEXT("2")},
83 {TEXT("r.Streaming.PoolSize"), TEXT("2000")},
84 {TEXT("r.Nanite.Streaming.StreamingPoolSize"), TEXT("768")},
85 {TEXT("r.ReflectionCaptureResolution"), TEXT("128")},
86 {TEXT("r.CustomDepth"), TEXT("1")},
87 {TEXT("r.DefaultFeature.MotionBlur"), TEXT("0")},
88 {TEXT("r.AntiAliasingMethod"), TEXT("2")},
89 {TEXT("r.Shadow.DistanceScale"), TEXT("1")},
90 {TEXT("r.Shadow.Virtual.UseFarShadowCulling"), TEXT("1")},
91 {TEXT("carla.Camera.UseRayTracing"), TEXT("0")},
92};
93
94constexpr CVarOverride kMediumOverrides[] = {
95 {TEXT("r.DynamicGlobalIlluminationMethod"), TEXT("0")},
96 {TEXT("r.ReflectionMethod"), TEXT("2")},
97 {TEXT("r.Streaming.PoolSize"), TEXT("3000")},
98 {TEXT("r.ReflectionCaptureResolution"), TEXT("256")},
99 {TEXT("r.AntiAliasingMethod"), TEXT("2")},
100 {TEXT("r.Shadow.DistanceScale"), TEXT("1.5")},
101 {TEXT("r.Shadow.Virtual.UseFarShadowCulling"), TEXT("1")},
102 {TEXT("carla.Camera.UseRayTracing"), TEXT("0")},
103};
104
105constexpr CVarOverride kHighOverrides[] = {
106 {TEXT("carla.Camera.UseRayTracing"), TEXT("0")},
107};
108
109std::span<const CVarOverride> OverridesForTier(QualityTier tier) {
110 switch (tier) {
111 case QualityTier::Low:
112 return kLowOverrides;
113 case QualityTier::Medium:
114 return kMediumOverrides;
115 case QualityTier::High:
116 return kHighOverrides;
117 case QualityTier::Epic:
118 break;
119 }
120 return {};
121}
122
123void ApplyOverrides(std::span<const CVarOverride> overrides) {
124 for (const auto &entry : overrides) {
125 if (auto *cv = IConsoleManager::Get().FindConsoleVariable(entry.name); cv != nullptr) {
126 cv->Set(entry.value, ECVF_SetByDeviceProfile);
127 }
128 }
129}
130
131// Each tier selects the matching bucket (0..3) of every scalability group
132// defined in DefaultScalability.ini. Applied at startup and again on
133// OnPostEngineInit so the persisted GameUserSettings.ini scalability cannot
134// shadow the selected tier.
135void ApplyScalabilityForTier(QualityTier tier) {
136 const int32 bucket = static_cast<int32>(tier);
137 Scalability::FQualityLevels q{};
138 q.ResolutionQuality = 100.0f;
139 q.ViewDistanceQuality = bucket;
140 q.AntiAliasingQuality = bucket;
141 q.ShadowQuality = bucket;
142 q.GlobalIlluminationQuality = bucket;
143 q.ReflectionQuality = bucket;
144 q.PostProcessQuality = bucket;
145 q.TextureQuality = bucket;
146 q.EffectsQuality = bucket;
147 q.FoliageQuality = bucket;
148 q.ShadingQuality = bucket;
149 Scalability::SetQualityLevels(q, /*bApply=*/true);
150}
151
152void OnPostEngineInit() {
153 // UGameUserSettings::ApplyNonResolutionSettings has run and re-applied
154 // scalability from GameUserSettings.ini at ECVF_SetByScalability,
155 // overwriting Low's bucket members. Re-apply the tier scalability so
156 // the persisted state cannot shadow the tier-selected buckets.
157 ApplyScalabilityForTier(GResolvedTier);
158}
159
160} // namespace
161
163 FString quality_level;
164 if (!FParse::Value(FCommandLine::Get(), TEXT("-quality-level="), quality_level)) {
165 quality_level = FString{ANSI_TO_TCHAR(kDefaultQualityLevel.data())};
166 }
167
168 const std::string quality_level_std{TCHAR_TO_UTF8(*quality_level)};
169 const auto tier = ResolveQualityLevel(quality_level_std);
170 if (!tier.has_value()) {
171 UE_LOG(
172 LogInit,
173 Warning,
174 TEXT("CarlaDeviceProfileSelector: unrecognised -quality-level=%s; no tier applied"),
175 *quality_level);
176 return;
177 }
178
179 GResolvedTier = *tier;
180
181 ApplyOverrides(OverridesForTier(*tier));
182 ApplyScalabilityForTier(*tier);
183
184 GPostEngineInitHandle = FCoreDelegates::GetOnPostEngineInit().AddStatic(&OnPostEngineInit);
185}
186
188 if (GPostEngineInitHandle.IsValid()) {
189 FCoreDelegates::GetOnPostEngineInit().Remove(GPostEngineInitHandle);
190 GPostEngineInitHandle.Reset();
191 }
192}
IMPLEMENT_MODULE(FCarlaDeviceProfileSelectorModule, CarlaDeviceProfileSelector)