QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GraphicsSetup.cc
Go to the documentation of this file.
1#include "GraphicsSetup.h"
2
3#include <QtCore/QDir>
4#include <QtCore/QRunnable>
5#include <QtCore/QStandardPaths>
6#include <QtCore/QString>
7#include <QtCore/QStringList>
8#include <QtQuick/QQuickGraphicsConfiguration>
9#include <QtQuick/QQuickGraphicsDevice>
10#include <QtQuick/QQuickWindow>
11#include <memory>
12#include <rhi/qrhi.h>
13
14#include "QGCLoggingCategory.h"
15
16QGC_LOGGING_CATEGORY(GraphicsSetupLog, "API.GraphicsSetup")
17
18namespace GraphicsSetup {
19
20namespace {
21
22constexpr const char* kEnvRhiDebug = "QGC_RHI_DEBUG";
23constexpr const char* kEnvRhiPipelineCache = "QGC_RHI_PIPELINE_CACHE";
24constexpr const char* kEnvHdrOutput = "QGC_HDR_OUTPUT";
25constexpr const char* kEnvForceVideoGpu = "QGC_FORCE_VIDEO_GPU";
26
27bool envFlag(const char* name)
28{
29 return qEnvironmentVariableIsSet(name) && (qEnvironmentVariable(name) != QLatin1String("0"));
30}
31
32bool envEnabledByDefault(const char* name)
33{
34 return !qEnvironmentVariableIsSet(name) || (qEnvironmentVariable(name) != QLatin1String("0"));
35}
36
37void configurePipelineCache(QQuickGraphicsConfiguration& config)
38{
39 if (!envEnabledByDefault(kEnvRhiPipelineCache)) {
40 return;
41 }
42
43 const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
44 if (cacheDir.isEmpty()) {
45 qCWarning(GraphicsSetupLog) << "RHI pipeline cache disabled: no writable cache location";
46 return;
47 }
48
49 QDir dir;
50 if (!dir.mkpath(cacheDir)) {
51 qCWarning(GraphicsSetupLog) << "RHI pipeline cache disabled: failed to create" << cacheDir;
52 return;
53 }
54
55 const QString cacheFile = cacheDir + QStringLiteral("/qgc_rhi_pipeline.cache");
56 config.setPipelineCacheLoadFile(cacheFile);
57 config.setPipelineCacheSaveFile(cacheFile);
58 qCDebug(GraphicsSetupLog) << "RHI pipeline cache:" << cacheFile;
59}
60
61#if defined(Q_OS_WIN)
62bool parseHex32(const QString& spec, quint32& value)
63{
64 QString token = spec.trimmed();
65 if (token.startsWith(QLatin1String("0x"), Qt::CaseInsensitive)) {
66 token.remove(0, 2);
67 }
68
69 if (token.isEmpty()) {
70 return false;
71 }
72
73 bool ok = false;
74 value = token.toUInt(&ok, 16);
75 return ok;
76}
77
78qint32 signExtend32(quint32 value)
79{
80 const qint64 signedValue =
81 (value <= 0x7FFFFFFFU) ? static_cast<qint64>(value) : (static_cast<qint64>(value) - 0x100000000LL);
82 return static_cast<qint32>(signedValue);
83}
84
85bool parseLuid(const QString& spec, quint32& low, qint32& high)
86{
87 const QStringList parts = spec.split(QLatin1Char(':'));
88 if (parts.size() != 2) {
89 return false;
90 }
91
92 quint32 highRaw = 0;
93 if (!parseHex32(parts.at(0), low) || !parseHex32(parts.at(1), highRaw)) {
94 return false;
95 }
96
97 high = signExtend32(highRaw);
98 return true;
99}
100
101void configureForcedD3DAdapter(QQuickWindow* window)
102{
103 if (!qEnvironmentVariableIsSet(kEnvForceVideoGpu)) {
104 return;
105 }
106
107 const QString spec = qEnvironmentVariable(kEnvForceVideoGpu);
108 quint32 luidLow = 0;
109 qint32 luidHigh = 0;
110 if (!parseLuid(spec, luidLow, luidHigh)) {
111 qCWarning(GraphicsSetupLog) << "QGC_FORCE_VIDEO_GPU malformed (expected hex 'low:high'):" << spec;
112 return;
113 }
114
115 // Must run before QRhi creation; Qt Quick owns the adapter after scene graph init.
116 window->setGraphicsDevice(QQuickGraphicsDevice::fromAdapter(luidLow, luidHigh));
117 qCWarning(GraphicsSetupLog).nospace()
118 << "QGC_FORCE_VIDEO_GPU: forcing adapter LUID low=0x" << QString::number(luidLow, 16) << " high=0x"
119 << QString::number(static_cast<quint32>(luidHigh), 16);
120}
121#else
122void warnForcedGpuUnsupported()
123{
124 if (qEnvironmentVariableIsSet(kEnvForceVideoGpu)) {
125 qCWarning(GraphicsSetupLog)
126 << "QGC_FORCE_VIDEO_GPU is Windows/D3D-only; ignoring LUID" << qEnvironmentVariable(kEnvForceVideoGpu);
127 }
128}
129#endif
130
131const char* hdrFormatName(QRhiSwapChain::Format f)
132{
133 switch (f) {
134 case QRhiSwapChain::SDR:
135 return "SDR";
136 case QRhiSwapChain::HDRExtendedSrgbLinear:
137 return "HDRExtendedSrgbLinear";
138 case QRhiSwapChain::HDR10:
139 return "HDR10";
140 case QRhiSwapChain::HDRExtendedDisplayP3Linear:
141 return "HDRExtendedDisplayP3Linear";
142 }
143 return "Unknown";
144}
145
146void probeHdr(QQuickWindow* window)
147{
148 QRhi* rhi = window->rhi();
149 if (!rhi) {
150 qCWarning(GraphicsSetupLog) << "HDR probe: no QRhi on render thread";
151 return;
152 }
153
154 std::unique_ptr<QRhiSwapChain> sc(rhi->newSwapChain());
155 if (!sc) {
156 qCWarning(GraphicsSetupLog) << "HDR probe: backend has no swapchain support";
157 return;
158 }
159 sc->setWindow(window);
160
161 const QRhiSwapChainHdrInfo info = sc->hdrInfo();
162 qCDebug(GraphicsSetupLog).nospace()
163 << "HDR display info: limitsType=" << static_cast<int>(info.limitsType)
164 << " luminanceBehavior=" << static_cast<int>(info.luminanceBehavior) << " sdrWhiteLevel=" << info.sdrWhiteLevel;
165
166 for (const QRhiSwapChain::Format f :
167 {QRhiSwapChain::HDRExtendedSrgbLinear, QRhiSwapChain::HDR10, QRhiSwapChain::HDRExtendedDisplayP3Linear}) {
168 qCDebug(GraphicsSetupLog) << "HDR format" << hdrFormatName(f) << "supported:" << sc->isFormatSupported(f);
169 }
170
171 if (envFlag(kEnvHdrOutput)) {
172 qCWarning(GraphicsSetupLog)
173 << "QGC_HDR_OUTPUT set but Qt Quick exposes no public swapchain-format setter in this"
174 << "Qt version; staying on SDR. (Diagnostic only.)";
175 }
176}
177
178} // namespace
179
180void configureMainWindow(QQuickWindow* window)
181{
182 if (!window) {
183 qCWarning(GraphicsSetupLog) << "configureMainWindow: null window";
184 return;
185 }
186
187 if (window->isSceneGraphInitialized()) {
188 qCWarning(GraphicsSetupLog)
189 << "Scene graph already initialized before configureMainWindow; skipping RHI config";
190 } else {
191 QQuickGraphicsConfiguration config = window->graphicsConfiguration();
192
193 if (envFlag(kEnvRhiDebug)) {
194 config.setDebugLayer(true);
195 config.setDebugMarkers(true);
196 config.setTimestamps(true);
197 qCDebug(GraphicsSetupLog) << "RHI debug layer/markers/timestamps enabled";
198 }
199
200 configurePipelineCache(config);
201 window->setGraphicsConfiguration(config);
202
203#if defined(Q_OS_WIN)
204 configureForcedD3DAdapter(window);
205#else
206 warnForcedGpuUnsupported();
207#endif
208 }
209
210 if (!envFlag(kEnvRhiDebug) && !envFlag(kEnvHdrOutput)) {
211 return;
212 }
213 QQuickWindow* win = window;
214 if (win->isSceneGraphInitialized()) {
215 win->scheduleRenderJob(QRunnable::create([win]() { probeHdr(win); }), QQuickWindow::BeforeSynchronizingStage);
216 win->update();
217 } else {
218 QObject::connect(
219 win, &QQuickWindow::sceneGraphInitialized, win, [win]() { probeHdr(win); }, Qt::DirectConnection);
220 }
221}
222
223} // namespace GraphicsSetup
Config config
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void configureMainWindow(QQuickWindow *window)