QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GstQgcAllocation.cc
Go to the documentation of this file.
1#include "GstQgcAllocation.h"
2
3#include <QtCore/qglobal.h>
4
5#include <gst/video/gstvideometa.h>
6#include <gst/video/video-info.h>
7
10#if GST_CHECK_VERSION(1, 24, 0)
11#include <gst/video/video-info-dma.h>
12#endif
13#if defined(QGC_HAS_GST_GLMEMORY_GPU_PATH)
14#include <gst/gl/gstglsyncmeta.h>
15#endif
16#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D11_GPU_PATH)
17#include <gst/d3d11/gstd3d11.h>
19#endif
20#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D12_GPU_PATH)
21#include <gst/d3d12/gstd3d12.h>
23#endif
24
25namespace GstQgc {
26
27namespace {
28
29// VAAPI/H.264 ref-frame queue typically 4–8; min=2 forced fallback allocations.
30constexpr guint kProposedMinBuffers = 4;
31
32// Advertise every meta API qgcqvideosink/GStreamerFrameMap consume so upstream keeps them;
33// else gst-vaapi/v4l2 strip crop/orientation metas and mapSampleToFrame does a full copy/rotate.
34void addConsumedAllocationMetas(GstQuery* query)
35{
36 gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
37 gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, NULL);
38#if defined(QGC_HAS_GST_VIDEO_ORIENTATION_META)
39 gst_query_add_allocation_meta(query, GST_VIDEO_ORIENTATION_META_API_TYPE, NULL);
40#endif
41#if defined(QGC_HAS_GST_GLMEMORY_GPU_PATH)
42 // glupload reads this to skip its own upload pass when upstream already provides GL texture.
43 gst_query_add_allocation_meta(query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL);
44 // Let the producer attach a sync meta and set its fence on the producing context; GstGlVideoBuffer
45 // waits on it at import time. Without this it synthesizes a same-context fence after the fact, which
46 // doesn't synchronize against the real producer — a cross-context tearing hazard on the Qt render thread.
47 gst_query_add_allocation_meta(query, GST_GL_SYNC_META_API_TYPE, NULL);
48#endif
49}
50
51#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D11_GPU_PATH)
52// Bind allocation to QRhi's shared device so import stays same-device instead of hitting
53// GstD3D11VideoBuffer's foreign-device copy path; SHADER_RESOURCE matches the importer's sampled texture.
54bool tryProposeD3D11Pool(GstQuery* query, GstCaps* caps, const GstVideoInfo* vinfo, gsize size)
55{
56 GstD3D11Device* device = GstD3D11ContextBridge::currentDevice();
57 if (!device) {
58 return false;
59 }
60 bool proposed = false;
61 if (GstBufferPool* pool = gst_d3d11_buffer_pool_new(device)) {
62 GstStructure* config = gst_buffer_pool_get_config(pool);
63 gst_buffer_pool_config_set_params(config, caps, size, kProposedMinBuffers, 0);
64 gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META);
65 if (GstD3D11AllocationParams* params = gst_d3d11_allocation_params_new(
66 device, vinfo, GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0)) {
67 gst_buffer_pool_config_set_d3d11_allocation_params(config, params);
68 gst_d3d11_allocation_params_free(params);
69 }
70 if (gst_buffer_pool_set_config(pool, config)) {
71 // The d3d11 pool may grow the buffer size to the allocated texture's pitch; re-read it.
72 GstStructure* updated = gst_buffer_pool_get_config(pool);
73 guint poolSize = static_cast<guint>(size);
74 gst_buffer_pool_config_get_params(updated, nullptr, &poolSize, nullptr, nullptr);
75 gst_structure_free(updated);
76 gst_query_add_allocation_pool(query, pool, poolSize, kProposedMinBuffers, 0);
77 proposed = true;
78 }
79 gst_object_unref(pool);
80 }
81 gst_object_unref(device); // currentDevice() returns a transfer-full ref.
82 return proposed;
83}
84#endif
85
86#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D12_GPU_PATH)
87// D3D12 counterpart of tryProposeD3D11Pool — steers allocation onto QRhi's shared (LUID-matched) adapter.
88bool tryProposeD3D12Pool(GstQuery* query, GstCaps* caps, const GstVideoInfo* vinfo, gsize size)
89{
90 GstD3D12Device* device = GstD3D12ContextBridge::currentDevice();
91 if (!device) {
92 return false;
93 }
94 bool proposed = false;
95 if (GstBufferPool* pool = gst_d3d12_buffer_pool_new(device)) {
96 GstStructure* config = gst_buffer_pool_get_config(pool);
97 gst_buffer_pool_config_set_params(config, caps, size, kProposedMinBuffers, 0);
98 gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META);
99 if (GstD3D12AllocationParams* params = gst_d3d12_allocation_params_new(
100 device, vinfo, GST_D3D12_ALLOCATION_FLAG_DEFAULT, D3D12_RESOURCE_FLAG_NONE,
101 D3D12_HEAP_FLAG_NONE)) {
102 gst_buffer_pool_config_set_d3d12_allocation_params(config, params);
103 gst_d3d12_allocation_params_free(params);
104 }
105 if (gst_buffer_pool_set_config(pool, config)) {
106 GstStructure* updated = gst_buffer_pool_get_config(pool);
107 guint poolSize = static_cast<guint>(size);
108 gst_buffer_pool_config_get_params(updated, nullptr, &poolSize, nullptr, nullptr);
109 gst_structure_free(updated);
110 gst_query_add_allocation_pool(query, pool, poolSize, kProposedMinBuffers, 0);
111 proposed = true;
112 }
113 gst_object_unref(pool);
114 }
115 gst_object_unref(device);
116 return proposed;
117}
118#endif
119
120// Propose a shared-device pool for D3D11/D3D12 caps; false for other HW memory or before the bridge is primed.
121bool tryProposeDeviceBoundPool(GstQuery* query, GstCaps* caps, const GstVideoInfo* vinfo, gsize size)
122{
123 GstCapsFeatures* features = gst_caps_get_features(caps, 0);
124 if (!features) {
125 return false;
126 }
127#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D11_GPU_PATH)
128 if (gst_caps_features_contains(features, GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
129 return tryProposeD3D11Pool(query, caps, vinfo, size);
130 }
131#endif
132#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D12_GPU_PATH)
133 if (gst_caps_features_contains(features, GST_CAPS_FEATURE_MEMORY_D3D12_MEMORY)) {
134 return tryProposeD3D12Pool(query, caps, vinfo, size);
135 }
136#endif
137 (void) query;
138 (void) vinfo;
139 (void) size;
140 return false;
141}
142
143bool tryProposeMetaPool(GstQuery* query, GstCaps* caps, gsize size)
144{
145 GstBufferPool* pool = gst_buffer_pool_new();
146 if (!pool) {
147 return false;
148 }
149
150 bool proposed = false;
151 GstStructure* config = gst_buffer_pool_get_config(pool);
152 gst_buffer_pool_config_set_params(config, caps, size, kProposedMinBuffers, 0);
153 gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META);
154 if (gst_buffer_pool_set_config(pool, config)) {
155 gst_query_add_allocation_pool(query, pool, size, kProposedMinBuffers, 0);
156 proposed = true;
157 }
158 gst_object_unref(pool);
159 return proposed;
160}
161
162} // namespace
163
164void populateAllocationQuery(GstQuery* query)
165{
166 GstCaps* caps = nullptr;
167 gboolean need_pool = FALSE;
168 gst_query_parse_allocation(query, &caps, &need_pool);
169 if (!caps) {
170 return;
171 }
172
173 GstVideoInfo vinfo;
174 if (!GstHw::dmaDrmAwareVideoInfo(caps, &vinfo)) {
175 return;
176 }
177 const gsize size = GST_VIDEO_INFO_SIZE(&vinfo);
178
179 GstCapsFeatures* features = gst_caps_get_features(caps, 0);
180 const bool is_system_memory =
181 !features || gst_caps_features_is_equal(features, GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY);
182
183 if (!is_system_memory) {
184 if (!tryProposeDeviceBoundPool(query, caps, &vinfo, size)) {
185 gst_query_add_allocation_pool(query, NULL, size, kProposedMinBuffers, 0);
186 }
187 addConsumedAllocationMetas(query);
188 return;
189 }
190
191 if (!need_pool) {
192 addConsumedAllocationMetas(query);
193 return;
194 }
195
196 (void) tryProposeMetaPool(query, caps, size);
197 addConsumedAllocationMetas(query);
198}
199
200GstPadProbeReturn videosinkQueryProbe(GstPad* pad, GstPadProbeInfo* info, gpointer user_data)
201{
202 (void) pad;
203 (void) user_data;
204 GstQuery* query = GST_PAD_PROBE_INFO_QUERY(info);
205 if (!query) {
206 return GST_PAD_PROBE_OK;
207 }
208
209 switch (GST_QUERY_TYPE(query)) {
210 case GST_QUERY_ALLOCATION:
212 return GST_PAD_PROBE_HANDLED;
213 case GST_QUERY_CONTEXT:
214 // Synchronous answer for gst.gl.GLDisplay/app_context — bus NEED_CONTEXT fallback races state changes and
215 // can isolate glupload from Qt's RHI context.
217 return GST_PAD_PROBE_HANDLED;
218 }
219 return GST_PAD_PROBE_OK;
220 default:
221 return GST_PAD_PROBE_OK;
222 }
223}
224
225} // namespace GstQgc
Config config
bool dmaDrmAwareVideoInfo(GstCaps *caps, GstVideoInfo *info)
GstPadProbeReturn videosinkQueryProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
void populateAllocationQuery(GstQuery *query)
bool answerSinkBinContextQuery(GstQuery *query) noexcept
Synchronously answer GST_QUERY_CONTEXT (gst.gl.GLDisplay/app_context); false -> let bus NEED_CONTEXT ...
Definition HwBuffers.cc:271