QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GstD3DContextBridgeCommon.cc
Go to the documentation of this file.
2
3#if defined(Q_OS_WIN) && (defined(QGC_HAS_GST_D3D11_GPU_PATH) || defined(QGC_HAS_GST_D3D12_GPU_PATH))
4
5#include <QtCore/QMutexLocker>
6#include <glib-object.h>
7
10#include "QGCRhiCapture.h"
11
12namespace GstD3DContextBridgeCommon {
13namespace {
14
15bool primeLocked(BridgeState& state, const BridgeOps& ops)
16{
17 if (state.primed) {
18 return true;
19 }
20 if (!checkSnapshotBackend(state, ops.cat(), ops.qrhiBackend, ops.apiName)) {
21 return false;
22 }
23 GstObject* device = ops.createDevice(ops.cat());
24 if (!device) {
25 return false; // createDevice already logged the cause.
26 }
27 state.device = device;
28 state.primed = true;
29 qCInfo(ops.cat()) << ops.apiName << "bridge primed: shared device =" << device;
30 return true;
31}
32
33} // namespace
34
35// Reads QGCRhiCapture::deviceSnapshot() via atomic loads — safe from the bus-sync thread without touching QRhi
36// cross-thread.
37bool checkSnapshotBackend(BridgeState& state, const QLoggingCategory& cat, int expectedBackend, const char* backendName)
38{
39 const int backend = QGCRhiCapture::deviceSnapshot().backend.load(std::memory_order_acquire);
40 if (backend < 0) {
41 qCDebug(cat) << "QRhi snapshot not yet populated; will retry on next NEED_CONTEXT";
42 return false;
43 }
44 if (backend != expectedBackend) {
45 if (!state.warnedWrongBackend) {
46 qCInfo(cat) << "QRhi backend tag is" << backend << "(not" << backendName << "); bridge inactive";
47 state.warnedWrongBackend = true;
48 }
49 return false;
50 }
51 return true;
52}
53
54void logHandoff(BridgeState& state, const QLoggingCategory& cat, GstElement* element, const char* apiName)
55{
56 if (!state.loggedFirstHandoff.exchange(true, std::memory_order_relaxed)) {
57 qCInfo(cat) << "First" << apiName << "device handoff to element" << GST_ELEMENT_NAME(element);
58 } else {
59 qCDebug(cat) << "Provided" << apiName << "device context to" << GST_ELEMENT_NAME(element);
60 }
61}
62
63gint64 readAdapterLuid(gpointer device)
64{
65 if (!device || !G_IS_OBJECT(device))
66 return 0;
67 gint64 luid = 0;
68 g_object_get(G_OBJECT(device), "adapter-luid", &luid, nullptr);
69 return luid;
70}
71
72void logAdapterMatch(gint64 expectedLuid, gpointer gstDevice, const QLoggingCategory& cat, const char* apiName)
73{
74 const gint64 actualLuid = readAdapterLuid(gstDevice);
75 if (actualLuid != expectedLuid) {
76 qCWarning(cat).noquote() << apiName << "bridge: gst device LUID mismatch — QRhi LUID=" << expectedLuid
77 << "but wrapped device LUID=" << actualLuid
78 << "(zero-copy will appear corrupt; check NEED_CONTEXT race)";
79 return;
80 }
81 qCInfo(cat).noquote() << apiName << "bridge adapter LUID="
82 << QString::asprintf("0x%llx", static_cast<long long>(expectedLuid));
83}
84
85void registerBridge(const QLoggingCategory& cat, GstBusSyncReply (*handler)(GstMessage*), void (*reset)())
86{
87 GstContextBridge::registerBridge(cat, "D3D", handler, reset);
88}
89
90bool prime(BridgeState& state, const BridgeOps& ops)
91{
92 QMutexLocker lock(&state.mutex);
93 return primeLocked(state, ops);
94}
95
96GstObject* currentDevice(BridgeState& state)
97{
98 QMutexLocker lock(&state.mutex);
99 if (!state.device) {
100 return nullptr;
101 }
102 return GST_OBJECT(gst_object_ref(state.device));
103}
104
105namespace {
106
107// Adapts a D3D BridgeState+BridgeOps pair onto the path-agnostic GstContextBridge skeleton. Both D3D bridges share this
108// TU, so the per-instance state is threaded through `user` rather than file statics. One context-type → one device.
109struct D3DUser
110{
111 BridgeState* state;
112 const BridgeOps* ops;
113};
114
115const QLoggingCategory& vtCat(void* user)
116{
117 return static_cast<D3DUser*>(user)->ops->cat();
118}
119
120QMutex& vtMutex(void* user)
121{
122 return static_cast<D3DUser*>(user)->state->mutex;
123}
124
125bool vtPrime(void* user)
126{
127 auto* d = static_cast<D3DUser*>(user);
128 return primeLocked(*d->state, *d->ops);
129}
130
131GstObject* vtRefObject(void* user, const char* /*contextType*/)
132{
133 auto* d = static_cast<D3DUser*>(user);
134 return d->state->device ? GST_OBJECT(gst_object_ref(d->state->device)) : nullptr;
135}
136
137GstContext* vtBuildContext(void* user, const char* /*contextType*/, GstObject* object)
138{
139 auto* d = static_cast<D3DUser*>(user);
140 GstContext* ctx = d->ops->makeContext(object);
141 if (!ctx) {
142 qCWarning(d->ops->cat()) << d->ops->apiName << "context_new failed";
143 }
144 return ctx;
145}
146
147void vtOnHandoff(void* user, GstElement* element, const char* /*contextType*/)
148{
149 auto* d = static_cast<D3DUser*>(user);
150 logHandoff(*d->state, d->ops->cat(), element, d->ops->apiName);
151}
152
153GstContextBridge::BridgeVTable makeVTable(const BridgeOps& ops, const char** typeStorage)
154{
155 typeStorage[0] = ops.contextType;
156 return GstContextBridge::BridgeVTable{
157 ops.apiName, typeStorage, 1, &vtCat, &vtMutex, &vtPrime, &vtRefObject, &vtBuildContext, &vtOnHandoff,
158 };
159}
160
161} // namespace
162
163GstBusSyncReply handleSyncMessage(BridgeState& state, const BridgeOps& ops, GstMessage* message)
164{
165 D3DUser user = {&state, &ops};
166 const char* types[1];
167 const GstContextBridge::BridgeVTable vt = makeVTable(ops, types);
168 return GstContextBridge::handleSyncMessage(vt, &user, message);
169}
170
171bool answerContextQuery(BridgeState& state, const BridgeOps& ops, GstQuery* query)
172{
173 D3DUser user = {&state, &ops};
174 const char* types[1];
175 const GstContextBridge::BridgeVTable vt = makeVTable(ops, types);
176 return GstContextBridge::answerContextQuery(vt, &user, query);
177}
178
179void reset(BridgeState& state, const BridgeOps& ops)
180{
181 QMutexLocker lock(&state.mutex);
182 gst_clear_object(&state.device);
183 state.primed = false;
184 state.warnedWrongBackend = false;
185 qCDebug(ops.cat()) << ops.apiName << "bridge reset";
186}
187
188} // namespace GstD3DContextBridgeCommon
189
190#endif // Q_OS_WIN && (QGC_HAS_GST_D3D11_GPU_PATH || QGC_HAS_GST_D3D12_GPU_PATH)
struct _GstElement GstElement
DeviceSnapshot & deviceSnapshot() noexcept
Returns the global snapshot. Atomic fields make individual reads thread-safe.
std::atomic< int > backend
QRhi::Implementation cast to int (-1 = unset)