This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit a387d21c3292dcacfc066e64295f9603c6c61aa9 Author: Lynne <[email protected]> AuthorDate: Sun Sep 27 01:15:56 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Sat Oct 3 12:15:47 2026 +0900 ffv1enc_vulkan: encode range coded slices as a single subgroup A range coded slice is encoded by a workgroup of 32 invocations, but only the first one ran the range coder, on a coder state and context states kept in shared memory, with two barriers per sample. The slice is now encoded by a single subgroup. Each invocation holds one of the 32 states of the current context, and the range coder runs on uniform values, in a new encoder section of rangecoder_subgroup.glsl. A decision reads its state from the invocation that holds it and updates it there, so no barriers are needed. The coder keeps the top of its interval instead of the bottom, which coding a one leaves unchanged. The line cache of the range coded encoder is kept per invocation, as in the decoder, and subgroups of 32 invocations are required. The output is unchanged. Encoding a 6464x4852 16-bit RGB frame with 1024 slices on an RX 6900 XT, with the frame in VRAM, goes from 202.5/178.5 ms to 179.0/154.9 ms with context model 1/0. --- libavcodec/ffv1enc_vulkan.c | 14 +++- libavcodec/vulkan/ffv1_common.glsl | 8 ++- libavcodec/vulkan/ffv1_enc.comp.glsl | 51 +++++--------- libavcodec/vulkan/rangecoder_subgroup.glsl | 108 +++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 38 deletions(-) diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c index 1f0d839ded..b358a2a0df 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -1065,7 +1065,8 @@ static int init_encode_shader(AVCodecContext *avctx, VkSpecializationInfo *sl) 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 []) { wg_x, 1, 1 }, 0); + (uint32_t []) { wg_x, 1, 1 }, + fv->ctx.ac != AC_GOLOMB_RICE ? CONTEXT_SIZE : 0); ff_vk_shader_add_push_const(shd, 0, sizeof(FFv1ShaderParams), VK_SHADER_STAGE_COMPUTE_BIT); @@ -1271,6 +1272,17 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) return err; } + /* Range coded slices are encoded by one subgroup of CONTEXT_SIZE + * invocations, each holding one of the states of a context */ + if (f->ac != AC_GOLOMB_RICE && + !(fv->s.subgroup_props.minSubgroupSize <= CONTEXT_SIZE && + fv->s.subgroup_props.maxSubgroupSize >= CONTEXT_SIZE && + (fv->s.subgroup_props.requiredSubgroupSizeStages & VK_SHADER_STAGE_COMPUTE_BIT))) { + av_log(avctx, AV_LOG_ERROR, "Range coded FFv1 encoding needs subgroups " + "of %i invocations\n", CONTEXT_SIZE); + return AVERROR(ENOTSUP); + } + maxsize = ffv1_vk_buffer_size(avctx); if (maxsize > fv->s.props_11.maxMemoryAllocationSize) { av_log(avctx, AV_LOG_WARNING, "Encoding buffer size (%zu) larger " diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl index 61e8446e9c..b23fd6f396 100644 --- a/libavcodec/vulkan/ffv1_common.glsl +++ b/libavcodec/vulkan/ffv1_common.glsl @@ -208,9 +208,13 @@ const uint32_t log2_run[41] = { #ifdef DECODE #define IMG_QUALI coherent readonly -VTYPE2 linecache; #else #define IMG_QUALI readonly +#endif + +#if defined(DECODE) || defined(RC_SUBGROUP) +VTYPE2 linecache; +#else shared VTYPE2 linecache; #endif @@ -315,7 +319,7 @@ ivec2 get_pred(IMG_QUALI uimage2D pred, ivec2 sp, ivec2 off, void linecache_load(IMG_QUALI uimage2D src, ivec2 sp, int y, uint comp) { -#ifdef DECODE +#if defined(DECODE) || defined(RC_SUBGROUP) linecache = VTYPE2(TYPE(0), TYPE(0)); if (y > 0) linecache[1] = TYPE(imageLoad(src, sp + LADDR(ivec2(0, y - 1)))[comp]); diff --git a/libavcodec/vulkan/ffv1_enc.comp.glsl b/libavcodec/vulkan/ffv1_enc.comp.glsl index c59ad5527f..daa94b996e 100644 --- a/libavcodec/vulkan/ffv1_enc.comp.glsl +++ b/libavcodec/vulkan/ffv1_enc.comp.glsl @@ -24,6 +24,9 @@ #extension GL_GOOGLE_include_directive : require #define ENCODE +#ifndef GOLOMB +#define RC_SUBGROUP +#endif /* Golomb slices start writing at rac_terminate()'s byte count, with no * alignment guarantee */ #define PB_UNALIGNED @@ -54,33 +57,9 @@ layout (set = 1, binding = 2, scalar) buffer slice_state_buf { uint8_t slice_rc_state[]; }; -#define WRITE(idx, val) put_rac(rc_state[idx], val) -void put_symbol(int v) -{ - bool is_nil = (v == 0); - WRITE(0, is_nil); - if (is_nil) - return; - - int a = abs(v); - int e = findMSB(a); - - for (int i = 0; i < e; i++) - WRITE(1 + min(i, 9), true); - WRITE(1 + min(e, 9), false); - - for (int i = e - 1; i >= 0; i--) - WRITE(22 + min(i, 9), bool(bitfieldExtract(a, i, 1))); - - WRITE(22 - 11 + min(e, 10), v < 0); -} - void encode_line_pcm(in SliceContext sc, readonly uimage2D img, ivec2 sp, int y, uint p, uint comp) { - if (gl_LocalInvocationID.x > 0) - return; - int w = sc.slice_dim.x; #ifdef BAYER w >>= 1; @@ -94,7 +73,7 @@ void encode_line_pcm(in SliceContext sc, readonly uimage2D img, #endif for (int x = 0; x < w; x++) { - uint v = imageLoad(img, sp + LADDR(ivec2(x, y)))[comp]; + uint v = subgroupBroadcastFirst(imageLoad(img, sp + LADDR(ivec2(x, y)))[comp]); for (uint i = (rct_offset >> 1); i > 0; i >>= 1) put_rac_equi(bool(v & i)); @@ -135,16 +114,10 @@ void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off, uint rc_off = state_off + CONTEXT_SIZE*d[0] + gl_LocalInvocationID.x; - rc_state[gl_LocalInvocationID.x] = slice_rc_state[rc_off]; - barrier(); - - if (gl_LocalInvocationID.x == 0) { - put_symbol(d[1]); - linecache_next(cur); - } - - barrier(); - slice_rc_state[rc_off] = rc_state[gl_LocalInvocationID.x]; + uint st = slice_rc_state[rc_off]; + put_isymbol(st, d[1]); + slice_rc_state[rc_off] = uint8_t(st); + linecache_next(cur); } } @@ -460,6 +433,8 @@ void finalize_slice(in uint slice_idx) uint32_t enc_len = hdr_len + flush_put_bits(pb); #else uint32_t enc_len = rac_terminate(); + if (gl_LocalInvocationID.x > 0) + return; #endif u8buf bs = u8buf(slice_data + rc.bs_start); @@ -498,12 +473,18 @@ void main(void) { uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x; +#ifdef GOLOMB if (gl_LocalInvocationID.x == 0) rc = slice_ctx[slice_idx].c; barrier(); +#else + rac_init_enc(slice_ctx[slice_idx].c); +#endif encode_slice(slice_ctx[slice_idx], slice_idx); +#ifdef GOLOMB if (gl_LocalInvocationID.x == 0) +#endif finalize_slice(slice_idx); } diff --git a/libavcodec/vulkan/rangecoder_subgroup.glsl b/libavcodec/vulkan/rangecoder_subgroup.glsl index da787dc99b..3a2e98d403 100644 --- a/libavcodec/vulkan/rangecoder_subgroup.glsl +++ b/libavcodec/vulkan/rangecoder_subgroup.glsl @@ -60,6 +60,114 @@ uint rac_range1(uint range, uint state24) return hi; } +#ifdef ENCODE +uint rc_top; +uint rc_oc; +int rc_ob; + +void rac_init_enc(in RangeCoder c) +{ + for (uint i = gl_SubgroupInvocationID; i < 512; i += gl_SubgroupSize) + zero_one_state[i] = rangecoder_state[i]; + barrier(); + + rc = c; + rc_top = rc.low + rc.range; + rc_oc = uint(rc.outstanding_count); + rc_ob = int(rc.outstanding_byte); +} + +void rac_renorm_enc(void) +{ + uint low = rc_top - rc.range; + rc.range <<= 8; + rc_top = ((low & 0xFFu) << 8) + rc.range; + + if (rc_ob < 0) { + rc_ob = int(low >> 8); + } else if (low - 0xFF01u < 0xFFu) { + rc_oc++; + } else { + uint lane = gl_SubgroupInvocationID; + uint carry = low >> 16; + if (lane == 0) + slice_data[rc.bs_off].v = uint8_t(uint(rc_ob) + carry); + for (uint k = 0; k < rc_oc; k += 32) + if (lane < rc_oc - k) + slice_data[rc.bs_off + 1 + k + lane].v = uint8_t(carry - 1u); + rc.bs_off += 1 + rc_oc; + rc_oc = 0; + rc_ob = int((low >> 8) & 0xFFu); + } +} + +void put_rac_range1(uint range1, bool bit) +{ + rc_top = bit ? rc_top : rc_top - range1; + rc.range = bit ? range1 : rc.range - range1; + if (rc.range < 0x100) + rac_renorm_enc(); +} + +void put_rac(uint state24, bool bit) +{ + put_rac_range1(rac_range1(rc.range, state24), bit); +} + +void put_rac_equi(bool bit) +{ + put_rac_range1(rc.range >> 1, bit); +} + +uint rac_terminate(void) +{ + uint range1 = (rc.range * 129) >> 8; + rc.range -= range1; + rc_top -= range1; + if (rc.range < 0x100) + rac_renorm_enc(); + + rc_top = rc_top - rc.range + 0x1FEu; + rc.range = 0xFFu; + rac_renorm_enc(); + rc_top = rc_top - rc.range + 0xFFu; + rc.range = 0xFFu; + rac_renorm_enc(); + + rc.low = rc_top - rc.range; + rc.outstanding_count = uint16_t(rc_oc); + rc.outstanding_byte = int16_t(rc_ob); + return rc.bs_off - rc.bs_start; +} + +void put_rac_state(inout uint st, uint i, bool bit) +{ + uint s = subgroupBroadcast(st, i); + put_rac(s << 24, bit); + uint ns = uint(zero_one_state[(uint(bit) << 8) + s]); + st = gl_SubgroupInvocationID == i ? ns : st; +} + +void put_isymbol(inout uint st, int v) +{ + put_rac_state(st, 0, v == 0); + if (v == 0) + return; + + uint a = abs(v); + int e = findMSB(a); + + for (int i = 0; i < e; i++) + put_rac_state(st, 1 + min(i, 9), true); + put_rac_state(st, 1 + min(e, 9), false); + + for (int i = e - 1; i >= 0; i--) + put_rac_state(st, 22 + min(i, 9), bitfieldExtract(a, i, 1) != 0); + + put_rac_state(st, 11 + min(e, 10), v < 0); +} +#endif + #ifdef DECODE uint rc_win; uint rc_dist; -- 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]
