From c2ce387385996c4e6824116931930b08cfcaecc9 Mon Sep 17 00:00:00 2001 From: Araz Iusubov Date: Mon, 11 Aug 2025 12:48:47 +0200 Subject: [PATCH] avcodec/d3d12va_encode: texture array support for HEVC This patch adds support for the texture array feature used by AMD boards in the D3D12 HEVC encoder. In texture array mode, a single texture array is shared for all reference and reconstructed pictures using different subresources. The implementation ensures compatibility and has been successfully tested on AMD, Intel, and NVIDIA GPUs. --- doc/APIchanges | 8 ++ libavcodec/d3d12va_encode.c | 168 +++++++++++++++++++++++-------- libavcodec/d3d12va_encode.h | 12 +++ libavcodec/d3d12va_encode_hevc.c | 5 +- libavutil/hwcontext_d3d12va.c | 87 +++++++++++++++- libavutil/hwcontext_d3d12va.h | 41 +++++++- 6 files changed, 271 insertions(+), 50 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index a3626d3c20..d69aaf2215 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,14 @@ The last version increases of all libraries were on 2025-03-28 API changes, most recent first: +2025-09-xx - xxxxxxxxxx - lavu 60.12.100 - hwcontext_d3d12va.h + Add support for texture array mode AVD3D12VAFrame.subresource_index, + AVD3D12VAFramesContext.texture_array + Add enum AVD3D12VAFrameFlags to define the behaviours of frame allocation. + Renanme AVD3D12VAFramesContext.flags to AVD3D12VAFramesContext.resource_flags. + Add flags to AVD3D12VAFramesContext + Add flags to AVD3D12VAFrame + 2025-09-xx - xxxxxxxxxx - lavfi 11.8.100 - buffersrc.h Add AVBufferSrcParameters.alpha_mode. diff --git a/libavcodec/d3d12va_encode.c b/libavcodec/d3d12va_encode.c index 008b0ddc2a..f34605ec41 100644 --- a/libavcodec/d3d12va_encode.c +++ b/libavcodec/d3d12va_encode.c @@ -191,7 +191,7 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, FFHWBaseEncodeContext *base_ctx = avctx->priv_data; D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12VAEncodePicture *pic = base_pic->priv; - AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames->hwctx; + AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames->hwctx; int err, i, j; HRESULT hr; char data[MAX_PARAM_BUFFER_SIZE]; @@ -201,6 +201,8 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, ID3D12VideoEncodeCommandList2 *cmd_list = ctx->command_list; D3D12_RESOURCE_BARRIER barriers[32] = { 0 }; D3D12_VIDEO_ENCODE_REFERENCE_FRAMES d3d12_refs = { 0 }; + int barriers_ref_index = 0; + D3D12_RESOURCE_BARRIER *barriers_ref = NULL; D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS input_args = { .SequenceControlDesc = { @@ -268,6 +270,8 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, av_log(avctx, AV_LOG_DEBUG, "Recon surface is %p.\n", pic->recon_surface->texture); + pic->subresource_index = ctx->is_texture_array ? pic->recon_surface->subresource_index : 0; + pic->output_buffer_ref = av_buffer_pool_get(ctx->output_buffer_pool); if (!pic->output_buffer_ref) { err = AVERROR(ENOMEM); @@ -324,11 +328,28 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, goto fail; } + if (ctx->is_texture_array) { + d3d12_refs.pSubresources = av_calloc(d3d12_refs.NumTexture2Ds, + sizeof(*d3d12_refs.pSubresources)); + if (!d3d12_refs.pSubresources) { + err = AVERROR(ENOMEM); + goto fail; + } + } + i = 0; - for (j = 0; j < base_pic->nb_refs[0]; j++) - d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic->refs[0][j]->priv)->recon_surface->texture; - for (j = 0; j < base_pic->nb_refs[1]; j++) - d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic->refs[1][j]->priv)->recon_surface->texture; + for (j = 0; j < base_pic->nb_refs[0]; j++) { + d3d12_refs.ppTexture2Ds[i] = ((D3D12VAEncodePicture *)base_pic->refs[0][j]->priv)->recon_surface->texture; + if (ctx->is_texture_array) + d3d12_refs.pSubresources[i] = ((D3D12VAEncodePicture *)base_pic->refs[0][j]->priv)->subresource_index; + i++; + } + for (j = 0; j < base_pic->nb_refs[1]; j++) { + d3d12_refs.ppTexture2Ds[i] = ((D3D12VAEncodePicture *)base_pic->refs[1][j]->priv)->recon_surface->texture; + if (ctx->is_texture_array) + d3d12_refs.pSubresources[i] = ((D3D12VAEncodePicture *)base_pic->refs[1][j]->priv)->subresource_index; + i++; + } } input_args.PictureControlDesc.IntraRefreshFrameIndex = 0; @@ -342,7 +363,7 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, output_args.Bitstream.pBuffer = pic->output_buffer; output_args.Bitstream.FrameStartOffset = pic->aligned_header_size; output_args.ReconstructedPicture.pReconstructedPicture = pic->recon_surface->texture; - output_args.ReconstructedPicture.ReconstructedPictureSubresource = 0; + output_args.ReconstructedPicture.ReconstructedPictureSubresource = ctx->is_texture_array ? pic->subresource_index : 0; output_args.EncoderOutputMetadata.pBuffer = pic->encoded_metadata; output_args.EncoderOutputMetadata.Offset = 0; @@ -368,52 +389,89 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, goto fail; } -#define TRANSITION_BARRIER(res, before, after) \ +#define TRANSITION_BARRIER(res, subres, before, after) \ (D3D12_RESOURCE_BARRIER) { \ .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, \ .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, \ .Transition = { \ .pResource = res, \ - .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, \ + .Subresource = subres, \ .StateBefore = before, \ .StateAfter = after, \ }, \ } barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); barriers[1] = TRANSITION_BARRIER(pic->output_buffer, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); - barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture, + barriers[2] = TRANSITION_BARRIER(pic->encoded_metadata, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); - barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, - D3D12_RESOURCE_STATE_COMMON, - D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); - barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata, + barriers[3] = TRANSITION_BARRIER(pic->resolved_metadata, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); - ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers); + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 4, barriers); - if (d3d12_refs.NumTexture2Ds) { - D3D12_RESOURCE_BARRIER refs_barriers[3]; + if (ctx->is_texture_array) + barriers_ref = av_calloc(base_ctx->recon_frames->initial_pool_size * ctx->plane_count, + sizeof(D3D12_RESOURCE_BARRIER)); + else + barriers_ref = av_calloc(MAX_DPB_SIZE, sizeof(D3D12_RESOURCE_BARRIER)); - for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) - refs_barriers[i] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], - D3D12_RESOURCE_STATE_COMMON, - D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); + if (ctx->is_texture_array) { + D3D12_RESOURCE_DESC references_tex_array_desc = { 0 }; + pic->recon_surface->texture->lpVtbl->GetDesc(pic->recon_surface->texture, &references_tex_array_desc); - ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, d3d12_refs.NumTexture2Ds, - refs_barriers); + for (uint32_t reference_subresource = 0; reference_subresource < references_tex_array_desc.DepthOrArraySize; + reference_subresource++) { + + uint32_t array_size = references_tex_array_desc.DepthOrArraySize; + uint32_t mip_slice = reference_subresource % references_tex_array_desc.MipLevels; + uint32_t array_slice = (reference_subresource / references_tex_array_desc.MipLevels) % array_size; + + for (uint32_t plane_slice = 0; plane_slice < ctx->plane_count; plane_slice++) { + uint32_t outputSubresource = mip_slice + array_slice * references_tex_array_desc.MipLevels + + plane_slice * references_tex_array_desc.MipLevels * array_size; + if (reference_subresource == pic->subresource_index) { + barriers_ref[barriers_ref_index++] = TRANSITION_BARRIER(pic->recon_surface->texture, outputSubresource, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); + } else { + barriers_ref[barriers_ref_index++] = TRANSITION_BARRIER(pic->recon_surface->texture, outputSubresource, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); + } + } + } + } else { + barriers_ref[barriers_ref_index++] = TRANSITION_BARRIER(pic->recon_surface->texture, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); + + if (d3d12_refs.NumTexture2Ds) { + for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) + barriers_ref[barriers_ref_index++] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); + } } + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, barriers_ref_index, barriers_ref); ID3D12VideoEncodeCommandList2_EncodeFrame(cmd_list, ctx->encoder, ctx->encoder_heap, &input_args, &output_args); barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); @@ -421,35 +479,32 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, ID3D12VideoEncodeCommandList2_ResolveEncoderOutputMetadata(cmd_list, &input_metadata, &output_metadata); - if (d3d12_refs.NumTexture2Ds) { - D3D12_RESOURCE_BARRIER refs_barriers[3]; + if (barriers_ref_index > 0) { + for (i = 0; i < barriers_ref_index; i++) + FFSWAP(D3D12_RESOURCE_STATES, barriers_ref[i].Transition.StateBefore, barriers_ref[i].Transition.StateAfter); - for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) - refs_barriers[i] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], - D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, - D3D12_RESOURCE_STATE_COMMON); - - ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, d3d12_refs.NumTexture2Ds, - refs_barriers); + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, barriers_ref_index, + barriers_ref); } barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, D3D12_RESOURCE_STATE_COMMON); barriers[1] = TRANSITION_BARRIER(pic->output_buffer, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, D3D12_RESOURCE_STATE_COMMON); - barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture, - D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, - D3D12_RESOURCE_STATE_COMMON); - barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, + barriers[2] = TRANSITION_BARRIER(pic->encoded_metadata, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, D3D12_RESOURCE_STATE_COMMON); - barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata, + barriers[3] = TRANSITION_BARRIER(pic->resolved_metadata, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, D3D12_RESOURCE_STATE_COMMON); - ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers); + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 4, barriers); hr = ID3D12VideoEncodeCommandList2_Close(cmd_list); if (FAILED(hr)) { @@ -488,6 +543,12 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, if (d3d12_refs.ppTexture2Ds) av_freep(&d3d12_refs.ppTexture2Ds); + if (ctx->is_texture_array && d3d12_refs.pSubresources) + av_freep(&d3d12_refs.pSubresources); + + if (barriers_ref) + av_freep(&barriers_ref); + return 0; fail: @@ -497,6 +558,12 @@ fail: if (d3d12_refs.ppTexture2Ds) av_freep(&d3d12_refs.ppTexture2Ds); + if (ctx->is_texture_array && d3d12_refs.pSubresources) + av_freep(&d3d12_refs.pSubresources); + + if (barriers_ref) + av_freep(&barriers_ref); + if (ctx->codec->free_picture_params) ctx->codec->free_picture_params(pic); @@ -1354,6 +1421,7 @@ fail: static int d3d12va_encode_create_recon_frames(AVCodecContext *avctx) { FFHWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; AVD3D12VAFramesContext *hwctx; enum AVPixelFormat recon_format; int err; @@ -1374,8 +1442,12 @@ static int d3d12va_encode_create_recon_frames(AVCodecContext *avctx) base_ctx->recon_frames->width = base_ctx->surface_width; base_ctx->recon_frames->height = base_ctx->surface_height; - hwctx->flags = D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | - D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + hwctx->resource_flags = D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | + D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + if (ctx->is_texture_array) { + base_ctx->recon_frames->initial_pool_size = MAX_DPB_SIZE + 1; + hwctx->flags |= AV_D3D12VA_FRAME_FLAG_TEXTURE_ARRAY; + } err = av_hwframe_ctx_init(base_ctx->recon_frames_ref); if (err < 0) { @@ -1409,6 +1481,7 @@ int ff_d3d12va_encode_init(AVCodecContext *avctx) FFHWBaseEncodeContext *base_ctx = avctx->priv_data; D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT support = { 0 }; + D3D12_FEATURE_DATA_FORMAT_INFO format_info = { 0 }; int err; HRESULT hr; @@ -1444,6 +1517,15 @@ int ff_d3d12va_encode_init(AVCodecContext *avctx) goto fail; } + format_info.Format = ((AVD3D12VAFramesContext *)base_ctx->input_frames->hwctx)->format; + if (FAILED(ID3D12VideoDevice_CheckFeatureSupport(ctx->hwctx->device, D3D12_FEATURE_FORMAT_INFO, + &format_info, sizeof(format_info)))) { + av_log(avctx, AV_LOG_ERROR, "Failed to query format plane count: 0x%x\n", hr); + err = AVERROR_EXTERNAL; + goto fail; + } + ctx->plane_count = format_info.PlaneCount; + err = d3d12va_encode_set_profile(avctx); if (err < 0) goto fail; @@ -1471,10 +1553,6 @@ int ff_d3d12va_encode_init(AVCodecContext *avctx) if (err < 0) goto fail; - err = d3d12va_encode_create_recon_frames(avctx); - if (err < 0) - goto fail; - err = d3d12va_encode_prepare_output_buffers(avctx); if (err < 0) goto fail; @@ -1500,6 +1578,10 @@ int ff_d3d12va_encode_init(AVCodecContext *avctx) goto fail; } + err = d3d12va_encode_create_recon_frames(avctx); + if (err < 0) + goto fail; + base_ctx->output_delay = base_ctx->b_per_p; base_ctx->decode_delay = base_ctx->max_b_depth; diff --git a/libavcodec/d3d12va_encode.h b/libavcodec/d3d12va_encode.h index 8af5554626..5bd1eedb7f 100644 --- a/libavcodec/d3d12va_encode.h +++ b/libavcodec/d3d12va_encode.h @@ -52,6 +52,8 @@ typedef struct D3D12VAEncodePicture { ID3D12Resource *encoded_metadata; ID3D12Resource *resolved_metadata; + int subresource_index; + D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA pic_ctl; int fence_value; @@ -194,6 +196,16 @@ typedef struct D3D12VAEncodeContext { */ AVBufferPool *output_buffer_pool; + /** + * Flag indicates if the HW is texture array mode. + */ + int is_texture_array; + + /** + * The number of planes in the input DXGI FORMAT. + */ + int plane_count; + /** * D3D12 video encoder. */ diff --git a/libavcodec/d3d12va_encode_hevc.c b/libavcodec/d3d12va_encode_hevc.c index 4f4dda1f8f..01e5b4cb4c 100644 --- a/libavcodec/d3d12va_encode_hevc.c +++ b/libavcodec/d3d12va_encode_hevc.c @@ -279,9 +279,8 @@ static int d3d12va_encode_hevc_init_sequence_params(AVCodecContext *avctx) } if (support.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) { - av_log(avctx, AV_LOG_ERROR, "D3D12 video encode on this device requires texture array support, " - "but it's not implemented.\n"); - return AVERROR_PATCHWELCOME; + ctx->is_texture_array = 1; + av_log(avctx, AV_LOG_DEBUG, "D3D12 video encode on this device uses texture array mode.\n"); } desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format); diff --git a/libavutil/hwcontext_d3d12va.c b/libavutil/hwcontext_d3d12va.c index 6507cf69c1..9368341d6d 100644 --- a/libavutil/hwcontext_d3d12va.c +++ b/libavutil/hwcontext_d3d12va.c @@ -49,6 +49,7 @@ typedef struct D3D12VAFramesContext { ID3D12GraphicsCommandList *command_list; AVD3D12VASyncContext sync_ctx; UINT luma_component_size; + int nb_surfaces_used; } D3D12VAFramesContext; typedef struct D3D12VADevicePriv { @@ -174,7 +175,8 @@ fail: static void d3d12va_frames_uninit(AVHWFramesContext *ctx) { - D3D12VAFramesContext *s = ctx->hwctx; + D3D12VAFramesContext *s = ctx->hwctx; + AVD3D12VAFramesContext *hwctx = ctx->hwctx; D3D12_OBJECT_RELEASE(s->sync_ctx.fence); if (s->sync_ctx.event) @@ -185,6 +187,9 @@ static void d3d12va_frames_uninit(AVHWFramesContext *ctx) D3D12_OBJECT_RELEASE(s->command_allocator); D3D12_OBJECT_RELEASE(s->command_list); D3D12_OBJECT_RELEASE(s->command_queue); + + if (hwctx->texture_array) + D3D12_OBJECT_RELEASE(hwctx->texture_array); } static int d3d12va_frames_get_constraints(AVHWDeviceContext *ctx, const void *hwconfig, AVHWFramesConstraints *constraints) @@ -220,7 +225,9 @@ static void free_texture(void *opaque, uint8_t *data) { AVD3D12VAFrame *frame = (AVD3D12VAFrame *)data; - D3D12_OBJECT_RELEASE(frame->texture); + if (!(frame->flags & AV_D3D12VA_FRAME_FLAG_TEXTURE_ARRAY)) + D3D12_OBJECT_RELEASE(frame->texture); + D3D12_OBJECT_RELEASE(frame->sync_ctx.fence); if (frame->sync_ctx.event) CloseHandle(frame->sync_ctx.event); @@ -228,6 +235,42 @@ static void free_texture(void *opaque, uint8_t *data) av_freep(&data); } +static AVBufferRef *d3d12va_pool_alloc_texture_array(AVHWFramesContext *ctx) +{ + AVD3D12VAFrame *frame = av_mallocz(sizeof(*frame)); + D3D12VAFramesContext *s = ctx->hwctx; + AVD3D12VAFramesContext *hwctx = ctx->hwctx; + AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx; + AVBufferRef *buf; + + frame->flags |= AV_D3D12VA_FRAME_FLAG_TEXTURE_ARRAY; + + 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; + + if (s->nb_surfaces_used >= ctx->initial_pool_size) { + av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n"); + goto fail; + } + + frame->texture = hwctx->texture_array; + frame->subresource_index = s->nb_surfaces_used; + + buf = av_buffer_create((uint8_t *)frame, sizeof(*frame), free_texture, NULL, 0); + + if (!buf) + goto fail; + + s->nb_surfaces_used++; + return buf; +fail: + free_texture(NULL, (uint8_t *)frame); + return NULL; +} + static AVBufferRef *d3d12va_pool_alloc(void *opaque, size_t size) { AVHWFramesContext *ctx = (AVHWFramesContext *)opaque; @@ -236,6 +279,10 @@ static AVBufferRef *d3d12va_pool_alloc(void *opaque, size_t size) AVBufferRef *buf; AVD3D12VAFrame *frame; + + if (ctx->initial_pool_size > 0 && hwctx->flags & AV_D3D12VA_FRAME_FLAG_TEXTURE_ARRAY) + return d3d12va_pool_alloc_texture_array(ctx); + D3D12_HEAP_PROPERTIES props = { .Type = D3D12_HEAP_TYPE_DEFAULT }; D3D12_RESOURCE_DESC desc = { .Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D, @@ -247,7 +294,7 @@ static AVBufferRef *d3d12va_pool_alloc(void *opaque, size_t size) .Format = hwctx->format, .SampleDesc = {.Count = 1, .Quality = 0 }, .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, - .Flags = hwctx->flags, + .Flags = hwctx->resource_flags, }; frame = av_mallocz(sizeof(AVD3D12VAFrame)); @@ -278,6 +325,34 @@ fail: return NULL; } +static int d3d12va_texture_array_init(AVHWFramesContext *ctx) +{ + AVD3D12VAFramesContext *hwctx = ctx->hwctx; + AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx; + + 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 = ctx->initial_pool_size, + .MipLevels = 1, + .Format = hwctx->format, + .SampleDesc = {.Count = 1, .Quality = 0 }, + .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + .Flags = hwctx->resource_flags, + }; + + if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->device, &props, D3D12_HEAP_FLAG_NONE, &desc, + D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&hwctx->texture_array))) { + av_log(ctx, AV_LOG_ERROR, "Could not create the texture array\n"); + return AVERROR(EINVAL); + } + return 0; +} + static int d3d12va_frames_init(AVHWFramesContext *ctx) { AVD3D12VAFramesContext *hwctx = ctx->hwctx; @@ -298,6 +373,12 @@ static int d3d12va_frames_init(AVHWFramesContext *ctx) return AVERROR(EINVAL); } + if (ctx->initial_pool_size > 0 && hwctx->flags & AV_D3D12VA_FRAME_FLAG_TEXTURE_ARRAY) { + int err = d3d12va_texture_array_init(ctx); + if (err < 0) + return err; + } + ffhwframesctx(ctx)->pool_internal = av_buffer_pool_init2(sizeof(AVD3D12VAFrame), ctx, d3d12va_pool_alloc, NULL); diff --git a/libavutil/hwcontext_d3d12va.h b/libavutil/hwcontext_d3d12va.h index 212a6a6146..1530de8b3c 100644 --- a/libavutil/hwcontext_d3d12va.h +++ b/libavutil/hwcontext_d3d12va.h @@ -99,6 +99,18 @@ typedef struct AVD3D12VASyncContext { uint64_t fence_value; } AVD3D12VASyncContext; +/** + * Define the behaviours of frame allocation. + */ +typedef enum AVD3D12VAFrameFlags { + AV_D3D12VA_FRAME_FLAG_NONE = 0, + + /** + * Indicates that frame data should be allocated using a texture array resource. + */ + AV_D3D12VA_FRAME_FLAG_TEXTURE_ARRAY = (1 << 1), +} AVD3D12VAFrameFlags; + /** * @brief D3D12VA frame descriptor for pool allocation. * @@ -111,12 +123,26 @@ typedef struct AVD3D12VAFrame { */ ID3D12Resource *texture; + /** + * Index of the subresource within the texture. + * + * In texture array mode, this specifies the array slice index. + * When texture array mode is not used, this value is always 0. + */ + int subresource_index; + /** * The sync context for the texture * * @see: https://learn.microsoft.com/en-us/windows/win32/medfound/direct3d-12-video-overview#directx-12-fences */ AVD3D12VASyncContext sync_ctx; + + /** + * A combination of AVD3D12VAFrameFlags. + * Set by AVD3D12VAFramesContext. + */ + AVD3D12VAFrameFlags flags; } AVD3D12VAFrame; /** @@ -136,7 +162,20 @@ typedef struct AVD3D12VAFramesContext { * * @see https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_resource_flags */ - D3D12_RESOURCE_FLAGS flags; + D3D12_RESOURCE_FLAGS resource_flags; + + /** + * In texture array mode, the D3D12 uses the same texture array (resource)for all + * pictures. + */ + ID3D12Resource *texture_array; + + /** + * A combination of AVD3D12VAFrameFlags. Unless AV_D3D12VA_FRAME_FLAG_NONE is set, + * autodetected flags will be OR'd based on the device and frame features during + * av_hwframe_ctx_init(). + */ + AVD3D12VAFrameFlags flags; } AVD3D12VAFramesContext; #endif /* AVUTIL_HWCONTEXT_D3D12VA_H */