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]

Reply via email to