This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 27afe04c3d2c119629517aa8ac3a4476b1c76fd9 Author: Julius Bairaktaris <[email protected]> AuthorDate: Sun Aug 30 16:04:10 2026 +0200 Commit: ArazIusubov <[email protected]> CommitDate: Wed Sep 16 22:05:28 2026 +0000 avfilter/amf: copy D3D11 decoder textures the components can read The AMF filter components read their input with a shader and reject a texture created without D3D11_BIND_SHADER_RESOURCE, which is what a D3D11VA decoder pool provides. Every AMF filter therefore fails on ffmpeg -hwaccel d3d11va -hwaccel_output_format d3d11 \ -i in.mp4 -vf vpp_amf ... with AMF_DIRECTX_FAILED from the first QueryOutput(), while frc_amf and the AMF encoders consume the same surfaces. The only way out was the device option SHADER=1, which the usual command lines do not set. Copy the slice into an AMF allocated surface, which carries the flags the components need. CopySubresourceRegion() runs on the copy engine, so it can read a decoder texture that a shader cannot. AMFSurface::Duplicate(), suggested upstream for this, returns AMF_OK but yields a surface that fails in exactly the same way, for AMF_MEMORY_DX11 and AMF_MEMORY_HOST alike. Only a frame whose texture lacks the flag is copied, so a pool created with SHADER=1 keeps the zero copy path, and only the components that need a shader readable input ask for the copy. The frame rate converter reads the decoder texture as it is, so it is left on the zero copy path through a flag on the shared filter context. Tested on Windows 11, RX 9070 XT, driver 32.0.31041.1004, with -hwaccel d3d11va -hwaccel_output_format d3d11: vpp_amf, vqe_amf and every sr_amf algorithm produce output matching the plain decode where they previously failed, and frc_amf is unchanged. Refs https://github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/605 Assisted-by: Claude Opus 5 Signed-off-by: Julius Bairaktaris <[email protected]> --- libavfilter/vf_amf_common.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ libavfilter/vf_amf_common.h | 1 + libavfilter/vf_vqe_amf.c | 1 + 3 files changed, 68 insertions(+) diff --git a/libavfilter/vf_amf_common.c b/libavfilter/vf_amf_common.c index 0d2a2f6257..2d70d6d954 100644 --- a/libavfilter/vf_amf_common.c +++ b/libavfilter/vf_amf_common.c @@ -38,6 +38,7 @@ #if CONFIG_D3D11VA #include <d3d11.h> +#include "libavutil/hwcontext_d3d11va.h" #endif int amf_filter_init(AVFilterContext *avctx) @@ -54,6 +55,7 @@ int amf_filter_init(AVFilterContext *avctx) } } ctx->format_opt = ctx->format; + ctx->shader_input = 1; return 0; } @@ -517,6 +519,58 @@ fail: return NULL; } +#if CONFIG_D3D11VA +/* The AMF filter components read their input with a shader, so they reject a + * texture created without D3D11_BIND_SHADER_RESOURCE, which is what a D3D11VA + * decoder pool gives us. Copy the slice into an AMF allocated surface, which + * carries the flags the components need. CopySubresourceRegion() uses the copy + * engine, so it can read the decoder texture that a shader cannot. */ +static int amf_copy_d3d11_texture(AVFilterContext *avctx, const AVFrame *frame, + int index, AMFSurface **ppSurface) +{ + AMFFilterContext *ctx = avctx->priv; + AVHWFramesContext *frames = (AVHWFramesContext*)frame->hw_frames_ctx->data; + AVD3D11VADeviceContext *hwctx = frames->device_ctx->hwctx; + ID3D11Texture2D *texture = (ID3D11Texture2D*)frame->data[0]; + AMFSurface *surface = NULL; + AMFPlane *plane; + D3D11_TEXTURE2D_DESC desc; + D3D11_BOX box; + AMF_RESULT res; + + res = ctx->amf_device_ctx->context->pVtbl->AllocSurface(ctx->amf_device_ctx->context, + AMF_MEMORY_DX11, av_av_to_amf_format(frames->sw_format), + frame->width, frame->height, &surface); + AMF_RETURN_IF_FALSE(avctx, res == AMF_OK, AVERROR(ENOMEM), "AllocSurface() failed with error %d\n", res); + + plane = surface->pVtbl->GetPlaneAt(surface, 0); + if (!plane) { + surface->pVtbl->Release(surface); + return AVERROR(ENOMEM); + } + + // The decoder pool is allocated with aligned dimensions, so copy the coded + // area rather than the whole source subresource. D3D11 wants even bounds + // for a planar format, and the source is at least that large. + texture->lpVtbl->GetDesc(texture, &desc); + box.left = 0; + box.top = 0; + box.front = 0; + box.right = FFMIN(FFALIGN(frame->width, 2), desc.Width); + box.bottom = FFMIN(FFALIGN(frame->height, 2), desc.Height); + box.back = 1; + + hwctx->lock(hwctx->lock_ctx); + hwctx->device_context->lpVtbl->CopySubresourceRegion(hwctx->device_context, + (ID3D11Resource*)plane->pVtbl->GetNative(plane), 0, 0, 0, 0, + (ID3D11Resource*)texture, index, &box); + hwctx->unlock(hwctx->lock_ctx); + + *ppSurface = surface; + return 0; +} +#endif + int amf_avframe_to_amfsurface(AVFilterContext *avctx, const AVFrame *frame, AMFSurface** ppSurface) { AMFVariantStruct var = { 0 }; @@ -533,6 +587,18 @@ int amf_avframe_to_amfsurface(AVFilterContext *avctx, const AVFrame *frame, AMFS static const GUID AMFTextureArrayIndexGUID = { 0x28115527, 0xe7c3, 0x4b66, { 0x99, 0xd3, 0x4f, 0x2a, 0xe6, 0xb4, 0x7f, 0xaf } }; ID3D11Texture2D *texture = (ID3D11Texture2D*)frame->data[0]; // actual texture int index = (intptr_t)frame->data[1]; // index is a slice in texture array is - set to tell AMF which slice to use + D3D11_TEXTURE2D_DESC desc; + int ret; + + texture->lpVtbl->GetDesc(texture, &desc); + if (ctx->shader_input && !(desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) && frame->hw_frames_ctx) { + ret = amf_copy_d3d11_texture(avctx, frame, index, &surface); + if (ret < 0) + return ret; + hw_surface = 1; + break; + } + texture->lpVtbl->SetPrivateData(texture, &AMFTextureArrayIndexGUID, sizeof(index), &index); res = ctx->amf_device_ctx->context->pVtbl->CreateSurfaceFromDX11Native(ctx->amf_device_ctx->context, texture, &surface, NULL); // wrap to AMF surface diff --git a/libavfilter/vf_amf_common.h b/libavfilter/vf_amf_common.h index 0d323dd480..eedaef7ba6 100644 --- a/libavfilter/vf_amf_common.h +++ b/libavfilter/vf_amf_common.h @@ -68,6 +68,7 @@ typedef struct AMFFilterContext { AVAMFDeviceContext *amf_device_ctx; int local_context; + int shader_input; } AMFFilterContext; int amf_filter_init(AVFilterContext *avctx); diff --git a/libavfilter/vf_vqe_amf.c b/libavfilter/vf_vqe_amf.c index d368770c89..63febf5c15 100644 --- a/libavfilter/vf_vqe_amf.c +++ b/libavfilter/vf_vqe_amf.c @@ -55,6 +55,7 @@ static int amf_vqe_init(AVFilterContext *avctx) { AMFVQEFilterContext *ctx = avctx->priv; ctx->common.format = AV_PIX_FMT_NONE; + ctx->common.shader_input = 1; return 0; } -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
