1
0
mirror of https://git.ffmpeg.org/ffmpeg.git synced 2026-10-24 02:09:03 +03:00

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.
This commit is contained in:
Lynne
2026-09-22 01:20:02 +09:00
parent eed9ec57c2
commit 03e58384a6
2 changed files with 34 additions and 24 deletions

View File

@@ -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. */

View File

@@ -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);
/* 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);
/* Accumulate total bits and error of all planes */
total_bits = sum_of_planes(bits);
total_score = sum_of_planes(error);
/* 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;