diff --git a/libavfilter/vulkan_filter.c b/libavfilter/vulkan_filter.c
index d3dc2fdacb..c354087dbc 100644
--- a/libavfilter/vulkan_filter.c
+++ b/libavfilter/vulkan_filter.c
@@ -19,6 +19,10 @@
  */
 
 #include "vulkan_filter.h"
+#include "libavutil/bprint.h"
+#include "libavutil/buffer.h"
+#include "libavutil/mem.h"
+#include "libavutil/pixdesc.h"
 #include "libavutil/vulkan_loader.h"
 
 int ff_vk_filter_init_context(AVFilterContext *avctx, FFVulkanContext *s,
@@ -457,3 +461,763 @@ fail:
     ff_vk_exec_discard_deps(vkctx, exec);
     return err;
 }
+
+static void free_data_buf(void *opaque, uint8_t *data)
+{
+    FFVulkanContext *ctx = opaque;
+    FFVkBuffer *buf = (FFVkBuffer *)data;
+    ff_vk_free_buf(ctx, buf);
+    av_free(data);
+}
+
+static AVBufferRef *alloc_data_buf(void *opaque, size_t size)
+{
+    AVBufferRef *ref;
+    uint8_t *buf = av_mallocz(size);
+    if (!buf)
+        return NULL;
+
+    ref = av_buffer_create(buf, size, free_data_buf, opaque, 0);
+    if (!ref)
+        av_free(buf);
+    return ref;
+}
+
+int ff_vk_get_pooled_buffer(FFVulkanContext *ctx, AVBufferPool **buf_pool,
+                            AVBufferRef **buf, VkBufferUsageFlags usage,
+                            void *create_pNext, size_t size,
+                            VkMemoryPropertyFlagBits mem_props)
+{
+    int err;
+    AVBufferRef *ref;
+    FFVkBuffer *data;
+
+    if (!(*buf_pool)) {
+        *buf_pool = av_buffer_pool_init2(sizeof(FFVkBuffer), ctx,
+                                         alloc_data_buf, NULL);
+        if (!(*buf_pool))
+            return AVERROR(ENOMEM);
+    }
+
+    *buf = ref = av_buffer_pool_get(*buf_pool);
+    if (!ref)
+        return AVERROR(ENOMEM);
+
+    data = (FFVkBuffer *)ref->data;
+    data->stage = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
+    data->access = VK_ACCESS_2_NONE;
+
+    if (data->size >= size)
+        return 0;
+
+    ff_vk_free_buf(ctx, data);
+    memset(data, 0, sizeof(*data));
+
+    av_log(ctx, AV_LOG_DEBUG, "Allocating buffer of %"SIZE_SPECIFIER" bytes for pool %p\n",
+           size, *buf_pool);
+
+    err = ff_vk_create_buf(ctx, data, size,
+                           create_pNext, NULL, usage,
+                           mem_props);
+    if (err < 0) {
+        av_buffer_unref(&ref);
+        return err;
+    }
+
+    if (mem_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+        err = ff_vk_map_buffer(ctx, data, &data->mapped_mem, 0);
+        if (err < 0) {
+            av_buffer_unref(&ref);
+            return err;
+        }
+    }
+
+    return 0;
+}
+
+int ff_vk_add_push_constant(FFVulkanPipeline *pl, int offset, int size,
+                            VkShaderStageFlagBits stage)
+{
+    VkPushConstantRange *pc;
+
+    pl->push_consts = av_realloc_array(pl->push_consts, sizeof(*pl->push_consts),
+                                       pl->push_consts_num + 1);
+    if (!pl->push_consts)
+        return AVERROR(ENOMEM);
+
+    pc = &pl->push_consts[pl->push_consts_num++];
+    memset(pc, 0, sizeof(*pc));
+
+    pc->stageFlags = stage;
+    pc->offset = offset;
+    pc->size = size;
+
+    return 0;
+}
+
+int ff_vk_init_sampler(FFVulkanContext *s, VkSampler *sampler,
+                       int unnorm_coords, VkFilter filt)
+{
+    VkResult ret;
+    FFVulkanFunctions *vk = &s->vkfn;
+
+    VkSamplerCreateInfo sampler_info = {
+        .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+        .magFilter = filt,
+        .minFilter = sampler_info.magFilter,
+        .mipmapMode = unnorm_coords ? VK_SAMPLER_MIPMAP_MODE_NEAREST :
+                                      VK_SAMPLER_MIPMAP_MODE_LINEAR,
+        .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+        .addressModeV = sampler_info.addressModeU,
+        .addressModeW = sampler_info.addressModeU,
+        .anisotropyEnable = VK_FALSE,
+        .compareOp = VK_COMPARE_OP_NEVER,
+        .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
+        .unnormalizedCoordinates = unnorm_coords,
+    };
+
+    ret = vk->CreateSampler(s->hwctx->act_dev, &sampler_info,
+                            s->hwctx->alloc, sampler);
+    if (ret != VK_SUCCESS) {
+        av_log(s, AV_LOG_ERROR, "Unable to init sampler: %s\n",
+               ff_vk_ret2str(ret));
+        return AVERROR_EXTERNAL;
+    }
+
+    return 0;
+}
+
+int ff_vk_mt_is_np_rgb(enum AVPixelFormat pix_fmt)
+{
+    if (pix_fmt == AV_PIX_FMT_ABGR   || pix_fmt == AV_PIX_FMT_BGRA   ||
+        pix_fmt == AV_PIX_FMT_RGBA   || pix_fmt == AV_PIX_FMT_RGB24  ||
+        pix_fmt == AV_PIX_FMT_BGR24  || pix_fmt == AV_PIX_FMT_RGB48  ||
+        pix_fmt == AV_PIX_FMT_RGBA64 || pix_fmt == AV_PIX_FMT_RGB565 ||
+        pix_fmt == AV_PIX_FMT_BGR565 || pix_fmt == AV_PIX_FMT_BGR0   ||
+        pix_fmt == AV_PIX_FMT_0BGR   || pix_fmt == AV_PIX_FMT_RGB0)
+        return 1;
+    return 0;
+}
+
+typedef struct ImageViewCtx {
+    VkImageView views[AV_NUM_DATA_POINTERS];
+    int nb_views;
+} ImageViewCtx;
+
+static void destroy_imageviews(void *opaque, uint8_t *data)
+{
+    FFVulkanContext *s = opaque;
+    FFVulkanFunctions *vk = &s->vkfn;
+    ImageViewCtx *iv = (ImageViewCtx *)data;
+
+    for (int i = 0; i < iv->nb_views; i++)
+        vk->DestroyImageView(s->hwctx->act_dev, iv->views[i], s->hwctx->alloc);
+
+    av_free(iv);
+}
+
+int ff_vk_create_imageviews(FFVulkanContext *s, FFVkExecContext *e,
+                            VkImageView views[AV_NUM_DATA_POINTERS],
+                            AVFrame *f)
+{
+    int err;
+    VkResult ret;
+    AVBufferRef *buf;
+    FFVulkanFunctions *vk = &s->vkfn;
+    AVHWFramesContext *hwfc = (AVHWFramesContext *)f->hw_frames_ctx->data;
+    const VkFormat *rep_fmts = av_vkfmt_from_pixfmt(hwfc->sw_format);
+    AVVkFrame *vkf = (AVVkFrame *)f->data[0];
+    const int nb_images = ff_vk_count_images(vkf);
+    const int nb_planes = av_pix_fmt_count_planes(hwfc->sw_format);
+
+    ImageViewCtx *iv = av_mallocz(sizeof(*iv));
+    if (!iv)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < nb_planes; i++) {
+        VkImageAspectFlags plane_aspect[] = { VK_IMAGE_ASPECT_COLOR_BIT,
+                                              VK_IMAGE_ASPECT_PLANE_0_BIT,
+                                              VK_IMAGE_ASPECT_PLANE_1_BIT,
+                                              VK_IMAGE_ASPECT_PLANE_2_BIT, };
+
+        VkImageViewCreateInfo view_create_info = {
+            .sType      = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+            .pNext      = NULL,
+            .image      = vkf->img[FFMIN(i, nb_images - 1)],
+            .viewType   = VK_IMAGE_VIEW_TYPE_2D,
+            .format     = rep_fmts[i],
+            .components = ff_comp_identity_map,
+            .subresourceRange = {
+                .aspectMask = plane_aspect[(nb_planes != nb_images) +
+                                           i*(nb_planes != nb_images)],
+                .levelCount = 1,
+                .layerCount = 1,
+            },
+        };
+
+        ret = vk->CreateImageView(s->hwctx->act_dev, &view_create_info,
+                                  s->hwctx->alloc, &iv->views[i]);
+        if (ret != VK_SUCCESS) {
+            av_log(s, AV_LOG_ERROR, "Failed to create imageview: %s\n",
+                   ff_vk_ret2str(ret));
+            err = AVERROR_EXTERNAL;
+            goto fail;
+        }
+
+        iv->nb_views++;
+    }
+
+    buf = av_buffer_create((uint8_t *)iv, sizeof(*iv), destroy_imageviews, s, 0);
+    if (!buf) {
+        err = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    /* Add to queue dependencies */
+    err = ff_vk_exec_add_dep_buf(s, e, &buf, 1, 0);
+    if (err < 0)
+        av_buffer_unref(&buf);
+
+    memcpy(views, iv->views, nb_planes*sizeof(*views));
+
+    return err;
+
+fail:
+    for (int i = 0; i < iv->nb_views; i++)
+        vk->DestroyImageView(s->hwctx->act_dev, iv->views[i], s->hwctx->alloc);
+    av_free(iv);
+    return err;
+}
+
+const char *ff_vk_shader_rep_fmt(enum AVPixelFormat pixfmt)
+{
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pixfmt);
+    const int high = desc->comp[0].depth > 8;
+    return high ? "rgba16f" : "rgba8";
+}
+
+int ff_vk_shader_init(FFVulkanPipeline *pl, FFVkSPIRVShader *shd, const char *name,
+                      VkShaderStageFlags stage, uint32_t required_subgroup_size)
+{
+    av_bprint_init(&shd->src, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    shd->shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+    shd->shader.stage = stage;
+
+    if (required_subgroup_size) {
+        shd->shader.flags |= VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT;
+        shd->shader.pNext = &shd->subgroup_info;
+        shd->subgroup_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO;
+        shd->subgroup_info.requiredSubgroupSize = required_subgroup_size;
+    }
+
+    shd->name = name;
+
+    GLSLF(0, #version %i                                                  ,460);
+    GLSLC(0, #define IS_WITHIN(v1, v2) ((v1.x < v2.x) && (v1.y < v2.y))       );
+    GLSLC(0,                                                                  );
+    GLSLC(0, #extension GL_EXT_buffer_reference : require                     );
+    GLSLC(0, #extension GL_EXT_buffer_reference2 : require                    );
+
+    return 0;
+}
+
+void ff_vk_shader_set_compute_sizes(FFVkSPIRVShader *shd, int x, int y, int z)
+{
+    shd->local_size[0] = x;
+    shd->local_size[1] = y;
+    shd->local_size[2] = z;
+
+    av_bprintf(&shd->src, "layout (local_size_x = %i, "
+               "local_size_y = %i, local_size_z = %i) in;\n\n",
+               shd->local_size[0], shd->local_size[1], shd->local_size[2]);
+}
+
+void ff_vk_shader_print(void *ctx, FFVkSPIRVShader *shd, int prio)
+{
+    int line = 0;
+    const char *p = shd->src.str;
+    const char *start = p;
+    const size_t len = strlen(p);
+
+    AVBPrint buf;
+    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    for (int i = 0; i < len; i++) {
+        if (p[i] == '\n') {
+            av_bprintf(&buf, "%i\t", ++line);
+            av_bprint_append_data(&buf, start, &p[i] - start + 1);
+            start = &p[i + 1];
+        }
+    }
+
+    av_log(ctx, prio, "Shader %s: \n%s", shd->name, buf.str);
+    av_bprint_finalize(&buf, NULL);
+}
+
+void ff_vk_shader_free(FFVulkanContext *s, FFVkSPIRVShader *shd)
+{
+    FFVulkanFunctions *vk = &s->vkfn;
+    av_bprint_finalize(&shd->src, NULL);
+
+    if (shd->shader.module)
+        vk->DestroyShaderModule(s->hwctx->act_dev, shd->shader.module, s->hwctx->alloc);
+}
+
+int ff_vk_shader_create(FFVulkanContext *s, FFVkSPIRVShader *shd,
+                        uint8_t *spirv, size_t spirv_size, const char *entrypoint)
+{
+    VkResult ret;
+    FFVulkanFunctions *vk = &s->vkfn;
+    VkShaderModuleCreateInfo shader_create;
+
+    shd->shader.pName = entrypoint;
+
+    av_log(s, AV_LOG_VERBOSE, "Shader %s compiled! Size: %zu bytes\n",
+           shd->name, spirv_size);
+
+    shader_create.sType    = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+    shader_create.pNext    = NULL;
+    shader_create.codeSize = spirv_size;
+    shader_create.flags    = 0;
+    shader_create.pCode    = (void *)spirv;
+
+    ret = vk->CreateShaderModule(s->hwctx->act_dev, &shader_create, NULL,
+                                 &shd->shader.module);
+    if (ret != VK_SUCCESS) {
+        av_log(s, AV_LOG_VERBOSE, "Error creating shader module: %s\n",
+               ff_vk_ret2str(ret));
+        return AVERROR_EXTERNAL;
+    }
+
+    return 0;
+}
+
+static const struct descriptor_props {
+    size_t struct_size; /* Size of the opaque which updates the descriptor */
+    const char *type;
+    int is_uniform;
+    int mem_quali;      /* Can use a memory qualifier */
+    int dim_needed;     /* Must indicate dimension */
+    int buf_content;    /* Must indicate buffer contents */
+} descriptor_props[] = {
+    [VK_DESCRIPTOR_TYPE_SAMPLER]                = { sizeof(VkDescriptorImageInfo),  "sampler",       1, 0, 0, 0, },
+    [VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]          = { sizeof(VkDescriptorImageInfo),  "texture",       1, 0, 1, 0, },
+    [VK_DESCRIPTOR_TYPE_STORAGE_IMAGE]          = { sizeof(VkDescriptorImageInfo),  "image",         1, 1, 1, 0, },
+    [VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT]       = { sizeof(VkDescriptorImageInfo),  "subpassInput",  1, 0, 0, 0, },
+    [VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] = { sizeof(VkDescriptorImageInfo),  "sampler",       1, 0, 1, 0, },
+    [VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]         = { sizeof(VkDescriptorBufferInfo),  NULL,           1, 0, 0, 1, },
+    [VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]         = { sizeof(VkDescriptorBufferInfo), "buffer",        0, 1, 0, 1, },
+    [VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] = { sizeof(VkDescriptorBufferInfo),  NULL,           1, 0, 0, 1, },
+    [VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] = { sizeof(VkDescriptorBufferInfo), "buffer",        0, 1, 0, 1, },
+    [VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]   = { sizeof(VkBufferView),           "samplerBuffer", 1, 0, 0, 0, },
+    [VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]   = { sizeof(VkBufferView),           "imageBuffer",   1, 0, 0, 0, },
+};
+
+int ff_vk_pipeline_descriptor_set_add(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                      FFVkSPIRVShader *shd,
+                                      FFVulkanDescriptorSetBinding *desc, int nb,
+                                      int read_only, int print_to_shader_only)
+{
+    VkResult ret;
+    int has_sampler = 0;
+    FFVulkanFunctions *vk = &s->vkfn;
+    FFVulkanDescriptorSet *set;
+    VkDescriptorSetLayoutCreateInfo desc_create_layout;
+
+    if (print_to_shader_only)
+        goto print;
+
+    /* Actual layout allocated for the pipeline */
+    set = av_realloc_array(pl->desc_set, sizeof(*pl->desc_set),
+                           pl->nb_descriptor_sets + 1);
+    if (!set)
+        return AVERROR(ENOMEM);
+    pl->desc_set = set;
+    set = &set[pl->nb_descriptor_sets];
+    memset(set, 0, sizeof(*set));
+
+    set->binding = av_calloc(nb, sizeof(*set->binding));
+    if (!set->binding)
+        return AVERROR(ENOMEM);
+
+    set->binding_offset = av_calloc(nb, sizeof(*set->binding_offset));
+    if (!set->binding_offset) {
+        av_freep(&set->binding);
+        return AVERROR(ENOMEM);
+    }
+
+    desc_create_layout = (VkDescriptorSetLayoutCreateInfo) {
+        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+        .bindingCount = nb,
+        .pBindings = set->binding,
+        .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT,
+    };
+
+    for (int i = 0; i < nb; i++) {
+        set->binding[i].binding            = i;
+        set->binding[i].descriptorType     = desc[i].type;
+        set->binding[i].descriptorCount    = FFMAX(desc[i].elems, 1);
+        set->binding[i].stageFlags         = desc[i].stages;
+        set->binding[i].pImmutableSamplers = desc[i].samplers;
+
+        if (desc[i].type == VK_DESCRIPTOR_TYPE_SAMPLER ||
+            desc[i].type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+            has_sampler |= 1;
+    }
+
+    set->usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT |
+                 VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
+    if (has_sampler)
+        set->usage |= VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;
+
+    ret = vk->CreateDescriptorSetLayout(s->hwctx->act_dev, &desc_create_layout,
+                                        s->hwctx->alloc, &set->layout);
+    if (ret != VK_SUCCESS) {
+        av_log(s, AV_LOG_ERROR, "Unable to init descriptor set layout: %s",
+               ff_vk_ret2str(ret));
+        return AVERROR_EXTERNAL;
+    }
+
+    vk->GetDescriptorSetLayoutSizeEXT(s->hwctx->act_dev, set->layout, &set->layout_size);
+
+    set->aligned_size = FFALIGN(set->layout_size, s->desc_buf_props.descriptorBufferOffsetAlignment);
+
+    for (int i = 0; i < nb; i++)
+        vk->GetDescriptorSetLayoutBindingOffsetEXT(s->hwctx->act_dev, set->layout,
+                                                   i, &set->binding_offset[i]);
+
+    set->read_only = read_only;
+    set->nb_bindings = nb;
+    pl->nb_descriptor_sets++;
+
+print:
+    /* Write shader info */
+    for (int i = 0; i < nb; i++) {
+        const struct descriptor_props *prop = &descriptor_props[desc[i].type];
+        GLSLA("layout (set = %i, binding = %i", pl->nb_descriptor_sets - 1, i);
+
+        if (desc[i].mem_layout)
+            GLSLA(", %s", desc[i].mem_layout);
+        GLSLA(")");
+
+        if (prop->is_uniform)
+            GLSLA(" uniform");
+
+        if (prop->mem_quali && desc[i].mem_quali)
+            GLSLA(" %s", desc[i].mem_quali);
+
+        if (prop->type)
+            GLSLA(" %s", prop->type);
+
+        if (prop->dim_needed)
+            GLSLA("%iD", desc[i].dimensions);
+
+        GLSLA(" %s", desc[i].name);
+
+        if (prop->buf_content)
+            GLSLA(" {\n    %s\n}", desc[i].buf_content);
+        else if (desc[i].elems > 0)
+            GLSLA("[%i]", desc[i].elems);
+
+        GLSLA(";");
+        GLSLA("\n");
+    }
+    GLSLA("\n");
+
+    return 0;
+}
+
+int ff_vk_exec_pipeline_register(FFVulkanContext *s, FFVkExecPool *pool,
+                                 FFVulkanPipeline *pl)
+{
+    int err;
+
+    pl->desc_bind = av_calloc(pl->nb_descriptor_sets, sizeof(*pl->desc_bind));
+    if (!pl->desc_bind)
+        return AVERROR(ENOMEM);
+
+    pl->bound_buffer_indices = av_calloc(pl->nb_descriptor_sets,
+                                         sizeof(*pl->bound_buffer_indices));
+    if (!pl->bound_buffer_indices)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < pl->nb_descriptor_sets; i++) {
+        FFVulkanDescriptorSet *set = &pl->desc_set[i];
+        int nb = set->read_only ? 1 : pool->pool_size;
+
+        err = ff_vk_create_buf(s, &set->buf, set->aligned_size*nb,
+                               NULL, NULL, set->usage,
+                               VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+                               VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+        if (err < 0)
+            return err;
+
+        err = ff_vk_map_buffer(s, &set->buf, &set->desc_mem, 0);
+        if (err < 0)
+            return err;
+
+        pl->desc_bind[i] = (VkDescriptorBufferBindingInfoEXT) {
+            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT,
+            .usage = set->usage,
+            .address = set->buf.address,
+        };
+
+        pl->bound_buffer_indices[i] = i;
+    }
+
+    return 0;
+}
+
+static inline void update_set_descriptor(FFVulkanContext *s, FFVkExecContext *e,
+                                         FFVulkanDescriptorSet *set,
+                                         int bind_idx, int array_idx,
+                                         VkDescriptorGetInfoEXT *desc_get_info,
+                                         size_t desc_size)
+{
+    FFVulkanFunctions *vk = &s->vkfn;
+    const size_t exec_offset = set->read_only ? 0 : set->aligned_size*e->idx;
+    void *desc = set->desc_mem +                 /* Base */
+                 exec_offset +                   /* Execution context */
+                 set->binding_offset[bind_idx] + /* Descriptor binding */
+                 array_idx*desc_size;            /* Array position */
+
+    vk->GetDescriptorEXT(s->hwctx->act_dev, desc_get_info, desc_size, desc);
+}
+
+static int vk_set_descriptor_image(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                   FFVkExecContext *e, int set, int bind, int offs,
+                                   VkImageView view, VkImageLayout layout,
+                                   VkSampler sampler)
+{
+    FFVulkanDescriptorSet *desc_set = &pl->desc_set[set];
+    VkDescriptorGetInfoEXT desc_get_info = {
+        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT,
+        .type = desc_set->binding[bind].descriptorType,
+    };
+    VkDescriptorImageInfo desc_img_info = {
+        .imageView = view,
+        .sampler = sampler,
+        .imageLayout = layout,
+    };
+    size_t desc_size;
+
+    switch (desc_get_info.type) {
+    case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+        desc_get_info.data.pSampledImage = &desc_img_info;
+        desc_size = s->desc_buf_props.sampledImageDescriptorSize;
+        break;
+    case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+        desc_get_info.data.pStorageImage = &desc_img_info;
+        desc_size = s->desc_buf_props.storageImageDescriptorSize;
+        break;
+    case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+        desc_get_info.data.pInputAttachmentImage = &desc_img_info;
+        desc_size = s->desc_buf_props.inputAttachmentDescriptorSize;
+        break;
+    case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+        desc_get_info.data.pCombinedImageSampler = &desc_img_info;
+        desc_size = s->desc_buf_props.combinedImageSamplerDescriptorSize;
+        break;
+    default:
+        av_log(s, AV_LOG_ERROR, "Invalid descriptor type at set %i binding %i: %i!\n",
+               set, bind, desc_get_info.type);
+        return AVERROR(EINVAL);
+        break;
+    };
+
+    update_set_descriptor(s, e, desc_set, bind, offs, &desc_get_info, desc_size);
+
+    return 0;
+}
+
+int ff_vk_set_descriptor_buffer(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                FFVkExecContext *e, int set, int bind, int offs,
+                                VkDeviceAddress addr, VkDeviceSize len, VkFormat fmt)
+{
+    FFVulkanDescriptorSet *desc_set = &pl->desc_set[set];
+    VkDescriptorGetInfoEXT desc_get_info = {
+        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT,
+        .type = desc_set->binding[bind].descriptorType,
+    };
+    VkDescriptorAddressInfoEXT desc_buf_info = {
+        .address = addr,
+        .range = len,
+        .format = fmt,
+    };
+    size_t desc_size;
+
+    switch (desc_get_info.type) {
+    case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+        desc_get_info.data.pUniformBuffer = &desc_buf_info;
+        desc_size = s->desc_buf_props.uniformBufferDescriptorSize;
+        break;
+    case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+        desc_get_info.data.pStorageBuffer = &desc_buf_info;
+        desc_size = s->desc_buf_props.storageBufferDescriptorSize;
+        break;
+    case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+        desc_get_info.data.pUniformTexelBuffer = &desc_buf_info;
+        desc_size = s->desc_buf_props.uniformTexelBufferDescriptorSize;
+        break;
+    case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+        desc_get_info.data.pStorageTexelBuffer = &desc_buf_info;
+        desc_size = s->desc_buf_props.storageTexelBufferDescriptorSize;
+        break;
+    default:
+        av_log(s, AV_LOG_ERROR, "Invalid descriptor type at set %i binding %i: %i!\n",
+               set, bind, desc_get_info.type);
+        return AVERROR(EINVAL);
+        break;
+    };
+
+    update_set_descriptor(s, e, desc_set, bind, offs, &desc_get_info, desc_size);
+
+    return 0;
+}
+
+void ff_vk_update_descriptor_img_array(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                       FFVkExecContext *e, AVFrame *f,
+                                       VkImageView *views, int set, int binding,
+                                       VkImageLayout layout, VkSampler sampler)
+{
+    AVHWFramesContext *hwfc = (AVHWFramesContext *)f->hw_frames_ctx->data;
+    const int nb_planes = av_pix_fmt_count_planes(hwfc->sw_format);
+
+    for (int i = 0; i < nb_planes; i++)
+        vk_set_descriptor_image(s, pl, e, set, binding, i,
+                                views[i], layout, sampler);
+}
+
+void ff_vk_update_push_exec(FFVulkanContext *s, FFVkExecContext *e,
+                            FFVulkanPipeline *pl,
+                            VkShaderStageFlagBits stage,
+                            int offset, size_t size, void *src)
+{
+    FFVulkanFunctions *vk = &s->vkfn;
+    vk->CmdPushConstants(e->buf, pl->pipeline_layout,
+                         stage, offset, size, src);
+}
+
+static int init_pipeline_layout(FFVulkanContext *s, FFVulkanPipeline *pl)
+{
+    VkResult ret;
+    FFVulkanFunctions *vk = &s->vkfn;
+    VkPipelineLayoutCreateInfo pipeline_layout_info;
+
+    VkDescriptorSetLayout *desc_layouts = av_malloc(pl->nb_descriptor_sets*
+                                                    sizeof(desc_layouts));
+    if (!desc_layouts)
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < pl->nb_descriptor_sets; i++)
+        desc_layouts[i] = pl->desc_set[i].layout;
+
+    /* Finally create the pipeline layout */
+    pipeline_layout_info = (VkPipelineLayoutCreateInfo) {
+        .sType                  = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+        .pSetLayouts            = desc_layouts,
+        .setLayoutCount         = pl->nb_descriptor_sets,
+        .pushConstantRangeCount = pl->push_consts_num,
+        .pPushConstantRanges    = pl->push_consts,
+    };
+
+    ret = vk->CreatePipelineLayout(s->hwctx->act_dev, &pipeline_layout_info,
+                                   s->hwctx->alloc, &pl->pipeline_layout);
+    av_free(desc_layouts);
+    if (ret != VK_SUCCESS) {
+        av_log(s, AV_LOG_ERROR, "Unable to init pipeline layout: %s\n",
+               ff_vk_ret2str(ret));
+        return AVERROR_EXTERNAL;
+    }
+
+    return 0;
+}
+
+int ff_vk_init_compute_pipeline(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                FFVkSPIRVShader *shd)
+{
+    int err;
+    VkResult ret;
+    FFVulkanFunctions *vk = &s->vkfn;
+
+    VkComputePipelineCreateInfo pipeline_create_info;
+
+    err = init_pipeline_layout(s, pl);
+    if (err < 0)
+        return err;
+
+    pipeline_create_info = (VkComputePipelineCreateInfo) {
+        .sType  = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
+        .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT,
+        .layout = pl->pipeline_layout,
+        .stage = shd->shader,
+    };
+
+    ret = vk->CreateComputePipelines(s->hwctx->act_dev, VK_NULL_HANDLE, 1,
+                                     &pipeline_create_info,
+                                     s->hwctx->alloc, &pl->pipeline);
+    if (ret != VK_SUCCESS) {
+        av_log(s, AV_LOG_ERROR, "Unable to init compute pipeline: %s\n",
+               ff_vk_ret2str(ret));
+        return AVERROR_EXTERNAL;
+    }
+
+    pl->bind_point = VK_PIPELINE_BIND_POINT_COMPUTE;
+    pl->wg_size[0] = shd->local_size[0];
+    pl->wg_size[1] = shd->local_size[1];
+    pl->wg_size[2] = shd->local_size[2];
+
+    return 0;
+}
+
+void ff_vk_exec_bind_pipeline(FFVulkanContext *s, FFVkExecContext *e,
+                              FFVulkanPipeline *pl)
+{
+    FFVulkanFunctions *vk = &s->vkfn;
+    VkDeviceSize offsets[1024];
+
+    /* Bind pipeline */
+    vk->CmdBindPipeline(e->buf, pl->bind_point, pl->pipeline);
+
+    if (pl->nb_descriptor_sets) {
+        for (int i = 0; i < pl->nb_descriptor_sets; i++)
+            offsets[i] = pl->desc_set[i].read_only ? 0 : pl->desc_set[i].aligned_size*e->idx;
+
+        /* Bind descriptor buffers */
+        vk->CmdBindDescriptorBuffersEXT(e->buf, pl->nb_descriptor_sets, pl->desc_bind);
+        /* Binding offsets */
+        vk->CmdSetDescriptorBufferOffsetsEXT(e->buf, pl->bind_point, pl->pipeline_layout,
+                                             0, pl->nb_descriptor_sets,
+                                             pl->bound_buffer_indices, offsets);
+    }
+}
+
+void ff_vk_pipeline_free(FFVulkanContext *s, FFVulkanPipeline *pl)
+{
+    FFVulkanFunctions *vk = &s->vkfn;
+
+    if (pl->pipeline)
+        vk->DestroyPipeline(s->hwctx->act_dev, pl->pipeline, s->hwctx->alloc);
+    if (pl->pipeline_layout)
+        vk->DestroyPipelineLayout(s->hwctx->act_dev, pl->pipeline_layout,
+                                  s->hwctx->alloc);
+
+    for (int i = 0; i < pl->nb_descriptor_sets; i++) {
+        FFVulkanDescriptorSet *set = &pl->desc_set[i];
+        if (set->buf.mem)
+            ff_vk_unmap_buffer(s, &set->buf, 0);
+        ff_vk_free_buf(s, &set->buf);
+        if (set->layout)
+            vk->DestroyDescriptorSetLayout(s->hwctx->act_dev, set->layout,
+                                           s->hwctx->alloc);
+        av_free(set->binding);
+        av_free(set->binding_offset);
+    }
+
+    av_freep(&pl->desc_set);
+    av_freep(&pl->desc_bind);
+    av_freep(&pl->bound_buffer_indices);
+    av_freep(&pl->push_consts);
+    pl->push_consts_num = 0;
+}
diff --git a/libavfilter/vulkan_filter.h b/libavfilter/vulkan_filter.h
index d2c14601d9..9655f43caa 100644
--- a/libavfilter/vulkan_filter.h
+++ b/libavfilter/vulkan_filter.h
@@ -25,6 +25,98 @@
 
 #include "vulkan.h"
 
+#include "libavutil/bprint.h"
+
+/* GLSL management macros */
+#define INDENT(N) INDENT_##N
+#define INDENT_0
+#define INDENT_1 INDENT_0 "    "
+#define INDENT_2 INDENT_1 INDENT_1
+#define INDENT_3 INDENT_2 INDENT_1
+#define INDENT_4 INDENT_3 INDENT_1
+#define INDENT_5 INDENT_4 INDENT_1
+#define INDENT_6 INDENT_5 INDENT_1
+#define C(N, S)          INDENT(N) #S "\n"
+
+#define GLSLC(N, S)                     \
+    do {                                \
+        av_bprintf(&shd->src, C(N, S)); \
+    } while (0)
+
+#define GLSLA(...)                          \
+    do {                                    \
+        av_bprintf(&shd->src, __VA_ARGS__); \
+    } while (0)
+
+#define GLSLF(N, S, ...)                             \
+    do {                                             \
+        av_bprintf(&shd->src, C(N, S), __VA_ARGS__); \
+    } while (0)
+
+#define GLSLD(D)                                        \
+    do {                                                \
+        av_bprintf(&shd->src, "\n");                    \
+        av_bprint_append_data(&shd->src, D, strlen(D)); \
+        av_bprintf(&shd->src, "\n");                    \
+    } while (0)
+
+typedef struct FFVkSPIRVShader {
+    const char *name;                       /* Name for id/debugging purposes */
+    AVBPrint src;
+    int local_size[3];                      /* Compute shader workgroup sizes */
+    VkPipelineShaderStageCreateInfo shader;
+    VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_info;
+} FFVkSPIRVShader;
+
+typedef struct FFVulkanDescriptorSetBinding {
+    const char         *name;
+    VkDescriptorType    type;
+    const char         *mem_layout;  /* Storage images (rgba8, etc.) and buffers (std430, etc.) */
+    const char         *mem_quali;   /* readonly, writeonly, etc. */
+    const char         *buf_content; /* For buffers */
+    uint32_t            dimensions;  /* Needed for e.g. sampler%iD */
+    uint32_t            elems;       /* 0 - scalar, 1 or more - vector */
+    VkShaderStageFlags  stages;
+    VkSampler           samplers[4]; /* Sampler to use for all elems */
+} FFVulkanDescriptorSetBinding;
+
+typedef struct FFVulkanDescriptorSet {
+    VkDescriptorSetLayout  layout;
+    FFVkBuffer             buf;
+    uint8_t               *desc_mem;
+    VkDeviceSize           layout_size;
+    VkDeviceSize           aligned_size; /* descriptorBufferOffsetAlignment */
+    VkDeviceSize           total_size; /* Once registered to an exec context */
+    VkBufferUsageFlags     usage;
+
+    VkDescriptorSetLayoutBinding *binding;
+    VkDeviceSize *binding_offset;
+    int nb_bindings;
+
+    int read_only;
+} FFVulkanDescriptorSet;
+
+typedef struct FFVulkanPipeline {
+    VkPipelineBindPoint bind_point;
+
+    /* Contexts */
+    VkPipelineLayout pipeline_layout;
+    VkPipeline       pipeline;
+
+    /* Push consts */
+    VkPushConstantRange *push_consts;
+    int push_consts_num;
+
+    /* Workgroup */
+    int wg_size[3];
+
+    /* Descriptors */
+    FFVulkanDescriptorSet *desc_set;
+    VkDescriptorBufferBindingInfoEXT *desc_bind;
+    uint32_t *bound_buffer_indices;
+    int nb_descriptor_sets;
+} FFVulkanPipeline;
+
 /**
  * General lavfi IO functions
  */
@@ -62,4 +154,89 @@ int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e,
                              AVFrame *out, AVFrame *in[], int nb_in,
                              VkSampler sampler, void *push_src, size_t push_size);
 
+/**
+ * Returns 1 if pixfmt is a usable RGB format.
+ */
+int ff_vk_mt_is_np_rgb(enum AVPixelFormat pix_fmt);
+
+/**
+ * Create an imageview and add it as a dependency to an execution.
+ */
+int ff_vk_create_imageviews(FFVulkanContext *s, FFVkExecContext *e,
+                            VkImageView views[AV_NUM_DATA_POINTERS],
+                            AVFrame *f);
+
+/** Initialize a pool and create AVBufferRefs containing FFVkBuffer.
+ * Threadsafe to use. Buffers are automatically mapped on creation if
+ * VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT is set in mem_props. Users should
+ * synchronize access themselvesd. Mainly meant for device-local buffers. */
+int ff_vk_get_pooled_buffer(FFVulkanContext *ctx, AVBufferPool **buf_pool,
+                            AVBufferRef **buf, VkBufferUsageFlags usage,
+                            void *create_pNext, size_t size,
+                            VkMemoryPropertyFlagBits mem_props);
+
+/**
+ * Create a sampler.
+ */
+int ff_vk_init_sampler(FFVulkanContext *s, VkSampler *sampler,
+                       int unnorm_coords, VkFilter filt);
+
+/**
+ * Returns the format to use for images in shaders.
+ */
+const char *ff_vk_shader_rep_fmt(enum AVPixelFormat pixfmt);
+/**
+ * Shader management.
+ */
+int ff_vk_shader_init(FFVulkanPipeline *pl, FFVkSPIRVShader *shd, const char *name,
+                      VkShaderStageFlags stage, uint32_t required_subgroup_size);
+void ff_vk_shader_set_compute_sizes(FFVkSPIRVShader *shd, int x, int y, int z);
+void ff_vk_shader_print(void *ctx, FFVkSPIRVShader *shd, int prio);
+int ff_vk_shader_create(FFVulkanContext *s, FFVkSPIRVShader *shd,
+                        uint8_t *spirv, size_t spirv_size, const char *entrypoint);
+void ff_vk_shader_free(FFVulkanContext *s, FFVkSPIRVShader *shd);
+
+/**
+ * Add/update push constants for execution.
+ */
+int ff_vk_add_push_constant(FFVulkanPipeline *pl, int offset, int size,
+                            VkShaderStageFlagBits stage);
+void ff_vk_update_push_exec(FFVulkanContext *s, FFVkExecContext *e,
+                            FFVulkanPipeline *pl,
+                            VkShaderStageFlagBits stage,
+                            int offset, size_t size, void *src);
+
+/**
+ * Add descriptor to a pipeline. Must be called before pipeline init.
+ */
+int ff_vk_pipeline_descriptor_set_add(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                      FFVkSPIRVShader *shd,
+                                      FFVulkanDescriptorSetBinding *desc, int nb,
+                                      int read_only, int print_to_shader_only);
+
+/* Initialize/free a pipeline. */
+int ff_vk_init_compute_pipeline(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                FFVkSPIRVShader *shd);
+void ff_vk_pipeline_free(FFVulkanContext *s, FFVulkanPipeline *pl);
+
+/**
+ * Register a pipeline with an exec pool.
+ * Pool may be NULL if all descriptor sets are read-only.
+ */
+int ff_vk_exec_pipeline_register(FFVulkanContext *s, FFVkExecPool *pool,
+                                 FFVulkanPipeline *pl);
+
+/* Bind pipeline */
+void ff_vk_exec_bind_pipeline(FFVulkanContext *s, FFVkExecContext *e,
+                              FFVulkanPipeline *pl);
+
+int ff_vk_set_descriptor_buffer(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                FFVkExecContext *e, int set, int bind, int offs,
+                                VkDeviceAddress addr, VkDeviceSize len, VkFormat fmt);
+
+void ff_vk_update_descriptor_img_array(FFVulkanContext *s, FFVulkanPipeline *pl,
+                                       FFVkExecContext *e, AVFrame *f,
+                                       VkImageView *views, int set, int binding,
+                                       VkImageLayout layout, VkSampler sampler);
+
 #endif /* AVFILTER_VULKAN_FILTER_H */
diff --git a/libavfilter/vulkan_glslang.c b/libavfilter/vulkan_glslang.c
index 845a530ee0..9ee6ad7c39 100644
--- a/libavfilter/vulkan_glslang.c
+++ b/libavfilter/vulkan_glslang.c
@@ -21,6 +21,7 @@
 #include <glslang/build_info.h>
 #include <glslang/Include/glslang_c_interface.h>
 
+#include "vulkan_filter.h"
 #include "vulkan_spirv.h"
 #include "libavutil/mem.h"
 #include "libavutil/avassert.h"
diff --git a/libavfilter/vulkan_shaderc.c b/libavfilter/vulkan_shaderc.c
index 38be1030ad..57c62b618b 100644
--- a/libavfilter/vulkan_shaderc.c
+++ b/libavfilter/vulkan_shaderc.c
@@ -19,6 +19,7 @@
 #include <shaderc/shaderc.h>
 
 #include "libavutil/mem.h"
+#include "vulkan_filter.h"
 #include "vulkan_spirv.h"
 
 static int shdc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx,
diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c
index 67b9526255..f609e8cd8d 100644
--- a/libavutil/vulkan.c
+++ b/libavutil/vulkan.c
@@ -1066,240 +1066,6 @@ void ff_vk_free_buf(FFVulkanContext *s, FFVkBuffer *buf)
         vk->FreeMemory(s->hwctx->act_dev, buf->mem, s->hwctx->alloc);
 }
 
-static void free_data_buf(void *opaque, uint8_t *data)
-{
-    FFVulkanContext *ctx = opaque;
-    FFVkBuffer *buf = (FFVkBuffer *)data;
-    ff_vk_free_buf(ctx, buf);
-    av_free(data);
-}
-
-static AVBufferRef *alloc_data_buf(void *opaque, size_t size)
-{
-    AVBufferRef *ref;
-    uint8_t *buf = av_mallocz(size);
-    if (!buf)
-        return NULL;
-
-    ref = av_buffer_create(buf, size, free_data_buf, opaque, 0);
-    if (!ref)
-        av_free(buf);
-    return ref;
-}
-
-int ff_vk_get_pooled_buffer(FFVulkanContext *ctx, AVBufferPool **buf_pool,
-                            AVBufferRef **buf, VkBufferUsageFlags usage,
-                            void *create_pNext, size_t size,
-                            VkMemoryPropertyFlagBits mem_props)
-{
-    int err;
-    AVBufferRef *ref;
-    FFVkBuffer *data;
-
-    if (!(*buf_pool)) {
-        *buf_pool = av_buffer_pool_init2(sizeof(FFVkBuffer), ctx,
-                                         alloc_data_buf, NULL);
-        if (!(*buf_pool))
-            return AVERROR(ENOMEM);
-    }
-
-    *buf = ref = av_buffer_pool_get(*buf_pool);
-    if (!ref)
-        return AVERROR(ENOMEM);
-
-    data = (FFVkBuffer *)ref->data;
-    data->stage = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
-    data->access = VK_ACCESS_2_NONE;
-
-    if (data->size >= size)
-        return 0;
-
-    ff_vk_free_buf(ctx, data);
-    memset(data, 0, sizeof(*data));
-
-    av_log(ctx, AV_LOG_DEBUG, "Allocating buffer of %"SIZE_SPECIFIER" bytes for pool %p\n",
-           size, *buf_pool);
-
-    err = ff_vk_create_buf(ctx, data, size,
-                           create_pNext, NULL, usage,
-                           mem_props);
-    if (err < 0) {
-        av_buffer_unref(&ref);
-        return err;
-    }
-
-    if (mem_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
-        err = ff_vk_map_buffer(ctx, data, &data->mapped_mem, 0);
-        if (err < 0) {
-            av_buffer_unref(&ref);
-            return err;
-        }
-    }
-
-    return 0;
-}
-
-int ff_vk_add_push_constant(FFVulkanPipeline *pl, int offset, int size,
-                            VkShaderStageFlagBits stage)
-{
-    VkPushConstantRange *pc;
-
-    pl->push_consts = av_realloc_array(pl->push_consts, sizeof(*pl->push_consts),
-                                       pl->push_consts_num + 1);
-    if (!pl->push_consts)
-        return AVERROR(ENOMEM);
-
-    pc = &pl->push_consts[pl->push_consts_num++];
-    memset(pc, 0, sizeof(*pc));
-
-    pc->stageFlags = stage;
-    pc->offset = offset;
-    pc->size = size;
-
-    return 0;
-}
-
-int ff_vk_init_sampler(FFVulkanContext *s, VkSampler *sampler,
-                       int unnorm_coords, VkFilter filt)
-{
-    VkResult ret;
-    FFVulkanFunctions *vk = &s->vkfn;
-
-    VkSamplerCreateInfo sampler_info = {
-        .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
-        .magFilter = filt,
-        .minFilter = sampler_info.magFilter,
-        .mipmapMode = unnorm_coords ? VK_SAMPLER_MIPMAP_MODE_NEAREST :
-                                      VK_SAMPLER_MIPMAP_MODE_LINEAR,
-        .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
-        .addressModeV = sampler_info.addressModeU,
-        .addressModeW = sampler_info.addressModeU,
-        .anisotropyEnable = VK_FALSE,
-        .compareOp = VK_COMPARE_OP_NEVER,
-        .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
-        .unnormalizedCoordinates = unnorm_coords,
-    };
-
-    ret = vk->CreateSampler(s->hwctx->act_dev, &sampler_info,
-                            s->hwctx->alloc, sampler);
-    if (ret != VK_SUCCESS) {
-        av_log(s, AV_LOG_ERROR, "Unable to init sampler: %s\n",
-               ff_vk_ret2str(ret));
-        return AVERROR_EXTERNAL;
-    }
-
-    return 0;
-}
-
-int ff_vk_mt_is_np_rgb(enum AVPixelFormat pix_fmt)
-{
-    if (pix_fmt == AV_PIX_FMT_ABGR   || pix_fmt == AV_PIX_FMT_BGRA   ||
-        pix_fmt == AV_PIX_FMT_RGBA   || pix_fmt == AV_PIX_FMT_RGB24  ||
-        pix_fmt == AV_PIX_FMT_BGR24  || pix_fmt == AV_PIX_FMT_RGB48  ||
-        pix_fmt == AV_PIX_FMT_RGBA64 || pix_fmt == AV_PIX_FMT_RGB565 ||
-        pix_fmt == AV_PIX_FMT_BGR565 || pix_fmt == AV_PIX_FMT_BGR0   ||
-        pix_fmt == AV_PIX_FMT_0BGR   || pix_fmt == AV_PIX_FMT_RGB0)
-        return 1;
-    return 0;
-}
-
-const char *ff_vk_shader_rep_fmt(enum AVPixelFormat pixfmt)
-{
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pixfmt);
-    const int high = desc->comp[0].depth > 8;
-    return high ? "rgba16f" : "rgba8";
-}
-
-typedef struct ImageViewCtx {
-    VkImageView views[AV_NUM_DATA_POINTERS];
-    int nb_views;
-} ImageViewCtx;
-
-static void destroy_imageviews(void *opaque, uint8_t *data)
-{
-    FFVulkanContext *s = opaque;
-    FFVulkanFunctions *vk = &s->vkfn;
-    ImageViewCtx *iv = (ImageViewCtx *)data;
-
-    for (int i = 0; i < iv->nb_views; i++)
-        vk->DestroyImageView(s->hwctx->act_dev, iv->views[i], s->hwctx->alloc);
-
-    av_free(iv);
-}
-
-int ff_vk_create_imageviews(FFVulkanContext *s, FFVkExecContext *e,
-                            VkImageView views[AV_NUM_DATA_POINTERS],
-                            AVFrame *f)
-{
-    int err;
-    VkResult ret;
-    AVBufferRef *buf;
-    FFVulkanFunctions *vk = &s->vkfn;
-    AVHWFramesContext *hwfc = (AVHWFramesContext *)f->hw_frames_ctx->data;
-    const VkFormat *rep_fmts = av_vkfmt_from_pixfmt(hwfc->sw_format);
-    AVVkFrame *vkf = (AVVkFrame *)f->data[0];
-    const int nb_images = ff_vk_count_images(vkf);
-    const int nb_planes = av_pix_fmt_count_planes(hwfc->sw_format);
-
-    ImageViewCtx *iv = av_mallocz(sizeof(*iv));
-    if (!iv)
-        return AVERROR(ENOMEM);
-
-    for (int i = 0; i < nb_planes; i++) {
-        VkImageAspectFlags plane_aspect[] = { VK_IMAGE_ASPECT_COLOR_BIT,
-                                              VK_IMAGE_ASPECT_PLANE_0_BIT,
-                                              VK_IMAGE_ASPECT_PLANE_1_BIT,
-                                              VK_IMAGE_ASPECT_PLANE_2_BIT, };
-
-        VkImageViewCreateInfo view_create_info = {
-            .sType      = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
-            .pNext      = NULL,
-            .image      = vkf->img[FFMIN(i, nb_images - 1)],
-            .viewType   = VK_IMAGE_VIEW_TYPE_2D,
-            .format     = rep_fmts[i],
-            .components = ff_comp_identity_map,
-            .subresourceRange = {
-                .aspectMask = plane_aspect[(nb_planes != nb_images) +
-                                           i*(nb_planes != nb_images)],
-                .levelCount = 1,
-                .layerCount = 1,
-            },
-        };
-
-        ret = vk->CreateImageView(s->hwctx->act_dev, &view_create_info,
-                                  s->hwctx->alloc, &iv->views[i]);
-        if (ret != VK_SUCCESS) {
-            av_log(s, AV_LOG_ERROR, "Failed to create imageview: %s\n",
-                   ff_vk_ret2str(ret));
-            err = AVERROR_EXTERNAL;
-            goto fail;
-        }
-
-        iv->nb_views++;
-    }
-
-    buf = av_buffer_create((uint8_t *)iv, sizeof(*iv), destroy_imageviews, s, 0);
-    if (!buf) {
-        err = AVERROR(ENOMEM);
-        goto fail;
-    }
-
-    /* Add to queue dependencies */
-    err = ff_vk_exec_add_dep_buf(s, e, &buf, 1, 0);
-    if (err < 0)
-        av_buffer_unref(&buf);
-
-    memcpy(views, iv->views, nb_planes*sizeof(*views));
-
-    return err;
-
-fail:
-    for (int i = 0; i < iv->nb_views; i++)
-        vk->DestroyImageView(s->hwctx->act_dev, iv->views[i], s->hwctx->alloc);
-    av_free(iv);
-    return err;
-}
-
 void ff_vk_frame_barrier(FFVulkanContext *s, FFVkExecContext *e,
                          AVFrame *pic, VkImageMemoryBarrier2 *bar, int *nb_bar,
                          VkPipelineStageFlags src_stage,
@@ -1343,532 +1109,6 @@ void ff_vk_frame_barrier(FFVulkanContext *s, FFVkExecContext *e,
     ff_vk_exec_update_frame(s, e, pic, &bar[*nb_bar - nb_images], NULL);
 }
 
-int ff_vk_shader_init(FFVulkanPipeline *pl, FFVkSPIRVShader *shd, const char *name,
-                      VkShaderStageFlags stage, uint32_t required_subgroup_size)
-{
-    av_bprint_init(&shd->src, 0, AV_BPRINT_SIZE_UNLIMITED);
-
-    shd->shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
-    shd->shader.stage = stage;
-
-    if (required_subgroup_size) {
-        shd->shader.flags |= VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT;
-        shd->shader.pNext = &shd->subgroup_info;
-        shd->subgroup_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO;
-        shd->subgroup_info.requiredSubgroupSize = required_subgroup_size;
-    }
-
-    shd->name = name;
-
-    GLSLF(0, #version %i                                                  ,460);
-    GLSLC(0, #define IS_WITHIN(v1, v2) ((v1.x < v2.x) && (v1.y < v2.y))       );
-    GLSLC(0,                                                                  );
-    GLSLC(0, #extension GL_EXT_buffer_reference : require                     );
-    GLSLC(0, #extension GL_EXT_buffer_reference2 : require                    );
-
-    return 0;
-}
-
-void ff_vk_shader_set_compute_sizes(FFVkSPIRVShader *shd, int x, int y, int z)
-{
-    shd->local_size[0] = x;
-    shd->local_size[1] = y;
-    shd->local_size[2] = z;
-
-    av_bprintf(&shd->src, "layout (local_size_x = %i, "
-               "local_size_y = %i, local_size_z = %i) in;\n\n",
-               shd->local_size[0], shd->local_size[1], shd->local_size[2]);
-}
-
-void ff_vk_shader_print(void *ctx, FFVkSPIRVShader *shd, int prio)
-{
-    int line = 0;
-    const char *p = shd->src.str;
-    const char *start = p;
-    const size_t len = strlen(p);
-
-    AVBPrint buf;
-    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
-
-    for (int i = 0; i < len; i++) {
-        if (p[i] == '\n') {
-            av_bprintf(&buf, "%i\t", ++line);
-            av_bprint_append_data(&buf, start, &p[i] - start + 1);
-            start = &p[i + 1];
-        }
-    }
-
-    av_log(ctx, prio, "Shader %s: \n%s", shd->name, buf.str);
-    av_bprint_finalize(&buf, NULL);
-}
-
-void ff_vk_shader_free(FFVulkanContext *s, FFVkSPIRVShader *shd)
-{
-    FFVulkanFunctions *vk = &s->vkfn;
-    av_bprint_finalize(&shd->src, NULL);
-
-    if (shd->shader.module)
-        vk->DestroyShaderModule(s->hwctx->act_dev, shd->shader.module, s->hwctx->alloc);
-}
-
-int ff_vk_shader_create(FFVulkanContext *s, FFVkSPIRVShader *shd,
-                        uint8_t *spirv, size_t spirv_size, const char *entrypoint)
-{
-    VkResult ret;
-    FFVulkanFunctions *vk = &s->vkfn;
-    VkShaderModuleCreateInfo shader_create;
-
-    shd->shader.pName = entrypoint;
-
-    av_log(s, AV_LOG_VERBOSE, "Shader %s compiled! Size: %zu bytes\n",
-           shd->name, spirv_size);
-
-    shader_create.sType    = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
-    shader_create.pNext    = NULL;
-    shader_create.codeSize = spirv_size;
-    shader_create.flags    = 0;
-    shader_create.pCode    = (void *)spirv;
-
-    ret = vk->CreateShaderModule(s->hwctx->act_dev, &shader_create, NULL,
-                                 &shd->shader.module);
-    if (ret != VK_SUCCESS) {
-        av_log(s, AV_LOG_VERBOSE, "Error creating shader module: %s\n",
-               ff_vk_ret2str(ret));
-        return AVERROR_EXTERNAL;
-    }
-
-    return 0;
-}
-
-static const struct descriptor_props {
-    size_t struct_size; /* Size of the opaque which updates the descriptor */
-    const char *type;
-    int is_uniform;
-    int mem_quali;      /* Can use a memory qualifier */
-    int dim_needed;     /* Must indicate dimension */
-    int buf_content;    /* Must indicate buffer contents */
-} descriptor_props[] = {
-    [VK_DESCRIPTOR_TYPE_SAMPLER]                = { sizeof(VkDescriptorImageInfo),  "sampler",       1, 0, 0, 0, },
-    [VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]          = { sizeof(VkDescriptorImageInfo),  "texture",       1, 0, 1, 0, },
-    [VK_DESCRIPTOR_TYPE_STORAGE_IMAGE]          = { sizeof(VkDescriptorImageInfo),  "image",         1, 1, 1, 0, },
-    [VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT]       = { sizeof(VkDescriptorImageInfo),  "subpassInput",  1, 0, 0, 0, },
-    [VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] = { sizeof(VkDescriptorImageInfo),  "sampler",       1, 0, 1, 0, },
-    [VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]         = { sizeof(VkDescriptorBufferInfo),  NULL,           1, 0, 0, 1, },
-    [VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]         = { sizeof(VkDescriptorBufferInfo), "buffer",        0, 1, 0, 1, },
-    [VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] = { sizeof(VkDescriptorBufferInfo),  NULL,           1, 0, 0, 1, },
-    [VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] = { sizeof(VkDescriptorBufferInfo), "buffer",        0, 1, 0, 1, },
-    [VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]   = { sizeof(VkBufferView),           "samplerBuffer", 1, 0, 0, 0, },
-    [VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]   = { sizeof(VkBufferView),           "imageBuffer",   1, 0, 0, 0, },
-};
-
-int ff_vk_pipeline_descriptor_set_add(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                      FFVkSPIRVShader *shd,
-                                      FFVulkanDescriptorSetBinding *desc, int nb,
-                                      int read_only, int print_to_shader_only)
-{
-    VkResult ret;
-    int has_sampler = 0;
-    FFVulkanFunctions *vk = &s->vkfn;
-    FFVulkanDescriptorSet *set;
-    VkDescriptorSetLayoutCreateInfo desc_create_layout;
-
-    if (print_to_shader_only)
-        goto print;
-
-    /* Actual layout allocated for the pipeline */
-    set = av_realloc_array(pl->desc_set, sizeof(*pl->desc_set),
-                           pl->nb_descriptor_sets + 1);
-    if (!set)
-        return AVERROR(ENOMEM);
-    pl->desc_set = set;
-    set = &set[pl->nb_descriptor_sets];
-    memset(set, 0, sizeof(*set));
-
-    set->binding = av_calloc(nb, sizeof(*set->binding));
-    if (!set->binding)
-        return AVERROR(ENOMEM);
-
-    set->binding_offset = av_calloc(nb, sizeof(*set->binding_offset));
-    if (!set->binding_offset) {
-        av_freep(&set->binding);
-        return AVERROR(ENOMEM);
-    }
-
-    desc_create_layout = (VkDescriptorSetLayoutCreateInfo) {
-        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
-        .bindingCount = nb,
-        .pBindings = set->binding,
-        .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT,
-    };
-
-    for (int i = 0; i < nb; i++) {
-        set->binding[i].binding            = i;
-        set->binding[i].descriptorType     = desc[i].type;
-        set->binding[i].descriptorCount    = FFMAX(desc[i].elems, 1);
-        set->binding[i].stageFlags         = desc[i].stages;
-        set->binding[i].pImmutableSamplers = desc[i].samplers;
-
-        if (desc[i].type == VK_DESCRIPTOR_TYPE_SAMPLER ||
-            desc[i].type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
-            has_sampler |= 1;
-    }
-
-    set->usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT |
-                 VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
-    if (has_sampler)
-        set->usage |= VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;
-
-    ret = vk->CreateDescriptorSetLayout(s->hwctx->act_dev, &desc_create_layout,
-                                        s->hwctx->alloc, &set->layout);
-    if (ret != VK_SUCCESS) {
-        av_log(s, AV_LOG_ERROR, "Unable to init descriptor set layout: %s",
-               ff_vk_ret2str(ret));
-        return AVERROR_EXTERNAL;
-    }
-
-    vk->GetDescriptorSetLayoutSizeEXT(s->hwctx->act_dev, set->layout, &set->layout_size);
-
-    set->aligned_size = FFALIGN(set->layout_size, s->desc_buf_props.descriptorBufferOffsetAlignment);
-
-    for (int i = 0; i < nb; i++)
-        vk->GetDescriptorSetLayoutBindingOffsetEXT(s->hwctx->act_dev, set->layout,
-                                                   i, &set->binding_offset[i]);
-
-    set->read_only = read_only;
-    set->nb_bindings = nb;
-    pl->nb_descriptor_sets++;
-
-print:
-    /* Write shader info */
-    for (int i = 0; i < nb; i++) {
-        const struct descriptor_props *prop = &descriptor_props[desc[i].type];
-        GLSLA("layout (set = %i, binding = %i", pl->nb_descriptor_sets - 1, i);
-
-        if (desc[i].mem_layout)
-            GLSLA(", %s", desc[i].mem_layout);
-        GLSLA(")");
-
-        if (prop->is_uniform)
-            GLSLA(" uniform");
-
-        if (prop->mem_quali && desc[i].mem_quali)
-            GLSLA(" %s", desc[i].mem_quali);
-
-        if (prop->type)
-            GLSLA(" %s", prop->type);
-
-        if (prop->dim_needed)
-            GLSLA("%iD", desc[i].dimensions);
-
-        GLSLA(" %s", desc[i].name);
-
-        if (prop->buf_content)
-            GLSLA(" {\n    %s\n}", desc[i].buf_content);
-        else if (desc[i].elems > 0)
-            GLSLA("[%i]", desc[i].elems);
-
-        GLSLA(";");
-        GLSLA("\n");
-    }
-    GLSLA("\n");
-
-    return 0;
-}
-
-int ff_vk_exec_pipeline_register(FFVulkanContext *s, FFVkExecPool *pool,
-                                 FFVulkanPipeline *pl)
-{
-    int err;
-
-    pl->desc_bind = av_calloc(pl->nb_descriptor_sets, sizeof(*pl->desc_bind));
-    if (!pl->desc_bind)
-        return AVERROR(ENOMEM);
-
-    pl->bound_buffer_indices = av_calloc(pl->nb_descriptor_sets,
-                                         sizeof(*pl->bound_buffer_indices));
-    if (!pl->bound_buffer_indices)
-        return AVERROR(ENOMEM);
-
-    for (int i = 0; i < pl->nb_descriptor_sets; i++) {
-        FFVulkanDescriptorSet *set = &pl->desc_set[i];
-        int nb = set->read_only ? 1 : pool->pool_size;
-
-        err = ff_vk_create_buf(s, &set->buf, set->aligned_size*nb,
-                               NULL, NULL, set->usage,
-                               VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
-                               VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
-        if (err < 0)
-            return err;
-
-        err = ff_vk_map_buffer(s, &set->buf, &set->desc_mem, 0);
-        if (err < 0)
-            return err;
-
-        pl->desc_bind[i] = (VkDescriptorBufferBindingInfoEXT) {
-            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT,
-            .usage = set->usage,
-            .address = set->buf.address,
-        };
-
-        pl->bound_buffer_indices[i] = i;
-    }
-
-    return 0;
-}
-
-static inline void update_set_descriptor(FFVulkanContext *s, FFVkExecContext *e,
-                                         FFVulkanDescriptorSet *set,
-                                         int bind_idx, int array_idx,
-                                         VkDescriptorGetInfoEXT *desc_get_info,
-                                         size_t desc_size)
-{
-    FFVulkanFunctions *vk = &s->vkfn;
-    const size_t exec_offset = set->read_only ? 0 : set->aligned_size*e->idx;
-    void *desc = set->desc_mem +                 /* Base */
-                 exec_offset +                   /* Execution context */
-                 set->binding_offset[bind_idx] + /* Descriptor binding */
-                 array_idx*desc_size;            /* Array position */
-
-    vk->GetDescriptorEXT(s->hwctx->act_dev, desc_get_info, desc_size, desc);
-}
-
-static int vk_set_descriptor_image(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                   FFVkExecContext *e, int set, int bind, int offs,
-                                   VkImageView view, VkImageLayout layout,
-                                   VkSampler sampler)
-{
-    FFVulkanDescriptorSet *desc_set = &pl->desc_set[set];
-    VkDescriptorGetInfoEXT desc_get_info = {
-        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT,
-        .type = desc_set->binding[bind].descriptorType,
-    };
-    VkDescriptorImageInfo desc_img_info = {
-        .imageView = view,
-        .sampler = sampler,
-        .imageLayout = layout,
-    };
-    size_t desc_size;
-
-    switch (desc_get_info.type) {
-    case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
-        desc_get_info.data.pSampledImage = &desc_img_info;
-        desc_size = s->desc_buf_props.sampledImageDescriptorSize;
-        break;
-    case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
-        desc_get_info.data.pStorageImage = &desc_img_info;
-        desc_size = s->desc_buf_props.storageImageDescriptorSize;
-        break;
-    case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
-        desc_get_info.data.pInputAttachmentImage = &desc_img_info;
-        desc_size = s->desc_buf_props.inputAttachmentDescriptorSize;
-        break;
-    case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
-        desc_get_info.data.pCombinedImageSampler = &desc_img_info;
-        desc_size = s->desc_buf_props.combinedImageSamplerDescriptorSize;
-        break;
-    default:
-        av_log(s, AV_LOG_ERROR, "Invalid descriptor type at set %i binding %i: %i!\n",
-               set, bind, desc_get_info.type);
-        return AVERROR(EINVAL);
-        break;
-    };
-
-    update_set_descriptor(s, e, desc_set, bind, offs, &desc_get_info, desc_size);
-
-    return 0;
-}
-
-int ff_vk_set_descriptor_buffer(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                FFVkExecContext *e, int set, int bind, int offs,
-                                VkDeviceAddress addr, VkDeviceSize len, VkFormat fmt)
-{
-    FFVulkanDescriptorSet *desc_set = &pl->desc_set[set];
-    VkDescriptorGetInfoEXT desc_get_info = {
-        .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT,
-        .type = desc_set->binding[bind].descriptorType,
-    };
-    VkDescriptorAddressInfoEXT desc_buf_info = {
-        .address = addr,
-        .range = len,
-        .format = fmt,
-    };
-    size_t desc_size;
-
-    switch (desc_get_info.type) {
-    case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
-        desc_get_info.data.pUniformBuffer = &desc_buf_info;
-        desc_size = s->desc_buf_props.uniformBufferDescriptorSize;
-        break;
-    case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
-        desc_get_info.data.pStorageBuffer = &desc_buf_info;
-        desc_size = s->desc_buf_props.storageBufferDescriptorSize;
-        break;
-    case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
-        desc_get_info.data.pUniformTexelBuffer = &desc_buf_info;
-        desc_size = s->desc_buf_props.uniformTexelBufferDescriptorSize;
-        break;
-    case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
-        desc_get_info.data.pStorageTexelBuffer = &desc_buf_info;
-        desc_size = s->desc_buf_props.storageTexelBufferDescriptorSize;
-        break;
-    default:
-        av_log(s, AV_LOG_ERROR, "Invalid descriptor type at set %i binding %i: %i!\n",
-               set, bind, desc_get_info.type);
-        return AVERROR(EINVAL);
-        break;
-    };
-
-    update_set_descriptor(s, e, desc_set, bind, offs, &desc_get_info, desc_size);
-
-    return 0;
-}
-
-void ff_vk_update_descriptor_img_array(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                       FFVkExecContext *e, AVFrame *f,
-                                       VkImageView *views, int set, int binding,
-                                       VkImageLayout layout, VkSampler sampler)
-{
-    AVHWFramesContext *hwfc = (AVHWFramesContext *)f->hw_frames_ctx->data;
-    const int nb_planes = av_pix_fmt_count_planes(hwfc->sw_format);
-
-    for (int i = 0; i < nb_planes; i++)
-        vk_set_descriptor_image(s, pl, e, set, binding, i,
-                                views[i], layout, sampler);
-}
-
-void ff_vk_update_push_exec(FFVulkanContext *s, FFVkExecContext *e,
-                            FFVulkanPipeline *pl,
-                            VkShaderStageFlagBits stage,
-                            int offset, size_t size, void *src)
-{
-    FFVulkanFunctions *vk = &s->vkfn;
-    vk->CmdPushConstants(e->buf, pl->pipeline_layout,
-                         stage, offset, size, src);
-}
-
-static int init_pipeline_layout(FFVulkanContext *s, FFVulkanPipeline *pl)
-{
-    VkResult ret;
-    FFVulkanFunctions *vk = &s->vkfn;
-    VkPipelineLayoutCreateInfo pipeline_layout_info;
-
-    VkDescriptorSetLayout *desc_layouts = av_malloc(pl->nb_descriptor_sets*
-                                                    sizeof(desc_layouts));
-    if (!desc_layouts)
-        return AVERROR(ENOMEM);
-
-    for (int i = 0; i < pl->nb_descriptor_sets; i++)
-        desc_layouts[i] = pl->desc_set[i].layout;
-
-    /* Finally create the pipeline layout */
-    pipeline_layout_info = (VkPipelineLayoutCreateInfo) {
-        .sType                  = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
-        .pSetLayouts            = desc_layouts,
-        .setLayoutCount         = pl->nb_descriptor_sets,
-        .pushConstantRangeCount = pl->push_consts_num,
-        .pPushConstantRanges    = pl->push_consts,
-    };
-
-    ret = vk->CreatePipelineLayout(s->hwctx->act_dev, &pipeline_layout_info,
-                                   s->hwctx->alloc, &pl->pipeline_layout);
-    av_free(desc_layouts);
-    if (ret != VK_SUCCESS) {
-        av_log(s, AV_LOG_ERROR, "Unable to init pipeline layout: %s\n",
-               ff_vk_ret2str(ret));
-        return AVERROR_EXTERNAL;
-    }
-
-    return 0;
-}
-
-int ff_vk_init_compute_pipeline(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                FFVkSPIRVShader *shd)
-{
-    int err;
-    VkResult ret;
-    FFVulkanFunctions *vk = &s->vkfn;
-
-    VkComputePipelineCreateInfo pipeline_create_info;
-
-    err = init_pipeline_layout(s, pl);
-    if (err < 0)
-        return err;
-
-    pipeline_create_info = (VkComputePipelineCreateInfo) {
-        .sType  = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
-        .flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT,
-        .layout = pl->pipeline_layout,
-        .stage = shd->shader,
-    };
-
-    ret = vk->CreateComputePipelines(s->hwctx->act_dev, VK_NULL_HANDLE, 1,
-                                     &pipeline_create_info,
-                                     s->hwctx->alloc, &pl->pipeline);
-    if (ret != VK_SUCCESS) {
-        av_log(s, AV_LOG_ERROR, "Unable to init compute pipeline: %s\n",
-               ff_vk_ret2str(ret));
-        return AVERROR_EXTERNAL;
-    }
-
-    pl->bind_point = VK_PIPELINE_BIND_POINT_COMPUTE;
-    pl->wg_size[0] = shd->local_size[0];
-    pl->wg_size[1] = shd->local_size[1];
-    pl->wg_size[2] = shd->local_size[2];
-
-    return 0;
-}
-
-void ff_vk_exec_bind_pipeline(FFVulkanContext *s, FFVkExecContext *e,
-                              FFVulkanPipeline *pl)
-{
-    FFVulkanFunctions *vk = &s->vkfn;
-    VkDeviceSize offsets[1024];
-
-    /* Bind pipeline */
-    vk->CmdBindPipeline(e->buf, pl->bind_point, pl->pipeline);
-
-    if (pl->nb_descriptor_sets) {
-        for (int i = 0; i < pl->nb_descriptor_sets; i++)
-            offsets[i] = pl->desc_set[i].read_only ? 0 : pl->desc_set[i].aligned_size*e->idx;
-
-        /* Bind descriptor buffers */
-        vk->CmdBindDescriptorBuffersEXT(e->buf, pl->nb_descriptor_sets, pl->desc_bind);
-        /* Binding offsets */
-        vk->CmdSetDescriptorBufferOffsetsEXT(e->buf, pl->bind_point, pl->pipeline_layout,
-                                             0, pl->nb_descriptor_sets,
-                                             pl->bound_buffer_indices, offsets);
-    }
-}
-
-void ff_vk_pipeline_free(FFVulkanContext *s, FFVulkanPipeline *pl)
-{
-    FFVulkanFunctions *vk = &s->vkfn;
-
-    if (pl->pipeline)
-        vk->DestroyPipeline(s->hwctx->act_dev, pl->pipeline, s->hwctx->alloc);
-    if (pl->pipeline_layout)
-        vk->DestroyPipelineLayout(s->hwctx->act_dev, pl->pipeline_layout,
-                                  s->hwctx->alloc);
-
-    for (int i = 0; i < pl->nb_descriptor_sets; i++) {
-        FFVulkanDescriptorSet *set = &pl->desc_set[i];
-        if (set->buf.mem)
-            ff_vk_unmap_buffer(s, &set->buf, 0);
-        ff_vk_free_buf(s, &set->buf);
-        if (set->layout)
-            vk->DestroyDescriptorSetLayout(s->hwctx->act_dev, set->layout,
-                                           s->hwctx->alloc);
-        av_free(set->binding);
-        av_free(set->binding_offset);
-    }
-
-    av_freep(&pl->desc_set);
-    av_freep(&pl->desc_bind);
-    av_freep(&pl->bound_buffer_indices);
-    av_freep(&pl->push_consts);
-    pl->push_consts_num = 0;
-}
-
 void ff_vk_uninit(FFVulkanContext *s)
 {
     av_freep(&s->query_props);
diff --git a/libavutil/vulkan.h b/libavutil/vulkan.h
index 15d954fcb8..e30f0361c3 100644
--- a/libavutil/vulkan.h
+++ b/libavutil/vulkan.h
@@ -25,43 +25,10 @@
 
 #include "thread.h"
 #include "pixdesc.h"
-#include "bprint.h"
 #include "hwcontext.h"
 #include "vulkan_functions.h"
 #include "hwcontext_vulkan.h"
 
-/* GLSL management macros */
-#define INDENT(N) INDENT_##N
-#define INDENT_0
-#define INDENT_1 INDENT_0 "    "
-#define INDENT_2 INDENT_1 INDENT_1
-#define INDENT_3 INDENT_2 INDENT_1
-#define INDENT_4 INDENT_3 INDENT_1
-#define INDENT_5 INDENT_4 INDENT_1
-#define INDENT_6 INDENT_5 INDENT_1
-#define C(N, S)          INDENT(N) #S "\n"
-
-#define GLSLC(N, S)                     \
-    do {                                \
-        av_bprintf(&shd->src, C(N, S)); \
-    } while (0)
-
-#define GLSLA(...)                          \
-    do {                                    \
-        av_bprintf(&shd->src, __VA_ARGS__); \
-    } while (0)
-
-#define GLSLF(N, S, ...)                             \
-    do {                                             \
-        av_bprintf(&shd->src, C(N, S), __VA_ARGS__); \
-    } while (0)
-
-#define GLSLD(D)                                        \
-    do {                                                \
-        av_bprintf(&shd->src, "\n");                    \
-        av_bprint_append_data(&shd->src, D, strlen(D)); \
-        av_bprintf(&shd->src, "\n");                    \
-    } while (0)
 
 /* Helper, pretty much every Vulkan return value needs to be checked */
 #define RET(x)                                                                 \
@@ -72,26 +39,6 @@
 
 #define DUP_SAMPLER(x) { x, x, x, x }
 
-typedef struct FFVkSPIRVShader {
-    const char *name;                       /* Name for id/debugging purposes */
-    AVBPrint src;
-    int local_size[3];                      /* Compute shader workgroup sizes */
-    VkPipelineShaderStageCreateInfo shader;
-    VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_info;
-} FFVkSPIRVShader;
-
-typedef struct FFVulkanDescriptorSetBinding {
-    const char         *name;
-    VkDescriptorType    type;
-    const char         *mem_layout;  /* Storage images (rgba8, etc.) and buffers (std430, etc.) */
-    const char         *mem_quali;   /* readonly, writeonly, etc. */
-    const char         *buf_content; /* For buffers */
-    uint32_t            dimensions;  /* Needed for e.g. sampler%iD */
-    uint32_t            elems;       /* 0 - scalar, 1 or more - vector */
-    VkShaderStageFlags  stages;
-    VkSampler           samplers[4]; /* Sampler to use for all elems */
-} FFVulkanDescriptorSetBinding;
-
 typedef struct FFVkBuffer {
     VkBuffer buf;
     VkDeviceMemory mem;
@@ -112,43 +59,6 @@ typedef struct FFVkQueueFamilyCtx {
     int nb_queues;
 } FFVkQueueFamilyCtx;
 
-typedef struct FFVulkanDescriptorSet {
-    VkDescriptorSetLayout  layout;
-    FFVkBuffer             buf;
-    uint8_t               *desc_mem;
-    VkDeviceSize           layout_size;
-    VkDeviceSize           aligned_size; /* descriptorBufferOffsetAlignment */
-    VkDeviceSize           total_size; /* Once registered to an exec context */
-    VkBufferUsageFlags     usage;
-
-    VkDescriptorSetLayoutBinding *binding;
-    VkDeviceSize *binding_offset;
-    int nb_bindings;
-
-    int read_only;
-} FFVulkanDescriptorSet;
-
-typedef struct FFVulkanPipeline {
-    VkPipelineBindPoint bind_point;
-
-    /* Contexts */
-    VkPipelineLayout pipeline_layout;
-    VkPipeline       pipeline;
-
-    /* Push consts */
-    VkPushConstantRange *push_consts;
-    int push_consts_num;
-
-    /* Workgroup */
-    int wg_size[3];
-
-    /* Descriptors */
-    FFVulkanDescriptorSet *desc_set;
-    VkDescriptorBufferBindingInfoEXT *desc_bind;
-    uint32_t *bound_buffer_indices;
-    int nb_descriptor_sets;
-} FFVulkanPipeline;
-
 typedef struct FFVkExecContext {
     uint32_t idx;
     const struct FFVkExecPool *parent;
@@ -297,16 +207,6 @@ extern const VkComponentMapping ff_comp_identity_map;
  */
 const char *ff_vk_ret2str(VkResult res);
 
-/**
- * Returns 1 if pixfmt is a usable RGB format.
- */
-int ff_vk_mt_is_np_rgb(enum AVPixelFormat pix_fmt);
-
-/**
- * Returns the format to use for images in shaders.
- */
-const char *ff_vk_shader_rep_fmt(enum AVPixelFormat pixfmt);
-
 /**
  * Loads props/mprops/driver_props
  */
@@ -371,13 +271,6 @@ int ff_vk_exec_mirror_sem_value(FFVulkanContext *s, FFVkExecContext *e,
                                 AVFrame *f);
 void ff_vk_exec_discard_deps(FFVulkanContext *s, FFVkExecContext *e);
 
-/**
- * Create an imageview and add it as a dependency to an execution.
- */
-int ff_vk_create_imageviews(FFVulkanContext *s, FFVkExecContext *e,
-                            VkImageView views[AV_NUM_DATA_POINTERS],
-                            AVFrame *f);
-
 void ff_vk_frame_barrier(FFVulkanContext *s, FFVkExecContext *e,
                          AVFrame *pic, VkImageMemoryBarrier2 *bar, int *nb_bar,
                          VkPipelineStageFlags src_stage,
@@ -421,75 +314,6 @@ static inline int ff_vk_unmap_buffer(FFVulkanContext *s, FFVkBuffer *buf, int fl
 
 void ff_vk_free_buf(FFVulkanContext *s, FFVkBuffer *buf);
 
-/** Initialize a pool and create AVBufferRefs containing FFVkBuffer.
- * Threadsafe to use. Buffers are automatically mapped on creation if
- * VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT is set in mem_props. Users should
- * synchronize access themselvesd. Mainly meant for device-local buffers. */
-int ff_vk_get_pooled_buffer(FFVulkanContext *ctx, AVBufferPool **buf_pool,
-                            AVBufferRef **buf, VkBufferUsageFlags usage,
-                            void *create_pNext, size_t size,
-                            VkMemoryPropertyFlagBits mem_props);
-
-/**
- * Create a sampler.
- */
-int ff_vk_init_sampler(FFVulkanContext *s, VkSampler *sampler,
-                       int unnorm_coords, VkFilter filt);
-
-/**
- * Shader management.
- */
-int ff_vk_shader_init(FFVulkanPipeline *pl, FFVkSPIRVShader *shd, const char *name,
-                      VkShaderStageFlags stage, uint32_t required_subgroup_size);
-void ff_vk_shader_set_compute_sizes(FFVkSPIRVShader *shd, int x, int y, int z);
-void ff_vk_shader_print(void *ctx, FFVkSPIRVShader *shd, int prio);
-int ff_vk_shader_create(FFVulkanContext *s, FFVkSPIRVShader *shd,
-                        uint8_t *spirv, size_t spirv_size, const char *entrypoint);
-void ff_vk_shader_free(FFVulkanContext *s, FFVkSPIRVShader *shd);
-
-/**
- * Add/update push constants for execution.
- */
-int ff_vk_add_push_constant(FFVulkanPipeline *pl, int offset, int size,
-                            VkShaderStageFlagBits stage);
-void ff_vk_update_push_exec(FFVulkanContext *s, FFVkExecContext *e,
-                            FFVulkanPipeline *pl,
-                            VkShaderStageFlagBits stage,
-                            int offset, size_t size, void *src);
-
-/**
- * Add descriptor to a pipeline. Must be called before pipeline init.
- */
-int ff_vk_pipeline_descriptor_set_add(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                      FFVkSPIRVShader *shd,
-                                      FFVulkanDescriptorSetBinding *desc, int nb,
-                                      int read_only, int print_to_shader_only);
-
-/* Initialize/free a pipeline. */
-int ff_vk_init_compute_pipeline(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                FFVkSPIRVShader *shd);
-void ff_vk_pipeline_free(FFVulkanContext *s, FFVulkanPipeline *pl);
-
-/**
- * Register a pipeline with an exec pool.
- * Pool may be NULL if all descriptor sets are read-only.
- */
-int ff_vk_exec_pipeline_register(FFVulkanContext *s, FFVkExecPool *pool,
-                                 FFVulkanPipeline *pl);
-
-/* Bind pipeline */
-void ff_vk_exec_bind_pipeline(FFVulkanContext *s, FFVkExecContext *e,
-                              FFVulkanPipeline *pl);
-
-int ff_vk_set_descriptor_buffer(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                FFVkExecContext *e, int set, int bind, int offs,
-                                VkDeviceAddress addr, VkDeviceSize len, VkFormat fmt);
-
-void ff_vk_update_descriptor_img_array(FFVulkanContext *s, FFVulkanPipeline *pl,
-                                       FFVkExecContext *e, AVFrame *f,
-                                       VkImageView *views, int set, int binding,
-                                       VkImageLayout layout, VkSampler sampler);
-
 /**
  * Frees main context.
  */
