3#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D12_GPU_PATH)
5#include <QtCore/QMutex>
10#include <gst/d3d12/gstd3d12.h>
25using GstD3DVideoBufferCommon::kMaxPlanes;
26using GstD3DVideoBufferCommon::MapDiagnostics;
27using D3D12FrameTextures = GstD3DVideoBufferCommon::FrameTextures<ID3D12Resource>;
28using GstD3DVideoBufferCommon::StagingKey;
29using GstD3DVideoBufferCommon::StagingKeyHash;
38 ID3D12Resource* resource =
nullptr;
39 ID3D12CommandAllocator* allocator =
nullptr;
40 ID3D12GraphicsCommandList* list =
nullptr;
43void releaseEntryResources(
const StagingEntry& e)
48 e.allocator->Release();
50 e.resource->Release();
56 StagingEntryRef() =
default;
58 explicit StagingEntryRef(
const StagingEntry& e) : _entry(e) {}
60 ~StagingEntryRef() { releaseEntryResources(_entry); }
62 StagingEntryRef(
const StagingEntryRef&) =
delete;
63 StagingEntryRef& operator=(
const StagingEntryRef&) =
delete;
65 StagingEntryRef(StagingEntryRef&& other) noexcept : _entry(other._entry) { other._entry = {}; }
67 StagingEntryRef& operator=(StagingEntryRef&& other)
noexcept
70 releaseEntryResources(_entry);
71 _entry = other._entry;
77 bool valid() const noexcept {
return _entry.resource && _entry.allocator && _entry.list; }
79 ID3D12Resource* resource() const noexcept {
return _entry.resource; }
81 ID3D12CommandAllocator* allocator() const noexcept {
return _entry.allocator; }
83 ID3D12GraphicsCommandList* list() const noexcept {
return _entry.list; }
85 ID3D12Resource* takeResource() noexcept
87 ID3D12Resource* resource = _entry.resource;
88 _entry.resource =
nullptr;
96StagingEntryRef addRefEntry(
const StagingEntry& e)
101 e.allocator->AddRef();
104 return StagingEntryRef(e);
109class StagingResourcePool
112 static StagingResourcePool& instance()
114 static StagingResourcePool pool;
118 ~StagingResourcePool() { clear(); }
122 StagingEntryRef acquire(ID3D12Device* dev, D3D12_COMMAND_LIST_TYPE queueType,
const StagingKey& key,
123 const D3D12_RESOURCE_DESC& dstDesc,
const D3D12_HEAP_PROPERTIES& heapProps,
int planeIdx,
126 QMutexLocker lock(&_mutex);
127 StagingEntry* cachedEntry = _entries.find(key);
128 if (cachedEntry && cachedEntry->resource && cachedEntry->allocator && cachedEntry->list) {
130 return addRefEntry(*cachedEntry);
134 if (FAILED(dev->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &dstDesc, D3D12_RESOURCE_STATE_COMMON,
135 nullptr, IID_PPV_ARGS(&e.resource)))) {
137 "mapTextures: CreateCommittedResource for slice copy failed (plane="
138 << planeIdx <<
" subresource=" << subIdx <<
")");
141 if (FAILED(dev->CreateCommandAllocator(queueType, IID_PPV_ARGS(&e.allocator))) ||
142 FAILED(dev->CreateCommandList(0, queueType, e.allocator,
nullptr, IID_PPV_ARGS(&e.list)))) {
144 "mapTextures: command allocator/list create failed (plane=" << planeIdx <<
")");
148 e.allocator->Release();
149 e.resource->Release();
155 StagingEntryRef ref = addRefEntry(e);
156 _entries.insert(key, e);
163 QMutexLocker lock(&_mutex);
168 static constexpr std::size_t kMaxEntries = 8;
172 [](
const StagingKey&, StagingEntry& e) {
173 releaseEntryResources(e);
181ID3D12Resource* copySliceToStaging(ID3D12Resource* resource, guint subIdx,
int planeIdx,
182 const D3D12_RESOURCE_DESC& srcDesc, ID3D12Device* d3dDev, GstD3D12CmdQueue* cmdQueue,
183 D3D12_COMMAND_LIST_TYPE queueType, guint64& maxFenceValue,
184 StagingEntryRef& retainedEntry)
186 D3D12_RESOURCE_DESC dstDesc = srcDesc;
187 dstDesc.DepthOrArraySize = 1;
188 dstDesc.MipLevels = 1;
190 D3D12_HEAP_PROPERTIES heapProps = {};
191 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
193 const StagingKey key{srcDesc.Width, srcDesc.Height, UINT(srcDesc.Format), planeIdx};
194 StagingEntryRef entry =
195 StagingResourcePool::instance().acquire(d3dDev, queueType, key, dstDesc, heapProps, planeIdx, subIdx);
196 if (!entry.valid()) {
200 if (FAILED(entry.allocator()->Reset()) || FAILED(entry.list()->Reset(entry.allocator(),
nullptr))) {
202 "mapTextures: command allocator/list reset failed (plane=" << planeIdx <<
")");
205 ID3D12GraphicsCommandList* cmdList = entry.list();
207 D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
208 srcLoc.pResource = resource;
209 srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
210 srcLoc.SubresourceIndex = subIdx;
212 D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
213 dstLoc.pResource = entry.resource();
214 dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
215 dstLoc.SubresourceIndex = 0;
219 D3D12_RESOURCE_BARRIER toCopySrc = {};
220 toCopySrc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
221 toCopySrc.Transition.pResource = resource;
222 toCopySrc.Transition.Subresource = subIdx;
223 toCopySrc.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
224 toCopySrc.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
225 cmdList->ResourceBarrier(1, &toCopySrc);
227 cmdList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc,
nullptr);
229 D3D12_RESOURCE_BARRIER toCommon = toCopySrc;
230 toCommon.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
231 toCommon.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
232 cmdList->ResourceBarrier(1, &toCommon);
236 ID3D12CommandList* lists[] = {cmdList};
237 guint64 fenceValue = 0;
239 if (FAILED(gst_d3d12_cmd_queue_execute_command_lists(cmdQueue, 1, lists, &fenceValue))) {
242 StagingResourcePool::instance().clear();
245 if (fenceValue > maxFenceValue) {
246 maxFenceValue = fenceValue;
248 ID3D12Resource* stagingResource = entry.takeResource();
249 retainedEntry = std::move(entry);
250 return stagingResource;
255void GstD3D12VideoBuffer::resetCachedState() noexcept
257 StagingResourcePool::instance().clear();
262struct D3D12CacheResetRegistrar
264 D3D12CacheResetRegistrar() { GstContextBridgeRegistry::registerCacheReset(&GstD3D12VideoBuffer::resetCachedState); }
267const D3D12CacheResetRegistrar s_d3d12CacheResetRegistrar;
270GstD3D12VideoBuffer::GstD3D12VideoBuffer(GstSample* sample,
const GstVideoInfo& videoInfo,
271 const QVideoFrameFormat& format)
276 resolvePlaneResources();
279GstD3D12VideoBuffer::~GstD3D12VideoBuffer()
281 for (
int i = 0; i < _resolvedCount; ++i) {
283 _resources[i]->Release();
287bool GstD3D12VideoBuffer::validatePlaneHandles()
const
293 return validatePlanes([](GstMemory* mem) {
294 if (!mem || !gst_is_d3d12_memory(mem))
296 return gst_d3d12_memory_get_resource_handle(GST_D3D12_MEMORY_CAST(mem)) !=
nullptr;
300void GstD3D12VideoBuffer::resolvePlaneResources()
302 GstBuffer* buffer = _sample ? gst_sample_get_buffer(_sample) : nullptr;
306 const int memCount = (std::min)(
int(gst_buffer_n_memory(buffer)),
kMaxPlanes);
307 GstD3DVideoBufferCommon::PlaneResourceGuard<ID3D12Resource> guard;
308 std::array<StagingEntryRef, kMaxPlanes> stagingLeases;
315 GstD3D12CmdQueue* q =
nullptr;
324 D3D12_COMMAND_LIST_TYPE queueType = D3D12_COMMAND_LIST_TYPE_COPY;
325 guint64 maxFenceValue = 0;
326 bool anyCopy =
false;
327 struct SubmittedCopyWait
329 GstD3D12CmdQueue*& queue;
330 guint64& maxFenceValue;
348 if (!anyCopy || !queue) {
352 if (FAILED(gst_d3d12_cmd_queue_fence_wait(queue, maxFenceValue))) {
354 "resolve: gst_d3d12_cmd_queue_fence_wait failed");
359 } submittedCopyWait{queueRef.q, maxFenceValue, anyCopy};
361 for (
int i = 0; i < memCount; ++i) {
362 GstMemory* mem = gst_buffer_peek_memory(buffer, i);
363 if (!mem || !gst_is_d3d12_memory(mem)) {
365 "resolve: plane" << i <<
"memory is not GstD3D12Memory (allocator="
366 << (mem && mem->allocator ? mem->allocator->mem_type :
"null") <<
")");
369 GstD3D12Memory* d3dmem = GST_D3D12_MEMORY_CAST(mem);
374 GstD3D12Device* bridgeDev = GstD3D12ContextBridge::currentDevice();
375 if (bridgeDev && d3dmem->device != bridgeDev) {
376 const gint64 bridgeLuid = GstD3DContextBridgeCommon::readAdapterLuid(bridgeDev);
377 const gint64 bufLuid = GstD3DContextBridgeCommon::readAdapterLuid(d3dmem->device);
378 gst_object_unref(bridgeDev);
380 "resolve: GstD3D12Memory on foreign device (bridge LUID="
381 << bridgeLuid <<
"buffer LUID=" << bufLuid
382 <<
"); bridge missed NEED_CONTEXT race — rejecting frame");
386 gst_object_unref(bridgeDev);
390 if (!gst_d3d12_memory_sync(d3dmem)) {
392 "resolve: gst_d3d12_memory_sync failed for plane" << i);
395 ID3D12Resource* resource = gst_d3d12_memory_get_resource_handle(d3dmem);
398 "resolve: gst_d3d12_memory_get_resource_handle returned null for plane" << i);
404 gst_d3d12_memory_get_subresource_index(d3dmem, guint(i), &subIdx);
405 D3D12_RESOURCE_DESC srcDesc = resource->GetDesc();
406 if (subIdx > 0 || srcDesc.DepthOrArraySize > 1) {
407 ID3D12Device* d3dDev = gst_d3d12_device_get_device_handle(d3dmem->device);
409 queueType = D3D12_COMMAND_LIST_TYPE_COPY;
410 GstD3D12CmdQueue* q = gst_d3d12_device_get_cmd_queue(d3dmem->device, queueType);
412 queueType = D3D12_COMMAND_LIST_TYPE_DIRECT;
413 q = gst_d3d12_device_get_cmd_queue(d3dmem->device, queueType);
422 if (!d3dDev || !queueRef.q) {
424 "mapTextures: could not obtain D3D12 device/queue for slice copy (plane=" << i <<
")");
427 ID3D12Resource* stagingResource = copySliceToStaging(resource, subIdx, i, srcDesc, d3dDev, queueRef.q,
428 queueType, maxFenceValue, stagingLeases[i]);
429 if (!stagingResource) {
433 guard.handles[i] = stagingResource;
436 guard.handles[i] = resource;
443 if (!submittedCopyWait.waitAndDisarm()) {
447 _resources = guard.handles;
448 _resolvedCount = memCount;
453QVideoFrameTexturesUPtr GstD3D12VideoBuffer::mapTextures(QRhi& rhi, QVideoFrameTexturesUPtr& old)
457 GstBuffer* buffer =
nullptr;
458 if (!checkMapPreconditions(rhi,
static_cast<int>(QRhi::D3D12), GstD3D12Log(), s_diag, buffer)) {
466 return GstD3DVideoBufferCommon::mapResolvedTextures(
468 _format.pixelFormat(), s_diag.loggedFirstSuccess, s_diag.loggedTextureCreateFail, GstD3D12Log,
"D3D12");
#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 recordSyncWait(HwVideoBufferPath path, bool gpuSide) noexcept
GL fence sync wait; split CPU-blocking vs GPU-side.
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 ...