| /* | |
| * Direct3D 12 HW acceleration. | |
| * | |
| * copyright (c) 2022-2023 Wu Jianhua <toqsxw@outlook.com> | |
| * | |
| * 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 "pixdesc.h" | |
| #include "pixfmt.h" | |
| #include "thread.h" | |
| #include "compat/w32dlfcn.h" | |
| #include <dxgi1_3.h> | |
| 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 }, | |
| }; |