diff --git a/debian/patches/0010-add-a-hack-for-opencl-reverse-mapping.patch b/debian/patches/0010-add-a-hack-for-opencl-reverse-mapping.patch index 202887b87..b6350cb34 100644 --- a/debian/patches/0010-add-a-hack-for-opencl-reverse-mapping.patch +++ b/debian/patches/0010-add-a-hack-for-opencl-reverse-mapping.patch @@ -180,7 +180,7 @@ Index: FFmpeg/libavutil/hwcontext_d3d11va.c + if (ctx->user_opaque) { + D3D11_TEXTURE2D_DESC *desc = ctx->user_opaque; + if (desc->BindFlags & D3D11_BIND_DECODER) -+ texDesc.BindFlags = D3D11_BIND_DECODER; ++ texDesc.BindFlags |= D3D11_BIND_DECODER; + } +#endif + diff --git a/debian/patches/0093-add-d3d11-deinterlace-filter.patch b/debian/patches/0093-add-d3d11-deinterlace-filter.patch new file mode 100644 index 000000000..b6695c0b9 --- /dev/null +++ b/debian/patches/0093-add-d3d11-deinterlace-filter.patch @@ -0,0 +1,1296 @@ +Index: FFmpeg/configure +=================================================================== +--- FFmpeg.orig/configure ++++ FFmpeg/configure +@@ -3549,6 +3549,8 @@ ddagrab_filter_deps="d3d11va IDXGIOutput + gfxcapture_filter_deps="cxx17 threads d3d11va IGraphicsCaptureItemInterop __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3" + gfxcapture_filter_extralibs="-lstdc++" + scale_d3d11_filter_deps="d3d11va" ++bwdif_d3d11_filter_deps="d3d11va" ++yadif_d3d11_filter_deps="d3d11va" + scale_d3d12_filter_deps="d3d12va ID3D12VideoProcessor" + deinterlace_d3d12_filter_deps="d3d12va ID3D12VideoProcessor" + mestimate_d3d12_filter_deps="d3d12va ID3D12VideoMotionEstimator d3d12_motion_estimator" +Index: FFmpeg/libavfilter/Makefile +=================================================================== +--- FFmpeg.orig/libavfilter/Makefile ++++ FFmpeg/libavfilter/Makefile +@@ -222,6 +222,7 @@ OBJS-$(CONFIG_BOXBLUR_FILTER) + OBJS-$(CONFIG_BOXBLUR_OPENCL_FILTER) += vf_avgblur_opencl.o opencl.o \ + opencl/avgblur.o boxblur.o + OBJS-$(CONFIG_BWDIF_FILTER) += vf_bwdif.o bwdifdsp.o yadif_common.o ++OBJS-$(CONFIG_BWDIF_D3D11_FILTER) += vf_yadif_d3d11.o yadif_common.o d3d11/deint.o + OBJS-$(CONFIG_BWDIF_CUDA_FILTER) += vf_bwdif_cuda.o vf_bwdif_cuda.ptx.o \ + yadif_common.o + OBJS-$(CONFIG_BWDIF_OPENCL_FILTER) += vf_bwdif_opencl.o opencl.o opencl/bwdif.o \ +@@ -609,6 +610,7 @@ OBJS-$(CONFIG_XMEDIAN_FILTER) + OBJS-$(CONFIG_XPSNR_FILTER) += vf_xpsnr.o framesync.o psnr.o + OBJS-$(CONFIG_XSTACK_FILTER) += vf_stack.o framesync.o + OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o yadif_common.o ++OBJS-$(CONFIG_YADIF_D3D11_FILTER) += vf_yadif_d3d11.o yadif_common.o d3d11/deint.o + OBJS-$(CONFIG_YADIF_CUDA_FILTER) += vf_yadif_cuda.o vf_yadif_cuda.ptx.o \ + yadif_common.o cuda/load_helper.o + OBJS-$(CONFIG_YADIF_OPENCL_FILTER) += vf_yadif_opencl.o opencl.o opencl/yadif.o \ +@@ -721,10 +723,16 @@ TOOLS-$(CONFIG_LIBZMQ) += zmqsend + + clean:: + $(RM) $(CLEANSUFFIXES:%=libavfilter/dnn/%) $(CLEANSUFFIXES:%=libavfilter/opencl/%) \ +- $(CLEANSUFFIXES:%=libavfilter/metal/%) ++ $(CLEANSUFFIXES:%=libavfilter/metal/%) $(CLEANSUFFIXES:%=libavfilter/d3d11/%) + + OPENCL = $(subst $(SRC_PATH)/,,$(wildcard $(SRC_PATH)/libavfilter/opencl/*.cl)) + .SECONDARY: $(OPENCL:.cl=.c) + libavfilter/opencl/%.c: TAG = OPENCL + libavfilter/opencl/%.c: $(SRC_PATH)/libavfilter/opencl/%.cl + $(M)$(SRC_PATH)/tools/source2c $< $@ ++ ++HLSL = $(subst $(SRC_PATH)/,,$(wildcard $(SRC_PATH)/libavfilter/d3d11/*.hlsl)) ++.SECONDARY: $(HLSL:.hlsl=.c) ++libavfilter/d3d11/%.c: TAG = HLSL ++libavfilter/d3d11/%.c: $(SRC_PATH)/libavfilter/d3d11/%.hlsl ++ $(M)$(SRC_PATH)/tools/source2c $< $@ +Index: FFmpeg/libavfilter/allfilters.c +=================================================================== +--- FFmpeg.orig/libavfilter/allfilters.c ++++ FFmpeg/libavfilter/allfilters.c +@@ -203,6 +203,7 @@ extern const FFFilter ff_vf_bm3d; + extern const FFFilter ff_vf_boxblur; + extern const FFFilter ff_vf_boxblur_opencl; + extern const FFFilter ff_vf_bwdif; ++extern const FFFilter ff_vf_bwdif_d3d11; + extern const FFFilter ff_vf_bwdif_cuda; + extern const FFFilter ff_vf_bwdif_opencl; + extern const FFFilter ff_vf_bwdif_videotoolbox; +@@ -563,6 +564,7 @@ extern const FFFilter ff_vf_xmedian; + extern const FFFilter ff_vf_xpsnr; + extern const FFFilter ff_vf_xstack; + extern const FFFilter ff_vf_yadif; ++extern const FFFilter ff_vf_yadif_d3d11; + extern const FFFilter ff_vf_yadif_cuda; + extern const FFFilter ff_vf_yadif_opencl; + extern const FFFilter ff_vf_yadif_videotoolbox; +Index: FFmpeg/libavfilter/vf_yadif_d3d11.c +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/vf_yadif_d3d11.c +@@ -0,0 +1,774 @@ ++/* ++ * D3D11 YADIF/BWDIF deinterlacing filters ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#ifndef COBJMACROS ++#define COBJMACROS ++#endif ++#include ++#include ++ ++#include "libavutil/avassert.h" ++#include "libavutil/common.h" ++#include "libavutil/hwcontext.h" ++#include "libavutil/hwcontext_d3d11va.h" ++#include "libavutil/mem.h" ++#include "libavutil/opt.h" ++#include "libavutil/pixdesc.h" ++ ++#include "filters.h" ++#include "video.h" ++#include "yadif.h" ++#include "vf_yadif_d3d11.h" ++#include "d3d11_source.h" ++ ++#define D3D11_RELEASE(p) do { if (p) { (p)->lpVtbl->Release(p); (p) = NULL; } } while (0) ++#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL3 ++#define D3DCOMPILE_OPTIMIZATION_LEVEL3 (1 << 15) ++#endif ++ ++ ++#define DEINT_D3D11_ALG_YADIF 0 ++#define DEINT_D3D11_ALG_BWDIF 1 ++ ++typedef HRESULT (WINAPI *D3DCompileProc)(LPCVOID, SIZE_T, LPCSTR, ++ const D3D_SHADER_MACRO *, ID3DInclude *, ++ LPCSTR, LPCSTR, UINT, UINT, ++ ID3D10Blob **, ID3D10Blob **); ++ ++typedef struct DeintD3D11Params { ++ int width; ++ int height; ++ int parity; ++ int tff; ++ int is_second_field; ++ int current_field; ++ int skip_spatial_check; ++ int algorithm; ++} DeintD3D11Params; ++ ++typedef struct DeintD3D11Context { ++ YADIFContext yadif; ++ ++ int algorithm; ++ ++ AVD3D11VADeviceContext *hwctx; ++ AVBufferRef *device_ref; ++ AVBufferRef *input_frames_ref; ++ AVHWFramesContext *input_frames; ++ ++ ID3D11Device *device; ++ ID3D11DeviceContext *context; ++ HMODULE d3dcompiler; ++ D3DCompileProc D3DCompile; ++ ++ ID3D11ComputeShader *cs_y; ++ ID3D11ComputeShader *cs_uv; ++ ID3D11Buffer *params_buf; ++ ++ ID3D11Texture2D *prev_tex; ++ ID3D11Texture2D *cur_tex; ++ ID3D11Texture2D *next_tex; ++ ID3D11Texture2D *work_tex; ++ int tex_w, tex_h; ++ int direct_input_srv; ++ int direct_output_uav; ++} DeintD3D11Context; ++ ++static void release_d3d11_resources(DeintD3D11Context *s) ++{ ++ D3D11_RELEASE(s->cs_y); ++ D3D11_RELEASE(s->cs_uv); ++ D3D11_RELEASE(s->params_buf); ++ D3D11_RELEASE(s->prev_tex); ++ D3D11_RELEASE(s->cur_tex); ++ D3D11_RELEASE(s->next_tex); ++ D3D11_RELEASE(s->work_tex); ++ s->tex_w = s->tex_h = 0; ++} ++ ++static int compile_shader(AVFilterContext *ctx, const char *entry, ID3D11ComputeShader **shader) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ ID3D10Blob *cs_blob = NULL; ++ ID3D10Blob *err_blob = NULL; ++ HRESULT hr; ++ ++ hr = s->D3DCompile(ff_source_deint_hlsl, strlen(ff_source_deint_hlsl), ++ NULL, NULL, NULL, entry, "cs_5_0", ++ D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &cs_blob, &err_blob); ++ if (FAILED(hr)) { ++ if (err_blob) { ++ av_log(ctx, AV_LOG_ERROR, "Failed compiling D3D11 deinterlace shader %s: %.*s\n", ++ entry, (int)err_blob->lpVtbl->GetBufferSize(err_blob), ++ (char *)err_blob->lpVtbl->GetBufferPointer(err_blob)); ++ } else { ++ av_log(ctx, AV_LOG_ERROR, "Failed compiling D3D11 deinterlace shader %s: HRESULT 0x%lX\n", ++ entry, (unsigned long)hr); ++ } ++ D3D11_RELEASE(err_blob); ++ return AVERROR_EXTERNAL; ++ } ++ ++ hr = s->device->lpVtbl->CreateComputeShader(s->device, ++ cs_blob->lpVtbl->GetBufferPointer(cs_blob), ++ cs_blob->lpVtbl->GetBufferSize(cs_blob), ++ NULL, shader); ++ D3D11_RELEASE(cs_blob); ++ D3D11_RELEASE(err_blob); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 deinterlace shader %s: HRESULT 0x%lX\n", ++ entry, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int init_shaders(AVFilterContext *ctx) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ D3D11_BUFFER_DESC bd = { 0 }; ++ HRESULT hr; ++ int ret; ++ ++ if (s->cs_y) ++ return 0; ++ ++ if (!s->d3dcompiler) { ++ s->d3dcompiler = LoadLibraryA("d3dcompiler_47.dll"); ++ if (!s->d3dcompiler) ++ s->d3dcompiler = LoadLibraryA("d3dcompiler_43.dll"); ++ } ++ if (!s->d3dcompiler) { ++ av_log(ctx, AV_LOG_ERROR, "Failed loading d3dcompiler DLL\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ s->D3DCompile = (D3DCompileProc)GetProcAddress(s->d3dcompiler, "D3DCompile"); ++ if (!s->D3DCompile) { ++ av_log(ctx, AV_LOG_ERROR, "Failed loading D3DCompile\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ if ((ret = compile_shader(ctx, "deint_y", &s->cs_y)) < 0 || ++ (ret = compile_shader(ctx, "deint_uv", &s->cs_uv)) < 0) ++ return ret; ++ ++ bd.ByteWidth = sizeof(DeintD3D11Params); ++ bd.Usage = D3D11_USAGE_DYNAMIC; ++ bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; ++ bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ++ hr = s->device->lpVtbl->CreateBuffer(s->device, &bd, NULL, &s->params_buf); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 deinterlace constant buffer: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ return 0; ++} ++ ++static int ensure_textures(AVFilterContext *ctx, int w, int h) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ D3D11_TEXTURE2D_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ if (s->prev_tex && s->cur_tex && s->next_tex && s->work_tex && s->tex_w == w && s->tex_h == h) ++ return 0; ++ ++ D3D11_RELEASE(s->prev_tex); ++ D3D11_RELEASE(s->cur_tex); ++ D3D11_RELEASE(s->next_tex); ++ D3D11_RELEASE(s->work_tex); ++ ++ desc.Width = w; ++ desc.Height = h; ++ desc.MipLevels = 1; ++ desc.ArraySize = 1; ++ desc.Format = DXGI_FORMAT_NV12; ++ desc.SampleDesc.Count = 1; ++ desc.Usage = D3D11_USAGE_DEFAULT; ++ desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; ++ ++ hr = s->device->lpVtbl->CreateTexture2D(s->device, &desc, NULL, &s->prev_tex); ++ if (FAILED(hr)) goto fail; ++ hr = s->device->lpVtbl->CreateTexture2D(s->device, &desc, NULL, &s->cur_tex); ++ if (FAILED(hr)) goto fail; ++ hr = s->device->lpVtbl->CreateTexture2D(s->device, &desc, NULL, &s->next_tex); ++ if (FAILED(hr)) goto fail; ++ ++ desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; ++ hr = s->device->lpVtbl->CreateTexture2D(s->device, &desc, NULL, &s->work_tex); ++ if (FAILED(hr)) goto fail; ++ ++ s->tex_w = w; ++ s->tex_h = h; ++ return 0; ++ ++fail: ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 deinterlace texture: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++} ++ ++static void fill_tex2d_srv_desc(ID3D11Texture2D *tex, UINT subresource, ++ DXGI_FORMAT fmt, D3D11_SHADER_RESOURCE_VIEW_DESC *desc) ++{ ++ D3D11_TEXTURE2D_DESC tex_desc; ++ UINT mip_slice = 0; ++ UINT array_slice = 0; ++ ++ ID3D11Texture2D_GetDesc(tex, &tex_desc); ++ if (tex_desc.MipLevels) { ++ mip_slice = subresource % tex_desc.MipLevels; ++ array_slice = subresource / tex_desc.MipLevels; ++ } ++ ++ desc->Format = fmt; ++ desc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; ++ desc->Texture2DArray.MostDetailedMip = mip_slice; ++ desc->Texture2DArray.MipLevels = 1; ++ desc->Texture2DArray.FirstArraySlice = array_slice; ++ desc->Texture2DArray.ArraySize = 1; ++} ++ ++static void fill_tex2d_uav_desc(ID3D11Texture2D *tex, UINT subresource, ++ DXGI_FORMAT fmt, D3D11_UNORDERED_ACCESS_VIEW_DESC *desc) ++{ ++ D3D11_TEXTURE2D_DESC tex_desc; ++ UINT mip_slice = 0; ++ UINT array_slice = 0; ++ ++ ID3D11Texture2D_GetDesc(tex, &tex_desc); ++ if (tex_desc.MipLevels) { ++ mip_slice = subresource % tex_desc.MipLevels; ++ array_slice = subresource / tex_desc.MipLevels; ++ } ++ ++ desc->Format = fmt; ++ desc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; ++ desc->Texture2DArray.MipSlice = mip_slice; ++ desc->Texture2DArray.FirstArraySlice = array_slice; ++ desc->Texture2DArray.ArraySize = 1; ++} ++ ++static int create_nv12_srv_view(AVFilterContext *ctx, ID3D11Texture2D *tex, ++ UINT subresource, int plane, int log_level, ++ ID3D11ShaderResourceView **srv) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ D3D11_SHADER_RESOURCE_VIEW_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ fill_tex2d_srv_desc(tex, subresource, plane ? DXGI_FORMAT_R8G8_UNORM : DXGI_FORMAT_R8_UNORM, &desc); ++ ++ hr = s->device->lpVtbl->CreateShaderResourceView(s->device, (ID3D11Resource *)tex, ++ &desc, srv); ++ if (FAILED(hr)) { ++ av_log(ctx, log_level, "Failed creating D3D11 deinterlace SRV plane %d: HRESULT 0x%lX\n", ++ plane, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int create_nv12_srv(AVFilterContext *ctx, ID3D11Texture2D *tex, int plane, ++ ID3D11ShaderResourceView **srv) ++{ ++ return create_nv12_srv_view(ctx, tex, 0, plane, AV_LOG_ERROR, srv); ++} ++ ++static int create_nv12_uav_view(AVFilterContext *ctx, ID3D11Texture2D *tex, ++ UINT subresource, int plane, int log_level, ++ ID3D11UnorderedAccessView **uav) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ D3D11_UNORDERED_ACCESS_VIEW_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ fill_tex2d_uav_desc(tex, subresource, plane ? DXGI_FORMAT_R8G8_UNORM : DXGI_FORMAT_R8_UNORM, &desc); ++ ++ hr = s->device->lpVtbl->CreateUnorderedAccessView(s->device, (ID3D11Resource *)tex, ++ &desc, uav); ++ if (FAILED(hr)) { ++ av_log(ctx, log_level, "Failed creating D3D11 deinterlace UAV plane %d: HRESULT 0x%lX\n", ++ plane, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int create_nv12_uav(AVFilterContext *ctx, ID3D11Texture2D *tex, int plane, ++ ID3D11UnorderedAccessView **uav) ++{ ++ return create_nv12_uav_view(ctx, tex, 0, plane, AV_LOG_ERROR, uav); ++} ++ ++static void copy_frame_to_texture(DeintD3D11Context *s, AVFrame *src, ID3D11Texture2D *dst) ++{ ++ ID3D11Texture2D *src_tex = (ID3D11Texture2D *)src->data[0]; ++ UINT src_sub = (UINT)(uintptr_t)src->data[1]; ++ D3D11_BOX box = { 0, 0, 0, src->width, src->height, 1 }; ++ ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst, 0, ++ 0, 0, 0, (ID3D11Resource *)src_tex, ++ src_sub, &box); ++} ++ ++static void copy_texture_to_frame(DeintD3D11Context *s, ID3D11Texture2D *src, AVFrame *dst) ++{ ++ ID3D11Texture2D *dst_tex = (ID3D11Texture2D *)dst->data[0]; ++ UINT dst_sub = (UINT)(uintptr_t)dst->data[1]; ++ ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst_tex, ++ dst_sub, 0, 0, 0, (ID3D11Resource *)src, ++ 0, NULL); ++} ++ ++static int create_frame_srvs(AVFilterContext *ctx, AVFrame *frame, ++ ID3D11ShaderResourceView **srv_y, ++ ID3D11ShaderResourceView **srv_uv) ++{ ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)frame->data[0]; ++ UINT subresource = (UINT)(uintptr_t)frame->data[1]; ++ int ret; ++ ++ ret = create_nv12_srv_view(ctx, tex, subresource, 0, AV_LOG_DEBUG, srv_y); ++ if (ret < 0) ++ return ret; ++ ret = create_nv12_srv_view(ctx, tex, subresource, 1, AV_LOG_DEBUG, srv_uv); ++ if (ret < 0) ++ D3D11_RELEASE(*srv_y); ++ return ret; ++} ++ ++static int prepare_input_srvs(AVFilterContext *ctx, ID3D11ShaderResourceView **srvs) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ YADIFContext *y = &s->yadif; ++ int ret; ++ ++ if (s->direct_input_srv) { ++ ret = create_frame_srvs(ctx, y->prev, &srvs[0], &srvs[3]); ++ if (ret >= 0) ++ ret = create_frame_srvs(ctx, y->cur, &srvs[1], &srvs[4]); ++ if (ret >= 0) ++ ret = create_frame_srvs(ctx, y->next, &srvs[2], &srvs[5]); ++ if (ret >= 0) { ++ if (s->direct_input_srv < 0) { ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader input: direct SRV\n"); ++ s->direct_input_srv = 1; ++ } ++ return 0; ++ } ++ for (int i = 0; i < 6; i++) ++ D3D11_RELEASE(srvs[i]); ++ if (s->direct_input_srv < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader input: copied to internal SRV texture\n"); ++ s->direct_input_srv = 0; ++ } ++ ++ copy_frame_to_texture(s, y->prev, s->prev_tex); ++ copy_frame_to_texture(s, y->cur, s->cur_tex); ++ copy_frame_to_texture(s, y->next, s->next_tex); ++ ++#define MAKE_SRV(tex, plane, idx) do { ret = create_nv12_srv(ctx, tex, plane, &srvs[idx]); if (ret < 0) goto fail; } while (0) ++ MAKE_SRV(s->prev_tex, 0, 0); ++ MAKE_SRV(s->cur_tex, 0, 1); ++ MAKE_SRV(s->next_tex, 0, 2); ++ MAKE_SRV(s->prev_tex, 1, 3); ++ MAKE_SRV(s->cur_tex, 1, 4); ++ MAKE_SRV(s->next_tex, 1, 5); ++#undef MAKE_SRV ++ ++ return 0; ++ ++fail: ++#undef MAKE_SRV ++ for (int i = 0; i < 6; i++) ++ D3D11_RELEASE(srvs[i]); ++ return ret; ++} ++ ++static int prepare_output_uavs(AVFilterContext *ctx, AVFrame *dst, ++ ID3D11UnorderedAccessView **uavs, int *direct) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)dst->data[0]; ++ UINT subresource = (UINT)(uintptr_t)dst->data[1]; ++ int ret; ++ ++ *direct = 0; ++ if (s->direct_output_uav) { ++ ret = create_nv12_uav_view(ctx, tex, subresource, 0, AV_LOG_DEBUG, &uavs[0]); ++ if (ret >= 0) ++ ret = create_nv12_uav_view(ctx, tex, subresource, 1, AV_LOG_DEBUG, &uavs[1]); ++ if (ret >= 0) { ++ *direct = 1; ++ return 0; ++ } ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ if (s->direct_output_uav < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: copied from internal UAV texture\n"); ++ s->direct_output_uav = 0; ++ } ++ ++ ret = create_nv12_uav(ctx, s->work_tex, 0, &uavs[0]); ++ if (ret < 0) ++ return ret; ++ ret = create_nv12_uav(ctx, s->work_tex, 1, &uavs[1]); ++ if (ret < 0) ++ D3D11_RELEASE(uavs[0]); ++ return ret; ++} ++ ++static int probe_output_uav_pool(AVFilterContext *ctx, AVBufferRef *frames_ref) ++{ ++ AVHWFramesContext *frames_ctx = (AVHWFramesContext *)frames_ref->data; ++ AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx; ++ AVFrame *frame = NULL; ++ ID3D11Texture2D *tex; ++ ID3D11UnorderedAccessView *uavs[2] = { NULL, NULL }; ++ UINT subresource; ++ int ret; ++ ++ if (frames_hwctx->texture) { ++ tex = frames_hwctx->texture; ++ subresource = 0; ++ } else { ++ frame = av_frame_alloc(); ++ if (!frame) ++ return AVERROR(ENOMEM); ++ ++ ret = av_hwframe_get_buffer(frames_ref, frame, 0); ++ if (ret < 0) ++ goto done; ++ ++ tex = (ID3D11Texture2D *)frame->data[0]; ++ subresource = (UINT)(uintptr_t)frame->data[1]; ++ } ++ ++ ret = create_nv12_uav_view(ctx, tex, subresource, 0, AV_LOG_DEBUG, &uavs[0]); ++ if (ret >= 0) ++ ret = create_nv12_uav_view(ctx, tex, subresource, 1, AV_LOG_DEBUG, &uavs[1]); ++ ++done: ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ av_frame_free(&frame); ++ return ret; ++} ++ ++static int run_filter(AVFilterContext *ctx, AVFrame *dst, int parity, int tff) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ YADIFContext *y = &s->yadif; ++ ID3D11ShaderResourceView *srvs[6] = { NULL }; ++ ID3D11ShaderResourceView *null_srvs[6] = { NULL }; ++ ID3D11UnorderedAccessView *uavs[2] = { NULL, NULL }; ++ ID3D11UnorderedAccessView *null_uavs[2] = { NULL, NULL }; ++ ID3D11Buffer *null_cb[1] = { NULL }; ++ D3D11_MAPPED_SUBRESOURCE mapped; ++ DeintD3D11Params params; ++ int direct_output = 0; ++ HRESULT hr; ++ int ret = 0; ++ ++ ret = ensure_textures(ctx, y->cur->width, y->cur->height); ++ if (ret < 0) ++ return ret; ++ ++ ret = prepare_input_srvs(ctx, srvs); ++ if (ret < 0) ++ goto fail; ++ ++ ret = prepare_output_uavs(ctx, dst, uavs, &direct_output); ++ if (ret < 0) ++ goto fail; ++ ++ params.width = y->cur->width; ++ params.height = y->cur->height; ++ params.parity = parity; ++ params.tff = tff; ++ params.is_second_field = !(parity ^ tff); ++ params.current_field = y->current_field; ++ params.skip_spatial_check = y->mode & 2; ++ params.algorithm = s->algorithm; ++ ++ hr = s->context->lpVtbl->Map(s->context, (ID3D11Resource *)s->params_buf, ++ 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed mapping D3D11 deinterlace constant buffer: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ memcpy(mapped.pData, ¶ms, sizeof(params)); ++ s->context->lpVtbl->Unmap(s->context, (ID3D11Resource *)s->params_buf, 0); ++ ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, &s->params_buf); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 6, srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, uavs, NULL); ++ ++ s->context->lpVtbl->CSSetShader(s->context, s->cs_y, NULL, 0); ++ s->context->lpVtbl->Dispatch(s->context, ++ (params.width + DEINT_D3D11_TGX - 1) / DEINT_D3D11_TGX, ++ (params.height + DEINT_D3D11_TGY - 1) / DEINT_D3D11_TGY, 1); ++ ++ s->context->lpVtbl->CSSetShader(s->context, s->cs_uv, NULL, 0); ++ s->context->lpVtbl->Dispatch(s->context, ++ (((params.width + 1) >> 1) + DEINT_D3D11_TGX - 1) / DEINT_D3D11_TGX, ++ (((params.height + 1) >> 1) + DEINT_D3D11_TGY - 1) / DEINT_D3D11_TGY, 1); ++ ++ s->context->lpVtbl->CSSetShader(s->context, NULL, NULL, 0); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 6, null_srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, null_uavs, NULL); ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, null_cb); ++ ++ if (!direct_output) ++ copy_texture_to_frame(s, s->work_tex, dst); ++ ++fail: ++ s->context->lpVtbl->CSSetShader(s->context, NULL, NULL, 0); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 6, null_srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, null_uavs, NULL); ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, null_cb); ++ for (int i = 0; i < 6; i++) ++ D3D11_RELEASE(srvs[i]); ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ return ret; ++} ++ ++static void filter(AVFilterContext *ctx, AVFrame *dst, int parity, int tff) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ YADIFContext *y = &s->yadif; ++ ++ run_filter(ctx, dst, parity, tff); ++ ++ if (s->algorithm == DEINT_D3D11_ALG_BWDIF && y->current_field == YADIF_FIELD_END) ++ y->current_field = YADIF_FIELD_NORMAL; ++} ++ ++static av_cold void deint_d3d11_uninit(AVFilterContext *ctx) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ ++ release_d3d11_resources(s); ++ ff_yadif_uninit(ctx); ++ av_buffer_unref(&s->device_ref); ++ av_buffer_unref(&s->input_frames_ref); ++ s->hwctx = NULL; ++ s->input_frames = NULL; ++ if (s->d3dcompiler) ++ FreeLibrary(s->d3dcompiler); ++} ++ ++static int config_input(AVFilterLink *inlink) ++{ ++ FilterLink *l = ff_filter_link(inlink); ++ AVFilterContext *ctx = inlink->dst; ++ DeintD3D11Context *s = ctx->priv; ++ ++ if (!l->hw_frames_ctx) { ++ av_log(ctx, AV_LOG_ERROR, "D3D11 deinterlacing requires a hardware frames context on input.\n"); ++ return AVERROR(EINVAL); ++ } ++ ++ s->input_frames_ref = av_buffer_ref(l->hw_frames_ctx); ++ if (!s->input_frames_ref) ++ return AVERROR(ENOMEM); ++ s->input_frames = (AVHWFramesContext *)s->input_frames_ref->data; ++ ++ if (s->input_frames->format != AV_PIX_FMT_D3D11) ++ return AVERROR(EINVAL); ++ if (s->input_frames->sw_format != AV_PIX_FMT_NV12) { ++ av_log(ctx, AV_LOG_ERROR, "D3D11 deinterlacing currently supports NV12 only, got %s\n", ++ av_get_pix_fmt_name(s->input_frames->sw_format)); ++ return AVERROR(ENOSYS); ++ } ++ ++ return 0; ++} ++ ++static int config_output(AVFilterLink *link) ++{ ++ FilterLink *l = ff_filter_link(link); ++ AVHWFramesContext *output_frames; ++ AVFilterContext *ctx = link->src; ++ DeintD3D11Context *s = ctx->priv; ++ YADIFContext *y = &s->yadif; ++ int ret; ++ ++ av_assert0(s->input_frames); ++ ++ s->device_ref = av_buffer_ref(s->input_frames->device_ref); ++ if (!s->device_ref) ++ return AVERROR(ENOMEM); ++ ++ s->hwctx = ((AVHWDeviceContext *)s->device_ref->data)->hwctx; ++ s->device = s->hwctx->device; ++ s->context = s->hwctx->device_context; ++ ++ av_buffer_unref(&l->hw_frames_ctx); ++ s->direct_input_srv = -1; ++ s->direct_output_uav = -1; ++ ++ for (int direct = 1; direct >= 0; direct--) { ++ l->hw_frames_ctx = av_hwframe_ctx_alloc(s->device_ref); ++ if (!l->hw_frames_ctx) ++ return AVERROR(ENOMEM); ++ ++ output_frames = (AVHWFramesContext *)l->hw_frames_ctx->data; ++ output_frames->format = AV_PIX_FMT_D3D11; ++ output_frames->sw_format = s->input_frames->sw_format; ++ output_frames->width = ctx->inputs[0]->w; ++ output_frames->height = ctx->inputs[0]->h; ++ output_frames->initial_pool_size = 4; ++ ++ { ++ AVD3D11VAFramesContext *frames_hwctx = output_frames->hwctx; ++ frames_hwctx->BindFlags = direct ? D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE | ++ D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_VIDEO_ENCODER ++ : D3D11_BIND_RENDER_TARGET | D3D11_BIND_VIDEO_ENCODER; ++ } ++ ++ ret = av_hwframe_ctx_init(l->hw_frames_ctx); ++ if (ret >= 0 && direct) { ++ ret = probe_output_uav_pool(ctx, l->hw_frames_ctx); ++ if (ret >= 0) { ++ s->direct_output_uav = 1; ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: direct UAV\n"); ++ break; ++ } ++ av_buffer_unref(&l->hw_frames_ctx); ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: copied from internal UAV texture\n"); ++ continue; ++ } else if (ret >= 0) { ++ s->direct_output_uav = 0; ++ break; ++ } ++ ++ av_buffer_unref(&l->hw_frames_ctx); ++ if (direct) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: UAV encoder pool rejected, using internal UAV texture\n"); ++ } ++ if (ret < 0) ++ return ret; ++ ++ output_frames = (AVHWFramesContext *)l->hw_frames_ctx->data; ++ ++ ret = ff_yadif_config_output_common(link); ++ if (ret < 0) ++ return ret; ++ ++ y->csp = av_pix_fmt_desc_get(output_frames->sw_format); ++ y->filter = filter; ++ ++ ret = init_shaders(ctx); ++ if (ret < 0) ++ return ret; ++ ++ av_log(ctx, AV_LOG_VERBOSE, "D3D11 %s config: %dx%d\n", ++ s->algorithm == DEINT_D3D11_ALG_BWDIF ? "bwdif" : "yadif", ++ link->w, link->h); ++ return 0; ++} ++ ++static av_cold int yadif_d3d11_init(AVFilterContext *ctx) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ s->algorithm = DEINT_D3D11_ALG_YADIF; ++ return 0; ++} ++ ++static av_cold int bwdif_d3d11_init(AVFilterContext *ctx) ++{ ++ DeintD3D11Context *s = ctx->priv; ++ s->algorithm = DEINT_D3D11_ALG_BWDIF; ++ return 0; ++} ++ ++static const AVClass yadif_d3d11_class = { ++ .class_name = "yadif_d3d11", ++ .item_name = av_default_item_name, ++ .option = ff_yadif_options, ++ .version = LIBAVUTIL_VERSION_INT, ++ .category = AV_CLASS_CATEGORY_FILTER, ++}; ++ ++static const AVClass bwdif_d3d11_class = { ++ .class_name = "bwdif_d3d11", ++ .item_name = av_default_item_name, ++ .option = ff_yadif_options, ++ .version = LIBAVUTIL_VERSION_INT, ++ .category = AV_CLASS_CATEGORY_FILTER, ++}; ++ ++static const AVFilterPad deint_d3d11_inputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .filter_frame = ff_yadif_filter_frame, ++ .config_props = config_input, ++ }, ++}; ++ ++static const AVFilterPad deint_d3d11_outputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .request_frame = ff_yadif_request_frame, ++ .config_props = config_output, ++ }, ++}; ++ ++const FFFilter ff_vf_yadif_d3d11 = { ++ .p.name = "yadif_d3d11", ++ .p.description = NULL_IF_CONFIG_SMALL("Deinterlace D3D11 frames using YADIF"), ++ .p.priv_class = &yadif_d3d11_class, ++ .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_HWDEVICE, ++ .priv_size = sizeof(DeintD3D11Context), ++ .init = yadif_d3d11_init, ++ .uninit = deint_d3d11_uninit, ++ FILTER_INPUTS(deint_d3d11_inputs), ++ FILTER_OUTPUTS(deint_d3d11_outputs), ++ FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11), ++ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, ++}; ++ ++const FFFilter ff_vf_bwdif_d3d11 = { ++ .p.name = "bwdif_d3d11", ++ .p.description = NULL_IF_CONFIG_SMALL("Deinterlace D3D11 frames using BWDIF"), ++ .p.priv_class = &bwdif_d3d11_class, ++ .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_HWDEVICE, ++ .priv_size = sizeof(DeintD3D11Context), ++ .init = bwdif_d3d11_init, ++ .uninit = deint_d3d11_uninit, ++ FILTER_INPUTS(deint_d3d11_inputs), ++ FILTER_OUTPUTS(deint_d3d11_outputs), ++ FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11), ++ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, ++}; +Index: FFmpeg/libavfilter/vf_yadif_d3d11.h +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/vf_yadif_d3d11.h +@@ -0,0 +1,29 @@ ++/* ++ * D3D11 YADIF/BWDIF constants ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVFILTER_VF_YADIF_D3D11_H ++#define AVFILTER_VF_YADIF_D3D11_H ++ ++#define DEINT_D3D11_TGX 16 ++#define DEINT_D3D11_TGY 16 ++ ++#endif /* AVFILTER_VF_YADIF_D3D11_H */ +Index: FFmpeg/libavfilter/d3d11/deint.hlsl +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/d3d11/deint.hlsl +@@ -0,0 +1,374 @@ ++/* ++ * D3D11 YADIF/BWDIF deinterlace ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++Texture2DArray prev_y : register(t0); ++Texture2DArray cur_y : register(t1); ++Texture2DArray next_y : register(t2); ++Texture2DArray prev_uv : register(t3); ++Texture2DArray cur_uv : register(t4); ++Texture2DArray next_uv : register(t5); ++ ++RWTexture2DArray dst_y : register(u0); ++RWTexture2DArray dst_uv : register(u1); ++ ++cbuffer Params : register(b0) { ++ int width; ++ int height; ++ int parity; ++ int tff; ++ int is_second_field; ++ int current_field; ++ int skip_spatial_check; ++ int algorithm; ++}; ++ ++int clampi(int v, int lo, int hi) ++{ ++ return min(max(v, lo), hi); ++} ++ ++int2 pos_y(int x, int y) ++{ ++ return int2(clampi(x, 0, width - 1), clampi(y, 0, height - 1)); ++} ++ ++int cw() ++{ ++ return (width + 1) >> 1; ++} ++ ++int ch() ++{ ++ return (height + 1) >> 1; ++} ++ ++int2 pos_uv(int x, int y) ++{ ++ return int2(clampi(x, 0, cw() - 1), clampi(y, 0, ch() - 1)); ++} ++ ++float py(int x, int y) ++{ ++ return prev_y.Load(int4(pos_y(x, y), 0, 0)).r; ++} ++ ++float cy(int x, int y) ++{ ++ return cur_y.Load(int4(pos_y(x, y), 0, 0)).r; ++} ++ ++float ny(int x, int y) ++{ ++ return next_y.Load(int4(pos_y(x, y), 0, 0)).r; ++} ++ ++float2 puv(int x, int y) ++{ ++ return prev_uv.Load(int4(pos_uv(x, y), 0, 0)).rg; ++} ++ ++float2 cuv(int x, int y) ++{ ++ return cur_uv.Load(int4(pos_uv(x, y), 0, 0)).rg; ++} ++ ++float2 nuv(int x, int y) ++{ ++ return next_uv.Load(int4(pos_uv(x, y), 0, 0)).rg; ++} ++ ++float max3f(float a, float b, float c) ++{ ++ return max(max(a, b), c); ++} ++ ++float min3f(float a, float b, float c) ++{ ++ return min(min(a, b), c); ++} ++ ++float2 max3f2(float2 a, float2 b, float2 c) ++{ ++ return max(max(a, b), c); ++} ++ ++float2 min3f2(float2 a, float2 b, float2 c) ++{ ++ return min(min(a, b), c); ++} ++ ++float spatial1(float a, float b, float c, float d, float e, float f, float g, ++ float h, float i, float j, float k, float l, float m, float n) ++{ ++ float spatial_pred = (d + k) * 0.5; ++ float spatial_score = abs(c - j) + abs(d - k) + abs(e - l); ++ ++ float score = abs(b - k) + abs(c - l) + abs(d - m); ++ if (score < spatial_score) { ++ spatial_pred = (c + l) * 0.5; ++ spatial_score = score; ++ score = abs(a - l) + abs(b - m) + abs(c - n); ++ if (score < spatial_score) { ++ spatial_pred = (b + m) * 0.5; ++ spatial_score = score; ++ } ++ } ++ ++ score = abs(d - i) + abs(e - j) + abs(f - k); ++ if (score < spatial_score) { ++ spatial_pred = (e + j) * 0.5; ++ spatial_score = score; ++ score = abs(e - h) + abs(f - i) + abs(g - j); ++ if (score < spatial_score) ++ spatial_pred = (f + i) * 0.5; ++ } ++ ++ return spatial_pred; ++} ++ ++float2 spatial2(float2 a, float2 b, float2 c, float2 d, float2 e, float2 f, float2 g, ++ float2 h, float2 i, float2 j, float2 k, float2 l, float2 m, float2 n) ++{ ++ return float2(spatial1(a.x, b.x, c.x, d.x, e.x, f.x, g.x, h.x, i.x, j.x, k.x, l.x, m.x, n.x), ++ spatial1(a.y, b.y, c.y, d.y, e.y, f.y, g.y, h.y, i.y, j.y, k.y, l.y, m.y, n.y)); ++} ++ ++float yadif_spatial_y(int x, int y) ++{ ++ return spatial1(cy(x - 3, y - 1), cy(x - 2, y - 1), cy(x - 1, y - 1), cy(x, y - 1), ++ cy(x + 1, y - 1), cy(x + 2, y - 1), cy(x + 3, y - 1), ++ cy(x - 3, y + 1), cy(x - 2, y + 1), cy(x - 1, y + 1), cy(x, y + 1), ++ cy(x + 1, y + 1), cy(x + 2, y + 1), cy(x + 3, y + 1)); ++} ++ ++float2 yadif_spatial_uv(int x, int y) ++{ ++ return spatial2(cuv(x - 3, y - 1), cuv(x - 2, y - 1), cuv(x - 1, y - 1), cuv(x, y - 1), ++ cuv(x + 1, y - 1), cuv(x + 2, y - 1), cuv(x + 3, y - 1), ++ cuv(x - 3, y + 1), cuv(x - 2, y + 1), cuv(x - 1, y + 1), cuv(x, y + 1), ++ cuv(x + 1, y + 1), cuv(x + 2, y + 1), cuv(x + 3, y + 1)); ++} ++ ++float temporal1(float A, float B, float C, float D, float E, float F, ++ float G, float H, float I, float J, float K, float L, ++ float spatial_pred) ++{ ++ float p0 = (C + H) * 0.5; ++ float p1 = F; ++ float p2 = (D + I) * 0.5; ++ float p3 = G; ++ float p4 = (E + J) * 0.5; ++ ++ float tdiff0 = abs(D - I); ++ float tdiff1 = (abs(A - F) + abs(B - G)) * 0.5; ++ float tdiff2 = (abs(K - F) + abs(G - L)) * 0.5; ++ float diff = max3f(tdiff0, tdiff1, tdiff2); ++ ++ if (!skip_spatial_check) { ++ float maxi = max3f(p2 - p3, p2 - p1, min(p0 - p1, p4 - p3)); ++ float mini = min3f(p2 - p3, p2 - p1, max(p0 - p1, p4 - p3)); ++ diff = max3f(diff, mini, -maxi); ++ } ++ ++ return clamp(spatial_pred, p2 - diff, p2 + diff); ++} ++ ++float2 temporal2(float2 A, float2 B, float2 C, float2 D, float2 E, float2 F, ++ float2 G, float2 H, float2 I, float2 J, float2 K, float2 L, ++ float2 spatial_pred) ++{ ++ return float2(temporal1(A.x, B.x, C.x, D.x, E.x, F.x, G.x, H.x, I.x, J.x, K.x, L.x, spatial_pred.x), ++ temporal1(A.y, B.y, C.y, D.y, E.y, F.y, G.y, H.y, I.y, J.y, K.y, L.y, spatial_pred.y)); ++} ++ ++float yadif_y(int x, int y) ++{ ++ float sp = yadif_spatial_y(x, y); ++ ++ if (is_second_field) ++ return temporal1(py(x, y - 1), py(x, y + 1), ++ cy(x, y - 2), cy(x, y), cy(x, y + 2), ++ cy(x, y - 1), cy(x, y + 1), ++ ny(x, y - 2), ny(x, y), ny(x, y + 2), ++ ny(x, y - 1), ny(x, y + 1), sp); ++ return temporal1(py(x, y - 1), py(x, y + 1), ++ py(x, y - 2), py(x, y), py(x, y + 2), ++ cy(x, y - 1), cy(x, y + 1), ++ cy(x, y - 2), cy(x, y), cy(x, y + 2), ++ ny(x, y - 1), ny(x, y + 1), sp); ++} ++ ++float2 yadif_uv(int x, int y) ++{ ++ float2 sp = yadif_spatial_uv(x, y); ++ ++ if (is_second_field) ++ return temporal2(puv(x, y - 1), puv(x, y + 1), ++ cuv(x, y - 2), cuv(x, y), cuv(x, y + 2), ++ cuv(x, y - 1), cuv(x, y + 1), ++ nuv(x, y - 2), nuv(x, y), nuv(x, y + 2), ++ nuv(x, y - 1), nuv(x, y + 1), sp); ++ return temporal2(puv(x, y - 1), puv(x, y + 1), ++ puv(x, y - 2), puv(x, y), puv(x, y + 2), ++ cuv(x, y - 1), cuv(x, y + 1), ++ cuv(x, y - 2), cuv(x, y), cuv(x, y + 2), ++ nuv(x, y - 1), nuv(x, y + 1), sp); ++} ++ ++float bwdif_intra1(float cur_prefs3, float cur_prefs, float cur_mrefs, float cur_mrefs3) ++{ ++ return clamp((5077.0 * (cur_mrefs + cur_prefs) - 981.0 * (cur_mrefs3 + cur_prefs3)) / 8192.0, 0.0, 1.0); ++} ++ ++float2 bwdif_intra2(float2 a, float2 b, float2 c, float2 d) ++{ ++ return clamp((5077.0 * (c + b) - 981.0 * (d + a)) / 8192.0, 0.0, 1.0); ++} ++ ++float bwdif_temp1(float cp3, float cp, float cm, float cm3, ++ float p2p4, float p2p2, float p20, float p2m2, float p2m4, ++ float p1p, float p1m, float n1p, float n1m, ++ float n2p4, float n2p2, float n20, float n2m2, float n2m4) ++{ ++ float c = cm; ++ float d = (p20 + n20) * 0.5; ++ float e = cp; ++ ++ float td0 = abs(p20 - n20); ++ float td1 = (abs(p1m - c) + abs(p1p - e)) * 0.5; ++ float td2 = (abs(n1m - c) + abs(n1p - e)) * 0.5; ++ float diff = max3f(td0 * 0.5, td1, td2); ++ ++ if (!diff) ++ return d; ++ ++ float b = ((p2m2 + n2m2) * 0.5) - c; ++ float f = ((p2p2 + n2p2) * 0.5) - e; ++ float dc = d - c; ++ float de = d - e; ++ ++ float mmax = max3f(de, dc, min(b, f)); ++ float mmin = min3f(de, dc, max(b, f)); ++ diff = max3f(diff, mmin, -mmax); ++ ++ float interpol; ++ if (abs(c - e) > td0) ++ interpol = (((5570.0 * (p20 + n20) - ++ 3801.0 * (p2m2 + n2m2 + p2p2 + n2p2) + ++ 1016.0 * (p2m4 + n2m4 + p2p4 + n2p4)) * 0.25) + ++ 4309.0 * (c + e) - 213.0 * (cm3 + cp3)) / 8192.0; ++ else ++ interpol = (5077.0 * (c + e) - 981.0 * (cm3 + cp3)) / 8192.0; ++ ++ return clamp(clamp(interpol, d - diff, d + diff), 0.0, 1.0); ++} ++ ++float2 bwdif_temp2(float2 cp3, float2 cp, float2 cm, float2 cm3, ++ float2 p2p4, float2 p2p2, float2 p20, float2 p2m2, float2 p2m4, ++ float2 p1p, float2 p1m, float2 n1p, float2 n1m, ++ float2 n2p4, float2 n2p2, float2 n20, float2 n2m2, float2 n2m4) ++{ ++ return float2(bwdif_temp1(cp3.x, cp.x, cm.x, cm3.x, ++ p2p4.x, p2p2.x, p20.x, p2m2.x, p2m4.x, ++ p1p.x, p1m.x, n1p.x, n1m.x, ++ n2p4.x, n2p2.x, n20.x, n2m2.x, n2m4.x), ++ bwdif_temp1(cp3.y, cp.y, cm.y, cm3.y, ++ p2p4.y, p2p2.y, p20.y, p2m2.y, p2m4.y, ++ p1p.y, p1m.y, n1p.y, n1m.y, ++ n2p4.y, n2p2.y, n20.y, n2m2.y, n2m4.y)); ++} ++ ++float bwdif_y(int x, int y) ++{ ++ float cp3 = cy(x, y + 3); ++ float cp = cy(x, y + 1); ++ float cm = cy(x, y - 1); ++ float cm3 = cy(x, y - 3); ++ ++ if (current_field == 0) ++ return bwdif_intra1(cp3, cp, cm, cm3); ++ ++ if (is_second_field) ++ return bwdif_temp1(cp3, cp, cm, cm3, ++ py(x, y + 4), py(x, y + 2), py(x, y), py(x, y - 2), py(x, y - 4), ++ cy(x, y + 1), cy(x, y - 1), ny(x, y + 1), ny(x, y - 1), ++ ny(x, y + 4), ny(x, y + 2), ny(x, y), ny(x, y - 2), ny(x, y - 4)); ++ return bwdif_temp1(cp3, cp, cm, cm3, ++ py(x, y + 4), py(x, y + 2), py(x, y), py(x, y - 2), py(x, y - 4), ++ py(x, y + 1), py(x, y - 1), cy(x, y + 1), cy(x, y - 1), ++ ny(x, y + 4), ny(x, y + 2), ny(x, y), ny(x, y - 2), ny(x, y - 4)); ++} ++ ++float2 bwdif_uv(int x, int y) ++{ ++ float2 cp3 = cuv(x, y + 3); ++ float2 cp = cuv(x, y + 1); ++ float2 cm = cuv(x, y - 1); ++ float2 cm3 = cuv(x, y - 3); ++ ++ if (current_field == 0) ++ return bwdif_intra2(cp3, cp, cm, cm3); ++ ++ if (is_second_field) ++ return bwdif_temp2(cp3, cp, cm, cm3, ++ puv(x, y + 4), puv(x, y + 2), puv(x, y), puv(x, y - 2), puv(x, y - 4), ++ cuv(x, y + 1), cuv(x, y - 1), nuv(x, y + 1), nuv(x, y - 1), ++ nuv(x, y + 4), nuv(x, y + 2), nuv(x, y), nuv(x, y - 2), nuv(x, y - 4)); ++ return bwdif_temp2(cp3, cp, cm, cm3, ++ puv(x, y + 4), puv(x, y + 2), puv(x, y), puv(x, y - 2), puv(x, y - 4), ++ puv(x, y + 1), puv(x, y - 1), cuv(x, y + 1), cuv(x, y - 1), ++ nuv(x, y + 4), nuv(x, y + 2), nuv(x, y), nuv(x, y - 2), nuv(x, y - 4)); ++} ++ ++[numthreads(16, 16, 1)] ++void deint_y(uint3 id : SV_DispatchThreadID) ++{ ++ if (id.x >= (uint)width || id.y >= (uint)height) ++ return; ++ ++ int x = id.x; ++ int y = id.y; ++ ++ if ((y & 1) == parity) { ++ dst_y[uint3(x, y, 0)] = cy(x, y); ++ return; ++ } ++ ++ dst_y[uint3(x, y, 0)] = algorithm == 1 ? bwdif_y(x, y) : yadif_y(x, y); ++} ++ ++[numthreads(16, 16, 1)] ++void deint_uv(uint3 id : SV_DispatchThreadID) ++{ ++ if (id.x >= (uint)cw() || id.y >= (uint)ch()) ++ return; ++ ++ int x = id.x; ++ int y = id.y; ++ ++ if ((y & 1) == parity) { ++ dst_uv[uint3(x, y, 0)] = cuv(x, y); ++ return; ++ } ++ ++ dst_uv[uint3(x, y, 0)] = algorithm == 1 ? bwdif_uv(x, y) : yadif_uv(x, y); ++} +Index: FFmpeg/libavfilter/d3d11_source.h +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/d3d11_source.h +@@ -0,0 +1,28 @@ ++/* ++ * D3D11 HLSL shader source declarations ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVFILTER_D3D11_SOURCE_H ++#define AVFILTER_D3D11_SOURCE_H ++ ++extern const char *ff_source_deint_hlsl; ++ ++#endif /* AVFILTER_D3D11_SOURCE_H */ diff --git a/debian/patches/0094-add-d3d11-video-processor-overlay-transpose-filters.patch b/debian/patches/0094-add-d3d11-video-processor-overlay-transpose-filters.patch new file mode 100644 index 000000000..761165ddb --- /dev/null +++ b/debian/patches/0094-add-d3d11-video-processor-overlay-transpose-filters.patch @@ -0,0 +1,1780 @@ +Index: FFmpeg/configure +=================================================================== +--- FFmpeg.orig/configure ++++ FFmpeg/configure +@@ -3549,6 +3549,8 @@ ddagrab_filter_deps="d3d11va IDXGIOutput + gfxcapture_filter_deps="cxx17 threads d3d11va IGraphicsCaptureItemInterop __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3" + gfxcapture_filter_extralibs="-lstdc++" + scale_d3d11_filter_deps="d3d11va" ++overlay_d3d11_filter_deps="d3d11va" ++transpose_d3d11_filter_deps="d3d11va ID3D11VideoContext1" + bwdif_d3d11_filter_deps="d3d11va" + yadif_d3d11_filter_deps="d3d11va" + scale_d3d12_filter_deps="d3d12va ID3D12VideoProcessor" +@@ -7139,6 +7141,7 @@ check_type "windows.h dxgi1_2.h" "IDXGIO + check_type "windows.h dxgi1_5.h" "IDXGIOutput5" + check_type "windows.h d3d11.h" "ID3D11VideoDecoder" + check_type "windows.h d3d11.h" "ID3D11VideoContext" ++check_type "windows.h d3d11.h d3d11_1.h" "ID3D11VideoContext1" + check_type "windows.h d3d12.h" "ID3D12Device" + check_type "windows.h d3d12video.h" "ID3D12VideoDecoder" + check_type "windows.h d3d12video.h" "ID3D12VideoEncoder" +Index: FFmpeg/libavfilter/Makefile +=================================================================== +--- FFmpeg.orig/libavfilter/Makefile ++++ FFmpeg/libavfilter/Makefile +@@ -429,6 +429,7 @@ OBJS-$(CONFIG_OCV_FILTER) + OBJS-$(CONFIG_OCIO_FILTER) += vf_opencolorio.o ocio_wrapper.o + OBJS-$(CONFIG_OSCILLOSCOPE_FILTER) += vf_datascope.o + OBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o framesync.o ++OBJS-$(CONFIG_OVERLAY_D3D11_FILTER) += vf_overlay_d3d11.o framesync.o d3d11/overlay.o + OBJS-$(CONFIG_OVERLAY_CUDA_FILTER) += vf_overlay_cuda.o framesync.o vf_overlay_cuda.ptx.o \ + cuda/load_helper.o + OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER) += vf_overlay_opencl.o opencl.o \ +@@ -567,6 +568,7 @@ OBJS-$(CONFIG_TONEMAP_VIDEOTOOLBOX_FILTE + metal/utils.o + OBJS-$(CONFIG_TPAD_FILTER) += vf_tpad.o + OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o ++OBJS-$(CONFIG_TRANSPOSE_D3D11_FILTER) += vf_transpose_d3d11.o + OBJS-$(CONFIG_TRANSPOSE_CUDA_FILTER) += vf_transpose_cuda.o vf_transpose_cuda.ptx.o \ + cuda/load_helper.o + OBJS-$(CONFIG_TRANSPOSE_NPP_FILTER) += vf_transpose_npp.o +Index: FFmpeg/libavfilter/allfilters.c +=================================================================== +--- FFmpeg.orig/libavfilter/allfilters.c ++++ FFmpeg/libavfilter/allfilters.c +@@ -405,6 +405,7 @@ extern const FFFilter ff_vf_overlay_vaap + extern const FFFilter ff_vf_overlay_videotoolbox; + extern const FFFilter ff_vf_overlay_vulkan; + extern const FFFilter ff_vf_overlay_cuda; ++extern const FFFilter ff_vf_overlay_d3d11; + extern const FFFilter ff_vf_owdenoise; + extern const FFFilter ff_vf_pad; + extern const FFFilter ff_vf_pad_cuda; +@@ -525,6 +526,7 @@ extern const FFFilter ff_vf_tonemap_vide + extern const FFFilter ff_vf_tpad; + extern const FFFilter ff_vf_transpose; + extern const FFFilter ff_vf_transpose_cuda; ++extern const FFFilter ff_vf_transpose_d3d11; + extern const FFFilter ff_vf_transpose_npp; + extern const FFFilter ff_vf_transpose_opencl; + extern const FFFilter ff_vf_transpose_vaapi; +Index: FFmpeg/libavfilter/vf_overlay_d3d11.c +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/vf_overlay_d3d11.c +@@ -0,0 +1,972 @@ ++/* ++ * D3D11 overlay filter ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#ifndef COBJMACROS ++#define COBJMACROS ++#endif ++#include ++#include ++ ++#include "libavutil/common.h" ++#include "libavutil/eval.h" ++#include "libavutil/hwcontext.h" ++#include "libavutil/hwcontext_d3d11va.h" ++#include "libavutil/mem.h" ++#include "libavutil/opt.h" ++#include "libavutil/pixdesc.h" ++ ++#include "vf_overlay_d3d11.h" ++#include "d3d11_source.h" ++#include "filters.h" ++#include "framesync.h" ++#include "video.h" ++ ++#define MAIN 0 ++#define OVERLAY 1 ++ ++#define D3D11_RELEASE(p) do { if (p) { (p)->lpVtbl->Release(p); (p) = NULL; } } while (0) ++#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL3 ++#define D3DCOMPILE_OPTIMIZATION_LEVEL3 (1 << 15) ++#endif ++ ++enum var_name { ++ VAR_MAIN_IW, VAR_MW, ++ VAR_MAIN_IH, VAR_MH, ++ VAR_OVERLAY_IW, ++ VAR_OVERLAY_IH, ++ VAR_OVERLAY_X, VAR_OX, ++ VAR_OVERLAY_Y, VAR_OY, ++ VAR_OVERLAY_W, VAR_OW, ++ VAR_OVERLAY_H, VAR_OH, ++ VAR_VARS_NB ++}; ++ ++typedef HRESULT (WINAPI *D3DCompileProc)(LPCVOID, SIZE_T, LPCSTR, ++ const D3D_SHADER_MACRO *, ID3DInclude *, ++ LPCSTR, LPCSTR, UINT, UINT, ++ ID3D10Blob **, ID3D10Blob **); ++ ++typedef struct OverlayD3D11Context { ++ const AVClass *classCtx; ++ FFFrameSync fs; ++ int opt_repeatlast; ++ int opt_shortest; ++ int opt_eof_action; ++ ++ char *overlay_ox; ++ char *overlay_oy; ++ char *overlay_ow; ++ char *overlay_oh; ++ double var_values[VAR_VARS_NB]; ++ int ox, oy, ow, oh; ++ float alpha; ++ ++ ID3D11Device *device; ++ ID3D11DeviceContext *context; ++ HMODULE d3dcompiler; ++ D3DCompileProc D3DCompile; ++ ++ ID3D11ComputeShader *cs_bgra_y; ++ ID3D11ComputeShader *cs_bgra_uv; ++ ID3D11Buffer *params_buf; ++ ++ ID3D11Texture2D *main_tex; ++ ID3D11Texture2D *work_tex; ++ ID3D11Texture2D *overlay_tex; ++ int main_tex_w, main_tex_h; ++ int work_w, work_h; ++ int overlay_tex_w, overlay_tex_h; ++ DXGI_FORMAT overlay_tex_format; ++ int direct_main_srv; ++ int direct_overlay_srv; ++ int direct_output_uav; ++ ++ AVBufferRef *hw_device_ctx; ++ AVBufferRef *hw_frames_ctx_out; ++ ++ int width, height; ++ enum AVPixelFormat main_sw_format; ++} OverlayD3D11Context; ++ ++static const char *const var_names[] = { ++ "main_w", "W", ++ "main_h", "H", ++ "overlay_iw", ++ "overlay_ih", ++ "overlay_x", "x", ++ "overlay_y", "y", ++ "overlay_w", "w", ++ "overlay_h", "h", ++ NULL ++}; ++ ++static void overlay_d3d11_release_resources(OverlayD3D11Context *s) ++{ ++ D3D11_RELEASE(s->cs_bgra_y); ++ D3D11_RELEASE(s->cs_bgra_uv); ++ D3D11_RELEASE(s->params_buf); ++ D3D11_RELEASE(s->main_tex); ++ D3D11_RELEASE(s->work_tex); ++ D3D11_RELEASE(s->overlay_tex); ++ s->main_tex_w = s->main_tex_h = 0; ++ s->work_w = s->work_h = 0; ++ s->overlay_tex_w = s->overlay_tex_h = 0; ++ s->overlay_tex_format = DXGI_FORMAT_UNKNOWN; ++} ++ ++static int overlay_d3d11_eval_expr(AVFilterContext *ctx) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ double *var_values = s->var_values; ++ AVExpr *ox_expr = NULL, *oy_expr = NULL, *ow_expr = NULL, *oh_expr = NULL; ++ int ret = 0; ++ ++#define PARSE_EXPR(e, str) do { \ ++ ret = av_expr_parse(&(e), (str), var_names, NULL, NULL, NULL, NULL, 0, ctx); \ ++ if (ret < 0) { \ ++ av_log(ctx, AV_LOG_ERROR, "Error parsing expression '%s'.\n", (str)); \ ++ goto release; \ ++ } \ ++} while (0) ++ PARSE_EXPR(ox_expr, s->overlay_ox); ++ PARSE_EXPR(oy_expr, s->overlay_oy); ++ PARSE_EXPR(ow_expr, s->overlay_ow); ++ PARSE_EXPR(oh_expr, s->overlay_oh); ++#undef PARSE_EXPR ++ ++ var_values[VAR_OVERLAY_W] = var_values[VAR_OW] = av_expr_eval(ow_expr, var_values, NULL); ++ var_values[VAR_OVERLAY_H] = var_values[VAR_OH] = av_expr_eval(oh_expr, var_values, NULL); ++ var_values[VAR_OVERLAY_X] = var_values[VAR_OX] = av_expr_eval(ox_expr, var_values, NULL); ++ var_values[VAR_OVERLAY_Y] = var_values[VAR_OY] = av_expr_eval(oy_expr, var_values, NULL); ++ var_values[VAR_OVERLAY_W] = var_values[VAR_OW] = av_expr_eval(ow_expr, var_values, NULL); ++ var_values[VAR_OVERLAY_H] = var_values[VAR_OH] = av_expr_eval(oh_expr, var_values, NULL); ++ ++release: ++ av_expr_free(ox_expr); ++ av_expr_free(oy_expr); ++ av_expr_free(ow_expr); ++ av_expr_free(oh_expr); ++ return ret; ++} ++ ++static int compile_shader(AVFilterContext *ctx, const char *entry, ID3D11ComputeShader **shader) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ ID3D10Blob *cs_blob = NULL; ++ ID3D10Blob *err_blob = NULL; ++ HRESULT hr; ++ ++ hr = s->D3DCompile(ff_source_overlay_hlsl, strlen(ff_source_overlay_hlsl), ++ NULL, NULL, NULL, entry, "cs_5_0", ++ D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &cs_blob, &err_blob); ++ if (FAILED(hr)) { ++ if (err_blob) { ++ av_log(ctx, AV_LOG_ERROR, "Failed compiling overlay shader %s: %.*s\n", ++ entry, (int)err_blob->lpVtbl->GetBufferSize(err_blob), ++ (char *)err_blob->lpVtbl->GetBufferPointer(err_blob)); ++ } else { ++ av_log(ctx, AV_LOG_ERROR, "Failed compiling overlay shader %s: HRESULT 0x%lX\n", ++ entry, (unsigned long)hr); ++ } ++ D3D11_RELEASE(err_blob); ++ return AVERROR_EXTERNAL; ++ } ++ ++ hr = s->device->lpVtbl->CreateComputeShader(s->device, ++ cs_blob->lpVtbl->GetBufferPointer(cs_blob), ++ cs_blob->lpVtbl->GetBufferSize(cs_blob), ++ NULL, shader); ++ D3D11_RELEASE(cs_blob); ++ D3D11_RELEASE(err_blob); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating overlay shader %s: HRESULT 0x%lX\n", ++ entry, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int overlay_d3d11_init_shader(AVFilterContext *ctx) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ D3D11_BUFFER_DESC bd = { 0 }; ++ HRESULT hr; ++ int ret; ++ ++ if (s->cs_bgra_y) ++ return 0; ++ ++ if (!s->d3dcompiler) { ++ s->d3dcompiler = LoadLibraryA("d3dcompiler_47.dll"); ++ if (!s->d3dcompiler) ++ s->d3dcompiler = LoadLibraryA("d3dcompiler_43.dll"); ++ } ++ if (!s->d3dcompiler) { ++ av_log(ctx, AV_LOG_ERROR, "Failed loading d3dcompiler DLL\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ s->D3DCompile = (D3DCompileProc)GetProcAddress(s->d3dcompiler, "D3DCompile"); ++ if (!s->D3DCompile) { ++ av_log(ctx, AV_LOG_ERROR, "Failed loading D3DCompile\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ if ((ret = compile_shader(ctx, "bgra_y", &s->cs_bgra_y)) < 0 || ++ (ret = compile_shader(ctx, "bgra_uv", &s->cs_bgra_uv)) < 0) ++ return ret; ++ ++ bd.ByteWidth = sizeof(OverlayD3D11Params); ++ bd.Usage = D3D11_USAGE_DYNAMIC; ++ bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; ++ bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ++ hr = s->device->lpVtbl->CreateBuffer(s->device, &bd, NULL, &s->params_buf); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating overlay constant buffer: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ return 0; ++} ++ ++static int ensure_texture(AVFilterContext *ctx, ID3D11Texture2D **tex, ++ int *cur_w, int *cur_h, DXGI_FORMAT *cur_fmt, ++ int w, int h, DXGI_FORMAT fmt, UINT bind_flags) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ D3D11_TEXTURE2D_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ if (*tex && *cur_w == w && *cur_h == h && (!cur_fmt || *cur_fmt == fmt)) ++ return 0; ++ ++ D3D11_RELEASE(*tex); ++ ++ desc.Width = w; ++ desc.Height = h; ++ desc.MipLevels = 1; ++ desc.ArraySize = 1; ++ desc.Format = fmt; ++ desc.SampleDesc.Count = 1; ++ desc.Usage = D3D11_USAGE_DEFAULT; ++ desc.BindFlags = bind_flags; ++ ++ hr = s->device->lpVtbl->CreateTexture2D(s->device, &desc, NULL, tex); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating overlay texture %dx%d format %u: HRESULT 0x%lX\n", ++ w, h, fmt, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ *cur_w = w; ++ *cur_h = h; ++ if (cur_fmt) ++ *cur_fmt = fmt; ++ return 0; ++} ++ ++static void fill_tex2d_srv_desc(ID3D11Texture2D *tex, UINT subresource, ++ DXGI_FORMAT fmt, D3D11_SHADER_RESOURCE_VIEW_DESC *desc) ++{ ++ D3D11_TEXTURE2D_DESC tex_desc; ++ UINT mip_slice = 0; ++ UINT array_slice = 0; ++ ++ ID3D11Texture2D_GetDesc(tex, &tex_desc); ++ if (tex_desc.MipLevels) { ++ mip_slice = subresource % tex_desc.MipLevels; ++ array_slice = subresource / tex_desc.MipLevels; ++ } ++ ++ desc->Format = fmt; ++ desc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; ++ desc->Texture2DArray.MostDetailedMip = mip_slice; ++ desc->Texture2DArray.MipLevels = 1; ++ desc->Texture2DArray.FirstArraySlice = array_slice; ++ desc->Texture2DArray.ArraySize = 1; ++} ++ ++static void fill_tex2d_uav_desc(ID3D11Texture2D *tex, UINT subresource, ++ DXGI_FORMAT fmt, D3D11_UNORDERED_ACCESS_VIEW_DESC *desc) ++{ ++ D3D11_TEXTURE2D_DESC tex_desc; ++ UINT mip_slice = 0; ++ UINT array_slice = 0; ++ ++ ID3D11Texture2D_GetDesc(tex, &tex_desc); ++ if (tex_desc.MipLevels) { ++ mip_slice = subresource % tex_desc.MipLevels; ++ array_slice = subresource / tex_desc.MipLevels; ++ } ++ ++ desc->Format = fmt; ++ desc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; ++ desc->Texture2DArray.MipSlice = mip_slice; ++ desc->Texture2DArray.FirstArraySlice = array_slice; ++ desc->Texture2DArray.ArraySize = 1; ++} ++ ++static int create_nv12_uav_view(AVFilterContext *ctx, ID3D11Texture2D *tex, ++ UINT subresource, int plane, ++ int log_level, ++ ID3D11UnorderedAccessView **uav) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ D3D11_UNORDERED_ACCESS_VIEW_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ fill_tex2d_uav_desc(tex, subresource, plane ? DXGI_FORMAT_R8G8_UNORM : DXGI_FORMAT_R8_UNORM, &desc); ++ ++ hr = s->device->lpVtbl->CreateUnorderedAccessView(s->device, ++ (ID3D11Resource *)tex, ++ &desc, uav); ++ if (FAILED(hr)) { ++ av_log(ctx, log_level, "Failed creating NV12 plane %d UAV: HRESULT 0x%lX\n", ++ plane, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int create_nv12_uav(AVFilterContext *ctx, ID3D11Texture2D *tex, int plane, ++ ID3D11UnorderedAccessView **uav) ++{ ++ return create_nv12_uav_view(ctx, tex, 0, plane, AV_LOG_ERROR, uav); ++} ++ ++static int create_srv_view(AVFilterContext *ctx, ID3D11Texture2D *tex, ++ UINT subresource, DXGI_FORMAT fmt, int plane, ++ int log_level, ++ ID3D11ShaderResourceView **srv) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ D3D11_SHADER_RESOURCE_VIEW_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ if (plane >= 0) ++ fmt = plane ? DXGI_FORMAT_R8G8_UNORM : DXGI_FORMAT_R8_UNORM; ++ fill_tex2d_srv_desc(tex, subresource, fmt, &desc); ++ ++ hr = s->device->lpVtbl->CreateShaderResourceView(s->device, ++ (ID3D11Resource *)tex, ++ &desc, srv); ++ if (FAILED(hr)) { ++ av_log(ctx, log_level, "Failed creating overlay SRV: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int create_srv(AVFilterContext *ctx, ID3D11Texture2D *tex, DXGI_FORMAT fmt, ++ int plane, ID3D11ShaderResourceView **srv) ++{ ++ return create_srv_view(ctx, tex, 0, fmt, plane, AV_LOG_ERROR, srv); ++} ++ ++static void copy_frame_to_texture(OverlayD3D11Context *s, AVFrame *src, ID3D11Texture2D *dst) ++{ ++ ID3D11Texture2D *src_tex = (ID3D11Texture2D *)src->data[0]; ++ UINT src_sub = (UINT)(uintptr_t)src->data[1]; ++ D3D11_BOX box = { 0, 0, 0, src->width, src->height, 1 }; ++ ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst, 0, ++ 0, 0, 0, (ID3D11Resource *)src_tex, ++ src_sub, &box); ++} ++ ++static void copy_texture_to_frame(OverlayD3D11Context *s, ID3D11Texture2D *src, AVFrame *dst) ++{ ++ ID3D11Texture2D *dst_tex = (ID3D11Texture2D *)dst->data[0]; ++ UINT dst_sub = (UINT)(uintptr_t)dst->data[1]; ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst_tex, ++ dst_sub, 0, 0, 0, (ID3D11Resource *)src, ++ 0, NULL); ++} ++ ++static void copy_frame_to_frame(OverlayD3D11Context *s, AVFrame *src, AVFrame *dst) ++{ ++ ID3D11Texture2D *src_tex = (ID3D11Texture2D *)src->data[0]; ++ ID3D11Texture2D *dst_tex = (ID3D11Texture2D *)dst->data[0]; ++ UINT src_sub = (UINT)(uintptr_t)src->data[1]; ++ UINT dst_sub = (UINT)(uintptr_t)dst->data[1]; ++ D3D11_BOX box = { 0, 0, 0, src->width, src->height, 1 }; ++ ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst_tex, ++ dst_sub, 0, 0, 0, (ID3D11Resource *)src_tex, ++ src_sub, &box); ++} ++ ++static int create_overlay_frame_srvs(AVFilterContext *ctx, AVFrame *overlay, ++ ID3D11ShaderResourceView **srvs) ++{ ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)overlay->data[0]; ++ UINT subresource = (UINT)(uintptr_t)overlay->data[1]; ++ ++ return create_srv_view(ctx, tex, subresource, DXGI_FORMAT_B8G8R8A8_UNORM, -1, ++ AV_LOG_DEBUG, &srvs[0]); ++} ++ ++static int create_main_frame_srvs(AVFilterContext *ctx, AVFrame *main, ++ ID3D11ShaderResourceView **srvs) ++{ ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)main->data[0]; ++ UINT subresource = (UINT)(uintptr_t)main->data[1]; ++ int ret; ++ ++ ret = create_srv_view(ctx, tex, subresource, DXGI_FORMAT_NV12, 0, ++ AV_LOG_DEBUG, &srvs[1]); ++ if (ret < 0) ++ return ret; ++ ret = create_srv_view(ctx, tex, subresource, DXGI_FORMAT_NV12, 1, ++ AV_LOG_DEBUG, &srvs[2]); ++ if (ret < 0) ++ D3D11_RELEASE(srvs[1]); ++ return ret; ++} ++ ++static int prepare_main_srvs(AVFilterContext *ctx, AVFrame *main, ++ ID3D11ShaderResourceView **srvs) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ int ret; ++ ++ if (s->direct_main_srv) { ++ ret = create_main_frame_srvs(ctx, main, srvs); ++ if (ret >= 0) { ++ if (s->direct_main_srv < 0) { ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader main input: direct SRV\n"); ++ s->direct_main_srv = 1; ++ } ++ return 0; ++ } ++ if (s->direct_main_srv < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader main input: copied to internal SRV texture\n"); ++ s->direct_main_srv = 0; ++ } ++ ++ ret = ensure_texture(ctx, &s->main_tex, &s->main_tex_w, &s->main_tex_h, NULL, ++ s->width, s->height, DXGI_FORMAT_NV12, ++ D3D11_BIND_SHADER_RESOURCE); ++ if (ret < 0) ++ return ret; ++ copy_frame_to_texture(s, main, s->main_tex); ++ ++ ret = create_srv(ctx, s->main_tex, DXGI_FORMAT_NV12, 0, &srvs[1]); ++ if (ret < 0) ++ return ret; ++ ret = create_srv(ctx, s->main_tex, DXGI_FORMAT_NV12, 1, &srvs[2]); ++ if (ret < 0) ++ D3D11_RELEASE(srvs[1]); ++ return ret; ++} ++ ++static int prepare_overlay_srvs(AVFilterContext *ctx, AVFrame *overlay, ++ ID3D11ShaderResourceView **srvs) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ int ret; ++ ++ if (s->direct_overlay_srv) { ++ ret = create_overlay_frame_srvs(ctx, overlay, srvs); ++ if (ret >= 0) { ++ if (s->direct_overlay_srv < 0) { ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader overlay input: direct SRV\n"); ++ s->direct_overlay_srv = 1; ++ } ++ return 0; ++ } ++ if (s->direct_overlay_srv < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader overlay input: copied to internal SRV texture\n"); ++ s->direct_overlay_srv = 0; ++ } ++ ++ ret = ensure_texture(ctx, &s->overlay_tex, &s->overlay_tex_w, &s->overlay_tex_h, ++ &s->overlay_tex_format, overlay->width, overlay->height, ++ DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_SHADER_RESOURCE); ++ if (ret < 0) ++ return ret; ++ copy_frame_to_texture(s, overlay, s->overlay_tex); ++ ++ return create_srv(ctx, s->overlay_tex, DXGI_FORMAT_B8G8R8A8_UNORM, -1, &srvs[0]); ++} ++ ++static int prepare_output_uavs(AVFilterContext *ctx, AVFrame *out, ++ ID3D11UnorderedAccessView **uavs, int *direct) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)out->data[0]; ++ UINT subresource = (UINT)(uintptr_t)out->data[1]; ++ int ret; ++ ++ *direct = 0; ++ if (s->direct_output_uav) { ++ ret = create_nv12_uav_view(ctx, tex, subresource, 0, AV_LOG_DEBUG, &uavs[0]); ++ if (ret >= 0) ++ ret = create_nv12_uav_view(ctx, tex, subresource, 1, AV_LOG_DEBUG, &uavs[1]); ++ if (ret >= 0) { ++ if (s->direct_output_uav < 0) { ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: direct UAV\n"); ++ s->direct_output_uav = 1; ++ } ++ *direct = 1; ++ return 0; ++ } ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ if (s->direct_output_uav < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: copied from internal UAV texture\n"); ++ s->direct_output_uav = 0; ++ } ++ ++ ret = ensure_texture(ctx, &s->work_tex, &s->work_w, &s->work_h, NULL, ++ s->width, s->height, DXGI_FORMAT_NV12, ++ D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS); ++ if (ret < 0) ++ return ret; ++ ret = create_nv12_uav(ctx, s->work_tex, 0, &uavs[0]); ++ if (ret < 0) ++ return ret; ++ ret = create_nv12_uav(ctx, s->work_tex, 1, &uavs[1]); ++ if (ret < 0) ++ D3D11_RELEASE(uavs[0]); ++ return ret; ++} ++ ++static int probe_output_uav_pool(AVFilterContext *ctx, AVBufferRef *frames_ref) ++{ ++ AVHWFramesContext *frames_ctx = (AVHWFramesContext *)frames_ref->data; ++ AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx; ++ AVFrame *frame = NULL; ++ ID3D11Texture2D *tex; ++ ID3D11UnorderedAccessView *uavs[2] = { NULL, NULL }; ++ UINT subresource; ++ int ret; ++ ++ if (frames_hwctx->texture) { ++ tex = frames_hwctx->texture; ++ subresource = 0; ++ } else { ++ frame = av_frame_alloc(); ++ if (!frame) ++ return AVERROR(ENOMEM); ++ ++ ret = av_hwframe_get_buffer(frames_ref, frame, 0); ++ if (ret < 0) ++ goto done; ++ ++ tex = (ID3D11Texture2D *)frame->data[0]; ++ subresource = (UINT)(uintptr_t)frame->data[1]; ++ } ++ ++ ret = create_nv12_uav_view(ctx, tex, subresource, 0, AV_LOG_DEBUG, &uavs[0]); ++ if (ret >= 0) ++ ret = create_nv12_uav_view(ctx, tex, subresource, 1, AV_LOG_DEBUG, &uavs[1]); ++ ++done: ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ av_frame_free(&frame); ++ return ret; ++} ++ ++ ++static int overlay_d3d11_blend(FFFrameSync *fs) ++{ ++ AVFilterContext *ctx = fs->parent; ++ OverlayD3D11Context *s = ctx->priv; ++ AVFilterLink *outlink = ctx->outputs[0]; ++ AVFrame *main = NULL, *overlay = NULL, *out = NULL; ++ ID3D11UnorderedAccessView *uavs[2] = { NULL, NULL }; ++ ID3D11ShaderResourceView *srvs[3] = { NULL }; ++ ID3D11ComputeShader *null_cs = NULL; ++ ID3D11UnorderedAccessView *null_uavs[2] = { NULL, NULL }; ++ ID3D11ShaderResourceView *null_srvs[3] = { NULL }; ++ ID3D11Buffer *null_cb[1] = { NULL }; ++ D3D11_MAPPED_SUBRESOURCE mapped; ++ OverlayD3D11Params params; ++ int direct_output = 0; ++ int ret; ++ HRESULT hr; ++ ++ ret = ff_framesync_dualinput_get(fs, &main, &overlay); ++ if (ret < 0) ++ return ret; ++ if (!main) ++ return AVERROR_BUG; ++ ++ out = av_frame_alloc(); ++ if (!out) ++ return AVERROR(ENOMEM); ++ ++ ret = av_hwframe_get_buffer(s->hw_frames_ctx_out, out, 0); ++ if (ret < 0) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to get output frame from pool\n"); ++ goto fail; ++ } ++ ++ ret = av_frame_copy_props(out, main); ++ if (ret < 0) ++ goto fail; ++ out->width = s->width; ++ out->height = s->height; ++ out->format = AV_PIX_FMT_D3D11; ++ ++ if (!overlay) { ++ copy_frame_to_frame(s, main, out); ++ av_frame_free(&main); ++ return ff_filter_frame(outlink, out); ++ } ++ ++ if ((s->ox & 1) || (s->oy & 1)) ++ av_log(ctx, AV_LOG_WARNING, "Overlay position should be even for NV12 chroma alignment\n"); ++ ++ ret = prepare_output_uavs(ctx, out, uavs, &direct_output); ++ if (ret < 0) ++ goto fail; ++ ++ ret = prepare_main_srvs(ctx, main, srvs); ++ if (ret < 0) ++ goto fail; ++ ++ ret = prepare_overlay_srvs(ctx, overlay, srvs); ++ if (ret < 0) ++ goto fail; ++ ++ params.dst_w = s->width; ++ params.dst_h = s->height; ++ params.ov_x = s->ox; ++ params.ov_y = s->oy; ++ params.ov_w = s->ow; ++ params.ov_h = s->oh; ++ params.ov_src_w = overlay->width; ++ params.ov_src_h = overlay->height; ++ params.alpha = s->alpha; ++ ++ hr = s->context->lpVtbl->Map(s->context, (ID3D11Resource *)s->params_buf, ++ 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed mapping overlay constant buffer: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ memcpy(mapped.pData, ¶ms, sizeof(params)); ++ s->context->lpVtbl->Unmap(s->context, (ID3D11Resource *)s->params_buf, 0); ++ ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, &s->params_buf); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, uavs, NULL); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 3, srvs); ++ ++ s->context->lpVtbl->CSSetShader(s->context, s->cs_bgra_y, NULL, 0); ++ s->context->lpVtbl->Dispatch(s->context, (s->width + OVERLAY_D3D11_THREAD_GROUP_X - 1) / OVERLAY_D3D11_THREAD_GROUP_X, ++ (s->height + OVERLAY_D3D11_THREAD_GROUP_Y - 1) / OVERLAY_D3D11_THREAD_GROUP_Y, 1); ++ ++ s->context->lpVtbl->CSSetShader(s->context, s->cs_bgra_uv, NULL, 0); ++ s->context->lpVtbl->Dispatch(s->context, (((s->width + 1) >> 1) + OVERLAY_D3D11_THREAD_GROUP_X - 1) / OVERLAY_D3D11_THREAD_GROUP_X, ++ (((s->height + 1) >> 1) + OVERLAY_D3D11_THREAD_GROUP_Y - 1) / OVERLAY_D3D11_THREAD_GROUP_Y, 1); ++ ++ s->context->lpVtbl->CSSetShader(s->context, null_cs, NULL, 0); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, null_uavs, NULL); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 3, null_srvs); ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, null_cb); ++ ++ if (!direct_output) ++ copy_texture_to_frame(s, s->work_tex, out); ++ ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ D3D11_RELEASE(srvs[0]); ++ D3D11_RELEASE(srvs[1]); ++ D3D11_RELEASE(srvs[2]); ++ av_frame_free(&main); ++ ++ return ff_filter_frame(outlink, out); ++ ++fail: ++ s->context->lpVtbl->CSSetShader(s->context, null_cs, NULL, 0); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, null_uavs, NULL); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 3, null_srvs); ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, null_cb); ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ D3D11_RELEASE(srvs[0]); ++ D3D11_RELEASE(srvs[1]); ++ D3D11_RELEASE(srvs[2]); ++ av_frame_free(&main); ++ av_frame_free(&out); ++ return ret; ++} ++ ++static int overlay_d3d11_config_input_main(AVFilterLink *inlink) ++{ ++ AVFilterContext *ctx = inlink->dst; ++ OverlayD3D11Context *s = ctx->priv; ++ ++ s->var_values[VAR_MAIN_IW] = s->var_values[VAR_MW] = inlink->w; ++ s->var_values[VAR_MAIN_IH] = s->var_values[VAR_MH] = inlink->h; ++ ++ return 0; ++} ++ ++static int overlay_d3d11_config_input_overlay(AVFilterLink *inlink) ++{ ++ AVFilterContext *ctx = inlink->dst; ++ OverlayD3D11Context *s = ctx->priv; ++ int ret; ++ ++ s->var_values[VAR_OVERLAY_IW] = inlink->w; ++ s->var_values[VAR_OVERLAY_IH] = inlink->h; ++ ++ ret = overlay_d3d11_eval_expr(ctx); ++ if (ret < 0) ++ return ret; ++ ++ s->ox = (int)s->var_values[VAR_OX]; ++ s->oy = (int)s->var_values[VAR_OY]; ++ s->ow = (int)s->var_values[VAR_OW]; ++ s->oh = (int)s->var_values[VAR_OH]; ++ ++ if (s->ow <= 0 || s->oh <= 0) { ++ av_log(ctx, AV_LOG_ERROR, "Invalid overlay dimensions %dx%d\n", s->ow, s->oh); ++ return AVERROR(EINVAL); ++ } ++ ++ return 0; ++} ++ ++static int overlay_d3d11_config_output(AVFilterLink *outlink) ++{ ++ AVFilterContext *ctx = outlink->src; ++ OverlayD3D11Context *s = ctx->priv; ++ AVFilterLink *mainlink = ctx->inputs[MAIN]; ++ AVFilterLink *overlaylink = ctx->inputs[OVERLAY]; ++ FilterLink *mainl = ff_filter_link(mainlink); ++ FilterLink *overlayl = ff_filter_link(overlaylink); ++ FilterLink *outl = ff_filter_link(outlink); ++ AVHWFramesContext *main_frames_ctx; ++ AVHWFramesContext *overlay_frames_ctx; ++ AVHWFramesContext *frames_ctx; ++ AVD3D11VAFramesContext *frames_hwctx; ++ AVHWDeviceContext *hwctx; ++ AVD3D11VADeviceContext *d3d11_hwctx; ++ int ret; ++ ++ overlay_d3d11_release_resources(s); ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ ++ if (!mainl->hw_frames_ctx || !overlayl->hw_frames_ctx) { ++ av_log(ctx, AV_LOG_ERROR, "Both inputs must have D3D11 hw_frames_ctx\n"); ++ return AVERROR(EINVAL); ++ } ++ ++ main_frames_ctx = (AVHWFramesContext *)mainl->hw_frames_ctx->data; ++ overlay_frames_ctx = (AVHWFramesContext *)overlayl->hw_frames_ctx->data; ++ if (main_frames_ctx->device_ref->data != overlay_frames_ctx->device_ref->data) { ++ av_log(ctx, AV_LOG_ERROR, "Main and overlay inputs must use the same D3D11 device\n"); ++ return AVERROR(EINVAL); ++ } ++ ++ s->main_sw_format = main_frames_ctx->sw_format; ++ ++ if (s->main_sw_format != AV_PIX_FMT_NV12) { ++ av_log(ctx, AV_LOG_ERROR, "overlay_d3d11 supports only NV12 main/output, got %s\n", ++ av_get_pix_fmt_name(s->main_sw_format)); ++ return AVERROR(ENOSYS); ++ } ++ if (overlay_frames_ctx->sw_format != AV_PIX_FMT_BGRA) { ++ av_log(ctx, AV_LOG_ERROR, "overlay_d3d11 supports only BGRA overlay, got %s\n", ++ av_get_pix_fmt_name(overlay_frames_ctx->sw_format)); ++ return AVERROR(ENOSYS); ++ } ++ ++ if (!s->hw_device_ctx) { ++ s->hw_device_ctx = av_buffer_ref(main_frames_ctx->device_ref); ++ if (!s->hw_device_ctx) ++ return AVERROR(ENOMEM); ++ } ++ ++ hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data; ++ d3d11_hwctx = (AVD3D11VADeviceContext *)hwctx->hwctx; ++ s->device = d3d11_hwctx->device; ++ s->context = d3d11_hwctx->device_context; ++ ++ s->width = mainlink->w; ++ s->height = mainlink->h; ++ outlink->w = s->width; ++ outlink->h = s->height; ++ outlink->time_base = mainlink->time_base; ++ ++ ret = overlay_d3d11_init_shader(ctx); ++ if (ret < 0) ++ return ret; ++ ++ s->direct_output_uav = -1; ++ s->direct_main_srv = -1; ++ s->direct_overlay_srv = -1; ++ ++ for (int direct = 1; direct >= 0; direct--) { ++ s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx); ++ if (!s->hw_frames_ctx_out) ++ return AVERROR(ENOMEM); ++ ++ frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx_out->data; ++ frames_ctx->format = AV_PIX_FMT_D3D11; ++ frames_ctx->sw_format = AV_PIX_FMT_NV12; ++ frames_ctx->width = s->width; ++ frames_ctx->height = s->height; ++ frames_ctx->initial_pool_size = 10; ++ if (ctx->extra_hw_frames > 0) ++ frames_ctx->initial_pool_size += ctx->extra_hw_frames; ++ ++ frames_hwctx = frames_ctx->hwctx; ++ frames_hwctx->MiscFlags = 0; ++ frames_hwctx->BindFlags = direct ? D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE | ++ D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_VIDEO_ENCODER ++ : D3D11_BIND_RENDER_TARGET | D3D11_BIND_VIDEO_ENCODER; ++ ++ ret = av_hwframe_ctx_init(s->hw_frames_ctx_out); ++ if (ret >= 0 && direct) { ++ ret = probe_output_uav_pool(ctx, s->hw_frames_ctx_out); ++ if (ret >= 0) { ++ s->direct_output_uav = 1; ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: direct UAV\n"); ++ break; ++ } ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: copied from internal UAV texture\n"); ++ continue; ++ } else if (ret >= 0) { ++ s->direct_output_uav = 0; ++ break; ++ } ++ ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ if (direct) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: UAV encoder pool rejected, using internal UAV texture\n"); ++ } ++ if (ret < 0) ++ return ret; ++ ++ av_buffer_unref(&outl->hw_frames_ctx); ++ outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out); ++ if (!outl->hw_frames_ctx) ++ return AVERROR(ENOMEM); ++ ++ ret = ff_framesync_init_dualinput(&s->fs, ctx); ++ if (ret < 0) ++ return ret; ++ s->fs.on_event = overlay_d3d11_blend; ++ s->fs.time_base = outlink->time_base; ++ s->fs.opt_repeatlast = s->opt_repeatlast; ++ s->fs.opt_shortest = s->opt_shortest; ++ s->fs.opt_eof_action = s->opt_eof_action; ++ ++ return ff_framesync_configure(&s->fs); ++} ++ ++static av_cold int overlay_d3d11_init(AVFilterContext *ctx) ++{ ++ return 0; ++} ++ ++static int overlay_d3d11_activate(AVFilterContext *ctx) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ return ff_framesync_activate(&s->fs); ++} ++ ++static av_cold void overlay_d3d11_uninit(AVFilterContext *ctx) ++{ ++ OverlayD3D11Context *s = ctx->priv; ++ ++ ff_framesync_uninit(&s->fs); ++ overlay_d3d11_release_resources(s); ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ av_buffer_unref(&s->hw_device_ctx); ++ if (s->d3dcompiler) ++ FreeLibrary(s->d3dcompiler); ++ av_freep(&s->overlay_ox); ++ av_freep(&s->overlay_oy); ++ av_freep(&s->overlay_ow); ++ av_freep(&s->overlay_oh); ++} ++ ++#define OFFSET(x) offsetof(OverlayD3D11Context, x) ++#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) ++ ++static const AVOption overlay_d3d11_options[] = { ++ { "x", "Overlay x position", OFFSET(overlay_ox), AV_OPT_TYPE_STRING, { .str = "0" }, 0, 255, FLAGS }, ++ { "y", "Overlay y position", OFFSET(overlay_oy), AV_OPT_TYPE_STRING, { .str = "0" }, 0, 255, FLAGS }, ++ { "w", "Overlay width", OFFSET(overlay_ow), AV_OPT_TYPE_STRING, { .str = "overlay_iw" }, 0, 255, FLAGS }, ++ { "h", "Overlay height", OFFSET(overlay_oh), AV_OPT_TYPE_STRING, { .str = "overlay_ih*overlay_w/overlay_iw" }, 0, 255, FLAGS }, ++ { "alpha", "Overlay global alpha", OFFSET(alpha), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 1.0, FLAGS }, ++ { "eof_action", "Action to take when encountering EOF from secondary input", ++ OFFSET(opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT }, ++ EOF_ACTION_REPEAT, EOF_ACTION_PASS, FLAGS, .unit = "eof_action" }, ++ { "repeat", "Repeat the previous frame", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, 0, 0, FLAGS, .unit = "eof_action" }, ++ { "endall", "End both streams", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, 0, 0, FLAGS, .unit = "eof_action" }, ++ { "pass", "Pass through the main input", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, 0, 0, FLAGS, .unit = "eof_action" }, ++ { "shortest", "force termination when the shortest input terminates", OFFSET(opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, ++ { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(opt_repeatlast), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, ++ { NULL } ++}; ++ ++AVFILTER_DEFINE_CLASS(overlay_d3d11); ++ ++static const AVFilterPad overlay_d3d11_inputs[] = { ++ { ++ .name = "main", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .config_props = overlay_d3d11_config_input_main, ++ }, ++ { ++ .name = "overlay", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .config_props = overlay_d3d11_config_input_overlay, ++ }, ++}; ++ ++static const AVFilterPad overlay_d3d11_outputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .config_props = overlay_d3d11_config_output, ++ }, ++}; ++ ++const FFFilter ff_vf_overlay_d3d11 = { ++ .p.name = "overlay_d3d11", ++ .p.description = NULL_IF_CONFIG_SMALL("Overlay one D3D11 video on top of another"), ++ .priv_size = sizeof(OverlayD3D11Context), ++ .p.priv_class = &overlay_d3d11_class, ++ .init = &overlay_d3d11_init, ++ .uninit = &overlay_d3d11_uninit, ++ .activate = &overlay_d3d11_activate, ++ FILTER_INPUTS(overlay_d3d11_inputs), ++ FILTER_OUTPUTS(overlay_d3d11_outputs), ++ FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11), ++ .p.flags = AVFILTER_FLAG_HWDEVICE, ++ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, ++}; +Index: FFmpeg/libavfilter/vf_overlay_d3d11.h +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/vf_overlay_d3d11.h +@@ -0,0 +1,44 @@ ++/* ++ * D3D11 overlay filter shader constants ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVFILTER_VF_OVERLAY_D3D11_H ++#define AVFILTER_VF_OVERLAY_D3D11_H ++ ++#define OVERLAY_D3D11_THREAD_GROUP_X 16 ++#define OVERLAY_D3D11_THREAD_GROUP_Y 16 ++ ++typedef struct OverlayD3D11Params { ++ int dst_w; ++ int dst_h; ++ int ov_x; ++ int ov_y; ++ int ov_w; ++ int ov_h; ++ int ov_src_w; ++ int ov_src_h; ++ float alpha; ++ float pad0; ++ float pad1; ++ float pad2; ++} OverlayD3D11Params; ++ ++#endif /* AVFILTER_VF_OVERLAY_D3D11_H */ +Index: FFmpeg/libavfilter/vf_transpose_d3d11.c +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/vf_transpose_d3d11.c +@@ -0,0 +1,529 @@ ++/* ++ * D3D11 transpose filter ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++ ++#include "libavutil/hwcontext.h" ++#include "libavutil/hwcontext_d3d11va.h" ++#include "libavutil/opt.h" ++#include "libavutil/pixdesc.h" ++ ++#include "filters.h" ++#include "transpose.h" ++#include "video.h" ++ ++#define D3D11_RELEASE(p) do { if (p) { (p)->lpVtbl->Release(p); (p) = NULL; } } while (0) ++ ++typedef struct TransposeD3D11Context { ++ const AVClass *classCtx; ++ ++ int passthrough; ++ int dir; ++ ++ D3D11_VIDEO_PROCESSOR_ROTATION rotation; ++ int rotation_enable; ++ int mirror_enable; ++ BOOL flip_h; ++ BOOL flip_v; ++ ++ ID3D11Device *device; ++ ID3D11DeviceContext *context; ++ ID3D11VideoDevice *videoDevice; ++ ID3D11VideoProcessor *processor; ++ ID3D11VideoProcessorEnumerator *enumerator; ++ ++ AVBufferRef *hw_device_ctx; ++ AVBufferRef *hw_frames_ctx_out; ++ ++ int input_width, input_height; ++ int output_width, output_height; ++ DXGI_FORMAT input_format; ++ int configured; ++} TransposeD3D11Context; ++ ++static void transpose_d3d11_release_resources(TransposeD3D11Context *s) ++{ ++ D3D11_RELEASE(s->processor); ++ D3D11_RELEASE(s->enumerator); ++ D3D11_RELEASE(s->videoDevice); ++} ++ ++static int transpose_d3d11_set_direction(AVFilterContext *ctx) ++{ ++ TransposeD3D11Context *s = ctx->priv; ++ ++ s->rotation = D3D11_VIDEO_PROCESSOR_ROTATION_IDENTITY; ++ s->rotation_enable = 0; ++ s->mirror_enable = 0; ++ s->flip_h = FALSE; ++ s->flip_v = FALSE; ++ ++ switch (s->dir) { ++ case TRANSPOSE_CCLOCK_FLIP: ++ s->rotation = D3D11_VIDEO_PROCESSOR_ROTATION_270; ++ s->rotation_enable = 1; ++ s->mirror_enable = 1; ++ s->flip_v = TRUE; ++ break; ++ case TRANSPOSE_CLOCK: ++ s->rotation = D3D11_VIDEO_PROCESSOR_ROTATION_90; ++ s->rotation_enable = 1; ++ break; ++ case TRANSPOSE_CCLOCK: ++ s->rotation = D3D11_VIDEO_PROCESSOR_ROTATION_270; ++ s->rotation_enable = 1; ++ break; ++ case TRANSPOSE_CLOCK_FLIP: ++ s->rotation = D3D11_VIDEO_PROCESSOR_ROTATION_90; ++ s->rotation_enable = 1; ++ s->mirror_enable = 1; ++ s->flip_v = TRUE; ++ break; ++ case TRANSPOSE_REVERSAL: ++ s->rotation = D3D11_VIDEO_PROCESSOR_ROTATION_180; ++ s->rotation_enable = 1; ++ break; ++ case TRANSPOSE_HFLIP: ++ s->mirror_enable = 1; ++ s->flip_h = TRUE; ++ break; ++ case TRANSPOSE_VFLIP: ++ s->mirror_enable = 1; ++ s->flip_v = TRUE; ++ break; ++ default: ++ av_log(ctx, AV_LOG_ERROR, "Unsupported transpose direction %d\n", s->dir); ++ return AVERROR(EINVAL); ++ } ++ ++ return 0; ++} ++ ++static int transpose_d3d11_configure_processor(TransposeD3D11Context *s, AVFilterContext *ctx) ++{ ++ AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data; ++ AVD3D11VADeviceContext *d3d11_hwctx = (AVD3D11VADeviceContext *)hwctx->hwctx; ++ D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc = { ++ .InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE, ++ .InputWidth = s->input_width, ++ .InputHeight = s->input_height, ++ .OutputWidth = s->output_width, ++ .OutputHeight = s->output_height, ++ .Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL, ++ }; ++ D3D11_VIDEO_PROCESSOR_CAPS caps = { 0 }; ++ HRESULT hr; ++ ++ s->device = d3d11_hwctx->device; ++ s->context = d3d11_hwctx->device_context; ++ ++ hr = s->device->lpVtbl->QueryInterface(s->device, &IID_ID3D11VideoDevice, ++ (void **)&s->videoDevice); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to get D3D11 video device interface: HRESULT 0x%lX\n", hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ hr = s->videoDevice->lpVtbl->CreateVideoProcessorEnumerator(s->videoDevice, ++ &contentDesc, &s->enumerator); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to create video processor enumerator: HRESULT 0x%lX\n", hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ hr = s->enumerator->lpVtbl->GetVideoProcessorCaps(s->enumerator, &caps); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to get video processor caps: HRESULT 0x%lX\n", hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ if (s->rotation_enable && ++ !(caps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_ROTATION)) { ++ av_log(ctx, AV_LOG_ERROR, "D3D11 video processor does not support rotation\n"); ++ return AVERROR(ENOSYS); ++ } ++ ++ if (s->mirror_enable && ++ !(caps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_MIRROR)) { ++ av_log(ctx, AV_LOG_ERROR, "D3D11 video processor does not support mirroring\n"); ++ return AVERROR(ENOSYS); ++ } ++ ++ hr = s->videoDevice->lpVtbl->CreateVideoProcessor(s->videoDevice, s->enumerator, ++ 0, &s->processor); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to create video processor: HRESULT 0x%lX\n", hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ av_log(ctx, AV_LOG_VERBOSE, ++ "D3D11 transpose processor configured: %dx%d -> %dx%d\n", ++ s->input_width, s->input_height, s->output_width, s->output_height); ++ return 0; ++} ++ ++static int transpose_d3d11_filter_frame(AVFilterLink *inlink, AVFrame *in) ++{ ++ AVFilterContext *ctx = inlink->dst; ++ TransposeD3D11Context *s = ctx->priv; ++ AVFilterLink *outlink = ctx->outputs[0]; ++ ID3D11VideoProcessorInputView *inputView = NULL; ++ ID3D11VideoProcessorOutputView *outputView = NULL; ++ ID3D11VideoContext *videoContext = NULL; ++ ID3D11VideoContext1 *videoContext1 = NULL; ++ AVFrame *out = NULL; ++ int ret = 0; ++ HRESULT hr; ++ ++ if (s->passthrough) ++ return ff_filter_frame(outlink, in); ++ ++ if (!in->hw_frames_ctx) { ++ av_log(ctx, AV_LOG_ERROR, "No hardware frames context in input frame\n"); ++ av_frame_free(&in); ++ return AVERROR(EINVAL); ++ } ++ ++ out = av_frame_alloc(); ++ if (!out) { ++ av_frame_free(&in); ++ return AVERROR(ENOMEM); ++ } ++ ++ ret = av_hwframe_get_buffer(s->hw_frames_ctx_out, out, 0); ++ if (ret < 0) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to get output frame from pool\n"); ++ goto fail; ++ } ++ ++ if (!s->configured) { ++ D3D11_TEXTURE2D_DESC textureDesc; ++ ID3D11Texture2D *input_texture = (ID3D11Texture2D *)in->data[0]; ++ input_texture->lpVtbl->GetDesc(input_texture, &textureDesc); ++ s->input_format = textureDesc.Format; ++ ++ ret = transpose_d3d11_configure_processor(s, ctx); ++ if (ret < 0) ++ goto fail; ++ s->configured = 1; ++ } ++ ++ hr = s->context->lpVtbl->QueryInterface(s->context, &IID_ID3D11VideoContext, ++ (void **)&videoContext); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to get video context: HRESULT 0x%lX\n", hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ ++ if (s->mirror_enable) { ++ hr = s->context->lpVtbl->QueryInterface(s->context, &IID_ID3D11VideoContext1, ++ (void **)&videoContext1); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to get video context 1 for mirroring: HRESULT 0x%lX\n", hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ } ++ ++ { ++ ID3D11Texture2D *input_texture = (ID3D11Texture2D *)in->data[0]; ++ int subIdx = (int)(intptr_t)in->data[1]; ++ D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC desc = { ++ .FourCC = s->input_format, ++ .ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D, ++ .Texture2D.ArraySlice = subIdx, ++ }; ++ ++ hr = s->videoDevice->lpVtbl->CreateVideoProcessorInputView( ++ s->videoDevice, (ID3D11Resource *)input_texture, s->enumerator, ++ &desc, &inputView); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to create input view: HRESULT 0x%lX\n", hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ } ++ ++ { ++ ID3D11Texture2D *output_texture = (ID3D11Texture2D *)out->data[0]; ++ D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC desc = { ++ .ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D, ++ .Texture2D.MipSlice = 0, ++ }; ++ ++ hr = s->videoDevice->lpVtbl->CreateVideoProcessorOutputView( ++ s->videoDevice, (ID3D11Resource *)output_texture, s->enumerator, ++ &desc, &outputView); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to create output view: HRESULT 0x%lX\n", hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ } ++ ++ { ++ RECT srcRect = { 0, 0, s->input_width, s->input_height }; ++ RECT dstRect = { 0, 0, s->output_width, s->output_height }; ++ D3D11_VIDEO_PROCESSOR_STREAM stream = { ++ .Enable = TRUE, ++ .pInputSurface = inputView, ++ .OutputIndex = 0, ++ .InputFrameOrField = 0, ++ }; ++ ++ videoContext->lpVtbl->VideoProcessorSetStreamSourceRect(videoContext, s->processor, ++ 0, TRUE, &srcRect); ++ videoContext->lpVtbl->VideoProcessorSetStreamDestRect(videoContext, s->processor, ++ 0, TRUE, &dstRect); ++ videoContext->lpVtbl->VideoProcessorSetStreamRotation(videoContext, s->processor, ++ 0, s->rotation_enable, ++ s->rotation); ++ if (videoContext1) ++ videoContext1->lpVtbl->VideoProcessorSetStreamMirror(videoContext1, s->processor, ++ 0, TRUE, ++ s->flip_h, s->flip_v); ++ ++ hr = videoContext->lpVtbl->VideoProcessorBlt(videoContext, s->processor, ++ outputView, 0, 1, &stream); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "VideoProcessorBlt failed: HRESULT 0x%lX\n", hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ } ++ ++ ret = av_frame_copy_props(out, in); ++ if (ret < 0) ++ goto fail; ++ ++ out->data[1] = (uint8_t *)(intptr_t)0; ++ out->width = s->output_width; ++ out->height = s->output_height; ++ out->format = AV_PIX_FMT_D3D11; ++ ++ D3D11_RELEASE(inputView); ++ D3D11_RELEASE(outputView); ++ D3D11_RELEASE(videoContext1); ++ D3D11_RELEASE(videoContext); ++ av_frame_free(&in); ++ ++ return ff_filter_frame(outlink, out); ++ ++fail: ++ D3D11_RELEASE(inputView); ++ D3D11_RELEASE(outputView); ++ D3D11_RELEASE(videoContext1); ++ D3D11_RELEASE(videoContext); ++ av_frame_free(&in); ++ av_frame_free(&out); ++ return ret; ++} ++ ++static int transpose_d3d11_config_props(AVFilterLink *outlink) ++{ ++ AVFilterContext *ctx = outlink->src; ++ TransposeD3D11Context *s = ctx->priv; ++ AVFilterLink *inlink = ctx->inputs[0]; ++ FilterLink *inl = ff_filter_link(inlink); ++ FilterLink *outl = ff_filter_link(outlink); ++ AVHWFramesContext *in_frames_ctx; ++ AVHWFramesContext *frames_ctx; ++ AVD3D11VAFramesContext *frames_hwctx; ++ int ret; ++ ++ transpose_d3d11_release_resources(s); ++ s->configured = 0; ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ ++ ret = transpose_d3d11_set_direction(ctx); ++ if (ret < 0) ++ return ret; ++ ++ if (!inl->hw_frames_ctx) { ++ av_log(ctx, AV_LOG_ERROR, "No hw_frames_ctx available on input link\n"); ++ return AVERROR(EINVAL); ++ } ++ ++ if ((inlink->w >= inlink->h && s->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) || ++ (inlink->w <= inlink->h && s->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) { ++ outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx); ++ if (!outl->hw_frames_ctx) ++ return AVERROR(ENOMEM); ++ outlink->w = inlink->w; ++ outlink->h = inlink->h; ++ av_log(ctx, AV_LOG_VERBOSE, ++ "w:%d h:%d -> w:%d h:%d (passthrough mode)\n", ++ inlink->w, inlink->h, outlink->w, outlink->h); ++ return 0; ++ } ++ ++ s->passthrough = TRANSPOSE_PT_TYPE_NONE; ++ ++ s->input_width = inlink->w; ++ s->input_height = inlink->h; ++ switch (s->dir) { ++ case TRANSPOSE_CCLOCK_FLIP: ++ case TRANSPOSE_CCLOCK: ++ case TRANSPOSE_CLOCK: ++ case TRANSPOSE_CLOCK_FLIP: ++ s->output_width = inlink->h; ++ s->output_height = inlink->w; ++ break; ++ default: ++ s->output_width = inlink->w; ++ s->output_height = inlink->h; ++ break; ++ } ++ outlink->w = s->output_width; ++ outlink->h = s->output_height; ++ ++ in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data; ++ if (!s->hw_device_ctx) { ++ s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref); ++ if (!s->hw_device_ctx) ++ return AVERROR(ENOMEM); ++ } ++ ++ { ++ AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data; ++ AVD3D11VADeviceContext *d3d11_hwctx = (AVD3D11VADeviceContext *)hwctx->hwctx; ++ s->device = d3d11_hwctx->device; ++ s->context = d3d11_hwctx->device_context; ++ } ++ ++ if (!s->device || !s->context) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to get valid D3D11 device or context\n"); ++ return AVERROR(EINVAL); ++ } ++ ++ s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx); ++ if (!s->hw_frames_ctx_out) ++ return AVERROR(ENOMEM); ++ ++ frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx_out->data; ++ frames_ctx->format = AV_PIX_FMT_D3D11; ++ frames_ctx->sw_format = in_frames_ctx->sw_format; ++ frames_ctx->width = s->output_width; ++ frames_ctx->height = s->output_height; ++ frames_ctx->initial_pool_size = 10; ++ if (ctx->extra_hw_frames > 0) ++ frames_ctx->initial_pool_size += ctx->extra_hw_frames; ++ ++ frames_hwctx = frames_ctx->hwctx; ++ frames_hwctx->MiscFlags = 0; ++ frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; ++ if (frames_ctx->sw_format == AV_PIX_FMT_NV12) ++ frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER; ++ ++ ret = av_hwframe_ctx_init(s->hw_frames_ctx_out); ++ if (ret < 0) { ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ return ret; ++ } ++ ++ outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out); ++ if (!outl->hw_frames_ctx) ++ return AVERROR(ENOMEM); ++ ++ av_log(ctx, AV_LOG_VERBOSE, "D3D11 transpose config: %dx%d -> %dx%d\n", ++ inlink->w, inlink->h, outlink->w, outlink->h); ++ return 0; ++} ++ ++static AVFrame *transpose_d3d11_get_video_buffer(AVFilterLink *inlink, int w, int h) ++{ ++ TransposeD3D11Context *s = inlink->dst->priv; ++ ++ return s->passthrough ? ++ ff_null_get_video_buffer(inlink, w, h) : ++ ff_default_get_video_buffer(inlink, w, h); ++} ++ ++static av_cold int transpose_d3d11_init(AVFilterContext *ctx) ++{ ++ return 0; ++} ++ ++static av_cold void transpose_d3d11_uninit(AVFilterContext *ctx) ++{ ++ TransposeD3D11Context *s = ctx->priv; ++ ++ transpose_d3d11_release_resources(s); ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ av_buffer_unref(&s->hw_device_ctx); ++} ++ ++#define OFFSET(x) offsetof(TransposeD3D11Context, x) ++#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) ++ ++static const AVOption transpose_d3d11_options[] = { ++ { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 6, FLAGS, .unit = "dir" }, ++ { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 0, FLAGS, .unit = "dir" }, ++ { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, 0, 0, FLAGS, .unit = "dir" }, ++ { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, 0, 0, FLAGS, .unit = "dir" }, ++ { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, 0, 0, FLAGS, .unit = "dir" }, ++ { "reversal", "rotate by half-turn", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, 0, 0, FLAGS, .unit = "dir" }, ++ { "hflip", "flip horizontally", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, 0, 0, FLAGS, .unit = "dir" }, ++ { "vflip", "flip vertically", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, 0, 0, FLAGS, .unit = "dir" }, ++ ++ { "passthrough", "do not apply transposition if the input matches the specified geometry", ++ OFFSET(passthrough), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_PT_TYPE_NONE }, 0, INT_MAX, FLAGS, .unit = "passthrough" }, ++ { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, ++ { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_PORTRAIT }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, ++ { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_LANDSCAPE }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, ++ ++ { NULL } ++}; ++ ++AVFILTER_DEFINE_CLASS(transpose_d3d11); ++ ++static const AVFilterPad transpose_d3d11_inputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .filter_frame = transpose_d3d11_filter_frame, ++ .get_buffer.video = transpose_d3d11_get_video_buffer, ++ }, ++}; ++ ++static const AVFilterPad transpose_d3d11_outputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .config_props = transpose_d3d11_config_props, ++ }, ++}; ++ ++const FFFilter ff_vf_transpose_d3d11 = { ++ .p.name = "transpose_d3d11", ++ .p.description = NULL_IF_CONFIG_SMALL("Transpose D3D11 video"), ++ .priv_size = sizeof(TransposeD3D11Context), ++ .p.priv_class = &transpose_d3d11_class, ++ .init = transpose_d3d11_init, ++ .uninit = transpose_d3d11_uninit, ++ FILTER_INPUTS(transpose_d3d11_inputs), ++ FILTER_OUTPUTS(transpose_d3d11_outputs), ++ FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11), ++ .p.flags = AVFILTER_FLAG_HWDEVICE, ++ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, ++}; +Index: FFmpeg/libavfilter/vf_scale_d3d11.c +=================================================================== +--- FFmpeg.orig/libavfilter/vf_scale_d3d11.c ++++ FFmpeg/libavfilter/vf_scale_d3d11.c +@@ -386,7 +386,9 @@ static int scale_d3d11_config_props(AVFi + + AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx; + frames_hwctx->MiscFlags = 0; +- frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_VIDEO_ENCODER; ++ frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; ++ if (frames_ctx->sw_format == AV_PIX_FMT_NV12) ++ frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER; + + ret = av_hwframe_ctx_init(s->hw_frames_ctx_out); + if (ret < 0) { +Index: FFmpeg/libavfilter/d3d11/overlay.hlsl +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/d3d11/overlay.hlsl +@@ -0,0 +1,128 @@ ++/* ++ * D3D11 overlay ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++Texture2DArray ov0 : register(t0); ++Texture2DArray main_y : register(t1); ++Texture2DArray main_uv : register(t2); ++ ++RWTexture2DArray dst_y : register(u0); ++RWTexture2DArray dst_uv : register(u1); ++ ++cbuffer Params : register(b0) { ++ int dst_w; ++ int dst_h; ++ int ov_x; ++ int ov_y; ++ int ov_w; ++ int ov_h; ++ int ov_src_w; ++ int ov_src_h; ++ float alpha; ++ float pad0; ++ float pad1; ++ float pad2; ++}; ++ ++int2 map_pos(int2 p) ++{ ++ int sx = min((int)(((p.x + 0.5) * ov_src_w) / ov_w), ov_src_w - 1); ++ int sy = min((int)(((p.y + 0.5) * ov_src_h) / ov_h), ov_src_h - 1); ++ return int2(sx, sy); ++} ++ ++float main_y_at(int x, int y) ++{ ++ return main_y.Load(int4(x, y, 0, 0)).r; ++} ++ ++float2 main_uv_at(int x, int y) ++{ ++ return main_uv.Load(int4(x, y, 0, 0)).rg; ++} ++ ++bool in_overlay_y(int x, int y) ++{ ++ return x >= ov_x && y >= ov_y && x < ov_x + ov_w && y < ov_y + ov_h; ++} ++ ++bool in_overlay_uv(int x, int y) ++{ ++ int ox = ov_x >> 1; ++ int oy = ov_y >> 1; ++ int cw = (ov_w + 1) >> 1; ++ int ch = (ov_h + 1) >> 1; ++ return x >= ox && y >= oy && x < ox + cw && y < oy + ch; ++} ++ ++float3 bgra_to_yuv(float4 c) ++{ ++ float y = 0.183 * c.r + 0.614 * c.g + 0.062 * c.b + 0.0625; ++ float u = -0.101 * c.r - 0.339 * c.g + 0.439 * c.b + 0.5; ++ float v = 0.439 * c.r - 0.399 * c.g - 0.040 * c.b + 0.5; ++ return saturate(float3(y, u, v)); ++} ++ ++[numthreads(16, 16, 1)] ++void bgra_y(uint3 id : SV_DispatchThreadID) ++{ ++ if (id.x >= (uint)dst_w || id.y >= (uint)dst_h) ++ return; ++ ++ int dx = id.x; ++ int dy = id.y; ++ ++ float b = main_y_at(dx, dy); ++ if (!in_overlay_y(dx, dy)) { ++ dst_y[uint3(dx, dy, 0)] = b; ++ return; ++ } ++ ++ float4 o = ov0.Load(int4(map_pos(int2(dx - ov_x, dy - ov_y)), 0, 0)); ++ float a = saturate(o.a * alpha); ++ float3 yuv = bgra_to_yuv(o); ++ dst_y[uint3(dx, dy, 0)] = mad(yuv.x, a, b * (1.0 - a)); ++} ++ ++[numthreads(16, 16, 1)] ++void bgra_uv(uint3 id : SV_DispatchThreadID) ++{ ++ uint dst_cw = (dst_w + 1) >> 1; ++ uint dst_ch = (dst_h + 1) >> 1; ++ if (id.x >= dst_cw || id.y >= dst_ch) ++ return; ++ ++ int dx = id.x; ++ int dy = id.y; ++ ++ float2 b = main_uv_at(dx, dy); ++ if (!in_overlay_uv(dx, dy)) { ++ dst_uv[uint3(dx, dy, 0)] = b; ++ return; ++ } ++ ++ int2 cp = int2(dx - (ov_x >> 1), dy - (ov_y >> 1)); ++ int2 lp = int2(min(cp.x * 2, max(ov_w - 1, 0)), min(cp.y * 2, max(ov_h - 1, 0))); ++ float4 o = ov0.Load(int4(map_pos(lp), 0, 0)); ++ float a = saturate(o.a * alpha); ++ float3 yuv = bgra_to_yuv(o); ++ dst_uv[uint3(dx, dy, 0)] = mad(yuv.yz, a, b * (1.0 - a)); ++} +Index: FFmpeg/libavfilter/d3d11_source.h +=================================================================== +--- FFmpeg.orig/libavfilter/d3d11_source.h ++++ FFmpeg/libavfilter/d3d11_source.h +@@ -24,5 +24,6 @@ + #define AVFILTER_D3D11_SOURCE_H + + extern const char *ff_source_deint_hlsl; ++extern const char *ff_source_overlay_hlsl; + + #endif /* AVFILTER_D3D11_SOURCE_H */ diff --git a/debian/patches/0095-fix-d3d11-scale-visible-source-rect.patch b/debian/patches/0095-fix-d3d11-scale-visible-source-rect.patch new file mode 100644 index 000000000..d1c56ded5 --- /dev/null +++ b/debian/patches/0095-fix-d3d11-scale-visible-source-rect.patch @@ -0,0 +1,35 @@ +diff --git a/libavfilter/vf_scale_d3d11.c b/libavfilter/vf_scale_d3d11.c +index c301f4a0c9..aab98c8cec 100644 +--- a/libavfilter/vf_scale_d3d11.c ++++ b/libavfilter/vf_scale_d3d11.c +@@ -206,8 +206,12 @@ static int scale_d3d11_filter_frame(AVFilterLink *inlink, AVFrame *in) + ID3D11Texture2D *input_texture = (ID3D11Texture2D *)in->data[0]; + input_texture->lpVtbl->GetDesc(input_texture, &textureDesc); + +- s->inputWidth = textureDesc.Width; +- s->inputHeight = textureDesc.Height; ++ /* D3D11 decoder textures may be padded (for example 1920x1152 ++ * for a visible 1920x1080 frame). Configure and sample only the ++ * visible frame area, otherwise the VP may scale uninitialized ++ * padding and show a green strip at the bottom. */ ++ s->inputWidth = in->width; ++ s->inputHeight = in->height; + s->input_format = textureDesc.Format; + + ret = scale_d3d11_configure_processor(s, ctx); +@@ -265,6 +269,15 @@ static int scale_d3d11_filter_frame(AVFilterLink *inlink, AVFrame *in) + goto fail; + } + ++ { ++ RECT srcRect = { 0, 0, in->width, in->height }; ++ RECT dstRect = { 0, 0, s->width, s->height }; ++ videoContext->lpVtbl->VideoProcessorSetStreamSourceRect(videoContext, s->processor, ++ 0, TRUE, &srcRect); ++ videoContext->lpVtbl->VideoProcessorSetStreamDestRect(videoContext, s->processor, ++ 0, TRUE, &dstRect); ++ } ++ + ///< Process the frame + hr = videoContext->lpVtbl->VideoProcessorBlt(videoContext, s->processor, s->outputView, 0, 1, &stream); + if (FAILED(hr)) { diff --git a/debian/patches/0096-add-d3d11-tonemap-filter.patch b/debian/patches/0096-add-d3d11-tonemap-filter.patch new file mode 100644 index 000000000..8bdc4aafa --- /dev/null +++ b/debian/patches/0096-add-d3d11-tonemap-filter.patch @@ -0,0 +1,1987 @@ +Index: FFmpeg/configure +=================================================================== +--- FFmpeg.orig/configure ++++ FFmpeg/configure +@@ -3535,6 +3535,7 @@ scale_cuda_filter_deps_any="cuda_nvcc cu + thumbnail_cuda_filter_deps="ffnvcodec" + thumbnail_cuda_filter_deps_any="cuda_nvcc cuda_llvm" + tonemap_cuda_filter_deps="ffnvcodec const_nan" ++tonemap_d3d11_filter_deps="d3d11va const_nan" + tonemap_cuda_filter_deps_any="cuda_nvcc cuda_llvm" + transpose_cuda_filter_deps="ffnvcodec" + transpose_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +Index: FFmpeg/libavfilter/Makefile +=================================================================== +--- FFmpeg.orig/libavfilter/Makefile ++++ FFmpeg/libavfilter/Makefile +@@ -560,6 +560,7 @@ OBJS-$(CONFIG_TONEMAP_FILTER) + OBJS-$(CONFIG_TONEMAPX_FILTER) += vf_tonemapx.o + OBJS-$(CONFIG_TONEMAP_CUDA_FILTER) += vf_tonemap_cuda.o cuda/tonemap.ptx.o \ + cuda/host_util.o cuda/load_helper.o ++OBJS-$(CONFIG_TONEMAP_D3D11_FILTER) += vf_tonemap_d3d11.o d3d11/tonemap.o + OBJS-$(CONFIG_TONEMAP_OPENCL_FILTER) += vf_tonemap_opencl.o opencl.o \ + opencl/tonemap.o opencl/colorspace_common.o + OBJS-$(CONFIG_TONEMAP_VAAPI_FILTER) += vf_tonemap_vaapi.o vaapi_vpp.o +Index: FFmpeg/libavfilter/allfilters.c +=================================================================== +--- FFmpeg.orig/libavfilter/allfilters.c ++++ FFmpeg/libavfilter/allfilters.c +@@ -520,6 +520,7 @@ extern const FFFilter ff_vf_tmix; + extern const FFFilter ff_vf_tonemap; + extern const FFFilter ff_vf_tonemapx; + extern const FFFilter ff_vf_tonemap_cuda; ++extern const FFFilter ff_vf_tonemap_d3d11; + extern const FFFilter ff_vf_tonemap_opencl; + extern const FFFilter ff_vf_tonemap_vaapi; + extern const FFFilter ff_vf_tonemap_videotoolbox; +Index: FFmpeg/libavfilter/vf_tonemap_d3d11.c +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/vf_tonemap_d3d11.c +@@ -0,0 +1,1267 @@ ++/* ++ * D3D11 tonemap filter ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#ifndef COBJMACROS ++#define COBJMACROS ++#endif ++#include ++#include ++ ++#include "libavutil/common.h" ++#include "libavutil/mathematics.h" ++#include "libavutil/hwcontext.h" ++#include "libavutil/hwcontext_d3d11va.h" ++#include "libavutil/dovi_meta.h" ++#include "libavutil/libm.h" ++#include "libavutil/mem.h" ++#include "libavutil/opt.h" ++#include "libavutil/pixdesc.h" ++ ++#include "colorspace.h" ++#include "filters.h" ++#include "formats.h" ++#include "video.h" ++#include "vf_tonemap_d3d11.h" ++#include "d3d11_source.h" ++ ++#define REF_WHITE_SCALE (REFERENCE_WHITE / REFERENCE_WHITE_ALT) ++#define D3D11_RELEASE(p) do { if (p) { (p)->lpVtbl->Release(p); (p) = NULL; } } while (0) ++#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL3 ++#define D3DCOMPILE_OPTIMIZATION_LEVEL3 (1 << 15) ++#endif ++ ++enum TonemapAlgorithm { ++ TONEMAP_NONE, ++ TONEMAP_LINEAR, ++ TONEMAP_GAMMA, ++ TONEMAP_CLIP, ++ TONEMAP_REINHARD, ++ TONEMAP_HABLE, ++ TONEMAP_MOBIUS, ++ TONEMAP_BT2390, ++ TONEMAP_COUNT, ++}; ++ ++enum TonemapMode { ++ TONEMAP_MODE_MAX, ++ TONEMAP_MODE_RGB, ++ TONEMAP_MODE_LUM, ++ TONEMAP_MODE_ITP, ++ TONEMAP_MODE_AUTO, ++ TONEMAP_MODE_COUNT, ++}; ++ ++typedef HRESULT (WINAPI *D3DCompileProc)(LPCVOID, SIZE_T, LPCSTR, ++ const D3D_SHADER_MACRO *, ID3DInclude *, ++ LPCSTR, LPCSTR, UINT, UINT, ++ ID3D10Blob **, ID3D10Blob **); ++ ++typedef struct TonemapD3D11Params { ++ int width; ++ int height; ++ int tonemap; ++ int tonemap_mode; ++ int trc_in; ++ int trc_out; ++ int full_range_in; ++ int full_range_out; ++ int in_depth; ++ int out_depth; ++ int chroma_loc; ++ int skip_tonemap; ++ int apply_dovi; ++ int pad_i0; ++ int pad_i1; ++ int pad_i2; ++ float tone_param; ++ float desat_param; ++ float peak; ++ float target_peak; ++ float input_quantization_offset; ++ float input_y_scale; ++ float input_uv_scale; ++ float output_quantization_offset; ++ float luma_src[4]; ++ float luma_dst[4]; ++ float rgb_matrix[3][4]; ++ float yuv_matrix[3][4]; ++ float rgb2rgb_matrix[3][4]; ++ float dovi_ycc2rgb_offset[4]; ++ float dovi_rgb_matrix[3][4]; ++ float dovi_lms2rgb_matrix[3][4]; ++ float dovi_params[6][4]; ++ float dovi_pivots[6][4]; ++ float dovi_coeffs[24][4]; ++ float dovi_mmr[144][4]; ++} TonemapD3D11Params; ++ ++typedef struct TonemapD3D11Context { ++ const AVClass *class; ++ ++ enum AVColorSpace colorspace; ++ enum AVColorTransferCharacteristic trc; ++ enum AVColorPrimaries primaries; ++ enum AVColorRange range; ++ enum AVPixelFormat format; ++ int apply_dovi; ++ int force_output_copy; ++ ++ int tonemap; ++ int tonemap_mode; ++ double peak; ++ double src_peak; ++ double target_peak; ++ double param; ++ double final_param; ++ double desat_param; ++ ++ AVBufferRef *hw_device_ctx; ++ AVBufferRef *hw_frames_ctx_out; ++ enum AVPixelFormat in_fmt; ++ enum AVPixelFormat out_fmt; ++ const AVPixFmtDescriptor *in_desc; ++ const AVPixFmtDescriptor *out_desc; ++ ++ ID3D11Device *device; ++ ID3D11DeviceContext *context; ++ HMODULE d3dcompiler; ++ D3DCompileProc D3DCompile; ++ ID3D11ComputeShader *cs; ++ ID3D11Buffer *params_buf; ++ ID3D11Texture2D *src_tex; ++ ID3D11Texture2D *work_tex; ++ int shader_tonemap, shader_mode, shader_trc_in, shader_trc_out; ++ int shader_full_in, shader_full_out, shader_out_depth, shader_skip, shader_dovi; ++ int src_w, src_h; ++ int work_w, work_h; ++ int direct_input_srv; ++ int direct_output_uav; ++ ++ struct FFDOVIMetadataRemap dovi; ++ int apply_dovi_frame; ++} TonemapD3D11Context; ++ ++ ++static const double dovi_lms2rgb_matrix[3][3] = ++{ ++ { 3.06441879, -2.16597676, 0.10155818}, ++ {-0.65612108, 1.78554118, -0.12943749}, ++ { 0.01736321, -0.04725154, 1.03004253}, ++}; ++ ++static const int colorspaces_out[] = { ++ AVCOL_SPC_UNSPECIFIED, ++ AVCOL_SPC_BT709, ++ AVCOL_SPC_BT2020_NCL, ++ -1 ++}; ++ ++static int get_rgb2rgb_matrix(enum AVColorPrimaries in, enum AVColorPrimaries out, ++ double rgb2rgb[3][3]) ++{ ++ double rgb2xyz[3][3], xyz2rgb[3][3]; ++ const AVColorPrimariesDesc *in_primaries = av_csp_primaries_desc_from_id(in); ++ const AVColorPrimariesDesc *out_primaries = av_csp_primaries_desc_from_id(out); ++ ++ if (!in_primaries || !out_primaries) ++ return AVERROR(EINVAL); ++ ++ ff_fill_rgb2xyz_table(&out_primaries->prim, &out_primaries->wp, rgb2xyz); ++ ff_matrix_invert_3x3(rgb2xyz, xyz2rgb); ++ ff_fill_rgb2xyz_table(&in_primaries->prim, &in_primaries->wp, rgb2xyz); ++ ff_matrix_mul_3x3(rgb2rgb, rgb2xyz, xyz2rgb); ++ return 0; ++} ++ ++static int format_is_supported(enum AVPixelFormat fmt) ++{ ++ return fmt == AV_PIX_FMT_NV12 || fmt == AV_PIX_FMT_P010 || fmt == AV_PIX_FMT_P016; ++} ++ ++static DXGI_FORMAT texture_format(enum AVPixelFormat fmt) ++{ ++ if (fmt == AV_PIX_FMT_P010) ++ return DXGI_FORMAT_P010; ++ if (fmt == AV_PIX_FMT_P016) ++ return DXGI_FORMAT_P016; ++ return DXGI_FORMAT_NV12; ++} ++ ++static DXGI_FORMAT plane_format(enum AVPixelFormat fmt, int plane) ++{ ++ if (fmt == AV_PIX_FMT_NV12) ++ return plane ? DXGI_FORMAT_R8G8_UNORM : DXGI_FORMAT_R8_UNORM; ++ return plane ? DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R16_UNORM; ++} ++ ++static void release_d3d11_resources(TonemapD3D11Context *s) ++{ ++ D3D11_RELEASE(s->cs); ++ D3D11_RELEASE(s->params_buf); ++ D3D11_RELEASE(s->src_tex); ++ D3D11_RELEASE(s->work_tex); ++ s->shader_tonemap = s->shader_mode = s->shader_trc_in = s->shader_trc_out = -1; ++ s->shader_full_in = s->shader_full_out = s->shader_out_depth = s->shader_skip = s->shader_dovi = -1; ++ s->src_w = s->src_h = 0; ++ s->work_w = s->work_h = 0; ++} ++ ++static void macro_int(char *buf, size_t size, int value) ++{ ++ snprintf(buf, size, "%d", value); ++} ++ ++static int compile_shader(AVFilterContext *ctx, ++ int tonemap, int mode, int trc_in, int trc_out, ++ int full_in, int full_out, int out_depth, ++ int skip, int dovi) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ ID3D10Blob *cs_blob = NULL; ++ ID3D10Blob *err_blob = NULL; ++ char tonemap_buf[16], mode_buf[16], trc_in_buf[16], trc_out_buf[16]; ++ char full_in_buf[16], full_out_buf[16], out_depth_buf[16], skip_buf[16], dovi_buf[16]; ++ D3D_SHADER_MACRO macros[] = { ++ { "TONEMAP_ALG", tonemap_buf }, ++ { "TONE_MODE", mode_buf }, ++ { "INPUT_TRC", trc_in_buf }, ++ { "OUTPUT_TRC", trc_out_buf }, ++ { "FULL_RANGE_IN_VAL", full_in_buf }, ++ { "FULL_RANGE_OUT_VAL", full_out_buf }, ++ { "OUT_DEPTH_VAL", out_depth_buf }, ++ { "SKIP_TONEMAP_VAL", skip_buf }, ++ { "APPLY_DOVI_VAL", dovi_buf }, ++ { NULL, NULL }, ++ }; ++ HRESULT hr; ++ ++ macro_int(tonemap_buf, sizeof(tonemap_buf), tonemap); ++ macro_int(mode_buf, sizeof(mode_buf), mode); ++ macro_int(trc_in_buf, sizeof(trc_in_buf), trc_in); ++ macro_int(trc_out_buf, sizeof(trc_out_buf), trc_out); ++ macro_int(full_in_buf, sizeof(full_in_buf), full_in); ++ macro_int(full_out_buf, sizeof(full_out_buf), full_out); ++ macro_int(out_depth_buf, sizeof(out_depth_buf), out_depth); ++ macro_int(skip_buf, sizeof(skip_buf), skip); ++ macro_int(dovi_buf, sizeof(dovi_buf), dovi); ++ ++ D3D11_RELEASE(s->cs); ++ hr = s->D3DCompile(ff_source_tonemap_hlsl, strlen(ff_source_tonemap_hlsl), ++ NULL, macros, NULL, "tonemap_main", "cs_5_0", ++ D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &cs_blob, &err_blob); ++ if (FAILED(hr)) { ++ if (err_blob) { ++ av_log(ctx, AV_LOG_ERROR, "Failed compiling D3D11 tonemap shader: %.*s\n", ++ (int)err_blob->lpVtbl->GetBufferSize(err_blob), ++ (char *)err_blob->lpVtbl->GetBufferPointer(err_blob)); ++ } else { ++ av_log(ctx, AV_LOG_ERROR, "Failed compiling D3D11 tonemap shader: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ } ++ D3D11_RELEASE(err_blob); ++ return AVERROR_EXTERNAL; ++ } ++ ++ hr = s->device->lpVtbl->CreateComputeShader(s->device, ++ cs_blob->lpVtbl->GetBufferPointer(cs_blob), ++ cs_blob->lpVtbl->GetBufferSize(cs_blob), ++ NULL, &s->cs); ++ D3D11_RELEASE(cs_blob); ++ D3D11_RELEASE(err_blob); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 tonemap shader: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ s->shader_tonemap = tonemap; ++ s->shader_mode = mode; ++ s->shader_trc_in = trc_in; ++ s->shader_trc_out = trc_out; ++ s->shader_full_in = full_in; ++ s->shader_full_out = full_out; ++ s->shader_out_depth = out_depth; ++ s->shader_skip = skip; ++ s->shader_dovi = dovi; ++ return 0; ++} ++ ++static int ensure_compiler_and_params(AVFilterContext *ctx) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ D3D11_BUFFER_DESC bd = { 0 }; ++ HRESULT hr; ++ ++ if (!s->d3dcompiler) { ++ s->d3dcompiler = LoadLibraryA("d3dcompiler_47.dll"); ++ if (!s->d3dcompiler) ++ s->d3dcompiler = LoadLibraryA("d3dcompiler_43.dll"); ++ } ++ if (!s->d3dcompiler) { ++ av_log(ctx, AV_LOG_ERROR, "Failed loading d3dcompiler DLL\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ s->D3DCompile = (D3DCompileProc)GetProcAddress(s->d3dcompiler, "D3DCompile"); ++ if (!s->D3DCompile) { ++ av_log(ctx, AV_LOG_ERROR, "Failed loading D3DCompile\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ if (s->params_buf) ++ return 0; ++ ++ bd.ByteWidth = sizeof(TonemapD3D11Params); ++ bd.Usage = D3D11_USAGE_DYNAMIC; ++ bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; ++ bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ++ hr = s->device->lpVtbl->CreateBuffer(s->device, &bd, NULL, &s->params_buf); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 tonemap constant buffer: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int ensure_shader_variant(AVFilterContext *ctx, const TonemapD3D11Params *params) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ int ret = ensure_compiler_and_params(ctx); ++ if (ret < 0) ++ return ret; ++ ++ if (s->cs && ++ s->shader_tonemap == params->tonemap && ++ s->shader_mode == params->tonemap_mode && ++ s->shader_trc_in == params->trc_in && ++ s->shader_trc_out == params->trc_out && ++ s->shader_full_in == params->full_range_in && ++ s->shader_full_out == params->full_range_out && ++ s->shader_out_depth == params->out_depth && ++ s->shader_skip == params->skip_tonemap && ++ s->shader_dovi == params->apply_dovi) ++ return 0; ++ ++ av_log(ctx, AV_LOG_DEBUG, "Compiling D3D11 tonemap shader variant: tone=%d mode=%d in_trc=%d out_trc=%d dovi=%d\n", ++ params->tonemap, params->tonemap_mode, params->trc_in, params->trc_out, params->apply_dovi); ++ return compile_shader(ctx, params->tonemap, params->tonemap_mode, ++ params->trc_in, params->trc_out, ++ params->full_range_in, params->full_range_out, ++ params->out_depth, params->skip_tonemap, ++ params->apply_dovi); ++} ++ ++static int ensure_texture(AVFilterContext *ctx, ID3D11Texture2D **tex, ++ int *cur_w, int *cur_h, int w, int h, ++ enum AVPixelFormat fmt, UINT bind_flags) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ D3D11_TEXTURE2D_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ if (*tex && *cur_w == w && *cur_h == h) ++ return 0; ++ ++ D3D11_RELEASE(*tex); ++ desc.Width = w; ++ desc.Height = h; ++ desc.MipLevels = 1; ++ desc.ArraySize = 1; ++ desc.Format = texture_format(fmt); ++ desc.SampleDesc.Count = 1; ++ desc.Usage = D3D11_USAGE_DEFAULT; ++ desc.BindFlags = bind_flags; ++ ++ hr = s->device->lpVtbl->CreateTexture2D(s->device, &desc, NULL, tex); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 tonemap texture: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ *cur_w = w; ++ *cur_h = h; ++ return 0; ++} ++ ++static void fill_tex2d_srv_desc(ID3D11Texture2D *tex, UINT subresource, ++ DXGI_FORMAT fmt, D3D11_SHADER_RESOURCE_VIEW_DESC *desc) ++{ ++ D3D11_TEXTURE2D_DESC tex_desc; ++ UINT mip_slice = 0, array_slice = 0; ++ ++ ID3D11Texture2D_GetDesc(tex, &tex_desc); ++ if (tex_desc.MipLevels) { ++ mip_slice = subresource % tex_desc.MipLevels; ++ array_slice = subresource / tex_desc.MipLevels; ++ } ++ ++ desc->Format = fmt; ++ desc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; ++ desc->Texture2DArray.MostDetailedMip = mip_slice; ++ desc->Texture2DArray.MipLevels = 1; ++ desc->Texture2DArray.FirstArraySlice = array_slice; ++ desc->Texture2DArray.ArraySize = 1; ++} ++ ++static void fill_tex2d_uav_desc(ID3D11Texture2D *tex, UINT subresource, ++ DXGI_FORMAT fmt, D3D11_UNORDERED_ACCESS_VIEW_DESC *desc) ++{ ++ D3D11_TEXTURE2D_DESC tex_desc; ++ UINT mip_slice = 0, array_slice = 0; ++ ++ ID3D11Texture2D_GetDesc(tex, &tex_desc); ++ if (tex_desc.MipLevels) { ++ mip_slice = subresource % tex_desc.MipLevels; ++ array_slice = subresource / tex_desc.MipLevels; ++ } ++ ++ desc->Format = fmt; ++ desc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; ++ desc->Texture2DArray.MipSlice = mip_slice; ++ desc->Texture2DArray.FirstArraySlice = array_slice; ++ desc->Texture2DArray.ArraySize = 1; ++} ++ ++static int create_srv_view(AVFilterContext *ctx, ID3D11Texture2D *tex, ++ UINT subresource, enum AVPixelFormat fmt, int plane, ++ int log_level, ID3D11ShaderResourceView **srv) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ D3D11_SHADER_RESOURCE_VIEW_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ fill_tex2d_srv_desc(tex, subresource, plane_format(fmt, plane), &desc); ++ hr = s->device->lpVtbl->CreateShaderResourceView(s->device, (ID3D11Resource *)tex, ++ &desc, srv); ++ if (FAILED(hr)) { ++ av_log(ctx, log_level, "Failed creating D3D11 tonemap SRV plane %d: HRESULT 0x%lX\n", ++ plane, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int create_uav_view(AVFilterContext *ctx, ID3D11Texture2D *tex, ++ UINT subresource, enum AVPixelFormat fmt, int plane, ++ int log_level, ID3D11UnorderedAccessView **uav) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ D3D11_UNORDERED_ACCESS_VIEW_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ fill_tex2d_uav_desc(tex, subresource, plane_format(fmt, plane), &desc); ++ hr = s->device->lpVtbl->CreateUnorderedAccessView(s->device, (ID3D11Resource *)tex, ++ &desc, uav); ++ if (FAILED(hr)) { ++ av_log(ctx, log_level, "Failed creating D3D11 tonemap UAV plane %d: HRESULT 0x%lX\n", ++ plane, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static void copy_frame_to_texture(TonemapD3D11Context *s, AVFrame *src, ID3D11Texture2D *dst) ++{ ++ ID3D11Texture2D *src_tex = (ID3D11Texture2D *)src->data[0]; ++ UINT src_sub = (UINT)(uintptr_t)src->data[1]; ++ D3D11_BOX box = { 0, 0, 0, src->width, src->height, 1 }; ++ ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst, 0, ++ 0, 0, 0, (ID3D11Resource *)src_tex, ++ src_sub, &box); ++} ++ ++static void copy_texture_to_frame(TonemapD3D11Context *s, ID3D11Texture2D *src, AVFrame *dst) ++{ ++ ID3D11Texture2D *dst_tex = (ID3D11Texture2D *)dst->data[0]; ++ UINT dst_sub = (UINT)(uintptr_t)dst->data[1]; ++ ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst_tex, ++ dst_sub, 0, 0, 0, (ID3D11Resource *)src, ++ 0, NULL); ++} ++ ++static int create_frame_srvs(AVFilterContext *ctx, AVFrame *frame, ++ ID3D11ShaderResourceView **srvs) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)frame->data[0]; ++ UINT subresource = (UINT)(uintptr_t)frame->data[1]; ++ int ret; ++ ++ ret = create_srv_view(ctx, tex, subresource, s->in_fmt, 0, AV_LOG_DEBUG, &srvs[0]); ++ if (ret < 0) ++ return ret; ++ ret = create_srv_view(ctx, tex, subresource, s->in_fmt, 1, AV_LOG_DEBUG, &srvs[1]); ++ if (ret < 0) ++ D3D11_RELEASE(srvs[0]); ++ return ret; ++} ++ ++static int prepare_input_srvs(AVFilterContext *ctx, AVFrame *input, ++ ID3D11ShaderResourceView **srvs) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ int ret; ++ ++ if (s->direct_input_srv) { ++ ret = create_frame_srvs(ctx, input, srvs); ++ if (ret >= 0) { ++ if (s->direct_input_srv < 0) { ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader input: direct SRV\n"); ++ s->direct_input_srv = 1; ++ } ++ return 0; ++ } ++ if (s->direct_input_srv < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader input: copied to internal SRV texture\n"); ++ s->direct_input_srv = 0; ++ } ++ ++ ++ ret = ensure_texture(ctx, &s->src_tex, &s->src_w, &s->src_h, ++ input->width, input->height, s->in_fmt, ++ D3D11_BIND_SHADER_RESOURCE); ++ if (ret < 0) ++ return ret; ++ copy_frame_to_texture(s, input, s->src_tex); ++ ret = create_srv_view(ctx, s->src_tex, 0, s->in_fmt, 0, AV_LOG_ERROR, &srvs[0]); ++ if (ret < 0) ++ return ret; ++ ret = create_srv_view(ctx, s->src_tex, 0, s->in_fmt, 1, AV_LOG_ERROR, &srvs[1]); ++ if (ret < 0) ++ D3D11_RELEASE(srvs[0]); ++ return ret; ++} ++ ++static int prepare_output_uavs(AVFilterContext *ctx, AVFrame *dst, ++ ID3D11UnorderedAccessView **uavs, int *direct) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)dst->data[0]; ++ UINT subresource = (UINT)(uintptr_t)dst->data[1]; ++ int ret; ++ ++ *direct = 0; ++ if (s->direct_output_uav) { ++ ret = create_uav_view(ctx, tex, subresource, s->out_fmt, 0, AV_LOG_DEBUG, &uavs[0]); ++ if (ret >= 0) ++ ret = create_uav_view(ctx, tex, subresource, s->out_fmt, 1, AV_LOG_DEBUG, &uavs[1]); ++ if (ret >= 0) { ++ if (s->direct_output_uav < 0) { ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: direct UAV\n"); ++ s->direct_output_uav = 1; ++ } ++ *direct = 1; ++ return 0; ++ } ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ if (s->direct_output_uav < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: copied from internal UAV texture\n"); ++ s->direct_output_uav = 0; ++ } ++ ++ ret = ensure_texture(ctx, &s->work_tex, &s->work_w, &s->work_h, ++ dst->width, dst->height, s->out_fmt, ++ D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS); ++ if (ret < 0) ++ return ret; ++ ret = create_uav_view(ctx, s->work_tex, 0, s->out_fmt, 0, AV_LOG_ERROR, &uavs[0]); ++ if (ret < 0) ++ return ret; ++ ret = create_uav_view(ctx, s->work_tex, 0, s->out_fmt, 1, AV_LOG_ERROR, &uavs[1]); ++ if (ret < 0) ++ D3D11_RELEASE(uavs[0]); ++ return ret; ++} ++ ++static int probe_output_uav_pool(AVFilterContext *ctx, AVBufferRef *frames_ref) ++{ ++ AVHWFramesContext *frames_ctx = (AVHWFramesContext *)frames_ref->data; ++ AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx; ++ TonemapD3D11Context *s = ctx->priv; ++ AVFrame *frame = NULL; ++ ID3D11Texture2D *tex; ++ ID3D11UnorderedAccessView *uavs[2] = { NULL, NULL }; ++ UINT subresource; ++ int ret; ++ ++ if (frames_hwctx->texture) { ++ tex = frames_hwctx->texture; ++ subresource = 0; ++ } else { ++ frame = av_frame_alloc(); ++ if (!frame) ++ return AVERROR(ENOMEM); ++ ret = av_hwframe_get_buffer(frames_ref, frame, 0); ++ if (ret < 0) ++ goto done; ++ tex = (ID3D11Texture2D *)frame->data[0]; ++ subresource = (UINT)(uintptr_t)frame->data[1]; ++ } ++ ++ ret = create_uav_view(ctx, tex, subresource, s->out_fmt, 0, AV_LOG_DEBUG, &uavs[0]); ++ if (ret >= 0) ++ ret = create_uav_view(ctx, tex, subresource, s->out_fmt, 1, AV_LOG_DEBUG, &uavs[1]); ++ ++done: ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ av_frame_free(&frame); ++ return ret; ++} ++ ++static void copy_matrix(const double src[3][3], float dst[3][4]) ++{ ++ for (int i = 0; i < 3; i++) { ++ for (int j = 0; j < 3; j++) ++ dst[i][j] = src[i][j]; ++ dst[i][3] = 0.0f; ++ } ++} ++ ++ ++static void fill_dovi_params(TonemapD3D11Params *params, ++ const struct FFDOVIMetadataRemap *dovi) ++{ ++ double ycc2rgb_offset[3] = { 0 }; ++ double lms2rgb[3][3]; ++ ++ for (int i = 0; i < 3; i++) { ++ for (int j = 0; j < 3; j++) ++ ycc2rgb_offset[i] -= dovi->nonlinear[i][j] * dovi->nonlinear_offset[j]; ++ params->dovi_ycc2rgb_offset[i] = ycc2rgb_offset[i]; ++ } ++ ++ ff_matrix_mul_3x3(lms2rgb, dovi_lms2rgb_matrix, dovi->linear); ++ copy_matrix(dovi->nonlinear, params->dovi_rgb_matrix); ++ copy_matrix(lms2rgb, params->dovi_lms2rgb_matrix); ++ ++ for (int c = 0; c < 3; c++) { ++ int has_poly = 0, has_mmr = 0, mmr_single = 1; ++ int mmr_idx = 0, min_order = 3, max_order = 1; ++ const struct FFDOVIReshapeData *comp = &dovi->comp[c]; ++ ++ if (!comp->num_pivots) ++ continue; ++ ++ for (int i = 0; i < comp->num_pivots - 1; i++) { ++ float *coeffs = params->dovi_coeffs[c * 8 + i]; ++ ++ switch (comp->method[i]) { ++ case 0: ++ has_poly = 1; ++ coeffs[3] = 0.0f; ++ for (int k = 0; k < 3; k++) ++ coeffs[k] = comp->poly_coeffs[i][k]; ++ break; ++ case 1: ++ min_order = FFMIN(min_order, comp->mmr_order[i]); ++ max_order = FFMAX(max_order, comp->mmr_order[i]); ++ mmr_single = !has_mmr; ++ has_mmr = 1; ++ coeffs[3] = (float)comp->mmr_order[i]; ++ coeffs[0] = comp->mmr_constant[i]; ++ coeffs[1] = (float)mmr_idx; ++ for (int j = 0; j < comp->mmr_order[i]; j++) { ++ float *mmr = params->dovi_mmr[c * 48 + mmr_idx]; ++ mmr[0] = comp->mmr_coeffs[i][j][0]; ++ mmr[1] = comp->mmr_coeffs[i][j][1]; ++ mmr[2] = comp->mmr_coeffs[i][j][2]; ++ mmr[3] = 0.0f; ++ mmr[4] = comp->mmr_coeffs[i][j][3]; ++ mmr[5] = comp->mmr_coeffs[i][j][4]; ++ mmr[6] = comp->mmr_coeffs[i][j][5]; ++ mmr[7] = comp->mmr_coeffs[i][j][6]; ++ mmr_idx += 2; ++ } ++ break; ++ } ++ } ++ ++ params->dovi_params[c * 2 + 0][0] = comp->num_pivots; ++ params->dovi_params[c * 2 + 0][1] = !!has_mmr; ++ params->dovi_params[c * 2 + 0][2] = !!has_poly; ++ params->dovi_params[c * 2 + 0][3] = mmr_single; ++ params->dovi_params[c * 2 + 1][0] = min_order; ++ params->dovi_params[c * 2 + 1][1] = max_order; ++ params->dovi_params[c * 2 + 1][2] = comp->pivots[0]; ++ params->dovi_params[c * 2 + 1][3] = comp->pivots[comp->num_pivots - 1]; ++ ++ for (int i = 0; i < 8; i++) ++ params->dovi_pivots[c * 2 + (i >> 2)][i & 3] = i < comp->num_pivots - 2 ? comp->pivots[i + 1] : 1e9f; ++ } ++} ++ ++static int fill_params(AVFilterContext *avctx, AVFrame *input, AVFrame *output, ++ TonemapD3D11Params *params) ++{ ++ TonemapD3D11Context *s = avctx->priv; ++ const AVLumaCoefficients *luma_src, *luma_dst; ++ double rgb2yuv[3][3], yuv2rgb[3][3], rgb2rgb[3][3] = { { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 } }; ++ int depth = s->in_desc->comp[0].depth == 16 ? 12 : s->in_desc->comp[0].depth; ++ int ret; ++ ++ luma_src = av_csp_luma_coeffs_from_avcsp(input->colorspace); ++ if (!luma_src) { ++ av_log(avctx, AV_LOG_ERROR, "Unsupported input colorspace %d (%s)\n", ++ input->colorspace, av_color_space_name(input->colorspace)); ++ return AVERROR(EINVAL); ++ } ++ luma_dst = av_csp_luma_coeffs_from_avcsp(output->colorspace); ++ if (!luma_dst) { ++ av_log(avctx, AV_LOG_ERROR, "Unsupported output colorspace %d (%s)\n", ++ output->colorspace, av_color_space_name(output->colorspace)); ++ return AVERROR(EINVAL); ++ } ++ ++ ff_fill_rgb2yuv_table(luma_src, rgb2yuv); ++ ff_matrix_invert_3x3(rgb2yuv, yuv2rgb); ++ ff_fill_rgb2yuv_table(luma_dst, rgb2yuv); ++ ++ if (input->color_primaries != output->color_primaries) { ++ ret = get_rgb2rgb_matrix(input->color_primaries, output->color_primaries, rgb2rgb); ++ if (ret < 0) ++ return ret; ++ } ++ ++ memset(params, 0, sizeof(*params)); ++ params->width = input->width; ++ params->height = input->height; ++ params->tonemap = s->tonemap; ++ params->tonemap_mode = s->tonemap_mode == TONEMAP_MODE_AUTO ? TONEMAP_MODE_ITP : s->tonemap_mode; ++ params->trc_in = input->color_trc; ++ params->trc_out = output->color_trc; ++ params->full_range_in = input->color_range == AVCOL_RANGE_JPEG; ++ params->full_range_out = output->color_range == AVCOL_RANGE_JPEG; ++ params->in_depth = s->in_desc->comp[0].depth; ++ params->out_depth = s->out_desc->comp[0].depth; ++ params->chroma_loc = output->chroma_location; ++ params->skip_tonemap = output->color_trc == AVCOL_TRC_SMPTE2084; ++ params->apply_dovi = s->apply_dovi_frame; ++ params->tone_param = s->final_param; ++ params->desat_param = s->desat_param; ++ params->peak = s->src_peak; ++ params->target_peak = s->target_peak; ++ params->input_quantization_offset = QUANTIZATION_OFFSET(depth); ++ params->input_y_scale = INPUT_Y_SCALE(depth); ++ params->input_uv_scale = INPUT_UV_SCALE(depth); ++ params->output_quantization_offset = s->out_desc->comp[0].depth == 10 ? QUANTIZATION_OFFSET(10) : 0.0; ++ params->luma_src[0] = av_q2d(luma_src->cr); ++ params->luma_src[1] = av_q2d(luma_src->cg); ++ params->luma_src[2] = av_q2d(luma_src->cb); ++ params->luma_dst[0] = av_q2d(luma_dst->cr); ++ params->luma_dst[1] = av_q2d(luma_dst->cg); ++ params->luma_dst[2] = av_q2d(luma_dst->cb); ++ copy_matrix(yuv2rgb, params->rgb_matrix); ++ copy_matrix(rgb2yuv, params->yuv_matrix); ++ copy_matrix(rgb2rgb, params->rgb2rgb_matrix); ++ if (s->apply_dovi_frame) ++ fill_dovi_params(params, &s->dovi); ++ return 0; ++} ++ ++static int run_filter(AVFilterContext *ctx, AVFrame *output, AVFrame *input) ++{ ++ TonemapD3D11Context *s = ctx->priv; ++ ID3D11ShaderResourceView *srvs[2] = { NULL, NULL }; ++ ID3D11ShaderResourceView *null_srvs[2] = { NULL, NULL }; ++ ID3D11UnorderedAccessView *uavs[2] = { NULL, NULL }; ++ ID3D11UnorderedAccessView *null_uavs[2] = { NULL, NULL }; ++ ID3D11Buffer *null_cb[1] = { NULL }; ++ D3D11_MAPPED_SUBRESOURCE mapped; ++ TonemapD3D11Params params; ++ int direct_output = 0; ++ HRESULT hr; ++ int ret; ++ ++ ret = prepare_input_srvs(ctx, input, srvs); ++ if (ret < 0) ++ goto fail; ++ ret = prepare_output_uavs(ctx, output, uavs, &direct_output); ++ if (ret < 0) ++ goto fail; ++ ret = fill_params(ctx, input, output, ¶ms); ++ if (ret < 0) ++ goto fail; ++ ++ ret = ensure_shader_variant(ctx, ¶ms); ++ if (ret < 0) ++ goto fail; ++ ++ hr = s->context->lpVtbl->Map(s->context, (ID3D11Resource *)s->params_buf, ++ 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed mapping D3D11 tonemap constant buffer: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ memcpy(mapped.pData, ¶ms, sizeof(params)); ++ s->context->lpVtbl->Unmap(s->context, (ID3D11Resource *)s->params_buf, 0); ++ ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, &s->params_buf); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 2, srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, uavs, NULL); ++ s->context->lpVtbl->CSSetShader(s->context, s->cs, NULL, 0); ++ s->context->lpVtbl->Dispatch(s->context, ++ (((params.width + 1) >> 1) + TONEMAP_D3D11_TGX - 1) / TONEMAP_D3D11_TGX, ++ (((params.height + 1) >> 1) + TONEMAP_D3D11_TGY - 1) / TONEMAP_D3D11_TGY, ++ 1); ++ ++ s->context->lpVtbl->CSSetShader(s->context, NULL, NULL, 0); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 2, null_srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, null_uavs, NULL); ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, null_cb); ++ ++ if (!direct_output) ++ copy_texture_to_frame(s, s->work_tex, output); ++ ++fail: ++ s->context->lpVtbl->CSSetShader(s->context, NULL, NULL, 0); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 2, null_srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, null_uavs, NULL); ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, null_cb); ++ D3D11_RELEASE(srvs[0]); ++ D3D11_RELEASE(srvs[1]); ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ return ret; ++} ++ ++static int tonemap_d3d11_filter_frame(AVFilterLink *inlink, AVFrame *input) ++{ ++ AVFilterContext *avctx = inlink->dst; ++ AVFilterLink *outlink = avctx->outputs[0]; ++ TonemapD3D11Context *s = avctx->priv; ++ AVFrame *output = NULL; ++ int ret; ++ ++ if (!input->hw_frames_ctx) ++ return AVERROR(EINVAL); ++ ++ s->apply_dovi_frame = 0; ++ if (s->apply_dovi) { ++ AVFrameSideData *dovi_sd = av_frame_get_side_data(input, AV_FRAME_DATA_DOVI_METADATA); ++ if (dovi_sd) { ++ const AVDOVIMetadata *metadata = (const AVDOVIMetadata *)dovi_sd->data; ++ const AVDOVIRpuDataHeader *rpu = av_dovi_get_header(metadata); ++ if (rpu->disable_residual_flag) { ++ ff_map_dovi_metadata(&s->dovi, metadata); ++ s->apply_dovi_frame = 1; ++ input->color_trc = AVCOL_TRC_SMPTE2084; ++ input->colorspace = AVCOL_SPC_BT2020_NCL; ++ input->color_primaries = AVCOL_PRI_BT2020; ++ if (rpu->bl_video_full_range_flag) ++ input->color_range = AVCOL_RANGE_JPEG; ++ } ++ } ++ } ++ ++ if (input->color_trc == AVCOL_TRC_UNSPECIFIED) ++ input->color_trc = AVCOL_TRC_SMPTE2084; ++ ++ if (input->color_trc != AVCOL_TRC_SMPTE2084 && input->color_trc != AVCOL_TRC_ARIB_STD_B67) { ++ av_log(avctx, AV_LOG_ERROR, "Unsupported input transfer function: %s\n", ++ av_color_transfer_name(input->color_trc)); ++ ret = AVERROR(ENOSYS); ++ goto fail; ++ } ++ ++ if (input->colorspace == AVCOL_SPC_UNSPECIFIED) ++ input->colorspace = AVCOL_SPC_BT2020_NCL; ++ if (input->color_primaries == AVCOL_PRI_UNSPECIFIED) ++ input->color_primaries = AVCOL_PRI_BT2020; ++ if (input->color_range == AVCOL_RANGE_UNSPECIFIED) ++ input->color_range = AVCOL_RANGE_MPEG; ++ output = ff_get_video_buffer(outlink, outlink->w, outlink->h); ++ if (!output) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ret = av_frame_copy_props(output, input); ++ if (ret < 0) ++ goto fail; ++ output->colorspace = input->colorspace; ++ output->color_primaries = input->color_primaries; ++ output->color_range = input->color_range; ++ ++ if (s->peak) { ++ s->src_peak = s->peak / 10.0f * REF_WHITE_SCALE; ++ } else if (s->apply_dovi_frame) { ++ s->src_peak = ff_determine_dovi_signal_peak((const AVDOVIMetadata *)av_frame_get_side_data(input, AV_FRAME_DATA_DOVI_METADATA)->data, 0) * REF_WHITE_SCALE; ++ av_log(avctx, AV_LOG_DEBUG, "Computed DOVI signal peak: %f at pts %"PRId64"\n", ++ s->src_peak, input->pts); ++ } else { ++ s->src_peak = ff_determine_signal_peak(input) * REF_WHITE_SCALE; ++ av_log(avctx, AV_LOG_DEBUG, "Computed signal peak: %f at pts %"PRId64"\n", ++ s->src_peak, input->pts); ++ } ++ if (s->src_peak <= REF_WHITE_SCALE) ++ s->src_peak = 10.0f * REF_WHITE_SCALE; ++ ++ if (s->trc != -1) ++ output->color_trc = s->trc; ++ if (output->color_trc == AVCOL_TRC_UNSPECIFIED) ++ output->color_trc = AVCOL_TRC_BT709; ++ if (s->primaries != -1) ++ output->color_primaries = s->primaries; ++ if (output->color_primaries == AVCOL_PRI_UNSPECIFIED) ++ output->color_primaries = AVCOL_PRI_BT709; ++ if (outlink->colorspace != AVCOL_SPC_UNSPECIFIED) ++ output->colorspace = outlink->colorspace; ++ else if (s->colorspace != -1) ++ output->colorspace = s->colorspace; ++ else ++ output->colorspace = AVCOL_SPC_BT709; ++ output->color_range = outlink->color_range == AVCOL_RANGE_UNSPECIFIED ? AVCOL_RANGE_MPEG : outlink->color_range; ++ ++ ret = run_filter(avctx, output, input); ++ if (ret < 0) ++ goto fail; ++ ++ if (output->color_trc != AVCOL_TRC_SMPTE2084) { ++ av_frame_remove_side_data(output, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); ++ av_frame_remove_side_data(output, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); ++ } ++ av_frame_remove_side_data(output, AV_FRAME_DATA_DOVI_RPU_BUFFER); ++ av_frame_remove_side_data(output, AV_FRAME_DATA_DOVI_METADATA); ++ ++ av_frame_free(&input); ++ return ff_filter_frame(outlink, output); ++ ++fail: ++ av_frame_free(&input); ++ av_frame_free(&output); ++ return ret; ++} ++ ++static int tonemap_d3d11_config_input(AVFilterLink *inlink) ++{ ++ AVFilterContext *avctx = inlink->dst; ++ FilterLink *inl = ff_filter_link(inlink); ++ ++ if (!inl->hw_frames_ctx) { ++ av_log(avctx, AV_LOG_ERROR, "D3D11 tonemap requires a hardware frames context on input.\n"); ++ return AVERROR(EINVAL); ++ } ++ return 0; ++} ++ ++static int tonemap_d3d11_config_output(AVFilterLink *outlink) ++{ ++ AVFilterContext *avctx = outlink->src; ++ AVFilterLink *inlink = avctx->inputs[0]; ++ FilterLink *inl = ff_filter_link(inlink); ++ FilterLink *outl = ff_filter_link(outlink); ++ TonemapD3D11Context *s = avctx->priv; ++ AVHWFramesContext *in_frames_ctx; ++ AVHWDeviceContext *device_ctx; ++ AVD3D11VADeviceContext *d3d11_ctx; ++ AVHWFramesContext *frames_ctx; ++ AVD3D11VAFramesContext *frames_hwctx; ++ enum AVPixelFormat out_fmt; ++ int ret; ++ ++ if (!inl->hw_frames_ctx) ++ return AVERROR(EINVAL); ++ in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data; ++ out_fmt = s->format == AV_PIX_FMT_NONE ? in_frames_ctx->sw_format : s->format; ++ ++ if (!format_is_supported(in_frames_ctx->sw_format)) { ++ av_log(avctx, AV_LOG_ERROR, "Unsupported input format: %s\n", ++ av_get_pix_fmt_name(in_frames_ctx->sw_format)); ++ return AVERROR(ENOSYS); ++ } ++ if (!format_is_supported(out_fmt)) { ++ av_log(avctx, AV_LOG_ERROR, "Unsupported output format: %s\n", ++ av_get_pix_fmt_name(out_fmt)); ++ return AVERROR(ENOSYS); ++ } ++ ++ s->in_fmt = in_frames_ctx->sw_format; ++ s->out_fmt = out_fmt; ++ s->in_desc = av_pix_fmt_desc_get(s->in_fmt); ++ s->out_desc = av_pix_fmt_desc_get(s->out_fmt); ++ if (s->in_desc->comp[0].depth != 10 && s->in_desc->comp[0].depth != 16) { ++ av_log(avctx, AV_LOG_ERROR, "Unsupported input format depth: %d\n", ++ s->in_desc->comp[0].depth); ++ return AVERROR(ENOSYS); ++ } ++ ++ if (!s->hw_device_ctx) { ++ s->hw_device_ctx = av_buffer_ref(in_frames_ctx->device_ref); ++ if (!s->hw_device_ctx) ++ return AVERROR(ENOMEM); ++ } ++ device_ctx = (AVHWDeviceContext *)s->hw_device_ctx->data; ++ d3d11_ctx = (AVD3D11VADeviceContext *)device_ctx->hwctx; ++ s->device = d3d11_ctx->device; ++ s->context = d3d11_ctx->device_context; ++ ++ outlink->w = inlink->w; ++ outlink->h = inlink->h; ++ outlink->time_base = inlink->time_base; ++ s->direct_output_uav = -1; ++ s->direct_input_srv = -1; ++ s->shader_tonemap = s->shader_mode = s->shader_trc_in = s->shader_trc_out = -1; ++ s->shader_full_in = s->shader_full_out = s->shader_out_depth = s->shader_skip = s->shader_dovi = -1; ++ for (int direct = !s->force_output_copy; direct >= 0; direct--) { ++ s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx); ++ if (!s->hw_frames_ctx_out) ++ return AVERROR(ENOMEM); ++ ++ frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx_out->data; ++ frames_ctx->format = AV_PIX_FMT_D3D11; ++ frames_ctx->sw_format = s->out_fmt; ++ frames_ctx->width = outlink->w; ++ frames_ctx->height = outlink->h; ++ frames_ctx->initial_pool_size = 10; ++ if (avctx->extra_hw_frames > 0) ++ frames_ctx->initial_pool_size += avctx->extra_hw_frames; ++ ++ frames_hwctx = frames_ctx->hwctx; ++ frames_hwctx->MiscFlags = 0; ++ frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; ++ if (direct) ++ frames_hwctx->BindFlags |= D3D11_BIND_UNORDERED_ACCESS; ++ if (s->out_fmt == AV_PIX_FMT_NV12) ++ frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER; ++ ++ ret = av_hwframe_ctx_init(s->hw_frames_ctx_out); ++ if (ret >= 0 && direct) { ++ ret = probe_output_uav_pool(avctx, s->hw_frames_ctx_out); ++ if (ret >= 0) { ++ s->direct_output_uav = 1; ++ av_log(avctx, AV_LOG_DEBUG, "D3D11 shader output: direct UAV\n"); ++ break; ++ } ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ av_log(avctx, AV_LOG_DEBUG, "D3D11 shader output: copied from internal UAV texture\n"); ++ continue; ++ } else if (ret >= 0) { ++ s->direct_output_uav = 0; ++ break; ++ } ++ ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ if (direct) ++ av_log(avctx, AV_LOG_DEBUG, "D3D11 shader output: UAV encoder pool rejected, using internal UAV texture\n"); ++ } ++ if (ret < 0) ++ return ret; ++ if (s->force_output_copy) ++ av_log(avctx, AV_LOG_DEBUG, "D3D11 shader output: forced copy from internal UAV texture\n"); ++ ++ av_buffer_unref(&outl->hw_frames_ctx); ++ outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out); ++ if (!outl->hw_frames_ctx) ++ return AVERROR(ENOMEM); ++ ++ if (s->trc != AVCOL_TRC_SMPTE2084 && s->trc != -1) { ++ av_frame_side_data_remove(&outlink->side_data, &outlink->nb_side_data, ++ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); ++ av_frame_side_data_remove(&outlink->side_data, &outlink->nb_side_data, ++ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); ++ } ++ return 0; ++} ++ ++static av_cold int tonemap_d3d11_preinit(AVFilterContext *avctx) ++{ ++ TonemapD3D11Context *s = avctx->priv; ++ s->final_param = NAN; ++ return 0; ++} ++ ++static av_cold int tonemap_d3d11_init(AVFilterContext *avctx) ++{ ++ TonemapD3D11Context *s = avctx->priv; ++ ++ if (s->tonemap_mode == TONEMAP_MODE_AUTO) ++ s->tonemap_mode = TONEMAP_MODE_ITP; ++ ++ switch (s->tonemap) { ++ case TONEMAP_GAMMA: ++ if (isnan(s->param)) ++ s->final_param = 1.8f; ++ break; ++ case TONEMAP_REINHARD: ++ if (!isnan(s->param)) ++ s->final_param = (1.0f - s->param) / s->param; ++ break; ++ case TONEMAP_MOBIUS: ++ if (isnan(s->param)) ++ s->final_param = 0.3f; ++ break; ++ case TONEMAP_BT2390: ++ if (isnan(s->param)) ++ s->final_param = 1.0f; ++ else ++ s->final_param = av_clipd(s->param, 0.5f, 2.0f); ++ break; ++ } ++ if (isnan(s->final_param)) ++ s->final_param = 1.0f; ++ ++ s->target_peak = 1.0f; ++ return 0; ++} ++ ++static av_cold void tonemap_d3d11_uninit(AVFilterContext *avctx) ++{ ++ TonemapD3D11Context *s = avctx->priv; ++ release_d3d11_resources(s); ++ av_buffer_unref(&s->hw_device_ctx); ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ if (s->d3dcompiler) { ++ FreeLibrary(s->d3dcompiler); ++ s->d3dcompiler = NULL; ++ } ++} ++ ++static int tonemap_d3d11_query_formats(AVFilterContext *avctx) ++{ ++ TonemapD3D11Context *s = avctx->priv; ++ AVFilterFormats *formats; ++ int ret; ++ const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_D3D11, AV_PIX_FMT_NONE }; ++ ++ // single format ++ formats = ff_make_format_list(pix_fmts); ++ ret = ff_formats_ref(formats, &avctx->inputs[0]->outcfg.formats); ++ if (ret < 0) ++ return ret; ++ ++ ret = ff_formats_ref(formats, &avctx->outputs[0]->incfg.formats); ++ if (ret < 0) ++ return ret; ++ ++ // colorspaces and ranges ++ if ((ret = ff_formats_ref(ff_all_color_spaces(), ++ &avctx->inputs[0]->outcfg.color_spaces)) < 0) ++ return ret; ++ ++ if ((ret = ff_formats_ref(ff_all_color_ranges(), ++ &avctx->inputs[0]->outcfg.color_ranges)) < 0) ++ return ret; ++ ++ formats = s->colorspace != -1 ++ ? ff_make_formats_list_singleton(s->colorspace) ++ : ff_make_format_list(colorspaces_out); ++ if ((ret = ff_formats_ref(formats, &avctx->outputs[0]->incfg.color_spaces)) < 0) ++ return ret; ++ ++ formats = s->range != -1 ++ ? ff_make_formats_list_singleton(s->range) ++ : ff_all_color_ranges(); ++ if ((ret = ff_formats_ref(formats, &avctx->outputs[0]->incfg.color_ranges)) < 0) ++ return ret; ++ ++ return 0; ++} ++ ++#define OFFSET(x) offsetof(TonemapD3D11Context, x) ++#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) ++static const AVOption tonemap_d3d11_options[] = { ++ { "tonemap", "Tonemap algorithm selection", OFFSET(tonemap), AV_OPT_TYPE_INT, { .i64 = TONEMAP_BT2390 }, TONEMAP_NONE, TONEMAP_COUNT - 1, FLAGS, "tonemap" }, ++ { "none", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_NONE }, 0, 0, FLAGS, "tonemap" }, ++ { "linear", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_LINEAR }, 0, 0, FLAGS, "tonemap" }, ++ { "gamma", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_GAMMA }, 0, 0, FLAGS, "tonemap" }, ++ { "clip", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_CLIP }, 0, 0, FLAGS, "tonemap" }, ++ { "reinhard", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_REINHARD }, 0, 0, FLAGS, "tonemap" }, ++ { "hable", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_HABLE }, 0, 0, FLAGS, "tonemap" }, ++ { "mobius", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_MOBIUS }, 0, 0, FLAGS, "tonemap" }, ++ { "bt2390", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_BT2390 }, 0, 0, FLAGS, "tonemap" }, ++ { "tonemap_mode", "Tonemap mode selection", OFFSET(tonemap_mode), AV_OPT_TYPE_INT, { .i64 = TONEMAP_MODE_AUTO }, TONEMAP_MODE_MAX, TONEMAP_MODE_COUNT - 1, FLAGS, "tonemap_mode" }, ++ { "max", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_MODE_MAX }, 0, 0, FLAGS, "tonemap_mode" }, ++ { "rgb", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_MODE_RGB }, 0, 0, FLAGS, "tonemap_mode" }, ++ { "lum", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_MODE_LUM }, 0, 0, FLAGS, "tonemap_mode" }, ++ { "itp", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_MODE_ITP }, 0, 0, FLAGS, "tonemap_mode" }, ++ { "auto", 0, 0, AV_OPT_TYPE_CONST, { .i64 = TONEMAP_MODE_AUTO }, 0, 0, FLAGS, "tonemap_mode" }, ++ { "transfer", "Set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, { .i64 = AVCOL_TRC_BT709 }, -1, INT_MAX, FLAGS, "transfer" }, ++ { "t", "Set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, { .i64 = AVCOL_TRC_BT709 }, -1, INT_MAX, FLAGS, "transfer" }, ++ { "bt709", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_TRC_BT709 }, 0, 0, FLAGS, "transfer" }, ++ { "bt2020", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_TRC_BT2020_10 }, 0, 0, FLAGS, "transfer" }, ++ { "smpte2084", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_TRC_SMPTE2084 }, 0, 0, FLAGS, "transfer" }, ++ { "matrix", "Set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_BT709 }, -1, INT_MAX, FLAGS, "matrix" }, ++ { "m", "Set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_BT709 }, -1, INT_MAX, FLAGS, "matrix" }, ++ { "bt709", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT709 }, 0, 0, FLAGS, "matrix" }, ++ { "bt2020", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT2020_NCL }, 0, 0, FLAGS, "matrix" }, ++ { "primaries", "Set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, { .i64 = AVCOL_PRI_BT709 }, -1, INT_MAX, FLAGS, "primaries" }, ++ { "p", "Set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, { .i64 = AVCOL_PRI_BT709 }, -1, INT_MAX, FLAGS, "primaries" }, ++ { "bt709", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_PRI_BT709 }, 0, 0, FLAGS, "primaries" }, ++ { "bt2020", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_PRI_BT2020 }, 0, 0, FLAGS, "primaries" }, ++ { "range", "Set color range", OFFSET(range), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS, "range" }, ++ { "r", "Set color range", OFFSET(range), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS, "range" }, ++ { "tv", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, ++ { "pc", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, ++ { "limited", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, ++ { "full", 0, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, ++ { "format", "Output pixel format", OFFSET(format), AV_OPT_TYPE_PIXEL_FMT, { .i64 = AV_PIX_FMT_NONE }, AV_PIX_FMT_NONE, INT_MAX, FLAGS, "fmt" }, ++ { "apply_dovi", "Apply Dolby Vision metadata if possible", OFFSET(apply_dovi), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, ++ { "force_output_copy", "Force output through an internal unordered-access texture", OFFSET(force_output_copy), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, ++ { "peak", "Signal peak override", OFFSET(peak), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, DBL_MAX, FLAGS }, ++ { "param", "Tonemap parameter", OFFSET(param), AV_OPT_TYPE_DOUBLE, { .dbl = NAN }, DBL_MIN, DBL_MAX, FLAGS }, ++ { "desat", "Desaturation parameter", OFFSET(desat_param), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, DBL_MAX, FLAGS }, ++ { NULL } ++}; ++ ++AVFILTER_DEFINE_CLASS(tonemap_d3d11); ++ ++static const AVFilterPad tonemap_d3d11_inputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .filter_frame = tonemap_d3d11_filter_frame, ++ .config_props = tonemap_d3d11_config_input, ++ }, ++}; ++ ++static const AVFilterPad tonemap_d3d11_outputs[] = { ++ { ++ .name = "default", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .config_props = tonemap_d3d11_config_output, ++ }, ++}; ++ ++const FFFilter ff_vf_tonemap_d3d11 = { ++ .p.name = "tonemap_d3d11", ++ .p.description = NULL_IF_CONFIG_SMALL("Perform HDR to SDR conversion with tonemapping on D3D11 frames."), ++ .priv_size = sizeof(TonemapD3D11Context), ++ .p.priv_class = &tonemap_d3d11_class, ++ .preinit = tonemap_d3d11_preinit, ++ .init = tonemap_d3d11_init, ++ .uninit = tonemap_d3d11_uninit, ++ FILTER_INPUTS(tonemap_d3d11_inputs), ++ FILTER_OUTPUTS(tonemap_d3d11_outputs), ++ FILTER_QUERY_FUNC(tonemap_d3d11_query_formats), ++ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, ++ .p.flags = AVFILTER_FLAG_HWDEVICE, ++}; +Index: FFmpeg/libavfilter/vf_tonemap_d3d11.h +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/vf_tonemap_d3d11.h +@@ -0,0 +1,29 @@ ++/* ++ * D3D11 tonemap filter shader constants ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVFILTER_VF_TONEMAP_D3D11_H ++#define AVFILTER_VF_TONEMAP_D3D11_H ++ ++#define TONEMAP_D3D11_TGX 16 ++#define TONEMAP_D3D11_TGY 16 ++ ++#endif /* AVFILTER_VF_TONEMAP_D3D11_H */ +Index: FFmpeg/libavfilter/d3d11/tonemap.hlsl +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/d3d11/tonemap.hlsl +@@ -0,0 +1,629 @@ ++/* ++ * D3D11 tonemap ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++Texture2DArray src_y : register(t0); ++Texture2DArray src_uv : register(t1); ++ ++RWTexture2DArray dst_y : register(u0); ++RWTexture2DArray dst_uv : register(u1); ++ ++cbuffer Params : register(b0) { ++ int width; ++ int height; ++ int tonemap; ++ int tonemap_mode; ++ int trc_in; ++ int trc_out; ++ int full_range_in; ++ int full_range_out; ++ int in_depth; ++ int out_depth; ++ int chroma_loc; ++ int skip_tonemap; ++ int apply_dovi; ++ int pad_i0; ++ int pad_i1; ++ int pad_i2; ++ float tone_param; ++ float desat_param; ++ float peak; ++ float target_peak; ++ float input_quantization_offset; ++ float input_y_scale; ++ float input_uv_scale; ++ float output_quantization_offset; ++ float3 luma_src; float pad0; ++ float3 luma_dst; float pad1; ++ row_major float3x3 rgb_matrix; ++ row_major float3x3 yuv_matrix; ++ row_major float3x3 rgb2rgb_matrix; ++ float3 dovi_ycc2rgb_offset; float dovi_pad0; ++ row_major float3x3 dovi_rgb_matrix; ++ row_major float3x3 dovi_lms2rgb_matrix; ++ float4 dovi_params[6]; ++ float4 dovi_pivots[6]; ++ float4 dovi_coeffs[24]; ++ float4 dovi_mmr[144]; ++}; ++ ++// d3d compiler at optimization level 3 would remove dead branches when comparing with those constants ++#ifndef TONEMAP_ALG ++#define TONEMAP_ALG tonemap ++#endif ++#ifndef TONE_MODE ++#define TONE_MODE tonemap_mode ++#endif ++#ifndef INPUT_TRC ++#define INPUT_TRC trc_in ++#endif ++#ifndef OUTPUT_TRC ++#define OUTPUT_TRC trc_out ++#endif ++#ifndef FULL_RANGE_IN_VAL ++#define FULL_RANGE_IN_VAL full_range_in ++#endif ++#ifndef FULL_RANGE_OUT_VAL ++#define FULL_RANGE_OUT_VAL full_range_out ++#endif ++#ifndef OUT_DEPTH_VAL ++#define OUT_DEPTH_VAL out_depth ++#endif ++#ifndef SKIP_TONEMAP_VAL ++#define SKIP_TONEMAP_VAL skip_tonemap ++#endif ++#ifndef APPLY_DOVI_VAL ++#define APPLY_DOVI_VAL apply_dovi ++#endif ++ ++#define FLOAT_EPS 1e-6 ++#define ST2084_MAX_LUMINANCE 10000.0 ++#define ARIB_B67_MAX_LUMINANCE 1000.0 ++#define REFERENCE_WHITE_ALT 203.0 ++#define ST2084_M1 0.1593017578125 ++#define ST2084_M2 78.84375 ++#define ST2084_C1 0.8359375 ++#define ST2084_C2 18.8515625 ++#define ST2084_C3 18.6875 ++#define ARIB_B67_A 0.17883277 ++#define ARIB_B67_B 0.28466892 ++#define ARIB_B67_C 0.55991073 ++#define BT2446B_HLG_LW 291.0 ++#define BT2446B_HLG_GAMMA 1.03 ++ ++float eotf_st2084_common(float x) ++{ ++ x = max(x, 0.0); ++ float xpow = pow(x, 1.0 / ST2084_M2); ++ float num = max(xpow - ST2084_C1, 0.0); ++ float den = max(ST2084_C2 - ST2084_C3 * xpow, FLOAT_EPS); ++ return pow(num / den, 1.0 / ST2084_M1); ++} ++ ++float eotf_st2084(float x) ++{ ++ return eotf_st2084_common(x) * ST2084_MAX_LUMINANCE / REFERENCE_WHITE_ALT; ++} ++ ++float inverse_eotf_st2084_common(float x) ++{ ++ x = max(x, 0.0); ++ float xpow = pow(x, ST2084_M1); ++ float num = (ST2084_C1 - 1.0) + (ST2084_C2 - ST2084_C3) * xpow; ++ float den = 1.0 + ST2084_C3 * xpow; ++ return pow(1.0 + num / den, ST2084_M2); ++} ++ ++float inverse_eotf_st2084(float x) ++{ ++ return inverse_eotf_st2084_common(x * REFERENCE_WHITE_ALT / ST2084_MAX_LUMINANCE); ++} ++ ++float3 eotf_st2084x3(float3 x) ++{ ++ return float3(eotf_st2084_common(x.x), ++ eotf_st2084_common(x.y), ++ eotf_st2084_common(x.z)) * ST2084_MAX_LUMINANCE / REFERENCE_WHITE_ALT; ++} ++ ++float3 inverse_eotf_bt1886x3(float3 x) ++{ ++ return float3(x.x > 0.0 ? pow(x.x, 1.0 / 2.4) : 0.0, ++ x.y > 0.0 ? pow(x.y, 1.0 / 2.4) : 0.0, ++ x.z > 0.0 ? pow(x.z, 1.0 / 2.4) : 0.0); ++} ++ ++float get_luma_src(float3 c) ++{ ++ return dot(luma_src, c); ++} ++ ++float get_luma_dst(float3 c) ++{ ++ return dot(luma_dst, c); ++} ++ ++float3 eotf_arib_b67x3(float3 x) ++{ ++ float pk = ARIB_B67_MAX_LUMINANCE / REFERENCE_WHITE_ALT; ++ float gamma = 1.2; ++ ++ if (OUTPUT_TRC != 16) { ++ pk = BT2446B_HLG_LW / REFERENCE_WHITE_ALT; ++ gamma = BT2446B_HLG_GAMMA; ++ } ++ ++ float3 a = 4.0 * x * x; ++ float3 b = exp((x - ARIB_B67_C) * (1.0 / ARIB_B67_A)) + ARIB_B67_B; ++ x = float3(x.x > 0.5 ? b.x : a.x, ++ x.y > 0.5 ? b.y : a.y, ++ x.z > 0.5 ? b.z : a.z); ++ x *= 1.0 / 12.0; ++ ++ float l = get_luma_src(x); ++ return x * pk * pow(max(l, 0.0), gamma - 1.0); ++} ++ ++float3 linearize(float3 c) ++{ ++ return INPUT_TRC == 18 ? eotf_arib_b67x3(c) : eotf_st2084x3(c); ++} ++ ++float3 delinearize(float3 c) ++{ ++ return OUTPUT_TRC == 16 ? c : inverse_eotf_bt1886x3(c); ++} ++ ++float hable_f(float x) ++{ ++ float a = 0.15, b = 0.50, c = 0.10, d = 0.20, e = 0.02, f = 0.30; ++ return (x * (x * a + b * c) + d * e) / (x * (x * a + b) + d * f) - e / f; ++} ++ ++float tone_direct(float s, float pk, float tpk) ++{ ++ return s; ++} ++ ++float tone_linear(float s, float pk, float tpk) ++{ ++ return s * tone_param / pk; ++} ++ ++float tone_gamma(float s, float pk, float tpk) ++{ ++ float p = s > 0.05 ? s / pk : 0.05 / pk; ++ float v = pow(p, 1.0 / tone_param); ++ return s > 0.05 ? v : (s * v / 0.05); ++} ++ ++float tone_clip(float s, float pk, float tpk) ++{ ++ return saturate(s * tone_param); ++} ++ ++float tone_reinhard(float s, float pk, float tpk) ++{ ++ return s / (s + tone_param) * (pk + tone_param) / pk; ++} ++ ++float tone_hable(float s, float pk, float tpk) ++{ ++ return hable_f(s) / hable_f(pk); ++} ++ ++float tone_mobius(float s, float pk, float tpk) ++{ ++ float j = tone_param; ++ ++ if (s <= j) ++ return s; ++ ++ float a = -j * j * (pk - 1.0) / (j * j - 2.0 * j + pk); ++ float b = (j * j - 2.0 * j * pk + pk) / max(pk - 1.0, FLOAT_EPS); ++ return (b * b + 2.0 * b * j + j * j) / (b - a) * (s + a) / (s + b); ++} ++ ++float tone_bt2390(float s, float pk_pq, float tpk_pq) ++{ ++ float scale = pk_pq > 0.0 ? 1.0 / pk_pq : 1.0; ++ float spq = s * scale; ++ float max_lum = tpk_pq * scale; ++ ++ float ks = (1.0 + tone_param) * max_lum - tone_param; ++ float tb = (spq - ks) / (1.0 - ks); ++ float tb2 = tb * tb; ++ float tb3 = tb2 * tb; ++ float pb = (2.0 * tb3 - 3.0 * tb2 + 1.0) * ks + ++ (tb3 - 2.0 * tb2 + tb) * (1.0 - ks) + ++ (-2.0 * tb3 + 3.0 * tb2) * max_lum; ++ float sig = spq < ks ? spq : pb; ++ ++ return sig * pk_pq; ++} ++ ++float tone_func(float s, float pk, float tpk) ++{ ++ if (TONEMAP_ALG == 1) ++ return tone_linear(s, pk, tpk); ++ if (TONEMAP_ALG == 2) ++ return tone_gamma(s, pk, tpk); ++ if (TONEMAP_ALG == 3) ++ return tone_clip(s, pk, tpk); ++ if (TONEMAP_ALG == 4) ++ return tone_reinhard(s, pk, tpk); ++ if (TONEMAP_ALG == 5) ++ return tone_hable(s, pk, tpk); ++ if (TONEMAP_ALG == 6) ++ return tone_mobius(s, pk, tpk); ++ if (TONEMAP_ALG == 7) ++ return tone_bt2390(s, pk, tpk); ++ return tone_direct(s, pk, tpk); ++} ++ ++float3 yuv2rgb(float y, float u, float v) ++{ ++ if (y > 0.0) ++ y += input_quantization_offset; ++ if (u > 0.0) ++ u += input_quantization_offset; ++ if (v > 0.0) ++ v += input_quantization_offset; ++ ++ if (FULL_RANGE_IN_VAL == 0) { ++ y = input_y_scale * y - 0.07305936073; ++ u = input_uv_scale * u - 0.5714285714; ++ v = input_uv_scale * v - 0.5714285714; ++ } else { ++ u -= 0.5; ++ v -= 0.5; ++ } ++ ++ return mul(rgb_matrix, float3(y, u, v)); ++} ++ ++float3 rgb2yuv(float3 c) ++{ ++ float3 yuv = mul(yuv_matrix, c); ++ ++ if (FULL_RANGE_OUT_VAL == 0) { ++ if (OUT_DEPTH_VAL > 8) { ++ yuv.x = floor(((219.0 * yuv.x + 16.0) * 256.0) + 0.5) / 65535.0; ++ yuv.y = floor(((224.0 * yuv.y + 128.0) * 256.0) + 0.5) / 65535.0; ++ yuv.z = floor(((224.0 * yuv.z + 128.0) * 256.0) + 0.5) / 65535.0; ++ } else { ++ yuv.x = floor((219.0 * yuv.x + 16.0) + 0.5) / 255.0; ++ yuv.y = floor((224.0 * yuv.y + 128.0) + 0.5) / 255.0; ++ yuv.z = floor((224.0 * yuv.z + 128.0) + 0.5) / 255.0; ++ } ++ } else { ++ yuv.yz += 0.5; ++ } ++ ++ if (yuv.x > 0.0) ++ yuv.x -= output_quantization_offset; ++ if (yuv.y > 0.0) ++ yuv.y -= output_quantization_offset; ++ if (yuv.z > 0.0) ++ yuv.z -= output_quantization_offset; ++ ++ return saturate(yuv); ++} ++ ++float3 lrgb2lrgb(float3 c) ++{ ++ return mul(rgb2rgb_matrix, c); ++} ++ ++float parabolic(float x, float t0, float x0, float y0) ++{ ++ float s = (y0 - t0) / sqrt(x0 - y0); ++ float ox = t0 - s * s * 0.25; ++ float oy = t0 - s * sqrt(s * s * 0.25); ++ return x < t0 ? x : s * sqrt(x - ox) + oy; ++} ++ ++float3 gamut_compress(float3 rgb) ++{ ++ float ac = max(max(rgb.x, rgb.y), rgb.z); ++ float3 d = ac == 0.0 ? float3(0.0, 0.0, 0.0) : (float3(ac, ac, ac) - rgb) / abs(ac); ++ float3 cd = float3(parabolic(d.x, 1.050508660266247, 1.5187050250638159, 1.0), ++ parabolic(d.y, 0.940509816042432, 1.0750082769546088, 1.0), ++ parabolic(d.z, 0.9771607996420639, 1.0887800403483898, 1.0)); ++ cd = min(cd, 1.0); ++ return float3(ac, ac, ac) - cd * abs(ac); ++} ++ ++void lrgb2ictcp(float3 c, out float i, out float ct, out float cp) ++{ ++ float l = 0.412109375 * c.x + 0.52392578125 * c.y + 0.06396484375 * c.z; ++ float m = 0.166748046875 * c.x + 0.720458984375 * c.y + 0.11279296875 * c.z; ++ float s = 0.024169921875 * c.x + 0.075439453125 * c.y + 0.900390625 * c.z; ++ ++ l = inverse_eotf_st2084(l); ++ m = inverse_eotf_st2084(m); ++ s = inverse_eotf_st2084(s); ++ ++ i = 0.5 * l + 0.5 * m; ++ ct = 1.61376953125 * l - 3.323486328125 * m + 1.709716796875 * s; ++ cp = 4.378173828125 * l - 4.24560546875 * m - 0.132568359375 * s; ++} ++ ++float3 ictcp2lrgb(float i, float ct, float cp) ++{ ++ float l = i + 0.008609037037933 * ct + 0.111029625003026 * cp; ++ float m = i - 0.008609037037933 * ct - 0.111029625003026 * cp; ++ float s = i + 0.560031335710679 * ct - 0.320627174987319 * cp; ++ ++ l = eotf_st2084(l); ++ m = eotf_st2084(m); ++ s = eotf_st2084(s); ++ ++ return float3( 3.436606694333079 * l - 2.506452118656270 * m + 0.069845424323191 * s, ++ -0.791329555598929 * l + 1.983600451792291 * m - 0.192270896193362 * s, ++ -0.025949899690593 * l - 0.098913714711726 * m + 1.124863614402319 * s); ++} ++ ++float3 map_rgb(float3 c) ++{ ++ float sig = max(max(c.x, max(c.y, c.z)), FLOAT_EPS); ++ ++ if (TONE_MODE == 1) { ++ float3 so = max(c, float3(FLOAT_EPS, FLOAT_EPS, FLOAT_EPS)); ++ float3 sn = so; ++ if (TONEMAP_ALG == 7) { ++ float sp = inverse_eotf_st2084(peak); ++ float dp = inverse_eotf_st2084(target_peak); ++ sn = float3(tone_func(inverse_eotf_st2084(min(so.x, peak)), sp, dp), ++ tone_func(inverse_eotf_st2084(min(so.y, peak)), sp, dp), ++ tone_func(inverse_eotf_st2084(min(so.z, peak)), sp, dp)); ++ sn = float3(eotf_st2084(sn.x), eotf_st2084(sn.y), eotf_st2084(sn.z)); ++ } else { ++ sn = float3(tone_func(so.x, peak, target_peak), ++ tone_func(so.y, peak, target_peak), ++ tone_func(so.z, peak, target_peak)); ++ } ++ sn = min(sn, float3(1.0, 1.0, 1.0)); ++ return c * (sn / so); ++ } ++ ++ float so = TONE_MODE == 0 ? sig : max(dot(luma_src, c), FLOAT_EPS); ++ float sn; ++ if (TONEMAP_ALG == 7) { ++ float sp = inverse_eotf_st2084(peak); ++ float dp = inverse_eotf_st2084(target_peak); ++ sn = eotf_st2084(tone_func(inverse_eotf_st2084(min(so, peak)), sp, dp)); ++ } else { ++ sn = tone_func(so, peak, target_peak); ++ } ++ sn = min(sn, 1.0); ++ return c * (sn / so); ++} ++ ++float3 map_itp(float3 c) ++{ ++ if (TONEMAP_ALG == 7) ++ c = min(c, peak); ++ ++ float i, ct, cp; ++ lrgb2ictcp(c, i, ct, cp); ++ float io = max(i, FLOAT_EPS); ++ ++ if (desat_param > 0.0) { ++ float coeff = exp(-pow(eotf_st2084(i) - (target_peak - desat_param) * 0.5, 2.0) / (2.0 * peak)); ++ ct *= coeff; ++ cp *= coeff; ++ } ++ ++ if (TONEMAP_ALG == 7) { ++ i = tone_func(i, inverse_eotf_st2084(peak), inverse_eotf_st2084(target_peak)); ++ } else { ++ i = eotf_st2084(i); ++ i = tone_func(i, peak, target_peak); ++ i = inverse_eotf_st2084(i); ++ } ++ i = min(i, 1.0); ++ ++ float factor = min(i / io, io / i); ++ ct *= factor; ++ cp *= factor; ++ ++ return ictcp2lrgb(i, ct, cp); ++} ++ ++float3 dovi_ycc2rgb(float3 yuv) ++{ ++ return mul(dovi_rgb_matrix, yuv) + dovi_ycc2rgb_offset; ++} ++ ++float3 dovi_lms2rgb(float3 c) ++{ ++ c = float3(eotf_st2084_common(c.x), ++ eotf_st2084_common(c.y), ++ eotf_st2084_common(c.z)); ++ c = mul(dovi_lms2rgb_matrix, c); ++ return float3(inverse_eotf_st2084_common(c.x), ++ inverse_eotf_st2084_common(c.y), ++ inverse_eotf_st2084_common(c.z)); ++} ++ ++float dovi_poly(float s, float4 coeffs) ++{ ++ return (coeffs.z * s + coeffs.y) * s + coeffs.x; ++} ++ ++float dovi_mmr_eval(float3 sig, float4 coeffs, int base_idx, int mmr_single, ++ int min_order, int max_order) ++{ ++ int idx = mmr_single != 0 ? 0 : (int)coeffs.y; ++ int order = (int)coeffs.w; ++ ++ float4 sx = float4(sig.x * sig.y, sig.x * sig.z, sig.y * sig.z, ++ sig.x * sig.y * sig.z); ++ float r = coeffs.x + dot(dovi_mmr[base_idx + idx + 0].xyz, sig) + ++ dot(dovi_mmr[base_idx + idx + 1], sx); ++ ++ if (max_order >= 2 && (min_order >= 2 || order >= 2)) { ++ float3 sig2 = sig * sig; ++ float4 sx2 = sx * sx; ++ r += dot(dovi_mmr[base_idx + idx + 2].xyz, sig2) + ++ dot(dovi_mmr[base_idx + idx + 3], sx2); ++ ++ if (max_order == 3 && (min_order == 3 || order >= 3)) ++ r += dot(dovi_mmr[base_idx + idx + 4].xyz, sig2 * sig) + ++ dot(dovi_mmr[base_idx + idx + 5], sx2 * sx); ++ } ++ ++ return r; ++} ++ ++float dovi_reshape_channel(float3 sig, int ch) ++{ ++ int po = ch * 2; ++ int co = ch * 8; ++ int mo = ch * 48; ++ ++ float4 p0 = dovi_params[po + 0]; ++ float4 p1 = dovi_params[po + 1]; ++ ++ int num = (int)p0.x; ++ if (num <= 0) ++ return sig[ch]; ++ ++ int has_mmr = (int)p0.y; ++ int has_poly = (int)p0.z; ++ int mmr_single = (int)p0.w; ++ int min_order = (int)p1.x; ++ int max_order = (int)p1.y; ++ float lo = p1.z; ++ float hi = p1.w; ++ ++ float s = clamp(sig[ch], 0.0, 1.0); ++ ++ int ci = 0; ++ if (num > 2) { ++ [unroll] for (int i = 0; i < 7; i++) ++ ci += s >= dovi_pivots[po + (i >> 2)][i & 3] ? 1 : 0; ++ } ++ ++ float4 coeffs = dovi_coeffs[co + ci]; ++ bool poly = (has_mmr != 0 && has_poly != 0) ? coeffs.w == 0.0 : has_poly != 0; ++ float r = poly ? dovi_poly(s, coeffs) ++ : dovi_mmr_eval(sig, coeffs, mo, mmr_single, min_order, max_order); ++ ++ return clamp(r, lo, hi); ++} ++ ++float3 dovi_reshape(float3 yuv) ++{ ++ float3 sig = clamp(yuv, 0.0, 1.0); ++ return float3(dovi_reshape_channel(sig, 0), ++ dovi_reshape_channel(sig, 1), ++ dovi_reshape_channel(sig, 2)); ++} ++ ++float3 process(float3 yuv) ++{ ++ if (APPLY_DOVI_VAL != 0) ++ yuv = dovi_reshape(yuv); ++ ++ float3 c; ++ if (APPLY_DOVI_VAL != 0) { ++ c = dovi_ycc2rgb(yuv); ++ c = dovi_lms2rgb(c); ++ c = linearize(c); ++ } else { ++ c = yuv2rgb(yuv.x, yuv.y, yuv.z); ++ c = linearize(c); ++ } ++ ++ if (TONE_MODE == 0 || TONE_MODE == 1) ++ c = lrgb2lrgb(c); ++ ++ if (SKIP_TONEMAP_VAL == 0) { ++ if (desat_param > 0.0 && TONE_MODE != 3) { ++ float sig = max(max(c.x, max(c.y, c.z)), FLOAT_EPS); ++ float luma = TONE_MODE == 2 ? max(dot(luma_src, c), FLOAT_EPS) : get_luma_dst(c); ++ float coeff = max(sig - 0.18, FLOAT_EPS) / max(sig, FLOAT_EPS); ++ coeff = pow(coeff, 10.0 / desat_param); ++ c = lerp(c, float3(luma, luma, luma), float3(coeff, coeff, coeff)); ++ } ++ c = TONE_MODE == 3 ? map_itp(c) : map_rgb(c); ++ } ++ ++ if (TONE_MODE != 0 && TONE_MODE != 1) ++ c = lrgb2lrgb(c); ++ if (TONE_MODE != 0 && TONE_MODE != 1) ++ c = gamut_compress(c); ++ ++ c = saturate(c); ++ return rgb2yuv(delinearize(c)); ++} ++ ++float3 chroma_sample(float3 a, float3 b, float3 c, float3 d) ++{ ++ if (chroma_loc == 1) ++ return (a + c) * 0.5; ++ if (chroma_loc == 3) ++ return a; ++ if (chroma_loc == 4) ++ return (a + b) * 0.5; ++ if (chroma_loc == 5) ++ return c; ++ if (chroma_loc == 6) ++ return (c + d) * 0.5; ++ return (a + b + c + d) * 0.25; ++} ++ ++[numthreads(16, 16, 1)] ++void tonemap_main(uint3 id : SV_DispatchThreadID) ++{ ++ uint cw = (width + 1) >> 1; ++ uint ch = (height + 1) >> 1; ++ uint x = id.x << 1; ++ uint y = id.y << 1; ++ ++ if (id.x >= cw || id.y >= ch || x >= (uint)width || y >= (uint)height) ++ return; ++ ++ float2 uv = src_uv.Load(int4(id.x, id.y, 0, 0)).rg; ++ ++ float3 yuv0 = float3(src_y.Load(int4(x, y, 0, 0)).r, uv); ++ float3 yuv1 = float3(src_y.Load(int4(min(x + 1, (uint)width - 1), y, 0, 0)).r, uv); ++ float3 yuv2 = float3(src_y.Load(int4(x, min(y + 1, (uint)height - 1), 0, 0)).r, uv); ++ float3 yuv3 = float3(src_y.Load(int4(min(x + 1, (uint)width - 1), min(y + 1, (uint)height - 1), 0, 0)).r, uv); ++ ++ float3 c0 = process(yuv0); ++ float3 c1 = process(yuv1); ++ float3 c2 = process(yuv2); ++ float3 c3 = process(yuv3); ++ ++ dst_y[uint3(x, y, 0)] = c0.x; ++ if (x + 1 < (uint)width) ++ dst_y[uint3(x + 1, y, 0)] = c1.x; ++ if (y + 1 < (uint)height) ++ dst_y[uint3(x, y + 1, 0)] = c2.x; ++ if (x + 1 < (uint)width && y + 1 < (uint)height) ++ dst_y[uint3(x + 1, y + 1, 0)] = c3.x; ++ ++ float3 cc = chroma_sample(c0, c1, c2, c3); ++ dst_uv[uint3(id.x, id.y, 0)] = cc.yz; ++} +Index: FFmpeg/libavfilter/d3d11_source.h +=================================================================== +--- FFmpeg.orig/libavfilter/d3d11_source.h ++++ FFmpeg/libavfilter/d3d11_source.h +@@ -25,5 +25,6 @@ + + extern const char *ff_source_deint_hlsl; + extern const char *ff_source_overlay_hlsl; ++extern const char *ff_source_tonemap_hlsl; + + #endif /* AVFILTER_D3D11_SOURCE_H */ diff --git a/debian/patches/0097-add-scale-d3d11-p010-shader-fallback.patch b/debian/patches/0097-add-scale-d3d11-p010-shader-fallback.patch new file mode 100644 index 000000000..984a4d42c --- /dev/null +++ b/debian/patches/0097-add-scale-d3d11-p010-shader-fallback.patch @@ -0,0 +1,848 @@ +Index: FFmpeg/libavfilter/vf_scale_d3d11.c +=================================================================== +--- FFmpeg.orig/libavfilter/vf_scale_d3d11.c ++++ FFmpeg/libavfilter/vf_scale_d3d11.c +@@ -21,7 +21,15 @@ + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + ++#include ++#ifndef COBJMACROS ++#define COBJMACROS ++#endif ++#include ++#include ++ + #include "libavutil/opt.h" ++#include "libavutil/mem.h" + #include "libavutil/pixdesc.h" + #include "compat/w32dlfcn.h" + +@@ -31,6 +39,26 @@ + #include "filters.h" + #include "scale_eval.h" + #include "video.h" ++#include "d3d11_source.h" ++ ++#define SCALE_D3D11_TGX 16 ++#define SCALE_D3D11_TGY 16 ++#define D3D11_RELEASE(p) do { if (p) { (p)->lpVtbl->Release(p); (p) = NULL; } } while (0) ++#ifndef D3DCOMPILE_OPTIMIZATION_LEVEL3 ++#define D3DCOMPILE_OPTIMIZATION_LEVEL3 (1 << 15) ++#endif ++ ++typedef HRESULT (WINAPI *D3DCompileProc)(LPCVOID, SIZE_T, LPCSTR, ++ const D3D_SHADER_MACRO *, ID3DInclude *, ++ LPCSTR, LPCSTR, UINT, UINT, ++ ID3D10Blob **, ID3D10Blob **); ++ ++typedef struct ScaleD3D11Params { ++ int src_w; ++ int src_h; ++ int dst_w; ++ int dst_h; ++} ScaleD3D11Params; + + typedef struct ScaleD3D11Context { + const AVClass *classCtx; +@@ -47,12 +75,26 @@ typedef struct ScaleD3D11Context { + ID3D11VideoProcessorOutputView *outputView; + ID3D11VideoProcessorInputView *inputView; + ++ HMODULE d3dcompiler; ++ D3DCompileProc D3DCompile; ++ ID3D11ComputeShader *cs_y; ++ ID3D11ComputeShader *cs_uv; ++ ID3D11Buffer *params_buf; ++ ID3D11Texture2D *src_tex; ++ ID3D11Texture2D *work_tex; ++ int src_tex_w, src_tex_h; ++ int work_w, work_h; ++ int shader_fallback; ++ int direct_input_srv; ++ int direct_output_uav; ++ + ///< Buffer references + AVBufferRef *hw_device_ctx; + AVBufferRef *hw_frames_ctx_out; + + ///< Dimensions and formats + int width, height; ++ enum AVPixelFormat in_format; + int inputWidth, inputHeight; + DXGI_FORMAT input_format; + DXGI_FORMAT output_format; +@@ -83,6 +125,340 @@ static void release_d3d11_resources(Scal + s->videoDevice->lpVtbl->Release(s->videoDevice); + s->videoDevice = NULL; + } ++ ++ D3D11_RELEASE(s->cs_y); ++ D3D11_RELEASE(s->cs_uv); ++ D3D11_RELEASE(s->params_buf); ++ D3D11_RELEASE(s->src_tex); ++ D3D11_RELEASE(s->work_tex); ++ s->src_tex_w = s->src_tex_h = 0; ++ s->work_w = s->work_h = 0; ++} ++ ++static DXGI_FORMAT scale_d3d11_texture_format(enum AVPixelFormat fmt) ++{ ++ return fmt == AV_PIX_FMT_P010 ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12; ++} ++ ++static DXGI_FORMAT scale_d3d11_plane_format(enum AVPixelFormat fmt, int plane) ++{ ++ if (fmt == AV_PIX_FMT_NV12) ++ return plane ? DXGI_FORMAT_R8G8_UNORM : DXGI_FORMAT_R8_UNORM; ++ return plane ? DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R16_UNORM; ++} ++ ++static int scale_d3d11_compile_shader(AVFilterContext *ctx, const char *entry, ++ ID3D11ComputeShader **shader) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ ID3D10Blob *cs_blob = NULL; ++ ID3D10Blob *err_blob = NULL; ++ HRESULT hr; ++ ++ hr = s->D3DCompile(ff_source_scale_hlsl, strlen(ff_source_scale_hlsl), ++ NULL, NULL, NULL, entry, "cs_5_0", ++ D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &cs_blob, &err_blob); ++ if (FAILED(hr)) { ++ if (err_blob) { ++ av_log(ctx, AV_LOG_ERROR, "Failed compiling D3D11 scale shader %s: %.*s\n", ++ entry, (int)err_blob->lpVtbl->GetBufferSize(err_blob), ++ (char *)err_blob->lpVtbl->GetBufferPointer(err_blob)); ++ } else { ++ av_log(ctx, AV_LOG_ERROR, "Failed compiling D3D11 scale shader %s: HRESULT 0x%lX\n", ++ entry, (unsigned long)hr); ++ } ++ D3D11_RELEASE(err_blob); ++ return AVERROR_EXTERNAL; ++ } ++ ++ hr = s->device->lpVtbl->CreateComputeShader(s->device, ++ cs_blob->lpVtbl->GetBufferPointer(cs_blob), ++ cs_blob->lpVtbl->GetBufferSize(cs_blob), ++ NULL, shader); ++ D3D11_RELEASE(cs_blob); ++ D3D11_RELEASE(err_blob); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 scale shader %s: HRESULT 0x%lX\n", ++ entry, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ return 0; ++} ++ ++static int scale_d3d11_ensure_shader(AVFilterContext *ctx) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ D3D11_BUFFER_DESC bd = { 0 }; ++ HRESULT hr; ++ int ret; ++ ++ if (!s->d3dcompiler) { ++ s->d3dcompiler = LoadLibraryA("d3dcompiler_47.dll"); ++ if (!s->d3dcompiler) ++ s->d3dcompiler = LoadLibraryA("d3dcompiler_43.dll"); ++ } ++ if (!s->d3dcompiler) { ++ av_log(ctx, AV_LOG_ERROR, "Failed loading d3dcompiler DLL\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ s->D3DCompile = (D3DCompileProc)GetProcAddress(s->d3dcompiler, "D3DCompile"); ++ if (!s->D3DCompile) { ++ av_log(ctx, AV_LOG_ERROR, "Failed loading D3DCompile\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ++ if (!s->params_buf) { ++ bd.ByteWidth = sizeof(ScaleD3D11Params); ++ bd.Usage = D3D11_USAGE_DYNAMIC; ++ bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; ++ bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ++ hr = s->device->lpVtbl->CreateBuffer(s->device, &bd, NULL, &s->params_buf); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 scale constant buffer: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ } ++ ++ if (!s->cs_y) { ++ ret = scale_d3d11_compile_shader(ctx, "scale_y", &s->cs_y); ++ if (ret < 0) ++ return ret; ++ } ++ if (!s->cs_uv) { ++ ret = scale_d3d11_compile_shader(ctx, "scale_uv", &s->cs_uv); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int scale_d3d11_ensure_texture(AVFilterContext *ctx, ID3D11Texture2D **tex, ++ int *cur_w, int *cur_h, int w, int h, ++ enum AVPixelFormat fmt, UINT bind_flags) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ D3D11_TEXTURE2D_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ if (*tex && *cur_w == w && *cur_h == h) ++ return 0; ++ ++ D3D11_RELEASE(*tex); ++ desc.Width = w; ++ desc.Height = h; ++ desc.MipLevels = 1; ++ desc.ArraySize = 1; ++ desc.Format = scale_d3d11_texture_format(fmt); ++ desc.SampleDesc.Count = 1; ++ desc.Usage = D3D11_USAGE_DEFAULT; ++ desc.BindFlags = bind_flags; ++ ++ hr = s->device->lpVtbl->CreateTexture2D(s->device, &desc, NULL, tex); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed creating D3D11 scale texture: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ ++ *cur_w = w; ++ *cur_h = h; ++ return 0; ++} ++ ++static void scale_d3d11_fill_srv_desc(ID3D11Texture2D *tex, UINT subresource, ++ DXGI_FORMAT fmt, D3D11_SHADER_RESOURCE_VIEW_DESC *desc) ++{ ++ D3D11_TEXTURE2D_DESC tex_desc; ++ UINT mip_slice = 0, array_slice = 0; ++ ++ ID3D11Texture2D_GetDesc(tex, &tex_desc); ++ if (tex_desc.MipLevels) { ++ mip_slice = subresource % tex_desc.MipLevels; ++ array_slice = subresource / tex_desc.MipLevels; ++ } ++ ++ desc->Format = fmt; ++ desc->ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; ++ desc->Texture2DArray.MostDetailedMip = mip_slice; ++ desc->Texture2DArray.MipLevels = 1; ++ desc->Texture2DArray.FirstArraySlice = array_slice; ++ desc->Texture2DArray.ArraySize = 1; ++} ++ ++static void scale_d3d11_fill_uav_desc(ID3D11Texture2D *tex, UINT subresource, ++ DXGI_FORMAT fmt, D3D11_UNORDERED_ACCESS_VIEW_DESC *desc) ++{ ++ D3D11_TEXTURE2D_DESC tex_desc; ++ UINT mip_slice = 0, array_slice = 0; ++ ++ ID3D11Texture2D_GetDesc(tex, &tex_desc); ++ if (tex_desc.MipLevels) { ++ mip_slice = subresource % tex_desc.MipLevels; ++ array_slice = subresource / tex_desc.MipLevels; ++ } ++ ++ desc->Format = fmt; ++ desc->ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; ++ desc->Texture2DArray.MipSlice = mip_slice; ++ desc->Texture2DArray.FirstArraySlice = array_slice; ++ desc->Texture2DArray.ArraySize = 1; ++} ++ ++static int scale_d3d11_create_srv(AVFilterContext *ctx, ID3D11Texture2D *tex, ++ UINT subresource, enum AVPixelFormat fmt, int plane, ++ int log_level, ID3D11ShaderResourceView **srv) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ D3D11_SHADER_RESOURCE_VIEW_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ scale_d3d11_fill_srv_desc(tex, subresource, scale_d3d11_plane_format(fmt, plane), &desc); ++ hr = s->device->lpVtbl->CreateShaderResourceView(s->device, (ID3D11Resource *)tex, ++ &desc, srv); ++ if (FAILED(hr)) { ++ av_log(ctx, log_level, "Failed creating D3D11 scale SRV plane %d: HRESULT 0x%lX\n", ++ plane, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static int scale_d3d11_create_uav(AVFilterContext *ctx, ID3D11Texture2D *tex, ++ UINT subresource, enum AVPixelFormat fmt, int plane, ++ int log_level, ID3D11UnorderedAccessView **uav) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ D3D11_UNORDERED_ACCESS_VIEW_DESC desc = { 0 }; ++ HRESULT hr; ++ ++ scale_d3d11_fill_uav_desc(tex, subresource, scale_d3d11_plane_format(fmt, plane), &desc); ++ hr = s->device->lpVtbl->CreateUnorderedAccessView(s->device, (ID3D11Resource *)tex, ++ &desc, uav); ++ if (FAILED(hr)) { ++ av_log(ctx, log_level, "Failed creating D3D11 scale UAV plane %d: HRESULT 0x%lX\n", ++ plane, (unsigned long)hr); ++ return AVERROR_EXTERNAL; ++ } ++ return 0; ++} ++ ++static void scale_d3d11_copy_frame_to_texture(ScaleD3D11Context *s, AVFrame *src, ID3D11Texture2D *dst) ++{ ++ ID3D11Texture2D *src_tex = (ID3D11Texture2D *)src->data[0]; ++ UINT src_sub = (UINT)(uintptr_t)src->data[1]; ++ D3D11_BOX box = { 0, 0, 0, src->width, src->height, 1 }; ++ ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst, 0, ++ 0, 0, 0, (ID3D11Resource *)src_tex, ++ src_sub, &box); ++} ++ ++static void scale_d3d11_copy_texture_to_frame(ScaleD3D11Context *s, ID3D11Texture2D *src, AVFrame *dst) ++{ ++ ID3D11Texture2D *dst_tex = (ID3D11Texture2D *)dst->data[0]; ++ UINT dst_sub = (UINT)(uintptr_t)dst->data[1]; ++ ++ s->context->lpVtbl->CopySubresourceRegion(s->context, (ID3D11Resource *)dst_tex, ++ dst_sub, 0, 0, 0, (ID3D11Resource *)src, ++ 0, NULL); ++} ++ ++static int scale_d3d11_create_frame_srvs(AVFilterContext *ctx, AVFrame *frame, ++ ID3D11ShaderResourceView **srvs) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)frame->data[0]; ++ UINT subresource = (UINT)(uintptr_t)frame->data[1]; ++ int ret; ++ ++ ret = scale_d3d11_create_srv(ctx, tex, subresource, s->in_format, 0, AV_LOG_DEBUG, &srvs[0]); ++ if (ret < 0) ++ return ret; ++ ret = scale_d3d11_create_srv(ctx, tex, subresource, s->in_format, 1, AV_LOG_DEBUG, &srvs[1]); ++ if (ret < 0) ++ D3D11_RELEASE(srvs[0]); ++ return ret; ++} ++ ++static int scale_d3d11_prepare_input_srvs(AVFilterContext *ctx, AVFrame *input, ++ ID3D11ShaderResourceView **srvs) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ int ret; ++ ++ if (s->direct_input_srv) { ++ ret = scale_d3d11_create_frame_srvs(ctx, input, srvs); ++ if (ret >= 0) { ++ if (s->direct_input_srv < 0) { ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader input: direct SRV\n"); ++ s->direct_input_srv = 1; ++ } ++ return 0; ++ } ++ if (s->direct_input_srv < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader input: copied to internal SRV texture\n"); ++ s->direct_input_srv = 0; ++ } ++ ++ ret = scale_d3d11_ensure_texture(ctx, &s->src_tex, &s->src_tex_w, &s->src_tex_h, ++ input->width, input->height, s->in_format, ++ D3D11_BIND_SHADER_RESOURCE); ++ if (ret < 0) ++ return ret; ++ scale_d3d11_copy_frame_to_texture(s, input, s->src_tex); ++ ret = scale_d3d11_create_srv(ctx, s->src_tex, 0, s->in_format, 0, AV_LOG_ERROR, &srvs[0]); ++ if (ret < 0) ++ return ret; ++ ret = scale_d3d11_create_srv(ctx, s->src_tex, 0, s->in_format, 1, AV_LOG_ERROR, &srvs[1]); ++ if (ret < 0) ++ D3D11_RELEASE(srvs[0]); ++ return ret; ++} ++ ++static int scale_d3d11_prepare_output_uavs(AVFilterContext *ctx, AVFrame *dst, ++ ID3D11UnorderedAccessView **uavs, int *direct) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ ID3D11Texture2D *tex = (ID3D11Texture2D *)dst->data[0]; ++ UINT subresource = (UINT)(uintptr_t)dst->data[1]; ++ int ret; ++ ++ *direct = 0; ++ if (s->direct_output_uav) { ++ ret = scale_d3d11_create_uav(ctx, tex, subresource, s->format, 0, AV_LOG_DEBUG, &uavs[0]); ++ if (ret >= 0) ++ ret = scale_d3d11_create_uav(ctx, tex, subresource, s->format, 1, AV_LOG_DEBUG, &uavs[1]); ++ if (ret >= 0) { ++ if (s->direct_output_uav < 0) { ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: direct UAV\n"); ++ s->direct_output_uav = 1; ++ } ++ *direct = 1; ++ return 0; ++ } ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ if (s->direct_output_uav < 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: copied from internal UAV texture\n"); ++ s->direct_output_uav = 0; ++ } ++ ++ ret = scale_d3d11_ensure_texture(ctx, &s->work_tex, &s->work_w, &s->work_h, ++ dst->width, dst->height, s->format, ++ D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS); ++ if (ret < 0) ++ return ret; ++ ret = scale_d3d11_create_uav(ctx, s->work_tex, 0, s->format, 0, AV_LOG_ERROR, &uavs[0]); ++ if (ret < 0) ++ return ret; ++ ret = scale_d3d11_create_uav(ctx, s->work_tex, 0, s->format, 1, AV_LOG_ERROR, &uavs[1]); ++ if (ret < 0) ++ D3D11_RELEASE(uavs[0]); ++ return ret; + } + + static int scale_d3d11_configure_processor(ScaleD3D11Context *s, AVFilterContext *ctx) { +@@ -144,6 +520,152 @@ static int scale_d3d11_configure_process + return 0; + } + ++ ++static int scale_d3d11_probe_output_uav_pool(AVFilterContext *ctx, AVBufferRef *frames_ref) ++{ ++ ScaleD3D11Context *s = ctx->priv; ++ AVD3D11VAFramesContext *frames_hwctx = ((AVHWFramesContext *)frames_ref->data)->hwctx; ++ AVFrame *frame = NULL; ++ ID3D11Texture2D *tex; ++ ID3D11UnorderedAccessView *uavs[2] = { NULL, NULL }; ++ UINT subresource; ++ int ret; ++ ++ if (frames_hwctx->texture) { ++ tex = frames_hwctx->texture; ++ subresource = 0; ++ } else { ++ frame = av_frame_alloc(); ++ if (!frame) ++ return AVERROR(ENOMEM); ++ ret = av_hwframe_get_buffer(frames_ref, frame, 0); ++ if (ret < 0) ++ goto done; ++ tex = (ID3D11Texture2D *)frame->data[0]; ++ subresource = (UINT)(uintptr_t)frame->data[1]; ++ } ++ ++ ret = scale_d3d11_create_uav(ctx, tex, subresource, s->format, 0, AV_LOG_DEBUG, &uavs[0]); ++ if (ret >= 0) ++ ret = scale_d3d11_create_uav(ctx, tex, subresource, s->format, 1, AV_LOG_DEBUG, &uavs[1]); ++ ++done: ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ av_frame_free(&frame); ++ return ret; ++} ++ ++static int scale_d3d11_filter_frame_shader(AVFilterLink *inlink, AVFrame *in) ++{ ++ AVFilterContext *ctx = inlink->dst; ++ ScaleD3D11Context *s = ctx->priv; ++ AVFilterLink *outlink = ctx->outputs[0]; ++ AVFrame *out = NULL; ++ ID3D11ShaderResourceView *srvs[2] = { NULL, NULL }; ++ ID3D11ShaderResourceView *null_srvs[2] = { NULL, NULL }; ++ ID3D11UnorderedAccessView *uavs[2] = { NULL, NULL }; ++ ID3D11UnorderedAccessView *null_uavs[2] = { NULL, NULL }; ++ ID3D11Buffer *null_cb[1] = { NULL }; ++ D3D11_MAPPED_SUBRESOURCE mapped; ++ ScaleD3D11Params params; ++ int direct_output = 0; ++ HRESULT hr; ++ int ret; ++ ++ out = av_frame_alloc(); ++ if (!out) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ ret = av_hwframe_get_buffer(s->hw_frames_ctx_out, out, 0); ++ if (ret < 0) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to get output frame from pool\n"); ++ goto fail; ++ } ++ ++ ret = av_frame_copy_props(out, in); ++ if (ret < 0) { ++ av_log(ctx, AV_LOG_ERROR, "Failed to copy frame properties\n"); ++ goto fail; ++ } ++ ++ out->width = s->width; ++ out->height = s->height; ++ out->format = AV_PIX_FMT_D3D11; ++ ++ ret = scale_d3d11_prepare_input_srvs(ctx, in, srvs); ++ if (ret < 0) ++ goto fail; ++ ret = scale_d3d11_prepare_output_uavs(ctx, out, uavs, &direct_output); ++ if (ret < 0) ++ goto fail; ++ ret = scale_d3d11_ensure_shader(ctx); ++ if (ret < 0) ++ goto fail; ++ ++ params.src_w = in->width; ++ params.src_h = in->height; ++ params.dst_w = s->width; ++ params.dst_h = s->height; ++ ++ hr = s->context->lpVtbl->Map(s->context, (ID3D11Resource *)s->params_buf, ++ 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); ++ if (FAILED(hr)) { ++ av_log(ctx, AV_LOG_ERROR, "Failed mapping D3D11 scale constant buffer: HRESULT 0x%lX\n", ++ (unsigned long)hr); ++ ret = AVERROR_EXTERNAL; ++ goto fail; ++ } ++ memcpy(mapped.pData, ¶ms, sizeof(params)); ++ s->context->lpVtbl->Unmap(s->context, (ID3D11Resource *)s->params_buf, 0); ++ ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, &s->params_buf); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 2, srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, uavs, NULL); ++ ++ s->context->lpVtbl->CSSetShader(s->context, s->cs_y, NULL, 0); ++ s->context->lpVtbl->Dispatch(s->context, ++ (params.dst_w + SCALE_D3D11_TGX - 1) / SCALE_D3D11_TGX, ++ (params.dst_h + SCALE_D3D11_TGY - 1) / SCALE_D3D11_TGY, ++ 1); ++ ++ s->context->lpVtbl->CSSetShader(s->context, s->cs_uv, NULL, 0); ++ s->context->lpVtbl->Dispatch(s->context, ++ (((params.dst_w + 1) >> 1) + SCALE_D3D11_TGX - 1) / SCALE_D3D11_TGX, ++ (((params.dst_h + 1) >> 1) + SCALE_D3D11_TGY - 1) / SCALE_D3D11_TGY, ++ 1); ++ ++ s->context->lpVtbl->CSSetShader(s->context, NULL, NULL, 0); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 2, null_srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, null_uavs, NULL); ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, null_cb); ++ ++ if (!direct_output) ++ scale_d3d11_copy_texture_to_frame(s, s->work_tex, out); ++ ++ D3D11_RELEASE(srvs[0]); ++ D3D11_RELEASE(srvs[1]); ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ av_frame_free(&in); ++ return ff_filter_frame(outlink, out); ++ ++fail: ++ s->context->lpVtbl->CSSetShader(s->context, NULL, NULL, 0); ++ s->context->lpVtbl->CSSetShaderResources(s->context, 0, 2, null_srvs); ++ s->context->lpVtbl->CSSetUnorderedAccessViews(s->context, 0, 2, null_uavs, NULL); ++ s->context->lpVtbl->CSSetConstantBuffers(s->context, 0, 1, null_cb); ++ D3D11_RELEASE(srvs[0]); ++ D3D11_RELEASE(srvs[1]); ++ D3D11_RELEASE(uavs[0]); ++ D3D11_RELEASE(uavs[1]); ++ av_frame_free(&in); ++ av_frame_free(&out); ++ return ret; ++} ++ + static int scale_d3d11_filter_frame(AVFilterLink *inlink, AVFrame *in) + { + AVFilterContext *ctx = inlink->dst; +@@ -185,6 +707,9 @@ static int scale_d3d11_filter_frame(AVFi + return AVERROR(EINVAL); + } + ++ if (s->shader_fallback) ++ return scale_d3d11_filter_frame_shader(inlink, in); ++ + ///< Allocate output frame + out = av_frame_alloc(); + if (!out) { +@@ -353,13 +878,6 @@ static int scale_d3d11_config_props(AVFi + return AVERROR(EINVAL); + } + +- ///< Propagate hw_frames_ctx to output +- outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx); +- if (!outl->hw_frames_ctx) { +- av_log(ctx, AV_LOG_ERROR, "Failed to propagate hw_frames_ctx to output\n"); +- return AVERROR(ENOMEM); +- } +- + ///< Initialize filter's hardware device context + if (!s->hw_device_ctx) { + AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data; +@@ -382,6 +900,29 @@ static int scale_d3d11_config_props(AVFi + return AVERROR(EINVAL); + } + ++ { ++ AVHWFramesContext *in_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data; ++ s->in_format = in_frames_ctx->sw_format; ++ } ++ ++ if (s->in_format != AV_PIX_FMT_NV12 && s->in_format != AV_PIX_FMT_P010) { ++ av_log(ctx, AV_LOG_ERROR, "Unsupported input format: %s\n", ++ av_get_pix_fmt_name(s->in_format)); ++ return AVERROR(ENOSYS); ++ } ++ if (s->format == AV_PIX_FMT_NONE) ++ s->format = s->in_format; ++ if (s->format != AV_PIX_FMT_NV12 && s->format != AV_PIX_FMT_P010) { ++ av_log(ctx, AV_LOG_ERROR, "Unsupported output format: %s\n", ++ av_get_pix_fmt_name(s->format)); ++ return AVERROR(ENOSYS); ++ } ++ ++ s->shader_fallback = s->format == AV_PIX_FMT_P010; ++ s->direct_input_srv = -1; ++ s->direct_output_uav = -1; ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ + ///< Create new hardware frames context for output + s->hw_frames_ctx_out = av_hwframe_ctx_alloc(s->hw_device_ctx); + if (!s->hw_frames_ctx_out) +@@ -399,16 +940,38 @@ static int scale_d3d11_config_props(AVFi + + AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx; + frames_hwctx->MiscFlags = 0; +- frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; +- if (frames_ctx->sw_format == AV_PIX_FMT_NV12) +- frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER; ++ if (s->shader_fallback) { ++ frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | ++ D3D11_BIND_SHADER_RESOURCE | ++ D3D11_BIND_UNORDERED_ACCESS; ++ } else { ++ frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; ++ if (frames_ctx->sw_format == AV_PIX_FMT_NV12) ++ frames_hwctx->BindFlags |= D3D11_BIND_VIDEO_ENCODER; ++ } + + ret = av_hwframe_ctx_init(s->hw_frames_ctx_out); + if (ret < 0) { + av_buffer_unref(&s->hw_frames_ctx_out); + return ret; + } ++ if (s->shader_fallback) { ++ ret = scale_d3d11_probe_output_uav_pool(ctx, s->hw_frames_ctx_out); ++ if (ret < 0) { ++ av_buffer_unref(&s->hw_frames_ctx_out); ++ return ret; ++ } ++ s->direct_output_uav = 1; ++ } ++ ++ ++ if (s->shader_fallback) { ++ av_log(ctx, AV_LOG_VERBOSE, "D3D11 scale: using P010 output path\n"); ++ if (s->direct_output_uav > 0) ++ av_log(ctx, AV_LOG_DEBUG, "D3D11 shader output: direct UAV\n"); ++ } + ++ av_buffer_unref(&outl->hw_frames_ctx); + outl->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx_out); + if (!outl->hw_frames_ctx) + return AVERROR(ENOMEM); +@@ -424,6 +987,11 @@ static av_cold void scale_d3d11_uninit(A + ///< Release D3D11 resources + release_d3d11_resources(s); + ++ if (s->d3dcompiler) { ++ FreeLibrary(s->d3dcompiler); ++ s->d3dcompiler = NULL; ++ } ++ + ///< Free the hardware device context reference + av_buffer_unref(&s->hw_frames_ctx_out); + av_buffer_unref(&s->hw_device_ctx); +@@ -453,7 +1021,9 @@ static const AVFilterPad scale_d3d11_out + #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + + static const AVOption scale_d3d11_options[] = { ++ { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS }, + { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS }, ++ { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS }, + { "height", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS }, + { "format", "Output video pixel format", OFFSET(format), AV_OPT_TYPE_PIXEL_FMT, { .i64 = AV_PIX_FMT_NONE }, INT_MIN, INT_MAX, .flags=FLAGS }, + { NULL } +@@ -463,7 +1033,7 @@ AVFILTER_DEFINE_CLASS(scale_d3d11); + + const FFFilter ff_vf_scale_d3d11 = { + .p.name = "scale_d3d11", +- .p.description = NULL_IF_CONFIG_SMALL("Scale video using Direct3D11"), ++ .p.description = NULL_IF_CONFIG_SMALL("Scale D3D11 video"), + .priv_size = sizeof(ScaleD3D11Context), + .p.priv_class = &scale_d3d11_class, + .init = scale_d3d11_init, +Index: FFmpeg/libavfilter/Makefile +=================================================================== +--- FFmpeg.orig/libavfilter/Makefile ++++ FFmpeg/libavfilter/Makefile +@@ -485,7 +485,7 @@ OBJS-$(CONFIG_ROBERTS_OPENCL_FILTER) + OBJS-$(CONFIG_ROTATE_FILTER) += vf_rotate.o + OBJS-$(CONFIG_SAB_FILTER) += vf_sab.o + OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o scale_eval.o framesync.o +-OBJS-$(CONFIG_SCALE_D3D11_FILTER) += vf_scale_d3d11.o scale_eval.o ++OBJS-$(CONFIG_SCALE_D3D11_FILTER) += vf_scale_d3d11.o scale_eval.o d3d11/scale.o + OBJS-$(CONFIG_SCALE_D3D12_FILTER) += vf_scale_d3d12.o scale_eval.o + OBJS-$(CONFIG_SCALE_CUDA_FILTER) += vf_scale_cuda.o scale_eval.o \ + vf_scale_cuda.ptx.o cuda/load_helper.o +Index: FFmpeg/libavfilter/d3d11/scale.hlsl +=================================================================== +--- /dev/null ++++ FFmpeg/libavfilter/d3d11/scale.hlsl +@@ -0,0 +1,123 @@ ++/* ++ * D3D11 scale ++ * ++ * Copyright (C) 2026 Gnattu OC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++Texture2DArray src_y : register(t0); ++Texture2DArray src_uv : register(t1); ++RWTexture2DArray dst_y : register(u0); ++RWTexture2DArray dst_uv : register(u1); ++ ++cbuffer Params : register(b0) { ++ int src_w; ++ int src_h; ++ int dst_w; ++ int dst_h; ++}; ++ ++float4 bicubic_coeffs(float x) ++{ ++ const float A = 0.0; ++ float x1 = x + 1.0; ++ float ix = 1.0 - x; ++ float4 r; ++ r.x = ((A * x1 - 5.0 * A) * x1 + 8.0 * A) * x1 - 4.0 * A; ++ r.y = ((A + 2.0) * x - (A + 3.0)) * x * x + 1.0; ++ r.z = ((A + 2.0) * ix - (A + 3.0)) * ix * ix + 1.0; ++ r.w = 1.0 - r.x - r.y - r.z; ++ return r; ++} ++ ++float sample_y_bicubic(float x, float y) ++{ ++ x = clamp(x, 0.0, (float)(src_w - 1)); ++ y = clamp(y, 0.0, (float)(src_h - 1)); ++ ++ float px = floor(x); ++ float py = floor(y); ++ float fx = x - px; ++ float fy = y - py; ++ float4 cx = bicubic_coeffs(fx); ++ float4 cy = bicubic_coeffs(fy); ++ ++ float rows[4]; ++ [unroll] for (int j = 0; j < 4; j++) { ++ int sy = clamp((int)py + j - 1, 0, src_h - 1); ++ float4 v; ++ [unroll] for (int i = 0; i < 4; i++) { ++ int sx = clamp((int)px + i - 1, 0, src_w - 1); ++ v[i] = src_y.Load(int4(sx, sy, 0, 0)); ++ } ++ rows[j] = dot(cx, v); ++ } ++ return saturate(dot(cy, float4(rows[0], rows[1], rows[2], rows[3]))); ++} ++ ++float2 sample_uv_bicubic(float x, float y, int src_cw, int src_ch) ++{ ++ x = clamp(x, 0.0, (float)(src_cw - 1)); ++ y = clamp(y, 0.0, (float)(src_ch - 1)); ++ ++ float px = floor(x); ++ float py = floor(y); ++ float fx = x - px; ++ float fy = y - py; ++ float4 cx = bicubic_coeffs(fx); ++ float4 cy = bicubic_coeffs(fy); ++ ++ float2 rows[4]; ++ [unroll] for (int j = 0; j < 4; j++) { ++ int sy = clamp((int)py + j - 1, 0, src_ch - 1); ++ float2 row = 0.0; ++ [unroll] for (int i = 0; i < 4; i++) { ++ int sx = clamp((int)px + i - 1, 0, src_cw - 1); ++ row += cx[i] * src_uv.Load(int4(sx, sy, 0, 0)); ++ } ++ rows[j] = row; ++ } ++ return saturate(cy.x * rows[0] + cy.y * rows[1] + cy.z * rows[2] + cy.w * rows[3]); ++} ++ ++[numthreads(16, 16, 1)] ++void scale_y(uint3 id : SV_DispatchThreadID) ++{ ++ if (id.x >= (uint)dst_w || id.y >= (uint)dst_h) ++ return; ++ ++ float sx = ((float)id.x + 0.5) * (float)src_w / (float)dst_w - 0.5; ++ float sy = ((float)id.y + 0.5) * (float)src_h / (float)dst_h - 0.5; ++ dst_y[int3(id.x, id.y, 0)] = sample_y_bicubic(sx, sy); ++} ++ ++[numthreads(16, 16, 1)] ++void scale_uv(uint3 id : SV_DispatchThreadID) ++{ ++ int src_cw = (src_w + 1) >> 1; ++ int src_ch = (src_h + 1) >> 1; ++ int dst_cw = (dst_w + 1) >> 1; ++ int dst_ch = (dst_h + 1) >> 1; ++ ++ if (id.x >= (uint)dst_cw || id.y >= (uint)dst_ch) ++ return; ++ ++ float sx = ((float)id.x + 0.5) * (float)src_cw / (float)dst_cw - 0.5; ++ float sy = ((float)id.y + 0.5) * (float)src_ch / (float)dst_ch - 0.5; ++ dst_uv[int3(id.x, id.y, 0)] = sample_uv_bicubic(sx, sy, src_cw, src_ch); ++} +Index: FFmpeg/libavfilter/d3d11_source.h +=================================================================== +--- FFmpeg.orig/libavfilter/d3d11_source.h ++++ FFmpeg/libavfilter/d3d11_source.h +@@ -25,6 +25,7 @@ + + extern const char *ff_source_deint_hlsl; + extern const char *ff_source_overlay_hlsl; ++extern const char *ff_source_scale_hlsl; + extern const char *ff_source_tonemap_hlsl; + + #endif /* AVFILTER_D3D11_SOURCE_H */ diff --git a/debian/patches/series b/debian/patches/series index 448f6ffd4..867a20473 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -88,3 +88,8 @@ 0088-add-webp-to-matroskadec-image-mime-types.patch 0089-relax-to-allow-safe-filenames-in-mkv-attachments.patch 0090-backport-a-fix-to-use-sw-pix-fmt-in-codec-par-if-set.patch +0093-add-d3d11-deinterlace-filter.patch +0094-add-d3d11-video-processor-overlay-transpose-filters.patch +0095-fix-d3d11-scale-visible-source-rect.patch +0096-add-d3d11-tonemap-filter.patch +0097-add-scale-d3d11-p010-shader-fallback.patch