QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GstVulkanVideoBuffer.cc
Go to the documentation of this file.
2
3#include <QtGui/qtguiglobal.h>
4
7#include "GstHwVideoBuffer.h"
9
10#if defined(QGC_HAS_GST_VULKAN_GPU_PATH) && QT_CONFIG(vulkan)
11
12#include <QtCore/QLoggingCategory>
13#include <QtCore/QSize>
14#include <private/qvideotexturehelper_p.h>
15#include <rhi/qrhi.h>
16#include <rhi/qrhi_platform.h>
17#include <vulkan/vulkan.h>
18
19// gst-vulkan's gstvkdecoder.h declares a struct member named `slots`, which collides with Qt's `slots` keyword macro
20// (qtmetamacros.h). Suppress the macro across the gst/vulkan includes only.
21#pragma push_macro("slots")
22#undef slots
23#include <gst/vulkan/gstvkdevice.h>
24#include <gst/vulkan/gstvkimagememory.h>
25#pragma pop_macro("slots")
26
27#include "QGCLoggingCategory.h"
28
29QGC_LOGGING_CATEGORY(GstVulkanBufLog, "Video.GStreamer.HwBuffers.GstVulkanBuf")
30
31namespace {
32
34
36std::atomic<bool> s_loggedDeviceMismatch{false};
37std::atomic<bool> s_loggedMultiMemory{false};
38std::atomic<bool> s_loggedSyncFallback{false};
39std::atomic<bool> s_loggedFirstSuccess{false};
40
41} // namespace
42
43GstVulkanVideoBuffer::GstVulkanVideoBuffer(GstSample* sample, const GstVideoInfo& videoInfo,
44 const QVideoFrameFormat& format)
45 : GstHwVideoBuffer(QVideoFrame::RhiTextureHandle, sample, videoInfo, format)
46{}
47
48bool GstVulkanVideoBuffer::validatePlaneHandles() const
49{
50 return validatePlanes([](GstMemory* mem) { return mem && gst_is_vulkan_image_memory(mem); });
51}
52
53QVideoFrameTexturesUPtr GstVulkanVideoBuffer::mapTextures(QRhi& rhi, QVideoFrameTexturesUPtr& /*old*/)
54{
56 if (!rhi.thread()->isCurrentThread()) {
57 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
58 }
59
60 GstBuffer* buffer = nullptr;
61 if (!checkMapPreconditions(rhi, static_cast<int>(QRhi::Vulkan), GstVulkanBufLog(), s_diag, buffer)) {
62 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
63 }
64
65 // Vulkan video decode yields one multiplanar VkImage (NV12/P010); disjoint multi-memory layouts are out of scope.
66 if (gst_buffer_n_memory(buffer) != 1) {
67 QGC_HW_WARN_ONCE(GstVulkanBufLog, s_loggedMultiMemory,
68 "Vulkan import: multi-memory buffer not supported — CPU fallback");
69 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
70 }
71
72 GstMemory* mem0 = gst_buffer_peek_memory(buffer, 0);
73 if (!mem0 || !gst_is_vulkan_image_memory(mem0)) {
74 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
75 }
76 auto* vkMem = reinterpret_cast<GstVulkanImageMemory*>(mem0);
77
78 const auto* nh = static_cast<const QRhiVulkanNativeHandles*>(rhi.nativeHandles());
79 if (!nh || nh->dev == VK_NULL_HANDLE) {
80 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
81 }
82
83 // Hard safety net: createFrom() requires the texture belong to QRhi's VkDevice. A VkImage from a different VkDevice
84 // is unusable (UB), so any mismatch routes to the CPU memcpy path. This is the expected outcome until gst-vulkan
85 // offers a same-VkDevice wrap (see GstVulkanContextBridge).
86 if (!vkMem->device || vkMem->device->device != nh->dev) {
89 QGC_HW_WARN_ONCE(GstVulkanBufLog, s_loggedDeviceMismatch,
90 "Vulkan import: GstVulkanImageMemory VkDevice != QRhi VkDevice — CPU fallback");
91 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
92 }
93
94 // No decoder->QRhi sync wired: real zero-copy needs a shared timeline semaphore (GstVulkanOperation); the only
95 // correct alternative — a per-frame vkDeviceWaitIdle — would stall the whole UI. Handing QRhi an unsynchronized
96 // VkImage would tear, so demote to CPU until sync lands: flip kVulkanSyncImplemented and add the semaphore wait
97 // here. Unreachable on gst-vulkan 1.24.2 (the device-match guard above fails first); reachable once a same-VkDevice
98 // wrap exists.
99 constexpr bool kVulkanSyncImplemented = false;
100 if constexpr (!kVulkanSyncImplemented) {
103 QGC_HW_WARN_ONCE(GstVulkanBufLog, s_loggedSyncFallback,
104 "Vulkan import: decoder sync unimplemented (no timeline semaphore) — CPU fallback");
105 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
106 }
107
108 const VkImage image = vkMem->image;
109 const int layout = static_cast<int>(vkMem->barrier.image_layout);
110 if (image == VK_NULL_HANDLE) {
111 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
112 }
113
114 // Pre-flight RHI format/size support before createFrom() so an unsupported import demotes to CPU on a query.
115 if (!GstHwImportPreflight::preflightOrRecord(&rhi, HwVideoBufferPath::Vulkan, _format.pixelFormat(),
116 _format.frameSize())) {
117 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
118 }
119
120 auto textures = std::make_unique<GstVulkanBorrowedFrameTextures>(&rhi, _format.frameSize(), _format.pixelFormat(),
121 image, layout);
122 if (!textures->texture(0)) {
125 QGC_HW_WARN_ONCE(GstVulkanBufLog, s_diag.loggedTextureCreateFail,
126 "Vulkan import: QRhiTexture::createFrom(VkImage) failed — CPU fallback");
127 return GstHwPathTelemetry::fail(HwVideoBufferPath::Vulkan);
128 }
129
130 logFirstSuccess(s_loggedFirstSuccess, GstVulkanBufLog(), "Vulkan", _format.frameSize(), _format.pixelFormat(), 1);
131 textures->setSourceSample(takeSample());
132 return textures;
133}
134
135#endif // QGC_HAS_GST_VULKAN_GPU_PATH
#define QGC_HW_WARN_ONCE(LOGCAT, FLAG,...)
Logs once via qCWarning(LOGCAT) the first time FLAG flips true; subsequent trips are silent.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
RAII timer: records mapTextures() wall time into the path's EWMA on scope exit.
Common base for GStreamer-backed QHwVideoBuffer subclasses.
void recordFallbackReason(HwVideoBufferPath attemptedPath, HwFallbackReason reason) noexcept
Per-(path,reason) fallback accounting; lets a bug report show why a path demoted to CPU.
constexpr int kMaxPlanes
Matches GST_VIDEO_MAX_PLANES (gst-video pins it at 4); single source of truth for every per-platform ...
QByteArray format(const QList< LogEntry > &entries, int fmt)
One-shot warning flags per failure cause; paths with extra causes (D3D, IOSurface) derive and add mem...