Commit 3eaf97e12c for ffmpeg
commit 3eaf97e12c7e25c22547e78f2c8d06a171b74d83
Author: Steven Xiao <steven.xiao@amd.com>
Date: Thu Oct 29 07:36:27 2026 -0400
avutil/hwcontext_d3d12va: support RGB formats and multi-plane transfers
Generalize the staging-buffer transfer path to use
ID3D12Device::GetCopyableFootprints() and the format's actual plane
count instead of hardcoding a two-plane NV12/P010/P012 layout. This
makes d3d12va_transfer_data() correct for single-plane packed RGB
formats as well as the existing semi-planar YUV ones.
Add the packed RGB formats bgra, rgba, x2bgr10 and rgbaf16 to
supported_formats, so hwupload/hwdownload and other libavutil
consumers can allocate and transfer d3d12 frames in those formats.
Signed-off-by: younengxiao <steven.xiao@amd.com>
diff --git a/libavutil/hwcontext_d3d12va.c b/libavutil/hwcontext_d3d12va.c
index f9d1721cd6..e7dc733bac 100644
--- a/libavutil/hwcontext_d3d12va.c
+++ b/libavutil/hwcontext_d3d12va.c
@@ -48,7 +48,13 @@ typedef struct D3D12VAFramesContext {
ID3D12CommandAllocator *command_allocator;
ID3D12GraphicsCommandList *command_list;
AVD3D12VASyncContext sync_ctx;
- UINT luma_component_size;
+
+ /* Per-plane copy layout for the staging buffer, computed once per
+ * frames context from ID3D12Device::GetCopyableFootprints(). */
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT plane_layouts[4];
+ UINT plane_count;
+ UINT64 staging_buffer_size;
+
int nb_surfaces_used;
} D3D12VAFramesContext;
@@ -68,9 +74,13 @@ 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 },
- { DXGI_FORMAT_P016, AV_PIX_FMT_P012 },
+ { DXGI_FORMAT_NV12, AV_PIX_FMT_NV12 },
+ { DXGI_FORMAT_P010, AV_PIX_FMT_P010 },
+ { DXGI_FORMAT_P016, AV_PIX_FMT_P012 },
+ { DXGI_FORMAT_B8G8R8A8_UNORM, AV_PIX_FMT_BGRA },
+ { DXGI_FORMAT_R8G8B8A8_UNORM, AV_PIX_FMT_RGBA },
+ { DXGI_FORMAT_R10G10B10A2_UNORM, AV_PIX_FMT_X2BGR10 },
+ { DXGI_FORMAT_R16G16B16A16_FLOAT, AV_PIX_FMT_RGBAF16 },
};
static void d3d12va_default_lock(void *ctx)
@@ -114,7 +124,7 @@ static int d3d12va_create_staging_buffer_resource(AVHWFramesContext *ctx, D3D12_
D3D12_RESOURCE_DESC desc = {
.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
.Alignment = 0,
- .Width = s->luma_component_size + (s->luma_component_size >> 1),
+ .Width = s->staging_buffer_size,
.Height = 1,
.DepthOrArraySize = 1,
.MipLevels = 1,
@@ -133,11 +143,48 @@ static int d3d12va_create_staging_buffer_resource(AVHWFramesContext *ctx, D3D12_
return 0;
}
-static int d3d12va_create_helper_objects(AVHWFramesContext *ctx)
+static int d3d12va_get_plane_layouts(AVHWFramesContext *ctx)
{
AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
D3D12VAFramesContext *s = ctx->hwctx;
AVD3D12VAFramesContext *frames_hwctx = &s->p;
+ D3D12_FEATURE_DATA_FORMAT_INFO format_info = { .Format = frames_hwctx->format };
+ HRESULT hr;
+
+ D3D12_RESOURCE_DESC desc = {
+ .Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D,
+ .Alignment = 0,
+ .Width = ctx->width,
+ .Height = ctx->height,
+ .DepthOrArraySize = 1,
+ .MipLevels = 1,
+ .Format = frames_hwctx->format,
+ .SampleDesc = { .Count = 1, .Quality = 0 },
+ .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
+ .Flags = D3D12_RESOURCE_FLAG_NONE,
+ };
+
+ hr = ID3D12Device_CheckFeatureSupport(device_hwctx->device, D3D12_FEATURE_FORMAT_INFO,
+ &format_info, sizeof(format_info));
+ if (FAILED(hr) || !format_info.PlaneCount ||
+ format_info.PlaneCount > FF_ARRAY_ELEMS(s->plane_layouts)) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to query plane count for DXGI format %d\n", frames_hwctx->format);
+ return AVERROR_EXTERNAL;
+ }
+
+ s->plane_count = format_info.PlaneCount;
+
+ ID3D12Device_GetCopyableFootprints(device_hwctx->device, &desc, 0, s->plane_count, 0,
+ s->plane_layouts, NULL, NULL, &s->staging_buffer_size);
+
+ return 0;
+}
+
+static int d3d12va_create_helper_objects(AVHWFramesContext *ctx)
+{
+ AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
+ D3D12VAFramesContext *s = ctx->hwctx;
+ int ret;
D3D12_COMMAND_QUEUE_DESC queue_desc = {
.Type = D3D12_COMMAND_LIST_TYPE_COPY,
@@ -145,8 +192,9 @@ static int d3d12va_create_helper_objects(AVHWFramesContext *ctx)
.NodeMask = 0,
};
- s->luma_component_size = FFALIGN(ctx->width * (frames_hwctx->format != DXGI_FORMAT_NV12 ? 2 : 1),
- D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * ctx->height;
+ ret = d3d12va_get_plane_layouts(ctx);
+ if (ret < 0)
+ return ret;
DX_CHECK(ID3D12Device_CreateFence(device_hwctx->device, 0, D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence, (void **)&s->sync_ctx.fence));
@@ -439,7 +487,6 @@ static int d3d12va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
{
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;
@@ -448,36 +495,17 @@ static int d3d12va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
AVD3D12VAFrame *f = (AVD3D12VAFrame *)frame->data[0];
ID3D12Resource *texture = (ID3D12Resource *)f->texture;
+ D3D12_RESOURCE_DESC texture_desc;
+ UINT array_size;
uint8_t *mapped_data;
- uint8_t *data[4];
- int linesizes[4];
+ uint8_t *data[4] = { NULL };
+ int linesizes[4] = { 0 };
- D3D12_TEXTURE_COPY_LOCATION staging_y_location = { 0 };
- D3D12_TEXTURE_COPY_LOCATION staging_uv_location = { 0 };
+ texture->lpVtbl->GetDesc(texture, &texture_desc);
+ array_size = texture_desc.DepthOrArraySize;
- 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,
- },
- };
+ D3D12_RESOURCE_BARRIER barriers[4];
if (frame->hw_frames_ctx->data != (uint8_t *)ctx || other->format != ctx->sw_format)
return AVERROR(EINVAL);
@@ -490,39 +518,21 @@ static int d3d12va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
goto fail;
}
- for (int i = 0; i < 4; i++)
- linesizes[i] = FFALIGN(frame->width * (frames_hwctx->format != DXGI_FORMAT_NV12 ? 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_NV12 ?
- 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_NV12 ?
- DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R8G8_UNORM,
- .Width = ctx->width >> 1,
- .Height = ctx->height >> 1,
- .Depth = 1,
- .RowPitch = linesizes[0],
+ for (UINT i = 0; i < s->plane_count; i++)
+ linesizes[i] = s->plane_layouts[i].Footprint.RowPitch;
+
+ for (UINT i = 0; i < s->plane_count; i++) {
+ barriers[i] = (D3D12_RESOURCE_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 = f->subresource_index + i * array_size,
},
- },
- };
+ };
+ }
DX_CHECK(ID3D12CommandAllocator_Reset(s->command_allocator));
@@ -537,21 +547,30 @@ static int d3d12va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
}
}
- 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);
+ ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, s->plane_count, barriers);
+
+ for (UINT i = 0; i < s->plane_count; i++) {
+ D3D12_TEXTURE_COPY_LOCATION staging_location = {
+ .pResource = s->staging_download_buffer,
+ .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
+ .PlacedFootprint = s->plane_layouts[i],
+ };
+ D3D12_TEXTURE_COPY_LOCATION texture_location = {
+ .pResource = texture,
+ .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
+ .SubresourceIndex = f->subresource_index + i * array_size,
+ };
+
+ ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list,
+ &staging_location, 0, 0, 0,
+ &texture_location, NULL);
+ }
- barrier.Transition.StateBefore = barrier.Transition.StateAfter;
- barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
- ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier);
+ for (UINT i = 0; i < s->plane_count; i++) {
+ barriers[i].Transition.StateBefore = barriers[i].Transition.StateAfter;
+ barriers[i].Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
+ }
+ ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, s->plane_count, barriers);
DX_CHECK(ID3D12GraphicsCommandList_Close(s->command_list));
@@ -564,7 +583,8 @@ static int d3d12va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
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);
+ for (UINT i = 0; i < s->plane_count; i++)
+ data[i] = mapped_data + s->plane_layouts[i].Offset;
av_image_copy2(dst->data, dst->linesize, data, linesizes,
ctx->sw_format, ctx->width, ctx->height);
@@ -579,29 +599,39 @@ static int d3d12va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
}
}
- 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);
+ for (UINT i = 0; i < s->plane_count; i++)
+ data[i] = mapped_data + s->plane_layouts[i].Offset;
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);
+ ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, s->plane_count, barriers);
+
+ for (UINT i = 0; i < s->plane_count; i++) {
+ D3D12_TEXTURE_COPY_LOCATION staging_location = {
+ .pResource = s->staging_upload_buffer,
+ .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
+ .PlacedFootprint = s->plane_layouts[i],
+ };
+ D3D12_TEXTURE_COPY_LOCATION texture_location = {
+ .pResource = texture,
+ .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
+ .SubresourceIndex = f->subresource_index + i * array_size,
+ };
+
+ ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list,
+ &texture_location, 0, 0, 0,
+ &staging_location, NULL);
+ }
- barrier.Transition.StateBefore = barrier.Transition.StateAfter;
- barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
- ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier);
+ for (UINT i = 0; i < s->plane_count; i++) {
+ barriers[i].Transition.StateBefore = barriers[i].Transition.StateAfter;
+ barriers[i].Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
+ }
+ ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, s->plane_count, barriers);
DX_CHECK(ID3D12GraphicsCommandList_Close(s->command_list));