This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 03e58384a6a54ce5db54681141f1ed517981a64c
Author:     Lynne <[email protected]>
AuthorDate: Tue Sep 22 01:20:02 2026 +0900
Commit:     Lynne <[email protected]>
CommitDate: Wed Sep 23 00:30:52 2026 +0900

    vulkan/prores_ks_estimate_slice: keep the planes of a slice within one 
subgroup
    
    The estimate sums the planes of a slice with subgroup shuffles, so
    their lanes must share a subgroup, which the workgroup size derived
    from the maximum subgroup size did not guarantee. Size the workgroup
    to a single subgroup and pin its size where the device allows it.
    
    The overquant search also ran the shuffles inside a loop the slices
    leave at different iterations; ANV then sank the unused sum out of the
    loop, and every overquant slice scored zero. Keep the loop
    subgroup-uniform instead.
    
    Output on ANV is now identical to RADV.
---
 libavcodec/proresenc_kostya_vulkan.c               | 17 +++++----
 .../vulkan/prores_ks_estimate_slice.comp.glsl      | 41 ++++++++++++----------
 2 files changed, 34 insertions(+), 24 deletions(-)

diff --git a/libavcodec/proresenc_kostya_vulkan.c 
b/libavcodec/proresenc_kostya_vulkan.c
index 2668d040f2..a8eb7d56bd 100644
--- a/libavcodec/proresenc_kostya_vulkan.c
+++ b/libavcodec/proresenc_kostya_vulkan.c
@@ -119,6 +119,7 @@ typedef struct ProresVulkanContext {
     AVRefStructPool *frame_size_buf_pool;
 
     size_t slice_slot_size;
+    int estimate_slices_per_wg;
     int payload_off;
 
     FFVulkanShader alpha_data_shd;
@@ -244,8 +245,13 @@ static int 
init_estimate_slice_pipeline(ProresVulkanContext *pv, FFVulkanShader*
     int err = 0;
     FFVulkanContext *vkctx = &pv->vkctx;
     FFVulkanDescriptorSetBinding *desc;
-    int subgroup_size = vkctx->subgroup_props.maxSubgroupSize;
-    int dim_x = pv->ctx.alpha_bits ? subgroup_size : (subgroup_size / 3) * 3;
+    /* A single subgroup per workgroup, pinned to its size where possible */
+    int required = vkctx->subgroup_props.requiredSubgroupSizeStages &
+                   VK_SHADER_STAGE_COMPUTE_BIT;
+    int dim_x = required ? vkctx->subgroup_props.maxSubgroupSize :
+                           vkctx->subgroup_props.minSubgroupSize;
+
+    pv->estimate_slices_per_wg = dim_x / pv->ctx.num_planes;
 
     SPEC_LIST_CREATE(sl, 8, 8 * sizeof(uint32_t))
     SPEC_LIST_ADD(sl, 0, 32, pv->ctx.mbs_per_slice);
@@ -258,7 +264,7 @@ static int init_estimate_slice_pipeline(ProresVulkanContext 
*pv, FFVulkanShader*
     SPEC_LIST_ADD(sl, 7, 32, pv->ctx.bits_per_mb);
 
     ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, sl,
-                      (uint32_t []) { dim_x, 1, 1 }, 0);
+                      (uint32_t []) { dim_x, 1, 1 }, required ? dim_x : 0);
 
     desc = (FFVulkanDescriptorSetBinding []) {
         {
@@ -424,8 +430,6 @@ static int vulkan_encode_prores_submit_frame(AVCodecContext 
*avctx, FFVkExecCont
     int err = 0, nb_img_bar = 0, i, is_chroma;
     int min_quant = ctx->profile_info->min_quant;
     int max_quant = ctx->profile_info->max_quant;
-    int subgroup_size = vkctx->subgroup_props.maxSubgroupSize;
-    int estimate_dim_x = ctx->alpha_bits ? subgroup_size : (subgroup_size / 3) 
* 3;
     const AVPixFmtDescriptor *desc = 
av_pix_fmt_desc_get(vkctx->frames->sw_format);
     VkImageView views[AV_NUM_DATA_POINTERS];
     VkImageMemoryBarrier2 img_bar[AV_NUM_DATA_POINTERS];
@@ -579,7 +583,8 @@ static int vulkan_encode_prores_submit_frame(AVCodecContext 
*avctx, FFVkExecCont
                                     &pv->prores_data_tables_buf, 0, 
pv->prores_data_tables_buf.size,
                                     VK_FORMAT_UNDEFINED);
     ff_vk_exec_bind_shader(vkctx, exec, &pv->estimate_slice_shd);
-    vk->CmdDispatch(exec->buf, (ctx->slices_per_picture * ctx->num_planes + 
estimate_dim_x - 1) / estimate_dim_x,
+    vk->CmdDispatch(exec->buf, (ctx->slices_per_picture + 
pv->estimate_slices_per_wg - 1) /
+                               pv->estimate_slices_per_wg,
                                ctx->force_quant ? 1 : (max_quant - min_quant + 
1), 1);
 
     /* Wait for writes to score buffer. */
diff --git a/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl 
b/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl
index 01d2531584..be0f95a9bd 100644
--- a/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl
+++ b/libavcodec/vulkan/prores_ks_estimate_slice.comp.glsl
@@ -23,6 +23,7 @@
 #extension GL_EXT_shader_explicit_arithmetic_types : require
 #extension GL_KHR_shader_subgroup_clustered : require
 #extension GL_KHR_shader_subgroup_shuffle : require
+#extension GL_KHR_shader_subgroup_vote : require
 #extension GL_EXT_maximal_reconvergence : require
 #extension GL_GOOGLE_include_directive : require
 
@@ -236,10 +237,12 @@ int sum_of_planes(int value)
 
 void main() [[maximally_reconverges]]
 {
-    uint slice = gl_GlobalInvocationID.x / num_planes;
+    /* One lane per plane of a slice; the workgroup is a single subgroup */
+    const uint slices_per_wg = gl_WorkGroupSize.x / num_planes;
+    uint slice = gl_WorkGroupID.x * slices_per_wg + gl_LocalInvocationID.x / 
num_planes;
     uint plane = gl_LocalInvocationID.x % num_planes;
     uint q = min_quant + gl_GlobalInvocationID.y;
-    if (slice >= slices_per_picture)
+    if (gl_LocalInvocationID.x >= slices_per_wg * num_planes || slice >= 
slices_per_picture)
         return;
 
     /* Estimate slice bits and error for specified quantizer and plane */
@@ -285,23 +288,25 @@ void main() [[maximally_reconverges]]
         scores[slice].score[max_quant + 1][plane] = error;
         scores[slice].overquant = int(max_quant);
     } else {
-        /* Keep searching until an encoding fits our budget */
-        for (q = max_quant + 1; q < 128; ++q) {
-            /* Estimate slice bits and error for specified quantizer and plane 
*/
-            error = 0;
-            bits = 0;
-            if (plane == 3)
-                bits = estimate_alpha_plane(slice);
-            else
-                bits = estimate_slice_plane(error, slice, plane, q);
-
-            /* Accumulate total bits and error of all planes */
-            total_bits = sum_of_planes(bits);
-            total_score = sum_of_planes(error);
+        /* Keep searching until an encoding fits our budget. The loop stays
+         * subgroup-uniform, as the sums must not run under divergence. */
+        bool done = false;
+        for (uint oq = max_quant + 1; oq < 128 && !subgroupAll(done); ++oq) {
+            int e = 0, b = 0;
+            if (!done)
+                b = plane == 3 ? estimate_alpha_plane(slice) :
+                                 estimate_slice_plane(e, slice, plane, oq);
 
-            /* If estimated bits fit within budget, we are done */
-            if (total_bits <= bits_per_mb * mbs_per_slice)
-                break;
+            int tb = sum_of_planes(b);
+            int ts = sum_of_planes(e);
+            if (!done) {
+                bits = b;
+                error = e;
+                total_bits = tb;
+                total_score = ts;
+                q = oq;
+                done = total_bits <= bits_per_mb * mbs_per_slice;
+            }
         }
 
         scores[slice].bits[max_quant + 1][plane] = bits;

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to