/* * Direct3D 12 HW acceleration. * * copyright (c) 2022-2023 Wu Jianhua * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "common.h" #include "hwcontext.h" #include "hwcontext_internal.h" #include "hwcontext_d3d12va_internal.h" #include "hwcontext_d3d12va.h" #include "imgutils.h" #include "mem.h" #include "pixdesc.h" #include "pixfmt.h" #include "thread.h" #include "compat/w32dlfcn.h" #include typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY2)(UINT Flags, REFIID riid, void **ppFactory); typedef struct D3D12VAFramesContext { /** * The public AVD3D12VAFramesContext. See hwcontext_d3d12va.h for it. */ AVD3D12VAFramesContext p; ID3D12Resource *staging_download_buffer; ID3D12Resource *staging_upload_buffer; ID3D12CommandQueue *command_queue; ID3D12CommandAllocator *command_allocator; ID3D12GraphicsCommandList *command_list; AVD3D12VASyncContext sync_ctx; UINT luma_component_size; } D3D12VAFramesContext; typedef struct D3D12VADevicePriv { /** * The public AVD3D12VADeviceContext. See hwcontext_d3d12va.h for it. */ AVD3D12VADeviceContext p; HANDLE d3d12lib; HANDLE dxgilib; PFN_CREATE_DXGI_FACTORY2 create_dxgi_factory2; PFN_D3D12_CREATE_DEVICE create_device; PFN_D3D12_GET_DEBUG_INTERFACE get_debug_interface; } D3D12VADevicePriv; static const struct { DXGI_FORMAT d3d_format; enum AVPixelFormat pix_fmt; } supported_formats[] = { { DXGI_FORMAT_NV12, AV_PIX_FMT_NV12 }, { DXGI_FORMAT_P010, AV_PIX_FMT_P010 }, }; static void d3d12va_default_lock(void *ctx) { WaitForSingleObjectEx(ctx, INFINITE, FALSE); } static void d3d12va_default_unlock(void *ctx) { ReleaseMutex(ctx); } static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx) { uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence); if (completion < psync_ctx->fence_value) { if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, psync_ctx->fence_value, psync_ctx->event))) return AVERROR(EINVAL); WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE); } return 0; } static inline int d3d12va_wait_queue_idle(AVD3D12VASyncContext *psync_ctx, ID3D12CommandQueue *command_queue) { DX_CHECK(ID3D12CommandQueue_Signal(command_queue, psync_ctx->fence, ++psync_ctx->fence_value)); return d3d12va_fence_completion(psync_ctx); fail: return AVERROR(EINVAL); } static int d3d12va_create_staging_buffer_resource(AVHWFramesContext *ctx, D3D12_RESOURCE_STATES states, ID3D12Resource **ppResource, int download) { AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx; D3D12VAFramesContext *s = ctx->hwctx; D3D12_HEAP_PROPERTIES props = { .Type = download ? D3D12_HEAP_TYPE_READBACK : D3D12_HEAP_TYPE_UPLOAD }; D3D12_RESOURCE_DESC desc = { .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, .Alignment = 0, .Width = s->luma_component_size + (s->luma_component_size >> 1), .Height = 1, .DepthOrArraySize = 1, .MipLevels = 1, .Format = DXGI_FORMAT_UNKNOWN, .SampleDesc = { .Count = 1, .Quality = 0 }, .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, .Flags = D3D12_RESOURCE_FLAG_NONE, }; if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->device, &props, D3D12_HEAP_FLAG_NONE, &desc, states, NULL, &IID_ID3D12Resource, (void **)ppResource))) { av_log(ctx, AV_LOG_ERROR, "Could not create the staging buffer resource\n"); return AVERROR_UNKNOWN; } return 0; } static int d3d12va_create_helper_objects(AVHWFramesContext *ctx) { AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx; D3D12VAFramesContext *s = ctx->hwctx; AVD3D12VAFramesContext *frames_hwctx = &s->p; D3D12_COMMAND_QUEUE_DESC queue_desc = { .Type = D3D12_COMMAND_LIST_TYPE_COPY, .Priority = 0, .NodeMask = 0, }; s->luma_component_size = FFALIGN(ctx->width * (frames_hwctx->format == DXGI_FORMAT_P010 ? 2 : 1), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * ctx->height; DX_CHECK(ID3D12Device_CreateFence(device_hwctx->device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&s->sync_ctx.fence)); s->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!s->sync_ctx.event) goto fail; DX_CHECK(ID3D12Device_CreateCommandQueue(device_hwctx->device, &queue_desc, &IID_ID3D12CommandQueue, (void **)&s->command_queue)); DX_CHECK(ID3D12Device_CreateCommandAllocator(device_hwctx->device, queue_desc.Type, &IID_ID3D12CommandAllocator, (void **)&s->command_allocator)); DX_CHECK(ID3D12Device_CreateCommandList(device_hwctx->device, 0, queue_desc.Type, s->command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&s->command_list)); DX_CHECK(ID3D12GraphicsCommandList_Close(s->command_list)); ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, (ID3D12CommandList **)&s->command_list); return d3d12va_wait_queue_idle(&s->sync_ctx, s->command_queue); fail: return AVERROR(EINVAL); } static void d3d12va_frames_uninit(AVHWFramesContext *ctx) { D3D12VAFramesContext *s = ctx->hwctx; D3D12_OBJECT_RELEASE(s->sync_ctx.fence); if (s->sync_ctx.event) CloseHandle(s->sync_ctx.event); D3D12_OBJECT_RELEASE(s->staging_download_buffer); D3D12_OBJECT_RELEASE(s->staging_upload_buffer); D3D12_OBJECT_RELEASE(s->command_allocator); D3D12_OBJECT_RELEASE(s->command_list); D3D12_OBJECT_RELEASE(s->command_queue); } static int d3d12va_frames_get_constraints(AVHWDeviceContext *ctx, const void *hwconfig, AVHWFramesConstraints *constraints) { HRESULT hr; int nb_sw_formats = 0; AVD3D12VADeviceContext *device_hwctx = ctx->hwctx; constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1, sizeof(*constraints->valid_sw_formats)); if (!constraints->valid_sw_formats) return AVERROR(ENOMEM); for (int i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support = { supported_formats[i].d3d_format }; hr = ID3D12Device_CheckFeatureSupport(device_hwctx->device, D3D12_FEATURE_FORMAT_SUPPORT, &format_support, sizeof(format_support)); if (SUCCEEDED(hr) && (format_support.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)) constraints->valid_sw_formats[nb_sw_formats++] = supported_formats[i].pix_fmt; } constraints->valid_sw_formats[nb_sw_formats] = AV_PIX_FMT_NONE; constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats)); if (!constraints->valid_hw_formats) return AVERROR(ENOMEM); constraints->valid_hw_formats[0] = AV_PIX_FMT_D3D12; constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; return 0; } static void free_texture(void *opaque, uint8_t *data) { AVD3D12VAFrame *frame = (AVD3D12VAFrame *)data; D3D12_OBJECT_RELEASE(frame->texture); D3D12_OBJECT_RELEASE(frame->sync_ctx.fence); if (frame->sync_ctx.event) CloseHandle(frame->sync_ctx.event); av_freep(&data); } static AVBufferRef *d3d12va_pool_alloc(void *opaque, size_t size) { AVHWFramesContext *ctx = (AVHWFramesContext *)opaque; AVD3D12VAFramesContext *hwctx = ctx->hwctx; AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx; AVBufferRef *buf; AVD3D12VAFrame *frame; D3D12_HEAP_PROPERTIES props = { .Type = D3D12_HEAP_TYPE_DEFAULT }; D3D12_RESOURCE_DESC desc = { .Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D, .Alignment = 0, .Width = ctx->width, .Height = ctx->height, .DepthOrArraySize = 1, .MipLevels = 1, .Format = hwctx->format, .SampleDesc = {.Count = 1, .Quality = 0 }, .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, .Flags = D3D12_RESOURCE_FLAG_NONE, }; frame = av_mallocz(sizeof(AVD3D12VAFrame)); if (!frame) return NULL; if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->device, &props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&frame->texture))) { av_log(ctx, AV_LOG_ERROR, "Could not create the texture\n"); goto fail; } DX_CHECK(ID3D12Device_CreateFence(device_hwctx->device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&frame->sync_ctx.fence)); frame->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!frame->sync_ctx.event) goto fail; buf = av_buffer_create((uint8_t *)frame, sizeof(frame), free_texture, NULL, 0); if (!buf) goto fail; return buf; fail: free_texture(NULL, (uint8_t *)frame); return NULL; } static int d3d12va_frames_init(AVHWFramesContext *ctx) { AVD3D12VAFramesContext *hwctx = ctx->hwctx; int i; for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { if (ctx->sw_format == supported_formats[i].pix_fmt) { if (hwctx->format != DXGI_FORMAT_UNKNOWN && hwctx->format != supported_formats[i].d3d_format) av_log(ctx, AV_LOG_WARNING, "Incompatible DXGI format provided by user, will be overided\n"); hwctx->format = supported_formats[i].d3d_format; break; } } if (i == FF_ARRAY_ELEMS(supported_formats)) { av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n", av_get_pix_fmt_name(ctx->sw_format)); return AVERROR(EINVAL); } ffhwframesctx(ctx)->pool_internal = av_buffer_pool_init2(sizeof(AVD3D12VAFrame), ctx, d3d12va_pool_alloc, NULL); if (!ffhwframesctx(ctx)->pool_internal) return AVERROR(ENOMEM); return 0; } static int d3d12va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame) { int ret; frame->buf[0] = av_buffer_pool_get(ctx->pool); if (!frame->buf[0]) return AVERROR(ENOMEM); ret = av_image_fill_arrays(frame->data, frame->linesize, NULL, ctx->sw_format, ctx->width, ctx->height, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); if (ret < 0) return ret; frame->data[0] = frame->buf[0]->data; frame->format = AV_PIX_FMT_D3D12; frame->width = ctx->width; frame->height = ctx->height; return 0; } static int d3d12va_transfer_get_formats(AVHWFramesContext *ctx, enum AVHWFrameTransferDirection dir, enum AVPixelFormat **formats) { enum AVPixelFormat *fmts; fmts = av_malloc_array(2, sizeof(*fmts)); if (!fmts) return AVERROR(ENOMEM); fmts[0] = ctx->sw_format; fmts[1] = AV_PIX_FMT_NONE; *formats = fmts; return 0; } static int d3d12va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src) { AVD3D12VADeviceContext *hwctx = ctx->device_ctx->hwctx; D3D12VAFramesContext *s = ctx->hwctx; AVD3D12VAFramesContext *frames_hwctx = &s->p; int ret; int download = src->format == AV_PIX_FMT_D3D12; const AVFrame *frame = download ? src : dst; const AVFrame *other = download ? dst : src; AVD3D12VAFrame *f = (AVD3D12VAFrame *)frame->data[0]; ID3D12Resource *texture = (ID3D12Resource *)f->texture; uint8_t *mapped_data; uint8_t *data[4]; int linesizes[4]; D3D12_TEXTURE_COPY_LOCATION staging_y_location = { 0 }; D3D12_TEXTURE_COPY_LOCATION staging_uv_location = { 0 }; D3D12_TEXTURE_COPY_LOCATION texture_y_location = { .pResource = texture, .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, .SubresourceIndex = 0, }; D3D12_TEXTURE_COPY_LOCATION texture_uv_location = { .pResource = texture, .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, .SubresourceIndex = 1, }; D3D12_RESOURCE_BARRIER barrier = { .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, .Transition = { .pResource = texture, .StateBefore = D3D12_RESOURCE_STATE_COMMON, .StateAfter = download ? D3D12_RESOURCE_STATE_COPY_SOURCE : D3D12_RESOURCE_STATE_COPY_DEST, .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, }, }; if (frame->hw_frames_ctx->data != (uint8_t *)ctx || other->format != ctx->sw_format) return AVERROR(EINVAL); hwctx->lock(hwctx->lock_ctx); if (!s->command_queue) { ret = d3d12va_create_helper_objects(ctx); if (ret < 0) goto fail; } for (int i = 0; i < 4; i++) linesizes[i] = FFALIGN(frame->width * (frames_hwctx->format == DXGI_FORMAT_P010 ? 2 : 1), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); staging_y_location = (D3D12_TEXTURE_COPY_LOCATION) { .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, .PlacedFootprint = { .Offset = 0, .Footprint = { .Format = frames_hwctx->format == DXGI_FORMAT_P010 ? DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_R8_UNORM, .Width = ctx->width, .Height = ctx->height, .Depth = 1, .RowPitch = linesizes[0], }, }, }; staging_uv_location = (D3D12_TEXTURE_COPY_LOCATION) { .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, .PlacedFootprint = { .Offset = s->luma_component_size, .Footprint = { .Format = frames_hwctx->format == DXGI_FORMAT_P010 ? DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R8G8_UNORM, .Width = ctx->width >> 1, .Height = ctx->height >> 1, .Depth = 1, .RowPitch = linesizes[0], }, }, }; DX_CHECK(ID3D12CommandAllocator_Reset(s->command_allocator)); DX_CHECK(ID3D12GraphicsCommandList_Reset(s->command_list, s->command_allocator, NULL)); if (download) { if (!s->staging_download_buffer) { ret = d3d12va_create_staging_buffer_resource(ctx, D3D12_RESOURCE_STATE_COPY_DEST, &s->staging_download_buffer, 1); if (ret < 0) { goto fail; } } staging_y_location.pResource = staging_uv_location.pResource = s->staging_download_buffer; ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier); ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list, &staging_y_location, 0, 0, 0, &texture_y_location, NULL); ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list, &staging_uv_location, 0, 0, 0, &texture_uv_location, NULL); barrier.Transition.StateBefore = barrier.Transition.StateAfter; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON; ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier); DX_CHECK(ID3D12GraphicsCommandList_Close(s->command_list)); DX_CHECK(ID3D12CommandQueue_Wait(s->command_queue, f->sync_ctx.fence, f->sync_ctx.fence_value)); ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, (ID3D12CommandList **)&s->command_list); ret = d3d12va_wait_queue_idle(&s->sync_ctx, s->command_queue); if (ret < 0) goto fail; DX_CHECK(ID3D12Resource_Map(s->staging_download_buffer, 0, NULL, (void **)&mapped_data)); av_image_fill_pointers(data, ctx->sw_format, ctx->height, mapped_data, linesizes); av_image_copy2(dst->data, dst->linesize, data, linesizes, ctx->sw_format, ctx->width, ctx->height); ID3D12Resource_Unmap(s->staging_download_buffer, 0, NULL); } else { if (!s->staging_upload_buffer) { ret = d3d12va_create_staging_buffer_resource(ctx, D3D12_RESOURCE_STATE_GENERIC_READ, &s->staging_upload_buffer, 0); if (ret < 0) { goto fail; } } staging_y_location.pResource = staging_uv_location.pResource = s->staging_upload_buffer; DX_CHECK(ID3D12Resource_Map(s->staging_upload_buffer, 0, NULL, (void **)&mapped_data)); av_image_fill_pointers(data, ctx->sw_format, ctx->height, mapped_data, linesizes); av_image_copy2(data, linesizes, src->data, src->linesize, ctx->sw_format, ctx->width, ctx->height); ID3D12Resource_Unmap(s->staging_upload_buffer, 0, NULL); ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier); ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list, &texture_y_location, 0, 0, 0, &staging_y_location, NULL); ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list, &texture_uv_location, 0, 0, 0, &staging_uv_location, NULL); barrier.Transition.StateBefore = barrier.Transition.StateAfter; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON; ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier); DX_CHECK(ID3D12GraphicsCommandList_Close(s->command_list)); ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, (ID3D12CommandList **)&s->command_list); ret = d3d12va_wait_queue_idle(&s->sync_ctx, s->command_queue); if (ret < 0) goto fail; } hwctx->unlock(hwctx->lock_ctx); return 0; fail: hwctx->unlock(hwctx->lock_ctx); return AVERROR(EINVAL); } static int d3d12va_load_functions(AVHWDeviceContext *hwdev) { D3D12VADevicePriv *priv = hwdev->hwctx; #if !HAVE_UWP priv->d3d12lib = dlopen("d3d12.dll", 0); priv->dxgilib = dlopen("dxgi.dll", 0); if (!priv->d3d12lib || !priv->dxgilib) goto fail; priv->create_device = (PFN_D3D12_CREATE_DEVICE)GetProcAddress(priv->d3d12lib, "D3D12CreateDevice"); if (!priv->create_device) goto fail; priv->create_dxgi_factory2 = (PFN_CREATE_DXGI_FACTORY2)GetProcAddress(priv->dxgilib, "CreateDXGIFactory2"); if (!priv->create_dxgi_factory2) goto fail; priv->get_debug_interface = (PFN_D3D12_GET_DEBUG_INTERFACE)GetProcAddress(priv->d3d12lib, "D3D12GetDebugInterface"); #else priv->create_device = (PFN_D3D12_CREATE_DEVICE) D3D12CreateDevice; priv->create_dxgi_factory2 = (PFN_CREATE_DXGI_FACTORY2) CreateDXGIFactory2; priv->get_debug_interface = (PFN_D3D12_GET_DEBUG_INTERFACE) D3D12GetDebugInterface; #endif return 0; fail: av_log(hwdev, AV_LOG_ERROR, "Failed to load D3D12 library or its functions\n"); return AVERROR_UNKNOWN; } static void d3d12va_device_free(AVHWDeviceContext *hwdev) { D3D12VADevicePriv *priv = hwdev->hwctx; AVD3D12VADeviceContext *ctx = &priv->p; D3D12_OBJECT_RELEASE(ctx->device); if (priv->d3d12lib) dlclose(priv->d3d12lib); if (priv->dxgilib) dlclose(priv->dxgilib); } static int d3d12va_device_init(AVHWDeviceContext *hwdev) { AVD3D12VADeviceContext *ctx = hwdev->hwctx; if (!ctx->lock) { ctx->lock_ctx = CreateMutex(NULL, 0, NULL); if (ctx->lock_ctx == INVALID_HANDLE_VALUE) { av_log(NULL, AV_LOG_ERROR, "Failed to create a mutex\n"); return AVERROR(EINVAL); } ctx->lock = d3d12va_default_lock; ctx->unlock = d3d12va_default_unlock; } if (!ctx->video_device) DX_CHECK(ID3D12Device_QueryInterface(ctx->device, &IID_ID3D12VideoDevice, (void **)&ctx->video_device)); return 0; fail: return AVERROR(EINVAL); } static void d3d12va_device_uninit(AVHWDeviceContext *hwdev) { AVD3D12VADeviceContext *device_hwctx = hwdev->hwctx; D3D12_OBJECT_RELEASE(device_hwctx->video_device); if (device_hwctx->lock == d3d12va_default_lock) { CloseHandle(device_hwctx->lock_ctx); device_hwctx->lock_ctx = INVALID_HANDLE_VALUE; device_hwctx->lock = NULL; } } static int d3d12va_device_create(AVHWDeviceContext *hwdev, const char *device, AVDictionary *opts, int flags) { D3D12VADevicePriv *priv = hwdev->hwctx; AVD3D12VADeviceContext *ctx = &priv->p; HRESULT hr; UINT create_flags = 0; IDXGIAdapter *pAdapter = NULL; int ret; int is_debug = !!av_dict_get(opts, "debug", NULL, 0); hwdev->free = d3d12va_device_free; ret = d3d12va_load_functions(hwdev); if (ret < 0) return ret; if (is_debug) { ID3D12Debug *pDebug; if (priv->get_debug_interface && SUCCEEDED(priv->get_debug_interface(&IID_ID3D12Debug, (void **)&pDebug))) { create_flags |= DXGI_CREATE_FACTORY_DEBUG; ID3D12Debug_EnableDebugLayer(pDebug); D3D12_OBJECT_RELEASE(pDebug); av_log(hwdev, AV_LOG_INFO, "D3D12 debug layer is enabled!\n"); } } if (!ctx->device) { IDXGIFactory2 *pDXGIFactory = NULL; hr = priv->create_dxgi_factory2(create_flags, &IID_IDXGIFactory2, (void **)&pDXGIFactory); if (SUCCEEDED(hr)) { int adapter = device ? atoi(device) : 0; if (FAILED(IDXGIFactory2_EnumAdapters(pDXGIFactory, adapter, &pAdapter))) pAdapter = NULL; IDXGIFactory2_Release(pDXGIFactory); } if (pAdapter) { DXGI_ADAPTER_DESC desc; hr = IDXGIAdapter2_GetDesc(pAdapter, &desc); if (!FAILED(hr)) { av_log(ctx, AV_LOG_INFO, "Using device %04x:%04x (%ls).\n", desc.VendorId, desc.DeviceId, desc.Description); } } hr = priv->create_device((IUnknown *)pAdapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (void **)&ctx->device); D3D12_OBJECT_RELEASE(pAdapter); if (FAILED(hr)) { av_log(ctx, AV_LOG_ERROR, "Failed to create Direct 3D 12 device (%lx)\n", (long)hr); return AVERROR_UNKNOWN; } } return 0; } const HWContextType ff_hwcontext_type_d3d12va = { .type = AV_HWDEVICE_TYPE_D3D12VA, .name = "D3D12VA", .device_hwctx_size = sizeof(D3D12VADevicePriv), .frames_hwctx_size = sizeof(D3D12VAFramesContext), .device_create = d3d12va_device_create, .device_init = d3d12va_device_init, .device_uninit = d3d12va_device_uninit, .frames_get_constraints = d3d12va_frames_get_constraints, .frames_init = d3d12va_frames_init, .frames_uninit = d3d12va_frames_uninit, .frames_get_buffer = d3d12va_get_buffer, .transfer_get_formats = d3d12va_transfer_get_formats, .transfer_data_to = d3d12va_transfer_data, .transfer_data_from = d3d12va_transfer_data, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_D3D12, AV_PIX_FMT_NONE }, };