Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PR] Convert and refactor FFv1 to compile-time SPIR-V (PR #21769)
@ 2026-02-16  8:12 Lynne via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: Lynne via ffmpeg-devel @ 2026-02-16  8:12 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Lynne

PR #21769 opened by Lynne
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21769
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21769.patch

This series of commits completes porting all Vulkan compute codecs to compile-time SPIR-V and does prepwork for adding support for 16-bit/32-bit float encode/decode.

Also fixes a few Vulkan framework issues, and drops support for descriptor buffers.


>From f01811d0e2ef3c91fb239cefbc5e6ebf94d32171 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 08:57:46 +0100
Subject: [PATCH 01/58] Makefile: specify GLSL version via command line
 arguments

GLSL strictly mandates the version must be the very first non-comment
statement, which results in issues when #including for templating.
---
 configure                                     | 4 ++--
 libavcodec/vulkan/dpx_copy.comp.glsl          | 1 -
 libavcodec/vulkan/dpx_unpack.comp.glsl        | 1 -
 libavcodec/vulkan/prores_idct.comp.glsl       | 1 -
 libavcodec/vulkan/prores_raw_decode.comp.glsl | 1 -
 libavcodec/vulkan/prores_raw_idct.comp.glsl   | 1 -
 libavcodec/vulkan/prores_vld.comp.glsl        | 1 -
 libavfilter/vulkan/avgblur.comp.glsl          | 1 -
 libavfilter/vulkan/bwdif.comp.glsl            | 1 -
 libavfilter/vulkan/debayer.comp.glsl          | 1 -
 10 files changed, 2 insertions(+), 11 deletions(-)

diff --git a/configure b/configure
index 8a397598cd..b2579916d2 100755
--- a/configure
+++ b/configure
@@ -7716,7 +7716,7 @@ probe_glslc(){
     if test_cmd $glslc_probe -v; then
         # glslang/glslangValidator
         glslc=$glslc_probe
-        glslcflags="-V --target-env spirv1.6"
+        glslcflags="-V --target-env spirv1.6 --glsl-version 460"
         glslc_opt_speed=""
         glslc_opt_size="-Os"
         glslc_opt_none="-Od"
@@ -7725,7 +7725,7 @@ probe_glslc(){
     elif test_cmd $glslc_probe --version; then
         # glslc
         glslc=$glslc_probe
-        glslcflags="--target-env=vulkan1.4 --target-spv=spv1.6"
+        glslcflags="--target-env=vulkan1.4 --target-spv=spv1.6 -std=460"
         glslc_opt_speed="-O"
         glslc_opt_size="-Os"
         glslc_opt_none="-O0"
diff --git a/libavcodec/vulkan/dpx_copy.comp.glsl b/libavcodec/vulkan/dpx_copy.comp.glsl
index d9651dbfdf..f106195182 100644
--- a/libavcodec/vulkan/dpx_copy.comp.glsl
+++ b/libavcodec/vulkan/dpx_copy.comp.glsl
@@ -18,7 +18,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
diff --git a/libavcodec/vulkan/dpx_unpack.comp.glsl b/libavcodec/vulkan/dpx_unpack.comp.glsl
index 3850cbf3e9..badc48a52a 100644
--- a/libavcodec/vulkan/dpx_unpack.comp.glsl
+++ b/libavcodec/vulkan/dpx_unpack.comp.glsl
@@ -18,7 +18,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
diff --git a/libavcodec/vulkan/prores_idct.comp.glsl b/libavcodec/vulkan/prores_idct.comp.glsl
index 800a93db66..1da19d34e9 100644
--- a/libavcodec/vulkan/prores_idct.comp.glsl
+++ b/libavcodec/vulkan/prores_idct.comp.glsl
@@ -16,7 +16,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
diff --git a/libavcodec/vulkan/prores_raw_decode.comp.glsl b/libavcodec/vulkan/prores_raw_decode.comp.glsl
index 9c4def2872..e4b00b1440 100644
--- a/libavcodec/vulkan/prores_raw_decode.comp.glsl
+++ b/libavcodec/vulkan/prores_raw_decode.comp.glsl
@@ -20,7 +20,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
diff --git a/libavcodec/vulkan/prores_raw_idct.comp.glsl b/libavcodec/vulkan/prores_raw_idct.comp.glsl
index 44efc9adb6..5e3a2fa67c 100644
--- a/libavcodec/vulkan/prores_raw_idct.comp.glsl
+++ b/libavcodec/vulkan/prores_raw_idct.comp.glsl
@@ -20,7 +20,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
diff --git a/libavcodec/vulkan/prores_vld.comp.glsl b/libavcodec/vulkan/prores_vld.comp.glsl
index 85b4dbdd61..9020d46fa1 100644
--- a/libavcodec/vulkan/prores_vld.comp.glsl
+++ b/libavcodec/vulkan/prores_vld.comp.glsl
@@ -16,7 +16,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
diff --git a/libavfilter/vulkan/avgblur.comp.glsl b/libavfilter/vulkan/avgblur.comp.glsl
index b53ec4092c..4cfd4f433d 100644
--- a/libavfilter/vulkan/avgblur.comp.glsl
+++ b/libavfilter/vulkan/avgblur.comp.glsl
@@ -18,7 +18,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 
 #extension GL_EXT_shader_image_load_formatted : require
diff --git a/libavfilter/vulkan/bwdif.comp.glsl b/libavfilter/vulkan/bwdif.comp.glsl
index fb18af3915..d38109a7fc 100644
--- a/libavfilter/vulkan/bwdif.comp.glsl
+++ b/libavfilter/vulkan/bwdif.comp.glsl
@@ -18,7 +18,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 
 #extension GL_EXT_shader_image_load_formatted : require
diff --git a/libavfilter/vulkan/debayer.comp.glsl b/libavfilter/vulkan/debayer.comp.glsl
index ccf4996c49..0a4e22de99 100644
--- a/libavfilter/vulkan/debayer.comp.glsl
+++ b/libavfilter/vulkan/debayer.comp.glsl
@@ -19,7 +19,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#version 460
 #pragma shader_stage(compute)
 
 #extension GL_EXT_shader_image_load_formatted : require
-- 
2.52.0


>From 665a1e3d80ec2e25a926e2e493c87d9b96d98bb8 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 14:36:15 +0100
Subject: [PATCH 02/58] vulkan: don't set FFVulkanDescriptorSetBinding.name
 when not necessary

It just bloats the code with unused strings.
---
 libavcodec/vulkan_dpx.c         | 30 +++++++++-----------
 libavcodec/vulkan_prores.c      | 50 +++++++++++++++------------------
 libavcodec/vulkan_prores_raw.c  |  8 ++----
 libavfilter/vf_avgblur_vulkan.c | 13 +++------
 libavfilter/vf_bwdif_vulkan.c   | 42 ++++++++++++---------------
 5 files changed, 60 insertions(+), 83 deletions(-)

diff --git a/libavcodec/vulkan_dpx.c b/libavcodec/vulkan_dpx.c
index 17f91c6ce4..a69e93108a 100644
--- a/libavcodec/vulkan_dpx.c
+++ b/libavcodec/vulkan_dpx.c
@@ -240,26 +240,22 @@ static int init_shader(AVCodecContext *avctx, FFVulkanContext *s,
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 
     const FFVulkanDescriptorSetBinding desc_set[] = {
-        {
-            .name       = "dst",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
-            .elems      = av_pix_fmt_count_planes(dec_frames_ctx->sw_format),
+        { /* dst */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = av_pix_fmt_count_planes(dec_frames_ctx->sw_format),
         },
-        {
-            .name        = "data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* data_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "data_buf16",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* data_buf16 */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "data_buf32",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* data_buf32 */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
     ff_vk_shader_add_descriptor_set(s, shd, desc_set, 2 + (2*!unpack), 0, 0);
diff --git a/libavcodec/vulkan_prores.c b/libavcodec/vulkan_prores.c
index c7451c20fd..69a9a080b9 100644
--- a/libavcodec/vulkan_prores.c
+++ b/libavcodec/vulkan_prores.c
@@ -373,22 +373,19 @@ static int init_decode_shader(AVCodecContext *avctx, FFVulkanContext *s,
     ff_vk_shader_add_push_const(shd, 0, sizeof(ProresVkParameters),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 
-    FFVulkanDescriptorSetBinding desc_set[] = {
-        {
-            .name        = "slice_offsets_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* slice_offsets_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "quant_idx_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* quant_idx_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name       = "dst",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
-            .elems      = av_pix_fmt_count_planes(dec_frames_ctx->sw_format),
+        { /* dst */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = av_pix_fmt_count_planes(dec_frames_ctx->sw_format),
         },
     };
     ff_vk_shader_add_descriptor_set(s, shd, desc_set, 3, 0, 0);
@@ -432,22 +429,19 @@ static int init_idct_shader(AVCodecContext *avctx, FFVulkanContext *s,
     ff_vk_shader_add_push_const(shd, 0, sizeof(ProresVkParameters),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 
-    FFVulkanDescriptorSetBinding desc_set[] = {
-        {
-            .name        = "quant_idx_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* quant_idx_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "qmat_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* qmat_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name       = "dst",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
-            .elems      = av_pix_fmt_count_planes(dec_frames_ctx->sw_format),
+        { /* dst */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = av_pix_fmt_count_planes(dec_frames_ctx->sw_format),
         },
     };
     RET(ff_vk_shader_add_descriptor_set(s, shd, desc_set, 3, 0, 0));
diff --git a/libavcodec/vulkan_prores_raw.c b/libavcodec/vulkan_prores_raw.c
index cd46900047..22d70bcb3f 100644
--- a/libavcodec/vulkan_prores_raw.c
+++ b/libavcodec/vulkan_prores_raw.c
@@ -284,14 +284,12 @@ fail:
 static int add_desc(AVCodecContext *avctx, FFVulkanContext *s,
                     FFVulkanShader *shd)
 {
-    FFVulkanDescriptorSetBinding desc_set[] = {
-        {
-            .name   = "dst",
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* dst */
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name   = "frame_data_buf",
+        { /* frame_data_buf */
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
diff --git a/libavfilter/vf_avgblur_vulkan.c b/libavfilter/vf_avgblur_vulkan.c
index 847390d064..db0b1b8612 100644
--- a/libavfilter/vf_avgblur_vulkan.c
+++ b/libavfilter/vf_avgblur_vulkan.c
@@ -54,8 +54,6 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in)
     FFVulkanContext *vkctx = &s->vkctx;
     const int planes = av_pix_fmt_count_planes(s->vkctx.output_format);
 
-    FFVulkanDescriptorSetBinding *desc;
-
     s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0);
     if (!s->qf) {
         av_log(ctx, AV_LOG_ERROR, "Device has no compute queues\n");
@@ -71,22 +69,19 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in)
     ff_vk_shader_add_push_const(&s->shd, 0, sizeof(s->opts),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 
-    desc = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name   = "input_img",
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* input_img */
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
             .elems  = planes,
         },
-        {
-            .name   = "output_img",
+        { /* output_img */
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
             .elems  = planes,
         },
     };
-
-    ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0);
+    ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 2, 0, 0);
 
     RET(ff_vk_shader_link(vkctx, &s->shd,
                           ff_avgblur_comp_spv_data,
diff --git a/libavfilter/vf_bwdif_vulkan.c b/libavfilter/vf_bwdif_vulkan.c
index c6fb6abe9c..5fefb3396d 100644
--- a/libavfilter/vf_bwdif_vulkan.c
+++ b/libavfilter/vf_bwdif_vulkan.c
@@ -51,7 +51,6 @@ static av_cold int init_filter(AVFilterContext *ctx)
     BWDIFVulkanContext *s = ctx->priv;
     FFVulkanContext *vkctx = &s->vkctx;
     const int planes = av_pix_fmt_count_planes(s->vkctx.output_format);
-    FFVulkanDescriptorSetBinding *desc;
 
     s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0);
     if (!s->qf) {
@@ -65,34 +64,29 @@ static av_cold int init_filter(AVFilterContext *ctx)
     ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
                       (uint32_t [3]) { 1, 64, planes }, 0);
 
-    desc = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name       = "prev",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .elems      = planes,
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* prev */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = planes,
         },
-        {
-            .name       = "cur",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .elems      = planes,
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* cur */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = planes,
         },
-        {
-            .name       = "next",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .elems      = planes,
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* next */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = planes,
         },
-        {
-            .name       = "dst",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .elems      = planes,
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* dst */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = planes,
         },
     };
-
-    ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 4, 0, 0);
+    ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 4, 0, 0);
 
     ff_vk_shader_add_push_const(&s->shd, 0, sizeof(BWDIFParameters),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
-- 
2.52.0


>From 63baa8c564573bd9c8ce1a1caa4344de95e26a09 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 08:59:03 +0100
Subject: [PATCH 03/58] vulkan_prores_raw: fix a single statement's indentation

Annoying.
---
 libavcodec/vulkan_prores_raw.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vulkan_prores_raw.c b/libavcodec/vulkan_prores_raw.c
index 22d70bcb3f..c62c631b33 100644
--- a/libavcodec/vulkan_prores_raw.c
+++ b/libavcodec/vulkan_prores_raw.c
@@ -306,8 +306,8 @@ static int init_decode_shader(AVCodecContext *avctx, FFVulkanContext *s,
 
     ff_vk_shader_add_push_const(shd, 0, sizeof(DecodePushData) - 64,
                                 VK_SHADER_STAGE_COMPUTE_BIT);
-   ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
-                     (uint32_t []) { 1, 4, 1 }, 0);
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
+                      (uint32_t []) { 1, 4, 1 }, 0);
 
     add_desc(avctx, s, shd);
 
-- 
2.52.0


>From 2fc627211cf0d0751c62411fbb33928f2529d22b Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 11:23:07 +0100
Subject: [PATCH 04/58] hwcontext_vulkan: disable shader object when debugging

---
 libavutil/hwcontext_vulkan.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 123ae4363c..6c4e005411 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -931,7 +931,8 @@ static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
             ((debug_mode == FF_VULKAN_DEBUG_VALIDATE) ||
              (debug_mode == FF_VULKAN_DEBUG_PRINTF) ||
              (debug_mode == FF_VULKAN_DEBUG_PRACTICES)) &&
-            !strcmp(tstr, VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME)) {
+            (!strcmp(tstr, VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME) ||
+             !strcmp(tstr, VK_EXT_SHADER_OBJECT_EXTENSION_NAME))) {
             continue;
         }
 
-- 
2.52.0


>From 1fc26cb49c121cbdfcea38b9d7ebc76106e3aeee Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Thu, 5 Feb 2026 16:14:49 +0100
Subject: [PATCH 05/58] hwcontext_vulkan: enable
 VK_KHR_shader_relaxed_extended_instruction by default

We compile our shaders with debug information by default.
---
 libavutil/hwcontext_vulkan.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 6c4e005411..683859fbdc 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -695,6 +695,9 @@ static const VulkanOptExtension optional_device_exts[] = {
     { VK_KHR_SHADER_SUBGROUP_ROTATE_EXTENSION_NAME,           FF_VK_EXT_SUBGROUP_ROTATE        },
     { VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME,                  FF_VK_EXT_HOST_IMAGE_COPY        },
     { VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, FF_VK_EXT_EXPLICIT_MEM_LAYOUT    },
+#ifdef VK_KHR_shader_relaxed_extended_instruction
+    { VK_KHR_SHADER_RELAXED_EXTENDED_INSTRUCTION_EXTENSION_NAME, FF_VK_EXT_RELAXED_EXTENDED_INSTR },
+#endif
 #ifdef VK_EXT_shader_long_vector
     { VK_EXT_SHADER_LONG_VECTOR_EXTENSION_NAME,               FF_VK_EXT_LONG_VECTOR            },
 #endif
@@ -984,10 +987,7 @@ static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
                 break;
             }
         }
-        if (found) {
-            av_log(ctx, AV_LOG_VERBOSE, "Using %s extension %s\n", mod, tstr);
-            ADD_VAL_TO_LIST(extension_names, extensions_found, tstr);
-        } else {
+        if (!found) {
             av_log(ctx, AV_LOG_ERROR, "Debug_printf/profile enabled, but extension \"%s\" not found!\n",
                    tstr);
             err = AVERROR(EINVAL);
-- 
2.52.0


>From 4ab6955416c614add8538124d38e9cd18e4c6852 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 8 Feb 2026 05:56:51 +0100
Subject: [PATCH 06/58] hwcontext_vulkan: zero-pad optional_instance_exts

The layer settings extension was enabled since the list was blank,
and some compilers complained about that, and it should have been
always supported on all platforms everywhere.

Unfortunately, some platforms LIE, claim they support it and yet
they error out that the extension is missing.

Juse zero pad the array.
---
 libavutil/hwcontext_vulkan.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 683859fbdc..6c266c4d3a 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -677,10 +677,10 @@ typedef struct VulkanOptExtension {
 } VulkanOptExtension;
 
 static const VulkanOptExtension optional_instance_exts[] = {
-    { VK_EXT_LAYER_SETTINGS_EXTENSION_NAME,                   FF_VK_EXT_NO_FLAG                },
 #ifdef __APPLE__
     { VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,          FF_VK_EXT_NO_FLAG                },
 #endif
+    { 0 },
 };
 
 static const VulkanOptExtension optional_device_exts[] = {
@@ -746,14 +746,14 @@ static const VulkanOptExtension optional_device_exts[] = {
 const char **av_vk_get_optional_instance_extensions(int *count)
 {
     const char **exts = av_malloc_array(sizeof(*exts),
-                                        FF_ARRAY_ELEMS(optional_instance_exts));
+                                        FF_ARRAY_ELEMS(optional_instance_exts) - 1);
     if (!exts)
         return NULL;
 
-    for (int i = 0; i < FF_ARRAY_ELEMS(optional_instance_exts); i++)
+    for (int i = 0; i < FF_ARRAY_ELEMS(optional_instance_exts) - 1; i++)
         exts[i] = optional_instance_exts[i].name;
 
-    *count = FF_ARRAY_ELEMS(optional_instance_exts);
+    *count = FF_ARRAY_ELEMS(optional_instance_exts) - 1;
     return exts;
 }
 
@@ -881,7 +881,7 @@ static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
     if (!dev) {
         mod = "instance";
         optional_exts = optional_instance_exts;
-        optional_exts_num = FF_ARRAY_ELEMS(optional_instance_exts);
+        optional_exts_num = FF_ARRAY_ELEMS(optional_instance_exts) - 1;
         user_exts = av_dict_get(opts, "instance_extensions", NULL, 0);
         if (user_exts) {
             user_exts_str = av_strdup(user_exts->value);
-- 
2.52.0


>From dedc0d4883465206995c8d95a64172cf7d380e12 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 08:36:35 +0100
Subject: [PATCH 07/58] hwcontext_vulkan: drop debug=3 (profile)

The mode is useless with pregenerated SPIR-V.
---
 libavutil/hwcontext_vulkan.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 6c266c4d3a..08ba35db05 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -853,8 +853,6 @@ enum FFVulkanDebugMode {
     FF_VULKAN_DEBUG_PRINTF = 2,
     /* Enables extra printouts */
     FF_VULKAN_DEBUG_PRACTICES = 3,
-    /* Disables validation but keeps shader debug info and optimizations */
-    FF_VULKAN_DEBUG_PROFILE = 4,
 
     FF_VULKAN_DEBUG_NB,
 };
@@ -977,8 +975,7 @@ static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
     }
 
 #ifdef VK_KHR_shader_relaxed_extended_instruction
-    if (((debug_mode == FF_VULKAN_DEBUG_PRINTF) ||
-         (debug_mode == FF_VULKAN_DEBUG_PROFILE)) && dev) {
+    if ((debug_mode == FF_VULKAN_DEBUG_PRINTF) && dev) {
         tstr = VK_KHR_SHADER_RELAXED_EXTENDED_INSTRUCTION_EXTENSION_NAME;
         found = 0;
         for (int j = 0; j < sup_ext_count; j++) {
@@ -1074,9 +1071,7 @@ static int check_layers(AVHWDeviceContext *ctx, AVDictionary *opts,
 
     /* Check for any properly supported validation layer */
     if (debug_opt) {
-        if (!strcmp(debug_opt->value, "profile")) {
-            mode = FF_VULKAN_DEBUG_PROFILE;
-        } else if (!strcmp(debug_opt->value, "printf")) {
+        if (!strcmp(debug_opt->value, "printf")) {
             mode = FF_VULKAN_DEBUG_PRINTF;
         } else if (!strcmp(debug_opt->value, "validate")) {
             mode = FF_VULKAN_DEBUG_VALIDATE;
@@ -1116,8 +1111,6 @@ static int check_layers(AVHWDeviceContext *ctx, AVDictionary *opts,
             err = AVERROR(ENOTSUP);
             goto end;
         }
-    } else if (mode == FF_VULKAN_DEBUG_PROFILE) {
-        *debug_mode = mode;
     }
 
     /* Process any custom layers enabled */
-- 
2.52.0


>From c70046027ad680450ffd2962725b87e63d735ab6 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sat, 14 Feb 2026 14:42:09 +0100
Subject: [PATCH 08/58] hwcontext_vulkan: correctly set stride for host image
 uploads

Cursed.
---
 libavutil/hwcontext_vulkan.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 08ba35db05..e35ece9757 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -4522,10 +4522,10 @@ static int vulkan_transfer_host(AVHWFramesContext *hwfc, AVFrame *hwf,
         for (int i = 0; i < planes; i++) {
             int img_idx = FFMIN(i, (nb_images - 1));
             uint32_t p_w, p_h;
-            get_plane_wh(&p_w, &p_h, swf->format,
-                         swf->linesize[i]/desc->comp[i].step, swf->height, i);
+            get_plane_wh(&p_w, &p_h, swf->format, swf->width, swf->height, i);
 
             region_info.pHostPointer = swf->data[i];
+            region_info.memoryRowLength = swf->linesize[i] / desc->comp[i].step;
             region_info.imageSubresource.aspectMask = ff_vk_aspect_flag(hwf, i);
             region_info.imageExtent = (VkExtent3D){ p_w, p_h, 1 };
             copy_info.dstImage = hwf_vk->img[img_idx];
-- 
2.52.0


>From a01c2861c51b99c330854f0fc54dd4b4f3c5cef5 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Fri, 13 Feb 2026 15:11:33 +0100
Subject: [PATCH 09/58] vulkan/dpx: bounds check with image sizes

Prevents out of bounds accesses.
---
 libavcodec/vulkan/dpx_copy.comp.glsl   | 5 ++++-
 libavcodec/vulkan/dpx_unpack.comp.glsl | 7 ++++---
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/libavcodec/vulkan/dpx_copy.comp.glsl b/libavcodec/vulkan/dpx_copy.comp.glsl
index f106195182..f83b03d6a0 100644
--- a/libavcodec/vulkan/dpx_copy.comp.glsl
+++ b/libavcodec/vulkan/dpx_copy.comp.glsl
@@ -69,9 +69,12 @@ uint read_data(uint off)
 void main(void)
 {
     ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+    ivec2 size = imageSize(dst[0]);
+    if (any(greaterThanEqual(pos, size)))
+        return;
 
     uint linesize;
-    linesize = align(imageSize(dst[0]).x*bits_per_comp*nb_comp, 32);
+    linesize = align(size.x*bits_per_comp*nb_comp, 32);
 
     uint offs = pos.y*linesize + pos.x*nb_comp*bits_per_comp;
     offs /= bits_per_comp;
diff --git a/libavcodec/vulkan/dpx_unpack.comp.glsl b/libavcodec/vulkan/dpx_unpack.comp.glsl
index badc48a52a..ed6959a904 100644
--- a/libavcodec/vulkan/dpx_unpack.comp.glsl
+++ b/libavcodec/vulkan/dpx_unpack.comp.glsl
@@ -90,14 +90,15 @@ i16vec4 parse_packed_in_32(ivec2 pos, int stride)
 void main(void)
 {
     ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
-    if (any(greaterThanEqual(pos, imageSize(dst[0]))))
+    ivec2 size = imageSize(dst[0]);
+    if (any(greaterThanEqual(pos, size)))
         return;
 
     i16vec4 p;
     if (packed_10bit)
-        p = parse_packed10_in_32(pos, imageSize(dst[0]).x);
+        p = parse_packed10_in_32(pos, size.x);
     else
-        p = parse_packed_in_32(pos, imageSize(dst[0]).x);
+        p = parse_packed_in_32(pos, size.x);
 
     if (nb_images == 1) {
         imageStore(dst[0], pos, p);
-- 
2.52.0


>From b927f4a289fcc0e9ef954f47e5705800ea6a7d44 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 15:56:41 +0100
Subject: [PATCH 10/58] vulkan/common: add debug shorthand

---
 libavcodec/vulkan/common.comp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libavcodec/vulkan/common.comp b/libavcodec/vulkan/common.comp
index 1ec9ae7e7c..578ec17c42 100644
--- a/libavcodec/vulkan/common.comp
+++ b/libavcodec/vulkan/common.comp
@@ -25,6 +25,11 @@
 
 layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in;
 
+#ifdef DEBUG
+#extension GL_EXT_debug_printf : require
+#define printf debugPrintfEXT
+#endif
+
 #extension GL_EXT_shader_explicit_arithmetic_types : require
 #extension GL_EXT_shader_explicit_arithmetic_types_int8 : require
 #extension GL_EXT_shader_explicit_arithmetic_types_int16 : require
-- 
2.52.0


>From 005ee09c3b1dde2143d5edb0fe1732e5aaa0d522 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 11:41:16 +0100
Subject: [PATCH 11/58] vulkan_ffv1: convert to compile-time SPIR-V generation

---
 configure                                     |   2 +-
 libavcodec/ffv1_vulkan.h                      |  16 +
 libavcodec/vulkan/Makefile                    |  11 +-
 libavcodec/vulkan/ffv1_common.glsl            | 252 +++++++
 .../{ffv1_dec.comp => ffv1_dec.comp.glsl}     | 150 ++---
 libavcodec/vulkan/ffv1_dec_golomb.comp.glsl   |  27 +
 libavcodec/vulkan/ffv1_dec_reset.comp.glsl    |  63 ++
 .../vulkan/ffv1_dec_reset_golomb.comp.glsl    |  27 +
 libavcodec/vulkan/ffv1_dec_rgb.comp.glsl      |  30 +
 .../vulkan/ffv1_dec_rgb_golomb.comp.glsl      |  27 +
 ...ec_setup.comp => ffv1_dec_setup.comp.glsl} |  36 +-
 libavcodec/vulkan/rangecoder.comp             |  14 +-
 libavcodec/vulkan_ffv1.c                      | 629 ++++++------------
 13 files changed, 757 insertions(+), 527 deletions(-)
 create mode 100644 libavcodec/vulkan/ffv1_common.glsl
 rename libavcodec/vulkan/{ffv1_dec.comp => ffv1_dec.comp.glsl} (75%)
 create mode 100644 libavcodec/vulkan/ffv1_dec_golomb.comp.glsl
 create mode 100644 libavcodec/vulkan/ffv1_dec_reset.comp.glsl
 create mode 100644 libavcodec/vulkan/ffv1_dec_reset_golomb.comp.glsl
 create mode 100644 libavcodec/vulkan/ffv1_dec_rgb.comp.glsl
 create mode 100644 libavcodec/vulkan/ffv1_dec_rgb_golomb.comp.glsl
 rename libavcodec/vulkan/{ffv1_dec_setup.comp => ffv1_dec_setup.comp.glsl} (85%)

diff --git a/configure b/configure
index b2579916d2..988a46835f 100755
--- a/configure
+++ b/configure
@@ -3340,7 +3340,7 @@ av1_vulkan_hwaccel_deps="vulkan"
 av1_vulkan_hwaccel_select="av1_decoder"
 dpx_vulkan_hwaccel_deps="vulkan spirv_compiler"
 dpx_vulkan_hwaccel_select="dpx_decoder"
-ffv1_vulkan_hwaccel_deps="vulkan spirv_library"
+ffv1_vulkan_hwaccel_deps="vulkan spirv_compiler"
 ffv1_vulkan_hwaccel_select="ffv1_decoder"
 h263_vaapi_hwaccel_deps="vaapi"
 h263_vaapi_hwaccel_select="h263_decoder"
diff --git a/libavcodec/ffv1_vulkan.h b/libavcodec/ffv1_vulkan.h
index 372478f4b7..a11c61ae4b 100644
--- a/libavcodec/ffv1_vulkan.h
+++ b/libavcodec/ffv1_vulkan.h
@@ -36,6 +36,22 @@ int ff_ffv1_vk_init_quant_table_data(FFVulkanContext *s,
 int ff_ffv1_vk_init_crc_table_data(FFVulkanContext *s,
                                    FFVkBuffer *vkb, FFV1Context *f);
 
+typedef struct FFv1ShaderParams {
+    VkDeviceAddress slice_data;
+    VkDeviceAddress slice_state;
+
+    uint32_t extend_lookup[8];
+    uint16_t context_count[8];
+
+    int fmt_lut[4];
+    uint16_t img_size[2];
+
+    uint32_t plane_state_size;
+    uint32_t key_frame;
+    uint32_t crcref;
+    int micro_version;
+} FFv1ShaderParams;
+
 typedef struct FFv1VkRCTParameters {
     int fmt_lut[4];
     int offset;
diff --git a/libavcodec/vulkan/Makefile b/libavcodec/vulkan/Makefile
index 860475d960..a9ff44c52d 100644
--- a/libavcodec/vulkan/Makefile
+++ b/libavcodec/vulkan/Makefile
@@ -7,10 +7,13 @@ OBJS-$(CONFIG_FFV1_VULKAN_ENCODER)  +=  vulkan/common.o \
 					vulkan/ffv1_enc_setup.o vulkan/ffv1_enc.o \
 					vulkan/ffv1_rct_search.o
 
-OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL)  +=  vulkan/common.o \
-					vulkan/rangecoder.o vulkan/ffv1_vlc.o \
-					vulkan/ffv1_common.o vulkan/ffv1_reset.o \
-					vulkan/ffv1_dec_setup.o vulkan/ffv1_dec.o
+OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL) += vulkan/ffv1_dec_setup.comp.spv.o \
+                                      vulkan/ffv1_dec_reset.comp.spv.o \
+                                      vulkan/ffv1_dec_reset_golomb.comp.spv.o \
+                                      vulkan/ffv1_dec.comp.spv.o \
+                                      vulkan/ffv1_dec_golomb.comp.spv.o \
+                                      vulkan/ffv1_dec_rgb.comp.spv.o \
+                                      vulkan/ffv1_dec_rgb_golomb.comp.spv.o
 
 OBJS-$(CONFIG_PRORES_RAW_VULKAN_HWACCEL) += vulkan/prores_raw_decode.comp.spv.o \
                                             vulkan/prores_raw_idct.comp.spv.o
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
new file mode 100644
index 0000000000..625e615054
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -0,0 +1,252 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2024 Lynne <dev@lynne.ee>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef VULKAN_FFV1_COMMON_H
+#define VULKAN_FFV1_COMMON_H
+
+#include "rangecoder.comp"
+#ifdef GOLOMB
+#include "ffv1_vlc.comp"
+#endif
+
+#define MAX_QUANT_TABLES 8
+#define MAX_CONTEXT_INPUTS 5
+#define MAX_QUANT_TABLE_SIZE 256
+#define MAX_QUANT_TABLE_MASK (MAX_QUANT_TABLE_SIZE - 1)
+
+layout (constant_id =  0) const int rgb_linecache = 2;
+layout (constant_id =  1) const bool has_crc = false;
+layout (constant_id =  2) const int version = 0;
+layout (constant_id =  3) const int quant_table_count = 0;
+layout (constant_id =  4) const bool has_extend_lookup = false;
+
+layout (constant_id =  5) const int rct_offset = 0;
+layout (constant_id =  6) const int colorspace = 0;
+layout (constant_id =  7) const bool transparency = false;
+layout (constant_id =  8) const bool planar_rgb = false;
+layout (constant_id =  9) const int codec_planes = 0;
+layout (constant_id = 10) const int color_planes = 0;
+layout (constant_id = 11) const int planes = 0;
+layout (constant_id = 12) const int bits_per_raw_sample = 0;
+
+layout (constant_id = 13) const int chroma_shift_x = 0;
+layout (constant_id = 14) const int chroma_shift_y = 0;
+const ivec2 chroma_shift = ivec2(chroma_shift_x, chroma_shift_y);
+
+layout (push_constant, scalar) uniform pushConstants {
+    u8buf slice_data;
+    u8buf slice_state;
+
+    bool extend_lookup[MAX_QUANT_TABLES];
+    uint16_t context_count[MAX_QUANT_TABLES];
+
+    ivec4 fmt_lut;
+    u16vec2 img_size;
+
+    uint plane_state_size;
+    bool key_frame;
+    uint32_t crcref;
+    int micro_version;
+};
+
+#define TYPE int32_t
+#define VTYPE2 i32vec2
+#define VTYPE3 i32vec3
+
+struct SliceContext {
+    RangeCoder c;
+
+#ifdef DECODE
+    GetBitContext gb;
+#else
+    PutBitContext pb; /* 8*8 bytes */
+#endif
+
+    ivec2 slice_dim;
+    ivec2 slice_pos;
+    ivec2 slice_rct_coef;
+    u8vec3 quant_table_idx;
+
+    uint slice_coding_mode;
+    bool slice_reset_contexts;
+};
+
+layout (set = 1, binding = 0) buffer slice_ctx_buf {
+    SliceContext slice_ctx[];
+};
+
+uint slice_coord(uint width, uint sx, uint num_h_slices, uint chroma_shift)
+{
+    uint mpw = 1 << chroma_shift;
+    uint awidth = align(width, mpw);
+
+    if ((version < 4) || ((version == 4) && (micro_version < 3)))
+        return width * sx / num_h_slices;
+
+    sx = (2 * awidth * sx + num_h_slices * mpw) / (2 * num_h_slices * mpw) * mpw;
+    if (sx == awidth)
+        sx = width;
+
+    return sx;
+}
+
+#if defined(ENCODE) || defined(DECODE)
+
+layout (set = 0, binding = 1, scalar) readonly uniform quant_buf {
+    int16_t quant_table[MAX_QUANT_TABLES]
+                       [MAX_CONTEXT_INPUTS]
+                       [MAX_QUANT_TABLE_SIZE];
+};
+
+/* -1, { -1, 0 } */
+int predict(int L, ivec2 top)
+{
+    return mid_pred(L, L + top[1] - top[0], top[1]);
+}
+
+/* { -2, -1 }, { -1, 0, 1 }, 0 */
+int get_context(VTYPE2 cur_l, VTYPE3 top_l, TYPE top2, uint8_t quant_table_idx)
+{
+    const int LT = top_l[0]; /* -1 */
+    const int T  = top_l[1]; /*  0 */
+    const int RT = top_l[2]; /*  1 */
+    const int L  = cur_l[1]; /* -1 */
+
+    int base = quant_table[quant_table_idx][0][(L - LT) & MAX_QUANT_TABLE_MASK] +
+               quant_table[quant_table_idx][1][(LT - T) & MAX_QUANT_TABLE_MASK] +
+               quant_table[quant_table_idx][2][(T - RT) & MAX_QUANT_TABLE_MASK];
+
+    if ((quant_table[quant_table_idx][3][127] == 0) &&
+        (quant_table[quant_table_idx][4][127] == 0))
+        return base;
+
+    const int TT = top2;     /* -2 */
+    const int LL = cur_l[0]; /* -2 */
+    return base +
+           quant_table[quant_table_idx][3][(LL - L) & MAX_QUANT_TABLE_MASK] +
+           quant_table[quant_table_idx][4][(TT - T) & MAX_QUANT_TABLE_MASK];
+}
+
+const uint32_t log2_run[41] = {
+     0,  0,  0,  0,  1,  1,  1,  1,
+     2,  2,  2,  2,  3,  3,  3,  3,
+     4,  4,  5,  5,  6,  6,  7,  7,
+     8,  9, 10, 11, 12, 13, 14, 15,
+    16, 17, 18, 19, 20, 21, 22, 23,
+    24,
+};
+
+#ifdef RGB
+#define RGB_LBUF (rgb_linecache - 1)
+#define LADDR(p) (ivec2((p).x, ((p).y & RGB_LBUF)))
+
+ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
+               int comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
+{
+    const ivec2 yoff_border1 = expectEXT(off.x == 0, false) ? off + ivec2(1, -1) : off;
+
+    /* Thanks to the same coincidence as below, we can skip checking if off == 0, 1 */
+    VTYPE3 top  = VTYPE3(TYPE(imageLoad(pred, sp + LADDR(yoff_border1 + ivec2(-1, -1)))[comp]),
+                         TYPE(imageLoad(pred, sp + LADDR(off + ivec2(0, -1)))[comp]),
+                         TYPE(imageLoad(pred, sp + LADDR(off + ivec2(min(1, sw - off.x - 1), -1)))[comp]));
+
+    /* Normally, we'd need to check if off != ivec2(0, 0) here, since otherwise, we must
+     * return zero. However, ivec2(-1,  0) + ivec2(1, -1) == ivec2(0, -1), e.g. previous
+     * row, 0 offset, same slice, which is zero since we zero out the buffer for RGB */
+    TYPE cur = TYPE(imageLoad(pred, sp + LADDR(yoff_border1 + ivec2(-1,  0)))[comp]);
+
+    int base = quant_table[quant_table_idx][0][(cur    - top[0]) & MAX_QUANT_TABLE_MASK] +
+               quant_table[quant_table_idx][1][(top[0] - top[1]) & MAX_QUANT_TABLE_MASK] +
+               quant_table[quant_table_idx][2][(top[1] - top[2]) & MAX_QUANT_TABLE_MASK];
+
+    if (has_extend_lookup && extend_lookup) {
+        TYPE cur2 = TYPE(0);
+        if (expectEXT(off.x > 0, true)) {
+            const ivec2 yoff_border2 = expectEXT(off.x == 1, false) ? ivec2(-1, -1) : ivec2(-2, 0);
+            cur2 = TYPE(imageLoad(pred, sp + LADDR(off + yoff_border2))[comp]);
+        }
+        base += quant_table[quant_table_idx][3][(cur2 - cur) & MAX_QUANT_TABLE_MASK];
+
+        /* top-2 became current upon swap when rgb_linecache == 2 */
+        ivec2 top2_off = off;
+        if (rgb_linecache != 2)
+            top2_off += ivec2(0, -2);
+
+        TYPE top2 = TYPE(imageLoad(pred, sp + LADDR(top2_off))[comp]);
+        base += quant_table[quant_table_idx][4][(top2 - top[1]) & MAX_QUANT_TABLE_MASK];
+    }
+
+    /* context, prediction */
+    return ivec2(base, predict(cur, VTYPE2(top)));
+}
+
+#else
+
+#define LADDR(p) (p)
+
+ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
+               int comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
+{
+    const ivec2 yoff_border1 = off.x == 0 ? ivec2(1, -1) : ivec2(0, 0);
+    sp += off;
+
+    VTYPE3 top  = VTYPE3(TYPE(0),
+                         TYPE(0),
+                         TYPE(0));
+    if (off.y > 0 && off != ivec2(0, 1))
+        top[0] = TYPE(imageLoad(pred, sp + ivec2(-1, -1) + yoff_border1)[comp]);
+    if (off.y > 0) {
+        top[1] = TYPE(imageLoad(pred, sp + ivec2(0, -1))[comp]);
+        top[2] = TYPE(imageLoad(pred, sp + ivec2(min(1, sw - off.x - 1), -1))[comp]);
+    }
+
+    TYPE cur = TYPE(0);
+    if (off != ivec2(0, 0))
+        cur = TYPE(imageLoad(pred, sp + ivec2(-1,  0) + yoff_border1)[comp]);
+
+    int base = quant_table[quant_table_idx][0][(cur - top[0]) & MAX_QUANT_TABLE_MASK] +
+               quant_table[quant_table_idx][1][(top[0] - top[1]) & MAX_QUANT_TABLE_MASK] +
+               quant_table[quant_table_idx][2][(top[1] - top[2]) & MAX_QUANT_TABLE_MASK];
+
+    if (has_extend_lookup && extend_lookup) {
+        TYPE cur2 = TYPE(0);
+        if (off.x > 0 && off != ivec2(1, 0)) {
+            const ivec2 yoff_border2 = off.x == 1 ? ivec2(1, -1) : ivec2(0, 0);
+            cur2 = TYPE(imageLoad(pred, sp + ivec2(-2,  0) + yoff_border2)[comp]);
+        }
+        base += quant_table[quant_table_idx][3][(cur2 - cur) & MAX_QUANT_TABLE_MASK];
+
+        TYPE top2 = TYPE(0);
+        if (off.y > 1)
+            top2 = TYPE(imageLoad(pred, sp + ivec2(0, -2))[comp]);
+        base += quant_table[quant_table_idx][4][(top2 - top[1]) & MAX_QUANT_TABLE_MASK];
+    }
+
+    /* context, prediction */
+    return ivec2(base, predict(cur, VTYPE2(top)));
+}
+
+#endif /* RGB */
+
+#endif /* ENCODE || DECODE */
+
+#endif /* VULKAN_FFV1_COMMON_H */
diff --git a/libavcodec/vulkan/ffv1_dec.comp b/libavcodec/vulkan/ffv1_dec.comp.glsl
similarity index 75%
rename from libavcodec/vulkan/ffv1_dec.comp
rename to libavcodec/vulkan/ffv1_dec.comp.glsl
index 22a961a6df..0d7dec48a3 100644
--- a/libavcodec/vulkan/ffv1_dec.comp
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -20,14 +20,23 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef GOLOMB
-#ifdef CACHED_SYMBOL_READER
-shared uint8_t state[CONTEXT_SIZE];
-#define READ(c, off) get_rac_direct(c, state[off])
-#else
-#define READ(c, off) get_rac(c, uint64_t(slice_state) + (state_off + off))
-#endif
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
 
+#define DECODE
+#include "common.comp"
+#include "ffv1_common.glsl"
+
+layout (set = 1, binding = 1, scalar) readonly buffer slice_offsets_buf {
+    u32vec2 slice_offsets[];
+};
+layout (set = 1, binding = 2, scalar) writeonly buffer slice_status_buf {
+    uint32_t slice_status[];
+};
+layout (set = 1, binding = 3) uniform uimage2D dec[];
+
+#ifndef GOLOMB
+#define READ(c, off) get_rac(c, uint64_t(slice_state) + (state_off + off))
 int get_isymbol(inout RangeCoder c, uint state_off)
 {
     if (READ(c, 0))
@@ -56,11 +65,6 @@ int get_isymbol(inout RangeCoder c, uint state_off)
 
 void decode_line_pcm(inout SliceContext sc, ivec2 sp, int w, int y, int p, int bits)
 {
-#ifdef CACHED_SYMBOL_READER
-    if (gl_LocalInvocationID.x > 0)
-        return;
-#endif
-
 #ifndef RGB
     if (p > 0 && p < 3) {
         w = ceil_rshift(w, chroma_shift.x);
@@ -79,7 +83,7 @@ void decode_line_pcm(inout SliceContext sc, ivec2 sp, int w, int y, int p, int b
 
 void decode_line(inout SliceContext sc, ivec2 sp, int w,
                  int y, int p, int bits, uint state_off,
-                 uint8_t quant_table_idx, const int run_index)
+                 uint8_t quant_table_idx, int run_index)
 {
 #ifndef RGB
     if (p > 0 && p < 3) {
@@ -90,34 +94,28 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
 
     for (int x = 0; x < w; x++) {
         ivec2 pr = get_pred(dec[p], sp, ivec2(x, y), 0, w,
-                            quant_table_idx, extend_lookup[quant_table_idx] > 0);
+                            quant_table_idx, extend_lookup[quant_table_idx]);
 
         uint context_off = state_off + CONTEXT_SIZE*abs(pr[0]);
-#ifdef CACHED_SYMBOL_READER
-        u8buf sb = u8buf(uint64_t(slice_state) + context_off + gl_LocalInvocationID.x);
-        state[gl_LocalInvocationID.x] = sb.v;
-        barrier();
-        if (gl_LocalInvocationID.x == 0) {
 
-#endif
+        int diff = get_isymbol(sc.c, context_off);
+        if (pr[0] < 0)
+            diff = -diff;
 
-            int diff = get_isymbol(sc.c, context_off);
-            if (pr[0] < 0)
-                diff = -diff;
-
-            uint v = zero_extend(pr[1] + diff, bits);
-            imageStore(dec[p], sp + LADDR(ivec2(x, y)), uvec4(v));
-
-#ifdef CACHED_SYMBOL_READER
-        }
-
-        barrier();
-        sb.v = state[gl_LocalInvocationID.x];
-#endif
+        uint v = zero_extend(pr[1] + diff, bits);
+        imageStore(dec[p], sp + LADDR(ivec2(x, y)), uvec4(v));
     }
 }
+#else
+void golomb_init(inout SliceContext sc)
+{
+    if (version == 3 && micro_version > 1 || version > 3)
+        get_rac_internal(sc.c, sc.c.range * 129 >> 8);
 
-#else /* GOLOMB */
+    uint64_t ac_byte_count = sc.c.bytestream - sc.c.bytestream_start - 1;
+    init_get_bits(sc.gb, u8buf(sc.c.bytestream_start + ac_byte_count),
+                  int(sc.c.bytestream_end - sc.c.bytestream_start - ac_byte_count));
+}
 
 void decode_line(inout SliceContext sc, ivec2 sp, int w,
                  int y, int p, int bits, uint state_off,
@@ -137,7 +135,7 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
         ivec2 pos = sp + ivec2(x, y);
         int diff;
         ivec2 pr = get_pred(dec[p], sp, ivec2(x, y), 0, w,
-                            quant_table_idx, extend_lookup[quant_table_idx] > 0);
+                            quant_table_idx, extend_lookup[quant_table_idx]);
 
         uint context_off = state_off + VLC_STATE_SIZE*abs(pr[0]);
         VlcState sb = VlcState(uint64_t(slice_state) + context_off);
@@ -209,7 +207,7 @@ void writeout_rgb(in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct)
         pix.r = int(imageLoad(dec[2], lpos)[0]);
         pix.g = int(imageLoad(dec[0], lpos)[0]);
         pix.b = int(imageLoad(dec[1], lpos)[0]);
-        if (transparency != 0)
+        if (transparency)
             pix.a = int(imageLoad(dec[3], lpos)[0]);
 
         if (expectEXT(apply_rct, true))
@@ -219,7 +217,7 @@ void writeout_rgb(in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct)
                         pix[fmt_lut[2]], pix[fmt_lut[3]]);
 
         imageStore(dst[0], pos, pix);
-        if (planar_rgb != 0) {
+        if (planar_rgb) {
             for (int i = 1; i < color_planes; i++)
                 imageStore(dst[i], pos, ivec4(pix[i]));
         }
@@ -232,71 +230,73 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
     int w = sc.slice_dim.x;
     ivec2 sp = sc.slice_pos;
 
-#ifndef RGB
     int bits = bits_per_raw_sample;
-#else
-    int bits = 9;
+#ifdef RGB
+    bits = 9;
     if (bits != 8 || sc.slice_coding_mode != 0)
         bits = bits_per_raw_sample + int(sc.slice_coding_mode != 1);
 
-    sp.y = int(gl_WorkGroupID.y)*RGB_LINECACHE;
+    sp.y = int(gl_WorkGroupID.y)*rgb_linecache;
 #endif
 
-    /* PCM coding */
 #ifndef GOLOMB
+    /* PCM coding */
     if (sc.slice_coding_mode == 1) {
-#ifndef RGB
-        for (int p = 0; p < planes; p++) {
-            int h = sc.slice_dim.y;
-            if (p > 0 && p < 3)
-                h = ceil_rshift(h, chroma_shift.y);
-
-            for (int y = 0; y < h; y++)
-                decode_line_pcm(sc, sp, w, y, p, bits);
-        }
-#else
+#ifdef RGB
         for (int y = 0; y < sc.slice_dim.y; y++) {
             for (int p = 0; p < color_planes; p++)
                 decode_line_pcm(sc, sp, w, y, p, bits);
 
             writeout_rgb(sc, sp, w, y, false);
         }
-#endif
-    } else
-
-    /* Arithmetic coding */
-#endif
-    {
-        u8vec4 quant_table_idx = sc.quant_table_idx.xyyz;
-        u32vec4 slice_state_off = (slice_idx*codec_planes + uvec4(0, 1, 1, 2))*plane_state_size;
-
-#ifndef RGB
+#else
         for (int p = 0; p < planes; p++) {
             int h = sc.slice_dim.y;
             if (p > 0 && p < 3)
                 h = ceil_rshift(h, chroma_shift.y);
 
-            int run_index = 0;
             for (int y = 0; y < h; y++)
-                decode_line(sc, sp, w, y, p, bits,
-                            slice_state_off[p], quant_table_idx[p], run_index);
-        }
-#else
-        int run_index = 0;
-        for (int y = 0; y < sc.slice_dim.y; y++) {
-            for (int p = 0; p < color_planes; p++)
-                decode_line(sc, sp, w, y, p, bits,
-                            slice_state_off[p], quant_table_idx[p], run_index);
-
-            writeout_rgb(sc, sp, w, y, true);
+                decode_line_pcm(sc, sp, w, y, p, bits);
         }
 #endif
+        return;
     }
+#endif
+
+    u8vec4 quant_table_idx = sc.quant_table_idx.xyyz;
+    u32vec4 slice_state_off = (slice_idx*codec_planes +
+                               uvec4(0, 1, 1, 2))*plane_state_size;
+
+#ifdef GOLOMB
+    golomb_init(sc);
+#endif
+
+#ifdef RGB
+    int run_index = 0;
+    for (int y = 0; y < sc.slice_dim.y; y++) {
+        for (int p = 0; p < color_planes; p++)
+            decode_line(sc, sp, w, y, p, bits,
+                        slice_state_off[p], quant_table_idx[p], run_index);
+
+        writeout_rgb(sc, sp, w, y, true);
+    }
+#else
+    for (int p = 0; p < planes; p++) {
+        int h = sc.slice_dim.y;
+        if (p > 0 && p < 3)
+            h = ceil_rshift(h, chroma_shift.y);
+
+        int run_index = 0;
+        for (int y = 0; y < h; y++)
+            decode_line(sc, sp, w, y, p, bits,
+                        slice_state_off[p], quant_table_idx[p], run_index);
+    }
+#endif
 }
 
 void main(void)
 {
-    const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+    uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
     decode_slice(slice_ctx[slice_idx], slice_idx);
 
     uint32_t status = corrupt ? uint32_t(corrupt) : overread;
diff --git a/libavcodec/vulkan/ffv1_dec_golomb.comp.glsl b/libavcodec/vulkan/ffv1_dec_golomb.comp.glsl
new file mode 100644
index 0000000000..4de62a4888
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_dec_golomb.comp.glsl
@@ -0,0 +1,27 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define GOLOMB
+#include "ffv1_dec.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_dec_reset.comp.glsl b/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
new file mode 100644
index 0000000000..e157923d0a
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
@@ -0,0 +1,63 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2024 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#include "common.comp"
+#include "ffv1_common.glsl"
+
+void main(void)
+{
+    const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+
+    if (!key_frame && !slice_ctx[slice_idx].slice_reset_contexts)
+        return;
+
+    const uint8_t qidx = slice_ctx[slice_idx].quant_table_idx[gl_WorkGroupID.z];
+    uint contexts = context_count[qidx];
+    uint64_t slice_state_off = uint64_t(slice_state) +
+                               slice_idx*plane_state_size*codec_planes;
+
+#ifdef GOLOMB
+    uint64_t start = slice_state_off +
+                     (gl_WorkGroupID.z*(plane_state_size/VLC_STATE_SIZE) +
+                      gl_LocalInvocationID.x)*VLC_STATE_SIZE;
+    for (uint x = gl_LocalInvocationID.x; x < contexts; x += gl_WorkGroupSize.x) {
+        VlcState sb = VlcState(start);
+        sb.drift     =  int16_t(0);
+        sb.error_sum = uint16_t(4);
+        sb.bias      =   int8_t(0);
+        sb.count     =  uint8_t(1);
+        start += gl_WorkGroupSize.x*VLC_STATE_SIZE;
+    }
+#else
+    uint64_t start = slice_state_off +
+                     gl_WorkGroupID.z*plane_state_size +
+                     (gl_LocalInvocationID.x << 2 /* dwords */); /* Bytes */
+    uint count_total = contexts*(CONTEXT_SIZE /* bytes */ >> 2 /* dwords */);
+    for (uint x = gl_LocalInvocationID.x; x < count_total; x += gl_WorkGroupSize.x) {
+        u32buf(start).v = 0x80808080;
+        start += gl_WorkGroupSize.x*(CONTEXT_SIZE >> 3 /* 1/8th of context */);
+    }
+#endif
+}
diff --git a/libavcodec/vulkan/ffv1_dec_reset_golomb.comp.glsl b/libavcodec/vulkan/ffv1_dec_reset_golomb.comp.glsl
new file mode 100644
index 0000000000..8d7ff27230
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_dec_reset_golomb.comp.glsl
@@ -0,0 +1,27 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define GOLOMB
+#include "ffv1_dec_reset.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_dec_rgb.comp.glsl b/libavcodec/vulkan/ffv1_dec_rgb.comp.glsl
new file mode 100644
index 0000000000..fe0d6957df
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_dec_rgb.comp.glsl
@@ -0,0 +1,30 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+#extension GL_EXT_shader_image_load_formatted : require
+
+layout (set = 1, binding = 4) writeonly uniform uimage2D dst[];
+
+#define RGB
+#include "ffv1_dec.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_dec_rgb_golomb.comp.glsl b/libavcodec/vulkan/ffv1_dec_rgb_golomb.comp.glsl
new file mode 100644
index 0000000000..8b9aadbd59
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_dec_rgb_golomb.comp.glsl
@@ -0,0 +1,27 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define GOLOMB
+#include "ffv1_dec_rgb.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_dec_setup.comp b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
similarity index 85%
rename from libavcodec/vulkan/ffv1_dec_setup.comp
rename to libavcodec/vulkan/ffv1_dec_setup.comp.glsl
index fd297cb70a..4607a8105a 100644
--- a/libavcodec/vulkan/ffv1_dec_setup.comp
+++ b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
@@ -20,6 +20,23 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#include "common.comp"
+#include "ffv1_common.glsl"
+
+layout (set = 0, binding = 1, scalar) uniform crc_ieee_buf {
+    uint32_t crc_ieee[256];
+};
+
+layout (set = 1, binding = 1, scalar) readonly buffer slice_offsets_buf {
+    u32vec2 slice_offsets[];
+};
+layout (set = 1, binding = 2, scalar) writeonly buffer slice_status_buf {
+    uint32_t slice_status[];
+};
+
 uint8_t setup_state[CONTEXT_SIZE];
 
 uint get_usymbol(inout RangeCoder c)
@@ -98,21 +115,9 @@ bool decode_slice_header(inout SliceContext sc)
     return false;
 }
 
-void golomb_init(inout SliceContext sc)
-{
-    if (version == 3 && micro_version > 1 || version > 3) {
-        setup_state[0] = uint8_t(129);
-        get_rac_direct(sc.c, setup_state[0]);
-    }
-
-    uint64_t ac_byte_count = sc.c.bytestream - sc.c.bytestream_start - 1;
-    init_get_bits(sc.gb, u8buf(sc.c.bytestream_start + ac_byte_count),
-                  int(sc.c.bytestream_end - sc.c.bytestream_start - ac_byte_count));
-}
-
 void main(void)
 {
-    const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+    uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
 
     u8buf bs = u8buf(slice_data + slice_offsets[slice_idx].x);
     uint32_t slice_size = slice_offsets[slice_idx].y;
@@ -125,10 +130,7 @@ void main(void)
 
     decode_slice_header(slice_ctx[slice_idx]);
 
-    if (golomb == 1)
-        golomb_init(slice_ctx[slice_idx]);
-
-    if (ec != 0 && check_crc != 0) {
+    if (has_crc) {
         uint32_t crc = crcref;
         for (int i = 0; i < slice_size; i++)
             crc = crc_ieee[(crc & 0xFF) ^ uint32_t(bs[i].v)] ^ (crc >> 8);
diff --git a/libavcodec/vulkan/rangecoder.comp b/libavcodec/vulkan/rangecoder.comp
index 98ff743b2e..7dffaf6c70 100644
--- a/libavcodec/vulkan/rangecoder.comp
+++ b/libavcodec/vulkan/rangecoder.comp
@@ -20,6 +20,14 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#ifndef CONTEXT_SIZE
+#define CONTEXT_SIZE 32
+
+layout (set = 0, binding = 0, scalar) uniform rangecoder_buf {
+    uint8_t zero_one_state[512];
+};
+#endif
+
 struct RangeCoder {
     uint64_t bytestream_start;
     uint64_t bytestream;
@@ -42,8 +50,6 @@ void rac_init(out RangeCoder r, u8buf data, uint buf_size)
     r.outstanding_byte = uint8_t(0xFF);
 }
 
-#if !defined(DECODE)
-
 #ifdef FULL_RENORM
 /* Full renorm version that can handle outstanding_byte == 0xFF */
 void renorm_encoder(inout RangeCoder c)
@@ -178,8 +184,6 @@ uint32_t rac_terminate(inout RangeCoder c)
     return uint32_t(uint64_t(c.bytestream) - uint64_t(c.bytestream_start));
 }
 
-#else
-
 /* Decoder */
 uint overread = 0;
 bool corrupt = false;
@@ -243,5 +247,3 @@ bool get_rac_equi(inout RangeCoder c)
 {
     return get_rac_internal(c, c.range >> 1);
 }
-
-#endif
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 7766d67511..044d95ceed 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -23,18 +23,30 @@
 
 #include "ffv1.h"
 #include "ffv1_vulkan.h"
-#include "libavutil/vulkan_spirv.h"
 #include "libavutil/mem.h"
 
 #define RGB_LINECACHE 2
 
-extern const char *ff_source_common_comp;
-extern const char *ff_source_rangecoder_comp;
-extern const char *ff_source_ffv1_vlc_comp;
-extern const char *ff_source_ffv1_common_comp;
-extern const char *ff_source_ffv1_dec_setup_comp;
-extern const char *ff_source_ffv1_reset_comp;
-extern const char *ff_source_ffv1_dec_comp;
+extern const unsigned char ff_ffv1_dec_setup_comp_spv_data[];
+extern const unsigned int ff_ffv1_dec_setup_comp_spv_len;
+
+extern const unsigned char ff_ffv1_dec_reset_comp_spv_data[];
+extern const unsigned int ff_ffv1_dec_reset_comp_spv_len;
+
+extern const unsigned char ff_ffv1_dec_reset_golomb_comp_spv_data[];
+extern const unsigned int ff_ffv1_dec_reset_golomb_comp_spv_len;
+
+extern const unsigned char ff_ffv1_dec_comp_spv_data[];
+extern const unsigned int ff_ffv1_dec_comp_spv_len;
+
+extern const unsigned char ff_ffv1_dec_rgb_comp_spv_data[];
+extern const unsigned int ff_ffv1_dec_rgb_comp_spv_len;
+
+extern const unsigned char ff_ffv1_dec_golomb_comp_spv_data[];
+extern const unsigned int ff_ffv1_dec_golomb_comp_spv_len;
+
+extern const unsigned char ff_ffv1_dec_rgb_golomb_comp_spv_data[];
+extern const unsigned int ff_ffv1_dec_rgb_golomb_comp_spv_len;
 
 const FFVulkanDecodeDescriptor ff_vk_dec_ffv1_desc = {
     .codec_id         = AV_CODEC_ID_FFV1,
@@ -64,80 +76,15 @@ typedef struct FFv1VulkanDecodeContext {
     FFVulkanShader reset;
     FFVulkanShader decode;
 
-    FFVkBuffer rangecoder_static_buf;
+    FFVkBuffer rangecoder_buf;
     FFVkBuffer quant_buf;
-    FFVkBuffer crc_tab_buf;
+    FFVkBuffer crc_buf;
 
     AVBufferPool *slice_state_pool;
     AVBufferPool *slice_offset_pool;
     AVBufferPool *slice_status_pool;
 } FFv1VulkanDecodeContext;
 
-typedef struct FFv1VkParameters {
-    VkDeviceAddress slice_data;
-    VkDeviceAddress slice_state;
-
-    int fmt_lut[4];
-    uint32_t img_size[2];
-    uint32_t chroma_shift[2];
-
-    uint32_t plane_state_size;
-    uint32_t crcref;
-    int rct_offset;
-
-    uint8_t extend_lookup[8];
-    uint8_t bits_per_raw_sample;
-    uint8_t quant_table_count;
-    uint8_t version;
-    uint8_t micro_version;
-    uint8_t key_frame;
-    uint8_t planes;
-    uint8_t codec_planes;
-    uint8_t color_planes;
-    uint8_t transparency;
-    uint8_t planar_rgb;
-    uint8_t colorspace;
-    uint8_t ec;
-    uint8_t golomb;
-    uint8_t check_crc;
-    uint8_t padding[3];
-} FFv1VkParameters;
-
-static void add_push_data(FFVulkanShader *shd)
-{
-    GLSLC(0, layout(push_constant, scalar) uniform pushConstants {  );
-    GLSLC(1,    u8buf slice_data;                                   );
-    GLSLC(1,    u8buf slice_state;                                  );
-    GLSLC(0,                                                        );
-    GLSLC(1,    ivec4 fmt_lut;                                      );
-    GLSLC(1,    uvec2 img_size;                                     );
-    GLSLC(1,    uvec2 chroma_shift;                                 );
-    GLSLC(0,                                                        );
-    GLSLC(1,    uint plane_state_size;                              );
-    GLSLC(1,    uint32_t crcref;                                    );
-    GLSLC(1,    int rct_offset;                                     );
-    GLSLC(0,                                                        );
-    GLSLC(1,    uint8_t extend_lookup[8];                           );
-    GLSLC(1,    uint8_t bits_per_raw_sample;                        );
-    GLSLC(1,    uint8_t quant_table_count;                          );
-    GLSLC(1,    uint8_t version;                                    );
-    GLSLC(1,    uint8_t micro_version;                              );
-    GLSLC(1,    uint8_t key_frame;                                  );
-    GLSLC(1,    uint8_t planes;                                     );
-    GLSLC(1,    uint8_t codec_planes;                               );
-    GLSLC(1,    uint8_t color_planes;                               );
-    GLSLC(1,    uint8_t transparency;                               );
-    GLSLC(1,    uint8_t planar_rgb;                                 );
-    GLSLC(1,    uint8_t colorspace;                                 );
-    GLSLC(1,    uint8_t ec;                                         );
-    GLSLC(1,    uint8_t golomb;                                     );
-    GLSLC(1,    uint8_t check_crc;                                  );
-    GLSLC(1,    uint8_t padding[3];                                 );
-    GLSLC(0, };                                                     );
-    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1VkParameters),
-                                VK_SHADER_STAGE_COMPUTE_BIT);
-}
-
 static int vk_ffv1_start_frame(AVCodecContext          *avctx,
                                const AVBufferRef       *buffer_ref,
                                av_unused const uint8_t *buffer,
@@ -291,13 +238,10 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
 
     FFV1Context *f = avctx->priv_data;
     FFv1VulkanDecodeContext *fv = ctx->sd_ctx;
-    FFv1VkParameters pd;
-    FFv1VkResetParameters pd_reset;
 
     AVHWFramesContext *hwfc = (AVHWFramesContext *)avctx->hw_frames_ctx->data;
     enum AVPixelFormat sw_format = hwfc->sw_format;
 
-    int bits = f->avctx->bits_per_raw_sample > 0 ? f->avctx->bits_per_raw_sample : 8;
     int is_rgb = !(f->colorspace == 0 && sw_format != AV_PIX_FMT_YA8) &&
                  !(sw_format == AV_PIX_FMT_YA8);
     int color_planes = av_pix_fmt_desc_get(avctx->sw_pix_fmt)->nb_components;
@@ -408,39 +352,25 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                                     VK_FORMAT_UNDEFINED);
 
     ff_vk_exec_bind_shader(&ctx->s, exec, &fv->setup);
-    pd = (FFv1VkParameters) {
+
+    FFv1ShaderParams pd = {
         .slice_data = slices_buf->address,
         .slice_state  = slice_state->address + f->slice_count*fp->slice_data_size,
 
         .img_size[0] = f->picture.f->width,
         .img_size[1] = f->picture.f->height,
-        .chroma_shift[0] = f->chroma_h_shift,
-        .chroma_shift[1] = f->chroma_v_shift,
 
         .plane_state_size = fp->plane_state_size,
-        .crcref = f->crcref,
-        .rct_offset = 1 << bits,
-
-        .bits_per_raw_sample = bits,
-        .quant_table_count = f->quant_table_count,
-        .version = f->version,
-        .micro_version = f->micro_version,
         .key_frame = f->picture.f->flags & AV_FRAME_FLAG_KEY,
-        .planes = av_pix_fmt_count_planes(sw_format),
-        .codec_planes = f->plane_count,
-        .color_planes = color_planes,
-        .transparency = f->transparency,
-        .planar_rgb = ff_vk_mt_is_np_rgb(sw_format) &&
-                      (ff_vk_count_images((AVVkFrame *)f->picture.f->data[0]) > 1),
-        .colorspace = f->colorspace,
-        .ec = f->ec,
-        .golomb = f->ac == AC_GOLOMB_RICE,
-        .check_crc = !!(avctx->err_recognition & AV_EF_CRCCHECK),
+        .crcref = f->crcref,
+        .micro_version = f->micro_version,
     };
-    for (int i = 0; i < f->quant_table_count; i++)
-        pd.extend_lookup[i] = (f->quant_tables[i][3][127] != 0) ||
-                              (f->quant_tables[i][4][127] != 0);
 
+    for (int i = 0; i < f->quant_table_count; i++) {
+        pd.context_count[i] = f->context_count[i];
+        pd.extend_lookup[i] = f->quant_tables[i][3][127] ||
+                              f->quant_tables[i][4][127];
+    }
 
     /* For some reason the C FFv1 encoder/decoder treats these differently */
     if (sw_format == AV_PIX_FMT_GBRP10 || sw_format == AV_PIX_FMT_GBRP12 ||
@@ -451,7 +381,7 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
 
     ff_vk_shader_update_push_const(&ctx->s, exec, &fv->setup,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd), &pd);
+                                   0, sizeof(FFv1ShaderParams), &pd);
 
     vk->CmdDispatch(exec->buf, f->num_h_slices, f->num_v_slices, 1);
 
@@ -476,21 +406,9 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                                     VK_FORMAT_UNDEFINED);
 
     ff_vk_exec_bind_shader(&ctx->s, exec, reset_shader);
-
-    pd_reset = (FFv1VkResetParameters) {
-        .slice_state = slice_state->address + f->slice_count*fp->slice_data_size,
-        .plane_state_size = fp->plane_state_size,
-        .codec_planes = f->plane_count,
-        .key_frame = f->picture.f->flags & AV_FRAME_FLAG_KEY,
-        .version = f->version,
-        .micro_version = f->micro_version,
-    };
-    for (int i = 0; i < f->quant_table_count; i++)
-        pd_reset.context_count[i] = f->context_count[i];
-
     ff_vk_shader_update_push_const(&ctx->s, exec, reset_shader,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd_reset), &pd_reset);
+                                   0, sizeof(FFv1ShaderParams), &pd);
 
     /* Sync between setup and reset shaders */
     ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
@@ -530,27 +448,33 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                                     slice_state,
                                     0, fp->slice_data_size*f->slice_count,
                                     VK_FORMAT_UNDEFINED);
-    ff_vk_shader_update_img_array(&ctx->s, exec, decode_shader,
-                                  decode_dst, decode_dst_view,
-                                  1, 1,
-                                  VK_IMAGE_LAYOUT_GENERAL,
-                                  VK_NULL_HANDLE);
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
+                                    1, 1, 0,
+                                    slice_offset,
+                                    0, 2*f->slice_count*sizeof(uint32_t),
+                                    VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
                                     1, 2, 0,
                                     slice_status,
                                     0, 2*f->slice_count*sizeof(uint32_t),
                                     VK_FORMAT_UNDEFINED);
+
+    ff_vk_shader_update_img_array(&ctx->s, exec, decode_shader,
+                                  decode_dst, decode_dst_view,
+                                  1, 3,
+                                  VK_IMAGE_LAYOUT_GENERAL,
+                                  VK_NULL_HANDLE);
     if (is_rgb)
         ff_vk_shader_update_img_array(&ctx->s, exec, decode_shader,
                                       f->picture.f, vp->view.out,
-                                      1, 3,
+                                      1, 4,
                                       VK_IMAGE_LAYOUT_GENERAL,
                                       VK_NULL_HANDLE);
 
     ff_vk_exec_bind_shader(&ctx->s, exec, decode_shader);
     ff_vk_shader_update_push_const(&ctx->s, exec, decode_shader,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd), &pd);
+                                   0, sizeof(FFv1ShaderParams), &pd);
 
     /* Sync probabilities between reset and decode shaders */
     ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
@@ -602,329 +526,175 @@ fail:
     return 0;
 }
 
-static void define_shared_code(FFVulkanShader *shd, int use32bit)
-{
-    int smp_bits = use32bit ? 32 : 16;
-
-    GLSLC(0, #define DECODE                                              );
-
-    av_bprintf(&shd->src, "#define RGB_LINECACHE %i\n"                   ,RGB_LINECACHE);
-    av_bprintf(&shd->src, "#define CONTEXT_SIZE %i\n"                    ,CONTEXT_SIZE);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_MASK 0x%x\n"          ,MAX_QUANT_TABLE_MASK);
-
-    GLSLF(0, #define TYPE int%i_t                                        ,smp_bits);
-    GLSLF(0, #define VTYPE2 i%ivec2                                      ,smp_bits);
-    GLSLF(0, #define VTYPE3 i%ivec3                                      ,smp_bits);
-    GLSLD(ff_source_rangecoder_comp);
-    GLSLD(ff_source_ffv1_common_comp);
-}
-
 static int init_setup_shader(FFV1Context *f, FFVulkanContext *s,
-                             FFVkExecPool *pool, FFVkSPIRVCompiler *spv,
-                             FFVulkanShader *shd)
+                             FFVkExecPool *pool, FFVulkanShader *shd,
+                             VkSpecializationInfo *sl)
 {
     int err;
-    FFVulkanDescriptorSetBinding *desc_set;
 
-    uint8_t *spv_data;
-    size_t spv_len;
-    void *spv_opaque = NULL;
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
+                      (uint32_t []) { 1, 1, 1 }, 0);
 
-    RET(ff_vk_shader_init(s, shd, "ffv1_dec_setup",
-                          VK_SHADER_STAGE_COMPUTE_BIT,
-                          (const char *[]) { "GL_EXT_buffer_reference",
-                                             "GL_EXT_buffer_reference2" }, 2,
-                          1, 1, 1,
-                          0));
+    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
+                                VK_SHADER_STAGE_COMPUTE_BIT);
 
-    /* Common codec header */
-    GLSLD(ff_source_common_comp);
-
-    add_push_data(shd);
-
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLES %i\n", MAX_QUANT_TABLES);
-    av_bprintf(&shd->src, "#define MAX_CONTEXT_INPUTS %i\n", MAX_CONTEXT_INPUTS);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_SIZE %i\n", MAX_QUANT_TABLE_SIZE);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "rangecoder_static_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint8_t zero_one_state[512];",
+    const FFVulkanDescriptorSetBinding desc_set_const[] = {
+        { /* rangecoder_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "crc_ieee_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint32_t crc_ieee[256];",
-        },
-        {
-            .name        = "quant_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "int16_t quant_table[MAX_QUANT_TABLES]"
-                           "[MAX_CONTEXT_INPUTS][MAX_QUANT_TABLE_SIZE];",
+        { /* crc_ieee_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
+    ff_vk_shader_add_descriptor_set(s, shd, desc_set_const, 2, 1, 0);
 
-    RET(ff_vk_shader_add_descriptor_set(s, shd, desc_set, 3, 1, 0));
-
-    define_shared_code(shd, 0 /* Irrelevant */);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "slice_data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .buf_content = "SliceContext slice_ctx",
-            .buf_elems   = f->max_slice_count,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* slice_data_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "slice_offsets_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_quali   = "readonly",
-            .buf_content = "u32vec2 slice_offsets",
-            .buf_elems   = 2*f->max_slice_count,
+        { /* slice_offsets_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "slice_status_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_quali   = "writeonly",
-            .buf_content = "uint32_t slice_status",
-            .buf_elems   = 2*f->max_slice_count,
+        { /* slice_status_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(s, shd, desc_set, 3, 0, 0));
+    ff_vk_shader_add_descriptor_set(s, shd, desc_set, 3, 0, 0);
 
-    GLSLD(ff_source_ffv1_dec_setup_comp);
-
-    RET(spv->compile_shader(s, spv, shd, &spv_data, &spv_len, "main",
-                            &spv_opaque));
-    RET(ff_vk_shader_link(s, shd, spv_data, spv_len, "main"));
+    RET(ff_vk_shader_link(s, shd,
+                          ff_ffv1_dec_setup_comp_spv_data,
+                          ff_ffv1_dec_setup_comp_spv_len, "main"));
 
     RET(ff_vk_shader_register_exec(s, pool, shd));
 
 fail:
-    if (spv_opaque)
-        spv->free_shader(spv, &spv_opaque);
-
     return err;
 }
 
 static int init_reset_shader(FFV1Context *f, FFVulkanContext *s,
-                             FFVkExecPool *pool, FFVkSPIRVCompiler *spv,
-                             FFVulkanShader *shd, int ac)
+                             FFVkExecPool *pool, FFVulkanShader *shd,
+                             VkSpecializationInfo *sl, int ac)
 {
     int err;
-    FFVulkanDescriptorSetBinding *desc_set;
-
-    uint8_t *spv_data;
-    size_t spv_len;
-    void *spv_opaque = NULL;
     int wg_dim = FFMIN(s->props.properties.limits.maxComputeWorkGroupSize[0], 1024);
 
-    RET(ff_vk_shader_init(s, shd, "ffv1_dec_reset",
-                          VK_SHADER_STAGE_COMPUTE_BIT,
-                          (const char *[]) { "GL_EXT_buffer_reference",
-                                             "GL_EXT_buffer_reference2" }, 2,
-                          wg_dim, 1, 1,
-                          0));
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
+                      (uint32_t []) { wg_dim, 1, 1 }, 0);
 
-    if (ac == AC_GOLOMB_RICE)
-        av_bprintf(&shd->src, "#define GOLOMB\n");
-
-    /* Common codec header */
-    GLSLD(ff_source_common_comp);
-
-    GLSLC(0, layout(push_constant, scalar) uniform pushConstants {             );
-    GLSLF(1,    uint context_count[%i];                                        ,MAX_QUANT_TABLES);
-    GLSLC(1,    u8buf slice_state;                                             );
-    GLSLC(1,    uint plane_state_size;                                         );
-    GLSLC(1,    uint8_t codec_planes;                                          );
-    GLSLC(1,    uint8_t key_frame;                                             );
-    GLSLC(1,    uint8_t version;                                               );
-    GLSLC(1,    uint8_t micro_version;                                         );
-    GLSLC(1,    uint8_t padding[1];                                            );
-    GLSLC(0, };                                                                );
-    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1VkResetParameters),
+    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLES %i\n", MAX_QUANT_TABLES);
-    av_bprintf(&shd->src, "#define MAX_CONTEXT_INPUTS %i\n", MAX_CONTEXT_INPUTS);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_SIZE %i\n", MAX_QUANT_TABLE_SIZE);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "rangecoder_static_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint8_t zero_one_state[512];",
-        },
-        {
-            .name        = "quant_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "int16_t quant_table[MAX_QUANT_TABLES]"
-                           "[MAX_CONTEXT_INPUTS][MAX_QUANT_TABLE_SIZE];",
+    const FFVulkanDescriptorSetBinding desc_set_const[] = {
+        { /* rangecoder_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(s, shd, desc_set, 2, 1, 0));
+    ff_vk_shader_add_descriptor_set(s, shd, desc_set_const, 1, 1, 0);
+
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* slice_data_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+        },
+    };
+    ff_vk_shader_add_descriptor_set(s, shd, desc_set, 1, 0, 0);
 
-    define_shared_code(shd, 0 /* Bit depth irrelevant for the reset shader */);
     if (ac == AC_GOLOMB_RICE)
-        GLSLD(ff_source_ffv1_vlc_comp);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "slice_data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .mem_quali   = "readonly",
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .buf_content = "SliceContext slice_ctx",
-            .buf_elems   = f->max_slice_count,
-        },
-    };
-    RET(ff_vk_shader_add_descriptor_set(s, shd, desc_set, 1, 0, 0));
-
-    GLSLD(ff_source_ffv1_reset_comp);
-
-    RET(spv->compile_shader(s, spv, shd, &spv_data, &spv_len, "main",
-                            &spv_opaque));
-    RET(ff_vk_shader_link(s, shd, spv_data, spv_len, "main"));
+        RET(ff_vk_shader_link(s, shd,
+                              ff_ffv1_dec_reset_golomb_comp_spv_data,
+                              ff_ffv1_dec_reset_golomb_comp_spv_len, "main"));
+    else
+        RET(ff_vk_shader_link(s, shd,
+                              ff_ffv1_dec_reset_comp_spv_data,
+                              ff_ffv1_dec_reset_comp_spv_len, "main"));
 
     RET(ff_vk_shader_register_exec(s, pool, shd));
 
 fail:
-    if (spv_opaque)
-        spv->free_shader(spv, &spv_opaque);
-
     return err;
 }
 
 static int init_decode_shader(FFV1Context *f, FFVulkanContext *s,
-                              FFVkExecPool *pool, FFVkSPIRVCompiler *spv,
-                              FFVulkanShader *shd,
+                              FFVkExecPool *pool, FFVulkanShader *shd,
                               AVHWFramesContext *dec_frames_ctx,
                               AVHWFramesContext *out_frames_ctx,
-                              int ac, int rgb)
+                              VkSpecializationInfo *sl, int ac, int rgb)
 {
     int err;
-    FFVulkanDescriptorSetBinding *desc_set;
 
-    uint8_t *spv_data;
-    size_t spv_len;
-    void *spv_opaque = NULL;
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
+                      (uint32_t []) { 1, 1, 1 }, 0);
 
-    int use_cached_reader = ac != AC_GOLOMB_RICE &&
-                            s->driver_props.driverID == VK_DRIVER_ID_MESA_RADV;
+    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
+                                VK_SHADER_STAGE_COMPUTE_BIT);
 
-    RET(ff_vk_shader_init(s, shd, "ffv1_dec",
-                          VK_SHADER_STAGE_COMPUTE_BIT,
-                          (const char *[]) { "GL_EXT_buffer_reference",
-                                             "GL_EXT_buffer_reference2" }, 2,
-                          use_cached_reader ? CONTEXT_SIZE : 1, 1, 1,
-                          0));
-
-    if (ac == AC_GOLOMB_RICE)
-        av_bprintf(&shd->src, "#define GOLOMB\n");
-
-    if (rgb)
-        av_bprintf(&shd->src, "#define RGB\n");
-
-    if (use_cached_reader)
-        av_bprintf(&shd->src, "#define CACHED_SYMBOL_READER 1\n");
-
-    /* Common codec header */
-    GLSLD(ff_source_common_comp);
-
-    add_push_data(shd);
-
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLES %i\n", MAX_QUANT_TABLES);
-    av_bprintf(&shd->src, "#define MAX_CONTEXT_INPUTS %i\n", MAX_CONTEXT_INPUTS);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_SIZE %i\n", MAX_QUANT_TABLE_SIZE);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "rangecoder_static_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint8_t zero_one_state[512];",
+    const FFVulkanDescriptorSetBinding desc_set_const[] = {
+        { /* rangecoder_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "quant_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "int16_t quant_table[MAX_QUANT_TABLES]"
-                           "[MAX_CONTEXT_INPUTS][MAX_QUANT_TABLE_SIZE];",
+        { /* quant_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
+    ff_vk_shader_add_descriptor_set(s, shd, desc_set_const, 2, 1, 0);
 
-    RET(ff_vk_shader_add_descriptor_set(s, shd, desc_set, 2, 1, 0));
-
-    define_shared_code(shd, f->use32bit);
-    if (ac == AC_GOLOMB_RICE)
-        GLSLD(ff_source_ffv1_vlc_comp);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "slice_data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .buf_content = "SliceContext slice_ctx",
-            .buf_elems   = f->max_slice_count,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* slice_data_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name       = "dec",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .dimensions = 2,
-            .mem_layout = ff_vk_shader_rep_fmt(dec_frames_ctx->sw_format,
-                                               FF_VK_REP_NATIVE),
-            .elems      = av_pix_fmt_count_planes(dec_frames_ctx->sw_format),
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* slice_offsets_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "slice_status_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_quali   = "writeonly",
-            .buf_content = "uint32_t slice_status",
-            .buf_elems   = 2*f->max_slice_count,
+        { /* slice_status_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name       = "dst",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .dimensions = 2,
-            .mem_layout = ff_vk_shader_rep_fmt(out_frames_ctx->sw_format,
-                                               FF_VK_REP_NATIVE),
-            .mem_quali  = "writeonly",
-            .elems      = av_pix_fmt_count_planes(out_frames_ctx->sw_format),
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* dec */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = av_pix_fmt_count_planes(dec_frames_ctx->sw_format),
+        },
+        { /* dst */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = av_pix_fmt_count_planes(out_frames_ctx->sw_format),
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(s, shd, desc_set, 3 + rgb, 0, 0));
+    ff_vk_shader_add_descriptor_set(s, shd, desc_set, 4 + rgb, 0, 0);
 
-    GLSLD(ff_source_ffv1_dec_comp);
-
-    RET(spv->compile_shader(s, spv, shd, &spv_data, &spv_len, "main",
-                            &spv_opaque));
-    RET(ff_vk_shader_link(s, shd, spv_data, spv_len, "main"));
+    if (ac == AC_GOLOMB_RICE) {
+        if (rgb)
+            ff_vk_shader_link(s, shd,
+                              ff_ffv1_dec_rgb_golomb_comp_spv_data,
+                              ff_ffv1_dec_rgb_golomb_comp_spv_len, "main");
+        else
+            ff_vk_shader_link(s, shd,
+                              ff_ffv1_dec_golomb_comp_spv_data,
+                              ff_ffv1_dec_golomb_comp_spv_len, "main");
+    } else {
+        if (rgb)
+            ff_vk_shader_link(s, shd,
+                              ff_ffv1_dec_rgb_comp_spv_data,
+                              ff_ffv1_dec_rgb_comp_spv_len, "main");
+        else
+            ff_vk_shader_link(s, shd,
+                              ff_ffv1_dec_comp_spv_data,
+                              ff_ffv1_dec_comp_spv_len, "main");
+    }
 
     RET(ff_vk_shader_register_exec(s, pool, shd));
 
 fail:
-    if (spv_opaque)
-        spv->free_shader(spv, &spv_opaque);
-
     return err;
 }
 
@@ -954,7 +724,8 @@ static int init_indirect(AVCodecContext *avctx, FFVulkanContext *s,
 
     err = av_hwframe_ctx_init(*dst);
     if (err < 0) {
-        av_log(avctx, AV_LOG_ERROR, "Unable to initialize frame pool with format %s: %s\n",
+        av_log(avctx, AV_LOG_ERROR,
+               "Unable to initialize frame pool with format %s: %s\n",
                av_get_pix_fmt_name(sw_format), av_err2str(err));
         av_buffer_unref(dst);
         return err;
@@ -973,9 +744,9 @@ static void vk_decode_ffv1_uninit(FFVulkanDecodeShared *ctx)
     ff_vk_shader_free(&ctx->s, &fv->reset);
     ff_vk_shader_free(&ctx->s, &fv->decode);
 
+    ff_vk_free_buf(&ctx->s, &fv->rangecoder_buf);
     ff_vk_free_buf(&ctx->s, &fv->quant_buf);
-    ff_vk_free_buf(&ctx->s, &fv->rangecoder_static_buf);
-    ff_vk_free_buf(&ctx->s, &fv->crc_tab_buf);
+    ff_vk_free_buf(&ctx->s, &fv->crc_buf);
 
     av_buffer_pool_uninit(&fv->slice_state_pool);
     av_buffer_pool_uninit(&fv->slice_offset_pool);
@@ -991,18 +762,11 @@ static int vk_decode_ffv1_init(AVCodecContext *avctx)
     FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data;
     FFVulkanDecodeShared *ctx = NULL;
     FFv1VulkanDecodeContext *fv;
-    FFVkSPIRVCompiler *spv;
 
     if (f->version < 3 ||
         (f->version == 4 && f->micro_version > 3))
         return AVERROR(ENOTSUP);
 
-    spv = ff_vk_spirv_init();
-    if (!spv) {
-        av_log(avctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n");
-        return AVERROR_EXTERNAL;
-    }
-
     err = ff_vk_decode_init(avctx);
     if (err < 0)
         return err;
@@ -1019,6 +783,8 @@ static int vk_decode_ffv1_init(AVCodecContext *avctx)
     AVHWFramesContext *hwfc = (AVHWFramesContext *)avctx->hw_frames_ctx->data;
     AVHWFramesContext *dctx = hwfc;
     enum AVPixelFormat sw_format = hwfc->sw_format;
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(sw_format);
+    int color_planes = av_pix_fmt_desc_get(avctx->sw_pix_fmt)->nb_components;
     int is_rgb = !(f->colorspace == 0 && sw_format != AV_PIX_FMT_YA8) &&
                  !(sw_format == AV_PIX_FMT_YA8);
 
@@ -1029,63 +795,78 @@ static int vk_decode_ffv1_init(AVCodecContext *avctx)
         dctx = (AVHWFramesContext *)fv->intermediate_frames_ref->data;
     }
 
+    SPEC_LIST_CREATE(sl, 15, 15*sizeof(uint32_t))
+
+    if (RGB_LINECACHE != 2)
+        SPEC_LIST_ADD(sl, 0, 32, RGB_LINECACHE);
+
+    if (f->ec && !!(avctx->err_recognition & AV_EF_CRCCHECK))
+        SPEC_LIST_ADD(sl, 1, 32, 1);
+
+    SPEC_LIST_ADD(sl,  2, 32, f->version);
+    SPEC_LIST_ADD(sl,  3, 32, f->quant_table_count);
+
+    for (int i = 0; i < f->quant_table_count; i++) {
+        if (f->quant_tables[i][3][127] || f->quant_tables[i][4][127]) {
+            SPEC_LIST_ADD(sl, 4, 32, 1);
+            break;
+        }
+    }
+
+    int bits = f->avctx->bits_per_raw_sample > 0 ? f->avctx->bits_per_raw_sample : 8;
+    SPEC_LIST_ADD(sl,  5, 32, 1 << bits);
+    SPEC_LIST_ADD(sl,  6, 32, f->colorspace);
+    SPEC_LIST_ADD(sl,  7, 32, f->transparency);
+    SPEC_LIST_ADD(sl,  8, 32, ff_vk_mt_is_np_rgb(sw_format) &&
+                              (desc->flags & AV_PIX_FMT_FLAG_PLANAR));
+    SPEC_LIST_ADD(sl,  9, 32, f->plane_count);
+    SPEC_LIST_ADD(sl, 10, 32, color_planes);
+    SPEC_LIST_ADD(sl, 11, 32, av_pix_fmt_count_planes(sw_format));
+    SPEC_LIST_ADD(sl, 12, 32, bits);
+
+    SPEC_LIST_ADD(sl, 13, 32, f->chroma_h_shift);
+    SPEC_LIST_ADD(sl, 14, 32, f->chroma_v_shift);
+
     /* Setup shader */
-    RET(init_setup_shader(f, &ctx->s, &ctx->exec_pool, spv, &fv->setup));
+    RET(init_setup_shader(f, &ctx->s, &ctx->exec_pool, &fv->setup, sl));
 
     /* Reset shader */
-    RET(init_reset_shader(f, &ctx->s, &ctx->exec_pool,
-                          spv, &fv->reset, f->ac));
+    RET(init_reset_shader(f, &ctx->s, &ctx->exec_pool, &fv->reset, sl, f->ac));
 
     /* Decode shaders */
-    RET(init_decode_shader(f, &ctx->s, &ctx->exec_pool,
-                           spv, &fv->decode,
-                           dctx,
-                           hwfc,
-                           f->ac,
-                           is_rgb));
+    RET(init_decode_shader(f, &ctx->s, &ctx->exec_pool, &fv->decode,
+                           dctx, hwfc, sl, f->ac, is_rgb));
 
-    /* Range coder data */
-    RET(ff_ffv1_vk_init_state_transition_data(&ctx->s,
-                                              &fv->rangecoder_static_buf,
-                                              f));
-
-    /* Quantization table data */
-    RET(ff_ffv1_vk_init_quant_table_data(&ctx->s,
-                                         &fv->quant_buf,
-                                         f));
-
-    /* CRC table buffer */
-    RET(ff_ffv1_vk_init_crc_table_data(&ctx->s,
-                                       &fv->crc_tab_buf,
-                                       f));
+    /* Init static data */
+    RET(ff_ffv1_vk_init_state_transition_data(&ctx->s, &fv->rangecoder_buf, f));
+    RET(ff_ffv1_vk_init_crc_table_data(&ctx->s, &fv->crc_buf, f));
+    RET(ff_ffv1_vk_init_quant_table_data(&ctx->s, &fv->quant_buf, f));
 
     /* Update setup global descriptors */
     RET(ff_vk_shader_update_desc_buffer(&ctx->s, &ctx->exec_pool.contexts[0],
                                         &fv->setup, 0, 0, 0,
-                                        &fv->rangecoder_static_buf,
-                                        0, fv->rangecoder_static_buf.size,
+                                        &fv->rangecoder_buf,
+                                        0, 512*sizeof(uint8_t),
                                         VK_FORMAT_UNDEFINED));
     RET(ff_vk_shader_update_desc_buffer(&ctx->s, &ctx->exec_pool.contexts[0],
                                         &fv->setup, 0, 1, 0,
-                                        &fv->crc_tab_buf,
-                                        0, fv->crc_tab_buf.size,
+                                        &fv->crc_buf,
+                                        0, 256*sizeof(uint32_t),
                                         VK_FORMAT_UNDEFINED));
 
     /* Update decode global descriptors */
     RET(ff_vk_shader_update_desc_buffer(&ctx->s, &ctx->exec_pool.contexts[0],
                                         &fv->decode, 0, 0, 0,
-                                        &fv->rangecoder_static_buf,
-                                        0, fv->rangecoder_static_buf.size,
+                                        &fv->rangecoder_buf,
+                                        0, 512*sizeof(uint8_t),
                                         VK_FORMAT_UNDEFINED));
     RET(ff_vk_shader_update_desc_buffer(&ctx->s, &ctx->exec_pool.contexts[0],
                                         &fv->decode, 0, 1, 0,
                                         &fv->quant_buf,
-                                        0, fv->quant_buf.size,
+                                        0, VK_WHOLE_SIZE,
                                         VK_FORMAT_UNDEFINED));
 
 fail:
-    spv->uninit(&spv);
-
     return err;
 }
 
-- 
2.52.0


>From 5fef03f28d5d5bce060f7e6e790f7137be40dcf7 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 18:08:36 +0100
Subject: [PATCH 12/58] vulkan_ffv1: precalculate bits and use a specialization
 constant

---
 libavcodec/vulkan/ffv1_common.glsl   |  2 +-
 libavcodec/vulkan/ffv1_dec.comp.glsl | 25 +++++++++++--------------
 libavcodec/vulkan_ffv1.c             |  2 +-
 3 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 625e615054..94b83ad09e 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -46,7 +46,7 @@ layout (constant_id =  8) const bool planar_rgb = false;
 layout (constant_id =  9) const int codec_planes = 0;
 layout (constant_id = 10) const int color_planes = 0;
 layout (constant_id = 11) const int planes = 0;
-layout (constant_id = 12) const int bits_per_raw_sample = 0;
+layout (constant_id = 12) const int bits = 0;
 
 layout (constant_id = 13) const int chroma_shift_x = 0;
 layout (constant_id = 14) const int chroma_shift_y = 0;
diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 0d7dec48a3..862f907972 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -63,7 +63,7 @@ int get_isymbol(inout RangeCoder c, uint state_off)
     return READ(c, min(e + 10, 21)) ? -a : a;
 }
 
-void decode_line_pcm(inout SliceContext sc, ivec2 sp, int w, int y, int p, int bits)
+void decode_line_pcm(inout SliceContext sc, ivec2 sp, int w, int y, int p)
 {
 #ifndef RGB
     if (p > 0 && p < 3) {
@@ -74,15 +74,17 @@ void decode_line_pcm(inout SliceContext sc, ivec2 sp, int w, int y, int p, int b
 
     for (int x = 0; x < w; x++) {
         uint v = 0;
-        for (int i = (bits - 1); i >= 0; i--)
-            v |= uint(get_rac_equi(sc.c)) << i;
+
+        [[unroll]]
+        for (uint i = (rct_offset >> 1); i > 0; i >>= 1)
+            v |= get_rac_equi(sc.c) ? i : 0;
 
         imageStore(dec[p], sp + LADDR(ivec2(x, y)), uvec4(v));
     }
 }
 
 void decode_line(inout SliceContext sc, ivec2 sp, int w,
-                 int y, int p, int bits, uint state_off,
+                 int y, int p, uint state_off,
                  uint8_t quant_table_idx, int run_index)
 {
 #ifndef RGB
@@ -118,7 +120,7 @@ void golomb_init(inout SliceContext sc)
 }
 
 void decode_line(inout SliceContext sc, ivec2 sp, int w,
-                 int y, int p, int bits, uint state_off,
+                 int y, int p, uint state_off,
                  uint8_t quant_table_idx, inout int run_index)
 {
 #ifndef RGB
@@ -230,12 +232,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
     int w = sc.slice_dim.x;
     ivec2 sp = sc.slice_pos;
 
-    int bits = bits_per_raw_sample;
 #ifdef RGB
-    bits = 9;
-    if (bits != 8 || sc.slice_coding_mode != 0)
-        bits = bits_per_raw_sample + int(sc.slice_coding_mode != 1);
-
     sp.y = int(gl_WorkGroupID.y)*rgb_linecache;
 #endif
 
@@ -245,7 +242,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
 #ifdef RGB
         for (int y = 0; y < sc.slice_dim.y; y++) {
             for (int p = 0; p < color_planes; p++)
-                decode_line_pcm(sc, sp, w, y, p, bits);
+                decode_line_pcm(sc, sp, w, y, p);
 
             writeout_rgb(sc, sp, w, y, false);
         }
@@ -256,7 +253,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
                 h = ceil_rshift(h, chroma_shift.y);
 
             for (int y = 0; y < h; y++)
-                decode_line_pcm(sc, sp, w, y, p, bits);
+                decode_line_pcm(sc, sp, w, y, p);
         }
 #endif
         return;
@@ -275,7 +272,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
     int run_index = 0;
     for (int y = 0; y < sc.slice_dim.y; y++) {
         for (int p = 0; p < color_planes; p++)
-            decode_line(sc, sp, w, y, p, bits,
+            decode_line(sc, sp, w, y, p,
                         slice_state_off[p], quant_table_idx[p], run_index);
 
         writeout_rgb(sc, sp, w, y, true);
@@ -288,7 +285,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
 
         int run_index = 0;
         for (int y = 0; y < h; y++)
-            decode_line(sc, sp, w, y, p, bits,
+            decode_line(sc, sp, w, y, p,
                         slice_state_off[p], quant_table_idx[p], run_index);
     }
 #endif
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 044d95ceed..e01321f890 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -822,7 +822,7 @@ static int vk_decode_ffv1_init(AVCodecContext *avctx)
     SPEC_LIST_ADD(sl,  9, 32, f->plane_count);
     SPEC_LIST_ADD(sl, 10, 32, color_planes);
     SPEC_LIST_ADD(sl, 11, 32, av_pix_fmt_count_planes(sw_format));
-    SPEC_LIST_ADD(sl, 12, 32, bits);
+    SPEC_LIST_ADD(sl, 12, 32, bits + is_rgb);
 
     SPEC_LIST_ADD(sl, 13, 32, f->chroma_h_shift);
     SPEC_LIST_ADD(sl, 14, 32, f->chroma_v_shift);
-- 
2.52.0


>From 5954c283d76e604600a8fee435c2772f3c13fb5c Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 18:55:29 +0100
Subject: [PATCH 13/58] vulkan_ffv1: remove golomb gb context from main slice
 context

---
 libavcodec/vulkan/ffv1_common.glsl   |  4 +---
 libavcodec/vulkan/ffv1_dec.comp.glsl | 12 +++++++-----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 94b83ad09e..48261072a7 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -75,9 +75,7 @@ layout (push_constant, scalar) uniform pushConstants {
 struct SliceContext {
     RangeCoder c;
 
-#ifdef DECODE
-    GetBitContext gb;
-#else
+#ifndef DECODE
     PutBitContext pb; /* 8*8 bytes */
 #endif
 
diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 862f907972..c5dde13868 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -109,13 +109,15 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
     }
 }
 #else
+GetBitContext gb;
+
 void golomb_init(inout SliceContext sc)
 {
     if (version == 3 && micro_version > 1 || version > 3)
         get_rac_internal(sc.c, sc.c.range * 129 >> 8);
 
     uint64_t ac_byte_count = sc.c.bytestream - sc.c.bytestream_start - 1;
-    init_get_bits(sc.gb, u8buf(sc.c.bytestream_start + ac_byte_count),
+    init_get_bits(gb, u8buf(sc.c.bytestream_start + ac_byte_count),
                   int(sc.c.bytestream_end - sc.c.bytestream_start - ac_byte_count));
 }
 
@@ -148,13 +150,13 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
         if (run_mode != 0) {
             if (run_count == 0 && run_mode == 1) {
                 int tmp_idx = int(log2_run[run_index]);
-                if (get_bit(sc.gb)) {
+                if (get_bit(gb)) {
                     run_count = 1 << tmp_idx;
                     if (x + run_count <= w)
                         run_index++;
                 } else {
                     if (tmp_idx != 0) {
-                        run_count = int(get_bits(sc.gb, tmp_idx));
+                        run_count = int(get_bits(gb, tmp_idx));
                     } else
                         run_count = 0;
 
@@ -168,14 +170,14 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
             if (run_count < 0) {
                 run_mode  = 0;
                 run_count = 0;
-                diff = read_vlc_symbol(sc.gb, sb, bits);
+                diff = read_vlc_symbol(gb, sb, bits);
                 if (diff >= 0)
                     diff++;
             } else {
                 diff = 0;
             }
         } else {
-            diff = read_vlc_symbol(sc.gb, sb, bits);
+            diff = read_vlc_symbol(gb, sb, bits);
         }
 
         if (pr[0] < 0)
-- 
2.52.0


>From e5fad7b20a5da9a92770f371714b28d04beda375 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 3 Feb 2026 23:59:47 +0100
Subject: [PATCH 14/58] ffv1enc_vulkan: remove golomb gb context from main
 slice context

---
 libavcodec/vulkan/ffv1_common.comp    |  8 --------
 libavcodec/vulkan/ffv1_common.glsl    |  4 ----
 libavcodec/vulkan/ffv1_enc.comp       | 26 ++++++++++++++++++++------
 libavcodec/vulkan/ffv1_enc_setup.comp | 14 --------------
 4 files changed, 20 insertions(+), 32 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_common.comp b/libavcodec/vulkan/ffv1_common.comp
index 5f654e2b29..b4bfd61217 100644
--- a/libavcodec/vulkan/ffv1_common.comp
+++ b/libavcodec/vulkan/ffv1_common.comp
@@ -23,19 +23,11 @@
 struct SliceContext {
     RangeCoder c;
 
-#if !defined(DECODE)
-    PutBitContext pb; /* 8*8 bytes */
-#else
-    GetBitContext gb;
-#endif
-
     ivec2 slice_dim;
     ivec2 slice_pos;
     ivec2 slice_rct_coef;
     u8vec3 quant_table_idx;
 
-    uint hdr_len; // only used for golomb
-
     uint slice_coding_mode;
     bool slice_reset_contexts;
 };
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 48261072a7..d77f2df96e 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -75,10 +75,6 @@ layout (push_constant, scalar) uniform pushConstants {
 struct SliceContext {
     RangeCoder c;
 
-#ifndef DECODE
-    PutBitContext pb; /* 8*8 bytes */
-#endif
-
     ivec2 slice_dim;
     ivec2 slice_pos;
     ivec2 slice_rct_coef;
diff --git a/libavcodec/vulkan/ffv1_enc.comp b/libavcodec/vulkan/ffv1_enc.comp
index f766b69a14..0a997b8a79 100644
--- a/libavcodec/vulkan/ffv1_enc.comp
+++ b/libavcodec/vulkan/ffv1_enc.comp
@@ -115,6 +115,17 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
 
 #else /* GOLOMB */
 
+uint hdr_len = 0;
+PutBitContext pb;
+
+void init_golomb(inout SliceContext sc)
+{
+    hdr_len = rac_terminate(sc.c);
+    init_put_bits(pb,
+                  OFFBUF(u8buf, sc.c.bytestream_start, hdr_len),
+                  slice_size_max - hdr_len);
+}
+
 void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
                  ivec2 sp, int y, int p, int comp, int bits,
                  uint8_t quant_table_idx, inout int run_index)
@@ -150,10 +161,10 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
                 while (run_count >= 1 << log2_run[run_index]) {
                     run_count -= 1 << log2_run[run_index];
                     run_index++;
-                    put_bits(sc.pb, 1, 1);
+                    put_bits(pb, 1, 1);
                 }
 
-                put_bits(sc.pb, 1 + log2_run[run_index], run_count);
+                put_bits(pb, 1 + log2_run[run_index], run_count);
                 if (run_index != 0)
                     run_index--;
                 run_count = 0;
@@ -168,7 +179,7 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
         if (!run_mode) {
             VlcState sb = VlcState(uint64_t(slice_state) + state_off + VLC_STATE_SIZE*d[0]);
             Symbol sym = get_vlc_symbol(sb, d[1], bits);
-            put_bits(sc.pb, sym.bits, sym.val);
+            put_bits(pb, sym.bits, sym.val);
         }
     }
 
@@ -176,11 +187,11 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
         while (run_count >= (1 << log2_run[run_index])) {
             run_count -= 1 << log2_run[run_index];
             run_index++;
-            put_bits(sc.pb, 1, 1);
+            put_bits(pb, 1, 1);
         }
 
         if (run_count > 0)
-            put_bits(sc.pb, 1, 1);
+            put_bits(pb, 1, 1);
     }
 }
 #endif
@@ -312,7 +323,7 @@ void finalize_slice(inout SliceContext sc, const uint slice_idx)
 #endif
 
 #ifdef GOLOMB
-    uint32_t enc_len = sc.hdr_len + flush_put_bits(sc.pb);
+    uint32_t enc_len = hdr_len + flush_put_bits(pb);
 #else
     uint32_t enc_len = rac_terminate(sc.c);
 #endif
@@ -353,6 +364,9 @@ void finalize_slice(inout SliceContext sc, const uint slice_idx)
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+#ifdef GOLOMB
+    init_golomb(slice_ctx[slice_idx]);
+#endif
     encode_slice(slice_ctx[slice_idx], slice_idx);
     finalize_slice(slice_ctx[slice_idx], slice_idx);
 }
diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp b/libavcodec/vulkan/ffv1_enc_setup.comp
index 5f8e6704b0..5338f94360 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp
@@ -99,16 +99,6 @@ void write_frame_header(inout SliceContext sc)
     put_rac_equi(sc.c, bool(key_frame));
 }
 
-#ifdef GOLOMB
-void init_golomb(inout SliceContext sc)
-{
-    sc.hdr_len = rac_terminate(sc.c);
-    init_put_bits(sc.pb,
-                  OFFBUF(u8buf, sc.c.bytestream_start, sc.hdr_len),
-                  slice_size_max - sc.hdr_len);
-}
-#endif
-
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
@@ -119,8 +109,4 @@ void main(void)
         write_frame_header(slice_ctx[slice_idx]);
 
     write_slice_header(slice_ctx[slice_idx]);
-
-#ifdef GOLOMB
-    init_golomb(slice_ctx[slice_idx]);
-#endif
 }
-- 
2.52.0


>From 91c749a314e65854f9f289e4e38c547c2fc4ab7e Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 00:31:11 +0100
Subject: [PATCH 15/58] vulkan_ffv1: move common spec constant setting to
 ffv1_vulkan.c

It will be shared between encoder and decoder.
---
 libavcodec/ffv1_vulkan.c | 34 ++++++++++++++++++++++++++++++++++
 libavcodec/ffv1_vulkan.h |  4 ++++
 libavcodec/vulkan_ffv1.c | 27 +--------------------------
 3 files changed, 39 insertions(+), 26 deletions(-)

diff --git a/libavcodec/ffv1_vulkan.c b/libavcodec/ffv1_vulkan.c
index a9f9f47f07..91cfb7bd73 100644
--- a/libavcodec/ffv1_vulkan.c
+++ b/libavcodec/ffv1_vulkan.c
@@ -21,6 +21,40 @@
 #include "ffv1_vulkan.h"
 #include "libavutil/crc.h"
 
+void ff_ffv1_vk_set_common_sl(AVCodecContext *avctx, FFV1Context *f,
+                              VkSpecializationInfo *sl,
+                              enum AVPixelFormat sw_format)
+{
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(sw_format);
+    int color_planes = av_pix_fmt_desc_get(sw_format)->nb_components;
+    int is_rgb = !(f->colorspace == 0 && sw_format != AV_PIX_FMT_YA8) &&
+                 !(sw_format == AV_PIX_FMT_YA8);
+
+    SPEC_LIST_ADD(sl,  2, 32, f->version);
+    SPEC_LIST_ADD(sl,  3, 32, f->quant_table_count);
+
+    for (int i = 0; i < f->quant_table_count; i++) {
+        if (f->quant_tables[i][3][127] || f->quant_tables[i][4][127]) {
+            SPEC_LIST_ADD(sl, 4, 32, 1);
+            break;
+        }
+    }
+
+    int bits = desc->comp[0].depth;
+    SPEC_LIST_ADD(sl,  5, 32, 1 << bits);
+    SPEC_LIST_ADD(sl,  6, 32, f->colorspace);
+    SPEC_LIST_ADD(sl,  7, 32, f->transparency);
+    SPEC_LIST_ADD(sl,  8, 32, ff_vk_mt_is_np_rgb(sw_format) &&
+                              (desc->flags & AV_PIX_FMT_FLAG_PLANAR));
+    SPEC_LIST_ADD(sl,  9, 32, f->plane_count);
+    SPEC_LIST_ADD(sl, 10, 32, color_planes);
+    SPEC_LIST_ADD(sl, 11, 32, av_pix_fmt_count_planes(sw_format));
+    SPEC_LIST_ADD(sl, 12, 32, bits + is_rgb);
+
+    SPEC_LIST_ADD(sl, 13, 32, f->chroma_h_shift);
+    SPEC_LIST_ADD(sl, 14, 32, f->chroma_v_shift);
+}
+
 int ff_ffv1_vk_update_state_transition_data(FFVulkanContext *s,
                                             FFVkBuffer *vkb, FFV1Context *f)
 {
diff --git a/libavcodec/ffv1_vulkan.h b/libavcodec/ffv1_vulkan.h
index a11c61ae4b..df7bac45de 100644
--- a/libavcodec/ffv1_vulkan.h
+++ b/libavcodec/ffv1_vulkan.h
@@ -24,6 +24,10 @@
 #include "libavutil/vulkan.h"
 #include "ffv1.h"
 
+void ff_ffv1_vk_set_common_sl(AVCodecContext *avctx, FFV1Context *f,
+                              VkSpecializationInfo *sl,
+                              enum AVPixelFormat sw_format);
+
 int ff_ffv1_vk_update_state_transition_data(FFVulkanContext *s,
                                             FFVkBuffer *vkb, FFV1Context *f);
 
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index e01321f890..bdc3be50b4 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -783,8 +783,6 @@ static int vk_decode_ffv1_init(AVCodecContext *avctx)
     AVHWFramesContext *hwfc = (AVHWFramesContext *)avctx->hw_frames_ctx->data;
     AVHWFramesContext *dctx = hwfc;
     enum AVPixelFormat sw_format = hwfc->sw_format;
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(sw_format);
-    int color_planes = av_pix_fmt_desc_get(avctx->sw_pix_fmt)->nb_components;
     int is_rgb = !(f->colorspace == 0 && sw_format != AV_PIX_FMT_YA8) &&
                  !(sw_format == AV_PIX_FMT_YA8);
 
@@ -796,6 +794,7 @@ static int vk_decode_ffv1_init(AVCodecContext *avctx)
     }
 
     SPEC_LIST_CREATE(sl, 15, 15*sizeof(uint32_t))
+    ff_ffv1_vk_set_common_sl(avctx, f, sl, sw_format);
 
     if (RGB_LINECACHE != 2)
         SPEC_LIST_ADD(sl, 0, 32, RGB_LINECACHE);
@@ -803,30 +802,6 @@ static int vk_decode_ffv1_init(AVCodecContext *avctx)
     if (f->ec && !!(avctx->err_recognition & AV_EF_CRCCHECK))
         SPEC_LIST_ADD(sl, 1, 32, 1);
 
-    SPEC_LIST_ADD(sl,  2, 32, f->version);
-    SPEC_LIST_ADD(sl,  3, 32, f->quant_table_count);
-
-    for (int i = 0; i < f->quant_table_count; i++) {
-        if (f->quant_tables[i][3][127] || f->quant_tables[i][4][127]) {
-            SPEC_LIST_ADD(sl, 4, 32, 1);
-            break;
-        }
-    }
-
-    int bits = f->avctx->bits_per_raw_sample > 0 ? f->avctx->bits_per_raw_sample : 8;
-    SPEC_LIST_ADD(sl,  5, 32, 1 << bits);
-    SPEC_LIST_ADD(sl,  6, 32, f->colorspace);
-    SPEC_LIST_ADD(sl,  7, 32, f->transparency);
-    SPEC_LIST_ADD(sl,  8, 32, ff_vk_mt_is_np_rgb(sw_format) &&
-                              (desc->flags & AV_PIX_FMT_FLAG_PLANAR));
-    SPEC_LIST_ADD(sl,  9, 32, f->plane_count);
-    SPEC_LIST_ADD(sl, 10, 32, color_planes);
-    SPEC_LIST_ADD(sl, 11, 32, av_pix_fmt_count_planes(sw_format));
-    SPEC_LIST_ADD(sl, 12, 32, bits + is_rgb);
-
-    SPEC_LIST_ADD(sl, 13, 32, f->chroma_h_shift);
-    SPEC_LIST_ADD(sl, 14, 32, f->chroma_v_shift);
-
     /* Setup shader */
     RET(init_setup_shader(f, &ctx->s, &ctx->exec_pool, &fv->setup, sl));
 
-- 
2.52.0


>From 3a185bf72d1572360ca0ecc8508d635b5a886ef2 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 11:14:21 +0100
Subject: [PATCH 16/58] ffv1enc_vulkan: convert RCT search shader to
 compile-time SPIR-V generation

---
 libavcodec/ffv1_vulkan.h                      |  12 --
 libavcodec/ffv1enc_vulkan.c                   | 198 +++++++-----------
 libavcodec/vulkan/Makefile                    |   5 +-
 libavcodec/vulkan/ffv1_common.glsl            |   3 +
 ...rch.comp => ffv1_enc_rct_search.comp.glsl} |  15 +-
 5 files changed, 91 insertions(+), 142 deletions(-)
 rename libavcodec/vulkan/{ffv1_rct_search.comp => ffv1_enc_rct_search.comp.glsl} (94%)

diff --git a/libavcodec/ffv1_vulkan.h b/libavcodec/ffv1_vulkan.h
index df7bac45de..5dfd12c3c6 100644
--- a/libavcodec/ffv1_vulkan.h
+++ b/libavcodec/ffv1_vulkan.h
@@ -56,18 +56,6 @@ typedef struct FFv1ShaderParams {
     int micro_version;
 } FFv1ShaderParams;
 
-typedef struct FFv1VkRCTParameters {
-    int fmt_lut[4];
-    int offset;
-    uint8_t bits;
-    uint8_t planar_rgb;
-    uint8_t color_planes;
-    uint8_t transparency;
-    uint8_t version;
-    uint8_t micro_version;
-    uint8_t padding[2];
-} FFv1VkRCTParameters;
-
 typedef struct FFv1VkResetParameters {
     uint32_t context_count[MAX_QUANT_TABLES];
     VkDeviceAddress slice_state;
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 3f3da6bbae..933f2e6609 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -111,10 +111,12 @@ extern const char *ff_source_rangecoder_comp;
 extern const char *ff_source_ffv1_vlc_comp;
 extern const char *ff_source_ffv1_common_comp;
 extern const char *ff_source_ffv1_reset_comp;
-extern const char *ff_source_ffv1_rct_search_comp;
 extern const char *ff_source_ffv1_enc_setup_comp;
 extern const char *ff_source_ffv1_enc_comp;
 
+extern const unsigned char ff_ffv1_enc_rct_search_comp_spv_data[];
+extern const unsigned int ff_ffv1_enc_rct_search_comp_spv_len;
+
 typedef struct FFv1VkParameters {
     VkDeviceAddress slice_state;
     VkDeviceAddress scratch_data;
@@ -192,63 +194,31 @@ static void add_push_data(FFVulkanShader *shd)
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 }
 
-typedef struct FFv1VkRCTSearchParameters {
-    int fmt_lut[4];
-    int rct_offset;
-    uint8_t planar_rgb;
-    uint8_t transparency;
-    uint8_t key_frame;
-    uint8_t force_pcm;
-    uint8_t version;
-    uint8_t micro_version;
-    uint8_t padding[2];
-} FFv1VkRCTSearchParameters;
-
 static int run_rct_search(AVCodecContext *avctx, FFVkExecContext *exec,
                           AVFrame *enc_in, VkImageView *enc_in_views,
-                          FFVkBuffer *slice_data_buf, uint32_t slice_data_size)
+                          FFVkBuffer *slice_data_buf, uint32_t slice_data_size,
+                          FFv1ShaderParams *pd)
 {
     VulkanEncodeFFv1Context *fv = avctx->priv_data;
     FFV1Context *f = &fv->ctx;
     FFVulkanFunctions *vk = &fv->s.vkfn;
-    AVHWFramesContext *src_hwfc = (AVHWFramesContext *)enc_in->hw_frames_ctx->data;
-    FFv1VkRCTSearchParameters pd;
 
     /* Update descriptors */
     ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->rct_search,
-                                    0, 0, 0,
+                                    1, 0, 0,
                                     slice_data_buf,
                                     0, slice_data_size*f->slice_count,
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_img_array(&fv->s, exec, &fv->rct_search,
                                   enc_in, enc_in_views,
-                                  0, 1,
+                                  1, 1,
                                   VK_IMAGE_LAYOUT_GENERAL,
                                   VK_NULL_HANDLE);
 
     ff_vk_exec_bind_shader(&fv->s, exec, &fv->rct_search);
-
-    pd = (FFv1VkRCTSearchParameters) {
-        .rct_offset = 1 << f->bits_per_raw_sample,
-        .planar_rgb = ff_vk_mt_is_np_rgb(src_hwfc->sw_format) &&
-                      (ff_vk_count_images((AVVkFrame *)enc_in->data[0]) > 1),
-        .transparency = f->transparency,
-        .key_frame = f->key_frame,
-        .force_pcm = fv->force_pcm,
-        .version = f->version,
-        .micro_version = f->micro_version,
-    };
-
-    if (avctx->sw_pix_fmt == AV_PIX_FMT_GBRP10 ||
-        avctx->sw_pix_fmt == AV_PIX_FMT_GBRP12 ||
-        avctx->sw_pix_fmt == AV_PIX_FMT_GBRP14)
-        memcpy(pd.fmt_lut, (int [4]) { 2, 1, 0, 3 }, 4*sizeof(int));
-    else
-        ff_vk_set_perm(avctx->sw_pix_fmt, pd.fmt_lut, 1);
-
     ff_vk_shader_update_push_const(&fv->s, exec, &fv->rct_search,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd), &pd);
+                                   0, sizeof(FFv1ShaderParams), pd);
 
     vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices, 1);
 
@@ -265,7 +235,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     FFVulkanFunctions *vk = &fv->s.vkfn;
 
     VulkanEncodeFFv1FrameData *fd = exec->opaque;
-    FFv1VkParameters pd;
+    FFv1VkParameters pd_old;
 
     /* Slice data */
     AVBufferRef *slice_data_ref;
@@ -401,6 +371,34 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
                                     FF_VK_REP_NATIVE));
     }
 
+    /* With everything allocated, setup push data */
+    FFv1ShaderParams pd = {
+        .slice_data = out_data_buf->address,
+        .slice_state = slice_data_buf->address + f->slice_count*256,
+
+        .img_size[0] = fv->s.frames->width,
+        .img_size[1] = fv->s.frames->height,
+
+        .plane_state_size = plane_state_size,
+        .key_frame = f->key_frame,
+        .crcref = f->crcref,
+        .micro_version = f->micro_version,
+    };
+
+    for (int i = 0; i < f->quant_table_count; i++) {
+        pd.context_count[i] = f->context_count[i];
+        pd.extend_lookup[i] = f->quant_tables[i][3][127] ||
+                              f->quant_tables[i][4][127];
+    }
+
+    /* For some reason the C FFv1 encoder/decoder treats these differently */
+    if (avctx->sw_pix_fmt == AV_PIX_FMT_GBRP10 ||
+        avctx->sw_pix_fmt == AV_PIX_FMT_GBRP12 ||
+        avctx->sw_pix_fmt == AV_PIX_FMT_GBRP14)
+        memcpy(pd.fmt_lut, (int [4]) { 2, 1, 0, 3 }, 4*sizeof(int));
+    else
+        ff_vk_set_perm(avctx->sw_pix_fmt, pd.fmt_lut, 1);
+
     /* Setup shader */
     ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->setup,
                                     1, 0, 0,
@@ -437,7 +435,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     if (fv->optimize_rct) {
         RET(run_rct_search(avctx, exec,
                            src, src_views,
-                           slice_data_buf, slice_data_size));
+                           slice_data_buf, slice_data_size, &pd));
 
         /* Make sure the writes are visible to the setup shader */
         ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
@@ -454,7 +452,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
 
     /* Run setup shader */
     ff_vk_exec_bind_shader(&fv->s, exec, &fv->setup);
-    pd = (FFv1VkParameters) {
+    pd_old = (FFv1VkParameters) {
         .slice_state = slice_data_buf->address + f->slice_count*256,
         .out_data = out_data_buf->address,
         .bits_per_raw_sample = f->bits_per_raw_sample,
@@ -491,16 +489,16 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     if (avctx->sw_pix_fmt == AV_PIX_FMT_GBRP10 ||
         avctx->sw_pix_fmt == AV_PIX_FMT_GBRP12 ||
         avctx->sw_pix_fmt == AV_PIX_FMT_GBRP14)
-        memcpy(pd.fmt_lut, (int [4]) { 2, 1, 0, 3 }, 4*sizeof(int));
+        memcpy(pd_old.fmt_lut, (int [4]) { 2, 1, 0, 3 }, 4*sizeof(int));
     else
-        ff_vk_set_perm(avctx->sw_pix_fmt, pd.fmt_lut, 1);
+        ff_vk_set_perm(avctx->sw_pix_fmt, pd_old.fmt_lut, 1);
 
     for (int i = 0; i < f->quant_table_count; i++)
-        pd.extend_lookup[i] = (f->quant_tables[i][3][127] != 0) ||
-                              (f->quant_tables[i][4][127] != 0);
+        pd_old.extend_lookup[i] = (f->quant_tables[i][3][127] != 0) ||
+                                  (f->quant_tables[i][4][127] != 0);
     ff_vk_shader_update_push_const(&fv->s, exec, &fv->setup,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd), &pd);
+                                   0, sizeof(pd_old), &pd_old);
     vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices, 1);
 
     /* Clean up temporary image */
@@ -623,7 +621,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     ff_vk_exec_bind_shader(&fv->s, exec, &fv->enc);
     ff_vk_shader_update_push_const(&fv->s, exec, &fv->enc,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd), &pd);
+                                   0, sizeof(pd_old), &pd_old);
     vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices, 1);
 
     /* Submit */
@@ -970,102 +968,46 @@ static void define_shared_code(AVCodecContext *avctx, FFVulkanShader *shd)
     GLSLD(ff_source_ffv1_common_comp);
 }
 
-static int init_rct_search_shader(AVCodecContext *avctx, FFVkSPIRVCompiler *spv)
+static int init_rct_search_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 {
     int err;
     VulkanEncodeFFv1Context *fv = avctx->priv_data;
-    FFV1Context *f = &fv->ctx;
     FFVulkanShader *shd = &fv->rct_search;
-    FFVulkanDescriptorSetBinding *desc_set;
 
-    uint8_t *spv_data;
-    size_t spv_len;
-    void *spv_opaque = NULL;
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
+                      (uint32_t []) { 32, 32, 1 }, 0);
 
-    RET(ff_vk_shader_init(&fv->s, shd, "ffv1_rct_search",
-                          VK_SHADER_STAGE_COMPUTE_BIT,
-                          (const char *[]) { "GL_EXT_buffer_reference",
-                                             "GL_EXT_buffer_reference2",
-                                             "GL_EXT_null_initializer" }, 3,
-                          32, 32, 1,
-                          0));
-
-    /* Common codec header */
-    GLSLD(ff_source_common_comp);
-
-    GLSLC(0, layout(push_constant, scalar) uniform pushConstants {             );
-    GLSLC(1,    ivec4 fmt_lut;                                                 );
-    GLSLC(1,    int rct_offset;                                                );
-    GLSLC(1,    uint8_t planar_rgb;                                            );
-    GLSLC(1,    uint8_t transparency;                                          );
-    GLSLC(1,    uint8_t key_frame;                                             );
-    GLSLC(1,    uint8_t force_pcm;                                             );
-    GLSLC(1,    uint8_t version;                                               );
-    GLSLC(1,    uint8_t micro_version;                                         );
-    GLSLC(1,    uint8_t padding[3];                                            );
-    GLSLC(0, };                                                                );
-    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1VkResetParameters),
+    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLES %i\n", MAX_QUANT_TABLES);
-    av_bprintf(&shd->src, "#define MAX_CONTEXT_INPUTS %i\n", MAX_CONTEXT_INPUTS);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_SIZE %i\n", MAX_QUANT_TABLE_SIZE);
-
-    /* Never used */
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "rangecoder_static_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint8_t zero_one_state[512];",
-        },
-        {
-            .name        = "quant_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "int16_t quant_table[MAX_QUANT_TABLES]"
-                           "[MAX_CONTEXT_INPUTS][MAX_QUANT_TABLE_SIZE];",
+    const FFVulkanDescriptorSetBinding desc_set_const[] = {
+        { /* rangecoder_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 2, 1, 1));
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set_const, 1, 1, 0);
 
-    define_shared_code(avctx, shd);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "slice_data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .buf_content = "SliceContext slice_ctx",
-            .buf_elems   = f->max_slice_count,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* slice_data_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name       = "src",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .dimensions = 2,
-            .mem_layout = ff_vk_shader_rep_fmt(fv->s.frames->sw_format,
-                                               FF_VK_REP_NATIVE),
-            .elems      = av_pix_fmt_count_planes(fv->s.frames->sw_format),
-            .mem_quali  = "readonly",
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* src */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = av_pix_fmt_count_planes(fv->s.frames->sw_format),
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 2, 0, 0));
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 2, 0, 0);
 
-    GLSLD(ff_source_ffv1_rct_search_comp);
-
-    RET(spv->compile_shader(&fv->s, spv, shd, &spv_data, &spv_len, "main",
-                            &spv_opaque));
-    RET(ff_vk_shader_link(&fv->s, shd, spv_data, spv_len, "main"));
+    RET(ff_vk_shader_link(&fv->s, shd,
+                          ff_ffv1_enc_rct_search_comp_spv_data,
+                          ff_ffv1_enc_rct_search_comp_spv_len, "main"));
 
     RET(ff_vk_shader_register_exec(&fv->s, &fv->exec_pool, shd));
 
 fail:
-    if (spv_opaque)
-        spv->free_shader(spv, &spv_opaque);
-
     return err;
 }
 
@@ -1575,8 +1517,14 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     /* Init rct search shader */
     fv->optimize_rct = fv->is_rgb && f->version >= 4 &&
                        !fv->force_pcm && fv->optimize_rct;
+
+    /* Init shader specialization consts */
+    SPEC_LIST_CREATE(sl, 16, 16*sizeof(uint32_t))
+    ff_ffv1_vk_set_common_sl(avctx, f, sl, fv->s.frames->sw_format);
+    SPEC_LIST_ADD(sl, 15, 32, fv->force_pcm);
+
     if (fv->optimize_rct) {
-        err = init_rct_search_shader(avctx, spv);
+        err = init_rct_search_shader(avctx, sl);
         if (err < 0) {
             spv->uninit(&spv);
             return err;
diff --git a/libavcodec/vulkan/Makefile b/libavcodec/vulkan/Makefile
index a9ff44c52d..79b5facc51 100644
--- a/libavcodec/vulkan/Makefile
+++ b/libavcodec/vulkan/Makefile
@@ -4,8 +4,9 @@ clean::
 OBJS-$(CONFIG_FFV1_VULKAN_ENCODER)  +=  vulkan/common.o \
 					vulkan/rangecoder.o vulkan/ffv1_vlc.o \
 					vulkan/ffv1_common.o vulkan/ffv1_reset.o \
-					vulkan/ffv1_enc_setup.o vulkan/ffv1_enc.o \
-					vulkan/ffv1_rct_search.o
+					vulkan/ffv1_enc_setup.o vulkan/ffv1_enc.o
+
+OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_rct_search.comp.spv.o
 
 OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL) += vulkan/ffv1_dec_setup.comp.spv.o \
                                       vulkan/ffv1_dec_reset.comp.spv.o \
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index d77f2df96e..e835cc7c9f 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -52,6 +52,9 @@ layout (constant_id = 13) const int chroma_shift_x = 0;
 layout (constant_id = 14) const int chroma_shift_y = 0;
 const ivec2 chroma_shift = ivec2(chroma_shift_x, chroma_shift_y);
 
+/* Encoder-only */
+layout (constant_id = 15) const bool force_pcm = false;
+
 layout (push_constant, scalar) uniform pushConstants {
     u8buf slice_data;
     u8buf slice_state;
diff --git a/libavcodec/vulkan/ffv1_rct_search.comp b/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
similarity index 94%
rename from libavcodec/vulkan/ffv1_rct_search.comp
rename to libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
index 055bde46c4..66fc73a8e5 100644
--- a/libavcodec/vulkan/ffv1_rct_search.comp
+++ b/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
@@ -20,13 +20,21 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define ENCODE
+#include "common.comp"
+#include "ffv1_common.glsl"
+
+layout (set = 1, binding = 1) uniform uimage2D src[];
+
 ivec3 load_components(ivec2 pos)
 {
     ivec3 pix = ivec3(imageLoad(src[0], pos));
-    if (planar_rgb != 0) {
+    if (planar_rgb)
         for (int i = 1; i < 3; i++)
             pix[i] = int(imageLoad(src[i], pos)[0]);
-    }
 
     return ivec3(pix[fmt_lut[0]], pix[fmt_lut[1]], pix[fmt_lut[2]]);
 }
@@ -132,8 +140,9 @@ void coeff_search(inout SliceContext sc)
 
 void main(void)
 {
-    if (force_pcm == 1)
+    if (force_pcm)
         return;
+
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
     coeff_search(slice_ctx[slice_idx]);
 }
-- 
2.52.0


>From b7cb0a965657c5d698b76a3b1e39a5655d33c0c9 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 11:47:39 +0100
Subject: [PATCH 17/58] ffv1enc_vulkan: convert reset shader to compile-time
 SPIR-V generation

---
 libavcodec/ffv1_vulkan.h                      |  11 --
 libavcodec/ffv1enc_vulkan.c                   | 110 +++++-------------
 libavcodec/vulkan/Makefile                    |   6 +-
 ...v1_reset.comp => ffv1_enc_reset.comp.glsl} |  12 +-
 .../vulkan/ffv1_enc_reset_golomb.comp.glsl    |  27 +++++
 5 files changed, 72 insertions(+), 94 deletions(-)
 rename libavcodec/vulkan/{ffv1_reset.comp => ffv1_enc_reset.comp.glsl} (88%)
 create mode 100644 libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl

diff --git a/libavcodec/ffv1_vulkan.h b/libavcodec/ffv1_vulkan.h
index 5dfd12c3c6..8ec50ca3f1 100644
--- a/libavcodec/ffv1_vulkan.h
+++ b/libavcodec/ffv1_vulkan.h
@@ -56,15 +56,4 @@ typedef struct FFv1ShaderParams {
     int micro_version;
 } FFv1ShaderParams;
 
-typedef struct FFv1VkResetParameters {
-    uint32_t context_count[MAX_QUANT_TABLES];
-    VkDeviceAddress slice_state;
-    uint32_t plane_state_size;
-    uint8_t codec_planes;
-    uint8_t key_frame;
-    uint8_t version;
-    uint8_t micro_version;
-    uint8_t padding[1];
-} FFv1VkResetParameters;
-
 #endif /* AVCODEC_FFV1_VULKAN_H */
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 933f2e6609..8eac6d47eb 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -110,10 +110,15 @@ extern const char *ff_source_common_comp;
 extern const char *ff_source_rangecoder_comp;
 extern const char *ff_source_ffv1_vlc_comp;
 extern const char *ff_source_ffv1_common_comp;
-extern const char *ff_source_ffv1_reset_comp;
 extern const char *ff_source_ffv1_enc_setup_comp;
 extern const char *ff_source_ffv1_enc_comp;
 
+extern const unsigned char ff_ffv1_enc_reset_comp_spv_data[];
+extern const unsigned int ff_ffv1_enc_reset_comp_spv_len;
+
+extern const unsigned char ff_ffv1_enc_reset_golomb_comp_spv_data[];
+extern const unsigned int ff_ffv1_enc_reset_golomb_comp_spv_len;
+
 extern const unsigned char ff_ffv1_enc_rct_search_comp_spv_data[];
 extern const unsigned int ff_ffv1_enc_rct_search_comp_spv_len;
 
@@ -537,25 +542,17 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     nb_buf_bar = 0;
 
     /* Run reset shader */
-    FFv1VkResetParameters pd_reset;
     ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->reset,
                                     1, 0, 0,
                                     slice_data_buf,
                                     0, slice_data_size*f->slice_count,
                                     VK_FORMAT_UNDEFINED);
-    ff_vk_exec_bind_shader(&fv->s, exec, &fv->reset);
-    pd_reset = (FFv1VkResetParameters) {
-        .slice_state = slice_data_buf->address + f->slice_count*256,
-        .plane_state_size = plane_state_size,
-        .codec_planes = f->plane_count,
-        .key_frame = f->key_frame,
-    };
-    for (int i = 0; i < f->quant_table_count; i++)
-        pd_reset.context_count[i] = f->context_count[i];
 
+    ff_vk_exec_bind_shader(&fv->s, exec, &fv->reset);
     ff_vk_shader_update_push_const(&fv->s, exec, &fv->reset,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd_reset), &pd_reset);
+                                   0, sizeof(FFv1ShaderParams), &pd);
+
     vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices,
                     f->plane_count);
 
@@ -1096,91 +1093,48 @@ fail:
     return err;
 }
 
-static int init_reset_shader(AVCodecContext *avctx, FFVkSPIRVCompiler *spv)
+static int init_reset_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 {
     int err;
     VulkanEncodeFFv1Context *fv = avctx->priv_data;
-    FFV1Context *f = &fv->ctx;
     FFVulkanShader *shd = &fv->reset;
-    FFVulkanDescriptorSetBinding *desc_set;
 
-    uint8_t *spv_data;
-    size_t spv_len;
-    void *spv_opaque = NULL;
     int wg_dim = FFMIN(fv->s.props.properties.limits.maxComputeWorkGroupSize[0], 1024);
 
-    RET(ff_vk_shader_init(&fv->s, shd, "ffv1_reset",
-                          VK_SHADER_STAGE_COMPUTE_BIT,
-                          (const char *[]) { "GL_EXT_buffer_reference",
-                                             "GL_EXT_buffer_reference2" }, 2,
-                          wg_dim, 1, 1,
-                          0));
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
+                      (uint32_t []) { wg_dim, 1, 1 }, 0);
 
-    /* Common codec header */
-    GLSLD(ff_source_common_comp);
-
-    GLSLC(0, layout(push_constant, scalar) uniform pushConstants {             );
-    GLSLF(1,    uint context_count[%i];                                        ,MAX_QUANT_TABLES);
-    GLSLC(1,    u8buf slice_state;                                             );
-    GLSLC(1,    uint plane_state_size;                                         );
-    GLSLC(1,    uint8_t codec_planes;                                          );
-    GLSLC(1,    uint8_t key_frame;                                             );
-    GLSLC(1,    uint8_t version;                                               );
-    GLSLC(1,    uint8_t micro_version;                                         );
-    GLSLC(1,    uint8_t padding[1];                                            );
-    GLSLC(0, };                                                                );
-    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1VkResetParameters),
+    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLES %i\n", MAX_QUANT_TABLES);
-    av_bprintf(&shd->src, "#define MAX_CONTEXT_INPUTS %i\n", MAX_CONTEXT_INPUTS);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_SIZE %i\n", MAX_QUANT_TABLE_SIZE);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "rangecoder_static_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint8_t zero_one_state[512];",
-        },
-        {
-            .name        = "quant_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "int16_t quant_table[MAX_QUANT_TABLES]"
-                           "[MAX_CONTEXT_INPUTS][MAX_QUANT_TABLE_SIZE];",
+    const FFVulkanDescriptorSetBinding desc_set_const[] = {
+        { /* rangecoder_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 2, 1, 0));
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set_const, 1, 1, 0);
 
-    define_shared_code(avctx, shd);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "slice_data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .mem_quali   = "readonly",
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .buf_content = "SliceContext slice_ctx",
-            .buf_elems   = f->max_slice_count,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* slice_data_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 1, 0, 0));
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 1, 0, 0);
 
-    GLSLD(ff_source_ffv1_reset_comp);
-
-    RET(spv->compile_shader(&fv->s, spv, shd, &spv_data, &spv_len, "main",
-                            &spv_opaque));
-    RET(ff_vk_shader_link(&fv->s, shd, spv_data, spv_len, "main"));
+    if (fv->ctx.ac == AC_GOLOMB_RICE)
+        RET(ff_vk_shader_link(&fv->s, shd,
+                              ff_ffv1_enc_reset_golomb_comp_spv_data,
+                              ff_ffv1_enc_reset_golomb_comp_spv_len, "main"));
+    else
+        RET(ff_vk_shader_link(&fv->s, shd,
+                              ff_ffv1_enc_reset_comp_spv_data,
+                              ff_ffv1_enc_reset_comp_spv_len, "main"));
 
     RET(ff_vk_shader_register_exec(&fv->s, &fv->exec_pool, shd));
 
 fail:
-    if (spv_opaque)
-        spv->free_shader(spv, &spv_opaque);
-
     return err;
 }
 
@@ -1539,7 +1493,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     }
 
     /* Init reset shader */
-    err = init_reset_shader(avctx, spv);
+    err = init_reset_shader(avctx, sl);
     if (err < 0) {
         spv->uninit(&spv);
         return err;
diff --git a/libavcodec/vulkan/Makefile b/libavcodec/vulkan/Makefile
index 79b5facc51..72cfeb7fd2 100644
--- a/libavcodec/vulkan/Makefile
+++ b/libavcodec/vulkan/Makefile
@@ -3,10 +3,12 @@ clean::
 
 OBJS-$(CONFIG_FFV1_VULKAN_ENCODER)  +=  vulkan/common.o \
 					vulkan/rangecoder.o vulkan/ffv1_vlc.o \
-					vulkan/ffv1_common.o vulkan/ffv1_reset.o \
+					vulkan/ffv1_common.o \
 					vulkan/ffv1_enc_setup.o vulkan/ffv1_enc.o
 
-OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_rct_search.comp.spv.o
+OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_reset.comp.spv.o \
+                                      vulkan/ffv1_enc_reset_golomb.comp.spv.o \
+                                      vulkan/ffv1_enc_rct_search.comp.spv.o
 
 OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL) += vulkan/ffv1_dec_setup.comp.spv.o \
                                       vulkan/ffv1_dec_reset.comp.spv.o \
diff --git a/libavcodec/vulkan/ffv1_reset.comp b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
similarity index 88%
rename from libavcodec/vulkan/ffv1_reset.comp
rename to libavcodec/vulkan/ffv1_enc_reset.comp.glsl
index cfb7dcc444..e157923d0a 100644
--- a/libavcodec/vulkan/ffv1_reset.comp
+++ b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
@@ -20,12 +20,17 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#include "common.comp"
+#include "ffv1_common.glsl"
+
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
 
-    if (key_frame == 0 &&
-        slice_ctx[slice_idx].slice_reset_contexts == false)
+    if (!key_frame && !slice_ctx[slice_idx].slice_reset_contexts)
         return;
 
     const uint8_t qidx = slice_ctx[slice_idx].quant_table_idx[gl_WorkGroupID.z];
@@ -35,7 +40,8 @@ void main(void)
 
 #ifdef GOLOMB
     uint64_t start = slice_state_off +
-                     (gl_WorkGroupID.z*(plane_state_size/VLC_STATE_SIZE) + gl_LocalInvocationID.x)*VLC_STATE_SIZE;
+                     (gl_WorkGroupID.z*(plane_state_size/VLC_STATE_SIZE) +
+                      gl_LocalInvocationID.x)*VLC_STATE_SIZE;
     for (uint x = gl_LocalInvocationID.x; x < contexts; x += gl_WorkGroupSize.x) {
         VlcState sb = VlcState(start);
         sb.drift     =  int16_t(0);
diff --git a/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl
new file mode 100644
index 0000000000..277f88c6c3
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl
@@ -0,0 +1,27 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define GOLOMB
+#include "ffv1_enc_reset.comp.glsl"
-- 
2.52.0


>From 756a9768bf677b6cb562433872585a63732ae07d Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 13:48:10 +0100
Subject: [PATCH 18/58] ffv1enc_vulkan: convert setup shader to compile-time
 SPIR-V generation

---
 libavcodec/ffv1_vulkan.h                      |   5 +
 libavcodec/ffv1enc_vulkan.c                   | 126 ++++++------------
 libavcodec/vulkan/Makefile                    |   5 +-
 libavcodec/vulkan/ffv1_common.glsl            |   7 +
 ...nc_setup.comp => ffv1_enc_setup.comp.glsl} |  16 ++-
 5 files changed, 69 insertions(+), 90 deletions(-)
 rename libavcodec/vulkan/{ffv1_enc_setup.comp => ffv1_enc_setup.comp.glsl} (89%)

diff --git a/libavcodec/ffv1_vulkan.h b/libavcodec/ffv1_vulkan.h
index 8ec50ca3f1..fcd6f98532 100644
--- a/libavcodec/ffv1_vulkan.h
+++ b/libavcodec/ffv1_vulkan.h
@@ -54,6 +54,11 @@ typedef struct FFv1ShaderParams {
     uint32_t key_frame;
     uint32_t crcref;
     int micro_version;
+
+    /* Encoder-only */
+    int sar[2];
+    int pic_mode;
+    uint32_t slice_size_max;
 } FFv1ShaderParams;
 
 #endif /* AVCODEC_FFV1_VULKAN_H */
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 8eac6d47eb..2f7c217f09 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -110,9 +110,11 @@ extern const char *ff_source_common_comp;
 extern const char *ff_source_rangecoder_comp;
 extern const char *ff_source_ffv1_vlc_comp;
 extern const char *ff_source_ffv1_common_comp;
-extern const char *ff_source_ffv1_enc_setup_comp;
 extern const char *ff_source_ffv1_enc_comp;
 
+extern const unsigned char ff_ffv1_enc_setup_comp_spv_data[];
+extern const unsigned int ff_ffv1_enc_setup_comp_spv_len;
+
 extern const unsigned char ff_ffv1_enc_reset_comp_spv_data[];
 extern const unsigned int ff_ffv1_enc_reset_comp_spv_len;
 
@@ -388,6 +390,12 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
         .key_frame = f->key_frame,
         .crcref = f->crcref,
         .micro_version = f->micro_version,
+
+        .sar[0] = pict->sample_aspect_ratio.num,
+        .sar[1] = pict->sample_aspect_ratio.den,
+        .pic_mode = !(pict->flags & AV_FRAME_FLAG_INTERLACED) ? 3 :
+                    !(pict->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 2 : 1,
+        .slice_size_max = out_data_buf->size / f->slice_count,
     };
 
     for (int i = 0; i < f->quant_table_count; i++) {
@@ -404,18 +412,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     else
         ff_vk_set_perm(avctx->sw_pix_fmt, pd.fmt_lut, 1);
 
-    /* Setup shader */
-    ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->setup,
-                                    1, 0, 0,
-                                    slice_data_buf,
-                                    0, slice_data_size*f->slice_count,
-                                    VK_FORMAT_UNDEFINED);
-    ff_vk_shader_update_img_array(&fv->s, exec, &fv->setup,
-                                  src, src_views,
-                                  1, 1,
-                                  VK_IMAGE_LAYOUT_GENERAL,
-                                  VK_NULL_HANDLE);
-
     /* Add a buffer barrier between previous and current frame */
     if (!f->key_frame)
         ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
@@ -456,7 +452,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     }
 
     /* Run setup shader */
-    ff_vk_exec_bind_shader(&fv->s, exec, &fv->setup);
     pd_old = (FFv1VkParameters) {
         .slice_state = slice_data_buf->address + f->slice_count*256,
         .out_data = out_data_buf->address,
@@ -501,9 +496,19 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     for (int i = 0; i < f->quant_table_count; i++)
         pd_old.extend_lookup[i] = (f->quant_tables[i][3][127] != 0) ||
                                   (f->quant_tables[i][4][127] != 0);
+
+    /* Setup shader */
+    ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->setup,
+                                    1, 0, 0,
+                                    slice_data_buf,
+                                    0, slice_data_size*f->slice_count,
+                                    VK_FORMAT_UNDEFINED);
+
+    ff_vk_exec_bind_shader(&fv->s, exec, &fv->setup);
     ff_vk_shader_update_push_const(&fv->s, exec, &fv->setup,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd_old), &pd_old);
+                                   0, sizeof(FFv1ShaderParams), &pd);
+
     vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices, 1);
 
     /* Clean up temporary image */
@@ -1008,88 +1013,41 @@ fail:
     return err;
 }
 
-static int init_setup_shader(AVCodecContext *avctx, FFVkSPIRVCompiler *spv)
+static int init_setup_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 {
     int err;
     VulkanEncodeFFv1Context *fv = avctx->priv_data;
-    FFV1Context *f = &fv->ctx;
     FFVulkanShader *shd = &fv->setup;
-    FFVulkanDescriptorSetBinding *desc_set;
 
-    uint8_t *spv_data;
-    size_t spv_len;
-    void *spv_opaque = NULL;
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
+                      (uint32_t []) { 1, 1, 1 }, 0);
 
-    RET(ff_vk_shader_init(&fv->s, shd, "ffv1_setup",
-                          VK_SHADER_STAGE_COMPUTE_BIT,
-                          (const char *[]) { "GL_EXT_buffer_reference",
-                                             "GL_EXT_buffer_reference2" }, 2,
-                          1, 1, 1,
-                          0));
+    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
+                                VK_SHADER_STAGE_COMPUTE_BIT);
 
-    /* Common codec header */
-    GLSLD(ff_source_common_comp);
-    add_push_data(shd);
-
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLES %i\n", MAX_QUANT_TABLES);
-    av_bprintf(&shd->src, "#define MAX_CONTEXT_INPUTS %i\n", MAX_CONTEXT_INPUTS);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_SIZE %i\n", MAX_QUANT_TABLE_SIZE);
-    av_bprintf(&shd->src, "#define FULL_RENORM\n");
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "rangecoder_static_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint8_t zero_one_state[512];",
-        },
-        { /* This descriptor is never used */
-            .name        = "quant_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "int16_t quant_table[MAX_QUANT_TABLES]"
-                           "[MAX_CONTEXT_INPUTS][MAX_QUANT_TABLE_SIZE];",
+    const FFVulkanDescriptorSetBinding desc_set_const[] = {
+        { /* rangecoder_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 2, 1, 0));
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set_const, 1, 1, 0);
 
-    define_shared_code(avctx, shd);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "slice_data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .buf_content = "SliceContext slice_ctx",
-            .buf_elems   = f->max_slice_count,
-        },
-        {
-            .name       = "src",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .dimensions = 2,
-            .mem_layout = ff_vk_shader_rep_fmt(fv->s.frames->sw_format,
-                                               FF_VK_REP_NATIVE),
-            .elems      = av_pix_fmt_count_planes(fv->s.frames->sw_format),
-            .mem_quali  = "readonly",
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* slice_data_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    RET(ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 2, 0, 0));
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 1, 0, 0);
 
-    GLSLD(ff_source_ffv1_enc_setup_comp);
-
-    RET(spv->compile_shader(&fv->s, spv, shd, &spv_data, &spv_len, "main",
-                            &spv_opaque));
-    RET(ff_vk_shader_link(&fv->s, shd, spv_data, spv_len, "main"));
+    RET(ff_vk_shader_link(&fv->s, shd,
+                          ff_ffv1_enc_setup_comp_spv_data,
+                          ff_ffv1_enc_setup_comp_spv_len, "main"));
 
     RET(ff_vk_shader_register_exec(&fv->s, &fv->exec_pool, shd));
 
 fail:
-    if (spv_opaque)
-        spv->free_shader(spv, &spv_opaque);
-
     return err;
 }
 
@@ -1473,9 +1431,11 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
                        !fv->force_pcm && fv->optimize_rct;
 
     /* Init shader specialization consts */
-    SPEC_LIST_CREATE(sl, 16, 16*sizeof(uint32_t))
+    SPEC_LIST_CREATE(sl, 18, 18*sizeof(uint32_t))
     ff_ffv1_vk_set_common_sl(avctx, f, sl, fv->s.frames->sw_format);
     SPEC_LIST_ADD(sl, 15, 32, fv->force_pcm);
+    SPEC_LIST_ADD(sl, 16, 32, fv->optimize_rct);
+    SPEC_LIST_ADD(sl, 17, 32, f->context_model);
 
     if (fv->optimize_rct) {
         err = init_rct_search_shader(avctx, sl);
@@ -1486,7 +1446,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     }
 
     /* Init setup shader */
-    err = init_setup_shader(avctx, spv);
+    err = init_setup_shader(avctx, sl);
     if (err < 0) {
         spv->uninit(&spv);
         return err;
@@ -1544,7 +1504,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     RET(ff_vk_shader_update_desc_buffer(&fv->s, &fv->exec_pool.contexts[0],
                                         &fv->setup, 0, 0, 0,
                                         &fv->rangecoder_static_buf,
-                                        0, fv->rangecoder_static_buf.size,
+                                        0, 512*sizeof(uint8_t),
                                         VK_FORMAT_UNDEFINED));
 
     /* Update encode global descriptors */
diff --git a/libavcodec/vulkan/Makefile b/libavcodec/vulkan/Makefile
index 72cfeb7fd2..0899632528 100644
--- a/libavcodec/vulkan/Makefile
+++ b/libavcodec/vulkan/Makefile
@@ -4,9 +4,10 @@ clean::
 OBJS-$(CONFIG_FFV1_VULKAN_ENCODER)  +=  vulkan/common.o \
 					vulkan/rangecoder.o vulkan/ffv1_vlc.o \
 					vulkan/ffv1_common.o \
-					vulkan/ffv1_enc_setup.o vulkan/ffv1_enc.o
+					vulkan/ffv1_enc.o
 
-OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_reset.comp.spv.o \
+OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_setup.comp.spv.o \
+                                      vulkan/ffv1_enc_reset.comp.spv.o \
                                       vulkan/ffv1_enc_reset_golomb.comp.spv.o \
                                       vulkan/ffv1_enc_rct_search.comp.spv.o
 
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index e835cc7c9f..3c13ab2c6a 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -54,6 +54,8 @@ const ivec2 chroma_shift = ivec2(chroma_shift_x, chroma_shift_y);
 
 /* Encoder-only */
 layout (constant_id = 15) const bool force_pcm = false;
+layout (constant_id = 16) const bool rct_search = false;
+layout (constant_id = 17) const bool context_model = false;
 
 layout (push_constant, scalar) uniform pushConstants {
     u8buf slice_data;
@@ -69,6 +71,11 @@ layout (push_constant, scalar) uniform pushConstants {
     bool key_frame;
     uint32_t crcref;
     int micro_version;
+
+    /* Encoder-only */
+    ivec2 sar;
+    int pic_mode;
+    uint slice_size_max;
 };
 
 #define TYPE int32_t
diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
similarity index 89%
rename from libavcodec/vulkan/ffv1_enc_setup.comp
rename to libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index 5338f94360..68ec6b7b88 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -20,12 +20,18 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define FULL_RENORM
+#include "common.comp"
+#include "ffv1_common.glsl"
+
 uint8_t state[CONTEXT_SIZE];
 
-void init_slice(inout SliceContext sc, const uint slice_idx)
+void init_slice(inout SliceContext sc, uint slice_idx)
 {
     /* Set coordinates */
-    uvec2 img_size = imageSize(src[0]);
     uint sxs = slice_coord(img_size.x, gl_WorkGroupID.x + 0,
                            gl_NumWorkGroups.x, chroma_shift.x);
     uint sxe = slice_coord(img_size.x, gl_WorkGroupID.x + 1,
@@ -37,15 +43,15 @@ void init_slice(inout SliceContext sc, const uint slice_idx)
 
     sc.slice_pos = ivec2(sxs, sys);
     sc.slice_dim = ivec2(sxe - sxs, sye - sys);
-    sc.slice_coding_mode = int(force_pcm == 1);
+    sc.slice_coding_mode = int(force_pcm);
     sc.slice_reset_contexts = sc.slice_coding_mode == 1;
     sc.quant_table_idx = u8vec3(context_model);
 
-    if ((rct_search == 0) || (sc.slice_coding_mode == 1))
+    if (!rct_search || (sc.slice_coding_mode == 1))
         sc.slice_rct_coef = ivec2(1, 1);
 
     rac_init(sc.c,
-             OFFBUF(u8buf, out_data, slice_idx * slice_size_max),
+             OFFBUF(u8buf, slice_data, slice_idx * slice_size_max),
              slice_size_max);
 }
 
-- 
2.52.0


>From fcd7bac9fc24fe2dc2100cebe479e869dc646fcd Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 15:50:59 +0100
Subject: [PATCH 19/58] ffv1enc_vulkan: convert encode shader to compile-time
 SPIR-V generation

---
 configure                                     |   2 +-
 libavcodec/ffv1enc_vulkan.c                   | 419 ++++--------------
 libavcodec/vulkan/Makefile                    |   9 +-
 libavcodec/vulkan/ffv1_common.comp            | 177 --------
 .../{ffv1_enc.comp => ffv1_enc.comp.glsl}     | 167 ++++---
 libavcodec/vulkan/ffv1_enc_golomb.comp.glsl   |  27 ++
 libavcodec/vulkan/ffv1_enc_rgb.comp.glsl      |  30 ++
 .../vulkan/ffv1_enc_rgb_golomb.comp.glsl      |  27 ++
 8 files changed, 247 insertions(+), 611 deletions(-)
 delete mode 100644 libavcodec/vulkan/ffv1_common.comp
 rename libavcodec/vulkan/{ffv1_enc.comp => ffv1_enc.comp.glsl} (69%)
 create mode 100644 libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
 create mode 100644 libavcodec/vulkan/ffv1_enc_rgb.comp.glsl
 create mode 100644 libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl

diff --git a/configure b/configure
index 988a46835f..6d23497fe8 100755
--- a/configure
+++ b/configure
@@ -3105,7 +3105,7 @@ exr_decoder_select="bswapdsp"
 exr_encoder_deps="zlib"
 ffv1_decoder_select="rangecoder"
 ffv1_encoder_select="rangecoder"
-ffv1_vulkan_encoder_select="vulkan spirv_library"
+ffv1_vulkan_encoder_select="vulkan spirv_compiler"
 ffvhuff_decoder_select="huffyuv_decoder"
 ffvhuff_encoder_select="huffyuv_encoder"
 fic_decoder_select="golomb"
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 2f7c217f09..9f4570c958 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -20,7 +20,6 @@
 
 #include "libavutil/mem.h"
 #include "libavutil/vulkan.h"
-#include "libavutil/vulkan_spirv.h"
 
 #include "avcodec.h"
 #include "internal.h"
@@ -121,86 +120,21 @@ extern const unsigned int ff_ffv1_enc_reset_comp_spv_len;
 extern const unsigned char ff_ffv1_enc_reset_golomb_comp_spv_data[];
 extern const unsigned int ff_ffv1_enc_reset_golomb_comp_spv_len;
 
+extern const unsigned char ff_ffv1_enc_comp_spv_data[];
+extern const unsigned int ff_ffv1_enc_comp_spv_len;
+
+extern const unsigned char ff_ffv1_enc_rgb_comp_spv_data[];
+extern const unsigned int ff_ffv1_enc_rgb_comp_spv_len;
+
+extern const unsigned char ff_ffv1_enc_golomb_comp_spv_data[];
+extern const unsigned int ff_ffv1_enc_golomb_comp_spv_len;
+
+extern const unsigned char ff_ffv1_enc_rgb_golomb_comp_spv_data[];
+extern const unsigned int ff_ffv1_enc_rgb_golomb_comp_spv_len;
+
 extern const unsigned char ff_ffv1_enc_rct_search_comp_spv_data[];
 extern const unsigned int ff_ffv1_enc_rct_search_comp_spv_len;
 
-typedef struct FFv1VkParameters {
-    VkDeviceAddress slice_state;
-    VkDeviceAddress scratch_data;
-    VkDeviceAddress out_data;
-
-    int32_t fmt_lut[4];
-    int32_t sar[2];
-    uint32_t chroma_shift[2];
-
-    uint32_t plane_state_size;
-    uint32_t context_count;
-    uint32_t crcref;
-    uint32_t slice_size_max;
-    int      rct_offset;
-
-    uint8_t extend_lookup[8];
-    uint8_t bits_per_raw_sample;
-    uint8_t context_model;
-    uint8_t version;
-    uint8_t micro_version;
-    uint8_t force_pcm;
-    uint8_t key_frame;
-    uint8_t components;
-    uint8_t planes;
-    uint8_t codec_planes;
-    uint8_t planar_rgb;
-    uint8_t transparency;
-    uint8_t colorspace;
-    uint8_t pic_mode;
-    uint8_t ec;
-    uint8_t ppi;
-    uint8_t chunks;
-    uint8_t rct_search;
-    uint8_t padding[3];
-} FFv1VkParameters;
-
-static void add_push_data(FFVulkanShader *shd)
-{
-    GLSLC(0, layout(push_constant, scalar) uniform pushConstants {            );
-    GLSLC(1,    u8buf slice_state;                                            );
-    GLSLC(1,    u8buf scratch_data;                                           );
-    GLSLC(1,    u8buf out_data;                                               );
-    GLSLC(0,                                                                  );
-    GLSLC(1,    ivec4 fmt_lut;                                                );
-    GLSLC(1,    ivec2 sar;                                                    );
-    GLSLC(1,    uvec2 chroma_shift;                                           );
-    GLSLC(0,                                                                  );
-    GLSLC(1,    uint plane_state_size;                                        );
-    GLSLC(1,    uint context_count;                                           );
-    GLSLC(1,    uint32_t crcref;                                              );
-    GLSLC(1,    uint32_t slice_size_max;                                      );
-    GLSLC(1,    int rct_offset;                                               );
-    GLSLC(0,                                                                  );
-    GLSLC(1,    uint8_t extend_lookup[8];                                     );
-    GLSLC(1,    uint8_t bits_per_raw_sample;                                  );
-    GLSLC(1,    uint8_t context_model;                                        );
-    GLSLC(1,    uint8_t version;                                              );
-    GLSLC(1,    uint8_t micro_version;                                        );
-    GLSLC(1,    uint8_t force_pcm;                                            );
-    GLSLC(1,    uint8_t key_frame;                                            );
-    GLSLC(1,    uint8_t components;                                           );
-    GLSLC(1,    uint8_t planes;                                               );
-    GLSLC(1,    uint8_t codec_planes;                                         );
-    GLSLC(1,    uint8_t planar_rgb;                                           );
-    GLSLC(1,    uint8_t transparency;                                         );
-    GLSLC(1,    uint8_t colorspace;                                           );
-    GLSLC(1,    uint8_t pic_mode;                                             );
-    GLSLC(1,    uint8_t ec;                                                   );
-    GLSLC(1,    uint8_t ppi;                                                  );
-    GLSLC(1,    uint8_t chunks;                                               );
-    GLSLC(1,    uint8_t rct_search;                                           );
-    GLSLC(1,    uint8_t padding[3];                                           );
-    GLSLC(0, };                                                               );
-    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1VkParameters),
-                                VK_SHADER_STAGE_COMPUTE_BIT);
-}
-
 static int run_rct_search(AVCodecContext *avctx, FFVkExecContext *exec,
                           AVFrame *enc_in, VkImageView *enc_in_views,
                           FFVkBuffer *slice_data_buf, uint32_t slice_data_size,
@@ -242,7 +176,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     FFVulkanFunctions *vk = &fv->s.vkfn;
 
     VulkanEncodeFFv1FrameData *fd = exec->opaque;
-    FFv1VkParameters pd_old;
 
     /* Slice data */
     AVBufferRef *slice_data_ref;
@@ -260,7 +193,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
 
     int has_inter = avctx->gop_size > 1;
     uint32_t context_count = f->context_count[f->context_model];
-    const AVPixFmtDescriptor *fmt_desc = av_pix_fmt_desc_get(avctx->sw_pix_fmt);
 
     AVFrame *src = (AVFrame *)pict;
     VkImageView src_views[AV_NUM_DATA_POINTERS];
@@ -451,52 +383,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
         nb_buf_bar = 0;
     }
 
-    /* Run setup shader */
-    pd_old = (FFv1VkParameters) {
-        .slice_state = slice_data_buf->address + f->slice_count*256,
-        .out_data = out_data_buf->address,
-        .bits_per_raw_sample = f->bits_per_raw_sample,
-        .sar[0] = pict->sample_aspect_ratio.num,
-        .sar[1] = pict->sample_aspect_ratio.den,
-        .chroma_shift[0] = f->chroma_h_shift,
-        .chroma_shift[1] = f->chroma_v_shift,
-        .plane_state_size = plane_state_size,
-        .context_count = context_count,
-        .crcref = f->crcref,
-        .rct_offset = 1 << f->bits_per_raw_sample,
-        .slice_size_max = out_data_buf->size / f->slice_count,
-        .context_model = fv->ctx.context_model,
-        .version = f->version,
-        .micro_version = f->micro_version,
-        .force_pcm = fv->force_pcm,
-        .key_frame = f->key_frame,
-        .components = fmt_desc->nb_components,
-        .planes = av_pix_fmt_count_planes(avctx->sw_pix_fmt),
-        .codec_planes = f->plane_count,
-        .planar_rgb = ff_vk_mt_is_np_rgb(avctx->sw_pix_fmt) &&
-                      (ff_vk_count_images((AVVkFrame *)src->data[0]) > 1),
-        .transparency = f->transparency,
-        .colorspace = f->colorspace,
-        .pic_mode = !(pict->flags & AV_FRAME_FLAG_INTERLACED) ? 3 :
-                    !(pict->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 2 : 1,
-        .ec = f->ec,
-        .ppi = fv->ppi,
-        .chunks = fv->chunks,
-        .rct_search = fv->optimize_rct,
-    };
-
-    /* For some reason the C FFv1 encoder/decoder treats these differently */
-    if (avctx->sw_pix_fmt == AV_PIX_FMT_GBRP10 ||
-        avctx->sw_pix_fmt == AV_PIX_FMT_GBRP12 ||
-        avctx->sw_pix_fmt == AV_PIX_FMT_GBRP14)
-        memcpy(pd_old.fmt_lut, (int [4]) { 2, 1, 0, 3 }, 4*sizeof(int));
-    else
-        ff_vk_set_perm(avctx->sw_pix_fmt, pd_old.fmt_lut, 1);
-
-    for (int i = 0; i < f->quant_table_count; i++)
-        pd_old.extend_lookup[i] = (f->quant_tables[i][3][127] != 0) ||
-                                  (f->quant_tables[i][4][127] != 0);
-
     /* Setup shader */
     ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->setup,
                                     1, 0, 0,
@@ -603,16 +489,16 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
                                     slice_data_buf,
                                     0, slice_data_size*f->slice_count,
                                     VK_FORMAT_UNDEFINED);
-    ff_vk_shader_update_img_array(&fv->s, exec, &fv->enc,
-                                  src, src_views,
-                                  1, 1,
-                                  VK_IMAGE_LAYOUT_GENERAL,
-                                  VK_NULL_HANDLE);
     ff_vk_shader_update_desc_buffer(&fv->s, exec,
-                                    &fv->enc, 1, 2, 0,
+                                    &fv->enc, 1, 1, 0,
                                     results_data_buf,
                                     0, results_data_buf->size,
                                     VK_FORMAT_UNDEFINED);
+    ff_vk_shader_update_img_array(&fv->s, exec, &fv->enc,
+                                  src, src_views,
+                                  1, 2,
+                                  VK_IMAGE_LAYOUT_GENERAL,
+                                  VK_NULL_HANDLE);
     if (fv->is_rgb)
         ff_vk_shader_update_img_array(&fv->s, exec, &fv->enc,
                                       tmp, tmp_views,
@@ -623,7 +509,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     ff_vk_exec_bind_shader(&fv->s, exec, &fv->enc);
     ff_vk_shader_update_push_const(&fv->s, exec, &fv->enc,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(pd_old), &pd_old);
+                                   0, sizeof(FFv1ShaderParams), &pd);
     vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices, 1);
 
     /* Submit */
@@ -893,83 +779,6 @@ static int init_indirect(AVCodecContext *avctx, enum AVPixelFormat sw_format)
     return 0;
 }
 
-static int check_support(AVHWFramesConstraints *constraints,
-                         enum AVPixelFormat fmt)
-{
-    for (int i = 0; constraints->valid_sw_formats[i]; i++) {
-        if (constraints->valid_sw_formats[i] == fmt)
-            return 1;
-    }
-    return 0;
-}
-
-static enum AVPixelFormat get_supported_rgb_buffer_fmt(AVCodecContext *avctx)
-{
-    VulkanEncodeFFv1Context *fv = avctx->priv_data;
-
-    enum AVPixelFormat fmt;
-    AVHWFramesConstraints *constraints;
-    constraints = av_hwdevice_get_hwframe_constraints(fv->s.device_ref,
-                                                      NULL);
-
-    /* What we'd like to optimally have */
-    fmt = fv->ctx.use32bit ?
-          (fv->ctx.transparency ? AV_PIX_FMT_RGBA128 : AV_PIX_FMT_RGB96) :
-          (fv->ctx.transparency ? AV_PIX_FMT_RGBA64  : AV_PIX_FMT_RGB48);
-    if (check_support(constraints, fmt))
-        goto end;
-
-    if (fv->ctx.use32bit) {
-        if (check_support(constraints, (fmt = AV_PIX_FMT_RGBA128)))
-            goto end;
-    } else {
-        if (check_support(constraints, (fmt = AV_PIX_FMT_RGBA64)))
-            goto end;
-
-        if (!fv->ctx.transparency &&
-            check_support(constraints, (fmt = AV_PIX_FMT_RGB96)))
-                goto end;
-
-        if (check_support(constraints, (fmt = AV_PIX_FMT_RGBA128)))
-            goto end;
-    }
-
-    fmt = AV_PIX_FMT_NONE;
-
-end:
-    av_hwframe_constraints_free(&constraints);
-    return fmt;
-}
-
-static void define_shared_code(AVCodecContext *avctx, FFVulkanShader *shd)
-{
-    VulkanEncodeFFv1Context *fv = avctx->priv_data;
-    FFV1Context *f = &fv->ctx;
-    int smp_bits = fv->ctx.use32bit ? 32 : 16;
-
-    av_bprintf(&shd->src, "#define RGB_LINECACHE %i\n"                   ,RGB_LINECACHE);
-    av_bprintf(&shd->src, "#define CONTEXT_SIZE %i\n"                    ,CONTEXT_SIZE);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_MASK 0x%x\n"          ,MAX_QUANT_TABLE_MASK);
-
-    if (f->ac == AC_GOLOMB_RICE) {
-        av_bprintf(&shd->src, "#define PB_UNALIGNED\n"                   );
-        av_bprintf(&shd->src, "#define GOLOMB\n"                         );
-    }
-
-    if (fv->is_rgb)
-        av_bprintf(&shd->src, "#define RGB\n");
-
-    GLSLF(0, #define TYPE int%i_t                                        ,smp_bits);
-    GLSLF(0, #define VTYPE2 i%ivec2                                      ,smp_bits);
-    GLSLF(0, #define VTYPE3 i%ivec3                                      ,smp_bits);
-    GLSLD(ff_source_rangecoder_comp);
-
-    if (f->ac == AC_GOLOMB_RICE)
-        GLSLD(ff_source_ffv1_vlc_comp);
-
-    GLSLD(ff_source_ffv1_common_comp);
-}
-
 static int init_rct_search_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 {
     int err;
@@ -1096,123 +905,78 @@ fail:
     return err;
 }
 
-static int init_encode_shader(AVCodecContext *avctx, FFVkSPIRVCompiler *spv)
+static int init_encode_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 {
     int err;
     VulkanEncodeFFv1Context *fv = avctx->priv_data;
-    FFV1Context *f = &fv->ctx;
     FFVulkanShader *shd = &fv->enc;
-    FFVulkanDescriptorSetBinding *desc_set;
 
-    uint8_t *spv_data;
-    size_t spv_len;
-    void *spv_opaque = NULL;
-    int use_cached_reader = fv->ctx.ac != AC_GOLOMB_RICE &&
-                            fv->s.driver_props.driverID == VK_DRIVER_ID_MESA_RADV;
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
+                      (uint32_t []) { 1, 1, 1 }, 0);
 
-    RET(ff_vk_shader_init(&fv->s, shd, "ffv1_enc",
-                          VK_SHADER_STAGE_COMPUTE_BIT,
-                          (const char *[]) { "GL_EXT_buffer_reference",
-                                             "GL_EXT_buffer_reference2" }, 2,
-                          use_cached_reader ? CONTEXT_SIZE : 1, 1, 1,
-                          0));
+    ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
+                                VK_SHADER_STAGE_COMPUTE_BIT);
 
-    /* Common codec header */
-    GLSLD(ff_source_common_comp);
-
-    add_push_data(shd);
-
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLES %i\n", MAX_QUANT_TABLES);
-    av_bprintf(&shd->src, "#define MAX_CONTEXT_INPUTS %i\n", MAX_CONTEXT_INPUTS);
-    av_bprintf(&shd->src, "#define MAX_QUANT_TABLE_SIZE %i\n", MAX_QUANT_TABLE_SIZE);
-
-    if (use_cached_reader)
-        av_bprintf(&shd->src, "#define CACHED_SYMBOL_READER 1\n");
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "rangecoder_static_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint8_t zero_one_state[512];",
+    const FFVulkanDescriptorSetBinding desc_set_const[] = {
+        { /* rangecoder_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "quant_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "int16_t quant_table[MAX_QUANT_TABLES]"
-                           "[MAX_CONTEXT_INPUTS][MAX_QUANT_TABLE_SIZE];",
+        { /* quant_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "crc_ieee_buf",
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = "uint32_t crc_ieee[256];",
+        { /* crc_ieee_buf */
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set_const, 3, 1, 0);
 
-    RET(ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 3, 1, 0));
-
-    define_shared_code(avctx, shd);
-
-    desc_set = (FFVulkanDescriptorSetBinding []) {
-        {
-            .name        = "slice_data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .buf_content = "SliceContext slice_ctx",
-            .buf_elems   = f->max_slice_count,
+    const FFVulkanDescriptorSetBinding desc_set[] = {
+        { /* slice_data_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name       = "src",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .dimensions = 2,
-            .mem_layout = ff_vk_shader_rep_fmt(fv->s.frames->sw_format,
-                                               FF_VK_REP_NATIVE),
-            .elems      = av_pix_fmt_count_planes(fv->s.frames->sw_format),
-            .mem_quali  = "readonly",
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
+        { /* slice_results_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
-        {
-            .name        = "results_data_buf",
-            .type        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_quali   = "writeonly",
-            .buf_content = "uint64_t slice_results[2048];",
+        { /* src */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems  = av_pix_fmt_count_planes(fv->s.frames->sw_format),
         },
-        { /* place holder for desc_set[3] */
-            .name       = "placeholder",
+        { /* tmp */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    if (fv->is_rgb) {
-        AVHWFramesContext *intermediate_frames_ctx;
-        intermediate_frames_ctx = (AVHWFramesContext *)fv->intermediate_frames_ref->data;
-        desc_set[3] = (FFVulkanDescriptorSetBinding) {
-            .name       = "tmp",
-            .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
-            .dimensions = 2,
-            .mem_layout = ff_vk_shader_rep_fmt(intermediate_frames_ctx->sw_format,
-                                               FF_VK_REP_NATIVE),
-            .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
-        };
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 3 + fv->is_rgb, 0, 0);
+
+    if (fv->ctx.ac == AC_GOLOMB_RICE) {
+        if (fv->is_rgb)
+            ff_vk_shader_link(&fv->s, shd,
+                              ff_ffv1_enc_rgb_golomb_comp_spv_data,
+                              ff_ffv1_enc_rgb_golomb_comp_spv_len, "main");
+        else
+            ff_vk_shader_link(&fv->s, shd,
+                              ff_ffv1_enc_golomb_comp_spv_data,
+                              ff_ffv1_enc_golomb_comp_spv_len, "main");
+    } else {
+        if (fv->is_rgb)
+            ff_vk_shader_link(&fv->s, shd,
+                              ff_ffv1_enc_rgb_comp_spv_data,
+                              ff_ffv1_enc_rgb_comp_spv_len, "main");
+        else
+            ff_vk_shader_link(&fv->s, shd,
+                              ff_ffv1_enc_comp_spv_data,
+                              ff_ffv1_enc_comp_spv_len, "main");
     }
-    RET(ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 3 + fv->is_rgb, 0, 0));
-
-    GLSLD(ff_source_ffv1_enc_comp);
-
-    RET(spv->compile_shader(&fv->s, spv, shd, &spv_data, &spv_len, "main",
-                            &spv_opaque));
-    RET(ff_vk_shader_link(&fv->s, shd, spv_data, spv_len, "main"));
 
     RET(ff_vk_shader_register_exec(&fv->s, &fv->exec_pool, shd));
 
 fail:
-    if (spv_opaque)
-        spv->free_shader(spv, &spv_opaque);
-
     return err;
 }
 
@@ -1222,7 +986,6 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     size_t maxsize, max_heap_size, max_host_size;
     VulkanEncodeFFv1Context *fv = avctx->priv_data;
     FFV1Context *f = &fv->ctx;
-    FFVkSPIRVCompiler *spv;
 
     if ((err = ff_ffv1_common_init(avctx, f)) < 0)
         return err;
@@ -1416,12 +1179,6 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     if (err < 0)
         return err;
 
-    spv = ff_vk_spirv_init();
-    if (!spv) {
-        av_log(avctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n");
-        return AVERROR_EXTERNAL;
-    }
-
     /* Detect the special RGB coding mode */
     fv->is_rgb = !(f->colorspace == 0 && avctx->sw_pix_fmt != AV_PIX_FMT_YA8) &&
                  !(avctx->sw_pix_fmt == AV_PIX_FMT_YA8);
@@ -1432,6 +1189,8 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
 
     /* Init shader specialization consts */
     SPEC_LIST_CREATE(sl, 18, 18*sizeof(uint32_t))
+    SPEC_LIST_ADD(sl,  0, 32, RGB_LINECACHE);
+    SPEC_LIST_ADD(sl,  1, 32, f->ec);
     ff_ffv1_vk_set_common_sl(avctx, f, sl, fv->s.frames->sw_format);
     SPEC_LIST_ADD(sl, 15, 32, fv->force_pcm);
     SPEC_LIST_ADD(sl, 16, 32, fv->optimize_rct);
@@ -1439,45 +1198,27 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
 
     if (fv->optimize_rct) {
         err = init_rct_search_shader(avctx, sl);
-        if (err < 0) {
-            spv->uninit(&spv);
+        if (err < 0)
             return err;
-        }
     }
 
     /* Init setup shader */
     err = init_setup_shader(avctx, sl);
-    if (err < 0) {
-        spv->uninit(&spv);
+    if (err < 0)
         return err;
-    }
 
     /* Init reset shader */
     err = init_reset_shader(avctx, sl);
-    if (err < 0) {
-        spv->uninit(&spv);
+    if (err < 0)
         return err;
-    }
 
-    if (fv->is_rgb) {
-        enum AVPixelFormat intermediate_fmt = get_supported_rgb_buffer_fmt(avctx);
-        if (intermediate_fmt == AV_PIX_FMT_NONE) {
-            av_log(avctx, AV_LOG_ERROR, "Unable to find a supported compatible "
-                                        "pixel format for RCT buffer!\n");
-            return AVERROR(ENOTSUP);
-        }
-
-        RET(init_indirect(avctx, intermediate_fmt));
-    }
+    if (fv->is_rgb)
+        RET(init_indirect(avctx, AV_PIX_FMT_RGBA128));
 
     /* Encode shader */
-    err = init_encode_shader(avctx, spv);
-    if (err < 0) {
-        spv->uninit(&spv);
+    err = init_encode_shader(avctx, sl);
+    if (err < 0)
         return err;
-    }
-
-    spv->uninit(&spv);
 
     /* Range coder data */
     err = ff_ffv1_vk_init_state_transition_data(&fv->s,
@@ -1516,12 +1257,12 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     RET(ff_vk_shader_update_desc_buffer(&fv->s, &fv->exec_pool.contexts[0],
                                         &fv->enc, 0, 1, 0,
                                         &fv->quant_buf,
-                                        0, fv->quant_buf.size,
+                                        0, VK_WHOLE_SIZE,
                                         VK_FORMAT_UNDEFINED));
     RET(ff_vk_shader_update_desc_buffer(&fv->s, &fv->exec_pool.contexts[0],
                                         &fv->enc, 0, 2, 0,
                                         &fv->crc_tab_buf,
-                                        0, fv->crc_tab_buf.size,
+                                        0, 256*sizeof(uint32_t),
                                         VK_FORMAT_UNDEFINED));
 
     /* Temporary frame */
diff --git a/libavcodec/vulkan/Makefile b/libavcodec/vulkan/Makefile
index 0899632528..8fa593df27 100644
--- a/libavcodec/vulkan/Makefile
+++ b/libavcodec/vulkan/Makefile
@@ -1,14 +1,13 @@
 clean::
 	$(RM) $(CLEANSUFFIXES:%=libavcodec/vulkan/%)
 
-OBJS-$(CONFIG_FFV1_VULKAN_ENCODER)  +=  vulkan/common.o \
-					vulkan/rangecoder.o vulkan/ffv1_vlc.o \
-					vulkan/ffv1_common.o \
-					vulkan/ffv1_enc.o
-
 OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += vulkan/ffv1_enc_setup.comp.spv.o \
                                       vulkan/ffv1_enc_reset.comp.spv.o \
                                       vulkan/ffv1_enc_reset_golomb.comp.spv.o \
+                                      vulkan/ffv1_enc.comp.spv.o \
+                                      vulkan/ffv1_enc_golomb.comp.spv.o \
+                                      vulkan/ffv1_enc_rgb.comp.spv.o \
+                                      vulkan/ffv1_enc_rgb_golomb.comp.spv.o \
                                       vulkan/ffv1_enc_rct_search.comp.spv.o
 
 OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL) += vulkan/ffv1_dec_setup.comp.spv.o \
diff --git a/libavcodec/vulkan/ffv1_common.comp b/libavcodec/vulkan/ffv1_common.comp
deleted file mode 100644
index b4bfd61217..0000000000
--- a/libavcodec/vulkan/ffv1_common.comp
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * FFv1 codec
- *
- * Copyright (c) 2024 Lynne <dev@lynne.ee>
- *
- * 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
- */
-
-struct SliceContext {
-    RangeCoder c;
-
-    ivec2 slice_dim;
-    ivec2 slice_pos;
-    ivec2 slice_rct_coef;
-    u8vec3 quant_table_idx;
-
-    uint slice_coding_mode;
-    bool slice_reset_contexts;
-};
-
-/* -1, { -1, 0 } */
-int predict(int L, ivec2 top)
-{
-    return mid_pred(L, L + top[1] - top[0], top[1]);
-}
-
-/* { -2, -1 }, { -1, 0, 1 }, 0 */
-int get_context(VTYPE2 cur_l, VTYPE3 top_l, TYPE top2, uint8_t quant_table_idx)
-{
-    const int LT = top_l[0]; /* -1 */
-    const int T  = top_l[1]; /*  0 */
-    const int RT = top_l[2]; /*  1 */
-    const int L  = cur_l[1]; /* -1 */
-
-    int base = quant_table[quant_table_idx][0][(L - LT) & MAX_QUANT_TABLE_MASK] +
-               quant_table[quant_table_idx][1][(LT - T) & MAX_QUANT_TABLE_MASK] +
-               quant_table[quant_table_idx][2][(T - RT) & MAX_QUANT_TABLE_MASK];
-
-    if ((quant_table[quant_table_idx][3][127] == 0) &&
-        (quant_table[quant_table_idx][4][127] == 0))
-        return base;
-
-    const int TT = top2;     /* -2 */
-    const int LL = cur_l[0]; /* -2 */
-    return base +
-           quant_table[quant_table_idx][3][(LL - L) & MAX_QUANT_TABLE_MASK] +
-           quant_table[quant_table_idx][4][(TT - T) & MAX_QUANT_TABLE_MASK];
-}
-
-const uint32_t log2_run[41] = {
-     0,  0,  0,  0,  1,  1,  1,  1,
-     2,  2,  2,  2,  3,  3,  3,  3,
-     4,  4,  5,  5,  6,  6,  7,  7,
-     8,  9, 10, 11, 12, 13, 14, 15,
-    16, 17, 18, 19, 20, 21, 22, 23,
-    24,
-};
-
-uint slice_coord(uint width, uint sx, uint num_h_slices, uint chroma_shift)
-{
-    uint mpw = 1 << chroma_shift;
-    uint awidth = align(width, mpw);
-
-    if ((version < 4) || ((version == 4) && (micro_version < 3)))
-        return width * sx / num_h_slices;
-
-    sx = (2 * awidth * sx + num_h_slices * mpw) / (2 * num_h_slices * mpw) * mpw;
-    if (sx == awidth)
-        sx = width;
-
-    return sx;
-}
-
-#ifdef RGB
-#define RGB_LBUF (RGB_LINECACHE - 1)
-#define LADDR(p) (ivec2((p).x, ((p).y & RGB_LBUF)))
-
-ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
-               int comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
-{
-    const ivec2 yoff_border1 = expectEXT(off.x == 0, false) ? off + ivec2(1, -1) : off;
-
-    /* Thanks to the same coincidence as below, we can skip checking if off == 0, 1 */
-    VTYPE3 top  = VTYPE3(TYPE(imageLoad(pred, sp + LADDR(yoff_border1 + ivec2(-1, -1)))[comp]),
-                         TYPE(imageLoad(pred, sp + LADDR(off + ivec2(0, -1)))[comp]),
-                         TYPE(imageLoad(pred, sp + LADDR(off + ivec2(min(1, sw - off.x - 1), -1)))[comp]));
-
-    /* Normally, we'd need to check if off != ivec2(0, 0) here, since otherwise, we must
-     * return zero. However, ivec2(-1,  0) + ivec2(1, -1) == ivec2(0, -1), e.g. previous
-     * row, 0 offset, same slice, which is zero since we zero out the buffer for RGB */
-    TYPE cur = TYPE(imageLoad(pred, sp + LADDR(yoff_border1 + ivec2(-1,  0)))[comp]);
-
-    int base = quant_table[quant_table_idx][0][(cur    - top[0]) & MAX_QUANT_TABLE_MASK] +
-               quant_table[quant_table_idx][1][(top[0] - top[1]) & MAX_QUANT_TABLE_MASK] +
-               quant_table[quant_table_idx][2][(top[1] - top[2]) & MAX_QUANT_TABLE_MASK];
-
-    if (expectEXT(extend_lookup, false)) {
-        TYPE cur2 = TYPE(0);
-        if (expectEXT(off.x > 0, true)) {
-            const ivec2 yoff_border2 = expectEXT(off.x == 1, false) ? ivec2(-1, -1) : ivec2(-2, 0);
-            cur2 = TYPE(imageLoad(pred, sp + LADDR(off + yoff_border2))[comp]);
-        }
-        base += quant_table[quant_table_idx][3][(cur2 - cur) & MAX_QUANT_TABLE_MASK];
-
-#if RGB_LINECACHE == 2
-        /* top-2 became current upon swap */
-        TYPE top2 = TYPE(imageLoad(pred, sp + LADDR(off))[comp]);
-#else
-        TYPE top2 = TYPE(imageLoad(pred, sp + LADDR(off + ivec2(0, -2)))[comp]);
-#endif
-        base += quant_table[quant_table_idx][4][(top2 - top[1]) & MAX_QUANT_TABLE_MASK];
-    }
-
-    /* context, prediction */
-    return ivec2(base, predict(cur, VTYPE2(top)));
-}
-
-#else /* RGB */
-
-#define LADDR(p) (p)
-
-ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
-               int comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
-{
-    const ivec2 yoff_border1 = off.x == 0 ? ivec2(1, -1) : ivec2(0, 0);
-    sp += off;
-
-    VTYPE3 top  = VTYPE3(TYPE(0),
-                         TYPE(0),
-                         TYPE(0));
-    if (off.y > 0 && off != ivec2(0, 1))
-        top[0] = TYPE(imageLoad(pred, sp + ivec2(-1, -1) + yoff_border1)[comp]);
-    if (off.y > 0) {
-        top[1] = TYPE(imageLoad(pred, sp + ivec2(0, -1))[comp]);
-        top[2] = TYPE(imageLoad(pred, sp + ivec2(min(1, sw - off.x - 1), -1))[comp]);
-    }
-
-    TYPE cur = TYPE(0);
-    if (off != ivec2(0, 0))
-        cur = TYPE(imageLoad(pred, sp + ivec2(-1,  0) + yoff_border1)[comp]);
-
-    int base = quant_table[quant_table_idx][0][(cur - top[0]) & MAX_QUANT_TABLE_MASK] +
-               quant_table[quant_table_idx][1][(top[0] - top[1]) & MAX_QUANT_TABLE_MASK] +
-               quant_table[quant_table_idx][2][(top[1] - top[2]) & MAX_QUANT_TABLE_MASK];
-
-    if (expectEXT(extend_lookup, false)) {
-        TYPE cur2 = TYPE(0);
-        if (off.x > 0 && off != ivec2(1, 0)) {
-            const ivec2 yoff_border2 = off.x == 1 ? ivec2(1, -1) : ivec2(0, 0);
-            cur2 = TYPE(imageLoad(pred, sp + ivec2(-2,  0) + yoff_border2)[comp]);
-        }
-        base += quant_table[quant_table_idx][3][(cur2 - cur) & MAX_QUANT_TABLE_MASK];
-
-        TYPE top2 = TYPE(0);
-        if (off.y > 1)
-            top2 = TYPE(imageLoad(pred, sp + ivec2(0, -2))[comp]);
-        base += quant_table[quant_table_idx][4][(top2 - top[1]) & MAX_QUANT_TABLE_MASK];
-    }
-
-    /* context, prediction */
-    return ivec2(base, predict(cur, VTYPE2(top)));
-}
-#endif
diff --git a/libavcodec/vulkan/ffv1_enc.comp b/libavcodec/vulkan/ffv1_enc.comp.glsl
similarity index 69%
rename from libavcodec/vulkan/ffv1_enc.comp
rename to libavcodec/vulkan/ffv1_enc.comp.glsl
index 0a997b8a79..71d5bb982c 100644
--- a/libavcodec/vulkan/ffv1_enc.comp
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -20,13 +20,24 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define ENCODE
+#include "common.comp"
+#include "ffv1_common.glsl"
+
+layout (set = 0, binding = 2, scalar) uniform crc_ieee_buf {
+    uint32_t crc_ieee[256];
+};
+
+layout (set = 1, binding = 1, scalar) writeonly buffer slice_results_buf {
+    uint64_t slice_results[];
+};
+layout (set = 1, binding = 2) uniform uimage2D src[];
+
 #ifndef GOLOMB
-#ifdef CACHED_SYMBOL_READER
-shared uint8_t state[CONTEXT_SIZE];
-#define WRITE(c, off, val) put_rac_direct(c, state[off], val)
-#else
 #define WRITE(c, off, val) put_rac(c, uint64_t(slice_state) + (state_off + off), val)
-#endif
 
 /* Note - only handles signed values */
 void put_symbol(inout RangeCoder c, uint state_off, int v)
@@ -50,15 +61,10 @@ void put_symbol(inout RangeCoder c, uint state_off, int v)
 }
 
 void encode_line_pcm(inout SliceContext sc, readonly uimage2D img,
-                     ivec2 sp, int y, int p, int comp, int bits)
+                     ivec2 sp, int y, int p, int comp)
 {
     int w = sc.slice_dim.x;
 
-#ifdef CACHED_SYMBOL_READER
-    if (gl_LocalInvocationID.x > 0)
-        return;
-#endif
-
 #ifndef RGB
     if (p > 0 && p < 3) {
         w = ceil_rshift(w, chroma_shift.x);
@@ -68,13 +74,15 @@ void encode_line_pcm(inout SliceContext sc, readonly uimage2D img,
 
     for (int x = 0; x < w; x++) {
         uint v = imageLoad(img, sp + LADDR(ivec2(x, y)))[comp];
-        for (int i = (bits - 1); i >= 0; i--)
-            put_rac_equi(sc.c, bool(bitfieldExtract(v, i, 1)));
+
+        [[unroll]]
+        for (uint i = (rct_offset >> 1); i > 0; i >>= 1)
+            put_rac_equi(sc.c, bool(v & i));
     }
 }
 
 void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
-                 ivec2 sp, int y, int p, int comp, int bits,
+                 ivec2 sp, int y, int p, int comp,
                  uint8_t quant_table_idx, const int run_index)
 {
     int w = sc.slice_dim.x;
@@ -88,7 +96,7 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
 
     for (int x = 0; x < w; x++) {
         ivec2 d = get_pred(img, sp, ivec2(x, y), comp, w,
-                           quant_table_idx, extend_lookup[quant_table_idx] > 0);
+                           quant_table_idx, extend_lookup[quant_table_idx]);
         d[1] = int(imageLoad(img, sp + LADDR(ivec2(x, y)))[comp]) - d[1];
 
         if (d[0] < 0)
@@ -97,19 +105,8 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
         d[1] = fold(d[1], bits);
 
         uint context_off = state_off + CONTEXT_SIZE*d[0];
-#ifdef CACHED_SYMBOL_READER
-        u8buf sb = u8buf(uint64_t(slice_state) + context_off + gl_LocalInvocationID.x);
-        state[gl_LocalInvocationID.x] = sb.v;
-        barrier();
-        if (gl_LocalInvocationID.x == 0)
-#endif
 
-            put_symbol(sc.c, context_off, d[1]);
-
-#ifdef CACHED_SYMBOL_READER
-        barrier();
-        sb.v = state[gl_LocalInvocationID.x];
-#endif
+        put_symbol(sc.c, context_off, d[1]);
     }
 }
 
@@ -127,7 +124,7 @@ void init_golomb(inout SliceContext sc)
 }
 
 void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
-                 ivec2 sp, int y, int p, int comp, int bits,
+                 ivec2 sp, int y, int p, int comp,
                  uint8_t quant_table_idx, inout int run_index)
 {
     int w = sc.slice_dim.x;
@@ -144,7 +141,7 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
 
     for (int x = 0; x < w; x++) {
         ivec2 d = get_pred(img, sp, ivec2(x, y), comp, w,
-                           quant_table_idx, extend_lookup[quant_table_idx] > 0);
+                           quant_table_idx, extend_lookup[quant_table_idx]);
         d[1] = int(imageLoad(img, sp + LADDR(ivec2(x, y)))[comp]) - d[1];
 
         if (d[0] < 0)
@@ -177,7 +174,8 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
         }
 
         if (!run_mode) {
-            VlcState sb = VlcState(uint64_t(slice_state) + state_off + VLC_STATE_SIZE*d[0]);
+            VlcState sb = VlcState(uint64_t(slice_state) +
+                                   state_off + VLC_STATE_SIZE*d[0]);
             Symbol sym = get_vlc_symbol(sb, d[1], bits);
             put_bits(pb, sym.bits, sym.val);
         }
@@ -200,8 +198,8 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
 ivec4 load_components(ivec2 pos)
 {
     ivec4 pix = ivec4(imageLoad(src[0], pos));
-    if (planar_rgb != 0) {
-        for (int i = 1; i < (3 + transparency); i++)
+    if (planar_rgb) {
+        for (int i = 1; i < (3 + int(transparency)); i++)
             pix[i] = int(imageLoad(src[i], pos)[0]);
     }
 
@@ -238,20 +236,14 @@ void encode_slice(inout SliceContext sc, const uint slice_idx)
 {
     ivec2 sp = sc.slice_pos;
 
-#ifndef RGB
-    int bits = bits_per_raw_sample;
-#else
-    int bits = 9;
-    if (bits != 8 || sc.slice_coding_mode != 0)
-        bits = bits_per_raw_sample + int(sc.slice_coding_mode != 1);
-
-    sp.y = int(gl_WorkGroupID.y)*RGB_LINECACHE;
+#ifdef RGB
+    sp.y = int(gl_WorkGroupID.y)*rgb_linecache;
 #endif
 
 #ifndef GOLOMB
     if (sc.slice_coding_mode == 1) {
 #ifndef RGB
-        for (int c = 0; c < components; c++) {
+        for (int c = 0; c < color_planes; c++) {
 
             int h = sc.slice_dim.y;
             if (c > 0 && c < 3)
@@ -262,66 +254,66 @@ void encode_slice(inout SliceContext sc, const uint slice_idx)
             int comp = c - p;
 
             for (int y = 0; y < h; y++)
-                encode_line_pcm(sc, src[p], sp, y, p, comp, bits);
+                encode_line_pcm(sc, src[p], sp, y, p, comp);
         }
 #else
         for (int y = 0; y < sc.slice_dim.y; y++) {
             preload_rgb(sc, sp, sc.slice_dim.x, y, false);
 
-            encode_line_pcm(sc, tmp, sp, y, 0, 1, bits);
-            encode_line_pcm(sc, tmp, sp, y, 0, 2, bits);
-            encode_line_pcm(sc, tmp, sp, y, 0, 0, bits);
-            if (transparency == 1)
-                encode_line_pcm(sc, tmp, sp, y, 0, 3, bits);
+            encode_line_pcm(sc, tmp, sp, y, 0, 1);
+            encode_line_pcm(sc, tmp, sp, y, 0, 2);
+            encode_line_pcm(sc, tmp, sp, y, 0, 0);
+            if (transparency)
+                encode_line_pcm(sc, tmp, sp, y, 0, 3);
         }
 #endif
-    } else
+        return;
+    }
+#endif
+
+    u8vec4 quant_table_idx = sc.quant_table_idx.xyyz;
+    u32vec4 slice_state_off = (slice_idx*codec_planes +
+                               uvec4(0, 1, 1, 2))*plane_state_size;
+
+#ifdef GOLOMB
+    init_golomb(slice_ctx[slice_idx]);
 #endif
-    {
-        u8vec4 quant_table_idx = sc.quant_table_idx.xyyz;
-        u32vec4 slice_state_off = (slice_idx*codec_planes + uvec4(0, 1, 1, 2))*plane_state_size;
 
 #ifndef RGB
-        for (int c = 0; c < components; c++) {
-            int run_index = 0;
-
-            int h = sc.slice_dim.y;
-            if (c > 0 && c < 3)
-                h = ceil_rshift(h, chroma_shift.y);
-
-            int p = min(c, planes - 1);
-            int comp = c - p;
-
-            for (int y = 0; y < h; y++)
-                encode_line(sc, src[p], slice_state_off[c], sp, y, p,
-                            comp, bits, quant_table_idx[c], run_index);
-        }
-#else
+    for (int c = 0; c < color_planes; c++) {
         int run_index = 0;
-        for (int y = 0; y < sc.slice_dim.y; y++) {
-            preload_rgb(sc, sp, sc.slice_dim.x, y, true);
 
-            encode_line(sc, tmp, slice_state_off[0],
-                        sp, y, 0, 1, bits, quant_table_idx[0], run_index);
-            encode_line(sc, tmp, slice_state_off[1],
-                        sp, y, 0, 2, bits, quant_table_idx[1], run_index);
-            encode_line(sc, tmp, slice_state_off[2],
-                        sp, y, 0, 0, bits, quant_table_idx[2], run_index);
-            if (transparency == 1)
-                encode_line(sc, tmp, slice_state_off[3],
-                            sp, y, 0, 3, bits, quant_table_idx[3], run_index);
-        }
-#endif
+        int h = sc.slice_dim.y;
+        if (c > 0 && c < 3)
+            h = ceil_rshift(h, chroma_shift.y);
+
+        int p = min(c, planes - 1);
+        int comp = c - p;
+
+        for (int y = 0; y < h; y++)
+            encode_line(sc, src[p], slice_state_off[c], sp, y, p,
+                        comp, quant_table_idx[c], run_index);
     }
+#else
+    int run_index = 0;
+    for (int y = 0; y < sc.slice_dim.y; y++) {
+        preload_rgb(sc, sp, sc.slice_dim.x, y, true);
+
+        encode_line(sc, tmp, slice_state_off[0],
+                    sp, y, 0, 1, quant_table_idx[0], run_index);
+        encode_line(sc, tmp, slice_state_off[1],
+                    sp, y, 0, 2, quant_table_idx[1], run_index);
+        encode_line(sc, tmp, slice_state_off[2],
+                    sp, y, 0, 0, quant_table_idx[2], run_index);
+        if (transparency)
+            encode_line(sc, tmp, slice_state_off[3],
+                        sp, y, 0, 3, quant_table_idx[3], run_index);
+    }
+#endif
 }
 
 void finalize_slice(inout SliceContext sc, const uint slice_idx)
 {
-#ifdef CACHED_SYMBOL_READER
-    if (gl_LocalInvocationID.x > 0)
-        return;
-#endif
-
 #ifdef GOLOMB
     uint32_t enc_len = hdr_len + flush_put_bits(pb);
 #else
@@ -338,7 +330,7 @@ void finalize_slice(inout SliceContext sc, const uint slice_idx)
     enc_len += 3;
 
     /* Calculate and write CRC */
-    if (ec != 0) {
+    if (has_crc) {
         bs[enc_len].v = uint8_t(0);
         enc_len++;
 
@@ -358,15 +350,12 @@ void finalize_slice(inout SliceContext sc, const uint slice_idx)
     }
 
     slice_results[slice_idx*2 + 0] = enc_len;
-    slice_results[slice_idx*2 + 1] = uint64_t(bs) - uint64_t(out_data);
+    slice_results[slice_idx*2 + 1] = uint64_t(bs) - uint64_t(slice_data);
 }
 
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
-#ifdef GOLOMB
-    init_golomb(slice_ctx[slice_idx]);
-#endif
     encode_slice(slice_ctx[slice_idx], slice_idx);
     finalize_slice(slice_ctx[slice_idx], slice_idx);
 }
diff --git a/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
new file mode 100644
index 0000000000..a120564602
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
@@ -0,0 +1,27 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define GOLOMB
+#include "ffv1_enc.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_rgb.comp.glsl b/libavcodec/vulkan/ffv1_enc_rgb.comp.glsl
new file mode 100644
index 0000000000..24c79c222e
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_enc_rgb.comp.glsl
@@ -0,0 +1,30 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+#extension GL_EXT_shader_image_load_formatted : require
+
+layout (set = 1, binding = 3) uniform uimage2D tmp;
+
+#define RGB
+#include "ffv1_enc.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
new file mode 100644
index 0000000000..8efffd19e8
--- /dev/null
+++ b/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
@@ -0,0 +1,27 @@
+/*
+ * FFv1 codec
+ *
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * 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
+ */
+
+#pragma shader_stage(compute)
+#extension GL_GOOGLE_include_directive : require
+
+#define GOLOMB
+#include "ffv1_enc_rgb.comp.glsl"
-- 
2.52.0


>From c6d79884252305de62ab820a3836bcd553f8e60b Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 17:32:29 +0100
Subject: [PATCH 20/58] avcodec/vulkan: standardize on .glsl extension

None of the files are strictly compute now.
---
 libavcodec/vulkan/{common.comp => common.glsl}         | 0
 libavcodec/vulkan/dpx_copy.comp.glsl                   | 2 +-
 libavcodec/vulkan/dpx_unpack.comp.glsl                 | 2 +-
 libavcodec/vulkan/ffv1_common.glsl                     | 4 ++--
 libavcodec/vulkan/ffv1_dec.comp.glsl                   | 2 +-
 libavcodec/vulkan/ffv1_dec_reset.comp.glsl             | 2 +-
 libavcodec/vulkan/ffv1_dec_setup.comp.glsl             | 2 +-
 libavcodec/vulkan/ffv1_enc.comp.glsl                   | 2 +-
 libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl        | 2 +-
 libavcodec/vulkan/ffv1_enc_reset.comp.glsl             | 2 +-
 libavcodec/vulkan/ffv1_enc_setup.comp.glsl             | 2 +-
 libavcodec/vulkan/{ffv1_vlc.comp => ffv1_vlc.glsl}     | 5 +++++
 libavcodec/vulkan/prores_idct.comp.glsl                | 2 +-
 libavcodec/vulkan/prores_raw_decode.comp.glsl          | 2 +-
 libavcodec/vulkan/prores_raw_idct.comp.glsl            | 2 +-
 libavcodec/vulkan/prores_vld.comp.glsl                 | 2 +-
 libavcodec/vulkan/{rangecoder.comp => rangecoder.glsl} | 7 +++++--
 17 files changed, 25 insertions(+), 17 deletions(-)
 rename libavcodec/vulkan/{common.comp => common.glsl} (100%)
 rename libavcodec/vulkan/{ffv1_vlc.comp => ffv1_vlc.glsl} (97%)
 rename libavcodec/vulkan/{rangecoder.comp => rangecoder.glsl} (98%)

diff --git a/libavcodec/vulkan/common.comp b/libavcodec/vulkan/common.glsl
similarity index 100%
rename from libavcodec/vulkan/common.comp
rename to libavcodec/vulkan/common.glsl
diff --git a/libavcodec/vulkan/dpx_copy.comp.glsl b/libavcodec/vulkan/dpx_copy.comp.glsl
index f83b03d6a0..124caea494 100644
--- a/libavcodec/vulkan/dpx_copy.comp.glsl
+++ b/libavcodec/vulkan/dpx_copy.comp.glsl
@@ -21,7 +21,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#include "common.comp"
+#include "common.glsl"
 
 layout (constant_id = 0) const bool big_endian = false;
 layout (constant_id = 1) const int type_bits = 0;
diff --git a/libavcodec/vulkan/dpx_unpack.comp.glsl b/libavcodec/vulkan/dpx_unpack.comp.glsl
index ed6959a904..e169ac07aa 100644
--- a/libavcodec/vulkan/dpx_unpack.comp.glsl
+++ b/libavcodec/vulkan/dpx_unpack.comp.glsl
@@ -21,7 +21,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#include "common.comp"
+#include "common.glsl"
 
 layout (constant_id = 0) const bool big_endian = false;
 layout (constant_id = 1) const bool packed_10bit = false;
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 3c13ab2c6a..375ce3a946 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -23,9 +23,9 @@
 #ifndef VULKAN_FFV1_COMMON_H
 #define VULKAN_FFV1_COMMON_H
 
-#include "rangecoder.comp"
+#include "rangecoder.glsl"
 #ifdef GOLOMB
-#include "ffv1_vlc.comp"
+#include "ffv1_vlc.glsl"
 #endif
 
 #define MAX_QUANT_TABLES 8
diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index c5dde13868..584735f35e 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -24,7 +24,7 @@
 #extension GL_GOOGLE_include_directive : require
 
 #define DECODE
-#include "common.comp"
+#include "common.glsl"
 #include "ffv1_common.glsl"
 
 layout (set = 1, binding = 1, scalar) readonly buffer slice_offsets_buf {
diff --git a/libavcodec/vulkan/ffv1_dec_reset.comp.glsl b/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
index e157923d0a..e708d03036 100644
--- a/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
@@ -23,7 +23,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#include "common.comp"
+#include "common.glsl"
 #include "ffv1_common.glsl"
 
 void main(void)
diff --git a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
index 4607a8105a..3502b4a176 100644
--- a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
@@ -23,7 +23,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#include "common.comp"
+#include "common.glsl"
 #include "ffv1_common.glsl"
 
 layout (set = 0, binding = 1, scalar) uniform crc_ieee_buf {
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index 71d5bb982c..7be01be1d9 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -24,7 +24,7 @@
 #extension GL_GOOGLE_include_directive : require
 
 #define ENCODE
-#include "common.comp"
+#include "common.glsl"
 #include "ffv1_common.glsl"
 
 layout (set = 0, binding = 2, scalar) uniform crc_ieee_buf {
diff --git a/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl b/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
index 66fc73a8e5..9c6dc2ecf3 100644
--- a/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
@@ -24,7 +24,7 @@
 #extension GL_GOOGLE_include_directive : require
 
 #define ENCODE
-#include "common.comp"
+#include "common.glsl"
 #include "ffv1_common.glsl"
 
 layout (set = 1, binding = 1) uniform uimage2D src[];
diff --git a/libavcodec/vulkan/ffv1_enc_reset.comp.glsl b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
index e157923d0a..e708d03036 100644
--- a/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
@@ -23,7 +23,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#include "common.comp"
+#include "common.glsl"
 #include "ffv1_common.glsl"
 
 void main(void)
diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index 68ec6b7b88..53b43e73fc 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -24,7 +24,7 @@
 #extension GL_GOOGLE_include_directive : require
 
 #define FULL_RENORM
-#include "common.comp"
+#include "common.glsl"
 #include "ffv1_common.glsl"
 
 uint8_t state[CONTEXT_SIZE];
diff --git a/libavcodec/vulkan/ffv1_vlc.comp b/libavcodec/vulkan/ffv1_vlc.glsl
similarity index 97%
rename from libavcodec/vulkan/ffv1_vlc.comp
rename to libavcodec/vulkan/ffv1_vlc.glsl
index 32a6ca9f37..e1c6cf66de 100644
--- a/libavcodec/vulkan/ffv1_vlc.comp
+++ b/libavcodec/vulkan/ffv1_vlc.glsl
@@ -20,6 +20,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#ifndef VULKAN_FFV1_VLC_H
+#define VULKAN_FFV1_VLC_H
+
 #define VLC_STATE_SIZE 8
 layout(buffer_reference, buffer_reference_align = VLC_STATE_SIZE) buffer VlcState {
     uint32_t error_sum;
@@ -157,3 +160,5 @@ int read_vlc_symbol(inout GetBitContext gb, inout VlcState state, int bits)
 
     return ret;
 }
+
+#endif /* VULKAN_FFV1_VLC_H */
diff --git a/libavcodec/vulkan/prores_idct.comp.glsl b/libavcodec/vulkan/prores_idct.comp.glsl
index 1da19d34e9..b514cb4c77 100644
--- a/libavcodec/vulkan/prores_idct.comp.glsl
+++ b/libavcodec/vulkan/prores_idct.comp.glsl
@@ -19,7 +19,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#include "common.comp"
+#include "common.glsl"
 #include "dct.glsl"
 
 layout (constant_id = 0) const bool interlaced = false;
diff --git a/libavcodec/vulkan/prores_raw_decode.comp.glsl b/libavcodec/vulkan/prores_raw_decode.comp.glsl
index e4b00b1440..88687da181 100644
--- a/libavcodec/vulkan/prores_raw_decode.comp.glsl
+++ b/libavcodec/vulkan/prores_raw_decode.comp.glsl
@@ -23,7 +23,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#include "common.comp"
+#include "common.glsl"
 
 struct TileData {
    ivec2 pos;
diff --git a/libavcodec/vulkan/prores_raw_idct.comp.glsl b/libavcodec/vulkan/prores_raw_idct.comp.glsl
index 5e3a2fa67c..04fb028055 100644
--- a/libavcodec/vulkan/prores_raw_idct.comp.glsl
+++ b/libavcodec/vulkan/prores_raw_idct.comp.glsl
@@ -23,7 +23,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#include "common.comp"
+#include "common.glsl"
 #include "dct.glsl"
 
 struct TileData {
diff --git a/libavcodec/vulkan/prores_vld.comp.glsl b/libavcodec/vulkan/prores_vld.comp.glsl
index 9020d46fa1..b0b17264a4 100644
--- a/libavcodec/vulkan/prores_vld.comp.glsl
+++ b/libavcodec/vulkan/prores_vld.comp.glsl
@@ -20,7 +20,7 @@
 #extension GL_GOOGLE_include_directive : require
 
 #define GET_BITS_SMEM 4
-#include "common.comp"
+#include "common.glsl"
 
 layout (constant_id = 0) const bool interlaced = false;
 
diff --git a/libavcodec/vulkan/rangecoder.comp b/libavcodec/vulkan/rangecoder.glsl
similarity index 98%
rename from libavcodec/vulkan/rangecoder.comp
rename to libavcodec/vulkan/rangecoder.glsl
index 7dffaf6c70..9d3eab5461 100644
--- a/libavcodec/vulkan/rangecoder.comp
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -20,13 +20,14 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef CONTEXT_SIZE
+#ifndef VULKAN_RANGECODER_H
+#define VULKAN_RANGECODER_H
+
 #define CONTEXT_SIZE 32
 
 layout (set = 0, binding = 0, scalar) uniform rangecoder_buf {
     uint8_t zero_one_state[512];
 };
-#endif
 
 struct RangeCoder {
     uint64_t bytestream_start;
@@ -247,3 +248,5 @@ bool get_rac_equi(inout RangeCoder c)
 {
     return get_rac_internal(c, c.range >> 1);
 }
+
+#endif /* VULKAN_RANGECODER_H */
-- 
2.52.0


>From 2823238d735ca584fff9d6f83681e0fa39dad532 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 17:01:21 +0100
Subject: [PATCH 21/58] avcodec: remove support for runtime SPIR-V compilation

Begone.
---
 configure                   |  2 +-
 libavcodec/Makefile         |  4 ----
 libavcodec/vulkan/Makefile  |  6 ------
 libavcodec/vulkan_glslang.c | 19 -------------------
 libavcodec/vulkan_shaderc.c | 19 -------------------
 5 files changed, 1 insertion(+), 49 deletions(-)
 delete mode 100644 libavcodec/vulkan_glslang.c
 delete mode 100644 libavcodec/vulkan_shaderc.c

diff --git a/configure b/configure
index 6d23497fe8..3b75e333cf 100755
--- a/configure
+++ b/configure
@@ -4264,7 +4264,7 @@ cws2fws_extralibs="zlib_extralibs"
 
 # libraries, in any order
 avcodec_deps="avutil"
-avcodec_suggest="libm stdatomic zlib spirv_library"
+avcodec_suggest="libm stdatomic zlib"
 avdevice_deps="avformat avcodec avutil"
 avdevice_suggest="libm stdatomic"
 avfilter_deps="avutil"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3d60347a19..50ac7d712b 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1320,10 +1320,6 @@ OBJS-$(HAVE_THREADS)                   += pthread.o pthread_slice.o pthread_fram
 
 OBJS-$(CONFIG_FRAME_THREAD_ENCODER)    += frame_thread_encoder.o
 
-# vulkan libs
-OBJS-$(CONFIG_LIBGLSLANG)              += vulkan_glslang.o
-OBJS-$(CONFIG_LIBSHADERC)              += vulkan_shaderc.o
-
 # Windows resource file
 SHLIBOBJS-$(HAVE_GNU_WINDRES)           += avcodecres.o
 
diff --git a/libavcodec/vulkan/Makefile b/libavcodec/vulkan/Makefile
index 8fa593df27..93133a9d2d 100644
--- a/libavcodec/vulkan/Makefile
+++ b/libavcodec/vulkan/Makefile
@@ -26,9 +26,3 @@ OBJS-$(CONFIG_PRORES_VULKAN_HWACCEL) += vulkan/prores_vld.comp.spv.o \
 
 OBJS-$(CONFIG_DPX_VULKAN_HWACCEL) += vulkan/dpx_unpack.comp.spv.o \
                                      vulkan/dpx_copy.comp.spv.o
-
-VULKAN = $(subst $(SRC_PATH)/,,$(wildcard $(SRC_PATH)/libavcodec/vulkan/*.comp))
-.SECONDARY: $(VULKAN:.comp=.c)
-libavcodec/vulkan/%.c: TAG = VULKAN
-libavcodec/vulkan/%.c: $(SRC_PATH)/libavcodec/vulkan/%.comp
-	$(M)$(SRC_PATH)/tools/source2c $< $@
diff --git a/libavcodec/vulkan_glslang.c b/libavcodec/vulkan_glslang.c
deleted file mode 100644
index 9aa41567a3..0000000000
--- a/libavcodec/vulkan_glslang.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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/vulkan_glslang.c"
diff --git a/libavcodec/vulkan_shaderc.c b/libavcodec/vulkan_shaderc.c
deleted file mode 100644
index 9f60bf4dfd..0000000000
--- a/libavcodec/vulkan_shaderc.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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/vulkan_shaderc.c"
-- 
2.52.0


>From 14ecc1cae06092ea3cc0ccd3c8d2b3151f33c3ab Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 18:18:07 +0100
Subject: [PATCH 22/58] vulkan_ffv1: use a loop to decode slice header symbols

All known drivers and implementations inline every single function.
This ends up being faster.
---
 libavcodec/vulkan/ffv1_dec_setup.comp.glsl | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
index 3502b4a176..cebff23517 100644
--- a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
@@ -37,7 +37,9 @@ layout (set = 1, binding = 2, scalar) writeonly buffer slice_status_buf {
     uint32_t slice_status[];
 };
 
-uint8_t setup_state[CONTEXT_SIZE];
+shared uint8_t setup_state[CONTEXT_SIZE];
+shared uint hdr_sym[4 + 4 + 3];
+const int nb_hdr_sym = 4 + codec_planes + 3;
 
 uint get_usymbol(inout RangeCoder c)
 {
@@ -68,10 +70,13 @@ bool decode_slice_header(inout SliceContext sc)
     for (int i = 0; i < CONTEXT_SIZE; i++)
         setup_state[i] = uint8_t(128);
 
-    uint sx = get_usymbol(sc.c);
-    uint sy = get_usymbol(sc.c);
-    uint sw = get_usymbol(sc.c) + 1;
-    uint sh = get_usymbol(sc.c) + 1;
+    for (int i = 0; i < nb_hdr_sym; i++)
+        hdr_sym[i] = get_usymbol(sc.c);
+
+    uint sx = hdr_sym[0];
+    uint sy = hdr_sym[1];
+    uint sw = hdr_sym[2] + 1;
+    uint sh = hdr_sym[3] + 1;
 
     if (sx < 0 || sy < 0 || sw <= 0 || sh <= 0 ||
         sx > (gl_NumWorkGroups.x - sw) || sy > (gl_NumWorkGroups.y - sh) ||
@@ -91,16 +96,12 @@ bool decode_slice_header(inout SliceContext sc)
     sc.slice_coding_mode = int(0);
 
     for (uint i = 0; i < codec_planes; i++) {
-        uint idx = get_usymbol(sc.c);
+        uint idx = hdr_sym[4 + i];
         if (idx >= quant_table_count)
             return true;
         sc.quant_table_idx[i] = uint8_t(idx);
     }
 
-    get_usymbol(sc.c);
-    get_usymbol(sc.c);
-    get_usymbol(sc.c);
-
     if (version >= 4) {
         sc.slice_reset_contexts = get_rac_direct(sc.c, setup_state[0]);
         sc.slice_coding_mode = get_usymbol(sc.c);
-- 
2.52.0


>From e3d4a3626b3bd16d5712052f17c993efd0a5c894 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 18:30:18 +0100
Subject: [PATCH 23/58] ffv1enc_vulkan: use a loop to write slice header
 symbols

Same as with the decoder.
---
 libavcodec/vulkan/ffv1_enc_setup.comp.glsl | 23 ++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index 53b43e73fc..900aa7432c 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -72,23 +72,30 @@ void put_usymbol(inout RangeCoder c, uint v)
         put_rac_direct(c, state[22 + min(i, 9)], bool(bitfieldExtract(v, i, 1)));
 }
 
+shared uint hdr_sym[4 + 4 + 3];
+const int nb_hdr_sym = 4 + codec_planes + 3;
+
 void write_slice_header(inout SliceContext sc)
 {
     [[unroll]]
     for (int i = 0; i < CONTEXT_SIZE; i++)
         state[i] = uint8_t(128);
 
-    put_usymbol(sc.c, gl_WorkGroupID.x);
-    put_usymbol(sc.c, gl_WorkGroupID.y);
-    put_usymbol(sc.c, 0);
-    put_usymbol(sc.c, 0);
+    hdr_sym[0] = gl_WorkGroupID.x;
+    hdr_sym[1] = gl_WorkGroupID.y;
+    hdr_sym[2] = 0;
+    hdr_sym[3] = 0;
 
+    [[unroll]]
     for (int i = 0; i < codec_planes; i++)
-        put_usymbol(sc.c, sc.quant_table_idx[i]);
+        hdr_sym[4 + i] = sc.quant_table_idx[i];
 
-    put_usymbol(sc.c, pic_mode);
-    put_usymbol(sc.c, sar.x);
-    put_usymbol(sc.c, sar.y);
+    hdr_sym[nb_hdr_sym - 3] = pic_mode;
+    hdr_sym[nb_hdr_sym - 2] = sar.x;
+    hdr_sym[nb_hdr_sym - 1] = sar.y;
+
+    for (int i = 0; i < nb_hdr_sym; i++)
+        put_usymbol(sc.c, hdr_sym[i]);
 
     if (version >= 4) {
         put_rac_direct(sc.c, state[0], sc.slice_reset_contexts);
-- 
2.52.0


>From 6a6199ab6c787c1e72cb009faeaa0606c28cd9f4 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 21:28:26 +0100
Subject: [PATCH 24/58] ffv1enc_vulkan: make reset shader independent from the
 setup shader

Allows them to run in parallel.
---
 libavcodec/ffv1enc_vulkan.c                | 24 ++++++++++++----------
 libavcodec/vulkan/ffv1_common.glsl         |  2 +-
 libavcodec/vulkan/ffv1_enc_reset.comp.glsl |  6 +-----
 3 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 9f4570c958..2a32342ea6 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -433,19 +433,21 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     nb_buf_bar = 0;
 
     /* Run reset shader */
-    ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->reset,
-                                    1, 0, 0,
-                                    slice_data_buf,
-                                    0, slice_data_size*f->slice_count,
-                                    VK_FORMAT_UNDEFINED);
+    if (f->key_frame || fv->force_pcm) {
+        ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->reset,
+                                        1, 0, 0,
+                                        slice_data_buf,
+                                        0, slice_data_size*f->slice_count,
+                                        VK_FORMAT_UNDEFINED);
 
-    ff_vk_exec_bind_shader(&fv->s, exec, &fv->reset);
-    ff_vk_shader_update_push_const(&fv->s, exec, &fv->reset,
-                                   VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(FFv1ShaderParams), &pd);
+        ff_vk_exec_bind_shader(&fv->s, exec, &fv->reset);
+        ff_vk_shader_update_push_const(&fv->s, exec, &fv->reset,
+                                       VK_SHADER_STAGE_COMPUTE_BIT,
+                                       0, sizeof(FFv1ShaderParams), &pd);
 
-    vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices,
-                    f->plane_count);
+        vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices,
+                        f->plane_count);
+    }
 
     /* Sync between reset and encode shaders */
     ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 375ce3a946..e7bc8f5e20 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -55,7 +55,7 @@ const ivec2 chroma_shift = ivec2(chroma_shift_x, chroma_shift_y);
 /* Encoder-only */
 layout (constant_id = 15) const bool force_pcm = false;
 layout (constant_id = 16) const bool rct_search = false;
-layout (constant_id = 17) const bool context_model = false;
+layout (constant_id = 17) const uint context_model = 0;
 
 layout (push_constant, scalar) uniform pushConstants {
     u8buf slice_data;
diff --git a/libavcodec/vulkan/ffv1_enc_reset.comp.glsl b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
index e708d03036..4d8637a0d3 100644
--- a/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
@@ -30,11 +30,7 @@ void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
 
-    if (!key_frame && !slice_ctx[slice_idx].slice_reset_contexts)
-        return;
-
-    const uint8_t qidx = slice_ctx[slice_idx].quant_table_idx[gl_WorkGroupID.z];
-    uint contexts = context_count[qidx];
+    uint contexts = context_count[context_model];
     uint64_t slice_state_off = uint64_t(slice_state) +
                                slice_idx*plane_state_size*codec_planes;
 
-- 
2.52.0


>From 9c5ebbc32fbd3b2d2dcfbfe4730a228754cb9d7b Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 4 Feb 2026 22:50:28 +0100
Subject: [PATCH 25/58] ffv1enc_vulkan: overhaul the synchronization

Allows for the setup and reset shaders to run in parallel.
---
 libavcodec/ffv1enc_vulkan.c | 169 +++++++++++++++++-------------------
 1 file changed, 79 insertions(+), 90 deletions(-)

diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 2a32342ea6..5a12bc2357 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -194,20 +194,11 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     int has_inter = avctx->gop_size > 1;
     uint32_t context_count = f->context_count[f->context_model];
 
-    AVFrame *src = (AVFrame *)pict;
-    VkImageView src_views[AV_NUM_DATA_POINTERS];
-
-    AVFrame *tmp = NULL;
-    VkImageView tmp_views[AV_NUM_DATA_POINTERS];
-
     VkImageMemoryBarrier2 img_bar[37];
     int nb_img_bar = 0;
     VkBufferMemoryBarrier2 buf_bar[8];
     int nb_buf_bar = 0;
 
-    /* Start recording */
-    ff_vk_exec_start(&fv->s, exec);
-
     /* Frame state */
     f->cur_enc_frame = pict;
     if (avctx->gop_size == 0 || f->picture_number % avctx->gop_size == 0) {
@@ -248,7 +239,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
             fv->keyframe_slice_data_ref = slice_data_ref;
     }
     slice_data_buf = (FFVkBuffer *)slice_data_ref->data;
-    ff_vk_exec_add_dep_buf(&fv->s, exec, &slice_data_ref, 1, has_inter);
 
     /* Allocate results buffer */
     RET(ff_vk_get_pooled_buffer(&fv->s, &fv->results_data_pool,
@@ -259,7 +249,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
                                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
     results_data_buf = (FFVkBuffer *)fd->results_data_ref->data;
-    ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->results_data_ref, 1, 1);
 
     /* Output buffer size */
     maxsize = ff_ffv1_encode_buffer_size(avctx);
@@ -277,22 +266,13 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
                                  VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT :
                                  fv->s.host_cached_flag)));
     out_data_buf = (FFVkBuffer *)fd->out_data_ref->data;
-    ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->out_data_ref, 1, 1);
 
-    /* Prepare input frame */
-    RET(ff_vk_exec_add_dep_frame(&fv->s, exec, src,
-                                 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
-                                 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
-
-    RET(ff_vk_create_imageviews(&fv->s, exec, src_views, src,
-                                FF_VK_REP_NATIVE));
-    ff_vk_frame_barrier(&fv->s, exec, src, img_bar, &nb_img_bar,
-                        VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
-                        VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
-                        VK_ACCESS_SHADER_READ_BIT,
-                        VK_IMAGE_LAYOUT_GENERAL,
-                        VK_QUEUE_FAMILY_IGNORED);
+    /* Image views */
+    AVFrame *src = (AVFrame *)pict;
+    VkImageView src_views[AV_NUM_DATA_POINTERS];
 
+    AVFrame *tmp = NULL;
+    VkImageView tmp_views[AV_NUM_DATA_POINTERS];
     if (fv->is_rgb) {
         /* Create a temporaty frame */
         tmp = av_frame_alloc();
@@ -301,13 +281,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
 
         RET(av_hwframe_get_buffer(fv->intermediate_frames_ref,
                                   tmp, 0));
-
-        RET(ff_vk_exec_add_dep_frame(&fv->s, exec, tmp,
-                                     VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
-                                     VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
-        RET(ff_vk_create_imageviews(&fv->s, exec, tmp_views,
-                                    tmp,
-                                    FF_VK_REP_NATIVE));
     }
 
     /* With everything allocated, setup push data */
@@ -344,28 +317,41 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     else
         ff_vk_set_perm(avctx->sw_pix_fmt, pd.fmt_lut, 1);
 
-    /* Add a buffer barrier between previous and current frame */
-    if (!f->key_frame)
-        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
-                          ALL_COMMANDS_BIT, NONE_KHR, NONE_KHR,
-                          COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
-                          0, slice_data_size*f->slice_count);
-    else
-        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
-                          COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
-                          COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
-                          0, slice_data_size*f->slice_count);
-    vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
-        .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
-        .pImageMemoryBarriers = img_bar,
-        .imageMemoryBarrierCount = nb_img_bar,
-        .pBufferMemoryBarriers = buf_bar,
-        .bufferMemoryBarrierCount = nb_buf_bar,
-    });
-    nb_img_bar = 0;
-    nb_buf_bar = 0;
+    /* Start recording */
+    ff_vk_exec_start(&fv->s, exec);
 
+    RET(ff_vk_create_imageviews(&fv->s, exec, src_views, src,
+                                FF_VK_REP_NATIVE));
+
+    ff_vk_exec_add_dep_buf(&fv->s, exec, &slice_data_ref, 1, has_inter);
+    ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->results_data_ref, 1, 1);
+    ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->out_data_ref, 1, 1);
+
+    RET(ff_vk_exec_add_dep_frame(&fv->s, exec, src,
+                                 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+                                 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
+    if (fv->is_rgb)
+        RET(ff_vk_exec_add_dep_frame(&fv->s, exec, tmp,
+                                     VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+                                     VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
+
+    /* Run RCT search if needed */
     if (fv->optimize_rct) {
+        /* Prepare the frame for reading */
+        ff_vk_frame_barrier(&fv->s, exec, src, img_bar, &nb_img_bar,
+                            VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+                            VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
+                            VK_ACCESS_SHADER_READ_BIT,
+                            VK_IMAGE_LAYOUT_GENERAL,
+                            VK_QUEUE_FAMILY_IGNORED);
+
+        vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+            .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+            .pImageMemoryBarriers = img_bar,
+            .imageMemoryBarrierCount = nb_img_bar,
+        });
+        nb_img_bar = 0;
+
         RET(run_rct_search(avctx, exec,
                            src, src_views,
                            slice_data_buf, slice_data_size, &pd));
@@ -397,9 +383,29 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
 
     vk->CmdDispatch(exec->buf, fv->ctx.num_h_slices, fv->ctx.num_v_slices, 1);
 
-    /* Clean up temporary image */
+    /* Clean up temporary image if needed */
     if (fv->is_rgb) {
         AVVkFrame *vkf = (AVVkFrame *)tmp->data[0];
+        vkf->layout[0] = VK_IMAGE_LAYOUT_UNDEFINED;
+        vkf->access[0] = VK_ACCESS_2_NONE;
+
+        RET(ff_vk_create_imageviews(&fv->s, exec, tmp_views,
+                                    tmp,
+                                    FF_VK_REP_NATIVE));
+
+        ff_vk_frame_barrier(&fv->s, exec, tmp, img_bar, &nb_img_bar,
+                            VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+                            VK_PIPELINE_STAGE_2_CLEAR_BIT,
+                            VK_ACCESS_2_TRANSFER_WRITE_BIT,
+                            VK_IMAGE_LAYOUT_GENERAL,
+                            VK_QUEUE_FAMILY_IGNORED);
+        vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+            .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+            .pImageMemoryBarriers = img_bar,
+            .imageMemoryBarrierCount = nb_img_bar,
+        });
+        nb_img_bar = 0;
+
         vk->CmdClearColorImage(exec->buf, vkf->img[0], VK_IMAGE_LAYOUT_GENERAL,
                                &((VkClearColorValue) { 0 }),
                                1, &((VkImageSubresourceRange) {
@@ -409,29 +415,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
                                }));
     }
 
-    /* Sync between setup and reset shaders */
-    ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
-                      COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
-                      COMPUTE_SHADER_BIT, SHADER_READ_BIT, NONE_KHR,
-                      0, slice_data_size*f->slice_count);
-    /* Prepare the probabilities */
-    if (!f->key_frame)
-        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
-                          ALL_COMMANDS_BIT, NONE_KHR, NONE_KHR,
-                          COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
-                          slice_data_size*f->slice_count, VK_WHOLE_SIZE);
-    else
-        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
-                          COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
-                          COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
-                          slice_data_size*f->slice_count, VK_WHOLE_SIZE);
-    vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
-        .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
-        .pBufferMemoryBarriers = buf_bar,
-        .bufferMemoryBarrierCount = nb_buf_bar,
-    });
-    nb_buf_bar = 0;
-
     /* Run reset shader */
     if (f->key_frame || fv->force_pcm) {
         ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->reset,
@@ -451,25 +434,31 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
 
     /* Sync between reset and encode shaders */
     ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
-                      COMPUTE_SHADER_BIT, SHADER_READ_BIT, NONE_KHR,
+                      COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
                       COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
                       0, slice_data_size*f->slice_count);
-    ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
-                      COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
-                      COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
-                      slice_data_size*f->slice_count, VK_WHOLE_SIZE);
-    ff_vk_buf_barrier(buf_bar[nb_buf_bar++], results_data_buf,
-                      ALL_COMMANDS_BIT, NONE_KHR, NONE_KHR,
-                      COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
-                      0, VK_WHOLE_SIZE);
-    ff_vk_buf_barrier(buf_bar[nb_buf_bar++], out_data_buf,
-                      ALL_COMMANDS_BIT, NONE_KHR, NONE_KHR,
-                      COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
-                      0, VK_WHOLE_SIZE);
+    if (f->key_frame || fv->force_pcm)
+        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
+                          COMPUTE_SHADER_BIT, SHADER_WRITE_BIT, NONE_KHR,
+                          COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
+                          slice_data_size*f->slice_count, VK_WHOLE_SIZE);
+    else
+        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_data_buf,
+                          COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
+                          COMPUTE_SHADER_BIT, SHADER_READ_BIT, SHADER_WRITE_BIT,
+                          slice_data_size*f->slice_count, VK_WHOLE_SIZE);
+
+    ff_vk_frame_barrier(&fv->s, exec, src, img_bar, &nb_img_bar,
+                        fv->optimize_rct ? VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT :
+                        VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+                        VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
+                        VK_ACCESS_SHADER_READ_BIT,
+                        VK_IMAGE_LAYOUT_GENERAL,
+                        VK_QUEUE_FAMILY_IGNORED);
 
     if (fv->is_rgb)
         ff_vk_frame_barrier(&fv->s, exec, tmp, img_bar, &nb_img_bar,
-                            VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+                            VK_PIPELINE_STAGE_2_CLEAR_BIT,
                             VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
                             VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
                             VK_IMAGE_LAYOUT_GENERAL,
-- 
2.52.0


>From e207ea7571592752599a62a1f87606a5a85d07b5 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 8 Feb 2026 06:04:56 +0100
Subject: [PATCH 26/58] ffv1enc: add descriptor information for GBRP

The C encoder does not support GBRP, this just adds info fields
so the Vulkan encoder can use it.
---
 libavcodec/ffv1enc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c
index 623bd2a3ea..b6d25013f0 100644
--- a/libavcodec/ffv1enc.c
+++ b/libavcodec/ffv1enc.c
@@ -906,6 +906,7 @@ av_cold int ff_ffv1_encode_setup_plane_info(AVCodecContext *avctx,
         s->use32bit = 1;
         s->version = FFMAX(s->version, 1);
         break;
+    case AV_PIX_FMT_GBRP:
     case AV_PIX_FMT_0RGB32:
         s->colorspace = 1;
         s->chroma_planes = 1;
-- 
2.52.0


>From 4fb39fa79a4d29bad8c696f80c648037e9b43ed8 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 8 Feb 2026 06:28:32 +0100
Subject: [PATCH 27/58] vulkan/rangecoder: clean up the type mess slightly

---
 libavcodec/vulkan/rangecoder.glsl | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index 9d3eab5461..15f6655359 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -34,10 +34,10 @@ struct RangeCoder {
     uint64_t bytestream;
     uint64_t bytestream_end;
 
-    int low;
-    int range;
+    uint     low;
+    uint     range;
     uint16_t outstanding_count;
-    uint8_t outstanding_byte;
+    uint8_t  outstanding_byte;
 };
 
 void rac_init(out RangeCoder r, u8buf data, uint buf_size)
@@ -89,7 +89,7 @@ void renorm_encoder(inout RangeCoder c)
 void renorm_encoder(inout RangeCoder c)
 {
     uint16_t oc = c.outstanding_count + uint16_t(1);
-    int low = c.low;
+    uint low = c.low;
 
     c.range <<= 8;
     c.low = bitfieldInsert(0, low, 8, 8);
@@ -115,7 +115,7 @@ void renorm_encoder(inout RangeCoder c)
 }
 #endif
 
-void put_rac_internal(inout RangeCoder c, const int range1, bool bit)
+void put_rac_internal(inout RangeCoder c, const uint range1, bool bit)
 {
 #ifdef DEBUG
     if (range1 >= c.range)
@@ -124,7 +124,7 @@ void put_rac_internal(inout RangeCoder c, const int range1, bool bit)
         debugPrintfEXT("Error: range1 <= 0");
 #endif
 
-    int ranged = c.range - range1;
+    uint ranged = c.range - range1;
     c.low += bit ? ranged : 0;
     c.range = bit ? range1 : ranged;
 
@@ -151,7 +151,7 @@ void put_rac_equi(inout RangeCoder c, bool bit)
 
 void put_rac_terminate(inout RangeCoder c)
 {
-    int range1 = (c.range * 129) >> 8;
+    uint range1 = (c.range * 129) >> 8;
 
 #ifdef DEBUG
     if (range1 >= c.range)
@@ -166,7 +166,7 @@ void put_rac_terminate(inout RangeCoder c)
 }
 
 /* Return the number of bytes written. */
-uint32_t rac_terminate(inout RangeCoder c)
+uint rac_terminate(inout RangeCoder c)
 {
     put_rac_terminate(c);
     c.range = uint16_t(0xFF);
@@ -182,7 +182,7 @@ uint32_t rac_terminate(inout RangeCoder c)
         debugPrintfEXT("Error: range < 0x100");
 #endif
 
-    return uint32_t(uint64_t(c.bytestream) - uint64_t(c.bytestream_start));
+    return uint(uint64_t(c.bytestream) - uint64_t(c.bytestream_start));
 }
 
 /* Decoder */
@@ -219,9 +219,9 @@ void refill(inout RangeCoder c)
     }
 }
 
-bool get_rac_internal(inout RangeCoder c, const int range1)
+bool get_rac_internal(inout RangeCoder c, const uint range1)
 {
-    int ranged = c.range - range1;
+    uint ranged = c.range - range1;
     bool bit = c.low >= ranged;
     c.low -= bit ? ranged : 0;
     c.range = (bit ? 0 : ranged) + (bit ? range1 : 0);
-- 
2.52.0


>From 6da69fb3d9ac08074ccf67741d3f444b92a3f606 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sat, 7 Feb 2026 01:55:21 +0100
Subject: [PATCH 28/58] vulkan_ffv1: implement parallel probability adaptation

---
 libavcodec/ffv1_vulkan.c             |  2 +-
 libavcodec/ffv1enc_vulkan.c          |  8 ++--
 libavcodec/vulkan/ffv1_dec.comp.glsl | 67 ++++++++++++++++++++--------
 libavcodec/vulkan/rangecoder.glsl    | 12 ++++-
 libavcodec/vulkan_ffv1.c             |  9 ++--
 5 files changed, 70 insertions(+), 28 deletions(-)

diff --git a/libavcodec/ffv1_vulkan.c b/libavcodec/ffv1_vulkan.c
index 91cfb7bd73..7908cc5f5a 100644
--- a/libavcodec/ffv1_vulkan.c
+++ b/libavcodec/ffv1_vulkan.c
@@ -86,7 +86,7 @@ static int init_state_transition_data(FFVulkanContext *s,
                          buf_len,
                          NULL, NULL,
                          VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
-                         VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                         VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
 
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 5a12bc2357..5edbe4735b 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -784,7 +784,7 @@ static int init_rct_search_shader(AVCodecContext *avctx, VkSpecializationInfo *s
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
@@ -827,7 +827,7 @@ static int init_setup_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
@@ -867,7 +867,7 @@ static int init_reset_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
@@ -910,7 +910,7 @@ static int init_encode_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
         { /* quant_buf */
diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 584735f35e..1f37c23b2a 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -36,35 +36,51 @@ layout (set = 1, binding = 2, scalar) writeonly buffer slice_status_buf {
 layout (set = 1, binding = 3) uniform uimage2D dec[];
 
 #ifndef GOLOMB
-#define READ(c, off) get_rac(c, uint64_t(slice_state) + (state_off + off))
-int get_isymbol(inout RangeCoder c, uint state_off)
+
+#define READ(c, idx) get_rac_noadapt(c, idx)
+int get_isymbol(inout RangeCoder c)
 {
     if (READ(c, 0))
         return 0;
 
     uint e = 1;
-    for (; e < 33; e++)
-        if (!READ(c, min(e, 10)))
+    for (; e < 11; e++) {
+        if (!READ(c, e))
             break;
-
-    if (expectEXT(e == 1, false)) {
-        return READ(c, 11) ? -1 : 1;
-    } else if (expectEXT(e == 33, false)) {
-        corrupt = true;
-        return 0;
     }
 
     int a = 1;
-    for (uint i = e + 20; i >= 22; i--) {
-        a <<= 1;
-        a |= int(READ(c, min(i, 31)));
+    uint i = e;
+
+    if (bits > 8 && e == 11) {
+        do {
+            rc_state[10] = zero_one_state[rc_state[10] + 256];
+            e++;
+        } while (READ(c, 10));
+
+        e--;
+        i = e - 1;
+
+        a += a + int(READ(c, 31));
+        for (; i >= 11; i--) {
+            rc_state[31] = zero_one_state[rc_state[31] +
+                                          (rc_data[31] ? 256 : 0)];
+            a += a + int(READ(c, 31));
+        }
     }
 
+    i += 20;
+    for (; i >= 22; i--)
+        a += a + int(READ(c, i));
+
     return READ(c, min(e + 10, 21)) ? -a : a;
 }
 
 void decode_line_pcm(inout SliceContext sc, ivec2 sp, int w, int y, int p)
 {
+    if (gl_LocalInvocationID.x > 0)
+        return;
+
 #ifndef RGB
     if (p > 0 && p < 3) {
         w = ceil_rshift(w, chroma_shift.x);
@@ -99,13 +115,28 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
                             quant_table_idx, extend_lookup[quant_table_idx]);
 
         uint context_off = state_off + CONTEXT_SIZE*abs(pr[0]);
+        u8buf cd = u8buf(uint64_t(slice_state) + context_off);
 
-        int diff = get_isymbol(sc.c, context_off);
-        if (pr[0] < 0)
-            diff = -diff;
+        rc_state[gl_LocalInvocationID.x] = cd[gl_LocalInvocationID.x].v;
+        rc_dec[gl_LocalInvocationID.x] = false;
+        barrier();
 
-        uint v = zero_extend(pr[1] + diff, bits);
-        imageStore(dec[p], sp + LADDR(ivec2(x, y)), uvec4(v));
+        int diff;
+        if (gl_LocalInvocationID.x == 0)
+            diff = get_isymbol(sc.c);
+
+        barrier();
+        uint i = gl_LocalInvocationID.x;
+        if (rc_dec[i])
+            cd[i].v = zero_one_state[rc_state[i] + (rc_data[i] ? 256 : 0)];
+
+        if (gl_LocalInvocationID.x == 0) {
+            if (pr[0] < 0)
+                diff = -diff;
+
+            uint v = zero_extend(pr[1] + diff, bits);
+            imageStore(dec[p], sp + LADDR(ivec2(x, y)), uvec4(v));
+        }
     }
 }
 #else
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index 15f6655359..545f13d463 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -25,7 +25,7 @@
 
 #define CONTEXT_SIZE 32
 
-layout (set = 0, binding = 0, scalar) uniform rangecoder_buf {
+layout (set = 0, binding = 0, scalar) readonly buffer rangecoder_buf {
     uint8_t zero_one_state[512];
 };
 
@@ -40,6 +40,10 @@ struct RangeCoder {
     uint8_t  outstanding_byte;
 };
 
+shared uint8_t rc_state[CONTEXT_SIZE];
+shared bool rc_data[CONTEXT_SIZE];
+shared bool rc_dec[CONTEXT_SIZE];
+
 void rac_init(out RangeCoder r, u8buf data, uint buf_size)
 {
     r.bytestream_start = uint64_t(data);
@@ -239,6 +243,12 @@ bool get_rac_direct(inout RangeCoder c, inout uint8_t state)
     return bit;
 }
 
+bool get_rac_noadapt(inout RangeCoder c, uint idx)
+{
+    rc_dec[idx] = true;
+    return (rc_data[idx] = get_rac_internal(c, c.range * rc_state[idx] >> 8));
+}
+
 bool get_rac(inout RangeCoder c, uint64_t state)
 {
     return get_rac_direct(c, u8buf(state).v);
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index bdc3be50b4..3b1bce97d1 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -540,7 +540,7 @@ static int init_setup_shader(FFV1Context *f, FFVulkanContext *s,
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
         { /* crc_ieee_buf */
@@ -591,7 +591,7 @@ static int init_reset_shader(FFV1Context *f, FFVulkanContext *s,
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
@@ -628,15 +628,16 @@ static int init_decode_shader(FFV1Context *f, FFVulkanContext *s,
 {
     int err;
 
+    uint32_t wg_x = ac != AC_GOLOMB_RICE ? CONTEXT_SIZE : 1;
     ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
-                      (uint32_t []) { 1, 1, 1 }, 0);
+                      (uint32_t []) { wg_x, 1, 1 }, 0);
 
     ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
         { /* quant_buf */
-- 
2.52.0


>From d47af0fc03ff5503972ad11a0e9c1f620e6c33d4 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 8 Feb 2026 03:58:11 +0100
Subject: [PATCH 29/58] vulkan_ffv1: use regular descriptors for slice state

HUGE speedup on AMD, HUGE speedup everywhere.
---
 libavcodec/vulkan/ffv1_dec.comp.glsl          | 30 +++++++++-----
 libavcodec/vulkan/ffv1_dec_reset.comp.glsl    | 40 +++++++++++--------
 libavcodec/vulkan/ffv1_dec_rgb.comp.glsl      |  2 +-
 libavcodec/vulkan/ffv1_enc_golomb.comp.glsl   |  1 +
 .../vulkan/ffv1_enc_reset_golomb.comp.glsl    |  1 +
 .../vulkan/ffv1_enc_rgb_golomb.comp.glsl      |  1 +
 libavcodec/vulkan/ffv1_vlc.glsl               |  8 +++-
 libavcodec/vulkan_ffv1.c                      | 28 +++++++++++--
 8 files changed, 79 insertions(+), 32 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 1f37c23b2a..720fa14cd2 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -33,10 +33,14 @@ layout (set = 1, binding = 1, scalar) readonly buffer slice_offsets_buf {
 layout (set = 1, binding = 2, scalar) writeonly buffer slice_status_buf {
     uint32_t slice_status[];
 };
-layout (set = 1, binding = 3) uniform uimage2D dec[];
+layout (set = 1, binding = 4) uniform uimage2D dec[];
 
 #ifndef GOLOMB
 
+layout (set = 1, binding = 3, scalar) buffer slice_state_buf {
+    uint8_t slice_rc_state[];
+};
+
 #define READ(c, idx) get_rac_noadapt(c, idx)
 int get_isymbol(inout RangeCoder c)
 {
@@ -114,10 +118,9 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
         ivec2 pr = get_pred(dec[p], sp, ivec2(x, y), 0, w,
                             quant_table_idx, extend_lookup[quant_table_idx]);
 
-        uint context_off = state_off + CONTEXT_SIZE*abs(pr[0]);
-        u8buf cd = u8buf(uint64_t(slice_state) + context_off);
+        uint rc_off = state_off + CONTEXT_SIZE*abs(pr[0]) + gl_LocalInvocationID.x;
 
-        rc_state[gl_LocalInvocationID.x] = cd[gl_LocalInvocationID.x].v;
+        rc_state[gl_LocalInvocationID.x] = slice_rc_state[rc_off];
         rc_dec[gl_LocalInvocationID.x] = false;
         barrier();
 
@@ -128,7 +131,8 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
         barrier();
         uint i = gl_LocalInvocationID.x;
         if (rc_dec[i])
-            cd[i].v = zero_one_state[rc_state[i] + (rc_data[i] ? 256 : 0)];
+            slice_rc_state[rc_off] = zero_one_state[rc_state[i] +
+                                                    (rc_data[i] ? 256 : 0)];
 
         if (gl_LocalInvocationID.x == 0) {
             if (pr[0] < 0)
@@ -139,7 +143,13 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
         }
     }
 }
-#else
+
+#else /* GOLOMB */
+
+layout (set = 1, binding = 3, scalar) buffer slice_state_buf {
+    VlcState slice_vlc_state[];
+};
+
 GetBitContext gb;
 
 void golomb_init(inout SliceContext sc)
@@ -172,8 +182,7 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
         ivec2 pr = get_pred(dec[p], sp, ivec2(x, y), 0, w,
                             quant_table_idx, extend_lookup[quant_table_idx]);
 
-        uint context_off = state_off + VLC_STATE_SIZE*abs(pr[0]);
-        VlcState sb = VlcState(uint64_t(slice_state) + context_off);
+        uint vlc_off = state_off + abs(pr[0]);
 
         if (pr[0] == 0 && run_mode == 0)
             run_mode = 1;
@@ -201,14 +210,14 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
             if (run_count < 0) {
                 run_mode  = 0;
                 run_count = 0;
-                diff = read_vlc_symbol(gb, sb, bits);
+                diff = read_vlc_symbol(gb, slice_vlc_state[vlc_off], bits);
                 if (diff >= 0)
                     diff++;
             } else {
                 diff = 0;
             }
         } else {
-            diff = read_vlc_symbol(gb, sb, bits);
+            diff = read_vlc_symbol(gb, slice_vlc_state[vlc_off], bits);
         }
 
         if (pr[0] < 0)
@@ -298,6 +307,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
                                uvec4(0, 1, 1, 2))*plane_state_size;
 
 #ifdef GOLOMB
+    slice_state_off >>= 3; // division by VLC_STATE_SIZE
     golomb_init(sc);
 #endif
 
diff --git a/libavcodec/vulkan/ffv1_dec_reset.comp.glsl b/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
index e708d03036..1aeb196e1e 100644
--- a/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
@@ -26,6 +26,18 @@
 #include "common.glsl"
 #include "ffv1_common.glsl"
 
+#ifdef GOLOMB
+#define PS_SHIFT 3
+layout (set = 1, binding = 1, scalar) writeonly buffer slice_state_buf {
+    VlcState slice_vlc_state[];
+};
+#else
+#define PS_SHIFT 2
+layout (set = 1, binding = 1, scalar) writeonly buffer slice_state_buf {
+    uint32_t slice_rc_state[];
+};
+#endif
+
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
@@ -34,30 +46,26 @@ void main(void)
         return;
 
     const uint8_t qidx = slice_ctx[slice_idx].quant_table_idx[gl_WorkGroupID.z];
+
     uint contexts = context_count[qidx];
-    uint64_t slice_state_off = uint64_t(slice_state) +
-                               slice_idx*plane_state_size*codec_planes;
+    uint plane_state_len = plane_state_size >> PS_SHIFT;
+    uint offs = slice_idx*plane_state_len*codec_planes +
+                gl_WorkGroupID.z*plane_state_len +
+                gl_LocalInvocationID.x;
 
 #ifdef GOLOMB
-    uint64_t start = slice_state_off +
-                     (gl_WorkGroupID.z*(plane_state_size/VLC_STATE_SIZE) +
-                      gl_LocalInvocationID.x)*VLC_STATE_SIZE;
     for (uint x = gl_LocalInvocationID.x; x < contexts; x += gl_WorkGroupSize.x) {
-        VlcState sb = VlcState(start);
-        sb.drift     =  int16_t(0);
-        sb.error_sum = uint16_t(4);
-        sb.bias      =   int8_t(0);
-        sb.count     =  uint8_t(1);
-        start += gl_WorkGroupSize.x*VLC_STATE_SIZE;
+        slice_vlc_state[offs].drift     =  int16_t(0);
+        slice_vlc_state[offs].error_sum = uint16_t(4);
+        slice_vlc_state[offs].bias      =   int8_t(0);
+        slice_vlc_state[offs].count     =  uint8_t(1);
+        offs += gl_WorkGroupSize.x;
     }
 #else
-    uint64_t start = slice_state_off +
-                     gl_WorkGroupID.z*plane_state_size +
-                     (gl_LocalInvocationID.x << 2 /* dwords */); /* Bytes */
     uint count_total = contexts*(CONTEXT_SIZE /* bytes */ >> 2 /* dwords */);
     for (uint x = gl_LocalInvocationID.x; x < count_total; x += gl_WorkGroupSize.x) {
-        u32buf(start).v = 0x80808080;
-        start += gl_WorkGroupSize.x*(CONTEXT_SIZE >> 3 /* 1/8th of context */);
+        slice_rc_state[offs] = 0x80808080;
+        offs += gl_WorkGroupSize.x;
     }
 #endif
 }
diff --git a/libavcodec/vulkan/ffv1_dec_rgb.comp.glsl b/libavcodec/vulkan/ffv1_dec_rgb.comp.glsl
index fe0d6957df..72dc31ba15 100644
--- a/libavcodec/vulkan/ffv1_dec_rgb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_rgb.comp.glsl
@@ -24,7 +24,7 @@
 #extension GL_GOOGLE_include_directive : require
 #extension GL_EXT_shader_image_load_formatted : require
 
-layout (set = 1, binding = 4) writeonly uniform uimage2D dst[];
+layout (set = 1, binding = 5) writeonly uniform uimage2D dst[];
 
 #define RGB
 #include "ffv1_dec.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
index a120564602..459c65d954 100644
--- a/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
@@ -23,5 +23,6 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
+#define VLC_BUFFER
 #define GOLOMB
 #include "ffv1_enc.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl
index 277f88c6c3..23eca0c7ed 100644
--- a/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl
@@ -23,5 +23,6 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
+#define VLC_BUFFER
 #define GOLOMB
 #include "ffv1_enc_reset.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
index 8efffd19e8..c7a3d17fd5 100644
--- a/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
@@ -23,5 +23,6 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
+#define VLC_BUFFER
 #define GOLOMB
 #include "ffv1_enc_rgb.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_vlc.glsl b/libavcodec/vulkan/ffv1_vlc.glsl
index e1c6cf66de..68353ae9ce 100644
--- a/libavcodec/vulkan/ffv1_vlc.glsl
+++ b/libavcodec/vulkan/ffv1_vlc.glsl
@@ -24,7 +24,13 @@
 #define VULKAN_FFV1_VLC_H
 
 #define VLC_STATE_SIZE 8
-layout(buffer_reference, buffer_reference_align = VLC_STATE_SIZE) buffer VlcState {
+#ifdef VLC_BUFFER
+layout(buffer_reference, buffer_reference_align = VLC_STATE_SIZE) buffer
+#else
+struct
+#endif
+
+VlcState {
     uint32_t error_sum;
     int16_t  drift;
     int8_t   bias;
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 3b1bce97d1..0e2cda1028 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -404,6 +404,12 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                                     slice_state,
                                     0, fp->slice_data_size*f->slice_count,
                                     VK_FORMAT_UNDEFINED);
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, reset_shader,
+                                    1, 1, 0,
+                                    slice_state,
+                                    f->slice_count*fp->slice_data_size,
+                                    VK_WHOLE_SIZE,
+                                    VK_FORMAT_UNDEFINED);
 
     ff_vk_exec_bind_shader(&ctx->s, exec, reset_shader);
     ff_vk_shader_update_push_const(&ctx->s, exec, reset_shader,
@@ -458,16 +464,22 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                                     slice_status,
                                     0, 2*f->slice_count*sizeof(uint32_t),
                                     VK_FORMAT_UNDEFINED);
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
+                                    1, 3, 0,
+                                    slice_state,
+                                    f->slice_count*fp->slice_data_size,
+                                    VK_WHOLE_SIZE,
+                                    VK_FORMAT_UNDEFINED);
 
     ff_vk_shader_update_img_array(&ctx->s, exec, decode_shader,
                                   decode_dst, decode_dst_view,
-                                  1, 3,
+                                  1, 4,
                                   VK_IMAGE_LAYOUT_GENERAL,
                                   VK_NULL_HANDLE);
     if (is_rgb)
         ff_vk_shader_update_img_array(&ctx->s, exec, decode_shader,
                                       f->picture.f, vp->view.out,
-                                      1, 4,
+                                      1, 5,
                                       VK_IMAGE_LAYOUT_GENERAL,
                                       VK_NULL_HANDLE);
 
@@ -602,8 +614,12 @@ static int init_reset_shader(FFV1Context *f, FFVulkanContext *s,
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
+        { /* slice_state_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+        },
     };
-    ff_vk_shader_add_descriptor_set(s, shd, desc_set, 1, 0, 0);
+    ff_vk_shader_add_descriptor_set(s, shd, desc_set, 2, 0, 0);
 
     if (ac == AC_GOLOMB_RICE)
         RET(ff_vk_shader_link(s, shd,
@@ -660,6 +676,10 @@ static int init_decode_shader(FFV1Context *f, FFVulkanContext *s,
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
+        { /* slice_state_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+        },
         { /* dec */
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
@@ -671,7 +691,7 @@ static int init_decode_shader(FFV1Context *f, FFVulkanContext *s,
             .elems  = av_pix_fmt_count_planes(out_frames_ctx->sw_format),
         },
     };
-    ff_vk_shader_add_descriptor_set(s, shd, desc_set, 4 + rgb, 0, 0);
+    ff_vk_shader_add_descriptor_set(s, shd, desc_set, 5 + rgb, 0, 0);
 
     if (ac == AC_GOLOMB_RICE) {
         if (rgb)
-- 
2.52.0


>From 5de20fa907e33b0d2484fe28c79dd0ce7a266326 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 8 Feb 2026 05:18:02 +0100
Subject: [PATCH 30/58] ffv1enc_vulkan: use regular descriptors for slice state

---
 libavcodec/ffv1_vulkan.h                      |  1 -
 libavcodec/ffv1enc_vulkan.c                   | 29 +++++++++++---
 libavcodec/vulkan/ffv1_common.glsl            |  1 -
 libavcodec/vulkan/ffv1_enc.comp.glsl          | 19 ++++++---
 libavcodec/vulkan/ffv1_enc_golomb.comp.glsl   |  1 -
 libavcodec/vulkan/ffv1_enc_reset.comp.glsl    | 39 +++++++++++--------
 .../vulkan/ffv1_enc_reset_golomb.comp.glsl    |  1 -
 libavcodec/vulkan/ffv1_enc_rgb.comp.glsl      |  2 +-
 .../vulkan/ffv1_enc_rgb_golomb.comp.glsl      |  1 -
 libavcodec/vulkan/ffv1_vlc.glsl               |  9 +----
 libavcodec/vulkan_ffv1.c                      |  1 -
 11 files changed, 62 insertions(+), 42 deletions(-)

diff --git a/libavcodec/ffv1_vulkan.h b/libavcodec/ffv1_vulkan.h
index fcd6f98532..8585315741 100644
--- a/libavcodec/ffv1_vulkan.h
+++ b/libavcodec/ffv1_vulkan.h
@@ -42,7 +42,6 @@ int ff_ffv1_vk_init_crc_table_data(FFVulkanContext *s,
 
 typedef struct FFv1ShaderParams {
     VkDeviceAddress slice_data;
-    VkDeviceAddress slice_state;
 
     uint32_t extend_lookup[8];
     uint16_t context_count[8];
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 5edbe4735b..d92b53a56c 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -286,7 +286,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     /* With everything allocated, setup push data */
     FFv1ShaderParams pd = {
         .slice_data = out_data_buf->address,
-        .slice_state = slice_data_buf->address + f->slice_count*256,
 
         .img_size[0] = fv->s.frames->width,
         .img_size[1] = fv->s.frames->height,
@@ -422,6 +421,12 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
                                         slice_data_buf,
                                         0, slice_data_size*f->slice_count,
                                         VK_FORMAT_UNDEFINED);
+        ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->reset,
+                                        1, 1, 0,
+                                        slice_data_buf,
+                                        f->slice_count*256,
+                                        VK_WHOLE_SIZE,
+                                        VK_FORMAT_UNDEFINED);
 
         ff_vk_exec_bind_shader(&fv->s, exec, &fv->reset);
         ff_vk_shader_update_push_const(&fv->s, exec, &fv->reset,
@@ -485,15 +490,21 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
                                     results_data_buf,
                                     0, results_data_buf->size,
                                     VK_FORMAT_UNDEFINED);
+    ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->enc,
+                                    1, 2, 0,
+                                    slice_data_buf,
+                                    f->slice_count*256,
+                                    VK_WHOLE_SIZE,
+                                    VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_img_array(&fv->s, exec, &fv->enc,
                                   src, src_views,
-                                  1, 2,
+                                  1, 3,
                                   VK_IMAGE_LAYOUT_GENERAL,
                                   VK_NULL_HANDLE);
     if (fv->is_rgb)
         ff_vk_shader_update_img_array(&fv->s, exec, &fv->enc,
                                       tmp, tmp_views,
-                                      1, 3,
+                                      1, 4,
                                       VK_IMAGE_LAYOUT_GENERAL,
                                       VK_NULL_HANDLE);
 
@@ -878,8 +889,12 @@ static int init_reset_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
+        { /* slice_state_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+        },
     };
-    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 1, 0, 0);
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 2, 0, 0);
 
     if (fv->ctx.ac == AC_GOLOMB_RICE)
         RET(ff_vk_shader_link(&fv->s, shd,
@@ -933,6 +948,10 @@ static int init_encode_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
+        { /* slice_state_buf */
+            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+        },
         { /* src */
             .type   = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
@@ -943,7 +962,7 @@ static int init_encode_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 3 + fv->is_rgb, 0, 0);
+    ff_vk_shader_add_descriptor_set(&fv->s, shd, desc_set, 4 + fv->is_rgb, 0, 0);
 
     if (fv->ctx.ac == AC_GOLOMB_RICE) {
         if (fv->is_rgb)
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index e7bc8f5e20..360890f153 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -59,7 +59,6 @@ layout (constant_id = 17) const uint context_model = 0;
 
 layout (push_constant, scalar) uniform pushConstants {
     u8buf slice_data;
-    u8buf slice_state;
 
     bool extend_lookup[MAX_QUANT_TABLES];
     uint16_t context_count[MAX_QUANT_TABLES];
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index 7be01be1d9..d50696992e 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -34,12 +34,15 @@ layout (set = 0, binding = 2, scalar) uniform crc_ieee_buf {
 layout (set = 1, binding = 1, scalar) writeonly buffer slice_results_buf {
     uint64_t slice_results[];
 };
-layout (set = 1, binding = 2) uniform uimage2D src[];
+layout (set = 1, binding = 3) uniform uimage2D src[];
 
 #ifndef GOLOMB
-#define WRITE(c, off, val) put_rac(c, uint64_t(slice_state) + (state_off + off), val)
 
-/* Note - only handles signed values */
+layout (set = 1, binding = 2, scalar) buffer slice_state_buf {
+    uint8_t slice_rc_state[];
+};
+
+#define WRITE(c, off, val) put_rac_direct(c, slice_rc_state[state_off + off], val)
 void put_symbol(inout RangeCoder c, uint state_off, int v)
 {
     bool is_nil = (v == 0);
@@ -112,6 +115,10 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
 
 #else /* GOLOMB */
 
+layout (set = 1, binding = 2, scalar) buffer slice_state_buf {
+    VlcState slice_vlc_state[];
+};
+
 uint hdr_len = 0;
 PutBitContext pb;
 
@@ -174,9 +181,8 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
         }
 
         if (!run_mode) {
-            VlcState sb = VlcState(uint64_t(slice_state) +
-                                   state_off + VLC_STATE_SIZE*d[0]);
-            Symbol sym = get_vlc_symbol(sb, d[1], bits);
+            Symbol sym = get_vlc_symbol(slice_vlc_state[state_off + d[0]],
+                                        d[1], bits);
             put_bits(pb, sym.bits, sym.val);
         }
     }
@@ -276,6 +282,7 @@ void encode_slice(inout SliceContext sc, const uint slice_idx)
                                uvec4(0, 1, 1, 2))*plane_state_size;
 
 #ifdef GOLOMB
+    slice_state_off >>= 3;
     init_golomb(slice_ctx[slice_idx]);
 #endif
 
diff --git a/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
index 459c65d954..a120564602 100644
--- a/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
@@ -23,6 +23,5 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#define VLC_BUFFER
 #define GOLOMB
 #include "ffv1_enc.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_reset.comp.glsl b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
index 4d8637a0d3..1ad0d99125 100644
--- a/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
@@ -26,34 +26,41 @@
 #include "common.glsl"
 #include "ffv1_common.glsl"
 
+#ifdef GOLOMB
+#define PS_SHIFT 3
+layout (set = 1, binding = 1, scalar) writeonly buffer slice_state_buf {
+    VlcState slice_vlc_state[];
+};
+#else
+#define PS_SHIFT 2
+layout (set = 1, binding = 1, scalar) writeonly buffer slice_state_buf {
+    uint32_t slice_rc_state[];
+};
+#endif
+
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
 
     uint contexts = context_count[context_model];
-    uint64_t slice_state_off = uint64_t(slice_state) +
-                               slice_idx*plane_state_size*codec_planes;
+    uint plane_state_len = plane_state_size >> PS_SHIFT;
+    uint offs = slice_idx*plane_state_len*codec_planes +
+                gl_WorkGroupID.z*plane_state_len +
+                gl_LocalInvocationID.x;
 
 #ifdef GOLOMB
-    uint64_t start = slice_state_off +
-                     (gl_WorkGroupID.z*(plane_state_size/VLC_STATE_SIZE) +
-                      gl_LocalInvocationID.x)*VLC_STATE_SIZE;
     for (uint x = gl_LocalInvocationID.x; x < contexts; x += gl_WorkGroupSize.x) {
-        VlcState sb = VlcState(start);
-        sb.drift     =  int16_t(0);
-        sb.error_sum = uint16_t(4);
-        sb.bias      =   int8_t(0);
-        sb.count     =  uint8_t(1);
-        start += gl_WorkGroupSize.x*VLC_STATE_SIZE;
+        slice_vlc_state[offs].drift     =  int16_t(0);
+        slice_vlc_state[offs].error_sum = uint16_t(4);
+        slice_vlc_state[offs].bias      =   int8_t(0);
+        slice_vlc_state[offs].count     =  uint8_t(1);
+        offs += gl_WorkGroupSize.x;
     }
 #else
-    uint64_t start = slice_state_off +
-                     gl_WorkGroupID.z*plane_state_size +
-                     (gl_LocalInvocationID.x << 2 /* dwords */); /* Bytes */
     uint count_total = contexts*(CONTEXT_SIZE /* bytes */ >> 2 /* dwords */);
     for (uint x = gl_LocalInvocationID.x; x < count_total; x += gl_WorkGroupSize.x) {
-        u32buf(start).v = 0x80808080;
-        start += gl_WorkGroupSize.x*(CONTEXT_SIZE >> 3 /* 1/8th of context */);
+        slice_rc_state[offs] = 0x80808080;
+        offs += gl_WorkGroupSize.x;
     }
 #endif
 }
diff --git a/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl
index 23eca0c7ed..277f88c6c3 100644
--- a/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_reset_golomb.comp.glsl
@@ -23,6 +23,5 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#define VLC_BUFFER
 #define GOLOMB
 #include "ffv1_enc_reset.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_rgb.comp.glsl b/libavcodec/vulkan/ffv1_enc_rgb.comp.glsl
index 24c79c222e..90d136be1f 100644
--- a/libavcodec/vulkan/ffv1_enc_rgb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_rgb.comp.glsl
@@ -24,7 +24,7 @@
 #extension GL_GOOGLE_include_directive : require
 #extension GL_EXT_shader_image_load_formatted : require
 
-layout (set = 1, binding = 3) uniform uimage2D tmp;
+layout (set = 1, binding = 4) uniform uimage2D tmp;
 
 #define RGB
 #include "ffv1_enc.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
index c7a3d17fd5..8efffd19e8 100644
--- a/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
@@ -23,6 +23,5 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
-#define VLC_BUFFER
 #define GOLOMB
 #include "ffv1_enc_rgb.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_vlc.glsl b/libavcodec/vulkan/ffv1_vlc.glsl
index 68353ae9ce..23e3c3ab42 100644
--- a/libavcodec/vulkan/ffv1_vlc.glsl
+++ b/libavcodec/vulkan/ffv1_vlc.glsl
@@ -23,14 +23,7 @@
 #ifndef VULKAN_FFV1_VLC_H
 #define VULKAN_FFV1_VLC_H
 
-#define VLC_STATE_SIZE 8
-#ifdef VLC_BUFFER
-layout(buffer_reference, buffer_reference_align = VLC_STATE_SIZE) buffer
-#else
-struct
-#endif
-
-VlcState {
+struct VlcState {
     uint32_t error_sum;
     int16_t  drift;
     int8_t   bias;
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 0e2cda1028..28bb6659d2 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -355,7 +355,6 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
 
     FFv1ShaderParams pd = {
         .slice_data = slices_buf->address,
-        .slice_state  = slice_state->address + f->slice_count*fp->slice_data_size,
 
         .img_size[0] = f->picture.f->width,
         .img_size[1] = f->picture.f->height,
-- 
2.52.0


>From a385365cf7588c764bc923800ed57483a80db6a8 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 8 Feb 2026 07:27:34 +0100
Subject: [PATCH 31/58] vulkan: drop support for descriptor buffers

Descriptor buffers were a neat attempt at organizing descriptors.
Simple, robust, reliable.

Unfortunately, driver support never caught on, and neither did validation
layer support.

Now they're being replaced by descriptor heaps, which promises to be
the future. We'll see how it goes.
---
 libavcodec/ffv1_vulkan.c        |   3 -
 libavcodec/ffv1enc_vulkan.c     |   6 +-
 libavcodec/vulkan_dpx.c         |   3 +-
 libavcodec/vulkan_ffv1.c        |   9 +-
 libavcodec/vulkan_prores_raw.c  |   3 +-
 libavfilter/vf_nlmeans_vulkan.c |   4 +-
 libavutil/hwcontext_vulkan.c    |  15 +-
 libavutil/vulkan.c              | 303 ++++++--------------------------
 libavutil/vulkan.h              |   3 +-
 libavutil/vulkan_functions.h    |   8 -
 libavutil/vulkan_loader.h       |   1 -
 11 files changed, 63 insertions(+), 295 deletions(-)

diff --git a/libavcodec/ffv1_vulkan.c b/libavcodec/ffv1_vulkan.c
index 7908cc5f5a..a67eb3e15d 100644
--- a/libavcodec/ffv1_vulkan.c
+++ b/libavcodec/ffv1_vulkan.c
@@ -85,7 +85,6 @@ static int init_state_transition_data(FFVulkanContext *s,
     RET(ff_vk_create_buf(s, vkb,
                          buf_len,
                          NULL, NULL,
-                         VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
                          VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
@@ -116,7 +115,6 @@ int ff_ffv1_vk_init_quant_table_data(FFVulkanContext *s,
     RET(ff_vk_create_buf(s, vkb,
                          buf_len,
                          NULL, NULL,
-                         VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
                          VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
                          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
@@ -142,7 +140,6 @@ int ff_ffv1_vk_init_crc_table_data(FFVulkanContext *s,
     RET(ff_vk_create_buf(s, vkb,
                          buf_len,
                          NULL, NULL,
-                         VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
                          VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
                          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index d92b53a56c..c81cb91158 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -229,8 +229,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     if (!slice_data_ref) {
         RET(ff_vk_get_pooled_buffer(&fv->s, &fv->slice_data_pool,
                                     &slice_data_ref,
-                                    VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-                                    VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+                                    VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                                     NULL, slice_state_size*f->slice_count,
                                     VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
 
@@ -243,8 +242,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     /* Allocate results buffer */
     RET(ff_vk_get_pooled_buffer(&fv->s, &fv->results_data_pool,
                                 &fd->results_data_ref,
-                                VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-                                VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+                                VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                                 NULL, 2*f->slice_count*sizeof(uint64_t),
                                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
diff --git a/libavcodec/vulkan_dpx.c b/libavcodec/vulkan_dpx.c
index a69e93108a..726ae09af9 100644
--- a/libavcodec/vulkan_dpx.c
+++ b/libavcodec/vulkan_dpx.c
@@ -72,8 +72,7 @@ static int vk_dpx_start_frame(AVCodecContext          *avctx,
         ctx->s.extensions & FF_VK_EXT_EXTERNAL_HOST_MEMORY)
         ff_vk_host_map_buffer(&ctx->s, &vp->slices_buf, (uint8_t *)buffer,
                               buffer_ref,
-                              VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-                              VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
+                              VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
 
     /* Prepare frame to be used */
     err = ff_vk_decode_prepare_frame_sdr(dec, dpx->frame, vp, 1,
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 28bb6659d2..258fc0e738 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -138,8 +138,7 @@ static int vk_ffv1_start_frame(AVCodecContext          *avctx,
     if (f->picture.f->flags & AV_FRAME_FLAG_KEY) {
         err = ff_vk_get_pooled_buffer(&ctx->s, &fv->slice_state_pool,
                                       &fp->slice_state,
-                                      VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-                                      VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+                                      VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                                       NULL, f->slice_count*fp->slice_state_size,
                                       VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
         if (err < 0)
@@ -154,8 +153,7 @@ static int vk_ffv1_start_frame(AVCodecContext          *avctx,
     /* Allocate slice offsets buffer */
     err = ff_vk_get_pooled_buffer(&ctx->s, &fv->slice_offset_pool,
                                   &fp->slice_offset_buf,
-                                  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-                                  VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+                                  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                                   NULL, 2*f->slice_count*sizeof(uint32_t),
                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
@@ -165,8 +163,7 @@ static int vk_ffv1_start_frame(AVCodecContext          *avctx,
     /* Allocate slice status buffer */
     err = ff_vk_get_pooled_buffer(&ctx->s, &fv->slice_status_pool,
                                   &fp->slice_status_buf,
-                                  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-                                  VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+                                  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                                   NULL, 2*f->slice_count*sizeof(uint32_t),
                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
diff --git a/libavcodec/vulkan_prores_raw.c b/libavcodec/vulkan_prores_raw.c
index c62c631b33..392b74a863 100644
--- a/libavcodec/vulkan_prores_raw.c
+++ b/libavcodec/vulkan_prores_raw.c
@@ -85,8 +85,7 @@ static int vk_prores_raw_start_frame(AVCodecContext          *avctx,
     /* Allocate tile data */
     err = ff_vk_get_pooled_buffer(&ctx->s, &prv->frame_data_pool,
                                   &pp->frame_data_buf,
-                                  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-                                  VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+                                  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                                   NULL, prr->nb_tiles*sizeof(TileData),
                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
diff --git a/libavfilter/vf_nlmeans_vulkan.c b/libavfilter/vf_nlmeans_vulkan.c
index 7a765d9f31..c1430707b7 100644
--- a/libavfilter/vf_nlmeans_vulkan.c
+++ b/libavfilter/vf_nlmeans_vulkan.c
@@ -668,7 +668,6 @@ static av_cold int init_filter(AVFilterContext *ctx)
     }
 
     RET(ff_vk_create_buf(&s->vkctx, &s->xyoffsets_buf, 2*s->nb_offsets*sizeof(int32_t), NULL, NULL,
-                         VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
                          VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
                          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
@@ -861,8 +860,7 @@ static int nlmeans_vulkan_filter_frame(AVFilterLink *link, AVFrame *in)
 
     err = ff_vk_get_pooled_buffer(&s->vkctx, &s->ws_buf_pool, &ws_buf,
                                   VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-                                  VK_BUFFER_USAGE_TRANSFER_DST_BIT |
-                                  VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+                                  VK_BUFFER_USAGE_TRANSFER_DST_BIT,
                                   NULL,
                                   ws_size * s-> opts.t * 2,
                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index e35ece9757..7fa1ffe532 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -112,7 +112,6 @@ typedef struct VulkanDeviceFeatures {
 
     VkPhysicalDeviceShaderObjectFeaturesEXT shader_object;
     VkPhysicalDeviceCooperativeMatrixFeaturesKHR cooperative_matrix;
-    VkPhysicalDeviceDescriptorBufferFeaturesEXT descriptor_buffer;
     VkPhysicalDeviceShaderAtomicFloatFeaturesEXT atomic_float;
 
 #ifdef VK_KHR_shader_relaxed_extended_instruction
@@ -274,8 +273,6 @@ static void device_features_init(AVHWDeviceContext *ctx, VulkanDeviceFeatures *f
                      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT);
     FF_VK_STRUCT_EXT(s, &feats->device, &feats->cooperative_matrix, FF_VK_EXT_COOP_MATRIX,
                      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR);
-    FF_VK_STRUCT_EXT(s, &feats->device, &feats->descriptor_buffer, FF_VK_EXT_DESCRIPTOR_BUFFER,
-                     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT);
     FF_VK_STRUCT_EXT(s, &feats->device, &feats->atomic_float, FF_VK_EXT_ATOMIC_FLOAT,
                      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT);
     FF_VK_STRUCT_EXT(s, &feats->device, &feats->explicit_mem_layout, FF_VK_EXT_EXPLICIT_MEM_LAYOUT,
@@ -374,9 +371,6 @@ static void device_features_copy_needed(VulkanDeviceFeatures *dst, VulkanDeviceF
 
     COPY_VAL(cooperative_matrix.cooperativeMatrix);
 
-    COPY_VAL(descriptor_buffer.descriptorBuffer);
-    COPY_VAL(descriptor_buffer.descriptorBufferPushDescriptors);
-
     COPY_VAL(atomic_float.shaderBufferFloat32Atomics);
     COPY_VAL(atomic_float.shaderBufferFloat32AtomicAdd);
 
@@ -687,7 +681,6 @@ static const VulkanOptExtension optional_device_exts[] = {
     /* Misc or required by other extensions */
     { VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,               FF_VK_EXT_PORTABILITY_SUBSET     },
     { VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,                  FF_VK_EXT_PUSH_DESCRIPTOR        },
-    { VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,                FF_VK_EXT_DESCRIPTOR_BUFFER      },
     { VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME,              FF_VK_EXT_DEVICE_DRM             },
     { VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME,              FF_VK_EXT_ATOMIC_FLOAT           },
     { VK_KHR_COOPERATIVE_MATRIX_EXTENSION_NAME,               FF_VK_EXT_COOP_MATRIX            },
@@ -918,11 +911,6 @@ static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
         tstr = optional_exts[i].name;
         found = 0;
 
-        /* Intel has had a bad descriptor buffer implementation for a while */
-        if (p->dprops.driverID == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA &&
-            !strcmp(tstr, VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME))
-            continue;
-
         /* Check if the device has ReBAR for host image copies */
         if (!strcmp(tstr, VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME) &&
             !vulkan_device_has_rebar(ctx))
@@ -932,8 +920,7 @@ static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts,
             ((debug_mode == FF_VULKAN_DEBUG_VALIDATE) ||
              (debug_mode == FF_VULKAN_DEBUG_PRINTF) ||
              (debug_mode == FF_VULKAN_DEBUG_PRACTICES)) &&
-            (!strcmp(tstr, VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME) ||
-             !strcmp(tstr, VK_EXT_SHADER_OBJECT_EXTENSION_NAME))) {
+            (!strcmp(tstr, VK_EXT_SHADER_OBJECT_EXTENSION_NAME))) {
             continue;
         }
 
diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c
index 0a60763350..51242c250f 100644
--- a/libavutil/vulkan.c
+++ b/libavutil/vulkan.c
@@ -165,8 +165,6 @@ int ff_vk_load_props(FFVulkanContext *s)
                      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT);
     FF_VK_STRUCT_EXT(s, &s->props, &s->coop_matrix_props, FF_VK_EXT_COOP_MATRIX,
                      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_PROPERTIES_KHR);
-    FF_VK_STRUCT_EXT(s, &s->props, &s->desc_buf_props, FF_VK_EXT_DESCRIPTOR_BUFFER,
-                     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT);
     FF_VK_STRUCT_EXT(s, &s->props, &s->optical_flow_props, FF_VK_EXT_OPTICAL_FLOW,
                      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV);
     FF_VK_STRUCT_EXT(s, &s->props, &s->host_image_props, FF_VK_EXT_HOST_IMAGE_COPY,
@@ -330,15 +328,6 @@ void ff_vk_exec_pool_free(FFVulkanContext *s, FFVkExecPool *pool)
     for (int i = 0; i < pool->nb_reg_shd; i++) {
         FFVulkanShaderData *sd = &pool->reg_shd[i];
 
-        if (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) {
-            for (int j = 0; j < sd->nb_descriptor_sets; j++) {
-                FFVulkanDescriptorSetData *set_data = &sd->desc_set_buf[j];
-                if (set_data->buf.mem)
-                    ff_vk_unmap_buffer(s, &set_data->buf, 0);
-                ff_vk_free_buf(s, &set_data->buf);
-            }
-        }
-
         if (sd->desc_pool)
             vk->DestroyDescriptorPool(s->hwctx->act_dev, sd->desc_pool,
                                       s->hwctx->alloc);
@@ -1037,16 +1026,6 @@ int ff_vk_create_buf(FFVulkanContext *s, FFVkBuffer *buf, size_t size,
     int use_ded_mem;
     FFVulkanFunctions *vk = &s->vkfn;
 
-    /* Buffer usage flags corresponding to buffer descriptor types */
-    const VkBufferUsageFlags desc_usage =
-        VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
-        VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
-        VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
-        VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
-
-    if ((s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) && (usage & desc_usage))
-        usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
-
     VkBufferCreateInfo buf_spawn = {
         .sType       = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
         .pNext       = pNext,
@@ -2263,8 +2242,7 @@ static int init_compute_pipeline(FFVulkanContext *s, FFVulkanShader *shd,
 
     VkComputePipelineCreateInfo pipeline_create_info = {
         .sType  = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
-        .flags = (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) ?
-                 VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT : 0x0,
+        .flags = 0x0,
         .layout = shd->pipeline_layout,
         .stage = (VkPipelineShaderStageCreateInfo) {
             .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
@@ -2335,19 +2313,17 @@ static int init_descriptors(FFVulkanContext *s, FFVulkanShader *shd)
     VkResult ret;
     FFVulkanFunctions *vk = &s->vkfn;
 
-    if (!(s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER)) {
-        int has_singular = 0;
-        int max_descriptors = 0;
-        for (int i = 0; i < shd->nb_descriptor_sets; i++) {
-            max_descriptors = FFMAX(max_descriptors, shd->desc_set[i].nb_bindings);
-            if (shd->desc_set[i].singular)
-                has_singular = 1;
-        }
-        shd->use_push = (s->extensions & FF_VK_EXT_PUSH_DESCRIPTOR) &&
-                        (max_descriptors <= s->push_desc_props.maxPushDescriptors) &&
-                        (shd->nb_descriptor_sets == 1) &&
-                        (has_singular == 0);
+    int has_singular = 0;
+    int max_descriptors = 0;
+    for (int i = 0; i < shd->nb_descriptor_sets; i++) {
+        max_descriptors = FFMAX(max_descriptors, shd->desc_set[i].nb_bindings);
+        if (shd->desc_set[i].singular)
+            has_singular = 1;
     }
+    shd->use_push = (s->extensions & FF_VK_EXT_PUSH_DESCRIPTOR) &&
+                    (max_descriptors <= s->push_desc_props.maxPushDescriptors) &&
+                    (shd->nb_descriptor_sets == 1) &&
+                    (has_singular == 0);
 
     for (int i = 0; i < shd->nb_descriptor_sets; i++) {
         FFVulkanDescriptorSet *set = &shd->desc_set[i];
@@ -2355,9 +2331,7 @@ static int init_descriptors(FFVulkanContext *s, FFVulkanShader *shd)
             .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
             .bindingCount = set->nb_bindings,
             .pBindings = set->binding,
-            .flags = (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) ?
-                     VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT :
-                     (shd->use_push) ?
+            .flags = (shd->use_push) ?
                      VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR :
                      0x0,
         };
@@ -2371,20 +2345,6 @@ static int init_descriptors(FFVulkanContext *s, FFVulkanShader *shd)
                    ff_vk_ret2str(ret));
             return AVERROR_EXTERNAL;
         }
-
-        if (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) {
-            vk->GetDescriptorSetLayoutSizeEXT(s->hwctx->act_dev, shd->desc_layout[i],
-                                              &set->layout_size);
-
-            set->aligned_size = FFALIGN(set->layout_size,
-                                        s->desc_buf_props.descriptorBufferOffsetAlignment);
-
-            for (int j = 0; j < set->nb_bindings; j++)
-                vk->GetDescriptorSetLayoutBindingOffsetEXT(s->hwctx->act_dev,
-                                                           shd->desc_layout[i],
-                                                           j,
-                                                           &set->binding_offset[j]);
-        }
     }
 
     return 0;
@@ -2444,11 +2404,6 @@ int ff_vk_shader_link(FFVulkanContext *s, FFVulkanShader *shd,
     if (err < 0)
         goto end;
 
-    if (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) {
-        for (int i = 0; i < shd->nb_descriptor_sets; i++)
-            shd->bound_buffer_indices[i] = i;
-    }
-
     if (s->extensions & FF_VK_EXT_SHADER_OBJECT) {
         err = create_shader_object(s, shd, spirv, spirv_len,
                                    &binary_size, entrypoint);
@@ -2526,8 +2481,6 @@ int ff_vk_shader_add_descriptor_set(FFVulkanContext *s, FFVulkanShader *shd,
                                     const FFVulkanDescriptorSetBinding *desc, int nb,
                                     int singular, int print_to_shader_only)
 {
-    int has_sampler = 0;
-
     if (print_to_shader_only)
         goto print;
 
@@ -2541,30 +2494,20 @@ int ff_vk_shader_add_descriptor_set(FFVulkanContext *s, FFVulkanShader *shd,
         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;
-
-    if (!(s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER)) {
-        for (int i = 0; i < nb; i++) {
-            int j;
-            for (j = 0; j < shd->nb_desc_pool_size; j++)
-                if (shd->desc_pool_size[j].type == desc[i].type)
-                    break;
-            if (j >= shd->nb_desc_pool_size) {
-                shd->nb_desc_pool_size++;
-                av_assert1(shd->nb_desc_pool_size < FF_VK_MAX_DESCRIPTOR_TYPES);
-            }
-            shd->desc_pool_size[j].type             = desc[i].type;
-            shd->desc_pool_size[j].descriptorCount += FFMAX(desc[i].elems, 1);
+    for (int i = 0; i < nb; i++) {
+        int j;
+        for (j = 0; j < shd->nb_desc_pool_size; j++)
+            if (shd->desc_pool_size[j].type == desc[i].type)
+                break;
+        if (j >= shd->nb_desc_pool_size) {
+            shd->nb_desc_pool_size++;
+            av_assert1(shd->nb_desc_pool_size < FF_VK_MAX_DESCRIPTOR_TYPES);
         }
+
+        shd->desc_pool_size[j].type             = desc[i].type;
+        shd->desc_pool_size[j].descriptorCount += FFMAX(desc[i].elems, 1);
     }
 
     set->singular = singular;
@@ -2637,8 +2580,6 @@ print:
 int ff_vk_shader_register_exec(FFVulkanContext *s, FFVkExecPool *pool,
                                FFVulkanShader *shd)
 {
-    int err;
-
     if (!shd->nb_descriptor_sets)
         return 0;
 
@@ -2648,32 +2589,7 @@ int ff_vk_shader_register_exec(FFVulkanContext *s, FFVkExecPool *pool,
     sd->shd = shd;
     sd->nb_descriptor_sets = shd->nb_descriptor_sets;
 
-    if (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) {
-        for (int i = 0; i < sd->nb_descriptor_sets; i++) {
-            FFVulkanDescriptorSet *set = &shd->desc_set[i];
-            FFVulkanDescriptorSetData *sdb = &sd->desc_set_buf[i];
-            int nb = set->singular ? 1 : pool->pool_size;
-
-            err = ff_vk_create_buf(s, &sdb->buf,
-                                   set->aligned_size*nb,
-                                   NULL, NULL, set->usage,
-                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
-                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
-                                   VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
-            if (err < 0)
-                return err;
-
-            err = ff_vk_map_buffer(s, &sdb->buf, &sdb->desc_mem, 0);
-            if (err < 0)
-                return err;
-
-            sd->desc_bind[i] = (VkDescriptorBufferBindingInfoEXT) {
-                .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT,
-                .usage = set->usage,
-                .address = sdb->buf.address,
-            };
-        }
-    } else if (!shd->use_push) {
+    if (!shd->use_push) {
         VkResult ret;
         FFVulkanFunctions *vk = &s->vkfn;
         VkDescriptorSetLayout *tmp_layouts;
@@ -2744,25 +2660,6 @@ static inline const FFVulkanShaderData *get_shd_data(FFVkExecContext *e,
     return NULL;
 }
 
-static inline void update_set_descriptor(FFVulkanContext *s, FFVkExecContext *e,
-                                         FFVulkanShader *shd, int set,
-                                         int bind_idx, int array_idx,
-                                         VkDescriptorGetInfoEXT *desc_get_info,
-                                         size_t desc_size)
-{
-    FFVulkanFunctions *vk = &s->vkfn;
-    FFVulkanDescriptorSet *desc_set = &shd->desc_set[set];
-    const FFVulkanShaderData *sd = get_shd_data(e, shd);
-    const size_t exec_offset = desc_set->singular ? 0 : desc_set->aligned_size*e->idx;
-
-    void *desc = sd->desc_set_buf[set].desc_mem +     /* Base */
-                 exec_offset +                        /* Execution context */
-                 desc_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 inline void update_set_pool_write(FFVulkanContext *s, FFVkExecContext *e,
                                          FFVulkanShader *shd, int set,
                                          VkWriteDescriptorSet *write_info)
@@ -2797,60 +2694,20 @@ int ff_vk_shader_update_img(FFVulkanContext *s, FFVkExecContext *e,
 {
     FFVulkanDescriptorSet *desc_set = &shd->desc_set[set];
 
-    if (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) {
-        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, shd, set, bind, offs,
-                              &desc_get_info, desc_size);
-    } else {
-        VkDescriptorImageInfo desc_pool_write_info_img = {
-            .sampler = sampler,
-            .imageView = view,
-            .imageLayout = layout,
-        };
-        VkWriteDescriptorSet desc_pool_write_info = {
-            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
-            .dstBinding = bind,
-            .descriptorCount = 1,
-            .dstArrayElement = offs,
-            .descriptorType = desc_set->binding[bind].descriptorType,
-            .pImageInfo = &desc_pool_write_info_img,
-        };
-        update_set_pool_write(s, e, shd, set, &desc_pool_write_info);
-    }
+    VkDescriptorImageInfo desc_pool_write_info_img = {
+        .sampler = sampler,
+        .imageView = view,
+        .imageLayout = layout,
+    };
+    VkWriteDescriptorSet desc_pool_write_info = {
+        .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+        .dstBinding = bind,
+        .descriptorCount = 1,
+        .dstArrayElement = offs,
+        .descriptorType = desc_set->binding[bind].descriptorType,
+        .pImageInfo = &desc_pool_write_info_img,
+    };
+    update_set_pool_write(s, e, shd, set, &desc_pool_write_info);
 
     return 0;
 }
@@ -2876,60 +2733,20 @@ int ff_vk_shader_update_desc_buffer(FFVulkanContext *s, FFVkExecContext *e,
 {
     FFVulkanDescriptorSet *desc_set = &shd->desc_set[set];
 
-    if (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) {
-        VkDescriptorGetInfoEXT desc_get_info = {
-            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT,
-            .type = desc_set->binding[bind].descriptorType,
-        };
-        VkDescriptorAddressInfoEXT desc_buf_info = {
-            .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT,
-            .address = buf->address + offset,
-            .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, shd, set, bind, elem, &desc_get_info, desc_size);
-    } else {
-        VkDescriptorBufferInfo desc_pool_write_info_buf = {
-            .buffer = buf->buf,
-            .offset = buf->virtual_offset + offset,
-            .range = len,
-        };
-        VkWriteDescriptorSet desc_pool_write_info = {
-            .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
-            .dstBinding = bind,
-            .descriptorCount = 1,
-            .dstArrayElement = elem,
-            .descriptorType = desc_set->binding[bind].descriptorType,
-            .pBufferInfo = &desc_pool_write_info_buf,
-        };
-        update_set_pool_write(s, e, shd, set, &desc_pool_write_info);
-    }
+    VkDescriptorBufferInfo desc_pool_write_info_buf = {
+        .buffer = buf->buf,
+        .offset = buf->virtual_offset + offset,
+        .range = len,
+    };
+    VkWriteDescriptorSet desc_pool_write_info = {
+        .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+        .dstBinding = bind,
+        .descriptorCount = 1,
+        .dstArrayElement = elem,
+        .descriptorType = desc_set->binding[bind].descriptorType,
+        .pBufferInfo = &desc_pool_write_info_buf,
+    };
+    update_set_pool_write(s, e, shd, set, &desc_pool_write_info);
 
     return 0;
 }
@@ -2948,7 +2765,6 @@ void ff_vk_exec_bind_shader(FFVulkanContext *s, FFVkExecContext *e,
                             FFVulkanShader *shd)
 {
     FFVulkanFunctions *vk = &s->vkfn;
-    VkDeviceSize offsets[1024];
     const FFVulkanShaderData *sd = get_shd_data(e, shd);
 
     if (s->extensions & FF_VK_EXT_SHADER_OBJECT) {
@@ -2959,20 +2775,7 @@ void ff_vk_exec_bind_shader(FFVulkanContext *s, FFVkExecContext *e,
     }
 
     if (sd && sd->nb_descriptor_sets) {
-        if (s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) {
-            for (int i = 0; i < sd->nb_descriptor_sets; i++)
-                offsets[i] = shd->desc_set[i].singular ?
-                             0 : shd->desc_set[i].aligned_size*e->idx;
-
-            /* Bind descriptor buffers */
-            vk->CmdBindDescriptorBuffersEXT(e->buf, sd->nb_descriptor_sets,
-                                            sd->desc_bind);
-            /* Binding offsets */
-            vk->CmdSetDescriptorBufferOffsetsEXT(e->buf, shd->bind_point,
-                                                 shd->pipeline_layout,
-                                                 0, sd->nb_descriptor_sets,
-                                                 shd->bound_buffer_indices, offsets);
-        } else if (!shd->use_push) {
+        if (!shd->use_push) {
             vk->CmdBindDescriptorSets(e->buf, shd->bind_point, shd->pipeline_layout,
                                       0, sd->nb_descriptor_sets,
                                       &sd->desc_sets[e->idx*sd->nb_descriptor_sets],
diff --git a/libavutil/vulkan.h b/libavutil/vulkan.h
index 6a30a68280..9d1a53e2be 100644
--- a/libavutil/vulkan.h
+++ b/libavutil/vulkan.h
@@ -258,9 +258,8 @@ typedef struct FFVulkanShader {
     FFVulkanDescriptorSet desc_set[FF_VK_MAX_DESCRIPTOR_SETS];
     int nb_descriptor_sets;
 
-    /* Descriptor buffer */
+    /* Descriptors */
     VkDescriptorSetLayout desc_layout[FF_VK_MAX_DESCRIPTOR_SETS];
-    uint32_t bound_buffer_indices[FF_VK_MAX_DESCRIPTOR_SETS];
 
     /* Descriptor pool */
     int use_push;
diff --git a/libavutil/vulkan_functions.h b/libavutil/vulkan_functions.h
index 79fb02e8e5..d4be2ca4e0 100644
--- a/libavutil/vulkan_functions.h
+++ b/libavutil/vulkan_functions.h
@@ -39,7 +39,6 @@ typedef uint64_t FFVulkanExtensions;
 #define FF_VK_EXT_EXTERNAL_WIN32_MEMORY  (1ULL <<  6) /* VK_KHR_external_memory_win32 */
 #define FF_VK_EXT_EXTERNAL_WIN32_SEM     (1ULL <<  7) /* VK_KHR_external_semaphore_win32 */
 
-#define FF_VK_EXT_DESCRIPTOR_BUFFER      (1ULL <<  8) /* VK_EXT_descriptor_buffer */
 #define FF_VK_EXT_DEVICE_DRM             (1ULL <<  9) /* VK_EXT_physical_device_drm */
 #define FF_VK_EXT_ATOMIC_FLOAT           (1ULL << 10) /* VK_EXT_shader_atomic_float */
 #define FF_VK_EXT_COOP_MATRIX            (1ULL << 11) /* VK_KHR_cooperative_matrix */
@@ -188,13 +187,6 @@ typedef uint64_t FFVulkanExtensions;
     MACRO(1, 1, FF_VK_EXT_NO_FLAG,              DestroyDescriptorPool)                   \
     MACRO(1, 1, FF_VK_EXT_NO_FLAG,              DestroyDescriptorSetLayout)              \
                                                                                          \
-    /* Descriptor buffers */                                                               \
-    MACRO(1, 1, FF_VK_EXT_DESCRIPTOR_BUFFER,    GetDescriptorSetLayoutSizeEXT)             \
-    MACRO(1, 1, FF_VK_EXT_DESCRIPTOR_BUFFER,    GetDescriptorSetLayoutBindingOffsetEXT)    \
-    MACRO(1, 1, FF_VK_EXT_DESCRIPTOR_BUFFER,    GetDescriptorEXT)                          \
-    MACRO(1, 1, FF_VK_EXT_DESCRIPTOR_BUFFER,    CmdBindDescriptorBuffersEXT)               \
-    MACRO(1, 1, FF_VK_EXT_DESCRIPTOR_BUFFER,    CmdSetDescriptorBufferOffsetsEXT)          \
-                                                                                           \
     /* DescriptorUpdateTemplate */                                                       \
     MACRO(1, 1, FF_VK_EXT_NO_FLAG,              UpdateDescriptorSetWithTemplate)         \
     MACRO(1, 1, FF_VK_EXT_NO_FLAG,              CreateDescriptorUpdateTemplate)          \
diff --git a/libavutil/vulkan_loader.h b/libavutil/vulkan_loader.h
index c3c43562a5..45a296e2fa 100644
--- a/libavutil/vulkan_loader.h
+++ b/libavutil/vulkan_loader.h
@@ -78,7 +78,6 @@ static inline uint64_t ff_vk_extensions_to_mask(const char * const *extensions,
         { VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,     FF_VK_EXT_EXTERNAL_WIN32_MEMORY  },
         { VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,  FF_VK_EXT_EXTERNAL_WIN32_SEM     },
 #endif
-        { VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,         FF_VK_EXT_DESCRIPTOR_BUFFER,     },
         { VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,               FF_VK_EXT_VIDEO_QUEUE            },
         { VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME,        FF_VK_EXT_VIDEO_ENCODE_QUEUE     },
         { VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME,        FF_VK_EXT_VIDEO_DECODE_QUEUE     },
-- 
2.52.0


>From 7e5fba110564a2586d2aac4be4dc3dca6dfcd6d4 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 8 Feb 2026 07:46:40 +0100
Subject: [PATCH 32/58] vulkan_ffv1: improve decode report results printout

---
 libavcodec/vulkan_ffv1.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 258fc0e738..260c92836d 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -885,15 +885,20 @@ static void vk_ffv1_free_frame_priv(AVRefStructOpaque _hwctx, void *data)
                                      1, &invalidate_data);
     }
 
+    int slice_error_cnt = 0;
+    int crc_mismatch_cnt = 0;
     for (int i = 0; i < fp->slice_num; i++) {
         uint32_t crc_res = 0;
         if (fp->crc_checked)
             crc_res = AV_RN32(slice_status->mapped_mem + 2*i*sizeof(uint32_t) + 0);
         uint32_t status = AV_RN32(slice_status->mapped_mem + 2*i*sizeof(uint32_t) + 4);
-        if (status || crc_res)
-            av_log(dev_ctx, AV_LOG_ERROR, "Slice %i status: 0x%x, CRC 0x%x\n",
-                   i, status, crc_res);
+        slice_error_cnt += !!status;
+        crc_mismatch_cnt += !!crc_res;
     }
+    if (slice_error_cnt || crc_mismatch_cnt)
+        av_log(dev_ctx, AV_LOG_ERROR, "Decode status: %i slices errored, "
+                                      "%i CRCs mismatched\n",
+               slice_error_cnt, crc_mismatch_cnt);
 
     av_buffer_unref(&fp->slice_state);
     av_buffer_unref(&fp->slice_offset_buf);
-- 
2.52.0


>From e2c38ae110b1f5126a52fc92570c736430ab4b69 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 8 Feb 2026 09:15:42 +0100
Subject: [PATCH 33/58] ffv1enc_vulkan: remove dead code

---
 libavcodec/ffv1enc_vulkan.c        | 77 +++++++++---------------------
 libavcodec/vulkan/ffv1_common.glsl | 23 ---------
 2 files changed, 22 insertions(+), 78 deletions(-)

diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index c81cb91158..dc3ff70e15 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -1035,65 +1035,32 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     if (f->version == 4 && f->micro_version > 4)
         f->micro_version = 3;
 
-    //if (fv->ctx.ac == AC_GOLOMB_RICE) {
-    if (0) {
-        int w_a = FFALIGN(avctx->width, LG_ALIGN_W);
-        int h_a = FFALIGN(avctx->height, LG_ALIGN_H);
-        int w_sl, h_sl;
+    f->num_h_slices = fv->num_h_slices;
+    f->num_v_slices = fv->num_v_slices;
 
-        /* Pixels per line an invocation handles */
-        int ppi = 0;
-        /* Chunk size */
-        int chunks = 0;
-
-        do {
-            if (ppi < 2)
-                ppi++;
-            chunks++;
-            w_sl = w_a / (LG_ALIGN_W*ppi);
-            h_sl = h_a / (LG_ALIGN_H*chunks);
-        } while (w_sl > MAX_SLICES / h_sl);
-
-        av_log(avctx, AV_LOG_VERBOSE, "Slice config: %ix%i, %i total\n",
-               LG_ALIGN_W*ppi, LG_ALIGN_H*chunks, w_sl*h_sl);
-        av_log(avctx, AV_LOG_VERBOSE, "Horizontal slices: %i (%i pixels per invoc)\n",
-               w_sl, ppi);
-        av_log(avctx, AV_LOG_VERBOSE, "Vertical slices: %i (%i chunks)\n",
-               h_sl, chunks);
-
-        f->num_h_slices = w_sl;
-        f->num_v_slices = h_sl;
-
-        fv->ppi = ppi;
-        fv->chunks = chunks;
-    } else {
-        f->num_h_slices = fv->num_h_slices;
-        f->num_v_slices = fv->num_v_slices;
-
-        if (f->num_h_slices <= 0 && f->num_v_slices <= 0) {
-            if (avctx->slices) {
-                err = ff_ffv1_encode_determine_slices(avctx);
-                if (err < 0)
-                    return err;
-            } else {
-                f->num_h_slices = 32;
-                f->num_v_slices = 32;
-            }
-        } else if (f->num_h_slices && f->num_v_slices <= 0) {
-            f->num_v_slices = MAX_SLICES / f->num_h_slices;
-        } else if (f->num_v_slices && f->num_h_slices <= 0) {
-            f->num_h_slices = MAX_SLICES / f->num_v_slices;
+    if (f->num_h_slices <= 0 && f->num_v_slices <= 0) {
+        if (avctx->slices) {
+            err = ff_ffv1_encode_determine_slices(avctx);
+            if (err < 0)
+                return err;
+        } else {
+            f->num_h_slices = 32;
+            f->num_v_slices = 32;
         }
+    } else if (f->num_h_slices && f->num_v_slices <= 0) {
+        f->num_v_slices = MAX_SLICES / f->num_h_slices;
+    } else if (f->num_v_slices && f->num_h_slices <= 0) {
+        f->num_h_slices = MAX_SLICES / f->num_v_slices;
+    }
 
-        f->num_h_slices = FFMIN(f->num_h_slices, avctx->width);
-        f->num_v_slices = FFMIN(f->num_v_slices, avctx->height);
+    f->num_h_slices = FFMIN(f->num_h_slices, avctx->width);
+    f->num_v_slices = FFMIN(f->num_v_slices, avctx->height);
 
-        if (f->num_h_slices * f->num_v_slices > MAX_SLICES) {
-            av_log(avctx, AV_LOG_ERROR, "Too many slices (%i), maximum supported "
-                                        "by the standard is %i\n",
-                   f->num_h_slices * f->num_v_slices, MAX_SLICES);
-            return AVERROR_PATCHWELCOME;
-        }
+    if (f->num_h_slices * f->num_v_slices > MAX_SLICES) {
+        av_log(avctx, AV_LOG_ERROR, "Too many slices (%i), maximum supported "
+                                    "by the standard is %i\n",
+               f->num_h_slices * f->num_v_slices, MAX_SLICES);
+        return AVERROR_PATCHWELCOME;
     }
 
     f->max_slice_count = f->num_h_slices * f->num_v_slices;
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 360890f153..62097f98aa 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -126,29 +126,6 @@ int predict(int L, ivec2 top)
     return mid_pred(L, L + top[1] - top[0], top[1]);
 }
 
-/* { -2, -1 }, { -1, 0, 1 }, 0 */
-int get_context(VTYPE2 cur_l, VTYPE3 top_l, TYPE top2, uint8_t quant_table_idx)
-{
-    const int LT = top_l[0]; /* -1 */
-    const int T  = top_l[1]; /*  0 */
-    const int RT = top_l[2]; /*  1 */
-    const int L  = cur_l[1]; /* -1 */
-
-    int base = quant_table[quant_table_idx][0][(L - LT) & MAX_QUANT_TABLE_MASK] +
-               quant_table[quant_table_idx][1][(LT - T) & MAX_QUANT_TABLE_MASK] +
-               quant_table[quant_table_idx][2][(T - RT) & MAX_QUANT_TABLE_MASK];
-
-    if ((quant_table[quant_table_idx][3][127] == 0) &&
-        (quant_table[quant_table_idx][4][127] == 0))
-        return base;
-
-    const int TT = top2;     /* -2 */
-    const int LL = cur_l[0]; /* -2 */
-    return base +
-           quant_table[quant_table_idx][3][(LL - L) & MAX_QUANT_TABLE_MASK] +
-           quant_table[quant_table_idx][4][(TT - T) & MAX_QUANT_TABLE_MASK];
-}
-
 const uint32_t log2_run[41] = {
      0,  0,  0,  0,  1,  1,  1,  1,
      2,  2,  2,  2,  3,  3,  3,  3,
-- 
2.52.0


>From 149404849b7f5029cec132c40912170f9de45185 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 00:04:43 +0100
Subject: [PATCH 34/58] vulkan_ffv1: use local RangeCoder struct, refactor
 overread checking

---
 libavcodec/vulkan/ffv1_dec.comp.glsl       | 68 +++++++++++++---------
 libavcodec/vulkan/ffv1_dec_setup.comp.glsl | 42 ++++++-------
 libavcodec/vulkan/rangecoder.glsl          | 66 +++++++++------------
 libavcodec/vulkan_ffv1.c                   | 13 +++--
 4 files changed, 95 insertions(+), 94 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 720fa14cd2..2c089dd64d 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -41,46 +41,49 @@ layout (set = 1, binding = 3, scalar) buffer slice_state_buf {
     uint8_t slice_rc_state[];
 };
 
-#define READ(c, idx) get_rac_noadapt(c, idx)
-int get_isymbol(inout RangeCoder c)
+#define READ(idx) get_rac_noadapt(idx)
+int get_isymbol(void)
 {
-    if (READ(c, 0))
+    if (READ(0))
         return 0;
 
-    uint e = 1;
+    int e = 1;
     for (; e < 11; e++) {
-        if (!READ(c, e))
+        if (!READ(e))
             break;
     }
 
     int a = 1;
-    uint i = e;
+    int i = e;
 
     if (bits > 8 && e == 11) {
         do {
             rc_state[10] = zero_one_state[rc_state[10] + 256];
             e++;
-        } while (READ(c, 10));
+        } while (READ(10));
 
         e--;
         i = e - 1;
 
-        a += a + int(READ(c, 31));
+        a <<= 1;
+        a |= int(READ(31));
         for (; i >= 11; i--) {
             rc_state[31] = zero_one_state[rc_state[31] +
                                           (rc_data[31] ? 256 : 0)];
-            a += a + int(READ(c, 31));
+            a <<= 1;
+            a |= int(READ(31));
         }
     }
 
-    i += 20;
-    for (; i >= 22; i--)
-        a += a + int(READ(c, i));
+    a <<= i - 1;
+    i -= 2;
+    for (; i >= 0; i--)
+        a |= int(READ(i + 22)) << i;
 
-    return READ(c, min(e + 10, 21)) ? -a : a;
+    return READ(min(e + 10, 21)) ? -a : a;
 }
 
-void decode_line_pcm(inout SliceContext sc, ivec2 sp, int w, int y, int p)
+void decode_line_pcm(ivec2 sp, int w, int y, int p)
 {
     if (gl_LocalInvocationID.x > 0)
         return;
@@ -97,13 +100,13 @@ void decode_line_pcm(inout SliceContext sc, ivec2 sp, int w, int y, int p)
 
         [[unroll]]
         for (uint i = (rct_offset >> 1); i > 0; i >>= 1)
-            v |= get_rac_equi(sc.c) ? i : 0;
+            v |= get_rac_equi() ? i : 0;
 
         imageStore(dec[p], sp + LADDR(ivec2(x, y)), uvec4(v));
     }
 }
 
-void decode_line(inout SliceContext sc, ivec2 sp, int w,
+void decode_line(ivec2 sp, int w,
                  int y, int p, uint state_off,
                  uint8_t quant_table_idx, int run_index)
 {
@@ -126,7 +129,7 @@ void decode_line(inout SliceContext sc, ivec2 sp, int w,
 
         int diff;
         if (gl_LocalInvocationID.x == 0)
-            diff = get_isymbol(sc.c);
+            diff = get_isymbol();
 
         barrier();
         uint i = gl_LocalInvocationID.x;
@@ -155,14 +158,14 @@ GetBitContext gb;
 void golomb_init(inout SliceContext sc)
 {
     if (version == 3 && micro_version > 1 || version > 3)
-        get_rac_internal(sc.c, sc.c.range * 129 >> 8);
+        get_rac_internal(rc.range * 129 >> 8);
 
-    uint64_t ac_byte_count = sc.c.bytestream - sc.c.bytestream_start - 1;
-    init_get_bits(gb, u8buf(sc.c.bytestream_start + ac_byte_count),
-                  int(sc.c.bytestream_end - sc.c.bytestream_start - ac_byte_count));
+    uint64_t ac_byte_count = rc.bytestream - rc.bytestream_start - 1;
+    init_get_bits(gb, u8buf(rc.bytestream_start + ac_byte_count),
+                  int(rc.bytestream_end - rc.bytestream_start - ac_byte_count));
 }
 
-void decode_line(inout SliceContext sc, ivec2 sp, int w,
+void decode_line(ivec2 sp, int w,
                  int y, int p, uint state_off,
                  uint8_t quant_table_idx, inout int run_index)
 {
@@ -284,7 +287,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
 #ifdef RGB
         for (int y = 0; y < sc.slice_dim.y; y++) {
             for (int p = 0; p < color_planes; p++)
-                decode_line_pcm(sc, sp, w, y, p);
+                decode_line_pcm(sp, w, y, p);
 
             writeout_rgb(sc, sp, w, y, false);
         }
@@ -295,7 +298,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
                 h = ceil_rshift(h, chroma_shift.y);
 
             for (int y = 0; y < h; y++)
-                decode_line_pcm(sc, sp, w, y, p);
+                decode_line_pcm(sp, w, y, p);
         }
 #endif
         return;
@@ -315,7 +318,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
     int run_index = 0;
     for (int y = 0; y < sc.slice_dim.y; y++) {
         for (int p = 0; p < color_planes; p++)
-            decode_line(sc, sp, w, y, p,
+            decode_line(sp, w, y, p,
                         slice_state_off[p], quant_table_idx[p], run_index);
 
         writeout_rgb(sc, sp, w, y, true);
@@ -328,7 +331,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
 
         int run_index = 0;
         for (int y = 0; y < h; y++)
-            decode_line(sc, sp, w, y, p,
+            decode_line(sp, w, y, p,
                         slice_state_off[p], quant_table_idx[p], run_index);
     }
 #endif
@@ -337,9 +340,16 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
 void main(void)
 {
     uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+
+    rc = slice_ctx[slice_idx].c;
+
     decode_slice(slice_ctx[slice_idx], slice_idx);
 
-    uint32_t status = corrupt ? uint32_t(corrupt) : overread;
-    if (status != 0)
-        slice_status[2*slice_idx + 1] = status;
+    if (gl_LocalInvocationID.x > 0)
+        return;
+
+    uint overread = 0;
+    if (rc.bytestream >= (rc.bytestream_end + MAX_OVERREAD))
+        overread = uint(rc.bytestream - rc.bytestream_end);
+    slice_status[2*slice_idx + 1] = overread;
 }
diff --git a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
index cebff23517..56abad0971 100644
--- a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
@@ -37,28 +37,22 @@ layout (set = 1, binding = 2, scalar) writeonly buffer slice_status_buf {
     uint32_t slice_status[];
 };
 
-shared uint8_t setup_state[CONTEXT_SIZE];
 shared uint hdr_sym[4 + 4 + 3];
 const int nb_hdr_sym = 4 + codec_planes + 3;
 
-uint get_usymbol(inout RangeCoder c)
+uint get_usymbol(void)
 {
-    if (get_rac_direct(c, setup_state[0]))
+    if (get_rac_direct(rc_state[0]))
         return 0;
 
     int e = 0;
-    while (get_rac_direct(c, setup_state[1 + min(e, 9)])) { // 1..10
+    while (get_rac_direct(rc_state[1 + min(e, 9)])) // 1..10
         e++;
-        if (e > 31) {
-            corrupt = true;
-            return 0;
-        }
-    }
 
     uint a = 1;
     for (int i = e - 1; i >= 0; i--) {
         a <<= 1;
-        a |= uint(get_rac_direct(c, setup_state[22 + min(i, 9)]));  // 22..31
+        a |= uint(get_rac_direct(rc_state[22 + min(i, 9)]));  // 22..31
     }
 
     return a;
@@ -68,10 +62,10 @@ bool decode_slice_header(inout SliceContext sc)
 {
     [[unroll]]
     for (int i = 0; i < CONTEXT_SIZE; i++)
-        setup_state[i] = uint8_t(128);
+        rc_state[i] = uint8_t(128);
 
     for (int i = 0; i < nb_hdr_sym; i++)
-        hdr_sym[i] = get_usymbol(sc.c);
+        hdr_sym[i] = get_usymbol();
 
     uint sx = hdr_sym[0];
     uint sy = hdr_sym[1];
@@ -79,10 +73,8 @@ bool decode_slice_header(inout SliceContext sc)
     uint sh = hdr_sym[3] + 1;
 
     if (sx < 0 || sy < 0 || sw <= 0 || sh <= 0 ||
-        sx > (gl_NumWorkGroups.x - sw) || sy > (gl_NumWorkGroups.y - sh) ||
-        corrupt) {
+        sx > (gl_NumWorkGroups.x - sw) || sy > (gl_NumWorkGroups.y - sh))
         return true;
-    }
 
     /* Set coordinates */
     uint sxs = slice_coord(img_size.x, sx     , gl_NumWorkGroups.x, chroma_shift.x);
@@ -103,11 +95,11 @@ bool decode_slice_header(inout SliceContext sc)
     }
 
     if (version >= 4) {
-        sc.slice_reset_contexts = get_rac_direct(sc.c, setup_state[0]);
-        sc.slice_coding_mode = get_usymbol(sc.c);
+        sc.slice_reset_contexts = get_rac_direct(rc_state[0]);
+        sc.slice_coding_mode = get_usymbol();
         if (sc.slice_coding_mode != 1 && colorspace == 1) {
-            sc.slice_rct_coef.x = int(get_usymbol(sc.c));
-            sc.slice_rct_coef.y = int(get_usymbol(sc.c));
+            sc.slice_rct_coef.x = int(get_usymbol());
+            sc.slice_rct_coef.y = int(get_usymbol());
             if (sc.slice_rct_coef.x + sc.slice_rct_coef.y > 4)
                 return true;
         }
@@ -123,14 +115,15 @@ void main(void)
     u8buf bs = u8buf(slice_data + slice_offsets[slice_idx].x);
     uint32_t slice_size = slice_offsets[slice_idx].y;
 
-    rac_init_dec(slice_ctx[slice_idx].c,
-                 bs, slice_size);
+    rac_init_dec(bs, slice_size);
 
     if (slice_idx == (gl_NumWorkGroups.x*gl_NumWorkGroups.y - 1))
-        get_rac_equi(slice_ctx[slice_idx].c);
+        get_rac_equi();
 
     decode_slice_header(slice_ctx[slice_idx]);
 
+    slice_ctx[slice_idx].c = rc;
+
     if (has_crc) {
         uint32_t crc = crcref;
         for (int i = 0; i < slice_size; i++)
@@ -139,5 +132,8 @@ void main(void)
         slice_status[2*slice_idx + 0] = crc;
     }
 
-    slice_status[2*slice_idx + 1] = corrupt ? uint32_t(corrupt) : overread;
+    uint overread = 0;
+    if (rc.bytestream >= (rc.bytestream_end + MAX_OVERREAD))
+        overread = uint(rc.bytestream - rc.bytestream_end);
+    slice_status[2*slice_idx + 1] = overread;
 }
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index 545f13d463..95fa6bba29 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -24,6 +24,7 @@
 #define VULKAN_RANGECODER_H
 
 #define CONTEXT_SIZE 32
+#define MAX_OVERREAD 2
 
 layout (set = 0, binding = 0, scalar) readonly buffer rangecoder_buf {
     uint8_t zero_one_state[512];
@@ -40,6 +41,7 @@ struct RangeCoder {
     uint8_t  outstanding_byte;
 };
 
+shared RangeCoder rc;
 shared uint8_t rc_state[CONTEXT_SIZE];
 shared bool rc_data[CONTEXT_SIZE];
 shared bool rc_dec[CONTEXT_SIZE];
@@ -189,74 +191,64 @@ uint rac_terminate(inout RangeCoder c)
     return uint(uint64_t(c.bytestream) - uint64_t(c.bytestream_start));
 }
 
-/* Decoder */
-uint overread = 0;
-bool corrupt = false;
-
-void rac_init_dec(out RangeCoder r, u8buf data, uint buf_size)
+void rac_init_dec(u8buf data, uint buf_size)
 {
-    overread = 0;
-    corrupt = false;
-
     /* Skip priming bytes */
-    rac_init(r, OFFBUF(u8buf, data, 2), buf_size - 2);
+    rac_init(rc, OFFBUF(u8buf, data, 2), buf_size - 2);
 
     u8vec2 prime = u8vec2buf(data).v;
     /* Switch endianness of the priming bytes */
-    r.low = pack16(prime.yx);
+    rc.low = pack16(prime.yx);
 
-    if (r.low >= 0xFF00) {
-        r.low = 0xFF00;
-        r.bytestream_end = uint64_t(data) + 2;
+    if (rc.low >= 0xFF00) {
+        rc.low = 0xFF00;
+        rc.bytestream_end = uint64_t(data) + 2;
     }
 }
 
-void refill(inout RangeCoder c)
+void refill(void)
 {
-    c.range <<= 8;
-    c.low   <<= 8;
-    if (expectEXT(c.bytestream < c.bytestream_end, false)) {
-        c.low |= u8buf(c.bytestream).v;
-        c.bytestream++;
-    } else {
-        overread++;
-    }
+    rc.range <<= 8;
+    rc.low   <<= 8;
+    if (expectEXT(rc.bytestream < rc.bytestream_end, true))
+        rc.low |= u8buf(rc.bytestream).v;
+    rc.bytestream++;
 }
 
-bool get_rac_internal(inout RangeCoder c, const uint range1)
+bool get_rac_internal(const uint range1)
 {
-    uint ranged = c.range - range1;
-    bool bit = c.low >= ranged;
-    c.low -= bit ? ranged : 0;
-    c.range = (bit ? 0 : ranged) + (bit ? range1 : 0);
+    uint ranged = rc.range - range1;
+    bool bit = rc.low >= ranged;
+    rc.low -= bit ? ranged : 0;
+    rc.range = (bit ? 0 : ranged) + (bit ? range1 : 0);
 
-    if (expectEXT(c.range < 0x100, false))
-        refill(c);
+    if (expectEXT(rc.range < 0x100, false))
+        refill();
 
     return bit;
 }
 
-bool get_rac_direct(inout RangeCoder c, inout uint8_t state)
+bool get_rac_direct(inout uint8_t state)
 {
-    bool bit = get_rac_internal(c, c.range * state >> 8);
+    bool bit = get_rac_internal(rc.range * state >> 8);
     state = zero_one_state[state + (bit ? 256 : 0)];
     return bit;
 }
 
-bool get_rac_noadapt(inout RangeCoder c, uint idx)
+bool get_rac_noadapt(uint idx)
 {
     rc_dec[idx] = true;
-    return (rc_data[idx] = get_rac_internal(c, c.range * rc_state[idx] >> 8));
+    return (rc_data[idx] = get_rac_internal(rc.range * rc_state[idx] >> 8));
 }
 
-bool get_rac(inout RangeCoder c, uint64_t state)
+bool get_rac(uint64_t state)
 {
-    return get_rac_direct(c, u8buf(state).v);
+    return get_rac_direct(u8buf(state).v);
 }
 
-bool get_rac_equi(inout RangeCoder c)
+bool get_rac_equi(void)
 {
-    return get_rac_internal(c, c.range >> 1);
+    return get_rac_internal(rc.range >> 1);
 }
 
 #endif /* VULKAN_RANGECODER_H */
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 260c92836d..8f5cab61d0 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -887,18 +887,21 @@ static void vk_ffv1_free_frame_priv(AVRefStructOpaque _hwctx, void *data)
 
     int slice_error_cnt = 0;
     int crc_mismatch_cnt = 0;
+    uint32_t max_overread = 0;
     for (int i = 0; i < fp->slice_num; i++) {
         uint32_t crc_res = 0;
+        uint8_t *ssp = slice_status->mapped_mem + 2*i*sizeof(uint32_t);
         if (fp->crc_checked)
-            crc_res = AV_RN32(slice_status->mapped_mem + 2*i*sizeof(uint32_t) + 0);
-        uint32_t status = AV_RN32(slice_status->mapped_mem + 2*i*sizeof(uint32_t) + 4);
-        slice_error_cnt += !!status;
+            crc_res = AV_RN32(ssp + 0);
+        uint32_t overread = AV_RN32(ssp + 4);
+        max_overread = FFMAX(overread, max_overread);
+        slice_error_cnt += !!overread;
         crc_mismatch_cnt += !!crc_res;
     }
     if (slice_error_cnt || crc_mismatch_cnt)
-        av_log(dev_ctx, AV_LOG_ERROR, "Decode status: %i slices errored, "
+        av_log(dev_ctx, AV_LOG_ERROR, "Decode status: %i slices overread (%i bytes max), "
                                       "%i CRCs mismatched\n",
-               slice_error_cnt, crc_mismatch_cnt);
+               slice_error_cnt, max_overread, crc_mismatch_cnt);
 
     av_buffer_unref(&fp->slice_state);
     av_buffer_unref(&fp->slice_offset_buf);
-- 
2.52.0


>From 545d6651914077b5c013ca1eb423ab70a719db3b Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 04:17:47 +0100
Subject: [PATCH 35/58] ffv1enc: use local RangeCoder struct

---
 libavcodec/vulkan/ffv1_enc.comp.glsl       |  28 +++--
 libavcodec/vulkan/ffv1_enc_setup.comp.glsl |  31 +++--
 libavcodec/vulkan/rangecoder.glsl          | 138 ++++++++++-----------
 3 files changed, 99 insertions(+), 98 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index d50696992e..48f2e33591 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -42,11 +42,11 @@ layout (set = 1, binding = 2, scalar) buffer slice_state_buf {
     uint8_t slice_rc_state[];
 };
 
-#define WRITE(c, off, val) put_rac_direct(c, slice_rc_state[state_off + off], val)
-void put_symbol(inout RangeCoder c, uint state_off, int v)
+#define WRITE(off, val) put_rac_direct(slice_rc_state[state_off + off], val)
+void put_symbol(uint state_off, int v)
 {
     bool is_nil = (v == 0);
-    WRITE(c, 0, is_nil);
+    WRITE(0, is_nil);
     if (is_nil)
         return;
 
@@ -54,13 +54,13 @@ void put_symbol(inout RangeCoder c, uint state_off, int v)
     const int e = findMSB(a);
 
     for (int i = 0; i < e; i++)
-        WRITE(c, 1 + min(i, 9), true);
-    WRITE(c, 1 + min(e, 9), false);
+        WRITE(1 + min(i, 9), true);
+    WRITE(1 + min(e, 9), false);
 
     for (int i = e - 1; i >= 0; i--)
-        WRITE(c, 22 + min(i, 9), bool(bitfieldExtract(a, i, 1)));
+        WRITE(22 + min(i, 9), bool(bitfieldExtract(a, i, 1)));
 
-    WRITE(c, 22 - 11 + min(e, 10), v < 0);
+    WRITE(22 - 11 + min(e, 10), v < 0);
 }
 
 void encode_line_pcm(inout SliceContext sc, readonly uimage2D img,
@@ -80,7 +80,7 @@ void encode_line_pcm(inout SliceContext sc, readonly uimage2D img,
 
         [[unroll]]
         for (uint i = (rct_offset >> 1); i > 0; i >>= 1)
-            put_rac_equi(sc.c, bool(v & i));
+            put_rac_equi(bool(v & i));
     }
 }
 
@@ -109,7 +109,7 @@ void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
 
         uint context_off = state_off + CONTEXT_SIZE*d[0];
 
-        put_symbol(sc.c, context_off, d[1]);
+        put_symbol(context_off, d[1]);
     }
 }
 
@@ -124,9 +124,9 @@ PutBitContext pb;
 
 void init_golomb(inout SliceContext sc)
 {
-    hdr_len = rac_terminate(sc.c);
+    hdr_len = rac_terminate();
     init_put_bits(pb,
-                  OFFBUF(u8buf, sc.c.bytestream_start, hdr_len),
+                  OFFBUF(u8buf, rc.bytestream_start, hdr_len),
                   slice_size_max - hdr_len);
 }
 
@@ -324,10 +324,10 @@ void finalize_slice(inout SliceContext sc, const uint slice_idx)
 #ifdef GOLOMB
     uint32_t enc_len = hdr_len + flush_put_bits(pb);
 #else
-    uint32_t enc_len = rac_terminate(sc.c);
+    uint32_t enc_len = rac_terminate();
 #endif
 
-    u8buf bs = u8buf(sc.c.bytestream_start);
+    u8buf bs = u8buf(rc.bytestream_start);
 
     /* Append slice length */
     u8vec4 enc_len_p = unpack8(enc_len);
@@ -363,6 +363,8 @@ void finalize_slice(inout SliceContext sc, const uint slice_idx)
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+
+    rc = slice_ctx[slice_idx].c;
     encode_slice(slice_ctx[slice_idx], slice_idx);
     finalize_slice(slice_ctx[slice_idx], slice_idx);
 }
diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index 900aa7432c..7f8f72f7ec 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -27,8 +27,6 @@
 #include "common.glsl"
 #include "ffv1_common.glsl"
 
-uint8_t state[CONTEXT_SIZE];
-
 void init_slice(inout SliceContext sc, uint slice_idx)
 {
     /* Set coordinates */
@@ -50,26 +48,25 @@ void init_slice(inout SliceContext sc, uint slice_idx)
     if (!rct_search || (sc.slice_coding_mode == 1))
         sc.slice_rct_coef = ivec2(1, 1);
 
-    rac_init(sc.c,
-             OFFBUF(u8buf, slice_data, slice_idx * slice_size_max),
+    rac_init(OFFBUF(u8buf, slice_data, slice_idx * slice_size_max),
              slice_size_max);
 }
 
-void put_usymbol(inout RangeCoder c, uint v)
+void put_usymbol(uint v)
 {
     bool is_nil = (v == 0);
-    put_rac_direct(c, state[0], is_nil);
+    put_rac_direct(rc_state[0], is_nil);
     if (is_nil)
         return;
 
     const int e = findMSB(v);
 
     for (int i = 0; i < e; i++)
-        put_rac_direct(c, state[1 + min(i, 9)], true);
-    put_rac_direct(c, state[1 + min(e, 9)], false);
+        put_rac_direct(rc_state[1 + min(i, 9)], true);
+    put_rac_direct(rc_state[1 + min(e, 9)], false);
 
     for (int i = e - 1; i >= 0; i--)
-        put_rac_direct(c, state[22 + min(i, 9)], bool(bitfieldExtract(v, i, 1)));
+        put_rac_direct(rc_state[22 + min(i, 9)], bool(bitfieldExtract(v, i, 1)));
 }
 
 shared uint hdr_sym[4 + 4 + 3];
@@ -79,7 +76,7 @@ void write_slice_header(inout SliceContext sc)
 {
     [[unroll]]
     for (int i = 0; i < CONTEXT_SIZE; i++)
-        state[i] = uint8_t(128);
+        rc_state[i] = uint8_t(128);
 
     hdr_sym[0] = gl_WorkGroupID.x;
     hdr_sym[1] = gl_WorkGroupID.y;
@@ -95,21 +92,21 @@ void write_slice_header(inout SliceContext sc)
     hdr_sym[nb_hdr_sym - 1] = sar.y;
 
     for (int i = 0; i < nb_hdr_sym; i++)
-        put_usymbol(sc.c, hdr_sym[i]);
+        put_usymbol(hdr_sym[i]);
 
     if (version >= 4) {
-        put_rac_direct(sc.c, state[0], sc.slice_reset_contexts);
-        put_usymbol(sc.c, sc.slice_coding_mode);
+        put_rac_direct(rc_state[0], sc.slice_reset_contexts);
+        put_usymbol(sc.slice_coding_mode);
         if (sc.slice_coding_mode != 1 && colorspace == 1) {
-            put_usymbol(sc.c, sc.slice_rct_coef.y);
-            put_usymbol(sc.c, sc.slice_rct_coef.x);
+            put_usymbol(sc.slice_rct_coef.y);
+            put_usymbol(sc.slice_rct_coef.x);
         }
     }
 }
 
 void write_frame_header(inout SliceContext sc)
 {
-    put_rac_equi(sc.c, bool(key_frame));
+    put_rac_equi(bool(key_frame));
 }
 
 void main(void)
@@ -122,4 +119,6 @@ void main(void)
         write_frame_header(slice_ctx[slice_idx]);
 
     write_slice_header(slice_ctx[slice_idx]);
+
+    slice_ctx[slice_idx].c = rc;
 }
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index 95fa6bba29..e4fb6557fa 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -46,71 +46,71 @@ shared uint8_t rc_state[CONTEXT_SIZE];
 shared bool rc_data[CONTEXT_SIZE];
 shared bool rc_dec[CONTEXT_SIZE];
 
-void rac_init(out RangeCoder r, u8buf data, uint buf_size)
+void rac_init(u8buf data, uint buf_size)
 {
-    r.bytestream_start = uint64_t(data);
-    r.bytestream = uint64_t(data);
-    r.bytestream_end = uint64_t(data) + buf_size;
-    r.low = 0;
-    r.range = 0xFF00;
-    r.outstanding_count = uint16_t(0);
-    r.outstanding_byte = uint8_t(0xFF);
+    rc.bytestream_start = uint64_t(data);
+    rc.bytestream = uint64_t(data);
+    rc.bytestream_end = uint64_t(data) + buf_size;
+    rc.low = 0;
+    rc.range = 0xFF00;
+    rc.outstanding_count = uint16_t(0);
+    rc.outstanding_byte = uint8_t(0xFF);
 }
 
 #ifdef FULL_RENORM
 /* Full renorm version that can handle outstanding_byte == 0xFF */
-void renorm_encoder(inout RangeCoder c)
+void renorm_encoder(void)
 {
     int bs_cnt = 0;
-    u8buf bytestream = u8buf(c.bytestream);
+    u8buf bytestream = u8buf(rc.bytestream);
 
-    if (c.outstanding_byte == 0xFF) {
-        c.outstanding_byte = uint8_t(c.low >> 8);
-    } else if (c.low <= 0xFF00) {
-        bytestream[bs_cnt++].v = c.outstanding_byte;
-        uint16_t cnt = c.outstanding_count;
+    if (rc.outstanding_byte == 0xFF) {
+        rc.outstanding_byte = uint8_t(rc.low >> 8);
+    } else if (rc.low <= 0xFF00) {
+        bytestream[bs_cnt++].v = rc.outstanding_byte;
+        uint16_t cnt = rc.outstanding_count;
         for (; cnt > 0; cnt--)
             bytestream[bs_cnt++].v = uint8_t(0xFF);
-        c.outstanding_count = uint16_t(0);
-        c.outstanding_byte = uint8_t(c.low >> 8);
-    } else if (c.low >= 0x10000) {
-        bytestream[bs_cnt++].v = c.outstanding_byte + uint8_t(1);
-        uint16_t cnt = c.outstanding_count;
+        rc.outstanding_count = uint16_t(0);
+        rc.outstanding_byte = uint8_t(rc.low >> 8);
+    } else if (rc.low >= 0x10000) {
+        bytestream[bs_cnt++].v = rc.outstanding_byte + uint8_t(1);
+        uint16_t cnt = rc.outstanding_count;
         for (; cnt > 0; cnt--)
             bytestream[bs_cnt++].v = uint8_t(0x00);
-        c.outstanding_count = uint16_t(0);
-        c.outstanding_byte = uint8_t(bitfieldExtract(c.low, 8, 8));
+        rc.outstanding_count = uint16_t(0);
+        rc.outstanding_byte = uint8_t(bitfieldExtract(rc.low, 8, 8));
     } else {
-        c.outstanding_count++;
+        rc.outstanding_count++;
     }
 
-    c.bytestream += bs_cnt;
-    c.range <<= 8;
-    c.low = bitfieldInsert(0, c.low, 8, 8);
+    rc.bytestream += bs_cnt;
+    rc.range <<= 8;
+    rc.low = bitfieldInsert(0, rc.low, 8, 8);
 }
 
 #else
 
 /* Cannot deal with outstanding_byte == -1 in the name of speed */
-void renorm_encoder(inout RangeCoder c)
+void renorm_encoder(void)
 {
-    uint16_t oc = c.outstanding_count + uint16_t(1);
-    uint low = c.low;
+    uint16_t oc = rc.outstanding_count + uint16_t(1);
+    uint low = rc.low;
 
-    c.range <<= 8;
-    c.low = bitfieldInsert(0, low, 8, 8);
+    rc.range <<= 8;
+    rc.low = bitfieldInsert(0, low, 8, 8);
 
     if (low > 0xFF00 && low < 0x10000) {
-        c.outstanding_count = oc;
+        rc.outstanding_count = oc;
         return;
     }
 
-    u8buf bs = u8buf(c.bytestream);
-    uint8_t outstanding_byte = c.outstanding_byte;
+    u8buf bs = u8buf(rc.bytestream);
+    uint8_t outstanding_byte = rc.outstanding_byte;
 
-    c.bytestream        = uint64_t(bs) + oc;
-    c.outstanding_count = uint16_t(0);
-    c.outstanding_byte  = uint8_t(low >> 8);
+    rc.bytestream        = uint64_t(bs) + oc;
+    rc.outstanding_count = uint16_t(0);
+    rc.outstanding_byte  = uint8_t(low >> 8);
 
     uint8_t obs = uint8_t(low > 0xFF00);
     uint8_t fill = obs - uint8_t(1); /* unsigned underflow */
@@ -121,80 +121,80 @@ void renorm_encoder(inout RangeCoder c)
 }
 #endif
 
-void put_rac_internal(inout RangeCoder c, const uint range1, bool bit)
+void put_rac_internal(const uint range1, bool bit)
 {
 #ifdef DEBUG
-    if (range1 >= c.range)
-        debugPrintfEXT("Error: range1 >= c.range");
+    if (range1 >= rc.range)
+        debugPrintfEXT("Error: range1 >= range");
     if (range1 <= 0)
         debugPrintfEXT("Error: range1 <= 0");
 #endif
 
-    uint ranged = c.range - range1;
-    c.low += bit ? ranged : 0;
-    c.range = bit ? range1 : ranged;
+    uint ranged = rc.range - range1;
+    rc.low += bit ? ranged : 0;
+    rc.range = bit ? range1 : ranged;
 
-    if (expectEXT(c.range < 0x100, false))
-        renorm_encoder(c);
+    if (expectEXT(rc.range < 0x100, false))
+        renorm_encoder();
 }
 
-void put_rac_direct(inout RangeCoder c, inout uint8_t state, bool bit)
+void put_rac_direct(inout uint8_t state, bool bit)
 {
-    put_rac_internal(c, (c.range * state) >> 8, bit);
+    put_rac_internal((rc.range * state) >> 8, bit);
     state = zero_one_state[(uint(bit) << 8) + state];
 }
 
-void put_rac(inout RangeCoder c, uint64_t state, bool bit)
+void put_rac(uint64_t state, bool bit)
 {
-    put_rac_direct(c, u8buf(state).v, bit);
+    put_rac_direct(u8buf(state).v, bit);
 }
 
 /* Equiprobable bit */
-void put_rac_equi(inout RangeCoder c, bool bit)
+void put_rac_equi(bool bit)
 {
-    put_rac_internal(c, c.range >> 1, bit);
+    put_rac_internal(rc.range >> 1, bit);
 }
 
-void put_rac_terminate(inout RangeCoder c)
+void put_rac_terminate(void)
 {
-    uint range1 = (c.range * 129) >> 8;
+    uint range1 = (rc.range * 129) >> 8;
 
 #ifdef DEBUG
-    if (range1 >= c.range)
+    if (range1 >= rc.range)
         debugPrintfEXT("Error: range1 >= c.range");
     if (range1 <= 0)
         debugPrintfEXT("Error: range1 <= 0");
 #endif
 
-    c.range -= range1;
-    if (expectEXT(c.range < 0x100, false))
-        renorm_encoder(c);
+    rc.range -= range1;
+    if (expectEXT(rc.range < 0x100, false))
+        renorm_encoder();
 }
 
 /* Return the number of bytes written. */
-uint rac_terminate(inout RangeCoder c)
+uint rac_terminate(void)
 {
-    put_rac_terminate(c);
-    c.range = uint16_t(0xFF);
-    c.low  += 0xFF;
-    renorm_encoder(c);
-    c.range = uint16_t(0xFF);
-    renorm_encoder(c);
+    put_rac_terminate();
+    rc.range = uint16_t(0xFF);
+    rc.low  += 0xFF;
+    renorm_encoder();
+    rc.range = uint16_t(0xFF);
+    renorm_encoder();
 
 #ifdef DEBUG
-    if (c.low != 0)
-        debugPrintfEXT("Error: c.low != 0");
-    if (c.range < 0x100)
+    if (rc.low != 0)
+        debugPrintfEXT("Error: low != 0");
+    if (rc.range < 0x100)
         debugPrintfEXT("Error: range < 0x100");
 #endif
 
-    return uint(uint64_t(c.bytestream) - uint64_t(c.bytestream_start));
+    return uint(uint64_t(rc.bytestream) - uint64_t(rc.bytestream_start));
 }
 
 void rac_init_dec(u8buf data, uint buf_size)
 {
     /* Skip priming bytes */
-    rac_init(rc, OFFBUF(u8buf, data, 2), buf_size - 2);
+    rac_init(OFFBUF(u8buf, data, 2), buf_size - 2);
 
     u8vec2 prime = u8vec2buf(data).v;
     /* Switch endianness of the priming bytes */
-- 
2.52.0


>From dfb45eae4d36c74a02478e05780b7855a840f706 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 04:30:51 +0100
Subject: [PATCH 36/58] ffv1dec: correctly track configured_width/height

The 2 parameters were not synchronized between decoding threads.
---
 libavcodec/ffv1dec.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c
index 630a60595d..28e6ec3c4d 100644
--- a/libavcodec/ffv1dec.c
+++ b/libavcodec/ffv1dec.c
@@ -508,15 +508,15 @@ static int read_header(FFV1Context *f, RangeCoder *c)
         return ret;
 
     if (f->configured_pix_fmt != f->pix_fmt ||
-        f->configured_width != f->avctx->width ||
-        f->configured_height != f->avctx->height ||
+        f->configured_width != f->width ||
+        f->configured_height != f->height ||
         f->configured_ac != f->ac) {
         f->avctx->pix_fmt = get_pixel_format(f);
         if (f->avctx->pix_fmt < 0)
             return AVERROR(EINVAL);
         f->configured_pix_fmt = f->pix_fmt;
-        f->configured_width = f->avctx->width;
-        f->configured_height = f->avctx->height;
+        f->configured_width = f->width;
+        f->configured_height = f->height;
         f->configured_ac = f->ac;
     }
 
@@ -924,6 +924,8 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
     fdst->pix_fmt             = fsrc->pix_fmt;
     fdst->configured_pix_fmt  = fsrc->configured_pix_fmt;
     fdst->configured_ac       = fsrc->configured_ac;
+    fdst->configured_width    = fsrc->configured_width;
+    fdst->configured_height   = fsrc->configured_height;
 
     fdst->ec                  = fsrc->ec;
     fdst->intra               = fsrc->intra;
-- 
2.52.0


>From f1447c755830623832bc6089bbd7f380c25c5eca Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 05:10:52 +0100
Subject: [PATCH 37/58] vulkan_ffv1: unify slice offsets and status into one
 buffer

Reduces allocations.
---
 libavcodec/vulkan_ffv1.c | 68 +++++++++++++++-------------------------
 1 file changed, 26 insertions(+), 42 deletions(-)

diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 8f5cab61d0..22c480a67e 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -61,12 +61,10 @@ typedef struct FFv1VulkanDecodePicture {
     uint32_t slice_state_size;
     uint32_t slice_data_size;
 
-    AVBufferRef *slice_offset_buf;
+    AVBufferRef *slice_feedback_buf;
     uint32_t    *slice_offset;
     int          slice_num;
-
-    AVBufferRef *slice_status_buf;
-    int crc_checked;
+    int          crc_checked;
 } FFv1VulkanDecodePicture;
 
 typedef struct FFv1VulkanDecodeContext {
@@ -81,8 +79,7 @@ typedef struct FFv1VulkanDecodeContext {
     FFVkBuffer crc_buf;
 
     AVBufferPool *slice_state_pool;
-    AVBufferPool *slice_offset_pool;
-    AVBufferPool *slice_status_pool;
+    AVBufferPool *slice_feedback_pool;
 } FFv1VulkanDecodeContext;
 
 static int vk_ffv1_start_frame(AVCodecContext          *avctx,
@@ -150,21 +147,11 @@ static int vk_ffv1_start_frame(AVCodecContext          *avctx,
             return AVERROR(ENOMEM);
     }
 
-    /* Allocate slice offsets buffer */
-    err = ff_vk_get_pooled_buffer(&ctx->s, &fv->slice_offset_pool,
-                                  &fp->slice_offset_buf,
+    /* Allocate slice offsets/status buffer */
+    err = ff_vk_get_pooled_buffer(&ctx->s, &fv->slice_feedback_pool,
+                                  &fp->slice_feedback_buf,
                                   VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                                  NULL, 2*f->slice_count*sizeof(uint32_t),
-                                  VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
-                                  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
-    if (err < 0)
-        return err;
-
-    /* Allocate slice status buffer */
-    err = ff_vk_get_pooled_buffer(&ctx->s, &fv->slice_status_pool,
-                                  &fp->slice_status_buf,
-                                  VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                                  NULL, 2*f->slice_count*sizeof(uint32_t),
+                                  NULL, 2*(2*f->slice_count*sizeof(uint32_t)),
                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
     if (err < 0)
@@ -200,7 +187,7 @@ static int vk_ffv1_decode_slice(AVCodecContext *avctx,
     FFv1VulkanDecodePicture *fp = f->hwaccel_picture_private;
     FFVulkanDecodePicture *vp = &fp->vp;
 
-    FFVkBuffer *slice_offset = (FFVkBuffer *)fp->slice_offset_buf->data;
+    FFVkBuffer *slice_offset = (FFVkBuffer *)fp->slice_feedback_buf->data;
     FFVkBuffer *slices_buf = vp->slices_buf ? (FFVkBuffer *)vp->slices_buf->data : NULL;
 
     if (slices_buf && slices_buf->host_ref) {
@@ -251,8 +238,7 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
 
     FFVkBuffer *slices_buf = (FFVkBuffer *)vp->slices_buf->data;
     FFVkBuffer *slice_state = (FFVkBuffer *)fp->slice_state->data;
-    FFVkBuffer *slice_offset = (FFVkBuffer *)fp->slice_offset_buf->data;
-    FFVkBuffer *slice_status = (FFVkBuffer *)fp->slice_status_buf->data;
+    FFVkBuffer *slice_feedback = (FFVkBuffer *)fp->slice_feedback_buf->data;
 
     VkImageView rct_image_views[AV_NUM_DATA_POINTERS];
 
@@ -301,11 +287,9 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
     }
 
     RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &fp->slice_state, 1, 1));
-    RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &fp->slice_status_buf, 1, 1));
+    RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &fp->slice_feedback_buf, 1, 1));
     RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0));
     vp->slices_buf = NULL;
-    RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &fp->slice_offset_buf, 1, 0));
-    fp->slice_offset_buf = NULL;
 
     /* Entry barrier for the slice state (not preserved between frames) */
     if (!(f->picture.f->flags & AV_FRAME_FLAG_KEY))
@@ -339,13 +323,14 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->setup,
                                     1, 1, 0,
-                                    slice_offset,
+                                    slice_feedback,
                                     0, 2*f->slice_count*sizeof(uint32_t),
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->setup,
                                     1, 2, 0,
-                                    slice_status,
-                                    0, 2*f->slice_count*sizeof(uint32_t),
+                                    slice_feedback,
+                                    2*f->slice_count*sizeof(uint32_t),
+                                    VK_WHOLE_SIZE,
                                     VK_FORMAT_UNDEFINED);
 
     ff_vk_exec_bind_shader(&ctx->s, exec, &fv->setup);
@@ -452,13 +437,14 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
                                     1, 1, 0,
-                                    slice_offset,
+                                    slice_feedback,
                                     0, 2*f->slice_count*sizeof(uint32_t),
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
                                     1, 2, 0,
-                                    slice_status,
-                                    0, 2*f->slice_count*sizeof(uint32_t),
+                                    slice_feedback,
+                                    2*f->slice_count*sizeof(uint32_t),
+                                    VK_WHOLE_SIZE,
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
                                     1, 3, 0,
@@ -766,8 +752,7 @@ static void vk_decode_ffv1_uninit(FFVulkanDecodeShared *ctx)
     ff_vk_free_buf(&ctx->s, &fv->crc_buf);
 
     av_buffer_pool_uninit(&fv->slice_state_pool);
-    av_buffer_pool_uninit(&fv->slice_offset_pool);
-    av_buffer_pool_uninit(&fv->slice_status_pool);
+    av_buffer_pool_uninit(&fv->slice_feedback_pool);
 
     av_freep(&fv);
 }
@@ -869,15 +854,16 @@ static void vk_ffv1_free_frame_priv(AVRefStructOpaque _hwctx, void *data)
 
     FFv1VulkanDecodePicture *fp = data;
     FFVulkanDecodePicture *vp = &fp->vp;
-    FFVkBuffer *slice_status = (FFVkBuffer *)fp->slice_status_buf->data;
+    FFVkBuffer *slice_feedback = (FFVkBuffer *)fp->slice_feedback_buf->data;
+    uint8_t *ssp = slice_feedback->mapped_mem + 2*fp->slice_num*sizeof(uint32_t);
 
     ff_vk_decode_free_frame(dev_ctx, vp);
 
     /* Invalidate slice/output data if needed */
-    if (!(slice_status->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
+    if (!(slice_feedback->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
         VkMappedMemoryRange invalidate_data = {
             .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
-            .memory = slice_status->mem,
+            .memory = slice_feedback->mem,
             .offset = 0,
             .size = 2*fp->slice_num*sizeof(uint32_t),
         };
@@ -890,10 +876,9 @@ static void vk_ffv1_free_frame_priv(AVRefStructOpaque _hwctx, void *data)
     uint32_t max_overread = 0;
     for (int i = 0; i < fp->slice_num; i++) {
         uint32_t crc_res = 0;
-        uint8_t *ssp = slice_status->mapped_mem + 2*i*sizeof(uint32_t);
         if (fp->crc_checked)
-            crc_res = AV_RN32(ssp + 0);
-        uint32_t overread = AV_RN32(ssp + 4);
+            crc_res = AV_RN32(ssp + 2*i*sizeof(uint32_t) + 0);
+        uint32_t overread = AV_RN32(ssp + 2*i*sizeof(uint32_t) + 4);
         max_overread = FFMAX(overread, max_overread);
         slice_error_cnt += !!overread;
         crc_mismatch_cnt += !!crc_res;
@@ -904,8 +889,7 @@ static void vk_ffv1_free_frame_priv(AVRefStructOpaque _hwctx, void *data)
                slice_error_cnt, max_overread, crc_mismatch_cnt);
 
     av_buffer_unref(&fp->slice_state);
-    av_buffer_unref(&fp->slice_offset_buf);
-    av_buffer_unref(&fp->slice_status_buf);
+    av_buffer_unref(&fp->slice_feedback_buf);
 }
 
 const FFHWAccel ff_ffv1_vulkan_hwaccel = {
-- 
2.52.0


>From 218ba01d45f57195c36a96239a662c91efba50a9 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 06:09:52 +0100
Subject: [PATCH 38/58] vulkan/rangecoder: clean up unused functions and
 redundant fields

---
 libavcodec/vulkan/ffv1_dec.comp.glsl       | 11 +++++++----
 libavcodec/vulkan/ffv1_dec_setup.comp.glsl |  8 ++++----
 libavcodec/vulkan/ffv1_enc.comp.glsl       | 14 +++++++------
 libavcodec/vulkan/ffv1_enc_setup.comp.glsl | 10 +++++-----
 libavcodec/vulkan/rangecoder.glsl          | 23 +++++-----------------
 5 files changed, 29 insertions(+), 37 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 2c089dd64d..cee1fb477a 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -35,13 +35,15 @@ layout (set = 1, binding = 2, scalar) writeonly buffer slice_status_buf {
 };
 layout (set = 1, binding = 4) uniform uimage2D dec[];
 
+uint64_t slice_start;
+
 #ifndef GOLOMB
 
 layout (set = 1, binding = 3, scalar) buffer slice_state_buf {
     uint8_t slice_rc_state[];
 };
 
-#define READ(idx) get_rac_noadapt(idx)
+#define READ(idx) get_rac_state(idx)
 int get_isymbol(void)
 {
     if (READ(0))
@@ -160,9 +162,9 @@ void golomb_init(inout SliceContext sc)
     if (version == 3 && micro_version > 1 || version > 3)
         get_rac_internal(rc.range * 129 >> 8);
 
-    uint64_t ac_byte_count = rc.bytestream - rc.bytestream_start - 1;
-    init_get_bits(gb, u8buf(rc.bytestream_start + ac_byte_count),
-                  int(rc.bytestream_end - rc.bytestream_start - ac_byte_count));
+    uint64_t ac_byte_count = rc.bytestream - slice_start - 1;
+    init_get_bits(gb, u8buf(slice_start + ac_byte_count),
+                  int(rc.bytestream_end - slice_start - ac_byte_count));
 }
 
 void decode_line(ivec2 sp, int w,
@@ -340,6 +342,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
 void main(void)
 {
     uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+    slice_start = uint64_t(slice_data) + slice_offsets[slice_idx].x;
 
     rc = slice_ctx[slice_idx].c;
 
diff --git a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
index 56abad0971..e3cf45c8ed 100644
--- a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
@@ -42,17 +42,17 @@ const int nb_hdr_sym = 4 + codec_planes + 3;
 
 uint get_usymbol(void)
 {
-    if (get_rac_direct(rc_state[0]))
+    if (get_rac(rc_state[0]))
         return 0;
 
     int e = 0;
-    while (get_rac_direct(rc_state[1 + min(e, 9)])) // 1..10
+    while (get_rac(rc_state[1 + min(e, 9)])) // 1..10
         e++;
 
     uint a = 1;
     for (int i = e - 1; i >= 0; i--) {
         a <<= 1;
-        a |= uint(get_rac_direct(rc_state[22 + min(i, 9)]));  // 22..31
+        a |= uint(get_rac(rc_state[22 + min(i, 9)]));  // 22..31
     }
 
     return a;
@@ -95,7 +95,7 @@ bool decode_slice_header(inout SliceContext sc)
     }
 
     if (version >= 4) {
-        sc.slice_reset_contexts = get_rac_direct(rc_state[0]);
+        sc.slice_reset_contexts = get_rac(rc_state[0]);
         sc.slice_coding_mode = get_usymbol();
         if (sc.slice_coding_mode != 1 && colorspace == 1) {
             sc.slice_rct_coef.x = int(get_usymbol());
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index 48f2e33591..c2a1d7125d 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -36,13 +36,15 @@ layout (set = 1, binding = 1, scalar) writeonly buffer slice_results_buf {
 };
 layout (set = 1, binding = 3) uniform uimage2D src[];
 
+uint64_t slice_start;
+
 #ifndef GOLOMB
 
 layout (set = 1, binding = 2, scalar) buffer slice_state_buf {
     uint8_t slice_rc_state[];
 };
 
-#define WRITE(off, val) put_rac_direct(slice_rc_state[state_off + off], val)
+#define WRITE(off, val) put_rac(slice_rc_state[state_off + off], val)
 void put_symbol(uint state_off, int v)
 {
     bool is_nil = (v == 0);
@@ -124,9 +126,8 @@ PutBitContext pb;
 
 void init_golomb(inout SliceContext sc)
 {
-    hdr_len = rac_terminate();
-    init_put_bits(pb,
-                  OFFBUF(u8buf, rc.bytestream_start, hdr_len),
+    hdr_len = rac_terminate(slice_start);
+    init_put_bits(pb, OFFBUF(u8buf, slice_start, hdr_len),
                   slice_size_max - hdr_len);
 }
 
@@ -324,10 +325,10 @@ void finalize_slice(inout SliceContext sc, const uint slice_idx)
 #ifdef GOLOMB
     uint32_t enc_len = hdr_len + flush_put_bits(pb);
 #else
-    uint32_t enc_len = rac_terminate();
+    uint32_t enc_len = rac_terminate(slice_start);
 #endif
 
-    u8buf bs = u8buf(rc.bytestream_start);
+    u8buf bs = u8buf(slice_start);
 
     /* Append slice length */
     u8vec4 enc_len_p = unpack8(enc_len);
@@ -363,6 +364,7 @@ void finalize_slice(inout SliceContext sc, const uint slice_idx)
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+    slice_start = uint64_t(slice_data) + slice_idx*slice_size_max;
 
     rc = slice_ctx[slice_idx].c;
     encode_slice(slice_ctx[slice_idx], slice_idx);
diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index 7f8f72f7ec..c57ba4074b 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -55,18 +55,18 @@ void init_slice(inout SliceContext sc, uint slice_idx)
 void put_usymbol(uint v)
 {
     bool is_nil = (v == 0);
-    put_rac_direct(rc_state[0], is_nil);
+    put_rac(rc_state[0], is_nil);
     if (is_nil)
         return;
 
     const int e = findMSB(v);
 
     for (int i = 0; i < e; i++)
-        put_rac_direct(rc_state[1 + min(i, 9)], true);
-    put_rac_direct(rc_state[1 + min(e, 9)], false);
+        put_rac(rc_state[1 + min(i, 9)], true);
+    put_rac(rc_state[1 + min(e, 9)], false);
 
     for (int i = e - 1; i >= 0; i--)
-        put_rac_direct(rc_state[22 + min(i, 9)], bool(bitfieldExtract(v, i, 1)));
+        put_rac(rc_state[22 + min(i, 9)], bool(bitfieldExtract(v, i, 1)));
 }
 
 shared uint hdr_sym[4 + 4 + 3];
@@ -95,7 +95,7 @@ void write_slice_header(inout SliceContext sc)
         put_usymbol(hdr_sym[i]);
 
     if (version >= 4) {
-        put_rac_direct(rc_state[0], sc.slice_reset_contexts);
+        put_rac(rc_state[0], sc.slice_reset_contexts);
         put_usymbol(sc.slice_coding_mode);
         if (sc.slice_coding_mode != 1 && colorspace == 1) {
             put_usymbol(sc.slice_rct_coef.y);
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index e4fb6557fa..549e0f0dda 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -31,7 +31,6 @@ layout (set = 0, binding = 0, scalar) readonly buffer rangecoder_buf {
 };
 
 struct RangeCoder {
-    uint64_t bytestream_start;
     uint64_t bytestream;
     uint64_t bytestream_end;
 
@@ -48,7 +47,6 @@ shared bool rc_dec[CONTEXT_SIZE];
 
 void rac_init(u8buf data, uint buf_size)
 {
-    rc.bytestream_start = uint64_t(data);
     rc.bytestream = uint64_t(data);
     rc.bytestream_end = uint64_t(data) + buf_size;
     rc.low = 0;
@@ -138,18 +136,12 @@ void put_rac_internal(const uint range1, bool bit)
         renorm_encoder();
 }
 
-void put_rac_direct(inout uint8_t state, bool bit)
+void put_rac(inout uint8_t state, bool bit)
 {
     put_rac_internal((rc.range * state) >> 8, bit);
     state = zero_one_state[(uint(bit) << 8) + state];
 }
 
-void put_rac(uint64_t state, bool bit)
-{
-    put_rac_direct(u8buf(state).v, bit);
-}
-
-/* Equiprobable bit */
 void put_rac_equi(bool bit)
 {
     put_rac_internal(rc.range >> 1, bit);
@@ -172,7 +164,7 @@ void put_rac_terminate(void)
 }
 
 /* Return the number of bytes written. */
-uint rac_terminate(void)
+uint rac_terminate(uint64_t bytestream_start)
 {
     put_rac_terminate();
     rc.range = uint16_t(0xFF);
@@ -188,7 +180,7 @@ uint rac_terminate(void)
         debugPrintfEXT("Error: range < 0x100");
 #endif
 
-    return uint(uint64_t(rc.bytestream) - uint64_t(rc.bytestream_start));
+    return uint(uint64_t(rc.bytestream) - bytestream_start);
 }
 
 void rac_init_dec(u8buf data, uint buf_size)
@@ -228,24 +220,19 @@ bool get_rac_internal(const uint range1)
     return bit;
 }
 
-bool get_rac_direct(inout uint8_t state)
+bool get_rac(inout uint8_t state)
 {
     bool bit = get_rac_internal(rc.range * state >> 8);
     state = zero_one_state[state + (bit ? 256 : 0)];
     return bit;
 }
 
-bool get_rac_noadapt(uint idx)
+bool get_rac_state(uint idx)
 {
     rc_dec[idx] = true;
     return (rc_data[idx] = get_rac_internal(rc.range * rc_state[idx] >> 8));
 }
 
-bool get_rac(uint64_t state)
-{
-    return get_rac_direct(u8buf(state).v);
-}
-
 bool get_rac_equi(void)
 {
     return get_rac_internal(rc.range >> 1);
-- 
2.52.0


>From 4e765917e8350299106b061e872ecd4b10464511 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 07:59:18 +0100
Subject: [PATCH 39/58] vulkan_ffv1: overhaul the synchronization

---
 libavcodec/vulkan_ffv1.c | 183 ++++++++++++++++++---------------------
 1 file changed, 84 insertions(+), 99 deletions(-)

diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 22c480a67e..40384c4baa 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -230,9 +230,6 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                  !(sw_format == AV_PIX_FMT_YA8);
     int color_planes = av_pix_fmt_desc_get(avctx->sw_pix_fmt)->nb_components;
 
-    FFVulkanShader *reset_shader;
-    FFVulkanShader *decode_shader;
-
     FFv1VulkanDecodePicture *fp = f->hwaccel_picture_private;
     FFVulkanDecodePicture *vp = &fp->vp;
 
@@ -242,9 +239,6 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
 
     VkImageView rct_image_views[AV_NUM_DATA_POINTERS];
 
-    AVFrame *decode_dst = is_rgb ? vp->dpb_frame : f->picture.f;
-    VkImageView *decode_dst_view = is_rgb ? rct_image_views : vp->view.out;
-
     VkImageMemoryBarrier2 img_bar[37];
     int nb_img_bar = 0;
     VkBufferMemoryBarrier2 buf_bar[8];
@@ -269,12 +263,6 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
         RET(ff_vk_exec_add_dep_frame(&ctx->s, exec, vp->dpb_frame,
                                      VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
                                      VK_PIPELINE_STAGE_2_CLEAR_BIT));
-        ff_vk_frame_barrier(&ctx->s, exec, decode_dst, img_bar, &nb_img_bar,
-                            VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
-                            VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
-                            VK_ACCESS_2_TRANSFER_WRITE_BIT,
-                            VK_IMAGE_LAYOUT_GENERAL,
-                            VK_QUEUE_FAMILY_IGNORED);
     }
 
     if (!(f->picture.f->flags & AV_FRAME_FLAG_KEY)) {
@@ -291,29 +279,11 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
     RET(ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0));
     vp->slices_buf = NULL;
 
-    /* Entry barrier for the slice state (not preserved between frames) */
-    if (!(f->picture.f->flags & AV_FRAME_FLAG_KEY))
-        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
-                          ALL_COMMANDS_BIT, NONE_KHR, NONE_KHR,
-                          COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT,
-                                              SHADER_STORAGE_WRITE_BIT,
-                          0, fp->slice_data_size*f->slice_count);
-    else
-        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
-                          COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT,
-                                              SHADER_STORAGE_WRITE_BIT,
-                          COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT,
-                                              SHADER_STORAGE_WRITE_BIT,
-                          0, fp->slice_data_size*f->slice_count);
-    vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
-        .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
-        .pImageMemoryBarriers = img_bar,
-        .imageMemoryBarrierCount = nb_img_bar,
-        .pBufferMemoryBarriers = buf_bar,
-        .bufferMemoryBarrierCount = nb_buf_bar,
-    });
-    nb_buf_bar = 0;
-    nb_img_bar = 0;
+    AVVkFrame *vkf = (AVVkFrame *)f->picture.f->data[0];
+    for (int i = 0; i < ff_vk_count_images(vkf); i++) {
+        vkf->layout[i] = VK_IMAGE_LAYOUT_UNDEFINED;
+        vkf->access[i] = VK_ACCESS_2_NONE;
+    }
 
     /* Setup shader */
     ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->setup,
@@ -367,7 +337,29 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
     vk->CmdDispatch(exec->buf, f->num_h_slices, f->num_v_slices, 1);
 
     if (is_rgb) {
-        AVVkFrame *vkf = (AVVkFrame *)vp->dpb_frame->data[0];
+        vkf = (AVVkFrame *)vp->dpb_frame->data[0];
+        for (int i = 0; i < 4; i++) {
+            vkf->layout[i] = VK_IMAGE_LAYOUT_UNDEFINED;
+            vkf->access[i] = VK_ACCESS_2_NONE;
+        }
+
+        ff_vk_frame_barrier(&ctx->s, exec, vp->dpb_frame, img_bar, &nb_img_bar,
+                            VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+                            VK_PIPELINE_STAGE_2_CLEAR_BIT,
+                            VK_ACCESS_2_TRANSFER_WRITE_BIT,
+                            VK_IMAGE_LAYOUT_GENERAL,
+                            VK_QUEUE_FAMILY_IGNORED);
+
+        vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+            .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+            .pImageMemoryBarriers = img_bar,
+            .imageMemoryBarrierCount = nb_img_bar,
+            .pBufferMemoryBarriers = buf_bar,
+            .bufferMemoryBarrierCount = nb_buf_bar,
+        });
+        nb_img_bar = 0;
+        nb_buf_bar = 0;
+
         for (int i = 0; i < color_planes; i++)
             vk->CmdClearColorImage(exec->buf, vkf->img[i], VK_IMAGE_LAYOUT_GENERAL,
                                    &((VkClearColorValue) { 0 }),
@@ -378,43 +370,21 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                                    }));
     }
 
-    /* Reset shader */
-    reset_shader = &fv->reset;
-    ff_vk_shader_update_desc_buffer(&ctx->s, exec, reset_shader,
-                                    1, 0, 0,
-                                    slice_state,
-                                    0, fp->slice_data_size*f->slice_count,
-                                    VK_FORMAT_UNDEFINED);
-    ff_vk_shader_update_desc_buffer(&ctx->s, exec, reset_shader,
-                                    1, 1, 0,
-                                    slice_state,
-                                    f->slice_count*fp->slice_data_size,
-                                    VK_WHOLE_SIZE,
-                                    VK_FORMAT_UNDEFINED);
-
-    ff_vk_exec_bind_shader(&ctx->s, exec, reset_shader);
-    ff_vk_shader_update_push_const(&ctx->s, exec, reset_shader,
-                                   VK_SHADER_STAGE_COMPUTE_BIT,
-                                   0, sizeof(FFv1ShaderParams), &pd);
-
     /* Sync between setup and reset shaders */
     ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
                       COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT,
                                           SHADER_STORAGE_WRITE_BIT,
                       COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT, NONE_KHR,
                       0, fp->slice_data_size*f->slice_count);
-    /* Probability data barrier */
+
+    /* Probability data barrier for P-frames */
     if (!(f->picture.f->flags & AV_FRAME_FLAG_KEY))
-        ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
-                          ALL_COMMANDS_BIT, NONE_KHR, NONE_KHR,
-                          COMPUTE_SHADER_BIT, SHADER_STORAGE_WRITE_BIT, NONE_KHR,
-                          fp->slice_data_size*f->slice_count, VK_WHOLE_SIZE);
-    else
         ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
                           COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT,
                                               SHADER_STORAGE_WRITE_BIT,
                           COMPUTE_SHADER_BIT, SHADER_STORAGE_WRITE_BIT, NONE_KHR,
                           fp->slice_data_size*f->slice_count, VK_WHOLE_SIZE);
+
     vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
         .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
         .pImageMemoryBarriers = img_bar,
@@ -425,62 +395,34 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
     nb_buf_bar = 0;
     nb_img_bar = 0;
 
-    vk->CmdDispatch(exec->buf, f->num_h_slices, f->num_v_slices,
-                    f->plane_count);
-
-    /* Decode */
-    decode_shader = &fv->decode;
-    ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
+    /* Reset shader */
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->reset,
                                     1, 0, 0,
                                     slice_state,
                                     0, fp->slice_data_size*f->slice_count,
                                     VK_FORMAT_UNDEFINED);
-    ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->reset,
                                     1, 1, 0,
-                                    slice_feedback,
-                                    0, 2*f->slice_count*sizeof(uint32_t),
-                                    VK_FORMAT_UNDEFINED);
-    ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
-                                    1, 2, 0,
-                                    slice_feedback,
-                                    2*f->slice_count*sizeof(uint32_t),
-                                    VK_WHOLE_SIZE,
-                                    VK_FORMAT_UNDEFINED);
-    ff_vk_shader_update_desc_buffer(&ctx->s, exec, decode_shader,
-                                    1, 3, 0,
                                     slice_state,
                                     f->slice_count*fp->slice_data_size,
                                     VK_WHOLE_SIZE,
                                     VK_FORMAT_UNDEFINED);
 
-    ff_vk_shader_update_img_array(&ctx->s, exec, decode_shader,
-                                  decode_dst, decode_dst_view,
-                                  1, 4,
-                                  VK_IMAGE_LAYOUT_GENERAL,
-                                  VK_NULL_HANDLE);
-    if (is_rgb)
-        ff_vk_shader_update_img_array(&ctx->s, exec, decode_shader,
-                                      f->picture.f, vp->view.out,
-                                      1, 5,
-                                      VK_IMAGE_LAYOUT_GENERAL,
-                                      VK_NULL_HANDLE);
-
-    ff_vk_exec_bind_shader(&ctx->s, exec, decode_shader);
-    ff_vk_shader_update_push_const(&ctx->s, exec, decode_shader,
+    ff_vk_exec_bind_shader(&ctx->s, exec, &fv->reset);
+    ff_vk_shader_update_push_const(&ctx->s, exec, &fv->reset,
                                    VK_SHADER_STAGE_COMPUTE_BIT,
                                    0, sizeof(FFv1ShaderParams), &pd);
 
+    vk->CmdDispatch(exec->buf, f->num_h_slices, f->num_v_slices,
+                    f->plane_count);
+
     /* Sync probabilities between reset and decode shaders */
-    ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
-                      COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT, NONE_KHR,
-                      COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT,
-                                          SHADER_STORAGE_WRITE_BIT,
-                      0, fp->slice_data_size*f->slice_count);
     ff_vk_buf_barrier(buf_bar[nb_buf_bar++], slice_state,
                       COMPUTE_SHADER_BIT, SHADER_STORAGE_WRITE_BIT, NONE_KHR,
                       COMPUTE_SHADER_BIT, SHADER_STORAGE_READ_BIT,
                                           SHADER_STORAGE_WRITE_BIT,
                       fp->slice_data_size*f->slice_count, VK_WHOLE_SIZE);
+
     /* Input frame barrier */
     ff_vk_frame_barrier(&ctx->s, exec, f->picture.f, img_bar, &nb_img_bar,
                         VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
@@ -491,7 +433,7 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
                         VK_QUEUE_FAMILY_IGNORED);
     if (is_rgb)
         ff_vk_frame_barrier(&ctx->s, exec, vp->dpb_frame, img_bar, &nb_img_bar,
-                            VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+                            VK_PIPELINE_STAGE_2_CLEAR_BIT,
                             VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
                             VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
                             VK_IMAGE_LAYOUT_GENERAL,
@@ -507,6 +449,49 @@ static int vk_ffv1_end_frame(AVCodecContext *avctx)
     nb_img_bar = 0;
     nb_buf_bar = 0;
 
+    /* Decode */
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->decode,
+                                    1, 0, 0,
+                                    slice_state,
+                                    0, fp->slice_data_size*f->slice_count,
+                                    VK_FORMAT_UNDEFINED);
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->decode,
+                                    1, 1, 0,
+                                    slice_feedback,
+                                    0, 2*f->slice_count*sizeof(uint32_t),
+                                    VK_FORMAT_UNDEFINED);
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->decode,
+                                    1, 2, 0,
+                                    slice_feedback,
+                                    2*f->slice_count*sizeof(uint32_t),
+                                    VK_WHOLE_SIZE,
+                                    VK_FORMAT_UNDEFINED);
+    ff_vk_shader_update_desc_buffer(&ctx->s, exec, &fv->decode,
+                                    1, 3, 0,
+                                    slice_state,
+                                    f->slice_count*fp->slice_data_size,
+                                    VK_WHOLE_SIZE,
+                                    VK_FORMAT_UNDEFINED);
+
+    AVFrame *decode_dst = is_rgb ? vp->dpb_frame : f->picture.f;
+    VkImageView *decode_dst_view = is_rgb ? rct_image_views : vp->view.out;
+    ff_vk_shader_update_img_array(&ctx->s, exec, &fv->decode,
+                                  decode_dst, decode_dst_view,
+                                  1, 4,
+                                  VK_IMAGE_LAYOUT_GENERAL,
+                                  VK_NULL_HANDLE);
+    if (is_rgb)
+        ff_vk_shader_update_img_array(&ctx->s, exec, &fv->decode,
+                                      f->picture.f, vp->view.out,
+                                      1, 5,
+                                      VK_IMAGE_LAYOUT_GENERAL,
+                                      VK_NULL_HANDLE);
+
+    ff_vk_exec_bind_shader(&ctx->s, exec, &fv->decode);
+    ff_vk_shader_update_push_const(&ctx->s, exec, &fv->decode,
+                                   VK_SHADER_STAGE_COMPUTE_BIT,
+                                   0, sizeof(FFv1ShaderParams), &pd);
+
     vk->CmdDispatch(exec->buf, f->num_h_slices, f->num_v_slices, 1);
 
     err = ff_vk_exec_submit(&ctx->s, exec);
-- 
2.52.0


>From 6635695f55c93761a1d1addb410d5b79c9bc8c32 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 08:11:04 +0100
Subject: [PATCH 40/58] vulkan/ffv1_common: use scalar alignment for the base
 slice structure

Scalar is the fastest for modern GPUs to use.
---
 libavcodec/vulkan/ffv1_common.glsl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 62097f98aa..9e793b4290 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -93,7 +93,7 @@ struct SliceContext {
     bool slice_reset_contexts;
 };
 
-layout (set = 1, binding = 0) buffer slice_ctx_buf {
+layout (set = 1, binding = 0, scalar) buffer slice_ctx_buf {
     SliceContext slice_ctx[];
 };
 
-- 
2.52.0


>From 3ea3e338a6fe4f933b9a5c1b899f349ac0517bd2 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 09:07:57 +0100
Subject: [PATCH 41/58] ffv1enc_vulkan: fix Golomb encoding

The issue is that the PB buffer address for Golomb may not be aligned
to mod 4.
---
 libavcodec/vulkan/ffv1_enc_golomb.comp.glsl     | 1 +
 libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
index a120564602..19dba23afd 100644
--- a/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_golomb.comp.glsl
@@ -23,5 +23,6 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
+#define PB_UNALIGNED
 #define GOLOMB
 #include "ffv1_enc.comp.glsl"
diff --git a/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl b/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
index 8efffd19e8..eba2ec30fe 100644
--- a/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_rgb_golomb.comp.glsl
@@ -23,5 +23,6 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
+#define PB_UNALIGNED
 #define GOLOMB
 #include "ffv1_enc_rgb.comp.glsl"
-- 
2.52.0


>From 93020f51f629f18d982de79773a677b6dc8ca1c8 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 14:47:35 +0100
Subject: [PATCH 42/58] vulkan/ffv1: mark buffers as uniform/readonly when
 needed

Should be a speedup in most cases.
---
 libavcodec/ffv1_vulkan.c                      |  1 +
 libavcodec/ffv1enc_vulkan.c                   |  8 ++++----
 libavcodec/vulkan/ffv1_common.glsl            | 10 +++++++++-
 libavcodec/vulkan/ffv1_dec.comp.glsl          |  6 +++---
 libavcodec/vulkan/ffv1_dec_reset.comp.glsl    |  1 +
 libavcodec/vulkan/ffv1_enc.comp.glsl          | 20 +++++++++----------
 .../vulkan/ffv1_enc_rct_search.comp.glsl      |  1 +
 libavcodec/vulkan/ffv1_enc_reset.comp.glsl    |  1 +
 libavcodec/vulkan/rangecoder.glsl             |  8 +++++++-
 libavcodec/vulkan_ffv1.c                      |  4 ++--
 10 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/libavcodec/ffv1_vulkan.c b/libavcodec/ffv1_vulkan.c
index a67eb3e15d..368e66b025 100644
--- a/libavcodec/ffv1_vulkan.c
+++ b/libavcodec/ffv1_vulkan.c
@@ -85,6 +85,7 @@ static int init_state_transition_data(FFVulkanContext *s,
     RET(ff_vk_create_buf(s, vkb,
                          buf_len,
                          NULL, NULL,
+                         VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
                          VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
                          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index dc3ff70e15..ee426c3985 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -793,7 +793,7 @@ static int init_rct_search_shader(AVCodecContext *avctx, VkSpecializationInfo *s
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
@@ -836,7 +836,7 @@ static int init_setup_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
@@ -876,7 +876,7 @@ static int init_reset_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
@@ -923,7 +923,7 @@ static int init_encode_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
         { /* quant_buf */
diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 9e793b4290..536a37b093 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -93,7 +93,15 @@ struct SliceContext {
     bool slice_reset_contexts;
 };
 
-layout (set = 1, binding = 0, scalar) buffer slice_ctx_buf {
+#if !defined(SB_QUALI)
+#if (defined(ENCODE) || defined(DECODE))
+#define SB_QUALI readonly
+#else
+#define SB_QUALI
+#endif
+#endif
+
+layout (set = 1, binding = 0, scalar) SB_QUALI buffer slice_ctx_buf {
     SliceContext slice_ctx[];
 };
 
diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index cee1fb477a..a08c067433 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -157,7 +157,7 @@ layout (set = 1, binding = 3, scalar) buffer slice_state_buf {
 
 GetBitContext gb;
 
-void golomb_init(inout SliceContext sc)
+void golomb_init(void)
 {
     if (version == 3 && micro_version > 1 || version > 3)
         get_rac_internal(rc.range * 129 >> 8);
@@ -274,7 +274,7 @@ void writeout_rgb(in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct)
 }
 #endif
 
-void decode_slice(inout SliceContext sc, const uint slice_idx)
+void decode_slice(in SliceContext sc, uint slice_idx)
 {
     int w = sc.slice_dim.x;
     ivec2 sp = sc.slice_pos;
@@ -313,7 +313,7 @@ void decode_slice(inout SliceContext sc, const uint slice_idx)
 
 #ifdef GOLOMB
     slice_state_off >>= 3; // division by VLC_STATE_SIZE
-    golomb_init(sc);
+    golomb_init();
 #endif
 
 #ifdef RGB
diff --git a/libavcodec/vulkan/ffv1_dec_reset.comp.glsl b/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
index 1aeb196e1e..c5f28a1953 100644
--- a/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_reset.comp.glsl
@@ -23,6 +23,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
+#define SB_QUALI readonly
 #include "common.glsl"
 #include "ffv1_common.glsl"
 
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index c2a1d7125d..a690014e94 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -52,8 +52,8 @@ void put_symbol(uint state_off, int v)
     if (is_nil)
         return;
 
-    const int a = abs(v);
-    const int e = findMSB(a);
+    int a = abs(v);
+    int e = findMSB(a);
 
     for (int i = 0; i < e; i++)
         WRITE(1 + min(i, 9), true);
@@ -65,7 +65,7 @@ void put_symbol(uint state_off, int v)
     WRITE(22 - 11 + min(e, 10), v < 0);
 }
 
-void encode_line_pcm(inout SliceContext sc, readonly uimage2D img,
+void encode_line_pcm(in SliceContext sc, readonly uimage2D img,
                      ivec2 sp, int y, int p, int comp)
 {
     int w = sc.slice_dim.x;
@@ -86,7 +86,7 @@ void encode_line_pcm(inout SliceContext sc, readonly uimage2D img,
     }
 }
 
-void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
+void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off,
                  ivec2 sp, int y, int p, int comp,
                  uint8_t quant_table_idx, const int run_index)
 {
@@ -124,14 +124,14 @@ layout (set = 1, binding = 2, scalar) buffer slice_state_buf {
 uint hdr_len = 0;
 PutBitContext pb;
 
-void init_golomb(inout SliceContext sc)
+void init_golomb(void)
 {
     hdr_len = rac_terminate(slice_start);
     init_put_bits(pb, OFFBUF(u8buf, slice_start, hdr_len),
                   slice_size_max - hdr_len);
 }
 
-void encode_line(inout SliceContext sc, readonly uimage2D img, uint state_off,
+void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off,
                  ivec2 sp, int y, int p, int comp,
                  uint8_t quant_table_idx, inout int run_index)
 {
@@ -239,7 +239,7 @@ void preload_rgb(in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct)
 }
 #endif
 
-void encode_slice(inout SliceContext sc, const uint slice_idx)
+void encode_slice(in SliceContext sc, uint slice_idx)
 {
     ivec2 sp = sc.slice_pos;
 
@@ -284,7 +284,7 @@ void encode_slice(inout SliceContext sc, const uint slice_idx)
 
 #ifdef GOLOMB
     slice_state_off >>= 3;
-    init_golomb(slice_ctx[slice_idx]);
+    init_golomb();
 #endif
 
 #ifndef RGB
@@ -320,7 +320,7 @@ void encode_slice(inout SliceContext sc, const uint slice_idx)
 #endif
 }
 
-void finalize_slice(inout SliceContext sc, const uint slice_idx)
+void finalize_slice(const uint slice_idx)
 {
 #ifdef GOLOMB
     uint32_t enc_len = hdr_len + flush_put_bits(pb);
@@ -368,5 +368,5 @@ void main(void)
 
     rc = slice_ctx[slice_idx].c;
     encode_slice(slice_ctx[slice_idx], slice_idx);
-    finalize_slice(slice_ctx[slice_idx], slice_idx);
+    finalize_slice(slice_idx);
 }
diff --git a/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl b/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
index 9c6dc2ecf3..face9583a4 100644
--- a/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
@@ -24,6 +24,7 @@
 #extension GL_GOOGLE_include_directive : require
 
 #define ENCODE
+#define SB_QUALI
 #include "common.glsl"
 #include "ffv1_common.glsl"
 
diff --git a/libavcodec/vulkan/ffv1_enc_reset.comp.glsl b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
index 1ad0d99125..549ad2d35e 100644
--- a/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_reset.comp.glsl
@@ -23,6 +23,7 @@
 #pragma shader_stage(compute)
 #extension GL_GOOGLE_include_directive : require
 
+#define SB_QUALI readonly
 #include "common.glsl"
 #include "ffv1_common.glsl"
 
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index 549e0f0dda..5ee2443b95 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -26,7 +26,13 @@
 #define CONTEXT_SIZE 32
 #define MAX_OVERREAD 2
 
-layout (set = 0, binding = 0, scalar) readonly buffer rangecoder_buf {
+#if !defined(GOLOMB) && (defined(DECODE))
+#define RC_BTYPE readonly buffer
+#else
+#define RC_BTYPE uniform
+#endif
+
+layout (set = 0, binding = 0, scalar) RC_BTYPE rangecoder_buf {
     uint8_t zero_one_state[512];
 };
 
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index 40384c4baa..cc71902156 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -519,7 +519,7 @@ static int init_setup_shader(FFV1Context *f, FFVulkanContext *s,
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
         { /* crc_ieee_buf */
@@ -570,7 +570,7 @@ static int init_reset_shader(FFV1Context *f, FFVulkanContext *s,
 
     const FFVulkanDescriptorSetBinding desc_set_const[] = {
         { /* rangecoder_buf */
-            .type   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+            .type   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
         },
     };
-- 
2.52.0


>From 59b5e43812a8b7124c79a49c46be1b967e9d0f8b Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Tue, 10 Feb 2026 16:32:39 +0100
Subject: [PATCH 43/58] vulkan/ffv1_enc: cache state probabilities

4x speedup on AMD.
---
 libavcodec/ffv1enc_vulkan.c          |  3 ++-
 libavcodec/vulkan/ffv1_enc.comp.glsl | 18 ++++++++++++++----
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index ee426c3985..8581a2efda 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -915,8 +915,9 @@ static int init_encode_shader(AVCodecContext *avctx, VkSpecializationInfo *sl)
     VulkanEncodeFFv1Context *fv = avctx->priv_data;
     FFVulkanShader *shd = &fv->enc;
 
+    uint32_t wg_x = fv->ctx.ac != AC_GOLOMB_RICE ? CONTEXT_SIZE : 1;
     ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
-                      (uint32_t []) { 1, 1, 1 }, 0);
+                      (uint32_t []) { wg_x, 1, 1 }, 0);
 
     ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams),
                                 VK_SHADER_STAGE_COMPUTE_BIT);
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index a690014e94..31ce832b52 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -44,8 +44,8 @@ layout (set = 1, binding = 2, scalar) buffer slice_state_buf {
     uint8_t slice_rc_state[];
 };
 
-#define WRITE(off, val) put_rac(slice_rc_state[state_off + off], val)
-void put_symbol(uint state_off, int v)
+#define WRITE(idx, val) put_rac(rc_state[idx], val)
+void put_symbol(int v)
 {
     bool is_nil = (v == 0);
     WRITE(0, is_nil);
@@ -68,6 +68,9 @@ void put_symbol(uint state_off, int v)
 void encode_line_pcm(in SliceContext sc, readonly uimage2D img,
                      ivec2 sp, int y, int p, int comp)
 {
+    if (gl_LocalInvocationID.x > 0)
+        return;
+
     int w = sc.slice_dim.x;
 
 #ifndef RGB
@@ -109,9 +112,16 @@ void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off,
 
         d[1] = fold(d[1], bits);
 
-        uint context_off = state_off + CONTEXT_SIZE*d[0];
+        uint rc_off = state_off + CONTEXT_SIZE*d[0] + gl_LocalInvocationID.x;
 
-        put_symbol(context_off, d[1]);
+        rc_state[gl_LocalInvocationID.x] = slice_rc_state[rc_off];
+        barrier();
+
+        if (gl_LocalInvocationID.x == 0)
+            put_symbol(d[1]);
+
+        barrier();
+        slice_rc_state[rc_off] = rc_state[gl_LocalInvocationID.x];
     }
 }
 
-- 
2.52.0


>From 4eec62a62d048aaefa9ccb7b2c63e3b4433aebb9 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 11 Feb 2026 14:51:52 +0100
Subject: [PATCH 44/58] vulkan/ffv1: finalize and initialize slices only in
 invocation == 0

---
 libavcodec/vulkan/ffv1_dec.comp.glsl | 17 +++++++++--------
 libavcodec/vulkan/ffv1_enc.comp.glsl |  9 +++++++--
 2 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index a08c067433..36bf7a18b3 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -344,15 +344,16 @@ void main(void)
     uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
     slice_start = uint64_t(slice_data) + slice_offsets[slice_idx].x;
 
-    rc = slice_ctx[slice_idx].c;
+    if (gl_LocalInvocationID.x == 0)
+        rc = slice_ctx[slice_idx].c;
+    barrier();
 
     decode_slice(slice_ctx[slice_idx], slice_idx);
 
-    if (gl_LocalInvocationID.x > 0)
-        return;
-
-    uint overread = 0;
-    if (rc.bytestream >= (rc.bytestream_end + MAX_OVERREAD))
-        overread = uint(rc.bytestream - rc.bytestream_end);
-    slice_status[2*slice_idx + 1] = overread;
+    if (gl_LocalInvocationID.x == 0) {
+        uint overread = 0;
+        if (rc.bytestream >= (rc.bytestream_end + MAX_OVERREAD))
+            overread = uint(rc.bytestream - rc.bytestream_end);
+        slice_status[2*slice_idx + 1] = overread;
+    }
 }
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index 31ce832b52..67fee09b2d 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -376,7 +376,12 @@ void main(void)
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
     slice_start = uint64_t(slice_data) + slice_idx*slice_size_max;
 
-    rc = slice_ctx[slice_idx].c;
+    if (gl_LocalInvocationID.x == 0)
+        rc = slice_ctx[slice_idx].c;
+    barrier();
+
     encode_slice(slice_ctx[slice_idx], slice_idx);
-    finalize_slice(slice_idx);
+
+    if (gl_LocalInvocationID.x == 0)
+        finalize_slice(slice_idx);
 }
-- 
2.52.0


>From 316325e6428ebca30c330c09382f045bdb94362a Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 11 Feb 2026 15:19:39 +0100
Subject: [PATCH 45/58] vulkan/ffv1: use loops to encode planes

Every function in SPIR-V gets inlined, always. So use loops.
---
 libavcodec/vulkan/ffv1_common.glsl   |  4 ++--
 libavcodec/vulkan/ffv1_enc.comp.glsl | 34 ++++++++++++----------------
 2 files changed, 16 insertions(+), 22 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 536a37b093..a0e1325817 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -148,7 +148,7 @@ const uint32_t log2_run[41] = {
 #define LADDR(p) (ivec2((p).x, ((p).y & RGB_LBUF)))
 
 ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
-               int comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
+               uint comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
 {
     const ivec2 yoff_border1 = expectEXT(off.x == 0, false) ? off + ivec2(1, -1) : off;
 
@@ -192,7 +192,7 @@ ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
 #define LADDR(p) (p)
 
 ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
-               int comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
+               uint comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
 {
     const ivec2 yoff_border1 = off.x == 0 ? ivec2(1, -1) : ivec2(0, 0);
     sp += off;
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index 67fee09b2d..c6a0564315 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -66,7 +66,7 @@ void put_symbol(int v)
 }
 
 void encode_line_pcm(in SliceContext sc, readonly uimage2D img,
-                     ivec2 sp, int y, int p, int comp)
+                     ivec2 sp, int y, uint p, uint comp)
 {
     if (gl_LocalInvocationID.x > 0)
         return;
@@ -90,7 +90,7 @@ void encode_line_pcm(in SliceContext sc, readonly uimage2D img,
 }
 
 void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off,
-                 ivec2 sp, int y, int p, int comp,
+                 ivec2 sp, int y, uint p, uint comp,
                  uint8_t quant_table_idx, const int run_index)
 {
     int w = sc.slice_dim.x;
@@ -142,7 +142,7 @@ void init_golomb(void)
 }
 
 void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off,
-                 ivec2 sp, int y, int p, int comp,
+                 ivec2 sp, int y, uint p, uint comp,
                  uint8_t quant_table_idx, inout int run_index)
 {
     int w = sc.slice_dim.x;
@@ -212,6 +212,8 @@ void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off,
 #endif
 
 #ifdef RGB
+const uvec4 rgb_plane_order = { 1, 2, 0, 3 };
+
 ivec4 load_components(ivec2 pos)
 {
     ivec4 pix = ivec4(imageLoad(src[0], pos));
@@ -277,11 +279,8 @@ void encode_slice(in SliceContext sc, uint slice_idx)
         for (int y = 0; y < sc.slice_dim.y; y++) {
             preload_rgb(sc, sp, sc.slice_dim.x, y, false);
 
-            encode_line_pcm(sc, tmp, sp, y, 0, 1);
-            encode_line_pcm(sc, tmp, sp, y, 0, 2);
-            encode_line_pcm(sc, tmp, sp, y, 0, 0);
-            if (transparency)
-                encode_line_pcm(sc, tmp, sp, y, 0, 3);
+            for (uint c = 0; c < color_planes; c++)
+                encode_line_pcm(sc, tmp, sp, y, 0, rgb_plane_order[c]);
         }
 #endif
         return;
@@ -298,15 +297,15 @@ void encode_slice(in SliceContext sc, uint slice_idx)
 #endif
 
 #ifndef RGB
-    for (int c = 0; c < color_planes; c++) {
+    for (uint c = 0; c < color_planes; c++) {
         int run_index = 0;
 
         int h = sc.slice_dim.y;
         if (c > 0 && c < 3)
             h = ceil_rshift(h, chroma_shift.y);
 
-        int p = min(c, planes - 1);
-        int comp = c - p;
+        uint p = min(c, planes - 1);
+        uint comp = c - p;
 
         for (int y = 0; y < h; y++)
             encode_line(sc, src[p], slice_state_off[c], sp, y, p,
@@ -317,15 +316,10 @@ void encode_slice(in SliceContext sc, uint slice_idx)
     for (int y = 0; y < sc.slice_dim.y; y++) {
         preload_rgb(sc, sp, sc.slice_dim.x, y, true);
 
-        encode_line(sc, tmp, slice_state_off[0],
-                    sp, y, 0, 1, quant_table_idx[0], run_index);
-        encode_line(sc, tmp, slice_state_off[1],
-                    sp, y, 0, 2, quant_table_idx[1], run_index);
-        encode_line(sc, tmp, slice_state_off[2],
-                    sp, y, 0, 0, quant_table_idx[2], run_index);
-        if (transparency)
-            encode_line(sc, tmp, slice_state_off[3],
-                        sp, y, 0, 3, quant_table_idx[3], run_index);
+        for (uint c = 0; c < color_planes; c++)
+            encode_line(sc, tmp, slice_state_off[c],
+                        sp, y, 0, rgb_plane_order[c],
+                        quant_table_idx[c], run_index);
     }
 #endif
 }
-- 
2.52.0


>From 4b2ca5408ac63331f81267989e582a9c74b1a9ca Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 11 Feb 2026 21:21:58 +0100
Subject: [PATCH 46/58] ffv1enc_vulkan: only return the encoded size, not its
 offset

The encoded offset is just a multiple of the index by the max slice size.
---
 libavcodec/ffv1enc_vulkan.c          | 18 ++++++++----------
 libavcodec/vulkan/ffv1_enc.comp.glsl |  5 ++---
 2 files changed, 10 insertions(+), 13 deletions(-)

diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 8581a2efda..edc692e842 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -243,7 +243,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     RET(ff_vk_get_pooled_buffer(&fv->s, &fv->results_data_pool,
                                 &fd->results_data_ref,
                                 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                                NULL, 2*f->slice_count*sizeof(uint64_t),
+                                NULL, f->slice_count*sizeof(uint32_t),
                                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
     results_data_buf = (FFVkBuffer *)fd->results_data_ref->data;
@@ -486,7 +486,7 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     ff_vk_shader_update_desc_buffer(&fv->s, exec,
                                     &fv->enc, 1, 1, 0,
                                     results_data_buf,
-                                    0, results_data_buf->size,
+                                    0, VK_WHOLE_SIZE,
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->enc,
                                     1, 2, 0,
@@ -607,7 +607,7 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
 
     FFVkBuffer *out_data_buf = (FFVkBuffer *)fd->out_data_ref->data;
     FFVkBuffer *results_data_buf = (FFVkBuffer *)fd->results_data_ref->data;
-    uint64_t *sc;
+    uint32_t slice_size_max = out_data_buf->size / f->slice_count;
 
     /* Make sure encoding's done */
     ff_vk_exec_wait(&fv->s, exec);
@@ -627,17 +627,15 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
     /* Calculate final size */
     pkt->size = 0;
     for (int i = 0; i < f->slice_count; i++) {
-        sc = &((uint64_t *)results_data_buf->mapped_mem)[i*2];
-        av_log(avctx, AV_LOG_DEBUG, "Slice %i size = %"PRIu64", "
-                                    "src offset = %"PRIu64"\n",
-               i, sc[0], sc[1]);
+        uint32_t sl_len = AV_RN32(results_data_buf->mapped_mem + i*4);
+        av_log(avctx, AV_LOG_DEBUG, "Slice %i size = %u\n", i, sl_len);
 
         fv->buf_regions[i] = (VkBufferCopy) {
-            .srcOffset = sc[1],
+            .srcOffset = i*slice_size_max,
             .dstOffset = pkt->size,
-            .size = sc[0],
+            .size = sl_len,
         };
-        pkt->size += sc[0];
+        pkt->size += sl_len;
     }
     av_log(avctx, AV_LOG_VERBOSE, "Encoded data: %iMiB\n", pkt->size / (1024*1024));
     av_buffer_unref(&fd->results_data_ref); /* No need for this buffer anymore */
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index c6a0564315..b3399e3e5f 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -32,7 +32,7 @@ layout (set = 0, binding = 2, scalar) uniform crc_ieee_buf {
 };
 
 layout (set = 1, binding = 1, scalar) writeonly buffer slice_results_buf {
-    uint64_t slice_results[];
+    uint32_t slice_results[];
 };
 layout (set = 1, binding = 3) uniform uimage2D src[];
 
@@ -361,8 +361,7 @@ void finalize_slice(const uint slice_idx)
         enc_len += 4;
     }
 
-    slice_results[slice_idx*2 + 0] = enc_len;
-    slice_results[slice_idx*2 + 1] = uint64_t(bs) - uint64_t(slice_data);
+    slice_results[slice_idx] = enc_len;
 }
 
 void main(void)
-- 
2.52.0


>From 2d2030c6bce76dfa8bf77cf0840cdf4b02f6c107 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 11 Feb 2026 21:44:04 +0100
Subject: [PATCH 47/58] vulkan/rangecoder: don't store pointers in the context

---
 libavcodec/vulkan/ffv1_common.glsl         |  3 +-
 libavcodec/vulkan/ffv1_dec.comp.glsl       | 13 +++---
 libavcodec/vulkan/ffv1_dec_setup.comp.glsl | 12 +++---
 libavcodec/vulkan/ffv1_enc.comp.glsl       | 11 ++---
 libavcodec/vulkan/ffv1_enc_setup.comp.glsl |  3 +-
 libavcodec/vulkan/rangecoder.glsl          | 47 ++++++++++------------
 6 files changed, 40 insertions(+), 49 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index a0e1325817..f7caa9f7b8 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -23,7 +23,6 @@
 #ifndef VULKAN_FFV1_COMMON_H
 #define VULKAN_FFV1_COMMON_H
 
-#include "rangecoder.glsl"
 #ifdef GOLOMB
 #include "ffv1_vlc.glsl"
 #endif
@@ -77,6 +76,8 @@ layout (push_constant, scalar) uniform pushConstants {
     uint slice_size_max;
 };
 
+#include "rangecoder.glsl"
+
 #define TYPE int32_t
 #define VTYPE2 i32vec2
 #define VTYPE3 i32vec3
diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 36bf7a18b3..9ceaf89d37 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -35,8 +35,6 @@ layout (set = 1, binding = 2, scalar) writeonly buffer slice_status_buf {
 };
 layout (set = 1, binding = 4) uniform uimage2D dec[];
 
-uint64_t slice_start;
-
 #ifndef GOLOMB
 
 layout (set = 1, binding = 3, scalar) buffer slice_state_buf {
@@ -162,9 +160,9 @@ void golomb_init(void)
     if (version == 3 && micro_version > 1 || version > 3)
         get_rac_internal(rc.range * 129 >> 8);
 
-    uint64_t ac_byte_count = rc.bytestream - slice_start - 1;
-    init_get_bits(gb, u8buf(slice_start + ac_byte_count),
-                  int(rc.bytestream_end - slice_start - ac_byte_count));
+    uint64_t ac_byte_count = rc.bs_off - rc.bs_start - 1;
+    init_get_bits(gb, u8buf(rc.bs_start + ac_byte_count),
+                  int(rc.bs_end - rc.bs_start - ac_byte_count));
 }
 
 void decode_line(ivec2 sp, int w,
@@ -342,7 +340,6 @@ void decode_slice(in SliceContext sc, uint slice_idx)
 void main(void)
 {
     uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
-    slice_start = uint64_t(slice_data) + slice_offsets[slice_idx].x;
 
     if (gl_LocalInvocationID.x == 0)
         rc = slice_ctx[slice_idx].c;
@@ -352,8 +349,8 @@ void main(void)
 
     if (gl_LocalInvocationID.x == 0) {
         uint overread = 0;
-        if (rc.bytestream >= (rc.bytestream_end + MAX_OVERREAD))
-            overread = uint(rc.bytestream - rc.bytestream_end);
+        if (rc.bs_off >= (rc.bs_end + MAX_OVERREAD))
+            overread = rc.bs_off - rc.bs_end;
         slice_status[2*slice_idx + 1] = overread;
     }
 }
diff --git a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
index e3cf45c8ed..011713a7dd 100644
--- a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
@@ -112,10 +112,7 @@ void main(void)
 {
     uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
 
-    u8buf bs = u8buf(slice_data + slice_offsets[slice_idx].x);
-    uint32_t slice_size = slice_offsets[slice_idx].y;
-
-    rac_init_dec(bs, slice_size);
+    rac_init_dec(slice_offsets[slice_idx].x, slice_offsets[slice_idx].y);
 
     if (slice_idx == (gl_NumWorkGroups.x*gl_NumWorkGroups.y - 1))
         get_rac_equi();
@@ -125,6 +122,9 @@ void main(void)
     slice_ctx[slice_idx].c = rc;
 
     if (has_crc) {
+        u8buf bs = u8buf(slice_data + slice_offsets[slice_idx].x);
+        uint32_t slice_size = slice_offsets[slice_idx].y;
+
         uint32_t crc = crcref;
         for (int i = 0; i < slice_size; i++)
             crc = crc_ieee[(crc & 0xFF) ^ uint32_t(bs[i].v)] ^ (crc >> 8);
@@ -133,7 +133,7 @@ void main(void)
     }
 
     uint overread = 0;
-    if (rc.bytestream >= (rc.bytestream_end + MAX_OVERREAD))
-        overread = uint(rc.bytestream - rc.bytestream_end);
+    if (rc.bs_off >= (rc.bs_end + MAX_OVERREAD))
+        overread = rc.bs_off - rc.bs_end;
     slice_status[2*slice_idx + 1] = overread;
 }
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index b3399e3e5f..f26dbf0bb1 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -36,8 +36,6 @@ layout (set = 1, binding = 1, scalar) writeonly buffer slice_results_buf {
 };
 layout (set = 1, binding = 3) uniform uimage2D src[];
 
-uint64_t slice_start;
-
 #ifndef GOLOMB
 
 layout (set = 1, binding = 2, scalar) buffer slice_state_buf {
@@ -136,8 +134,8 @@ PutBitContext pb;
 
 void init_golomb(void)
 {
-    hdr_len = rac_terminate(slice_start);
-    init_put_bits(pb, OFFBUF(u8buf, slice_start, hdr_len),
+    hdr_len = rac_terminate();
+    init_put_bits(pb, OFFBUF(u8buf, rc.bs_start, hdr_len),
                   slice_size_max - hdr_len);
 }
 
@@ -329,10 +327,10 @@ void finalize_slice(const uint slice_idx)
 #ifdef GOLOMB
     uint32_t enc_len = hdr_len + flush_put_bits(pb);
 #else
-    uint32_t enc_len = rac_terminate(slice_start);
+    uint32_t enc_len = rac_terminate();
 #endif
 
-    u8buf bs = u8buf(slice_start);
+    u8buf bs = u8buf(slice_data + rc.bs_start);
 
     /* Append slice length */
     u8vec4 enc_len_p = unpack8(enc_len);
@@ -367,7 +365,6 @@ void finalize_slice(const uint slice_idx)
 void main(void)
 {
     const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
-    slice_start = uint64_t(slice_data) + slice_idx*slice_size_max;
 
     if (gl_LocalInvocationID.x == 0)
         rc = slice_ctx[slice_idx].c;
diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index c57ba4074b..860b50a73c 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -48,8 +48,7 @@ void init_slice(inout SliceContext sc, uint slice_idx)
     if (!rct_search || (sc.slice_coding_mode == 1))
         sc.slice_rct_coef = ivec2(1, 1);
 
-    rac_init(OFFBUF(u8buf, slice_data, slice_idx * slice_size_max),
-             slice_size_max);
+    rac_init(slice_idx*slice_size_max, slice_size_max);
 }
 
 void put_usymbol(uint v)
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index 5ee2443b95..614b727a87 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -37,9 +37,9 @@ layout (set = 0, binding = 0, scalar) RC_BTYPE rangecoder_buf {
 };
 
 struct RangeCoder {
-    uint64_t bytestream;
-    uint64_t bytestream_end;
-
+    uint     bs_start;
+    uint     bs_off;
+    uint     bs_end;
     uint     low;
     uint     range;
     uint16_t outstanding_count;
@@ -51,10 +51,11 @@ shared uint8_t rc_state[CONTEXT_SIZE];
 shared bool rc_data[CONTEXT_SIZE];
 shared bool rc_dec[CONTEXT_SIZE];
 
-void rac_init(u8buf data, uint buf_size)
+void rac_init(uint bs_start, uint bs_len)
 {
-    rc.bytestream = uint64_t(data);
-    rc.bytestream_end = uint64_t(data) + buf_size;
+    rc.bs_start = bs_start;
+    rc.bs_off = bs_start;
+    rc.bs_end = bs_start + bs_len;
     rc.low = 0;
     rc.range = 0xFF00;
     rc.outstanding_count = uint16_t(0);
@@ -66,29 +67,27 @@ void rac_init(u8buf data, uint buf_size)
 void renorm_encoder(void)
 {
     int bs_cnt = 0;
-    u8buf bytestream = u8buf(rc.bytestream);
 
     if (rc.outstanding_byte == 0xFF) {
         rc.outstanding_byte = uint8_t(rc.low >> 8);
     } else if (rc.low <= 0xFF00) {
-        bytestream[bs_cnt++].v = rc.outstanding_byte;
+        slice_data[rc.bs_off++].v = rc.outstanding_byte;
         uint16_t cnt = rc.outstanding_count;
         for (; cnt > 0; cnt--)
-            bytestream[bs_cnt++].v = uint8_t(0xFF);
+            slice_data[rc.bs_off++].v = uint8_t(0xFF);
         rc.outstanding_count = uint16_t(0);
         rc.outstanding_byte = uint8_t(rc.low >> 8);
     } else if (rc.low >= 0x10000) {
-        bytestream[bs_cnt++].v = rc.outstanding_byte + uint8_t(1);
+        slice_data[rc.bs_off++].v = rc.outstanding_byte + uint8_t(1);
         uint16_t cnt = rc.outstanding_count;
         for (; cnt > 0; cnt--)
-            bytestream[bs_cnt++].v = uint8_t(0x00);
+            slice_data[rc.bs_off++].v = uint8_t(0x00);
         rc.outstanding_count = uint16_t(0);
         rc.outstanding_byte = uint8_t(bitfieldExtract(rc.low, 8, 8));
     } else {
         rc.outstanding_count++;
     }
 
-    rc.bytestream += bs_cnt;
     rc.range <<= 8;
     rc.low = bitfieldInsert(0, rc.low, 8, 8);
 }
@@ -109,19 +108,17 @@ void renorm_encoder(void)
         return;
     }
 
-    u8buf bs = u8buf(rc.bytestream);
     uint8_t outstanding_byte = rc.outstanding_byte;
 
-    rc.bytestream        = uint64_t(bs) + oc;
     rc.outstanding_count = uint16_t(0);
     rc.outstanding_byte  = uint8_t(low >> 8);
 
     uint8_t obs = uint8_t(low > 0xFF00);
     uint8_t fill = obs - uint8_t(1); /* unsigned underflow */
 
-    bs[0].v = outstanding_byte + obs;
+    slice_data[rc.bs_off++].v = outstanding_byte + obs;
     for (int i = 1; i < oc; i++)
-        bs[i].v = fill;
+        slice_data[rc.bs_off++].v = fill;
 }
 #endif
 
@@ -170,7 +167,7 @@ void put_rac_terminate(void)
 }
 
 /* Return the number of bytes written. */
-uint rac_terminate(uint64_t bytestream_start)
+uint rac_terminate(void)
 {
     put_rac_terminate();
     rc.range = uint16_t(0xFF);
@@ -186,21 +183,21 @@ uint rac_terminate(uint64_t bytestream_start)
         debugPrintfEXT("Error: range < 0x100");
 #endif
 
-    return uint(uint64_t(rc.bytestream) - bytestream_start);
+    return rc.bs_off - rc.bs_start;
 }
 
-void rac_init_dec(u8buf data, uint buf_size)
+void rac_init_dec(uint bs_start, uint bs_len)
 {
     /* Skip priming bytes */
-    rac_init(OFFBUF(u8buf, data, 2), buf_size - 2);
+    rac_init(bs_start + 2, bs_len - 2);
 
-    u8vec2 prime = u8vec2buf(data).v;
+    u8vec2 prime = u8vec2buf(slice_data + bs_start).v;
     /* Switch endianness of the priming bytes */
     rc.low = pack16(prime.yx);
 
     if (rc.low >= 0xFF00) {
         rc.low = 0xFF00;
-        rc.bytestream_end = uint64_t(data) + 2;
+        rc.bs_end = bs_start + 2;
     }
 }
 
@@ -208,9 +205,9 @@ void refill(void)
 {
     rc.range <<= 8;
     rc.low   <<= 8;
-    if (expectEXT(rc.bytestream < rc.bytestream_end, true))
-        rc.low |= u8buf(rc.bytestream).v;
-    rc.bytestream++;
+    if (expectEXT(rc.bs_off < rc.bs_end, true))
+        rc.low |= slice_data[rc.bs_off].v;
+    rc.bs_off++;
 }
 
 bool get_rac_internal(const uint range1)
-- 
2.52.0


>From 232753bc493c6d9b6f61621239482ac4a55a1b13 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Wed, 11 Feb 2026 22:31:06 +0100
Subject: [PATCH 48/58] vulkan/ffv1_dec_setup: roll a put_rac inside a loop

This saves 16KiB of memory.
Yeah, things go large when all compilers inline everything.
---
 libavcodec/vulkan/ffv1_enc_setup.comp.glsl | 5 ++---
 libavcodec/vulkan/rangecoder.glsl          | 2 --
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index 860b50a73c..4537cb7b58 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -60,9 +60,8 @@ void put_usymbol(uint v)
 
     const int e = findMSB(v);
 
-    for (int i = 0; i < e; i++)
-        put_rac(rc_state[1 + min(i, 9)], true);
-    put_rac(rc_state[1 + min(e, 9)], false);
+    for (int i = 0; i <= e; i++)
+        put_rac(rc_state[1 + min(i, 9)], i < e);
 
     for (int i = e - 1; i >= 0; i--)
         put_rac(rc_state[22 + min(i, 9)], bool(bitfieldExtract(v, i, 1)));
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index 614b727a87..6bd8d2f264 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -66,8 +66,6 @@ void rac_init(uint bs_start, uint bs_len)
 /* Full renorm version that can handle outstanding_byte == 0xFF */
 void renorm_encoder(void)
 {
-    int bs_cnt = 0;
-
     if (rc.outstanding_byte == 0xFF) {
         rc.outstanding_byte = uint8_t(rc.low >> 8);
     } else if (rc.low <= 0xFF00) {
-- 
2.52.0


>From 048458843470d6a81ca14515f2b26458a43354db Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Fri, 13 Feb 2026 12:33:10 +0100
Subject: [PATCH 49/58] vulkan/ffv1: synchronize before/after RCT
 transform/preload

---
 libavcodec/vulkan/ffv1_dec.comp.glsl | 3 +++
 libavcodec/vulkan/ffv1_enc.comp.glsl | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 9ceaf89d37..842cb8d51a 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -246,6 +246,9 @@ ivec4 transform_sample(ivec4 pix, ivec2 rct_coef)
 
 void writeout_rgb(in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct)
 {
+    memoryBarrierImage();
+    barrier();
+
     for (uint x = gl_LocalInvocationID.x; x < w; x += gl_WorkGroupSize.x) {
         ivec2 lpos = sp + LADDR(ivec2(x, y));
         ivec2 pos = sc.slice_pos + ivec2(x, y);
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index f26dbf0bb1..6a42747e84 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -246,6 +246,9 @@ void preload_rgb(in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct)
 
         imageStore(tmp, lpos, pix);
     }
+
+    memoryBarrierImage();
+    barrier();
 }
 #endif
 
-- 
2.52.0


>From f16e698191aada37118118701fa41f023d6b7b36 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Fri, 13 Feb 2026 13:17:38 +0100
Subject: [PATCH 50/58] vulkan/ffv1_dec: synchronize image writes when decoding

---
 libavcodec/vulkan/ffv1_dec.comp.glsl | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index 842cb8d51a..bcec99fed9 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -127,23 +127,22 @@ void decode_line(ivec2 sp, int w,
         rc_dec[gl_LocalInvocationID.x] = false;
         barrier();
 
-        int diff;
-        if (gl_LocalInvocationID.x == 0)
-            diff = get_isymbol();
-
-        barrier();
-        uint i = gl_LocalInvocationID.x;
-        if (rc_dec[i])
-            slice_rc_state[rc_off] = zero_one_state[rc_state[i] +
-                                                    (rc_data[i] ? 256 : 0)];
-
         if (gl_LocalInvocationID.x == 0) {
+            int diff = get_isymbol();
             if (pr[0] < 0)
                 diff = -diff;
 
             uint v = zero_extend(pr[1] + diff, bits);
             imageStore(dec[p], sp + LADDR(ivec2(x, y)), uvec4(v));
         }
+
+        /* Image write now visible to other invocs */
+        memoryBarrierImage();
+        barrier();
+        if (rc_dec[gl_LocalInvocationID.x])
+            slice_rc_state[rc_off] =
+                zero_one_state[rc_state[gl_LocalInvocationID.x] +
+                               (rc_data[gl_LocalInvocationID.x] ? 256 : 0)];
     }
 }
 
-- 
2.52.0


>From 1059e4ad8996c0ef2473d4d0f4131afe0ebb3ecf Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Fri, 13 Feb 2026 13:49:42 +0100
Subject: [PATCH 51/58] vulkan/ffv1: improve compiler hints

Don't unroll unless needed, don't use const in function arguments,
don't use expect unless actually needed.
---
 libavcodec/vulkan/ffv1_common.glsl   | 8 ++++----
 libavcodec/vulkan/ffv1_dec.comp.glsl | 3 +--
 libavcodec/vulkan/ffv1_enc.comp.glsl | 9 ++++-----
 libavcodec/vulkan/ffv1_vlc.glsl      | 2 +-
 libavcodec/vulkan/rangecoder.glsl    | 4 ++--
 5 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index f7caa9f7b8..9032bdf612 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -151,7 +151,7 @@ const uint32_t log2_run[41] = {
 ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
                uint comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
 {
-    const ivec2 yoff_border1 = expectEXT(off.x == 0, false) ? off + ivec2(1, -1) : off;
+    ivec2 yoff_border1 = expectEXT(off.x == 0, false) ? off + ivec2(1, -1) : off;
 
     /* Thanks to the same coincidence as below, we can skip checking if off == 0, 1 */
     VTYPE3 top  = VTYPE3(TYPE(imageLoad(pred, sp + LADDR(yoff_border1 + ivec2(-1, -1)))[comp]),
@@ -170,7 +170,7 @@ ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
     if (has_extend_lookup && extend_lookup) {
         TYPE cur2 = TYPE(0);
         if (expectEXT(off.x > 0, true)) {
-            const ivec2 yoff_border2 = expectEXT(off.x == 1, false) ? ivec2(-1, -1) : ivec2(-2, 0);
+            ivec2 yoff_border2 = expectEXT(off.x == 1, false) ? ivec2(-1, -1) : ivec2(-2, 0);
             cur2 = TYPE(imageLoad(pred, sp + LADDR(off + yoff_border2))[comp]);
         }
         base += quant_table[quant_table_idx][3][(cur2 - cur) & MAX_QUANT_TABLE_MASK];
@@ -195,7 +195,7 @@ ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
 ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
                uint comp, int sw, uint8_t quant_table_idx, bool extend_lookup)
 {
-    const ivec2 yoff_border1 = off.x == 0 ? ivec2(1, -1) : ivec2(0, 0);
+    ivec2 yoff_border1 = off.x == 0 ? ivec2(1, -1) : ivec2(0, 0);
     sp += off;
 
     VTYPE3 top  = VTYPE3(TYPE(0),
@@ -219,7 +219,7 @@ ivec2 get_pred(readonly uimage2D pred, ivec2 sp, ivec2 off,
     if (has_extend_lookup && extend_lookup) {
         TYPE cur2 = TYPE(0);
         if (off.x > 0 && off != ivec2(1, 0)) {
-            const ivec2 yoff_border2 = off.x == 1 ? ivec2(1, -1) : ivec2(0, 0);
+            ivec2 yoff_border2 = off.x == 1 ? ivec2(1, -1) : ivec2(0, 0);
             cur2 = TYPE(imageLoad(pred, sp + ivec2(-2,  0) + yoff_border2)[comp]);
         }
         base += quant_table[quant_table_idx][3][(cur2 - cur) & MAX_QUANT_TABLE_MASK];
diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index bcec99fed9..a6fa01df96 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -98,7 +98,6 @@ void decode_line_pcm(ivec2 sp, int w, int y, int p)
     for (int x = 0; x < w; x++) {
         uint v = 0;
 
-        [[unroll]]
         for (uint i = (rct_offset >> 1); i > 0; i >>= 1)
             v |= get_rac_equi() ? i : 0;
 
@@ -259,7 +258,7 @@ void writeout_rgb(in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct)
         if (transparency)
             pix.a = int(imageLoad(dec[3], lpos)[0]);
 
-        if (expectEXT(apply_rct, true))
+        if (apply_rct)
             pix = transform_sample(pix, sc.slice_rct_coef);
         else
             pix = ivec4(pix[fmt_lut[0]], pix[fmt_lut[1]],
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index 6a42747e84..cde9b941bd 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -81,7 +81,6 @@ void encode_line_pcm(in SliceContext sc, readonly uimage2D img,
     for (int x = 0; x < w; x++) {
         uint v = imageLoad(img, sp + LADDR(ivec2(x, y)))[comp];
 
-        [[unroll]]
         for (uint i = (rct_offset >> 1); i > 0; i >>= 1)
             put_rac_equi(bool(v & i));
     }
@@ -89,7 +88,7 @@ void encode_line_pcm(in SliceContext sc, readonly uimage2D img,
 
 void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off,
                  ivec2 sp, int y, uint p, uint comp,
-                 uint8_t quant_table_idx, const int run_index)
+                 uint8_t quant_table_idx, in int run_index)
 {
     int w = sc.slice_dim.x;
 
@@ -241,7 +240,7 @@ void preload_rgb(in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct)
 
         ivec4 pix = load_components(pos);
 
-        if (expectEXT(apply_rct, true))
+        if (apply_rct)
             transform_sample(pix, sc.slice_rct_coef);
 
         imageStore(tmp, lpos, pix);
@@ -325,7 +324,7 @@ void encode_slice(in SliceContext sc, uint slice_idx)
 #endif
 }
 
-void finalize_slice(const uint slice_idx)
+void finalize_slice(in uint slice_idx)
 {
 #ifdef GOLOMB
     uint32_t enc_len = hdr_len + flush_put_bits(pb);
@@ -367,7 +366,7 @@ void finalize_slice(const uint slice_idx)
 
 void main(void)
 {
-    const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
+    uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
 
     if (gl_LocalInvocationID.x == 0)
         rc = slice_ctx[slice_idx].c;
diff --git a/libavcodec/vulkan/ffv1_vlc.glsl b/libavcodec/vulkan/ffv1_vlc.glsl
index 23e3c3ab42..f362d3afbb 100644
--- a/libavcodec/vulkan/ffv1_vlc.glsl
+++ b/libavcodec/vulkan/ffv1_vlc.glsl
@@ -30,7 +30,7 @@ struct VlcState {
     uint8_t  count;
 };
 
-void update_vlc_state(inout VlcState state, const int v)
+void update_vlc_state(inout VlcState state, in int v)
 {
     int drift = state.drift;
     int count = state.count;
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index 6bd8d2f264..ba85a67a98 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -120,7 +120,7 @@ void renorm_encoder(void)
 }
 #endif
 
-void put_rac_internal(const uint range1, bool bit)
+void put_rac_internal(in uint range1, bool bit)
 {
 #ifdef DEBUG
     if (range1 >= rc.range)
@@ -208,7 +208,7 @@ void refill(void)
     rc.bs_off++;
 }
 
-bool get_rac_internal(const uint range1)
+bool get_rac_internal(in uint range1)
 {
     uint ranged = rc.range - range1;
     bool bit = rc.low >= ranged;
-- 
2.52.0


>From 6a7d9d2f22284d98cc1fb439d43f3ddf70c17aaf Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sat, 14 Feb 2026 00:37:10 +0100
Subject: [PATCH 52/58] vulkan/ffv1: optimize get_isymbol

---
 libavcodec/vulkan/ffv1_dec.comp.glsl | 43 +++++++++++++++++-----------
 libavcodec/vulkan/rangecoder.glsl    |  2 --
 2 files changed, 26 insertions(+), 19 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index a6fa01df96..fef3d4cbae 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -42,45 +42,51 @@ layout (set = 1, binding = 3, scalar) buffer slice_state_buf {
 };
 
 #define READ(idx) get_rac_state(idx)
+shared int sym_e;
+shared bool rc_dec[CONTEXT_SIZE];
 int get_isymbol(void)
 {
+    sym_e = 0;
+    rc_dec[0] = true;
     if (READ(0))
         return 0;
 
     int e = 1;
     for (; e < 11; e++) {
+        rc_dec[e] = true;
         if (!READ(e))
             break;
     }
 
     int a = 1;
-    int i = e;
+    sym_e = e + 10;
+    rc_dec[sym_e] = true;
 
-    if (bits > 8 && e == 11) {
+    if (bits > 10 && e == 11) {
         do {
             rc_state[10] = zero_one_state[rc_state[10] + 256];
             e++;
         } while (READ(10));
 
-        e--;
-        i = e - 1;
-
-        a <<= 1;
-        a |= int(READ(31));
-        for (; i >= 11; i--) {
+        a = READ(31) ? 0x3 : 0x2;
+        for (e -= 2; e >= 11; e--) {
             rc_state[31] = zero_one_state[rc_state[31] +
                                           (rc_data[31] ? 256 : 0)];
             a <<= 1;
             a |= int(READ(31));
         }
+
+        rc_dec[31] = true;
     }
 
-    a <<= i - 1;
-    i -= 2;
-    for (; i >= 0; i--)
-        a |= int(READ(i + 22)) << i;
+    e += 20;
+    for (; e >= 22; e--) {
+        a <<= 1;
+        a |= int(READ(e));
+        rc_dec[e] = true;
+    }
 
-    return READ(min(e + 10, 21)) ? -a : a;
+    return READ(sym_e) ? -a : a;
 }
 
 void decode_line_pcm(ivec2 sp, int w, int y, int p)
@@ -105,6 +111,8 @@ void decode_line_pcm(ivec2 sp, int w, int y, int p)
     }
 }
 
+shared ivec2 pr;
+
 void decode_line(ivec2 sp, int w,
                  int y, int p, uint state_off,
                  uint8_t quant_table_idx, int run_index)
@@ -117,13 +125,15 @@ void decode_line(ivec2 sp, int w,
 #endif
 
     for (int x = 0; x < w; x++) {
-        ivec2 pr = get_pred(dec[p], sp, ivec2(x, y), 0, w,
-                            quant_table_idx, extend_lookup[quant_table_idx]);
+        if (gl_LocalInvocationID.x == 0)
+            pr = get_pred(dec[p], sp, ivec2(x, y), 0, w,
+                          quant_table_idx, extend_lookup[quant_table_idx]);
+        barrier();
 
         uint rc_off = state_off + CONTEXT_SIZE*abs(pr[0]) + gl_LocalInvocationID.x;
 
-        rc_state[gl_LocalInvocationID.x] = slice_rc_state[rc_off];
         rc_dec[gl_LocalInvocationID.x] = false;
+        rc_state[gl_LocalInvocationID.x] = slice_rc_state[rc_off];
         barrier();
 
         if (gl_LocalInvocationID.x == 0) {
@@ -136,7 +146,6 @@ void decode_line(ivec2 sp, int w,
         }
 
         /* Image write now visible to other invocs */
-        memoryBarrierImage();
         barrier();
         if (rc_dec[gl_LocalInvocationID.x])
             slice_rc_state[rc_off] =
diff --git a/libavcodec/vulkan/rangecoder.glsl b/libavcodec/vulkan/rangecoder.glsl
index ba85a67a98..f86f632ee3 100644
--- a/libavcodec/vulkan/rangecoder.glsl
+++ b/libavcodec/vulkan/rangecoder.glsl
@@ -49,7 +49,6 @@ struct RangeCoder {
 shared RangeCoder rc;
 shared uint8_t rc_state[CONTEXT_SIZE];
 shared bool rc_data[CONTEXT_SIZE];
-shared bool rc_dec[CONTEXT_SIZE];
 
 void rac_init(uint bs_start, uint bs_len)
 {
@@ -230,7 +229,6 @@ bool get_rac(inout uint8_t state)
 
 bool get_rac_state(uint idx)
 {
-    rc_dec[idx] = true;
     return (rc_data[idx] = get_rac_internal(rc.range * rc_state[idx] >> 8));
 }
 
-- 
2.52.0


>From 95533109c80a904ccfe822f25f53582652247f0d Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sat, 14 Feb 2026 05:21:55 +0100
Subject: [PATCH 53/58] vulkan/ffv1: unify all constants buffer into a single
 buffer

Less allocations is always better.
---
 libavcodec/ffv1_vulkan.c    | 99 +++++++++----------------------------
 libavcodec/ffv1_vulkan.h    | 12 +----
 libavcodec/ffv1enc_vulkan.c | 43 +++++-----------
 libavcodec/vulkan_ffv1.c    | 27 +++++-----
 4 files changed, 47 insertions(+), 134 deletions(-)

diff --git a/libavcodec/ffv1_vulkan.c b/libavcodec/ffv1_vulkan.c
index 368e66b025..2a2226016f 100644
--- a/libavcodec/ffv1_vulkan.c
+++ b/libavcodec/ffv1_vulkan.c
@@ -55,103 +55,50 @@ void ff_ffv1_vk_set_common_sl(AVCodecContext *avctx, FFV1Context *f,
     SPEC_LIST_ADD(sl, 14, 32, f->chroma_v_shift);
 }
 
-int ff_ffv1_vk_update_state_transition_data(FFVulkanContext *s,
-                                            FFVkBuffer *vkb, FFV1Context *f)
+static void set_crc_tab(uint32_t *buf)
 {
-    int err;
-    uint8_t *buf_mapped;
-
-    RET(ff_vk_map_buffer(s, vkb, &buf_mapped, 0));
-
-    for (int i = 1; i < 256; i++) {
-        buf_mapped[256 + i] = f->state_transition[i];
-        buf_mapped[256 - i] = 256 - (int)f->state_transition[i];
+    for (uint32_t i = 0; i < 256; i++) {
+        uint32_t c = i << 24;
+        for (int j = 0; j < 8; j++)
+            c = (c << 1) ^ (0x04C11DB7 & (((int32_t) c) >> 31));
+        buf[i] = av_bswap32(c);
     }
-
-    RET(ff_vk_unmap_buffer(s, vkb, 1));
-
-fail:
-    return err;
 }
 
-static int init_state_transition_data(FFVulkanContext *s,
-                                      FFVkBuffer *vkb, FFV1Context *f,
-                                      int (*write_data)(FFVulkanContext *s,
-                                                        FFVkBuffer *vkb, FFV1Context *f))
+static void set_rc_state_tab(FFV1Context *f, uint8_t *buf)
 {
-    int err;
-    size_t buf_len = 512*sizeof(uint8_t);
-
-    RET(ff_vk_create_buf(s, vkb,
-                         buf_len,
-                         NULL, NULL,
-                         VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
-                         VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                         VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
-                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
-
-    write_data(s, vkb, f);
-
-fail:
-    return err;
+    for (int i = 1; i < 256; i++) {
+        buf[256 + i] = f->state_transition[i];
+        buf[256 - i] = 256 - (int)f->state_transition[i];
+    }
 }
 
-int ff_ffv1_vk_init_state_transition_data(FFVulkanContext *s,
-                                          FFVkBuffer *vkb, FFV1Context *f)
-{
-    return init_state_transition_data(s, vkb, f,
-                                      ff_ffv1_vk_update_state_transition_data);
-}
-
-int ff_ffv1_vk_init_quant_table_data(FFVulkanContext *s,
-                                     FFVkBuffer *vkb, FFV1Context *f)
+int ff_ffv1_vk_init_consts(FFVulkanContext *s, FFVkBuffer *vkb, FFV1Context *f)
 {
     int err;
 
-    int16_t *buf_mapped;
-    size_t buf_len = MAX_QUANT_TABLES*
+    uint8_t *buf_mapped;
+    size_t buf_len = 256*sizeof(uint32_t) + /* CRC */
+                     512*sizeof(uint8_t) + /* Rangecoder */
+                     MAX_QUANT_TABLES*
                      MAX_CONTEXT_INPUTS*
                      MAX_QUANT_TABLE_SIZE*sizeof(int16_t);
 
     RET(ff_vk_create_buf(s, vkb,
                          buf_len,
                          NULL, NULL,
+                         VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
                          VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
                          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
     RET(ff_vk_map_buffer(s, vkb, (void *)&buf_mapped, 0));
 
-    memcpy(buf_mapped, f->quant_tables,
-           sizeof(f->quant_tables));
-
-    RET(ff_vk_unmap_buffer(s, vkb, 1));
-
-fail:
-    return err;
-}
-
-int ff_ffv1_vk_init_crc_table_data(FFVulkanContext *s,
-                                   FFVkBuffer *vkb, FFV1Context *f)
-{
-    int err;
-
-    uint32_t *buf_mapped;
-    size_t buf_len = 256*sizeof(int32_t);
-
-    RET(ff_vk_create_buf(s, vkb,
-                         buf_len,
-                         NULL, NULL,
-                         VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
-                         VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
-                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
-    RET(ff_vk_map_buffer(s, vkb, (void *)&buf_mapped, 0));
-
-    for (uint32_t i = 0; i < 256; i++) {
-        uint32_t c = i << 24;
-        for (int j = 0; j < 8; j++)
-            c = (c << 1) ^ (0x04C11DB7 & (((int32_t) c) >> 31));
-        buf_mapped[i] = av_bswap32(c);
-    }
+    set_crc_tab((uint32_t *)buf_mapped);
+
+    set_rc_state_tab(f, buf_mapped + 256*sizeof(uint32_t));
+
+    memcpy(buf_mapped + 256*sizeof(uint32_t) + 512*sizeof(uint8_t),
+           f->quant_tables, sizeof(f->quant_tables));
 
     RET(ff_vk_unmap_buffer(s, vkb, 1));
 
diff --git a/libavcodec/ffv1_vulkan.h b/libavcodec/ffv1_vulkan.h
index 8585315741..9a206afaca 100644
--- a/libavcodec/ffv1_vulkan.h
+++ b/libavcodec/ffv1_vulkan.h
@@ -28,17 +28,7 @@ void ff_ffv1_vk_set_common_sl(AVCodecContext *avctx, FFV1Context *f,
                               VkSpecializationInfo *sl,
                               enum AVPixelFormat sw_format);
 
-int ff_ffv1_vk_update_state_transition_data(FFVulkanContext *s,
-                                            FFVkBuffer *vkb, FFV1Context *f);
-
-int ff_ffv1_vk_init_state_transition_data(FFVulkanContext *s,
-                                          FFVkBuffer *vkb, FFV1Context *f);
-
-int ff_ffv1_vk_init_quant_table_data(FFVulkanContext *s,
-                                     FFVkBuffer *vkb, FFV1Context *f);
-
-int ff_ffv1_vk_init_crc_table_data(FFVulkanContext *s,
-                                   FFVkBuffer *vkb, FFV1Context *f);
+int ff_ffv1_vk_init_consts(FFVulkanContext *s, FFVkBuffer *vkb, FFV1Context *f);
 
 typedef struct FFv1ShaderParams {
     VkDeviceAddress slice_data;
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index edc692e842..83a8ab7182 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -78,9 +78,7 @@ typedef struct VulkanEncodeFFv1Context {
     FFVulkanShader enc;
 
     /* Constant read-only buffers */
-    FFVkBuffer quant_buf;
-    FFVkBuffer rangecoder_static_buf;
-    FFVkBuffer crc_tab_buf;
+    FFVkBuffer consts_buf;
 
     /* Slice data buffer pool */
     AVBufferPool *slice_data_pool;
@@ -1194,48 +1192,33 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     if (err < 0)
         return err;
 
-    /* Range coder data */
-    err = ff_ffv1_vk_init_state_transition_data(&fv->s,
-                                                &fv->rangecoder_static_buf,
-                                                f);
-    if (err < 0)
-        return err;
-
-    /* Quantization table data */
-    err = ff_ffv1_vk_init_quant_table_data(&fv->s,
-                                           &fv->quant_buf,
-                                           f);
-    if (err < 0)
-        return err;
-
-    /* CRC table buffer */
-    err = ff_ffv1_vk_init_crc_table_data(&fv->s,
-                                         &fv->crc_tab_buf,
-                                         f);
+    /* Constant data */
+    err = ff_ffv1_vk_init_consts(&fv->s, &fv->consts_buf, f);
     if (err < 0)
         return err;
 
     /* Update setup global descriptors */
     RET(ff_vk_shader_update_desc_buffer(&fv->s, &fv->exec_pool.contexts[0],
                                         &fv->setup, 0, 0, 0,
-                                        &fv->rangecoder_static_buf,
-                                        0, 512*sizeof(uint8_t),
+                                        &fv->consts_buf,
+                                        256*sizeof(uint32_t), 512*sizeof(uint8_t),
                                         VK_FORMAT_UNDEFINED));
 
     /* Update encode global descriptors */
     RET(ff_vk_shader_update_desc_buffer(&fv->s, &fv->exec_pool.contexts[0],
                                         &fv->enc, 0, 0, 0,
-                                        &fv->rangecoder_static_buf,
-                                        0, fv->rangecoder_static_buf.size,
+                                        &fv->consts_buf,
+                                        256*sizeof(uint32_t), 512*sizeof(uint8_t),
                                         VK_FORMAT_UNDEFINED));
     RET(ff_vk_shader_update_desc_buffer(&fv->s, &fv->exec_pool.contexts[0],
                                         &fv->enc, 0, 1, 0,
-                                        &fv->quant_buf,
-                                        0, VK_WHOLE_SIZE,
+                                        &fv->consts_buf,
+                                        256*sizeof(uint32_t) + 512*sizeof(uint8_t),
+                                        VK_WHOLE_SIZE,
                                         VK_FORMAT_UNDEFINED));
     RET(ff_vk_shader_update_desc_buffer(&fv->s, &fv->exec_pool.contexts[0],
                                         &fv->enc, 0, 2, 0,
-                                        &fv->crc_tab_buf,
+                                        &fv->consts_buf,
                                         0, 256*sizeof(uint32_t),
                                         VK_FORMAT_UNDEFINED));
 
@@ -1291,9 +1274,7 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx)
     av_buffer_unref(&fv->keyframe_slice_data_ref);
     av_buffer_pool_uninit(&fv->slice_data_pool);
 
-    ff_vk_free_buf(&fv->s, &fv->quant_buf);
-    ff_vk_free_buf(&fv->s, &fv->rangecoder_static_buf);
-    ff_vk_free_buf(&fv->s, &fv->crc_tab_buf);
+    ff_vk_free_buf(&fv->s, &fv->consts_buf);
 
     av_free(fv->buf_regions);
     av_frame_free(&fv->frame);
diff --git a/libavcodec/vulkan_ffv1.c b/libavcodec/vulkan_ffv1.c
index cc71902156..e807fe4de6 100644
--- a/libavcodec/vulkan_ffv1.c
+++ b/libavcodec/vulkan_ffv1.c
@@ -74,9 +74,7 @@ typedef struct FFv1VulkanDecodeContext {
     FFVulkanShader reset;
     FFVulkanShader decode;
 
-    FFVkBuffer rangecoder_buf;
-    FFVkBuffer quant_buf;
-    FFVkBuffer crc_buf;
+    FFVkBuffer consts_buf;
 
     AVBufferPool *slice_state_pool;
     AVBufferPool *slice_feedback_pool;
@@ -732,9 +730,7 @@ static void vk_decode_ffv1_uninit(FFVulkanDecodeShared *ctx)
     ff_vk_shader_free(&ctx->s, &fv->reset);
     ff_vk_shader_free(&ctx->s, &fv->decode);
 
-    ff_vk_free_buf(&ctx->s, &fv->rangecoder_buf);
-    ff_vk_free_buf(&ctx->s, &fv->quant_buf);
-    ff_vk_free_buf(&ctx->s, &fv->crc_buf);
+    ff_vk_free_buf(&ctx->s, &fv->consts_buf);
 
     av_buffer_pool_uninit(&fv->slice_state_pool);
     av_buffer_pool_uninit(&fv->slice_feedback_pool);
@@ -800,32 +796,31 @@ static int vk_decode_ffv1_init(AVCodecContext *avctx)
                            dctx, hwfc, sl, f->ac, is_rgb));
 
     /* Init static data */
-    RET(ff_ffv1_vk_init_state_transition_data(&ctx->s, &fv->rangecoder_buf, f));
-    RET(ff_ffv1_vk_init_crc_table_data(&ctx->s, &fv->crc_buf, f));
-    RET(ff_ffv1_vk_init_quant_table_data(&ctx->s, &fv->quant_buf, f));
+    RET(ff_ffv1_vk_init_consts(&ctx->s, &fv->consts_buf, f));
 
     /* Update setup global descriptors */
     RET(ff_vk_shader_update_desc_buffer(&ctx->s, &ctx->exec_pool.contexts[0],
                                         &fv->setup, 0, 0, 0,
-                                        &fv->rangecoder_buf,
-                                        0, 512*sizeof(uint8_t),
+                                        &fv->consts_buf,
+                                        256*sizeof(uint32_t), 512*sizeof(uint8_t),
                                         VK_FORMAT_UNDEFINED));
     RET(ff_vk_shader_update_desc_buffer(&ctx->s, &ctx->exec_pool.contexts[0],
                                         &fv->setup, 0, 1, 0,
-                                        &fv->crc_buf,
+                                        &fv->consts_buf,
                                         0, 256*sizeof(uint32_t),
                                         VK_FORMAT_UNDEFINED));
 
     /* Update decode global descriptors */
     RET(ff_vk_shader_update_desc_buffer(&ctx->s, &ctx->exec_pool.contexts[0],
                                         &fv->decode, 0, 0, 0,
-                                        &fv->rangecoder_buf,
-                                        0, 512*sizeof(uint8_t),
+                                        &fv->consts_buf,
+                                        256*sizeof(uint32_t), 512*sizeof(uint8_t),
                                         VK_FORMAT_UNDEFINED));
     RET(ff_vk_shader_update_desc_buffer(&ctx->s, &ctx->exec_pool.contexts[0],
                                         &fv->decode, 0, 1, 0,
-                                        &fv->quant_buf,
-                                        0, VK_WHOLE_SIZE,
+                                        &fv->consts_buf,
+                                        256*sizeof(uint32_t) + 512*sizeof(uint8_t),
+                                        VK_WHOLE_SIZE,
                                         VK_FORMAT_UNDEFINED));
 
 fail:
-- 
2.52.0


>From 01b33b27b29bbe0018af238425bc805e6bc4f163 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sat, 14 Feb 2026 16:29:30 +0100
Subject: [PATCH 54/58] ffv1enc_vulkan: allocate all results memory upfront

Suballocation is the Vulkan way.
---
 libavcodec/ffv1enc_vulkan.c | 57 +++++++++++++++++--------------------
 libavutil/vulkan.c          |  4 ++-
 2 files changed, 29 insertions(+), 32 deletions(-)

diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index 83a8ab7182..a45494d8d8 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -43,9 +43,6 @@ typedef struct VulkanEncodeFFv1FrameData {
     /* Output data */
     AVBufferRef *out_data_ref;
 
-    /* Results data */
-    AVBufferRef *results_data_ref;
-
     /* Copied from the source */
     int64_t pts;
     int64_t duration;
@@ -53,6 +50,7 @@ typedef struct VulkanEncodeFFv1FrameData {
     AVBufferRef *frame_opaque_ref;
 
     int key_frame;
+    int idx;
 } VulkanEncodeFFv1FrameData;
 
 typedef struct VulkanEncodeFFv1Context {
@@ -80,6 +78,9 @@ typedef struct VulkanEncodeFFv1Context {
     /* Constant read-only buffers */
     FFVkBuffer consts_buf;
 
+    /* Results buffer */
+    FFVkBuffer results_buf;
+
     /* Slice data buffer pool */
     AVBufferPool *slice_data_pool;
     AVBufferRef *keyframe_slice_data_ref;
@@ -87,9 +88,6 @@ typedef struct VulkanEncodeFFv1Context {
     /* Output data buffer */
     AVBufferPool *out_data_pool;
 
-    /* Slice results buffer */
-    AVBufferPool *results_data_pool;
-
     /* Intermediate frame pool */
     AVBufferRef *intermediate_frames_ref;
 
@@ -186,9 +184,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     size_t maxsize;
     FFVkBuffer *out_data_buf;
 
-    /* Results data */
-    FFVkBuffer *results_data_buf;
-
     int has_inter = avctx->gop_size > 1;
     uint32_t context_count = f->context_count[f->context_model];
 
@@ -237,15 +232,6 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     }
     slice_data_buf = (FFVkBuffer *)slice_data_ref->data;
 
-    /* Allocate results buffer */
-    RET(ff_vk_get_pooled_buffer(&fv->s, &fv->results_data_pool,
-                                &fd->results_data_ref,
-                                VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                                NULL, f->slice_count*sizeof(uint32_t),
-                                VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
-                                VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
-    results_data_buf = (FFVkBuffer *)fd->results_data_ref->data;
-
     /* Output buffer size */
     maxsize = ff_ffv1_encode_buffer_size(avctx);
     maxsize = FFMIN(maxsize, fv->s.props_11.maxMemoryAllocationSize);
@@ -314,12 +300,12 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
 
     /* Start recording */
     ff_vk_exec_start(&fv->s, exec);
+    fd->idx = exec->idx;
 
     RET(ff_vk_create_imageviews(&fv->s, exec, src_views, src,
                                 FF_VK_REP_NATIVE));
 
     ff_vk_exec_add_dep_buf(&fv->s, exec, &slice_data_ref, 1, has_inter);
-    ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->results_data_ref, 1, 1);
     ff_vk_exec_add_dep_buf(&fv->s, exec, &fd->out_data_ref, 1, 1);
 
     RET(ff_vk_exec_add_dep_frame(&fv->s, exec, src,
@@ -483,8 +469,9 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&fv->s, exec,
                                     &fv->enc, 1, 1, 0,
-                                    results_data_buf,
-                                    0, VK_WHOLE_SIZE,
+                                    &fv->results_buf,
+                                    fd->idx*f->max_slice_count*sizeof(uint32_t),
+                                    f->slice_count*sizeof(uint32_t),
                                     VK_FORMAT_UNDEFINED);
     ff_vk_shader_update_desc_buffer(&fv->s, exec, &fv->enc,
                                     1, 2, 0,
@@ -604,19 +591,19 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
     VulkanEncodeFFv1FrameData *fd = exec->opaque;
 
     FFVkBuffer *out_data_buf = (FFVkBuffer *)fd->out_data_ref->data;
-    FFVkBuffer *results_data_buf = (FFVkBuffer *)fd->results_data_ref->data;
     uint32_t slice_size_max = out_data_buf->size / f->slice_count;
 
     /* Make sure encoding's done */
     ff_vk_exec_wait(&fv->s, exec);
 
     /* Invalidate slice/output data if needed */
-    if (!(results_data_buf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
+    uint32_t rb_off = fd->idx*f->max_slice_count*sizeof(uint32_t);
+    if (!(fv->results_buf.flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
         VkMappedMemoryRange invalidate_data = {
             .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
-            .memory = results_data_buf->mem,
-            .offset = 0,
-            .size = VK_WHOLE_SIZE,
+            .memory = fv->results_buf.mem,
+            .offset = rb_off,
+            .size = f->slice_count*sizeof(uint32_t),
         };
         vk->InvalidateMappedMemoryRanges(fv->s.hwctx->act_dev,
                                          1, &invalidate_data);
@@ -624,8 +611,9 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
 
     /* Calculate final size */
     pkt->size = 0;
+    uint8_t *rb = fv->results_buf.mapped_mem + rb_off;
     for (int i = 0; i < f->slice_count; i++) {
-        uint32_t sl_len = AV_RN32(results_data_buf->mapped_mem + i*4);
+        uint32_t sl_len = AV_RN32(rb + i*4);
         av_log(avctx, AV_LOG_DEBUG, "Slice %i size = %u\n", i, sl_len);
 
         fv->buf_regions[i] = (VkBufferCopy) {
@@ -636,7 +624,6 @@ static int get_packet(AVCodecContext *avctx, FFVkExecContext *exec,
         pkt->size += sl_len;
     }
     av_log(avctx, AV_LOG_VERBOSE, "Encoded data: %iMiB\n", pkt->size / (1024*1024));
-    av_buffer_unref(&fd->results_data_ref); /* No need for this buffer anymore */
 
     /* Allocate packet */
     if ((err = ff_get_encode_buffer(avctx, pkt, pkt->size, 0)) < 0)
@@ -1239,6 +1226,15 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx)
     if (!fv->buf_regions)
         return AVERROR(ENOMEM);
 
+    /* Buffers */
+    RET(ff_vk_create_buf(&fv->s, &fv->results_buf,
+                         fv->async_depth*f->max_slice_count*sizeof(uint32_t),
+                         NULL, NULL,
+                         VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                         VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
+    RET(ff_vk_map_buffer(&fv->s, &fv->results_buf, NULL, 0));
+
 fail:
     return err;
 }
@@ -1259,7 +1255,6 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx)
         for (int i = 0; i < fv->async_depth; i++) {
             VulkanEncodeFFv1FrameData *fd = &fv->exec_ctx_info[i];
             av_buffer_unref(&fd->out_data_ref);
-            av_buffer_unref(&fd->results_data_ref);
             av_buffer_unref(&fd->frame_opaque_ref);
         }
     }
@@ -1267,13 +1262,13 @@ static av_cold int vulkan_encode_ffv1_close(AVCodecContext *avctx)
 
     av_buffer_unref(&fv->intermediate_frames_ref);
 
-    av_buffer_pool_uninit(&fv->results_data_pool);
-
     av_buffer_pool_uninit(&fv->out_data_pool);
 
     av_buffer_unref(&fv->keyframe_slice_data_ref);
     av_buffer_pool_uninit(&fv->slice_data_pool);
 
+    ff_vk_free_buf(&fv->s, &fv->results_buf);
+
     ff_vk_free_buf(&fv->s, &fv->consts_buf);
 
     av_free(fv->buf_regions);
diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c
index 51242c250f..5264abd7ad 100644
--- a/libavutil/vulkan.c
+++ b/libavutil/vulkan.c
@@ -1126,7 +1126,9 @@ int ff_vk_map_buffers(FFVulkanContext *s, FFVkBuffer **buf, uint8_t *mem[],
                    ff_vk_ret2str(ret));
             return AVERROR_EXTERNAL;
         }
-        mem[i] = buf[i]->mapped_mem = dst;
+        buf[i]->mapped_mem = dst;
+        if (mem)
+            mem[i] = dst;
     }
 
     if (!invalidate)
-- 
2.52.0


>From 1347c24573899d14ba676e1cf4d122df66e825c5 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sat, 14 Feb 2026 16:37:25 +0100
Subject: [PATCH 55/58] ffv1enc_vulkan: allocate a device-only output buffer if
 possible

This avoids needing to map HUGE 4GiB chunks of memory.
---
 libavcodec/ffv1enc_vulkan.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index a45494d8d8..a06dc618fa 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -237,16 +237,23 @@ static int vulkan_encode_ffv1_submit_frame(AVCodecContext *avctx,
     maxsize = FFMIN(maxsize, fv->s.props_11.maxMemoryAllocationSize);
 
     /* Allocate output buffer */
+    VkMemoryPropertyFlagBits out_buf_flags;
+    if (maxsize < fv->max_heap_size) {
+        out_buf_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+        /* If we can't map host memory, we can't let the GPU copy its buffer. */
+        if (!fv->s.extensions & FF_VK_EXT_EXTERNAL_HOST_MEMORY)
+            out_buf_flags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+    } else {
+        out_buf_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+                        fv->s.host_cached_flag;
+    }
+
     RET(ff_vk_get_pooled_buffer(&fv->s, &fv->out_data_pool,
                                 &fd->out_data_ref,
                                 VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
                                 VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
                                 VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
-                                NULL, maxsize,
-                                VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
-                                (maxsize < fv->max_heap_size ?
-                                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT :
-                                 fv->s.host_cached_flag)));
+                                NULL, maxsize, out_buf_flags));
     out_data_buf = (FFVkBuffer *)fd->out_data_ref->data;
 
     /* Image views */
-- 
2.52.0


>From 9d1ce9aad3266a882efba0ba68fabe1ccf56cd37 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sat, 14 Feb 2026 17:14:35 +0100
Subject: [PATCH 56/58] ffv1enc_vulkan: use direct values rather than reading
 from struct

This saves indirection and allows compilers to eliminate more
code during compilation.
---
 libavcodec/vulkan/ffv1_enc.comp.glsl       |  7 +++----
 libavcodec/vulkan/ffv1_enc_setup.comp.glsl | 10 +++++-----
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index cde9b941bd..d18259274c 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -260,7 +260,7 @@ void encode_slice(in SliceContext sc, uint slice_idx)
 #endif
 
 #ifndef GOLOMB
-    if (sc.slice_coding_mode == 1) {
+    if (force_pcm) {
 #ifndef RGB
         for (int c = 0; c < color_planes; c++) {
 
@@ -287,7 +287,6 @@ void encode_slice(in SliceContext sc, uint slice_idx)
     }
 #endif
 
-    u8vec4 quant_table_idx = sc.quant_table_idx.xyyz;
     u32vec4 slice_state_off = (slice_idx*codec_planes +
                                uvec4(0, 1, 1, 2))*plane_state_size;
 
@@ -309,7 +308,7 @@ void encode_slice(in SliceContext sc, uint slice_idx)
 
         for (int y = 0; y < h; y++)
             encode_line(sc, src[p], slice_state_off[c], sp, y, p,
-                        comp, quant_table_idx[c], run_index);
+                        comp, U8(context_model), run_index);
     }
 #else
     int run_index = 0;
@@ -319,7 +318,7 @@ void encode_slice(in SliceContext sc, uint slice_idx)
         for (uint c = 0; c < color_planes; c++)
             encode_line(sc, tmp, slice_state_off[c],
                         sp, y, 0, rgb_plane_order[c],
-                        quant_table_idx[c], run_index);
+                        U8(context_model), run_index);
     }
 #endif
 }
diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index 4537cb7b58..4350b34b5e 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -45,7 +45,7 @@ void init_slice(inout SliceContext sc, uint slice_idx)
     sc.slice_reset_contexts = sc.slice_coding_mode == 1;
     sc.quant_table_idx = u8vec3(context_model);
 
-    if (!rct_search || (sc.slice_coding_mode == 1))
+    if (!rct_search || force_pcm)
         sc.slice_rct_coef = ivec2(1, 1);
 
     rac_init(slice_idx*slice_size_max, slice_size_max);
@@ -83,7 +83,7 @@ void write_slice_header(inout SliceContext sc)
 
     [[unroll]]
     for (int i = 0; i < codec_planes; i++)
-        hdr_sym[4 + i] = sc.quant_table_idx[i];
+        hdr_sym[4 + i] = context_model;
 
     hdr_sym[nb_hdr_sym - 3] = pic_mode;
     hdr_sym[nb_hdr_sym - 2] = sar.x;
@@ -93,9 +93,9 @@ void write_slice_header(inout SliceContext sc)
         put_usymbol(hdr_sym[i]);
 
     if (version >= 4) {
-        put_rac(rc_state[0], sc.slice_reset_contexts);
-        put_usymbol(sc.slice_coding_mode);
-        if (sc.slice_coding_mode != 1 && colorspace == 1) {
+        put_rac(rc_state[0], force_pcm);
+        put_usymbol(uint(force_pcm));
+        if (!force_pcm && colorspace == 1) {
             put_usymbol(sc.slice_rct_coef.y);
             put_usymbol(sc.slice_rct_coef.x);
         }
-- 
2.52.0


>From 6256b1f34c8d6ce8155cce8faedecf0edf0567a2 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sat, 14 Feb 2026 17:37:03 +0100
Subject: [PATCH 57/58] vulkan/ffv1: keep track of RCT Ry/By coeffs using
 vector suffixes

This makes it far easier to read, particularly because when reading
or writing, their order is swapped.
---
 libavcodec/vulkan/ffv1_dec.comp.glsl            | 2 +-
 libavcodec/vulkan/ffv1_dec_setup.comp.glsl      | 6 +++---
 libavcodec/vulkan/ffv1_enc.comp.glsl            | 2 +-
 libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl | 2 +-
 libavcodec/vulkan/ffv1_enc_setup.comp.glsl      | 4 ++--
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libavcodec/vulkan/ffv1_dec.comp.glsl b/libavcodec/vulkan/ffv1_dec.comp.glsl
index fef3d4cbae..bfc5bd18c2 100644
--- a/libavcodec/vulkan/ffv1_dec.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec.comp.glsl
@@ -244,7 +244,7 @@ ivec4 transform_sample(ivec4 pix, ivec2 rct_coef)
 {
     pix.b -= rct_offset;
     pix.r -= rct_offset;
-    pix.g -= (pix.b*rct_coef.y + pix.r*rct_coef.x) >> 2;
+    pix.g -= (pix.b*rct_coef.g + pix.r*rct_coef.r) >> 2;
     pix.b += pix.g;
     pix.r += pix.g;
     return ivec4(pix[fmt_lut[0]], pix[fmt_lut[1]],
diff --git a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
index 011713a7dd..2f2a1dcd9d 100644
--- a/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_dec_setup.comp.glsl
@@ -98,9 +98,9 @@ bool decode_slice_header(inout SliceContext sc)
         sc.slice_reset_contexts = get_rac(rc_state[0]);
         sc.slice_coding_mode = get_usymbol();
         if (sc.slice_coding_mode != 1 && colorspace == 1) {
-            sc.slice_rct_coef.x = int(get_usymbol());
-            sc.slice_rct_coef.y = int(get_usymbol());
-            if (sc.slice_rct_coef.x + sc.slice_rct_coef.y > 4)
+            sc.slice_rct_coef.g = int(get_usymbol());
+            sc.slice_rct_coef.r = int(get_usymbol());
+            if (sc.slice_rct_coef.g + sc.slice_rct_coef.r > 4)
                 return true;
         }
     }
diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl
index d18259274c..a56dca396f 100644
--- a/libavcodec/vulkan/ffv1_enc.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc.comp.glsl
@@ -227,7 +227,7 @@ void transform_sample(inout ivec4 pix, ivec2 rct_coef)
 {
     pix.b -= pix.g;
     pix.r -= pix.g;
-    pix.g += (pix.r*rct_coef.x + pix.b*rct_coef.y) >> 2;
+    pix.g += (pix.b*rct_coef.g + pix.r*rct_coef.r) >> 2;
     pix.b += rct_offset;
     pix.r += rct_offset;
 }
diff --git a/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl b/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
index face9583a4..d72c667b10 100644
--- a/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_rct_search.comp.glsl
@@ -71,7 +71,7 @@ ivec3 transform_sample(ivec3 pix, ivec2 rct_coef)
 {
     pix.b -= pix.g;
     pix.r -= pix.g;
-    pix.g += (pix.r*rct_coef.x + pix.b*rct_coef.y) >> 2;
+    pix.g += (pix.b*rct_coef.g + pix.r*rct_coef.r) >> 2;
     pix.b += rct_offset;
     pix.r += rct_offset;
     return pix;
diff --git a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
index 4350b34b5e..62e774ef86 100644
--- a/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
+++ b/libavcodec/vulkan/ffv1_enc_setup.comp.glsl
@@ -96,8 +96,8 @@ void write_slice_header(inout SliceContext sc)
         put_rac(rc_state[0], force_pcm);
         put_usymbol(uint(force_pcm));
         if (!force_pcm && colorspace == 1) {
-            put_usymbol(sc.slice_rct_coef.y);
-            put_usymbol(sc.slice_rct_coef.x);
+            put_usymbol(sc.slice_rct_coef.g);
+            put_usymbol(sc.slice_rct_coef.r);
         }
     }
 }
-- 
2.52.0


>From 572da0ba1211ab590d37dc05988f5354e4fd6bf4 Mon Sep 17 00:00:00 2001
From: Lynne <dev@lynne.ee>
Date: Sun, 15 Feb 2026 17:43:22 +0100
Subject: [PATCH 58/58] ffv1enc_vulkan: perform non-RGB prediction in 16-bits

---
 libavcodec/vulkan/ffv1_common.glsl | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl
index 9032bdf612..5a8bb7dd4d 100644
--- a/libavcodec/vulkan/ffv1_common.glsl
+++ b/libavcodec/vulkan/ffv1_common.glsl
@@ -78,9 +78,15 @@ layout (push_constant, scalar) uniform pushConstants {
 
 #include "rangecoder.glsl"
 
+#if !defined(RGB)
+#define TYPE int16_t
+#define VTYPE2 i16vec2
+#define VTYPE3 i16vec3
+#else
 #define TYPE int32_t
 #define VTYPE2 i32vec2
 #define VTYPE3 i32vec3
+#endif
 
 struct SliceContext {
     RangeCoder c;
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-02-16  8:13 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-16  8:12 [FFmpeg-devel] [PR] Convert and refactor FFv1 to compile-time SPIR-V (PR #21769) Lynne via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git