From patchwork Fri Mar 30 03:14:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rostislav Pehlivanov X-Patchwork-Id: 8233 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.1.70 with SMTP id c67csp2412243jad; Thu, 29 Mar 2018 20:20:35 -0700 (PDT) X-Google-Smtp-Source: AIpwx4/zGFe5FjWQjQDFFO0mdP0jw1CV+Q+UBeGAqktwreCJEWyroYvLeroLevRocKAJiwyA3VqK X-Received: by 10.223.166.79 with SMTP id k73mr8488028wrc.200.1522380035085; Thu, 29 Mar 2018 20:20:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1522380035; cv=none; d=google.com; s=arc-20160816; b=uPwO5Zl8IxtB9VDp6Z5KeuhgXBLnDaVb2MHb/fZwmrwmp54iqgE96cAiNalxBNSL3l cML4OOEtY5q0coPU4qy7PeDEyu2ot+Rx9ZYwpkimqRzQOkK6dgjxrfV33M+gEz0SAvy5 lUgx9/GvaYEZ9MH8CMnUGXbPtH7S4otDB7NWgDDEuKbGEDGCH/CUtmemNoh+UR7qL5yZ UYbOEVuxEOHH8Akuw1azRBD6f7M7HZ6rQR1V2i7tbTXe6IhmLz9sJYSkzb+P+dTCE2EX vnccMWeIwcHEFh/O3Xe8Nmrdu0PYzH2WmUWzlLFZfqoDMqa6L/KHlB+iwI9RoDWmbEXX yPhQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=08I8Zt3atMisGJdWnBKFbrxTXq9lli6/XhCO1rjiOwI=; b=PKtlAwDwNXwRrWs9qtf0QkWGe2pDHg1G6lpYT+ic6smLo5zcTAnbq/ZAWPiRtgXPex uR6TmBwGwlJoWT1/pqBOAG0SZHDLbtXjDCbna/M3ygn6ZCM5OvhJtUJ63421qmSFedhl e4Rz/NkNd60ZpGbs/QrPOFbTLiKHVB4vEPsMk0v/fI5lcIIFUP5xolP8FMdmuJXt15XA t8lRM8NUIo820RSbuOJxsnYawsbGOUJKlPVGXenaoESfayC1jy2A3NiIiQWnXGnKxafd o45OQFEWu8wmP30TdPrrXH70+TbeCsMkZ8Qfu/9UqzdKVZa/mjXxbHRCZ88j7/R5r/vJ ANSQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=lTrE8z/u; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y44si6028214wrc.168.2018.03.29.20.20.34; Thu, 29 Mar 2018 20:20:35 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=lTrE8z/u; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8B642689F17; Fri, 30 Mar 2018 06:20:14 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f67.google.com (mail-wm0-f67.google.com [74.125.82.67]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DD39D689C7D for ; Fri, 30 Mar 2018 06:20:07 +0300 (EEST) Received: by mail-wm0-f67.google.com with SMTP id a20so645158wmd.1 for ; Thu, 29 Mar 2018 20:20:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=EPIXYQ2Xo0xQPqBBKy0zBF0STOyF8dHE75LlyHCZAzk=; b=lTrE8z/u/2rm5WQ6JsnIlQ+wDBSWHGZFFtUEcmh7akgifuQTbEG8w6IRSPrKGJpp9G gAafgTUyYlzsq5w9Qe8afbq8yHCg1qz0iF4M5eKLGGLBRGSQpqFBEgnP/pv8170f3y/r GQXktXVlj9RXVfT83afy9ap9S60JwMoO93g7ATsScvAMj18+YEhc1Uw0OCXFPR4gyTCB YbVovZgZ1i+5kS6WgRYFeEFttt1q4OprvUS9med67YHBnhM/7BbdZaeW1h/ySIFt1/nG 5zTbjID0wdrZa5Irk+VGtam9tCodRzmwaR5C5No+zZXAJhmI5Ffr8tiXB/Sp9umHmPYb T/Zg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=EPIXYQ2Xo0xQPqBBKy0zBF0STOyF8dHE75LlyHCZAzk=; b=dyzro716Hz/w3RJciKqPzxypsS+TK5U2hfUGKVOftOlyDjOGamyXu/POk2vGnsyVHd XZBOJEyS9rnerwnTNCUJoLONLgpTC0p9PsRU1dsd5SHU0W1V6MpMam6O5KLuK8bTh6CS RI5hT0om0wPnuyAC3Ftv58JvV5u+i6Efjkyj+q/MeeWnrGfQtFX9X5bOpVJCSYfHWL0x uYeJZ1DhtHBxOWUgYPHlhWKS6OV9avS2HFKBkwTC9oQgxdb74I4kJ+kvDJVdbfQQ96bg Xi6ZQPC5MJ3CyV241TPC4EdKUKqq/Sw7W3JddyMiX5GXG08B3hvOHUz93dU2/iI/FX49 y3pw== X-Gm-Message-State: AElRT7HSNf1Mw/88EsLQ9pejSxGaCD/DXacLQTHjNLlTuUPLiBhDgw5c lX3RXxJunEyPEQuHcHnmlpQGLWwS X-Received: by 10.28.159.68 with SMTP id i65mr1003067wme.27.1522379682068; Thu, 29 Mar 2018 20:14:42 -0700 (PDT) Received: from moonbase.pars.ee ([2a00:23c4:7c88:af00:c5c7:81e6:8fcc:20eb]) by smtp.gmail.com with ESMTPSA id n12sm6430940wrg.16.2018.03.29.20.14.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 29 Mar 2018 20:14:41 -0700 (PDT) From: Rostislav Pehlivanov To: ffmpeg-devel@ffmpeg.org Date: Fri, 30 Mar 2018 04:14:34 +0100 Message-Id: <20180330031434.22245-4-atomnuker@gmail.com> X-Mailer: git-send-email 2.16.3 In-Reply-To: <20180330031434.22245-1-atomnuker@gmail.com> References: <20180330031434.22245-1-atomnuker@gmail.com> Subject: [FFmpeg-devel] [PATCH 3/3] lavfi: add a Vulkan avgblur filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Rostislav Pehlivanov MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" This commit adds an average blur Vulkan filter which functions exactly the same as avgblur but on Vulkan surfaces. Currently contains a workaround that will be removed for the actual, non-RFC version. It implements a clever way of minimizing texel fetches by storing all texels needed to filter and entire wavefront's worth of workgroups in a shared cache, and then averaging over the area needed. Currently, it lacks the ability to avoid edges of images and will mix 0s around the edges of planes. This will be fixed for the non-RFC version. Signed-off-by: Rostislav Pehlivanov --- configure | 1 + libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_avgblur_vulkan.c | 353 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 356 insertions(+) create mode 100644 libavfilter/vf_avgblur_vulkan.c diff --git a/configure b/configure index 3621b5cdeb..388e45fed1 100755 --- a/configure +++ b/configure @@ -3293,6 +3293,7 @@ ass_filter_deps="libass" atempo_filter_deps="avcodec" atempo_filter_select="rdft" avgblur_opencl_filter_deps="opencl" +avgblur_vulkan_filter_deps="vulkan libshaderc" azmq_filter_deps="libzmq" blackframe_filter_deps="gpl" boxblur_filter_deps="gpl" diff --git a/libavfilter/Makefile b/libavfilter/Makefile index a90ca30ad7..f0a47320c8 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -141,6 +141,7 @@ OBJS-$(CONFIG_ATADENOISE_FILTER) += vf_atadenoise.o OBJS-$(CONFIG_AVGBLUR_FILTER) += vf_avgblur.o OBJS-$(CONFIG_AVGBLUR_OPENCL_FILTER) += vf_avgblur_opencl.o opencl.o \ opencl/avgblur.o +OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vf_avgblur_vulkan.o vulkan.o OBJS-$(CONFIG_BBOX_FILTER) += bbox.o vf_bbox.o OBJS-$(CONFIG_BENCH_FILTER) += f_bench.o OBJS-$(CONFIG_BITPLANENOISE_FILTER) += vf_bitplanenoise.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 1cf13409ca..3cbaecd726 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -150,6 +150,7 @@ static void register_all(void) REGISTER_FILTER(ATADENOISE, atadenoise, vf); REGISTER_FILTER(AVGBLUR, avgblur, vf); REGISTER_FILTER(AVGBLUR_OPENCL, avgblur_opencl, vf); + REGISTER_FILTER(AVGBLUR_VULKAN, avgblur_vulkan, vf); REGISTER_FILTER(BBOX, bbox, vf); REGISTER_FILTER(BENCH, bench, vf); REGISTER_FILTER(BITPLANENOISE, bitplanenoise, vf); diff --git a/libavfilter/vf_avgblur_vulkan.c b/libavfilter/vf_avgblur_vulkan.c new file mode 100644 index 0000000000..a2c0fddd98 --- /dev/null +++ b/libavfilter/vf_avgblur_vulkan.c @@ -0,0 +1,353 @@ +/* + * 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 "libavutil/opt.h" +#include "vulkan.h" +#include "internal.h" + +typedef struct AvgBlurVulkanContext { + VulkanFilterContext vkctx; + + int initialized; + AVVkExecContext exec; + AVVkBuffer shader_buf; + + /* Shader updators, must be in the main filter struct */ + VkDescriptorImageInfo input_images[3]; + VkDescriptorImageInfo output_images[3]; + + int size_x; + int size_y; + int planes; +} AvgBlurVulkanContext; + +#define RET(x) \ + do { \ + if ((err = (x)) < 0) \ + goto fail; \ + } while (0) + +static const char blur_kernel[] = { + C(0, #define CACHE_SIZE (ivec2(gl_WorkGroupSize) + FILTER_RADIUS*2) ) + C(0, shared vec4 cache[AREA(CACHE_SIZE)]; ) + C(0, ) + C(0, void blur_kernel(int idx, ivec2 pos) ) + C(0, { ) + C(1, ivec2 d; ) + C(1, const ivec2 s = CACHE_SIZE; ) + C(1, const ivec2 w = ivec2(gl_WorkGroupSize); ) + C(1, const ivec2 l = ivec2(gl_LocalInvocationID.xy); ) + C(1, ) + C(1, for (d.y = l.y; d.y < s.y; d.y += w.y) { ) + C(2, for (d.x = l.x; d.x < s.x; d.x += w.x) { ) + C(3, const ivec2 np = pos + d - l - FILTER_RADIUS; ) + C(3, cache[d.y*s.x + d.x] = imageLoad(input_img[idx], np); ) + C(2, } ) + C(1, } ) + C(0, ) + C(1, barrier(); ) + C(0, ) + C(1, vec4 avg = vec4(0.0f); ) + C(1, ivec2 start = ivec2(0); ) + C(1, ivec2 end = FILTER_RADIUS*2 + 1; ) + C(1, for (d.y = start.y; d.y < end.y; d.y++) ) + C(2, for (d.x = start.x; d.x < end.x; d.x++) ) + C(3, avg += cache[(l.y + d.y)*s.x + l.x + d.x]; ) + C(0, ) + C(1, avg /= AREA(end - start); ) + C(1, imageStore(output_img[idx], pos, avg); ) + C(0, } ) +}; + +static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) +{ + int err; + AvgBlurVulkanContext *s = ctx->priv; + + /* Create sampler */ + ff_vk_init_sampler(ctx, NULL); + + { /* Create the shader */ + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + + SPIRVShader *shd = ff_vk_init_shader(ctx, "avgblur_compute", + VK_SHADER_STAGE_COMPUTE_BIT); + ff_vk_set_compute_shader_sizes(ctx, shd, (int [3]){ 16, 16, 1 }); + + VulkanDescriptorSetBinding desc_i[2] = { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = "rgba8", + .mem_quali = "readonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .updater = s->input_images, + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = "rgba8", + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .updater = s->output_images, + }, + }; + + RET(ff_vk_add_descriptor_set(ctx, shd, desc_i, 2, 0)); /* set 0 */ + + GLSLF(0, #define FILTER_RADIUS ivec2(%i, %i), s->size_x, s->size_y); + GLSLD( blur_kernel ); + GLSLC(0, #define IS_WITHIN(v1, v2) ((v1.x < v2.x) && (v1.y < v2.y)) ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLF(1, for (int i = 0; i < %i; i++) {, planes); + GLSLC(2, if (!IS_WITHIN(pos, imageSize(input_img[i]))) { ); + GLSLC(3, barrier(); ); + GLSLC(3, continue; ); + GLSLC(2, } ); + GLSLC(2, else barrier(); ); /* Workaround */ + GLSLF(2, if ((0x%x & (1 << i)) != 0), s->planes); + GLSLC(3, blur_kernel(i, pos); ); + GLSLC(2, else ); + GLSLC(3, COPY_IMG(output_img[i], input_img[i], pos); ); + GLSLC(1, } ); + GLSLC(0, } ); + + RET(ff_vk_compile_shader(ctx, shd, "main")); + } + + RET(ff_vk_init_pipeline_layout(ctx)); + + /* Execution context */ + RET(av_vk_create_exec_ctx(s->vkctx.device, &s->exec, + s->vkctx.hwctx->queue_family_comp_index)); + + /* The pipeline */ + RET(ff_vk_init_compute_pipeline(ctx)); + + s->initialized = 1; + + return 0; + +fail: + return err; +} + +static int process_frames(AVFilterContext *avctx, AVVkFrame *out, AVVkFrame *in) +{ + int err; + AvgBlurVulkanContext *s = avctx->priv; + int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + + VkCommandBufferBeginInfo cmd_start = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + + VkComponentMapping null_map = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }; + + for (int i = 0; i < planes; i++) { + RET(ff_vk_create_imageview(avctx, &s->input_images[i].imageView, in, + ff_vk_plane_rep_fmt(s->vkctx.input_format, i), + ff_vk_aspect_flags(s->vkctx.input_format, i), + null_map, NULL)); + + RET(ff_vk_create_imageview(avctx, &s->output_images[i].imageView, out, + ff_vk_plane_rep_fmt(s->vkctx.output_format, i), + ff_vk_aspect_flags(s->vkctx.output_format, i), + null_map, NULL)); + + s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + ff_vk_update_descriptor_set(avctx, 0); + + vkBeginCommandBuffer(s->exec.buf, &cmd_start); + + { + VkImageMemoryBarrier bar[2] = { + { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = in->layout, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = in->img, + .subresourceRange.aspectMask = ff_vk_aspect_flags(s->vkctx.input_format, -1), + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }, + { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = out->layout, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = out->img, + .subresourceRange.aspectMask = ff_vk_aspect_flags(s->vkctx.input_format, -1), + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }, + }; + + vkCmdPipelineBarrier(s->exec.buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, + 0, NULL, 0, NULL, 2, bar); + + in->layout = bar[0].newLayout; + in->access = bar[0].dstAccessMask; + + out->layout = bar[1].newLayout; + out->access = bar[1].dstAccessMask; + } + + vkCmdBindPipeline(s->exec.buf, VK_PIPELINE_BIND_POINT_COMPUTE, s->vkctx.pipeline); + vkCmdBindDescriptorSets(s->exec.buf, VK_PIPELINE_BIND_POINT_COMPUTE, s->vkctx.pipeline_layout, 0, s->vkctx.descriptor_sets_num, s->vkctx.desc_set, 0, 0); + vkCmdDispatch(s->exec.buf, + FFALIGN(s->vkctx.output_width, s->vkctx.shaders[0].local_size[0])/s->vkctx.shaders[0].local_size[0], + FFALIGN(s->vkctx.output_height, s->vkctx.shaders[0].local_size[1])/s->vkctx.shaders[0].local_size[1], 1); + + vkEndCommandBuffer(s->exec.buf); + + VkSubmitInfo s_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &s->exec.buf, + }; + + VkResult ret = vkQueueSubmit(s->exec.queue, 1, &s_info, s->exec.fence); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to submit command buffer: %s\n", + av_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } else { + vkWaitForFences(s->vkctx.hwctx->act_dev, 1, &s->exec.fence, VK_TRUE, UINT64_MAX); + vkResetFences(s->vkctx.hwctx->act_dev, 1, &s->exec.fence); + } + +fail: + + for (int i = 0; i < planes; i++) { + ff_vk_destroy_imageview(avctx, s->input_images[i].imageView); + ff_vk_destroy_imageview(avctx, s->output_images[i].imageView); + } + + return err; +} + +static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int err; + AVFilterContext *ctx = link->dst; + AvgBlurVulkanContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + + AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + err = AVERROR(ENOMEM); + goto fail; + } + + if (!s->initialized) + RET(init_filter(ctx, in)); + + RET(process_frames(ctx, (AVVkFrame *)out->data[0], + (AVVkFrame *) in->data[0])); + + err = av_frame_copy_props(out, in); + if (err < 0) + goto fail; + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&in); + av_frame_free(&out); + return err; +} + +static void avgblur_vulkan_uninit(AVFilterContext *avctx) +{ + AvgBlurVulkanContext *s = avctx->priv; + + av_vk_free_exec_ctx(s->vkctx.device, &s->exec); + av_vk_free_buf(s->vkctx.device, &s->shader_buf); + ff_vk_filter_uninit(avctx); + + s->initialized = 0; +} + +#define OFFSET(x) offsetof(AvgBlurVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption avgblur_vulkan_options[] = { + { "sizeX", "Set horizontal radius", OFFSET(size_x), AV_OPT_TYPE_INT, {.i64 = 2}, 0, 32, .flags = FLAGS }, + { "planes", "Set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64 = 0xF}, 0, 0xF, .flags = FLAGS }, + { "sizeY", "Set vertical radius", OFFSET(size_y), AV_OPT_TYPE_INT, {.i64 = 2}, 0, 32, .flags = FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(avgblur_vulkan); + +static const AVFilterPad avgblur_vulkan_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &avgblur_vulkan_filter_frame, + .config_props = &ff_vk_filter_config_input, + }, + { NULL } +}; + +static const AVFilterPad avgblur_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_vk_filter_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_avgblur_vulkan = { + .name = "avgblur_vulkan", + .description = NULL_IF_CONFIG_SMALL("Apply avgblur mask to input video"), + .priv_size = sizeof(AvgBlurVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &avgblur_vulkan_uninit, + .query_formats = &ff_vk_filter_query_formats, + .inputs = avgblur_vulkan_inputs, + .outputs = avgblur_vulkan_outputs, + .priv_class = &avgblur_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +};