QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GstContextBridgeRegistry.cc
Go to the documentation of this file.
2
4
5#if defined(QGC_HAS_ANY_GPU_PATH)
6
7#include <QtCore/QMutex>
8#include <array>
9#include <atomic>
10
11QGC_LOGGING_CATEGORY(GstContextBridgeRegistryLog, "Video.GStreamer.HwBuffers.GstContextBridgeRegistry")
12
13namespace GstContextBridgeRegistry {
14
15namespace {
16
17// Arrays are write-once at static-init (happen-before any GstBus thread), so dispatch reads are lock-free; mutex only
18// serializes concurrent registrations. unregister is NOT safe with concurrent dispatch.
19// One slot per compiled GPU path that registers a bridge; deriving the cap from the path macros (not a fixed count)
20// makes a silent drop impossible.
21constexpr int kMaxBridges = 0
22#if defined(QGC_HAS_GST_GLMEMORY_GPU_PATH)
23 + 1
24#endif
25#if defined(QGC_HAS_GST_D3D11_GPU_PATH)
26 + 1
27#endif
28#if defined(QGC_HAS_GST_D3D12_GPU_PATH)
29 + 1
30#endif
31#if defined(QGC_HAS_GST_VULKAN_GPU_PATH)
32 + 1
33#endif
34 ;
35// Cache-reset participants: the import-cache-owning paths (NOT the bridge set). Sized from its own
36// macro list so adding a cache path without a slot is a compile-time array overflow, never a silent drop.
37constexpr int kMaxCacheResets = 0
38#if defined(QGC_HAS_GST_DMABUF_GPU_PATH)
39 + 1
40#endif
41#if defined(QGC_HAS_GST_D3D11_GPU_PATH)
42 + 1
43#endif
44#if defined(QGC_HAS_GST_D3D12_GPU_PATH)
45 + 1
46#endif
47#if defined(QGC_HAS_GST_IOSURFACE_GPU_PATH)
48 + 1
49#endif
50#if defined(QGC_HAS_GST_AHARDWAREBUFFER_GPU_PATH)
51 + 1
52#endif
53 ;
54QMutex s_mutex;
55std::array<BridgeHandler, kMaxBridges> s_handlers{};
56std::atomic<int> s_handlerCount{0};
57std::array<ResetCallback, kMaxBridges> s_resets{};
58std::atomic<int> s_resetCount{0};
59std::array<ResetCallback, (kMaxCacheResets > 0 ? kMaxCacheResets : 1)> s_cacheResets{};
60std::atomic<int> s_cacheResetCount{0};
61
62} // namespace
63
64RegistrationHandle registerBridgeHandler(BridgeHandler handler)
65{
66 if (handler == nullptr) {
67 return kInvalidHandle;
68 }
69 QMutexLocker lock(&s_mutex);
70 const int count = s_handlerCount.load(std::memory_order_relaxed);
71 for (int i = 0; i < count; ++i) {
72 if (s_handlers[i] == handler)
73 return static_cast<RegistrationHandle>(i);
74 }
75 if (count >= kMaxBridges) {
76 qCWarning(GstContextBridgeRegistryLog) << "bridge handler registry full (kMaxBridges=" << kMaxBridges
77 << "); handler will not receive GstBus messages";
78 return kInvalidHandle;
79 }
80 s_handlers[count] = handler;
81 s_handlerCount.store(count + 1, std::memory_order_release);
82 return static_cast<RegistrationHandle>(count);
83}
84
85RegistrationHandle registerResetCallback(ResetCallback callback)
86{
87 if (callback == nullptr) {
88 return kInvalidHandle;
89 }
90 QMutexLocker lock(&s_mutex);
91 const int count = s_resetCount.load(std::memory_order_relaxed);
92 for (int i = 0; i < count; ++i) {
93 if (s_resets[i] == callback)
94 return static_cast<RegistrationHandle>(i);
95 }
96 if (count >= kMaxBridges) {
97 qCWarning(GstContextBridgeRegistryLog) << "reset callback registry full (kMaxBridges=" << kMaxBridges
98 << "); callback will not run on QRhi teardown";
99 return kInvalidHandle;
100 }
101 s_resets[count] = callback;
102 s_resetCount.store(count + 1, std::memory_order_release);
103 return static_cast<RegistrationHandle>(count);
104}
105
106RegistrationHandle registerCacheReset(ResetCallback callback)
107{
108 if (callback == nullptr) {
109 return kInvalidHandle;
110 }
111 QMutexLocker lock(&s_mutex);
112 const int count = s_cacheResetCount.load(std::memory_order_relaxed);
113 for (int i = 0; i < count; ++i) {
114 if (s_cacheResets[i] == callback)
115 return static_cast<RegistrationHandle>(i);
116 }
117 if (count >= kMaxCacheResets) {
118 qCWarning(GstContextBridgeRegistryLog) << "cache-reset registry full (kMaxCacheResets=" << kMaxCacheResets
119 << "); callback will not run on GPU device-loss";
120 return kInvalidHandle;
121 }
122 s_cacheResets[count] = callback;
123 s_cacheResetCount.store(count + 1, std::memory_order_release);
124 return static_cast<RegistrationHandle>(count);
125}
126
127// First GST_BUS_DROP wins; bridges must differ on context-type so no bridge shadows another.
128GstBusSyncReply dispatchBridges(GstMessage* message)
129{
130 // Registry lock must not be held across a bridge callout — each handler takes its own per-bridge mutex.
131 const int count = s_handlerCount.load(std::memory_order_acquire);
132 for (int i = 0; i < count; ++i) {
133 const BridgeHandler h = s_handlers[i];
134 if (h && (h(message) == GST_BUS_DROP))
135 return GST_BUS_DROP;
136 }
137 return GST_BUS_PASS;
138}
139
140void resetAllBridges()
141{
142 const int count = s_resetCount.load(std::memory_order_acquire);
143 for (int i = 0; i < count; ++i) {
144 const ResetCallback cb = s_resets[i];
145 if (cb)
146 cb();
147 }
148}
149
150void resetAllCaches()
151{
152 // No registry lock across the callout: each cache reset takes its own per-backend mutex (or is a
153 // lock-free atomic-store deferred flag). Array is write-once at static-init (happens-before any
154 // GstBus thread), so iteration is lock-free.
155 const int count = s_cacheResetCount.load(std::memory_order_acquire);
156 for (int i = 0; i < count; ++i) {
157 const ResetCallback cb = s_cacheResets[i];
158 if (cb)
159 cb();
160 }
161}
162
163#ifdef QGC_GST_BUILD_TESTING
164void clearForTest()
165{
166 resetAllBridges();
167 resetAllCaches();
168 QMutexLocker lock(&s_mutex);
169 s_handlers.fill(nullptr);
170 s_resets.fill(nullptr);
171 s_cacheResets.fill(nullptr);
172 s_handlerCount.store(0, std::memory_order_release);
173 s_resetCount.store(0, std::memory_order_release);
174 s_cacheResetCount.store(0, std::memory_order_release);
175}
176#endif
177
178} // namespace GstContextBridgeRegistry
179
180#endif // QGC_HAS_ANY_GPU_PATH
#define QGC_LOGGING_CATEGORY(name, categoryStr)