mirror of
https://github.com/librempeg/librempeg
synced 2024-11-22 18:49:58 +00:00
a1a1d49355
There are lots of files that don't need it: The number of object files that actually need it went down from 2011 to 884 here. Keep it for external users in order to not cause breakages. Also improve the other headers a bit while just at it. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com> Signed-off-by: Paul B Mahol <onemda@gmail.com>
699 lines
24 KiB
C
699 lines
24 KiB
C
/*
|
|
* 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 "mem.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 },
|
|
};
|