1 /*
2 * Direct3D 12 HW acceleration.
3 *
4 * copyright (c) 2022-2023 Wu Jianhua <toqsxw@outlook.com>
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "config.h"
34 #include <dxgi1_3.h>
35
37
39 /**
40 * The public AVD3D12VAFramesContext. See hwcontext_d3d12va.h for it.
41 */
43
52
54 /**
55 * The public AVD3D12VADeviceContext. See hwcontext_d3d12va.h for it.
56 */
64
65 static const struct {
71 };
72
74 {
75 WaitForSingleObjectEx(
ctx, INFINITE, FALSE);
76 }
77
79 {
81 }
82
84 {
85 uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->
fence);
86 if (completion < psync_ctx->fence_value) {
87 if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->
fence, psync_ctx->
fence_value, psync_ctx->
event)))
89
90 WaitForSingleObjectEx(psync_ctx->
event, INFINITE, FALSE);
91 }
92
93 return 0;
94 }
95
97 {
100
103 }
104
106 ID3D12Resource **ppResource, int download)
107 {
110 D3D12_HEAP_PROPERTIES props = { .Type = download ? D3D12_HEAP_TYPE_READBACK : D3D12_HEAP_TYPE_UPLOAD };
111 D3D12_RESOURCE_DESC
desc = {
112 .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
113 .Alignment = 0,
114 .Width =
s->luma_component_size + (
s->luma_component_size >> 1),
115 .Height = 1,
116 .DepthOrArraySize = 1,
117 .MipLevels = 1,
118 .Format = DXGI_FORMAT_UNKNOWN,
119 .SampleDesc = { .Count = 1, .Quality = 0 },
120 .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
121 .Flags = D3D12_RESOURCE_FLAG_NONE,
122 };
123
124 if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->
device, &props, D3D12_HEAP_FLAG_NONE, &
desc,
125 states,
NULL, &IID_ID3D12Resource, (
void **)ppResource))) {
128 }
129
130 return 0;
131 }
132
134 {
138
139 D3D12_COMMAND_QUEUE_DESC queue_desc = {
140 .Type = D3D12_COMMAND_LIST_TYPE_COPY,
141 .Priority = 0,
142 .NodeMask = 0,
143 };
144
145 s->luma_component_size =
FFALIGN(
ctx->width * (frames_hwctx->
format == DXGI_FORMAT_P010 ? 2 : 1),
146 D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) *
ctx->height;
147
148 DX_CHECK(ID3D12Device_CreateFence(device_hwctx->
device, 0, D3D12_FENCE_FLAG_NONE,
149 &IID_ID3D12Fence, (
void **)&
s->sync_ctx.fence));
150
151 s->sync_ctx.event = CreateEvent(
NULL, FALSE, FALSE,
NULL);
152 if (!
s->sync_ctx.event)
154
155 DX_CHECK(ID3D12Device_CreateCommandQueue(device_hwctx->
device, &queue_desc,
156 &IID_ID3D12CommandQueue, (
void **)&
s->command_queue));
157
158 DX_CHECK(ID3D12Device_CreateCommandAllocator(device_hwctx->
device, queue_desc.Type,
159 &IID_ID3D12CommandAllocator, (
void **)&
s->command_allocator));
160
161 DX_CHECK(ID3D12Device_CreateCommandList(device_hwctx->
device, 0, queue_desc.Type,
162 s->command_allocator,
NULL, &IID_ID3D12GraphicsCommandList, (
void **)&
s->command_list));
163
164 DX_CHECK(ID3D12GraphicsCommandList_Close(
s->command_list));
165
166 ID3D12CommandQueue_ExecuteCommandLists(
s->command_queue, 1, (ID3D12CommandList **)&
s->command_list);
167
169
172 }
173
175 {
177
179 if (
s->sync_ctx.event)
180 CloseHandle(
s->sync_ctx.event);
181
187 }
188
190 {
191 HRESULT hr;
192 int nb_sw_formats = 0;
194
199
201 D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support = {
supported_formats[
i].d3d_format };
202 hr = ID3D12Device_CheckFeatureSupport(device_hwctx->
device, D3D12_FEATURE_FORMAT_SUPPORT, &format_support,
sizeof(format_support));
203 if (SUCCEEDED(hr) && (format_support.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D))
205 }
207
211
214
215 return 0;
216 }
217
219 {
221
224 if (
frame->sync_ctx.event)
225 CloseHandle(
frame->sync_ctx.event);
226
228 }
229
231 {
235
238 D3D12_HEAP_PROPERTIES props = { .Type = D3D12_HEAP_TYPE_DEFAULT };
239 D3D12_RESOURCE_DESC
desc = {
240 .Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D,
241 .Alignment = 0,
243 .Height =
ctx->height,
244 .DepthOrArraySize = 1,
245 .MipLevels = 1,
247 .SampleDesc = {.Count = 1, .Quality = 0 },
248 .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
249 .Flags = D3D12_RESOURCE_FLAG_NONE,
250 };
251
255
256 if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->
device, &props, D3D12_HEAP_FLAG_NONE, &
desc,
257 D3D12_RESOURCE_STATE_COMMON,
NULL, &IID_ID3D12Resource, (
void **)&
frame->texture))) {
260 }
261
262 DX_CHECK(ID3D12Device_CreateFence(device_hwctx->
device, 0, D3D12_FENCE_FLAG_NONE,
263 &IID_ID3D12Fence, (
void **)&
frame->sync_ctx.fence));
264
265 frame->sync_ctx.event = CreateEvent(
NULL, FALSE, FALSE,
NULL);
266 if (!
frame->sync_ctx.event)
268
270 if (!buf)
272
273 return buf;
274
278 }
279
281 {
284
287 if (hwctx->
format != DXGI_FORMAT_UNKNOWN &&
291 break;
292 }
293 }
298 }
299
302
305
306 return 0;
307 }
308
310 {
312
316
319 D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
322
327
328 return 0;
329 }
330
334 {
336
338 if (!fmts)
340
341 fmts[0] =
ctx->sw_format;
343
345
346 return 0;
347 }
348
351 {
355
360
362 ID3D12Resource *texture = (ID3D12Resource *)
f->texture;
363
364 uint8_t *mapped_data;
366 int linesizes[4];
367
368 D3D12_TEXTURE_COPY_LOCATION staging_y_location = { 0 };
369 D3D12_TEXTURE_COPY_LOCATION staging_uv_location = { 0 };
370
371 D3D12_TEXTURE_COPY_LOCATION texture_y_location = {
372 .pResource = texture,
373 .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
374 .SubresourceIndex = 0,
375 };
376
377 D3D12_TEXTURE_COPY_LOCATION texture_uv_location = {
378 .pResource = texture,
379 .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
380 .SubresourceIndex = 1,
381 };
382
383 D3D12_RESOURCE_BARRIER barrier = {
384 .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
385 .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
386 .Transition = {
387 .pResource = texture,
388 .StateBefore = D3D12_RESOURCE_STATE_COMMON,
389 .StateAfter = download ? D3D12_RESOURCE_STATE_COPY_SOURCE : D3D12_RESOURCE_STATE_COPY_DEST,
390 .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
391 },
392 };
393
396
398
399 if (!
s->command_queue) {
403 }
404
405 for (
int i = 0;
i < 4;
i++)
407 D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
408
409 staging_y_location = (D3D12_TEXTURE_COPY_LOCATION) {
410 .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
411 .PlacedFootprint = {
412 .Offset = 0,
413 .Footprint = {
414 .Format = frames_hwctx->
format == DXGI_FORMAT_P010 ?
415 DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_R8_UNORM,
417 .Height =
ctx->height,
418 .Depth = 1,
419 .RowPitch = linesizes[0],
420 },
421 },
422 };
423
424 staging_uv_location = (D3D12_TEXTURE_COPY_LOCATION) {
425 .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
426 .PlacedFootprint = {
427 .Offset =
s->luma_component_size,
428 .Footprint = {
429 .Format = frames_hwctx->
format == DXGI_FORMAT_P010 ?
430 DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R8G8_UNORM,
431 .Width =
ctx->width >> 1,
432 .Height =
ctx->height >> 1,
433 .Depth = 1,
434 .RowPitch = linesizes[0],
435 },
436 },
437 };
438
439 DX_CHECK(ID3D12CommandAllocator_Reset(
s->command_allocator));
440
441 DX_CHECK(ID3D12GraphicsCommandList_Reset(
s->command_list,
s->command_allocator,
NULL));
442
443 if (download) {
444 if (!
s->staging_download_buffer) {
446 &
s->staging_download_buffer, 1);
449 }
450 }
451
452 staging_y_location.pResource = staging_uv_location.pResource =
s->staging_download_buffer;
453
454 ID3D12GraphicsCommandList_ResourceBarrier(
s->command_list, 1, &barrier);
455
456 ID3D12GraphicsCommandList_CopyTextureRegion(
s->command_list,
457 &staging_y_location, 0, 0, 0,
458 &texture_y_location,
NULL);
459
460 ID3D12GraphicsCommandList_CopyTextureRegion(
s->command_list,
461 &staging_uv_location, 0, 0, 0,
462 &texture_uv_location,
NULL);
463
464 barrier.Transition.StateBefore = barrier.Transition.StateAfter;
465 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
466 ID3D12GraphicsCommandList_ResourceBarrier(
s->command_list, 1, &barrier);
467
468 DX_CHECK(ID3D12GraphicsCommandList_Close(
s->command_list));
469
470 DX_CHECK(ID3D12CommandQueue_Wait(
s->command_queue,
f->sync_ctx.fence,
f->sync_ctx.fence_value));
471
472 ID3D12CommandQueue_ExecuteCommandLists(
s->command_queue, 1, (ID3D12CommandList **)&
s->command_list);
473
477
478 DX_CHECK(ID3D12Resource_Map(
s->staging_download_buffer, 0,
NULL, (
void **)&mapped_data));
480
482 ctx->sw_format,
ctx->width,
ctx->height);
483
484 ID3D12Resource_Unmap(
s->staging_download_buffer, 0,
NULL);
485 } else {
486 if (!
s->staging_upload_buffer) {
488 &
s->staging_upload_buffer, 0);
491 }
492 }
493
494 staging_y_location.pResource = staging_uv_location.pResource =
s->staging_upload_buffer;
495
496 DX_CHECK(ID3D12Resource_Map(
s->staging_upload_buffer, 0,
NULL, (
void **)&mapped_data));
498
500 ctx->sw_format,
ctx->width,
ctx->height);
501
502 ID3D12Resource_Unmap(
s->staging_upload_buffer, 0,
NULL);
503
504 ID3D12GraphicsCommandList_ResourceBarrier(
s->command_list, 1, &barrier);
505
506 ID3D12GraphicsCommandList_CopyTextureRegion(
s->command_list,
507 &texture_y_location, 0, 0, 0,
508 &staging_y_location,
NULL);
509
510 ID3D12GraphicsCommandList_CopyTextureRegion(
s->command_list,
511 &texture_uv_location, 0, 0, 0,
512 &staging_uv_location,
NULL);
513
514 barrier.Transition.StateBefore = barrier.Transition.StateAfter;
515 barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
516 ID3D12GraphicsCommandList_ResourceBarrier(
s->command_list, 1, &barrier);
517
518 DX_CHECK(ID3D12GraphicsCommandList_Close(
s->command_list));
519
520 ID3D12CommandQueue_ExecuteCommandLists(
s->command_queue, 1, (ID3D12CommandList **)&
s->command_list);
521
525 }
526
528
529 return 0;
530
534 }
535
537 {
539
540 #if !HAVE_UWP
541 priv->
d3d12lib = dlopen(
"d3d12.dll", 0);
542 priv->
dxgilib = dlopen(
"dxgi.dll", 0);
543
546
550
554
556 #else
557 priv->
create_device = (PFN_D3D12_CREATE_DEVICE) D3D12CreateDevice;
560 #endif
561 return 0;
562
566 }
567
569 {
572
574
577
580 }
581
583 {
585
588 if (
ctx->lock_ctx == INVALID_HANDLE_VALUE) {
591 }
594 }
595
596 if (!
ctx->video_device)
597 DX_CHECK(ID3D12Device_QueryInterface(
ctx->device, &IID_ID3D12VideoDevice, (
void **)&
ctx->video_device));
598
599 return 0;
600
603 }
604
606 {
608
610
612 CloseHandle(device_hwctx->
lock_ctx);
613 device_hwctx->
lock_ctx = INVALID_HANDLE_VALUE;
615 }
616 }
617
620 {
623
624 HRESULT hr;
625 UINT create_flags = 0;
626 IDXGIAdapter *pAdapter =
NULL;
627
630
632
636
637 if (is_debug) {
638 ID3D12Debug *pDebug;
640 create_flags |= DXGI_CREATE_FACTORY_DEBUG;
641 ID3D12Debug_EnableDebugLayer(pDebug);
644 }
645 }
646
648 IDXGIFactory2 *pDXGIFactory =
NULL;
649
651 if (SUCCEEDED(hr)) {
652 int adapter = device ? atoi(device) : 0;
653 if (FAILED(IDXGIFactory2_EnumAdapters(pDXGIFactory, adapter, &pAdapter)))
655 IDXGIFactory2_Release(pDXGIFactory);
656 }
657
658 if (pAdapter) {
659 DXGI_ADAPTER_DESC
desc;
660 hr = IDXGIAdapter2_GetDesc(pAdapter, &
desc);
661 if (!FAILED(hr)) {
664 }
665 }
666
667 hr = priv->
create_device((IUnknown *)pAdapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (
void **)&
ctx->device);
669 if (FAILED(hr)) {
672 }
673 }
674
675 return 0;
676 }
677
680 .name = "D3D12VA",
681
684
695
697 };