3#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D11_GPU_PATH)
5#include <QtCore/QMutex>
10#include <gst/d3d11/gstd3d11.h>
24using GstD3DVideoBufferCommon::kMaxPlanes;
25using GstD3DVideoBufferCommon::MapDiagnostics;
26using D3D11FrameTextures = GstD3DVideoBufferCommon::FrameTextures<ID3D11Texture2D>;
27using GstD3DVideoBufferCommon::StagingKey;
28using GstD3DVideoBufferCommon::StagingKeyHash;
36class StagingTexturePool
39 static StagingTexturePool& instance()
41 static StagingTexturePool pool;
45 ~StagingTexturePool() { clear(); }
49 ID3D11Texture2D* acquire(ID3D11Device* dev,
const StagingKey& key,
const D3D11_TEXTURE2D_DESC& dstDesc,
50 int planeIdx, guint subIdx)
52 QMutexLocker lock(&_mutex);
53 StagingRing* ring = _entries.find(key);
55 _entries.insert(key, StagingRing{});
56 ring = _entries.find(key);
58 const std::size_t slot = ring->next;
59 ring->next = (ring->next + 1) % kRingSize;
60 if (ring->textures[slot]) {
62 ring->textures[slot]->AddRef();
63 return ring->textures[slot];
65 ID3D11Texture2D* tex =
nullptr;
66 if (FAILED(dev->CreateTexture2D(&dstDesc,
nullptr, &tex))) {
68 "mapTextures: CreateTexture2D for slice copy failed (plane=" << planeIdx <<
"subresource="
73 ring->textures[slot] = tex;
80 QMutexLocker lock(&_mutex);
86 static constexpr std::size_t kRingSize = 3;
90 std::array<ID3D11Texture2D*, kRingSize> textures{};
94 static constexpr std::size_t kMaxEntries = 8;
98 [](
const StagingKey&, StagingRing& ring) {
99 for (ID3D11Texture2D*& tex : ring.textures) {
110ID3D11Texture2D* copySliceToStaging(ID3D11Texture2D* tex, guint subIdx,
int planeIdx,
111 const D3D11_TEXTURE2D_DESC& srcDesc, GstD3D11Memory* d3dmem)
113 ID3D11Device* d3dDev = gst_d3d11_device_get_device_handle(d3dmem->device);
114 ID3D11DeviceContext* d3dCtx = gst_d3d11_device_get_device_context_handle(d3dmem->device);
115 D3D11_TEXTURE2D_DESC dstDesc = srcDesc;
116 dstDesc.ArraySize = 1;
117 dstDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
118 dstDesc.MiscFlags = 0;
119 dstDesc.MipLevels = 1;
120 const StagingKey key{srcDesc.Width, srcDesc.Height, UINT(srcDesc.Format), planeIdx};
121 ID3D11Texture2D* stagingTex = StagingTexturePool::instance().acquire(d3dDev, key, dstDesc, planeIdx, subIdx);
126 gst_d3d11_device_lock(d3dmem->device);
127 d3dCtx->CopySubresourceRegion(stagingTex, 0, 0, 0, 0, tex, subIdx,
nullptr);
128 gst_d3d11_device_unlock(d3dmem->device);
134void GstD3D11VideoBuffer::resetCachedState() noexcept
136 StagingTexturePool::instance().clear();
141struct D3D11CacheResetRegistrar
143 D3D11CacheResetRegistrar() { GstContextBridgeRegistry::registerCacheReset(&GstD3D11VideoBuffer::resetCachedState); }
146const D3D11CacheResetRegistrar s_d3d11CacheResetRegistrar;
149GstD3D11VideoBuffer::GstD3D11VideoBuffer(GstSample* sample,
const GstVideoInfo& videoInfo,
150 const QVideoFrameFormat& format)
155 resolvePlaneResources();
158GstD3D11VideoBuffer::~GstD3D11VideoBuffer()
160 for (
int i = 0; i < _resolvedCount; ++i) {
162 _textures[i]->Release();
166bool GstD3D11VideoBuffer::validatePlaneHandles()
const
172 return validatePlanes([](GstMemory* mem) {
173 if (!mem || !gst_is_d3d11_memory(mem))
176 return gst_d3d11_memory_get_resource_handle(GST_D3D11_MEMORY_CAST(mem)) !=
nullptr;
180void GstD3D11VideoBuffer::resolvePlaneResources()
182 GstBuffer* buffer = _sample ? gst_sample_get_buffer(_sample) : nullptr;
186 const int memCount = (std::min)(
int(gst_buffer_n_memory(buffer)),
kMaxPlanes);
187 GstD3DVideoBufferCommon::PlaneResourceGuard<ID3D11Texture2D> guard;
188 GstD3D11Device* copyDevice =
nullptr;
190 for (
int i = 0; i < memCount; ++i) {
191 GstMemory* mem = gst_buffer_peek_memory(buffer, i);
192 if (!mem || !gst_is_d3d11_memory(mem)) {
194 "resolve: plane" << i <<
"memory is not GstD3D11Memory (allocator="
195 << (mem && mem->allocator ? mem->allocator->mem_type :
"null") <<
")");
202 GstD3D11Device* bridgeDev = GstD3D11ContextBridge::currentDevice();
203 GstD3D11Device* bufDev = GST_D3D11_MEMORY_CAST(mem)->device;
204 if (bridgeDev && bufDev != bridgeDev) {
205 const gint64 bridgeLuid = GstD3DContextBridgeCommon::readAdapterLuid(bridgeDev);
206 const gint64 bufLuid = GstD3DContextBridgeCommon::readAdapterLuid(bufDev);
207 gst_object_unref(bridgeDev);
209 "resolve: GstD3D11Memory on foreign device (bridge LUID="
210 << bridgeLuid <<
"buffer LUID=" << bufLuid
211 <<
"); bridge missed NEED_CONTEXT race — rejecting frame");
215 gst_object_unref(bridgeDev);
217 ID3D11Texture2D* tex =
218 reinterpret_cast<ID3D11Texture2D*
>(gst_d3d11_memory_get_resource_handle(GST_D3D11_MEMORY_CAST(mem)));
221 "resolve: gst_d3d11_memory_get_resource_handle returned null for plane" << i);
225 const guint subIdx = gst_d3d11_memory_get_subresource_index(GST_D3D11_MEMORY_CAST(mem));
226 D3D11_TEXTURE2D_DESC srcDesc = {};
227 tex->GetDesc(&srcDesc);
228 if (subIdx > 0 || srcDesc.ArraySize > 1) {
229 ID3D11Texture2D* stagingTex = copySliceToStaging(tex, subIdx, i, srcDesc, GST_D3D11_MEMORY_CAST(mem));
233 copyDevice = GST_D3D11_MEMORY_CAST(mem)->device;
234 guard.handles[i] = stagingTex;
237 guard.handles[i] = tex;
244 ID3D11DeviceContext* d3dCtx = gst_d3d11_device_get_device_context_handle(copyDevice);
245 gst_d3d11_device_lock(copyDevice);
247 gst_d3d11_device_unlock(copyDevice);
250 _textures = guard.handles;
251 _resolvedCount = memCount;
256QVideoFrameTexturesUPtr GstD3D11VideoBuffer::mapTextures(QRhi& rhi, QVideoFrameTexturesUPtr& old)
259 GstBuffer* buffer =
nullptr;
260 if (!checkMapPreconditions(rhi,
static_cast<int>(QRhi::D3D11), GstD3D11Log(), s_diag, buffer)) {
267 return GstD3DVideoBufferCommon::mapResolvedTextures(
269 _format.pixelFormat(), s_diag.loggedFirstSuccess, s_diag.loggedTextureCreateFail, GstD3D11Log,
"D3D11");
#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)
Common base for GStreamer-backed QHwVideoBuffer subclasses.
void recordImageCacheHit(HwVideoBufferPath path) noexcept
Native image/texture cache hit/miss accounting.
void recordImageCacheMiss(HwVideoBufferPath path) noexcept
constexpr int kMaxPlanes
Matches GST_VIDEO_MAX_PLANES (gst-video pins it at 4); single source of truth for every per-platform ...