This commit is contained in:
2025-08-27 15:09:05 +09:00
parent 57961d84de
commit a13f66d917
9896 changed files with 2193048 additions and 730 deletions
+24
View File
@@ -0,0 +1,24 @@
GEN_CLEANSUFFIXES = *.o *.c *.d
clean::
$(RM) $(GEN_CLEANSUFFIXES:%=libavcodec/vulkan/%)
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_rct.o vulkan/ffv1_enc_setup.o \
vulkan/ffv1_rct_search.o vulkan/ffv1_enc.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_PRORES_RAW_VULKAN_HWACCEL) += vulkan/common.o \
vulkan/prores_raw.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 $< $@
+279
View File
@@ -0,0 +1,279 @@
/*
* 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
*/
layout(buffer_reference, buffer_reference_align = 1) buffer u8buf {
uint8_t v;
};
layout(buffer_reference, buffer_reference_align = 1) buffer u8vec2buf {
u8vec2 v;
};
layout(buffer_reference, buffer_reference_align = 1) buffer u8vec4buf {
u8vec4 v;
};
layout(buffer_reference, buffer_reference_align = 2) buffer u16buf {
uint16_t v;
};
layout(buffer_reference, buffer_reference_align = 4) buffer u32buf {
uint32_t v;
};
layout(buffer_reference, buffer_reference_align = 4) buffer u32vec2buf {
u32vec2 v;
};
layout(buffer_reference, buffer_reference_align = 8) buffer u64buf {
uint64_t v;
};
#define OFFBUF(type, b, l) \
type(uint64_t(b) + uint64_t(l))
#define zero_extend(a, p) \
((a) & ((1 << (p)) - 1))
#define sign_extend(val, bits) \
bitfieldExtract(val, 0, bits)
#define fold(diff, bits) \
sign_extend(diff, bits)
#define mid_pred(a, b, c) \
max(min((a), (b)), min(max((a), (b)), (c)))
/* TODO: optimize */
uint align(uint src, uint a)
{
uint res = src % a;
if (res == 0)
return src;
return src + a - res;
}
/* TODO: optimize */
uint64_t align64(uint64_t src, uint64_t a)
{
uint64_t res = src % a;
if (res == 0)
return src;
return src + a - res;
}
#define reverse4(src) \
(pack32(unpack8(uint32_t(src)).wzyx))
u32vec2 reverse8(uint64_t src)
{
u32vec2 tmp = unpack32(src);
tmp.x = reverse4(tmp.x);
tmp.y = reverse4(tmp.y);
return tmp.yx;
}
#ifdef PB_32
#define BIT_BUF_TYPE uint32_t
#define BUF_TYPE u32buf
#define BUF_REVERSE(src) reverse4(src)
#define BUF_BITS uint8_t(32)
#define BUF_BYTES uint8_t(4)
#define BYTE_EXTRACT(src, byte_off) \
(uint8_t(bitfieldExtract((src), ((byte_off) << 3), 8)))
#else
#define BIT_BUF_TYPE uint64_t
#define BUF_TYPE u32vec2buf
#define BUF_REVERSE(src) reverse8(src)
#define BUF_BITS uint8_t(64)
#define BUF_BYTES uint8_t(8)
#define BYTE_EXTRACT(src, byte_off) \
(uint8_t(((src) >> ((byte_off) << 3)) & 0xFF))
#endif
struct PutBitContext {
uint64_t buf_start;
uint64_t buf;
BIT_BUF_TYPE bit_buf;
uint8_t bit_left;
};
void put_bits(inout PutBitContext pb, const uint32_t n, uint32_t value)
{
if (n < pb.bit_left) {
pb.bit_buf = (pb.bit_buf << n) | value;
pb.bit_left -= uint8_t(n);
} else {
pb.bit_buf <<= pb.bit_left;
pb.bit_buf |= (value >> (n - pb.bit_left));
#ifdef PB_UNALIGNED
u8buf bs = u8buf(pb.buf);
[[unroll]]
for (uint8_t i = uint8_t(0); i < BUF_BYTES; i++)
bs[i].v = BYTE_EXTRACT(pb.bit_buf, BUF_BYTES - uint8_t(1) - i);
#else
#ifdef DEBUG
if ((pb.buf % BUF_BYTES) != 0)
debugPrintfEXT("put_bits buffer is not aligned!");
#endif
BUF_TYPE bs = BUF_TYPE(pb.buf);
bs.v = BUF_REVERSE(pb.bit_buf);
#endif
pb.buf = uint64_t(bs) + BUF_BYTES;
pb.bit_left += BUF_BITS - uint8_t(n);
pb.bit_buf = value;
}
}
uint32_t flush_put_bits(inout PutBitContext pb)
{
/* Align bits to MSBs */
if (pb.bit_left < BUF_BITS)
pb.bit_buf <<= pb.bit_left;
if (pb.bit_left < BUF_BITS) {
uint to_write = ((BUF_BITS - pb.bit_left - 1) >> 3) + 1;
u8buf bs = u8buf(pb.buf);
for (int i = 0; i < to_write; i++)
bs[i].v = BYTE_EXTRACT(pb.bit_buf, BUF_BYTES - uint8_t(1) - i);
pb.buf = uint64_t(bs) + to_write;
}
pb.bit_left = BUF_BITS;
pb.bit_buf = 0x0;
return uint32_t(pb.buf - pb.buf_start);
}
void init_put_bits(out PutBitContext pb, u8buf data, uint64_t len)
{
pb.buf_start = uint64_t(data);
pb.buf = uint64_t(data);
pb.bit_buf = 0;
pb.bit_left = BUF_BITS;
}
uint64_t put_bits_count(in PutBitContext pb)
{
return (pb.buf - pb.buf_start)*8 + BUF_BITS - pb.bit_left;
}
uint32_t put_bytes_count(in PutBitContext pb)
{
uint64_t num_bytes = (pb.buf - pb.buf_start) + ((BUF_BITS - pb.bit_left) >> 3);
return uint32_t(num_bytes);
}
struct GetBitContext {
uint64_t buf_start;
uint64_t buf;
uint64_t buf_end;
uint64_t bits;
int bits_valid;
int size_in_bits;
};
#define LOAD64() \
{ \
u8vec4buf ptr = u8vec4buf(gb.buf); \
uint32_t rf1 = pack32((ptr[0].v).wzyx); \
uint32_t rf2 = pack32((ptr[1].v).wzyx); \
gb.buf += 8; \
gb.bits = uint64_t(rf1) << 32 | uint64_t(rf2); \
gb.bits_valid = 64; \
}
#define RELOAD32() \
{ \
u8vec4buf ptr = u8vec4buf(gb.buf); \
uint32_t rf = pack32((ptr[0].v).wzyx); \
gb.buf += 4; \
gb.bits = uint64_t(rf) << (32 - gb.bits_valid) | gb.bits; \
gb.bits_valid += 32; \
}
void init_get_bits(inout GetBitContext gb, u8buf data, int len)
{
gb.buf = gb.buf_start = uint64_t(data);
gb.buf_end = uint64_t(data) + len;
gb.size_in_bits = len * 8;
/* Preload */
LOAD64()
}
bool get_bit(inout GetBitContext gb)
{
if (gb.bits_valid == 0)
LOAD64()
bool val = bool(gb.bits >> (64 - 1));
gb.bits <<= 1;
gb.bits_valid--;
return val;
}
uint get_bits(inout GetBitContext gb, int n)
{
if (n == 0)
return 0;
if (n > gb.bits_valid)
RELOAD32()
uint val = uint(gb.bits >> (64 - n));
gb.bits <<= n;
gb.bits_valid -= n;
return val;
}
uint show_bits(inout GetBitContext gb, int n)
{
if (n > gb.bits_valid)
RELOAD32()
return uint(gb.bits >> (64 - n));
}
void skip_bits(inout GetBitContext gb, int n)
{
if (n > gb.bits_valid)
RELOAD32()
gb.bits <<= n;
gb.bits_valid -= n;
}
int tell_bits(in GetBitContext gb)
{
return int(gb.buf - gb.buf_start) * 8 - gb.bits_valid;
}
int left_bits(in GetBitContext gb)
{
return gb.size_in_bits - int(gb.buf - gb.buf_start) * 8 + gb.bits_valid;
}
+181
View File
@@ -0,0 +1,181 @@
/*
* 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;
#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;
};
/* -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];
/* top-2 became current upon swap */
TYPE top2 = TYPE(imageLoad(pred, sp + LADDR(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 /* 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
+302
View File
@@ -0,0 +1,302 @@
/*
* 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 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
int get_isymbol(inout RangeCoder c, uint state_off)
{
if (READ(c, 0))
return 0;
uint e = 1;
for (; e < 33; e++)
if (!READ(c, min(e, 10)))
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)));
}
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)
{
#ifdef CACHED_SYMBOL_READER
if (gl_LocalInvocationID.x > 0)
return;
#endif
#ifndef RGB
if (p > 0 && p < 3) {
w >>= chroma_shift.x;
sp >>= chroma_shift;
}
#endif
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;
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,
uint8_t quant_table_idx, const int run_index)
{
#ifndef RGB
if (p > 0 && p < 3) {
w >>= chroma_shift.x;
sp >>= chroma_shift;
}
#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] > 0);
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;
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
}
}
#else /* GOLOMB */
void decode_line(inout SliceContext sc, ivec2 sp, int w,
int y, int p, int bits, uint state_off,
uint8_t quant_table_idx, inout int run_index)
{
#ifndef RGB
if (p > 0 && p < 3) {
w >>= chroma_shift.x;
sp >>= chroma_shift;
}
#endif
int run_count = 0;
int run_mode = 0;
for (int x = 0; x < w; x++) {
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);
uint context_off = state_off + VLC_STATE_SIZE*abs(pr[0]);
VlcState sb = VlcState(uint64_t(slice_state) + context_off);
if (pr[0] == 0 && run_mode == 0)
run_mode = 1;
if (run_mode != 0) {
if (run_count == 0 && run_mode == 1) {
int tmp_idx = int(log2_run[run_index]);
if (get_bit(sc.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));
} else
run_count = 0;
if (run_index != 0)
run_index--;
run_mode = 2;
}
}
run_count--;
if (run_count < 0) {
run_mode = 0;
run_count = 0;
diff = read_vlc_symbol(sc.gb, sb, bits);
if (diff >= 0)
diff++;
} else {
diff = 0;
}
} else {
diff = read_vlc_symbol(sc.gb, sb, bits);
}
if (pr[0] < 0)
diff = -diff;
uint v = zero_extend(pr[1] + diff, bits);
imageStore(dec[p], sp + LADDR(ivec2(x, y)), uvec4(v));
}
}
#endif
#ifdef RGB
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.b += pix.g;
pix.r += pix.g;
return ivec4(pix[fmt_lut[0]], pix[fmt_lut[1]],
pix[fmt_lut[2]], pix[fmt_lut[3]]);
}
void writeout_rgb(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;
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)
pix.a = int(imageLoad(dec[3], lpos)[0]);
if (expectEXT(apply_rct, true))
pix = transform_sample(pix, sc.slice_rct_coef);
imageStore(dst[0], pos, pix);
if (planar_rgb != 0) {
for (int i = 1; i < color_planes; i++)
imageStore(dst[i], pos, ivec4(pix[i]));
}
}
}
#endif
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;
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
/* PCM coding */
#ifndef GOLOMB
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 >>= chroma_shift.y;
for (int y = 0; y < h; y++)
decode_line_pcm(sc, sp, w, y, p, bits);
}
#else
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
for (int p = 0; p < planes; p++) {
int h = sc.slice_dim.y;
if (p > 0 && p < 3)
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);
}
#endif
}
}
void main(void)
{
const 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;
if (status != 0)
slice_status[2*slice_idx + 1] = status;
}
@@ -0,0 +1,140 @@
/*
* 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
*/
uint8_t setup_state[CONTEXT_SIZE];
uint get_usymbol(inout RangeCoder c)
{
if (get_rac_direct(c, setup_state[0]))
return 0;
int e = 0;
while (get_rac_direct(c, setup_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
}
return a;
}
bool decode_slice_header(inout SliceContext sc)
{
[[unroll]]
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;
if (sx < 0 || sy < 0 || sw <= 0 || sh <= 0 ||
sx > (gl_NumWorkGroups.x - sw) || sy > (gl_NumWorkGroups.y - sh) ||
corrupt) {
return true;
}
/* Set coordinates */
uint sxs = slice_coord(img_size.x, sx , gl_NumWorkGroups.x, chroma_shift.x);
uint sxe = slice_coord(img_size.x, sx + sw, gl_NumWorkGroups.x, chroma_shift.x);
uint sys = slice_coord(img_size.y, sy , gl_NumWorkGroups.y, chroma_shift.y);
uint sye = slice_coord(img_size.y, sy + sh, gl_NumWorkGroups.y, chroma_shift.y);
sc.slice_pos = ivec2(sxs, sys);
sc.slice_dim = ivec2(sxe - sxs, sye - sys);
sc.slice_rct_coef = ivec2(1, 1);
sc.slice_coding_mode = int(0);
for (uint i = 0; i < codec_planes; i++) {
uint idx = get_usymbol(sc.c);
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);
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));
if (sc.slice_rct_coef.x + sc.slice_rct_coef.y > 4)
return true;
}
}
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;
u8buf bs = u8buf(slice_data + slice_offsets[2*slice_idx + 0]);
uint32_t slice_size = slice_offsets[2*slice_idx + 1];
rac_init_dec(slice_ctx[slice_idx].c,
bs, slice_size);
if (slice_idx == (gl_NumWorkGroups.x*gl_NumWorkGroups.y - 1))
get_rac_equi(slice_ctx[slice_idx].c);
decode_slice_header(slice_ctx[slice_idx]);
if (golomb == 1)
golomb_init(slice_ctx[slice_idx]);
if (ec != 0 && check_crc != 0) {
uint32_t crc = crcref;
for (int i = 0; i < slice_size; i++)
crc = crc_ieee[(crc & 0xFF) ^ uint32_t(bs[i].v)] ^ (crc >> 8);
slice_status[2*slice_idx + 0] = crc;
}
slice_status[2*slice_idx + 1] = corrupt ? uint32_t(corrupt) : overread;
}
+358
View File
@@ -0,0 +1,358 @@
/*
* 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 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)
{
bool is_nil = (v == 0);
WRITE(c, 0, is_nil);
if (is_nil)
return;
const int a = abs(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);
for (int i = e - 1; i >= 0; i--)
WRITE(c, 22 + min(i, 9), bool(bitfieldExtract(a, i, 1)));
WRITE(c, 22 - 11 + min(e, 10), v < 0);
}
void encode_line_pcm(inout SliceContext sc, readonly uimage2D img,
ivec2 sp, int y, int p, int comp, int bits)
{
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 >>= chroma_shift.x;
sp >>= chroma_shift;
}
#endif
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)));
}
}
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, const int run_index)
{
int w = sc.slice_dim.x;
#ifndef RGB
if (p > 0 && p < 3) {
w >>= chroma_shift.x;
sp >>= chroma_shift;
}
#endif
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);
d[1] = int(imageLoad(img, sp + LADDR(ivec2(x, y)))[comp]) - d[1];
if (d[0] < 0)
d = -d;
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
}
}
#else /* GOLOMB */
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)
{
int w = sc.slice_dim.x;
#ifndef RGB
if (p > 0 && p < 3) {
w >>= chroma_shift.x;
sp >>= chroma_shift;
}
#endif
int run_count = 0;
bool run_mode = false;
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);
d[1] = int(imageLoad(img, sp + LADDR(ivec2(x, y)))[comp]) - d[1];
if (d[0] < 0)
d = -d;
d[1] = fold(d[1], bits);
if (d[0] == 0)
run_mode = true;
if (run_mode) {
if (d[1] != 0) {
/* A very unlikely loop */
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(sc.pb, 1 + log2_run[run_index], run_count);
if (run_index != 0)
run_index--;
run_count = 0;
run_mode = false;
if (d[1] > 0)
d[1]--;
} else {
run_count++;
}
}
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);
}
}
if (run_mode) {
while (run_count >= (1 << log2_run[run_index])) {
run_count -= 1 << log2_run[run_index];
run_index++;
put_bits(sc.pb, 1, 1);
}
if (run_count > 0)
put_bits(sc.pb, 1, 1);
}
}
#endif
#ifdef RGB
ivec4 load_components(ivec2 pos)
{
ivec4 pix = ivec4(imageLoad(src[0], pos));
if (planar_rgb != 0) {
for (int i = 1; i < (3 + transparency); i++)
pix[i] = int(imageLoad(src[i], pos)[0]);
}
return ivec4(pix[fmt_lut[0]], pix[fmt_lut[1]],
pix[fmt_lut[2]], pix[fmt_lut[3]]);
}
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.b += rct_offset;
pix.r += rct_offset;
}
void preload_rgb(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(pos);
if (expectEXT(apply_rct, true))
transform_sample(pix, sc.slice_rct_coef);
imageStore(tmp, lpos, pix);
}
}
#endif
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;
#endif
#ifndef GOLOMB
if (sc.slice_coding_mode == 1) {
#ifndef RGB
for (int c = 0; c < components; c++) {
int h = sc.slice_dim.y;
if (c > 0 && c < 3)
h >>= chroma_shift.y;
/* Takes into account dual-plane YUV formats */
int p = min(c, planes - 1);
int comp = c - p;
for (int y = 0; y < h; y++)
encode_line_pcm(sc, src[p], sp, y, p, comp, bits);
}
#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);
}
#endif
} else
#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 >>= 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
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
}
}
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 = sc.hdr_len + flush_put_bits(sc.pb);
#else
uint32_t enc_len = rac_terminate(sc.c);
#endif
u8buf bs = u8buf(sc.c.bytestream_start);
/* Append slice length */
u8vec4 enc_len_p = unpack8(enc_len);
bs[enc_len + 0].v = enc_len_p.z;
bs[enc_len + 1].v = enc_len_p.y;
bs[enc_len + 2].v = enc_len_p.x;
enc_len += 3;
/* Calculate and write CRC */
if (ec != 0) {
bs[enc_len].v = uint8_t(0);
enc_len++;
uint32_t crc = crcref;
for (int i = 0; i < enc_len; i++)
crc = crc_ieee[(crc & 0xFF) ^ uint32_t(bs[i].v)] ^ (crc >> 8);
if (crcref != 0x00000000)
crc ^= 0x8CD88196;
u8vec4 crc_p = unpack8(crc);
bs[enc_len + 0].v = crc_p.x;
bs[enc_len + 1].v = crc_p.y;
bs[enc_len + 2].v = crc_p.z;
bs[enc_len + 3].v = crc_p.w;
enc_len += 4;
}
slice_results[slice_idx*2 + 0] = enc_len;
slice_results[slice_idx*2 + 1] = uint64_t(bs) - uint64_t(out_data);
}
void main(void)
{
const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
encode_slice(slice_ctx[slice_idx], slice_idx);
finalize_slice(slice_ctx[slice_idx], slice_idx);
}
@@ -0,0 +1,79 @@
/*
* 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
*/
ivec4 load_components(ivec2 pos)
{
ivec4 pix = ivec4(imageLoad(src[0], pos));
if (planar_rgb != 0) {
for (int i = 1; i < (3 + transparency); i++)
pix[i] = int(imageLoad(src[i], pos)[0]);
}
return ivec4(pix[fmt_lut[0]], pix[fmt_lut[1]],
pix[fmt_lut[2]], pix[fmt_lut[3]]);
}
void bypass_sample(ivec2 pos)
{
imageStore(dst[0], pos, load_components(pos));
}
void bypass_block(in SliceContext sc)
{
ivec2 start = ivec2(gl_LocalInvocationID) + sc.slice_pos;
ivec2 end = sc.slice_pos + sc.slice_dim;
for (uint y = start.y; y < end.y; y += gl_WorkGroupSize.y)
for (uint x = start.x; x < end.x; x += gl_WorkGroupSize.x)
bypass_sample(ivec2(x, y));
}
void transform_sample(ivec2 pos, ivec2 rct_coef)
{
ivec4 pix = load_components(pos);
pix.b -= pix.g;
pix.r -= pix.g;
pix.g += (pix.r*rct_coef.x + pix.b*rct_coef.y) >> 2;
pix.b += offset;
pix.r += offset;
imageStore(dst[0], pos, pix);
}
void transform_block(in SliceContext sc)
{
const ivec2 rct_coef = sc.slice_rct_coef;
const ivec2 start = ivec2(gl_LocalInvocationID) + sc.slice_pos;
const ivec2 end = sc.slice_pos + sc.slice_dim;
for (uint y = start.y; y < end.y; y += gl_WorkGroupSize.y)
for (uint x = start.x; x < end.x; x += gl_WorkGroupSize.x)
transform_sample(ivec2(x, y), rct_coef);
}
void main()
{
const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
if (slice_ctx[slice_idx].slice_coding_mode == 1)
bypass_block(slice_ctx[slice_idx]);
else
transform_block(slice_ctx[slice_idx]);
}
@@ -0,0 +1,126 @@
/*
* 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
*/
uint8_t state[CONTEXT_SIZE];
void init_slice(inout SliceContext sc, const 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,
gl_NumWorkGroups.x, chroma_shift.x);
uint sys = slice_coord(img_size.y, gl_WorkGroupID.y + 0,
gl_NumWorkGroups.y, chroma_shift.y);
uint sye = slice_coord(img_size.y, gl_WorkGroupID.y + 1,
gl_NumWorkGroups.y, chroma_shift.y);
sc.slice_pos = ivec2(sxs, sys);
sc.slice_dim = ivec2(sxe - sxs, sye - sys);
sc.slice_coding_mode = int(force_pcm == 1);
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))
sc.slice_rct_coef = ivec2(1, 1);
rac_init(sc.c,
OFFBUF(u8buf, out_data, slice_idx * slice_size_max),
slice_size_max);
}
void put_usymbol(inout RangeCoder c, uint v)
{
bool is_nil = (v == 0);
put_rac_direct(c, 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);
for (int i = e - 1; i >= 0; i--)
put_rac_direct(c, state[22 + min(i, 9)], bool(bitfieldExtract(v, i, 1)));
}
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);
for (int i = 0; i < codec_planes; i++)
put_usymbol(sc.c, sc.quant_table_idx[i]);
put_usymbol(sc.c, pic_mode);
put_usymbol(sc.c, sar.x);
put_usymbol(sc.c, sar.y);
if (version >= 4) {
put_rac_direct(sc.c, state[0], sc.slice_reset_contexts);
put_usymbol(sc.c, 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);
}
}
}
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;
init_slice(slice_ctx[slice_idx], slice_idx);
if (slice_idx == 0)
write_frame_header(slice_ctx[slice_idx]);
write_slice_header(slice_ctx[slice_idx]);
#ifdef GOLOMB
init_golomb(slice_ctx[slice_idx]);
#endif
}
+90
View File
@@ -0,0 +1,90 @@
/*
* 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
*/
ivec4 load_components(ivec2 pos)
{
ivec4 pix = ivec4(imageLoad(src[0], pos));
if (planar_rgb != 0) {
for (int i = 1; i < (3 + transparency); i++)
pix[i] = int(imageLoad(src[i], pos)[0]);
}
return ivec4(pix[fmt_lut[0]], pix[fmt_lut[1]],
pix[fmt_lut[2]], pix[fmt_lut[3]]);
}
void bypass_sample(ivec2 pos)
{
imageStore(dst[0], pos, load_components(pos));
}
void bypass_block(in SliceContext sc)
{
ivec2 start = ivec2(gl_LocalInvocationID) + sc.slice_pos;
ivec2 end = sc.slice_pos + sc.slice_dim;
for (uint y = start.y; y < end.y; y += gl_WorkGroupSize.y)
for (uint x = start.x; x < end.x; x += gl_WorkGroupSize.x)
bypass_sample(ivec2(x, y));
}
void transform_sample(ivec2 pos, ivec2 rct_coef)
{
ivec4 pix = load_components(pos);
pix.b -= offset;
pix.r -= offset;
pix.g -= (pix.r*rct_coef.x + pix.b*rct_coef.y) >> 2;
pix.b += pix.g;
pix.r += pix.g;
imageStore(dst[0], pos, pix);
}
void transform_sample(ivec2 pos, ivec2 rct_coef)
{
ivec4 pix = load_components(pos);
pix.b -= pix.g;
pix.r -= pix.g;
pix.g += (pix.r*rct_coef.x + pix.b*rct_coef.y) >> 2;
pix.b += offset;
pix.r += offset;
imageStore(dst[0], pos, pix);
}
void transform_block(in SliceContext sc)
{
const ivec2 rct_coef = sc.slice_rct_coef;
const ivec2 start = ivec2(gl_LocalInvocationID) + sc.slice_pos;
const ivec2 end = sc.slice_pos + sc.slice_dim;
for (uint y = start.y; y < end.y; y += gl_WorkGroupSize.y)
for (uint x = start.x; x < end.x; x += gl_WorkGroupSize.x)
transform_sample(ivec2(x, y), rct_coef);
}
void main()
{
const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
if (slice_ctx[slice_idx].slice_coding_mode == 1)
bypass_block(slice_ctx[slice_idx]);
else
transform_block(slice_ctx[slice_idx]);
}
@@ -0,0 +1,139 @@
/*
* 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
*/
ivec3 load_components(ivec2 pos)
{
ivec3 pix = ivec3(imageLoad(src[0], pos));
if (planar_rgb != 0) {
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]]);
}
#define NUM_CHECKS 15
const ivec2 rct_y_coeff[NUM_CHECKS] = {
ivec2(0, 0), // 4G
ivec2(0, 1), // 3G + B
ivec2(1, 0), // R + 3G
ivec2(1, 1), // R + 2G + B
ivec2(0, 2), // 2G + 2B
ivec2(2, 0), // 2R + 2G
ivec2(2, 2), // 2R + 2B
ivec2(0, 3), // 1G + 3B
ivec2(3, 0), // 3R + 1G
ivec2(0, 4), // 4B
ivec2(4, 0), // 4R
ivec2(1, 2), // R + G + 2B
ivec2(2, 1), // 2R + G + B
ivec2(3, 1), // 3R + B
ivec2(1, 3), // R + 3B
};
shared ivec3 pix_buf[gl_WorkGroupSize.x + 1][gl_WorkGroupSize.y + 1] = { };
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.b += rct_offset;
pix.r += rct_offset;
return pix;
}
uint get_dist(ivec3 cur)
{
ivec3 LL = pix_buf[gl_LocalInvocationID.x + 0][gl_LocalInvocationID.y + 1];
ivec3 TL = pix_buf[gl_LocalInvocationID.x + 0][gl_LocalInvocationID.y + 0];
ivec3 TT = pix_buf[gl_LocalInvocationID.x + 1][gl_LocalInvocationID.y + 0];
ivec3 pred = ivec3(predict(LL.r, ivec2(TL.r, TT.r)),
predict(LL.g, ivec2(TL.g, TT.g)),
predict(LL.b, ivec2(TL.b, TT.b)));
uvec3 c = abs(pred - cur);
return mid_pred(c.r, c.g, c.b);
}
shared uint score_cols[gl_WorkGroupSize.y] = { };
shared uint score_mode[16] = { };
void process(ivec2 pos)
{
ivec3 pix = load_components(pos);
for (int i = 0; i < NUM_CHECKS; i++) {
ivec3 tx_pix = transform_sample(pix, rct_y_coeff[i]);
pix_buf[gl_LocalInvocationID.x + 1][gl_LocalInvocationID.y + 1] = tx_pix;
memoryBarrierShared();
uint dist = get_dist(tx_pix);
atomicAdd(score_mode[i], dist);
}
}
void coeff_search(inout SliceContext sc)
{
uvec2 img_size = imageSize(src[0]);
uint sxs = slice_coord(img_size.x, gl_WorkGroupID.x + 0,
gl_NumWorkGroups.x, 0);
uint sxe = slice_coord(img_size.x, gl_WorkGroupID.x + 1,
gl_NumWorkGroups.x, 0);
uint sys = slice_coord(img_size.y, gl_WorkGroupID.y + 0,
gl_NumWorkGroups.y, 0);
uint sye = slice_coord(img_size.y, gl_WorkGroupID.y + 1,
gl_NumWorkGroups.y, 0);
for (uint y = sys + gl_LocalInvocationID.y; y < sye; y += gl_WorkGroupSize.y) {
for (uint x = sxs + gl_LocalInvocationID.x; x < sxe; x += gl_WorkGroupSize.x) {
process(ivec2(x, y));
}
}
if (gl_LocalInvocationID.x == 0 && gl_LocalInvocationID.y == 0) {
uint min_score = 0xFFFFFFFF;
uint min_idx = 3;
for (int i = 0; i < NUM_CHECKS; i++) {
if (score_mode[i] < min_score) {
min_score = score_mode[i];
min_idx = i;
}
}
sc.slice_rct_coef = rct_y_coeff[min_idx];
}
}
void main(void)
{
if (force_pcm == 1)
return;
const uint slice_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
coeff_search(slice_ctx[slice_idx]);
}
+57
View File
@@ -0,0 +1,57 @@
/*
* 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
*/
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)
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
}
+159
View File
@@ -0,0 +1,159 @@
/*
* 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
*/
#define VLC_STATE_SIZE 8
layout(buffer_reference, buffer_reference_align = VLC_STATE_SIZE) buffer VlcState {
uint32_t error_sum;
int16_t drift;
int8_t bias;
uint8_t count;
};
void update_vlc_state(inout VlcState state, const int v)
{
int drift = state.drift;
int count = state.count;
int bias = state.bias;
state.error_sum += uint16_t(abs(v));
drift += v;
if (count == 128) { // FIXME: variable
count >>= 1;
drift >>= 1;
state.error_sum >>= 1;
}
count++;
if (drift <= -count) {
bias = max(bias - 1, -128);
drift = max(drift + count, -count + 1);
} else if (drift > 0) {
bias = min(bias + 1, 127);
drift = min(drift - count, 0);
}
state.bias = int8_t(bias);
state.drift = int16_t(drift);
state.count = uint8_t(count);
}
struct Symbol {
uint32_t bits;
uint32_t val;
};
Symbol set_ur_golomb(int i, int k, int limit, int esc_len)
{
int e;
Symbol sym;
#ifdef DEBUG
if (i < 0)
debugPrintfEXT("Error: i is zero!");
#endif
e = i >> k;
if (e < limit) {
sym.bits = e + k + 1;
sym.val = (1 << k) + zero_extend(i, k);
} else {
sym.bits = limit + esc_len;
sym.val = i - limit + 1;
}
return sym;
}
/**
* write signed golomb rice code (ffv1).
*/
Symbol set_sr_golomb(int i, int k, int limit, int esc_len)
{
int v;
v = -2 * i - 1;
v ^= (v >> 31);
return set_ur_golomb(v, k, limit, esc_len);
}
Symbol get_vlc_symbol(inout VlcState state, int v, int bits)
{
int i, k, code;
Symbol sym;
v = fold(v - int(state.bias), bits);
i = state.count;
k = 0;
while (i < state.error_sum) { // FIXME: optimize
k++;
i += i;
}
#ifdef DEBUG
if (k > 16)
debugPrintfEXT("Error: k > 16!");
#endif
code = v ^ ((2 * state.drift + state.count) >> 31);
update_vlc_state(state, v);
return set_sr_golomb(code, k, 12, bits);
}
uint get_ur_golomb(inout GetBitContext gb, int k, int limit, int esc_len)
{
for (uint i = 0; i < 12; i++)
if (get_bit(gb))
return get_bits(gb, k) + (i << k);
return get_bits(gb, esc_len) + 11;
}
int get_sr_golomb(inout GetBitContext gb, int k, int limit, int esc_len)
{
int v = int(get_ur_golomb(gb, k, limit, esc_len));
return (v >> 1) ^ -(v & 1);
}
int read_vlc_symbol(inout GetBitContext gb, inout VlcState state, int bits)
{
int k, i, v, ret;
i = state.count;
k = 0;
while (i < state.error_sum) { // FIXME: optimize
k++;
i += i;
}
v = get_sr_golomb(gb, k, 12, bits);
v ^= ((2 * state.drift + state.count) >> 31);
ret = fold(v + state.bias, bits);
update_vlc_state(state, v);
return ret;
}
+347
View File
@@ -0,0 +1,347 @@
/*
* ProRes RAW decoder
*
* Copyright (c) 2025 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
*/
#define I16(x) (int16_t(x))
#define COMP_ID (gl_LocalInvocationID.z)
#define BLOCK_ID (gl_LocalInvocationID.y)
#define ROW_ID (gl_LocalInvocationID.x)
GetBitContext gb;
shared float btemp[gl_WorkGroupSize.z][16][64] = { };
shared float block[gl_WorkGroupSize.z][16][64];
void idct8_horiz(const uint row_id)
{
float t0, t1, t2, t3, t4, t5, t6, t7, u8;
float u0, u1, u2, u3, u4, u5, u6, u7;
/* Input */
t0 = block[COMP_ID][BLOCK_ID][8*row_id + 0];
u4 = block[COMP_ID][BLOCK_ID][8*row_id + 1];
t2 = block[COMP_ID][BLOCK_ID][8*row_id + 2];
u6 = block[COMP_ID][BLOCK_ID][8*row_id + 3];
t1 = block[COMP_ID][BLOCK_ID][8*row_id + 4];
u5 = block[COMP_ID][BLOCK_ID][8*row_id + 5];
t3 = block[COMP_ID][BLOCK_ID][8*row_id + 6];
u7 = block[COMP_ID][BLOCK_ID][8*row_id + 7];
/* Embedded scaled inverse 4-point Type-II DCT */
u0 = t0 + t1;
u1 = t0 - t1;
u3 = t2 + t3;
u2 = (t2 - t3)*(1.4142135623730950488016887242097f) - u3;
t0 = u0 + u3;
t3 = u0 - u3;
t1 = u1 + u2;
t2 = u1 - u2;
/* Embedded scaled inverse 4-point Type-IV DST */
t5 = u5 + u6;
t6 = u5 - u6;
t7 = u4 + u7;
t4 = u4 - u7;
u7 = t7 + t5;
u5 = (t7 - t5)*(1.4142135623730950488016887242097f);
u8 = (t4 + t6)*(1.8477590650225735122563663787936f);
u4 = u8 - t4*(1.0823922002923939687994464107328f);
u6 = u8 - t6*(2.6131259297527530557132863468544f);
t7 = u7;
t6 = t7 - u6;
t5 = t6 + u5;
t4 = t5 - u4;
/* Butterflies */
u0 = t0 + t7;
u7 = t0 - t7;
u6 = t1 + t6;
u1 = t1 - t6;
u2 = t2 + t5;
u5 = t2 - t5;
u4 = t3 + t4;
u3 = t3 - t4;
/* Output */
btemp[COMP_ID][BLOCK_ID][0*8 + row_id] = u0;
btemp[COMP_ID][BLOCK_ID][1*8 + row_id] = u1;
btemp[COMP_ID][BLOCK_ID][2*8 + row_id] = u2;
btemp[COMP_ID][BLOCK_ID][3*8 + row_id] = u3;
btemp[COMP_ID][BLOCK_ID][4*8 + row_id] = u4;
btemp[COMP_ID][BLOCK_ID][5*8 + row_id] = u5;
btemp[COMP_ID][BLOCK_ID][6*8 + row_id] = u6;
btemp[COMP_ID][BLOCK_ID][7*8 + row_id] = u7;
}
void idct8_vert(const uint row_id)
{
float t0, t1, t2, t3, t4, t5, t6, t7, u8;
float u0, u1, u2, u3, u4, u5, u6, u7;
/* Input */
t0 = btemp[COMP_ID][BLOCK_ID][8*row_id + 0] + 0.5f; // NOTE
u4 = btemp[COMP_ID][BLOCK_ID][8*row_id + 1];
t2 = btemp[COMP_ID][BLOCK_ID][8*row_id + 2];
u6 = btemp[COMP_ID][BLOCK_ID][8*row_id + 3];
t1 = btemp[COMP_ID][BLOCK_ID][8*row_id + 4];
u5 = btemp[COMP_ID][BLOCK_ID][8*row_id + 5];
t3 = btemp[COMP_ID][BLOCK_ID][8*row_id + 6];
u7 = btemp[COMP_ID][BLOCK_ID][8*row_id + 7];
/* Embedded scaled inverse 4-point Type-II DCT */
u0 = t0 + t1;
u1 = t0 - t1;
u3 = t2 + t3;
u2 = (t2 - t3)*(1.4142135623730950488016887242097f) - u3;
t0 = u0 + u3;
t3 = u0 - u3;
t1 = u1 + u2;
t2 = u1 - u2;
/* Embedded scaled inverse 4-point Type-IV DST */
t5 = u5 + u6;
t6 = u5 - u6;
t7 = u4 + u7;
t4 = u4 - u7;
u7 = t7 + t5;
u5 = (t7 - t5)*(1.4142135623730950488016887242097f);
u8 = (t4 + t6)*(1.8477590650225735122563663787936f);
u4 = u8 - t4*(1.0823922002923939687994464107328f);
u6 = u8 - t6*(2.6131259297527530557132863468544f);
t7 = u7;
t6 = t7 - u6;
t5 = t6 + u5;
t4 = t5 - u4;
/* Butterflies */
u0 = t0 + t7;
u7 = t0 - t7;
u6 = t1 + t6;
u1 = t1 - t6;
u2 = t2 + t5;
u5 = t2 - t5;
u4 = t3 + t4;
u3 = t3 - t4;
/* Output */
block[COMP_ID][BLOCK_ID][0*8 + row_id] = u0;
block[COMP_ID][BLOCK_ID][1*8 + row_id] = u1;
block[COMP_ID][BLOCK_ID][2*8 + row_id] = u2;
block[COMP_ID][BLOCK_ID][3*8 + row_id] = u3;
block[COMP_ID][BLOCK_ID][4*8 + row_id] = u4;
block[COMP_ID][BLOCK_ID][5*8 + row_id] = u5;
block[COMP_ID][BLOCK_ID][6*8 + row_id] = u6;
block[COMP_ID][BLOCK_ID][7*8 + row_id] = u7;
}
int16_t get_value(int16_t codebook)
{
const int16_t switch_bits = codebook >> 8;
const int16_t rice_order = codebook & I16(0xf);
const int16_t exp_order = (codebook >> 4) & I16(0xf);
uint32_t b = show_bits(gb, 32);
if (expectEXT(b == 0, false))
return I16(0);
int16_t q = I16(31) - I16(findMSB(b));
if ((b & 0x80000000) != 0) {
skip_bits(gb, 1 + rice_order);
return I16((b & 0x7FFFFFFF) >> (31 - rice_order));
}
if (q <= switch_bits) {
skip_bits(gb, q + rice_order + 1);
return I16((q << rice_order) +
(((b << (q + 1)) >> 1) >> (31 - rice_order)));
}
int16_t bits = exp_order + (q << 1) - switch_bits;
skip_bits(gb, bits);
return I16((b >> (32 - bits)) +
((switch_bits + 1) << rice_order) -
(1 << exp_order));
}
#define TODCCODEBOOK(x) ((x + 1) >> 1)
void read_dc_vals(const uint nb_blocks)
{
int16_t dc, dc_add;
int16_t prev_dc = I16(0), sign = I16(0);
/* Special handling for first block */
dc = get_value(I16(700));
prev_dc = (dc >> 1) ^ -(dc & I16(1));
btemp[COMP_ID][0][0] = prev_dc;
for (uint n = 1; n < nb_blocks; n++) {
if (expectEXT(left_bits(gb) <= 0, false))
break;
uint8_t dc_codebook;
if ((n & 15) == 1)
dc_codebook = uint8_t(100);
else
dc_codebook = dc_cb[min(TODCCODEBOOK(dc), 13 - 1)];
dc = get_value(dc_codebook);
sign = sign ^ dc & int16_t(1);
dc_add = (-sign ^ I16(TODCCODEBOOK(dc))) + sign;
sign = I16(dc_add < 0);
prev_dc += dc_add;
btemp[COMP_ID][n][0] = prev_dc;
}
}
void read_ac_vals(const uint nb_blocks)
{
const uint nb_codes = nb_blocks << 6;
const uint log2_nb_blocks = findMSB(nb_blocks);
const uint block_mask = (1 << log2_nb_blocks) - 1;
int16_t ac, rn, ln;
int16_t ac_codebook = I16(49);
int16_t rn_codebook = I16( 0);
int16_t ln_codebook = I16(66);
int16_t sign;
int16_t val;
for (uint n = nb_blocks; n <= nb_codes;) {
if (expectEXT(left_bits(gb) <= 0, false))
break;
ln = get_value(ln_codebook);
for (uint i = 0; i < ln; i++) {
if (expectEXT(left_bits(gb) <= 0, false))
break;
if (expectEXT(n >= nb_codes, false))
break;
ac = get_value(ac_codebook);
ac_codebook = ac_cb[min(ac, 95 - 1)];
sign = -int16_t(get_bit(gb));
val = ((ac + I16(1)) ^ sign) - sign;
btemp[COMP_ID][n & block_mask][n >> log2_nb_blocks] = val;
n++;
}
if (expectEXT(n >= nb_codes, false))
break;
rn = get_value(rn_codebook);
rn_codebook = rn_cb[min(rn, 28 - 1)];
n += rn + 1;
if (expectEXT(n >= nb_codes, false))
break;
if (expectEXT(left_bits(gb) <= 0, false))
break;
ac = get_value(ac_codebook);
sign = -int16_t(get_bit(gb));
val = ((ac + I16(1)) ^ sign) - sign;
btemp[COMP_ID][n & block_mask][n >> log2_nb_blocks] = val;
ac_codebook = ac_cb[min(ac, 95 - 1)];
ln_codebook = ln_cb[min(ac, 15 - 1)];
n++;
}
}
void main(void)
{
const uint tile_idx = gl_WorkGroupID.y*gl_NumWorkGroups.x + gl_WorkGroupID.x;
TileData td = tile_data[tile_idx];
if (expectEXT(td.pos.x >= frame_size.x, false))
return;
uint64_t pkt_offset = uint64_t(pkt_data) + td.offset;
u8vec2buf hdr_data = u8vec2buf(pkt_offset);
float qscale = float(pack16(hdr_data[0].v.yx)) / 2.0f;
ivec4 size = ivec4(td.size,
pack16(hdr_data[2].v.yx),
pack16(hdr_data[1].v.yx),
pack16(hdr_data[3].v.yx));
size[0] = size[0] - size[1] - size[2] - size[3] - 8;
if (expectEXT(size[0] < 0, false))
return;
const ivec2 offs = td.pos + ivec2(COMP_ID & 1, COMP_ID >> 1);
const uint w = min(tile_size.x, frame_size.x - td.pos.x) / 2;
const uint nb_blocks = w / 8;
const ivec4 comp_offset = ivec4(size[2] + size[1] + size[3],
size[2],
0,
size[2] + size[1]);
if (BLOCK_ID == 0 && ROW_ID == 0) {
init_get_bits(gb, u8buf(pkt_offset + 8 + comp_offset[COMP_ID]),
size[COMP_ID]);
read_dc_vals(nb_blocks);
read_ac_vals(nb_blocks);
}
barrier();
[[unroll]]
for (uint i = gl_LocalInvocationID.x; i < 64; i += gl_WorkGroupSize.x)
block[COMP_ID][BLOCK_ID][i] = (btemp[COMP_ID][BLOCK_ID][scan[i]] / 16384.0) *
(float(qmat[i]) / 295.0) *
idct_8x8_scales[i] * qscale;
barrier();
#ifdef PARALLEL_ROWS
idct8_horiz(ROW_ID);
barrier();
idct8_vert(ROW_ID);
#else
for (uint j = 0; j < 8; j++)
idct8_horiz(j);
barrier();
for (uint j = 0; j < 8; j++)
idct8_vert(j);
#endif
barrier();
[[unroll]]
for (uint i = gl_LocalInvocationID.x; i < 64; i += gl_WorkGroupSize.x)
imageStore(dst,
offs + 2*ivec2(BLOCK_ID*8 + (i & 7), i >> 3),
vec4(block[COMP_ID][BLOCK_ID][i]));
}
+241
View File
@@ -0,0 +1,241 @@
/*
* 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 RangeCoder {
uint64_t bytestream_start;
uint64_t bytestream;
uint64_t bytestream_end;
int low;
int range;
uint16_t outstanding_count;
uint8_t outstanding_byte;
};
#ifdef FULL_RENORM
/* Full renorm version that can handle outstanding_byte == 0xFF */
void renorm_encoder(inout RangeCoder c)
{
int bs_cnt = 0;
u8buf bytestream = u8buf(c.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;
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;
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));
} else {
c.outstanding_count++;
}
c.bytestream += bs_cnt;
c.range <<= 8;
c.low = bitfieldInsert(0, c.low, 8, 8);
}
#else
/* Cannot deal with outstanding_byte == -1 in the name of speed */
void renorm_encoder(inout RangeCoder c)
{
uint16_t oc = c.outstanding_count + uint16_t(1);
int low = c.low;
c.range <<= 8;
c.low = bitfieldInsert(0, low, 8, 8);
if (low > 0xFF00 && low < 0x10000) {
c.outstanding_count = oc;
return;
}
u8buf bs = u8buf(c.bytestream);
uint8_t outstanding_byte = c.outstanding_byte;
c.bytestream = uint64_t(bs) + oc;
c.outstanding_count = uint16_t(0);
c.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;
for (int i = 1; i < oc; i++)
bs[i].v = fill;
}
#endif
void put_rac_internal(inout RangeCoder c, const int range1, bool bit)
{
#ifdef DEBUG
if (range1 >= c.range)
debugPrintfEXT("Error: range1 >= c.range");
if (range1 <= 0)
debugPrintfEXT("Error: range1 <= 0");
#endif
int ranged = c.range - range1;
c.low += bit ? ranged : 0;
c.range = bit ? range1 : ranged;
if (expectEXT(c.range < 0x100, false))
renorm_encoder(c);
}
void put_rac_direct(inout RangeCoder c, inout uint8_t state, bool bit)
{
put_rac_internal(c, (c.range * state) >> 8, bit);
state = zero_one_state[(uint(bit) << 8) + state];
}
void put_rac(inout RangeCoder c, uint64_t state, bool bit)
{
put_rac_direct(c, u8buf(state).v, bit);
}
/* Equiprobable bit */
void put_rac_equi(inout RangeCoder c, bool bit)
{
put_rac_internal(c, c.range >> 1, bit);
}
void put_rac_terminate(inout RangeCoder c)
{
int range1 = (c.range * 129) >> 8;
#ifdef DEBUG
if (range1 >= c.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);
}
/* Return the number of bytes written. */
uint32_t rac_terminate(inout RangeCoder c)
{
put_rac_terminate(c);
c.range = uint16_t(0xFF);
c.low += 0xFF;
renorm_encoder(c);
c.range = uint16_t(0xFF);
renorm_encoder(c);
#ifdef DEBUG
if (c.low != 0)
debugPrintfEXT("Error: c.low != 0");
if (c.range < 0x100)
debugPrintfEXT("Error: range < 0x100");
#endif
return uint32_t(uint64_t(c.bytestream) - uint64_t(c.bytestream_start));
}
void rac_init(out RangeCoder r, 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);
}
/* Decoder */
uint overread = 0;
bool corrupt = false;
void rac_init_dec(out RangeCoder r, u8buf data, uint buf_size)
{
overread = 0;
corrupt = false;
/* Skip priming bytes */
rac_init(r, OFFBUF(u8buf, data, 2), buf_size - 2);
u8vec2 prime = u8vec2buf(data).v;
/* Switch endianness of the priming bytes */
r.low = pack16(prime.yx);
if (r.low >= 0xFF00) {
r.low = 0xFF00;
r.bytestream_end = uint64_t(data) + 2;
}
}
void refill(inout RangeCoder c)
{
c.range <<= 8;
c.low <<= 8;
if (expectEXT(c.bytestream < c.bytestream_end, false)) {
c.low |= u8buf(c.bytestream).v;
c.bytestream++;
} else {
overread++;
}
}
bool get_rac_internal(inout RangeCoder c, const int range1)
{
int ranged = c.range - range1;
bool bit = c.low >= ranged;
c.low -= bit ? ranged : 0;
c.range = (bit ? 0 : ranged) + (bit ? range1 : 0);
if (expectEXT(c.range < 0x100, false))
refill(c);
return bit;
}
bool get_rac_direct(inout RangeCoder c, inout uint8_t state)
{
bool bit = get_rac_internal(c, c.range * state >> 8);
state = zero_one_state[state + (bit ? 256 : 0)];
return bit;
}
bool get_rac(inout RangeCoder c, uint64_t state)
{
return get_rac_direct(c, u8buf(state).v);
}
bool get_rac_equi(inout RangeCoder c)
{
return get_rac_internal(c, c.range >> 1);
}