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}