QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCRhiCapture.cc
Go to the documentation of this file.
1#include "QGCRhiCapture.h"
2
3#if defined(QGC_HAS_ANY_GPU_PATH)
4#include "HwBuffers.h"
5#endif
6
7#include <QtCore/QRunnable>
8#include <QtQuick/QQuickWindow>
9#include <rhi/qrhi.h>
10#if (defined(Q_OS_WIN) && (defined(QGC_HAS_GST_D3D11_GPU_PATH) || defined(QGC_HAS_GST_D3D12_GPU_PATH))) || \
11 (defined(QGC_HAS_GST_VULKAN_GPU_PATH) && QT_CONFIG(vulkan))
12#include <rhi/qrhi_platform.h>
13#endif
14
15#include <array>
16#include <atomic>
17
18namespace QGCRhiCapture {
19
20namespace {
21std::atomic<QRhi*> s_cachedRhi{nullptr};
22// The connected window (or null after it is destroyed); read only by connectWindow's idempotency
23// guard. Atomic because the destroyed lambda nulls it on the window's thread.
24std::atomic<QQuickWindow*> s_connectedWindow{nullptr};
25DeviceSnapshot s_snapshot;
26// Per-connection so a window swap (e.g. popout video) can disconnect the prior window's lambdas before they clobber the
27// new window's cached RHI.
28std::array<QMetaObject::Connection, 3> s_connections;
29
30void clearSnapshot()
31{
32 s_snapshot.backend.store(-1, std::memory_order_release);
33 s_snapshot.d3d11Device.store(nullptr, std::memory_order_release);
34 s_snapshot.d3d12Device.store(nullptr, std::memory_order_release);
35 s_snapshot.adapterLuid.store(0, std::memory_order_release);
36 s_snapshot.vkPhysicalDevice.store(nullptr, std::memory_order_release);
37 s_snapshot.vkQueueFamilyIdx.store(0, std::memory_order_release);
38 s_snapshot.vkQueueIdx.store(0, std::memory_order_release);
39 s_snapshot.framesInFlight.store(0, std::memory_order_release);
40}
41
42void populateSnapshot(QRhi* rhi)
43{
44 if (!rhi) {
45 clearSnapshot();
46 return;
47 }
48 const int backend = static_cast<int>(rhi->backend());
49 void* d3d11 = nullptr;
50 void* d3d12 = nullptr;
51 qint64 luid = 0;
52 void* vkPhysDev = nullptr;
53 quint32 vkQueueFamilyIdx = 0;
54 quint32 vkQueueIdx = 0;
55
56#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D11_GPU_PATH)
57 if (rhi->backend() == QRhi::D3D11) {
58 if (auto* h = static_cast<const QRhiD3D11NativeHandles*>(rhi->nativeHandles())) {
59 d3d11 = h->dev;
60 // Sign-extend qint32 HighPart so it matches LARGE_INTEGER::QuadPart bit-for-bit.
61 luid = (static_cast<qint64>(h->adapterLuidHigh) << 32) |
62 (static_cast<qint64>(h->adapterLuidLow) & 0xFFFFFFFFLL);
63 }
64 }
65#endif
66#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D12_GPU_PATH)
67 if (rhi->backend() == QRhi::D3D12) {
68 if (auto* h = static_cast<const QRhiD3D12NativeHandles*>(rhi->nativeHandles())) {
69 d3d12 = h->dev;
70 luid = (static_cast<qint64>(h->adapterLuidHigh) << 32) |
71 (static_cast<qint64>(h->adapterLuidLow) & 0xFFFFFFFFLL);
72 }
73 }
74#endif
75#if defined(QGC_HAS_GST_VULKAN_GPU_PATH) && QT_CONFIG(vulkan)
76 if (rhi->backend() == QRhi::Vulkan) {
77 if (auto* h = static_cast<const QRhiVulkanNativeHandles*>(rhi->nativeHandles())) {
78 vkPhysDev = static_cast<void*>(h->physDev);
79 vkQueueFamilyIdx = h->gfxQueueFamilyIdx;
80 vkQueueIdx = h->gfxQueueIdx;
81 }
82 }
83#endif
84
85 // Publish device pointers before the backend tag so a consumer's acquire-load on backend != -1 is guaranteed to see
86 // populated handles.
87 s_snapshot.d3d11Device.store(d3d11, std::memory_order_release);
88 s_snapshot.d3d12Device.store(d3d12, std::memory_order_release);
89 s_snapshot.adapterLuid.store(luid, std::memory_order_release);
90 s_snapshot.vkPhysicalDevice.store(vkPhysDev, std::memory_order_release);
91 s_snapshot.vkQueueFamilyIdx.store(vkQueueFamilyIdx, std::memory_order_release);
92 s_snapshot.vkQueueIdx.store(vkQueueIdx, std::memory_order_release);
93 s_snapshot.framesInFlight.store(rhi->resourceLimit(QRhi::FramesInFlight), std::memory_order_release);
94 s_snapshot.backend.store(backend, std::memory_order_release);
95}
96} // namespace
97
98QRhi* cachedRhi() noexcept
99{
100 return s_cachedRhi.load(std::memory_order_acquire);
101}
102
104{
105 return s_snapshot;
106}
107
108void connectWindow(QQuickWindow* window)
109{
110 if (!window)
111 return;
112 if (s_connectedWindow.load(std::memory_order_acquire) == window)
113 return; // idempotent — avoid duplicate connections
114
115 // Window swap: the old window's QRhi can be torn down before the new window's SG initializes; drop the cached
116 // QRhi/snapshot and native GPU handles now so bus-sync threads never dereference a dead device across the gap.
117 if (s_connectedWindow.load(std::memory_order_acquire) != nullptr) {
118 s_cachedRhi.store(nullptr, std::memory_order_release);
119 clearSnapshot();
120#if defined(QGC_HAS_ANY_GPU_PATH)
122#endif
123 }
124
125 // Detach the prior window's signals first, else its destroyed/invalidated lambdas later wipe cachedRhi() and reset
126 // bridges the new window is using.
127 for (std::size_t i = 0; i < s_connections.size(); ++i) {
128 auto& conn = s_connections[i];
129 QObject::disconnect(conn);
130 conn = QMetaObject::Connection();
131 }
132
133 s_connectedWindow.store(window, std::memory_order_release);
134 // sceneGraphInitialized fires on the render thread where rhi() is valid — snapshot native device pointers here so
135 // bus-sync callbacks never deref QRhi* cross-thread.
136 s_connections[0] = QObject::connect(
137 window, &QQuickWindow::sceneGraphInitialized, window,
138 [window]() {
139 QRhi* rhi = window->rhi();
140 s_cachedRhi.store(rhi, std::memory_order_release);
141 populateSnapshot(rhi);
142 },
143 Qt::DirectConnection);
144 s_connections[1] = QObject::connect(
145 window, &QQuickWindow::sceneGraphInvalidated, window,
146 []() {
147 s_cachedRhi.store(nullptr, std::memory_order_release);
148 clearSnapshot();
149#if defined(QGC_HAS_ANY_GPU_PATH)
150 // Drop native GPU handles that wrap the now-defunct QRhi-owned device/context.
152#endif
153 },
154 Qt::DirectConnection);
155 // Clear cache when window is destroyed so a stale QRhi* is never returned.
156 s_connections[2] = QObject::connect(
157 window, &QQuickWindow::destroyed, window,
158 [](QObject*) {
159 s_connectedWindow.store(nullptr, std::memory_order_release);
160 s_cachedRhi.store(nullptr, std::memory_order_release);
161 clearSnapshot();
162#if defined(QGC_HAS_ANY_GPU_PATH)
164#endif
165 },
166 Qt::DirectConnection);
167
168 // SG already initialized (video init runs after first render) — publish on the render thread, where rhi() is valid.
169 if (window->isSceneGraphInitialized()) {
170 window->scheduleRenderJob(QRunnable::create([window]() {
171 QRhi* rhi = window->rhi();
172 s_cachedRhi.store(rhi, std::memory_order_release);
173 populateSnapshot(rhi);
174 }),
175 QQuickWindow::BeforeSynchronizingStage);
176 window->update();
177 }
178}
179
180} // namespace QGCRhiCapture
void resetCachedGpuResources() noexcept
Drop process-wide native GPU handles tied to the current Qt scene-graph device/context.
Definition HwBuffers.cc:192
Resolves and caches the QRhi* driving QGC's main scene graph; Qt has no global RHI accessor.
QRhi * cachedRhi() noexcept
Cached QRhi* maintained by sceneGraph signals; safe from any thread via acquire ordering.
void connectWindow(QQuickWindow *window)
DeviceSnapshot & deviceSnapshot() noexcept
Returns the global snapshot. Atomic fields make individual reads thread-safe.
std::atomic< quint32 > vkQueueIdx
QRhi's gfx queue index (Vulkan backend only)
std::atomic< quint32 > vkQueueFamilyIdx
QRhi's gfx queue family (Vulkan backend only)
std::atomic< int > backend
QRhi::Implementation cast to int (-1 = unset)
std::atomic< void * > d3d11Device
ID3D11Device* (Windows only)
std::atomic< void * > d3d12Device
ID3D12Device* (Windows only)
std::atomic< void * > vkPhysicalDevice
VkPhysicalDevice (Vulkan backend only)
std::atomic< qint64 > adapterLuid
Composed (high<<32)|low LUID, 0 if unknown.
std::atomic< int > framesInFlight
QRhi::FramesInFlight resource limit (0 = unset)