code wiki / (root) / nx_av1_tile.nx

nx_av1_tile.nx source

↩ module page · 224 lines · 7905 B

1// nx_av1_tile.nx -- AV1/AV2 tile geometry and tile_info, writer and reader. 2// 3// Layer four, on nx_av1_ec.nx / nx_av1_obu.nx / nx_av1_seq.nx. Tiles are how 4// AV1 and AV2 parallelise: a frame is split into independently decodable 5// rectangles, and every tile group in the bitstream is indexed against this 6// geometry. Get the geometry wrong and the tile data is read at the wrong 7// offsets -- the entropy decoder then produces confident nonsense. 8// 9// THE UNITS CHANGE THREE TIMES. Pixels -> 4x4 mode-info units -> superblocks, 10// and the superblock step is 64 or 128 depending on a sequence-header flag. 11// MiCols is 2 * ((width + 7) >> 3), which is NOT (width + 3) >> 2: the frame 12// is rounded to an 8-pixel boundary FIRST and then counted in 4x4 units, so 13// odd sizes round differently than the obvious formula gives. A 1x1 frame has 14// 2 mode-info columns, not 1. 15// 16// ns() IS NOT f(n). The non-symmetric code spends w-1 bits on small values 17// and w bits on large ones, so that an alphabet whose size is not a power of 18// two costs no wasted bits. Reading it as a plain fixed-width field consumes 19// one bit too few on every large value and desynchronises everything after. 20// 21// genealogy_id: av1_spec_5_9_15_tile_info 22// lineage_id: nx_av1_tile_v1 23// license_tier: ORIGINAL 24 25import "nx_syscalls.nx" 26import "nx_bitstream.nx" 27import "nx_av1_seq.nx" 28 29const NX_TILE_MAX_WIDTH: i64 = 4096 30const NX_TILE_MAX_AREA: i64 = 9437184 31const NX_TILE_MAX_COLS: i64 = 64 32const NX_TILE_MAX_ROWS: i64 = 64 33 34const NX_TILE_FLD_COLSLOG2: i64 = 0 35const NX_TILE_FLD_ROWSLOG2: i64 = 1 36const NX_TILE_FLD_COLS: i64 = 2 37const NX_TILE_FLD_ROWS: i64 = 3 38const NX_TILE_FLD_UNIFORM: i64 = 4 39const NX_TILE_FLD_SBCOLS: i64 = 5 40const NX_TILE_FLD_SBROWS: i64 = 6 41 42func nx_av1_min2(a: i64, b: i64) -> i64 { if a < b { return a } return b } 43func nx_av1_max2(a: i64, b: i64) -> i64 { if a > b { return a } return b } 44 45// ===== tile_log2 ================================================== 46// 47// The smallest k with (blk << k) >= target. Bounded so a zero or negative 48// blk cannot spin. 49 50func nx_av1_tile_log2(blk: i64, target: i64) -> i64 { 51 if blk <= 0 { return 0 - 1 } 52 var k: i64 = 0 53 while (blk << k) < target { 54 k = k + 1 55 if k > 40 { return 0 - 1 } 56 } 57 return k 58} 59 60// ===== pixels -> mode-info units -> superblocks ==================== 61// 62// The 8-pixel rounding happens BEFORE the 4x4 count, which is why this is not 63// simply (width + 3) >> 2. 64 65func nx_av1_mi_cols(width: i64) -> i64 { 66 if width <= 0 { return 0 } 67 return 2 * ((width + 7) >> 3) 68} 69 70func nx_av1_mi_rows(height: i64) -> i64 { 71 if height <= 0 { return 0 } 72 return 2 * ((height + 7) >> 3) 73} 74 75func nx_av1_sb_cols(mi_cols: i64, use128: i64) -> i64 { 76 if use128 == 1 { return (mi_cols + 31) >> 5 } 77 return (mi_cols + 15) >> 4 78} 79 80func nx_av1_sb_rows(mi_rows: i64, use128: i64) -> i64 { 81 if use128 == 1 { return (mi_rows + 31) >> 5 } 82 return (mi_rows + 15) >> 4 83} 84 85// ===== ns(n): the non-symmetric code ============================== 86// 87// Small values cost w-1 bits, large ones w. Returns -1 on a bad n or an 88// exhausted bitstream. 89 90func nx_av1_floorlog2b(v: i64) -> i64 { 91 if v <= 0 { return 0 - 1 } 92 var x: i64 = v 93 var n: i64 = 0 94 while x > 1 { x = x >> 1; n = n + 1 } 95 return n 96} 97 98func nx_av1_ns_read(bs: *NxBitStream, n: i64) -> i64 { 99 if n <= 0 { return 0 - 1 } 100 if n == 1 { return 0 } 101 let w: i64 = nx_av1_floorlog2b(n) + 1 102 let m: i64 = (1 << w) - n 103 let v: i64 = nx_bitstream_read_msb(bs, w - 1) 104 if bs.overflow == 1 { return 0 - 1 } 105 if v < m { return v } 106 let extra: i64 = nx_bitstream_read_msb(bs, 1) 107 if bs.overflow == 1 { return 0 - 1 } 108 return (v << 1) - m + extra 109} 110 111func nx_av1_ns_write(w: *NxAv1Bw, v: i64, n: i64) -> i64 { 112 if n <= 0 { return 0 } 113 if v < 0 { return 0 } 114 if v >= n { return 0 } 115 if n == 1 { return 1 } 116 let wd: i64 = nx_av1_floorlog2b(n) + 1 117 let m: i64 = (1 << wd) - n 118 if v < m { 119 nx_av1_bw_put(w, v, wd - 1) 120 } else { 121 let t: i64 = v + m 122 nx_av1_bw_put(w, t >> 1, wd - 1) 123 nx_av1_bw_put(w, t & 1, 1) 124 } 125 return 1 126} 127 128// ===== uniform tile_info ========================================== 129// 130// Writes the uniform-spacing form: a flag, then increment bits raising the 131// column and row log2 counts from their spec-derived minimums. 132 133func nx_av1_tile_write_uniform(w: *NxAv1Bw, width: i64, height: i64, 134 use128: i64, cols_log2: i64, rows_log2: i64) -> i64 { 135 let mi_c: i64 = nx_av1_mi_cols(width) 136 let mi_r: i64 = nx_av1_mi_rows(height) 137 if mi_c <= 0 { return 0 } 138 if mi_r <= 0 { return 0 } 139 let sb_c: i64 = nx_av1_sb_cols(mi_c, use128) 140 let sb_r: i64 = nx_av1_sb_rows(mi_r, use128) 141 142 var sb_shift: i64 = 4 143 if use128 == 1 { sb_shift = 5 } 144 let sb_size: i64 = sb_shift + 2 145 let max_w_sb: i64 = NX_TILE_MAX_WIDTH >> sb_size 146 let max_a_sb: i64 = NX_TILE_MAX_AREA >> (2 * sb_size) 147 148 let min_c: i64 = nx_av1_tile_log2(max_w_sb, sb_c) 149 let max_c: i64 = nx_av1_tile_log2(1, nx_av1_min2(sb_c, NX_TILE_MAX_COLS)) 150 let max_r: i64 = nx_av1_tile_log2(1, nx_av1_min2(sb_r, NX_TILE_MAX_ROWS)) 151 if min_c < 0 { return 0 } 152 if max_c < 0 { return 0 } 153 if max_r < 0 { return 0 } 154 let min_tiles: i64 = nx_av1_max2(min_c, nx_av1_tile_log2(max_a_sb, sb_r * sb_c)) 155 if cols_log2 < min_c { return 0 } 156 if cols_log2 > max_c { return 0 } 157 let min_r: i64 = nx_av1_max2(min_tiles - cols_log2, 0) 158 if rows_log2 < min_r { return 0 } 159 if rows_log2 > max_r { return 0 } 160 161 nx_av1_bw_put(w, 1, 1) // uniform_tile_spacing_flag 162 var c: i64 = min_c 163 while c < max_c { 164 if c < cols_log2 { nx_av1_bw_put(w, 1, 1); c = c + 1 } else { nx_av1_bw_put(w, 0, 1); c = max_c } 165 } 166 var r: i64 = min_r 167 while r < max_r { 168 if r < rows_log2 { nx_av1_bw_put(w, 1, 1); r = r + 1 } else { nx_av1_bw_put(w, 0, 1); r = max_r } 169 } 170 return 1 171} 172 173func nx_av1_tile_parse(bs: *NxBitStream, width: i64, height: i64, 174 use128: i64, fld: *i64) -> i64 { 175 let mi_c: i64 = nx_av1_mi_cols(width) 176 let mi_r: i64 = nx_av1_mi_rows(height) 177 if mi_c <= 0 { return 0 } 178 if mi_r <= 0 { return 0 } 179 let sb_c: i64 = nx_av1_sb_cols(mi_c, use128) 180 let sb_r: i64 = nx_av1_sb_rows(mi_r, use128) 181 182 var sb_shift: i64 = 4 183 if use128 == 1 { sb_shift = 5 } 184 let sb_size: i64 = sb_shift + 2 185 let max_w_sb: i64 = NX_TILE_MAX_WIDTH >> sb_size 186 let max_a_sb: i64 = NX_TILE_MAX_AREA >> (2 * sb_size) 187 188 let min_c: i64 = nx_av1_tile_log2(max_w_sb, sb_c) 189 let max_c: i64 = nx_av1_tile_log2(1, nx_av1_min2(sb_c, NX_TILE_MAX_COLS)) 190 let max_r: i64 = nx_av1_tile_log2(1, nx_av1_min2(sb_r, NX_TILE_MAX_ROWS)) 191 if min_c < 0 { return 0 } 192 if max_c < 0 { return 0 } 193 if max_r < 0 { return 0 } 194 let min_tiles: i64 = nx_av1_max2(min_c, nx_av1_tile_log2(max_a_sb, sb_r * sb_c)) 195 196 let uniform: i64 = nx_bitstream_read_msb(bs, 1) 197 if uniform != 1 { return 0 } 198 199 var cols_log2: i64 = min_c 200 var go: i64 = 1 201 while go == 1 { 202 if cols_log2 >= max_c { go = 0 } else { 203 if nx_bitstream_read_msb(bs, 1) == 1 { cols_log2 = cols_log2 + 1 } else { go = 0 } 204 } 205 } 206 let min_r: i64 = nx_av1_max2(min_tiles - cols_log2, 0) 207 var rows_log2: i64 = min_r 208 go = 1 209 while go == 1 { 210 if rows_log2 >= max_r { go = 0 } else { 211 if nx_bitstream_read_msb(bs, 1) == 1 { rows_log2 = rows_log2 + 1 } else { go = 0 } 212 } 213 } 214 if bs.overflow == 1 { return 0 } 215 216 fld[NX_TILE_FLD_COLSLOG2] = cols_log2 217 fld[NX_TILE_FLD_ROWSLOG2] = rows_log2 218 fld[NX_TILE_FLD_COLS] = 1 << cols_log2 219 fld[NX_TILE_FLD_ROWS] = 1 << rows_log2 220 fld[NX_TILE_FLD_UNIFORM] = uniform 221 fld[NX_TILE_FLD_SBCOLS] = sb_c 222 fld[NX_TILE_FLD_SBROWS] = sb_r 223 return 1 224}