3#if defined(Q_OS_WIN) && defined(QGC_HAS_GST_D3D12_GPU_PATH)
10#include <QtCore/QVarLengthArray>
12#include <gst/d3d12/gstd3d12.h>
20using GstD3DVideoBufferCommon::kMaxPlanes;
21using GstD3DVideoBufferCommon::MapDiagnostics;
22using GstD3DVideoBufferCommon::fail;
23using D3D12FrameTextures = GstD3DVideoBufferCommon::FrameTextures<ID3D12Resource>;
29ID3D12Resource *copySliceToStaging(ID3D12Resource *resource, guint subIdx,
int planeIdx,
30 const D3D12_RESOURCE_DESC &srcDesc,
31 GstD3D12Memory *d3dmem)
33 ID3D12Device *d3dDev = gst_d3d12_device_get_device_handle(d3dmem->device);
34 D3D12_COMMAND_LIST_TYPE queueType = D3D12_COMMAND_LIST_TYPE_COPY;
35 GstD3D12CmdQueue *cmdQueue = gst_d3d12_device_get_cmd_queue(d3dmem->device, queueType);
37 queueType = D3D12_COMMAND_LIST_TYPE_DIRECT;
38 cmdQueue = gst_d3d12_device_get_cmd_queue(d3dmem->device, queueType);
40 ID3D12CommandQueue *rawQueue = cmdQueue ? gst_d3d12_cmd_queue_get_handle(cmdQueue) : nullptr;
41 if (!d3dDev || !rawQueue) {
42 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedTextureCreateFail,
43 "mapTextures: could not obtain D3D12 device/queue for slice copy (plane="
48 D3D12_RESOURCE_DESC dstDesc = srcDesc;
49 dstDesc.DepthOrArraySize = 1;
50 dstDesc.MipLevels = 1;
52 D3D12_HEAP_PROPERTIES heapProps{};
53 heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
55 ID3D12Resource *stagingResource =
nullptr;
56 if (FAILED(d3dDev->CreateCommittedResource(
57 &heapProps, D3D12_HEAP_FLAG_NONE, &dstDesc,
58 D3D12_RESOURCE_STATE_COMMON,
nullptr, IID_PPV_ARGS(&stagingResource)))) {
59 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedTextureCreateFail,
60 "mapTextures: CreateCommittedResource for slice copy failed (plane=" << planeIdx
61 <<
" subresource=" << subIdx <<
")");
66 ID3D12CommandAllocator *cmdAlloc =
nullptr;
67 ID3D12GraphicsCommandList *cmdList =
nullptr;
69 if (SUCCEEDED(d3dDev->CreateCommandAllocator(queueType, IID_PPV_ARGS(&cmdAlloc))) &&
70 SUCCEEDED(d3dDev->CreateCommandList(0, queueType,
71 cmdAlloc,
nullptr, IID_PPV_ARGS(&cmdList)))) {
72 D3D12_TEXTURE_COPY_LOCATION srcLoc{};
73 srcLoc.pResource = resource;
74 srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
75 srcLoc.SubresourceIndex = subIdx;
77 D3D12_TEXTURE_COPY_LOCATION dstLoc{};
78 dstLoc.pResource = stagingResource;
79 dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
80 dstLoc.SubresourceIndex = 0;
85 D3D12_RESOURCE_BARRIER toCopySrc{};
86 toCopySrc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
87 toCopySrc.Transition.pResource = resource;
88 toCopySrc.Transition.Subresource = subIdx;
89 toCopySrc.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
90 toCopySrc.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
91 cmdList->ResourceBarrier(1, &toCopySrc);
93 cmdList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc,
nullptr);
95 D3D12_RESOURCE_BARRIER toCommon = toCopySrc;
96 toCommon.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
97 toCommon.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
98 cmdList->ResourceBarrier(1, &toCommon);
102 ID3D12CommandList *lists[] = { cmdList };
103 rawQueue->ExecuteCommandLists(1, lists);
105 ID3D12Fence *fence =
nullptr;
106 if (SUCCEEDED(d3dDev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)))) {
107 rawQueue->Signal(fence, 1);
108 HANDLE
event = CreateEvent(
nullptr, FALSE, FALSE,
nullptr);
110 fence->SetEventOnCompletion(1, event);
111 WaitForSingleObject(event, INFINITE);
118 if (cmdList) cmdList->Release();
119 if (cmdAlloc) cmdAlloc->Release();
122 stagingResource->Release();
125 return stagingResource;
130GstD3D12VideoBuffer::GstD3D12VideoBuffer(GstSample *sample,
131 const GstVideoInfo &videoInfo,
132 const QVideoFrameFormat &format)
137GstD3D12VideoBuffer::~GstD3D12VideoBuffer() =
default;
139QAbstractVideoBuffer::MapData GstD3D12VideoBuffer::map(QVideoFrame::MapMode )
144bool GstD3D12VideoBuffer::validatePlaneHandles()
const
146 if (!_sample)
return false;
147 GstBuffer *buffer = gst_sample_get_buffer(_sample);
148 if (!buffer)
return false;
149 const int memCount = qMin(
int(gst_buffer_n_memory(buffer)), kMaxPlanes);
150 if (memCount <= 0)
return false;
151 for (
int i = 0; i < memCount; ++i) {
152 GstMemory *mem = gst_buffer_peek_memory(buffer, i);
153 if (!mem || !gst_is_d3d12_memory(mem))
return false;
154 if (!gst_d3d12_memory_get_resource_handle(GST_D3D12_MEMORY_CAST(mem))) {
161QVideoFrameTexturesUPtr GstD3D12VideoBuffer::mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr & )
163 Q_ASSERT(rhi.thread()->isCurrentThread());
169 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedNullSample,
"mapTextures: GstSample is null");
172 if (rhi.backend() != QRhi::D3D12) {
173 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedBadBackend,
174 "mapTextures: QRhi backend is" << rhi.backendName() <<
"(D3D12 required)");
178 GstBuffer *buffer = gst_sample_get_buffer(_sample);
180 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedNullBuffer,
"mapTextures: GstSample has no buffer");
184 const int memCount = qMin(
int(gst_buffer_n_memory(buffer)), kMaxPlanes);
185 std::array<ID3D12Resource *, kMaxPlanes> resources{};
186 QVarLengthArray<ID3D12Resource *, kMaxPlanes> refdResources;
187 for (
int i = 0; i < memCount; ++i) {
188 GstMemory *mem = gst_buffer_peek_memory(buffer, i);
189 if (!mem || !gst_is_d3d12_memory(mem)) {
190 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedNonD3DMemory,
191 "mapTextures: plane" << i <<
"memory is not GstD3D12Memory (allocator="
192 << (mem && mem->allocator ? mem->allocator->mem_type :
"null")
194 for (
auto *r : refdResources) r->Release();
197 GstD3D12Memory *d3dmem = GST_D3D12_MEMORY_CAST(mem);
203 GstD3D12Device *bridgeDev = GstD3D12ContextBridge::currentDevice();
204 if (bridgeDev && d3dmem->device != bridgeDev) {
205 const gint64 bridgeLuid = GstD3DContextBridgeCommon::readAdapterLuid(bridgeDev);
206 const gint64 bufLuid = GstD3DContextBridgeCommon::readAdapterLuid(d3dmem->device);
207 gst_object_unref(bridgeDev);
208 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedDeviceMismatch,
209 "mapTextures: GstD3D12Memory on foreign device (bridge LUID="
210 << bridgeLuid <<
"buffer LUID=" << bufLuid
211 <<
"); bridge missed NEED_CONTEXT race — rejecting frame");
214 if (bridgeDev) gst_object_unref(bridgeDev);
219 if (!gst_d3d12_memory_sync(d3dmem)) {
220 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedNullResource,
221 "mapTextures: gst_d3d12_memory_sync failed for plane" << i);
222 for (
auto *r : refdResources) r->Release();
225 ID3D12Resource *resource = gst_d3d12_memory_get_resource_handle(d3dmem);
227 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedNullResource,
228 "mapTextures: gst_d3d12_memory_get_resource_handle returned null for plane" << i);
229 for (
auto *r : refdResources) r->Release();
234 gst_d3d12_memory_get_subresource_index(d3dmem, guint(i), &subIdx);
235 D3D12_RESOURCE_DESC srcDesc = resource->GetDesc();
236 if (subIdx > 0 || srcDesc.DepthOrArraySize > 1) {
237 ID3D12Resource *stagingResource = copySliceToStaging(resource, subIdx, i, srcDesc, d3dmem);
238 if (!stagingResource) {
239 for (
auto *r : refdResources) r->Release();
242 refdResources.append(stagingResource);
243 resources[i] = stagingResource;
246 refdResources.append(resource);
247 resources[i] = resource;
251 auto textures = std::make_unique<D3D12FrameTextures>(&rhi, _format.frameSize(),
252 _format.pixelFormat(), resources, memCount);
255 for (
int i = 0; i < memCount; ++i) {
256 if (!textures->texture(
static_cast<uint
>(i))) {
257 QGC_D3D_WARN_ONCE(GstD3D12Log, s_diag.loggedTextureCreateFail,
258 "mapTextures: QRhiTexture::createFrom failed plane=" << i
259 <<
" (size=" << _format.frameSize()
260 <<
" format=" <<
int(_format.pixelFormat()) <<
" planes=" << memCount <<
")");
265 if (!s_diag.loggedFirstSuccess.exchange(
true, std::memory_order_relaxed)) {
266 qCInfo(GstD3D12Log) <<
"First D3D12 zero-copy mapTextures success: size=" << _format.frameSize()
267 <<
"format=" << int(_format.pixelFormat()) <<
"planes=" << memCount;
272quint64 GstD3D12VideoBuffer::takeMapFailureCount()
274 return GstD3DVideoBufferCommon::takeMapFailureCount(s_diag);
277quint64 GstD3D12VideoBuffer::peekMapFailureCount()
279 return GstD3DVideoBufferCommon::peekMapFailureCount(s_diag);
#define QGC_LOGGING_CATEGORY(name, categoryStr)