This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 5a81cd4f2b167dbc30c0884a49cd626b4eb55c9e Author: Lynne <[email protected]> AuthorDate: Sun Sep 27 01:32:33 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Sat Oct 3 12:15:47 2026 +0900 ffv1enc_vulkan: compute contexts and residuals 32 samples at a time Every invocation computed the context, the prediction and the residual of each sample in turn, from neighbours kept in the line cache and loaded from the image, and the states of a sample were only loaded once it was reached. The invocations now compute them for 32 samples at once, one sample each, together with the masks of the states each sample will use, and the image loads of the next 32 samples are issued before the current ones are coded. The states of the next sample are loaded while the current one is coded, and forwarded when both samples use the same context. The byte queue is checked in the loop condition, the extended lookup becomes a specialisation constant, as the encoder uses a single quant table, and RGB rows are preloaded four samples per invocation at a time. 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 126.6/101.6 ms to 53.2/51.2 ms with context model 1/0. Large slices gain the most: with 24 slices, the same frame now takes 1.5 s to encode, where the previous code ran into the GPU's job timeout and lost the device. --- libavcodec/ffv1enc_vulkan.c | 7 +- libavcodec/vulkan/ffv1_common.glsl | 8 +- libavcodec/vulkan/ffv1_enc.comp.glsl | 184 ++++++++++++++++++++++++++++------- 3 files changed, 155 insertions(+), 44 deletions(-) diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c index b358a2a0df..265ae9bf35 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -1331,7 +1331,7 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) !fv->force_pcm && fv->optimize_rct && !f->bayer; /* Init shader specialization consts */ - SPEC_LIST_CREATE(sl, 19, 19*sizeof(uint32_t)) + SPEC_LIST_CREATE(sl, 20, 20*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); @@ -1340,6 +1340,11 @@ static av_cold int vulkan_encode_ffv1_init(AVCodecContext *avctx) SPEC_LIST_ADD(sl, 17, 32, f->context_model); SPEC_LIST_ADD(sl, 18, 32, f->remap_mode); + if (f->ac != AC_GOLOMB_RICE) { + const int16_t (*qt)[MAX_QUANT_TABLE_SIZE] = f->quant_tables[f->context_model]; + SPEC_LIST_ADD(sl, 19, 32, qt[3][127] || qt[4][127]); + } + if (fv->optimize_rct) { err = init_rct_search_shader(avctx, sl); if (err < 0) diff --git a/libavcodec/vulkan/ffv1_common.glsl b/libavcodec/vulkan/ffv1_common.glsl index b23fd6f396..61e8446e9c 100644 --- a/libavcodec/vulkan/ffv1_common.glsl +++ b/libavcodec/vulkan/ffv1_common.glsl @@ -208,13 +208,9 @@ const uint32_t log2_run[41] = { #ifdef DECODE #define IMG_QUALI coherent readonly -#else -#define IMG_QUALI readonly -#endif - -#if defined(DECODE) || defined(RC_SUBGROUP) VTYPE2 linecache; #else +#define IMG_QUALI readonly shared VTYPE2 linecache; #endif @@ -319,7 +315,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) { -#if defined(DECODE) || defined(RC_SUBGROUP) +#ifdef DECODE 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 3e46a7f838..bb311532dd 100644 --- a/libavcodec/vulkan/ffv1_enc.comp.glsl +++ b/libavcodec/vulkan/ffv1_enc.comp.glsl @@ -57,6 +57,8 @@ layout (set = 1, binding = 2, scalar) buffer slice_state_buf { uint8_t slice_rc_state[]; }; +layout (constant_id = 19) const bool enc_ext = false; + void encode_line_pcm(in SliceContext sc, readonly uimage2D img, ivec2 sp, int y, uint p, uint comp) { @@ -101,42 +103,145 @@ void encode_line(in SliceContext sc, readonly uimage2D img, uint state_off, return; #endif - linecache_load(img, sp, y, comp); + int lane = int(gl_SubgroupInvocationID); + uint last_off = ~0u; + uint adapted = 0; - 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]); - TYPE cur = TYPE(imageLoad(img, sp + LADDR(ivec2(x, y)))[comp]); - d[1] = int(cur) - d[1]; + ivec2 pc = ivec2(min(lane, w - 1), y); + uvec4 rcur, rT, rTR, rL, rTL, rLL = uvec4(0), rTT = uvec4(0); +#ifdef RGB + rcur = imageLoad(img, sp + LADDR(pc)); + rT = imageLoad(img, sp + LADDR(pc + ivec2(0, -1))); + rTR = imageLoad(img, sp + LADDR(ivec2(min(pc.x + 1, w - 1), y - 1))); + rL = imageLoad(img, sp + LADDR(pc.x > 0 ? pc + ivec2(-1, 0) : ivec2(0, y - 1))); + rTL = imageLoad(img, sp + LADDR(pc.x > 0 ? pc + ivec2(-1, -1) : ivec2(0, y - 2))); + if (enc_ext) { + rLL = pc.x > 0 ? imageLoad(img, sp + LADDR(pc.x > 1 ? pc + ivec2(-2, 0) : ivec2(0, y - 1))) : uvec4(0); + rTT = imageLoad(img, sp + LADDR(pc + ivec2(0, -2))); + } +#else + int y1 = max(y - 1, 0); + int y2 = max(y - 2, 0); + rcur = imageLoad(img, sp + pc); + rT = imageLoad(img, sp + ivec2(pc.x, y1)); + rTR = imageLoad(img, sp + ivec2(min(pc.x + 1, w - 1), y1)); + rL = imageLoad(img, sp + (pc.x > 0 ? pc + ivec2(-1, 0) : ivec2(0, y1))); + rTL = imageLoad(img, sp + (pc.x > 0 ? ivec2(pc.x - 1, y1) : ivec2(0, y2))); + if (enc_ext) { + rLL = pc.x > 0 && (pc.x > 1 || y > 0) ? + imageLoad(img, sp + (pc.x > 1 ? pc + ivec2(-2, 0) : ivec2(0, y1))) : uvec4(0); + rTT = y > 1 ? imageLoad(img, sp + ivec2(pc.x, y2)) : uvec4(0); + } +#endif - if (d[0] < 0) - d = -d; + for (int x = 0; x < w; x += 32) { + int px = min(x + lane, w - 1); + int cur = int(TYPE(rcur[comp])); + int T = int(TYPE(rT[comp])); + int TR = int(TYPE(rTR[comp])); + int L = int(TYPE(rL[comp])); + int TL = int(TYPE(rTL[comp])); + int LL = int(TYPE(rLL[comp])); + int TT = int(TYPE(rTT[comp])); +#ifndef RGB + if (y < 1) { + T = 0; + TR = 0; + TL = 0; + L = px > 0 ? L : 0; + } else if (y < 2 && px == 0) { + TL = 0; + } +#endif - d[1] = fold(d[1], bits); + int c = quant_table[quant_table_idx][0][(L - TL) & MAX_QUANT_TABLE_MASK] + + quant_table[quant_table_idx][1][(TL - T) & MAX_QUANT_TABLE_MASK] + + quant_table[quant_table_idx][2][(T - TR) & MAX_QUANT_TABLE_MASK]; + if (enc_ext) + c += quant_table[quant_table_idx][3][(LL - L) & MAX_QUANT_TABLE_MASK] + + quant_table[quant_table_idx][4][(TT - T) & MAX_QUANT_TABLE_MASK]; - uint lane = gl_SubgroupInvocationID; - uint rc_off = state_off + CONTEXT_SIZE*d[0] + lane; - uint st = slice_rc_state[rc_off]; + int d = cur - predict(L, ivec2(TL, T)); + if (c < 0) { + c = -c; + d = -d; + } + d = fold(d, bits); + uint soff = state_off + CONTEXT_SIZE*uint(c); - uint ad = abs(d[1]); + uint ad = abs(d); int e = findMSB(ad); int ec = clamp(e, 0, 9); int es = 11 + min(e, 10); - uint used = d[1] == 0 ? 1u : 1u | ((4u << ec) - 2u) | (((1u << ec) - 1u) << 22) | (1u << es); - uint ones = d[1] == 0 ? 1u : ((2u << ec) - 2u) | ((ad & ((1u << ec) - 1u)) << 22) | - (uint(d[1] < 0) << es); - uint nst = zero_one_state[(bitfieldExtract(ones, int(lane), 1) << 8) + st]; + uint used = d == 0 ? 1u : 1u | ((4u << ec) - 2u) | (((1u << ec) - 1u) << 22) | (1u << es); + uint ones = d == 0 ? 1u : ((2u << ec) - 2u) | ((ad & ((1u << ec) - 1u)) << 22) | + (uint(d < 0) << es); - uint s10, s31; - put_isymbol(st, d[1], s10, s31); + int n = min(w - x, 32); + uint so_n = subgroupBroadcast(soff, 0); + int v_n = subgroupBroadcast(d, 0); + uint used_n = subgroupBroadcast(used, 0); + uint ones_n = subgroupBroadcast(ones, 0); + uint st = uint(slice_rc_state[so_n + lane]); + st = so_n == last_off ? adapted : st; - uint adapted = bitfieldExtract(used, int(lane), 1) != 0 ? nst : st; - if (ad >= 1024) - adapted = lane == 10 ? s10 : lane == 31 ? s31 : adapted; - slice_rc_state[rc_off] = uint8_t(adapted); - linecache_next(cur); - if (rc_nev > 42) - rac_emit(); + if (x + 32 < w) { + pc = ivec2(min(x + 32 + lane, w - 1), y); +#ifdef RGB + rcur = imageLoad(img, sp + LADDR(pc)); + rT = imageLoad(img, sp + LADDR(pc + ivec2(0, -1))); + rTR = imageLoad(img, sp + LADDR(ivec2(min(pc.x + 1, w - 1), y - 1))); + rL = imageLoad(img, sp + LADDR(pc + ivec2(-1, 0))); + rTL = imageLoad(img, sp + LADDR(pc + ivec2(-1, -1))); + if (enc_ext) { + rLL = imageLoad(img, sp + LADDR(pc + ivec2(-2, 0))); + rTT = imageLoad(img, sp + LADDR(pc + ivec2(0, -2))); + } +#else + rcur = imageLoad(img, sp + pc); + rT = imageLoad(img, sp + ivec2(pc.x, y1)); + rTR = imageLoad(img, sp + ivec2(min(pc.x + 1, w - 1), y1)); + rL = imageLoad(img, sp + pc + ivec2(-1, 0)); + rTL = imageLoad(img, sp + ivec2(pc.x - 1, y1)); + if (enc_ext) { + rLL = imageLoad(img, sp + pc + ivec2(-2, 0)); + rTT = y > 1 ? imageLoad(img, sp + ivec2(pc.x, y2)) : uvec4(0); + } +#endif + } + + int j = 0; + while (true) { + do { + uint so = so_n; + int v = v_n; + uint used_j = used_n; + uint ones_j = ones_n; + uint nst = zero_one_state[(bitfieldExtract(ones_j, lane, 1) << 8) + st]; + + int jn = min(j + 1, n - 1); + so_n = subgroupBroadcast(soff, jn); + v_n = subgroupBroadcast(d, jn); + used_n = subgroupBroadcast(used, jn); + ones_n = subgroupBroadcast(ones, jn); + uint ld = uint(slice_rc_state[so_n + lane]); + + uint s10, s31; + put_isymbol(st, v, s10, s31); + + adapted = bitfieldExtract(used_j, lane, 1) != 0 ? nst : st; + if (abs(v) >= 1024) + adapted = lane == 10 ? s10 : lane == 31 ? s31 : adapted; + + slice_rc_state[so + lane] = uint8_t(adapted); + last_off = so; + st = so_n == so ? adapted : ld; + } while (++j < n && rc_nev <= 42); + if (rc_nev > 42) + rac_emit(); + if (j >= n) + break; + } } } @@ -282,21 +387,26 @@ void transform_sample(inout ivec4 pix, ivec2 rct_coef, int offset) void preload_rgb(uint slice_idx, in SliceContext sc, ivec2 sp, int w, int y, bool apply_rct) { - 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); - - ivec4 pix = load_components(slice_idx, sc, pos); + for (uint x0 = 0; x0 < w; x0 += 4*gl_WorkGroupSize.x) { + ivec4 pix[4]; + [[unroll]] for (uint k = 0; k < 4; k++) { + uint x = min(x0 + k*gl_WorkGroupSize.x + gl_LocalInvocationID.x, w - 1); + pix[k] = load_components(slice_idx, sc, sc.slice_pos + ivec2(x, y)); + } + [[unroll]] for (uint k = 0; k < 4; k++) { + uint x = x0 + k*gl_WorkGroupSize.x + gl_LocalInvocationID.x; + if (x < w) { #ifdef FLOAT - if (apply_rct) - transform_sample(pix, sc.slice_rct_coef, sc.remap_count[0]); + if (apply_rct) + transform_sample(pix[k], sc.slice_rct_coef, sc.remap_count[0]); #else - if (apply_rct) - transform_sample(pix, sc.slice_rct_coef, rct_offset); + if (apply_rct) + transform_sample(pix[k], sc.slice_rct_coef, rct_offset); #endif - - imageStore(tmp, lpos, pix); + imageStore(tmp, sp + LADDR(ivec2(x, y)), pix[k]); + } + } } memoryBarrierImage(); -- 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]
