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>
This commit is contained in:
Steven Xiao committed 2026-11-02 12:35:20 +00:00
1 parent b034eb5e7e
commit 3eaf97e12c
1 file changed
+122 -92
+122 -92
View File
@@ -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);
for (UINT i = 0; i < s->plane_count; i++)
linesizes[i] = s->plane_layouts[i].Footprint.RowPitch;
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],
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,
},
},
};
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],
},
},
};
};
}
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, s->plane_count, barriers);
ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier);
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_y_location, 0, 0, 0,
&texture_y_location, NULL);
ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list,
&staging_location, 0, 0, 0,
&texture_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);
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_ResourceBarrier(s->command_list, s->plane_count, barriers);
ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list,
&texture_y_location, 0, 0, 0,
&staging_y_location, NULL);
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_uv_location, 0, 0, 0,
&staging_uv_location, NULL);
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));