...
This commit is contained in:
@@ -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 $< $@
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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]));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user