/src/avm/av2/common/ccso.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, Alliance for Open Media. All rights reserved |
3 | | * |
4 | | * This source code is subject to the terms of the BSD 3-Clause Clear License |
5 | | * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear |
6 | | * License was not distributed with this source code in the LICENSE file, you |
7 | | * can obtain it at aomedia.org/license/software-license/bsd-3-c-c/. If the |
8 | | * Alliance for Open Media Patent License 1.0 was not distributed with this |
9 | | * source code in the PATENTS file, you can obtain it at |
10 | | * aomedia.org/license/patent-license/. |
11 | | */ |
12 | | |
13 | | #include <assert.h> |
14 | | #include <math.h> |
15 | | #include <string.h> |
16 | | |
17 | | #include "config/avm_scale_rtcd.h" |
18 | | |
19 | | #include "avm/avm_integer.h" |
20 | | #include "av2/common/ccso.h" |
21 | | #include "av2/common/reconinter.h" |
22 | | #include "av2/common/av2_common_int.h" |
23 | | |
24 | | // Derivation of CCSO unit size, ccso unit shall not across tile boundaries |
25 | | int get_ccso_unit_size_log2_adaptive_tile(const AV2_COMMON *cm, |
26 | | int sb_size_log2, |
27 | 363k | int unit_size_log2) { |
28 | 363k | if (cm->seq_params.ccso_unit_matches_sb_size) |
29 | 270k | return sb_size_log2; |
30 | 93.2k | else if (cm->tiles.cols == 1 && cm->tiles.rows == 1) |
31 | 6.76k | return unit_size_log2; |
32 | 86.5k | int unit_size = unit_size_log2; |
33 | 86.5k | if (sb_size_log2 < unit_size_log2) { |
34 | 86.3k | int e2 = 0, e4 = 0; |
35 | 551k | for (int i = 0; i < cm->tiles.cols - 1; ++i) { |
36 | 465k | const int size = |
37 | 465k | cm->tiles.col_start_sb[i + 1] - cm->tiles.col_start_sb[i]; |
38 | 465k | e2 += size & 1; |
39 | 465k | e4 += size & 3; |
40 | 465k | } |
41 | 99.3k | for (int i = 0; i < cm->tiles.rows - 1; ++i) { |
42 | 13.0k | const int size = |
43 | 13.0k | cm->tiles.row_start_sb[i + 1] - cm->tiles.row_start_sb[i]; |
44 | 13.0k | e2 += size & 1; |
45 | 13.0k | e4 += size & 3; |
46 | 13.0k | } |
47 | 86.3k | if (e4 == 0) |
48 | 807 | unit_size = AVMMIN(sb_size_log2 + 2, unit_size_log2); |
49 | 85.5k | else if (e2 == 0) |
50 | 1.15k | unit_size = AVMMIN(sb_size_log2 + 1, unit_size_log2); |
51 | 84.4k | else |
52 | 84.4k | unit_size = AVMMIN(sb_size_log2, unit_size_log2); |
53 | 86.3k | } else { |
54 | 147 | unit_size = sb_size_log2; |
55 | 147 | } |
56 | 86.5k | return unit_size; |
57 | 363k | } |
58 | | |
59 | | /* Pad the border of a frame */ |
60 | | void extend_ccso_border(const YV12_BUFFER_CONFIG *frame, uint16_t *buf, |
61 | 9 | const int d) { |
62 | 9 | int s = frame->y_width + (CCSO_PADDING_SIZE << 1); |
63 | 9 | int h = frame->y_height; |
64 | 9 | int w = frame->y_width; |
65 | 9 | uint16_t *p = &buf[d * s + d]; |
66 | 3.66k | for (int y = 0; y < h; y++) { |
67 | 21.9k | for (int x = 0; x < d; x++) { |
68 | 18.2k | *(p - d + x) = p[0]; |
69 | 18.2k | p[w + x] = p[w - 1]; |
70 | 18.2k | } |
71 | 3.65k | p += s; |
72 | 3.65k | } |
73 | 9 | p -= (s + d); |
74 | 54 | for (int y = 0; y < d; y++) { |
75 | 45 | memcpy(p + (y + 1) * s, p, sizeof(uint16_t) * (w + (d << 1))); |
76 | 45 | } |
77 | 9 | p -= ((h - 1) * s); |
78 | 54 | for (int y = 0; y < d; y++) { |
79 | 45 | memcpy(p - (y + 1) * s, p, sizeof(uint16_t) * (w + (d << 1))); |
80 | 45 | } |
81 | 9 | } |
82 | | |
83 | | void extend_ccso_tile_border(const int tile_height, const int tile_width, |
84 | | const int tile_stride, uint16_t *buf, |
85 | 17 | const int d) { |
86 | 17 | uint16_t *p = &buf[d * tile_stride + d]; |
87 | 7.32k | for (int y = 0; y < tile_height; y++) { |
88 | 43.8k | for (int x = 0; x < d; x++) { |
89 | 36.5k | *(p - d + x) = p[0]; |
90 | 36.5k | p[tile_width + x] = p[tile_width - 1]; |
91 | 36.5k | } |
92 | 7.30k | p += tile_stride; |
93 | 7.30k | } |
94 | 17 | p -= (tile_stride + d); |
95 | 102 | for (int y = 0; y < d; y++) { |
96 | 85 | memcpy(p + (y + 1) * tile_stride, p, |
97 | 85 | sizeof(uint16_t) * (tile_width + (d << 1))); |
98 | 85 | } |
99 | 17 | p -= ((tile_height - 1) * tile_stride); |
100 | 102 | for (int y = 0; y < d; y++) { |
101 | 85 | memcpy(p - (y + 1) * tile_stride, p, |
102 | 85 | sizeof(uint16_t) * (tile_width + (d << 1))); |
103 | 85 | } |
104 | 17 | } |
105 | | |
106 | | /* Derive the quantized index, later it can be used for retriving offset values |
107 | | * from the look-up table */ |
108 | | void cal_filter_support(int *rec_luma_idx, const uint16_t *rec_y, |
109 | | const int quant_step_size, const int inv_quant_step, |
110 | 128 | const int *rec_idx, const int edge_clf) { |
111 | 128 | if (edge_clf == 0) { |
112 | 0 | for (int i = 0; i < 2; i++) { |
113 | 0 | int d = rec_y[rec_idx[i]] - rec_y[0]; |
114 | 0 | if (d > quant_step_size) |
115 | 0 | rec_luma_idx[i] = 2; |
116 | 0 | else if (d < inv_quant_step) |
117 | 0 | rec_luma_idx[i] = 0; |
118 | 0 | else |
119 | 0 | rec_luma_idx[i] = 1; |
120 | 0 | } |
121 | 128 | } else { // if (edge_clf == 1) |
122 | 384 | for (int i = 0; i < 2; i++) { |
123 | 256 | int d = rec_y[rec_idx[i]] - rec_y[0]; |
124 | 256 | if (d < inv_quant_step) |
125 | 0 | rec_luma_idx[i] = 0; |
126 | 256 | else |
127 | 256 | rec_luma_idx[i] = 1; |
128 | 256 | } |
129 | 128 | } |
130 | 128 | } |
131 | | |
132 | | /* Derive sample locations for CCSO */ |
133 | | void derive_ccso_sample_pos(int *rec_idx, const int ccso_stride, |
134 | 34 | const uint8_t ext_filter_support) { |
135 | | // Input sample locations for CCSO |
136 | | // 4 2 0 3 5 |
137 | | // 6 1 x 1 6 |
138 | | // 5 3 0 2 4 |
139 | 34 | assert(ext_filter_support < 7); |
140 | 34 | if (ext_filter_support == 0) { |
141 | 28 | rec_idx[0] = -1 * ccso_stride; |
142 | 28 | rec_idx[1] = 1 * ccso_stride; |
143 | 28 | } else if (ext_filter_support == 1) { |
144 | 0 | rec_idx[0] = -1; |
145 | 0 | rec_idx[1] = 1; |
146 | 6 | } else if (ext_filter_support == 4) { |
147 | 0 | rec_idx[0] = -ccso_stride - 2; |
148 | 0 | rec_idx[1] = ccso_stride + 2; |
149 | 6 | } else if (ext_filter_support == 5) { |
150 | 2 | rec_idx[0] = ccso_stride - 2; |
151 | 2 | rec_idx[1] = -ccso_stride + 2; |
152 | 4 | } else if (ext_filter_support == 2) { |
153 | 0 | rec_idx[0] = -1 * ccso_stride - 1; |
154 | 0 | rec_idx[1] = 1 * ccso_stride + 1; |
155 | 4 | } else if (ext_filter_support == 3) { |
156 | 0 | rec_idx[0] = -1 * ccso_stride + 1; |
157 | 0 | rec_idx[1] = 1 * ccso_stride - 1; |
158 | 4 | } else { // ext_filter_support == 6 |
159 | 4 | rec_idx[0] = 2; |
160 | 4 | rec_idx[1] = -2; |
161 | 4 | } |
162 | 34 | } |
163 | | |
164 | | /* Derive rect area of a tile for CCSO process */ |
165 | | INLINE static AV2PixelRect av2_get_tile_rect_ccso(const TileInfo *tile_info, |
166 | | const AV2_COMMON *cm, |
167 | 32 | MACROBLOCKD *xd, int is_uv) { |
168 | 32 | AV2PixelRect r; |
169 | | |
170 | | // Calculate position in the Y plane |
171 | 32 | r.left = tile_info->mi_col_start * MI_SIZE; |
172 | 32 | r.right = tile_info->mi_col_end * MI_SIZE; |
173 | 32 | r.top = tile_info->mi_row_start * MI_SIZE; |
174 | 32 | r.bottom = tile_info->mi_row_end * MI_SIZE; |
175 | | |
176 | 32 | const int frame_w = xd->plane[0].dst.width; |
177 | 32 | const int frame_h = xd->plane[0].dst.height; |
178 | | |
179 | | // Make sure we don't fall off the bottom-right of the frame. |
180 | 32 | r.right = AVMMIN(r.right, frame_w); |
181 | 32 | r.bottom = AVMMIN(r.bottom, frame_h); |
182 | | |
183 | | // Convert to coordinates in the appropriate plane |
184 | 32 | const int ss_x = is_uv && cm->seq_params.subsampling_x; |
185 | 32 | const int ss_y = is_uv && cm->seq_params.subsampling_y; |
186 | | |
187 | 32 | r.left = ROUND_POWER_OF_TWO(r.left, ss_x); |
188 | 32 | r.right = ROUND_POWER_OF_TWO(r.right, ss_x); |
189 | 32 | r.top = ROUND_POWER_OF_TWO(r.top, ss_y); |
190 | 32 | r.bottom = ROUND_POWER_OF_TWO(r.bottom, ss_y); |
191 | | |
192 | 32 | return r; |
193 | 32 | } |
194 | | |
195 | | // Cross-component Sample Offset band offset only case. |
196 | | void ccso_filter_block_hbd_wo_buf_bo_only_c( |
197 | | const uint16_t *src_y, uint16_t *dst_yuv, const int x, const int y, |
198 | | const int pic_width, const int pic_height, const int8_t *offset_buf, |
199 | | const int src_y_stride, const int dst_stride, const int y_uv_hscale, |
200 | | const int y_uv_vscale, const int max_val, const int blk_size_x, |
201 | 0 | const int blk_size_y, const bool isSingleBand, const uint8_t shift_bits) { |
202 | 0 | const int y_end = AVMMIN(pic_height - y, blk_size_y); |
203 | 0 | const int x_end = AVMMIN(pic_width - x, blk_size_x); |
204 | 0 | for (int y_start = 0; y_start < y_end; y_start++) { |
205 | 0 | const int y_pos = y_start; |
206 | 0 | for (int x_start = 0; x_start < x_end; x_start++) { |
207 | 0 | const int x_pos = x + x_start; |
208 | 0 | const int band_num = isSingleBand |
209 | 0 | ? 0 |
210 | 0 | : src_y[(y_pos << y_uv_vscale) * src_y_stride + |
211 | 0 | (x_pos << y_uv_hscale)] >> |
212 | 0 | shift_bits; |
213 | 0 | const int lut_idx_ext = (band_num << 4); |
214 | 0 | const int offset_val = offset_buf[lut_idx_ext]; |
215 | 0 | dst_yuv[y_pos * dst_stride + x_pos] = |
216 | 0 | clamp(offset_val + dst_yuv[y_pos * dst_stride + x_pos], 0, max_val); |
217 | 0 | } |
218 | 0 | } |
219 | 0 | } |
220 | | |
221 | | void ccso_filter_block_hbd_wo_buf_c( |
222 | | const uint16_t *src_y, uint16_t *dst_yuv, const int x, const int y, |
223 | | const int pic_width, const int pic_height, int *src_cls, |
224 | | const int8_t *offset_buf, const int src_y_stride, const int dst_stride, |
225 | | const int y_uv_hscale, const int y_uv_vscale, const int thr, |
226 | | const int neg_thr, const int *src_loc, const int max_val, |
227 | | const int blk_size_x, const int blk_size_y, const bool isSingleBand, |
228 | 34 | const uint8_t shift_bits, const int edge_clf, const uint8_t ccso_bo_only) { |
229 | 34 | const int y_end = AVMMIN(pic_height - y, blk_size_y); |
230 | 34 | const int x_end = AVMMIN(pic_width - x, blk_size_x); |
231 | 898 | for (int y_start = 0; y_start < y_end; y_start++) { |
232 | 864 | const int y_pos = y_start; |
233 | 14.6k | for (int x_start = 0; x_start < x_end; x_start++) { |
234 | 13.8k | const int x_pos = x + x_start; |
235 | 13.8k | if (!ccso_bo_only) { |
236 | 0 | cal_filter_support(src_cls, |
237 | 0 | &src_y[(y_pos << y_uv_vscale) * src_y_stride + |
238 | 0 | (x_pos << y_uv_hscale)], |
239 | 0 | thr, neg_thr, src_loc, edge_clf); |
240 | 13.8k | } else { |
241 | 13.8k | src_cls[0] = 0; |
242 | 13.8k | src_cls[1] = 0; |
243 | 13.8k | } |
244 | 13.8k | const int band_num = isSingleBand |
245 | 13.8k | ? 0 |
246 | 13.8k | : src_y[(y_pos << y_uv_vscale) * src_y_stride + |
247 | 0 | (x_pos << y_uv_hscale)] >> |
248 | 0 | shift_bits; |
249 | 13.8k | const int lut_idx_ext = (band_num << 4) + (src_cls[0] << 2) + src_cls[1]; |
250 | 13.8k | const int offset_val = offset_buf[lut_idx_ext]; |
251 | 13.8k | dst_yuv[y_pos * dst_stride + x_pos] = |
252 | 13.8k | clamp(offset_val + dst_yuv[y_pos * dst_stride + x_pos], 0, max_val); |
253 | 13.8k | } |
254 | 864 | } |
255 | 34 | } |
256 | | |
257 | | // If there is at-least 1 segment is lossless in a frame, we have |
258 | | // to do 4x4 processing, because minimum lossless block can be 4x4 |
259 | | // size. Although, regardless the value of |
260 | | // cm->features.has_lossless_segment, we can always do 4x4 |
261 | | // processing, however, for software optimization purpose we have |
262 | | // used full block processing for whole lossy frame. |
263 | | void ccso_filter_block_hbd_wo_buf_4x4_c( |
264 | | AV2_COMMON *cm, const uint16_t *src_y, uint16_t *dst_yuv, |
265 | | int tile_col_start, int tile_row_start, const int x, const int y, |
266 | | const int pic_width, const int pic_height, int *src_cls, |
267 | | const int8_t *offset_buf, const int src_y_stride, const int dst_stride, |
268 | | const int y_uv_hscale, const int y_uv_vscale, const int thr, |
269 | | const int neg_thr, const int *src_loc, const int max_val, |
270 | | const int blk_size_x, const int blk_size_y, const bool isSingleBand, |
271 | | const uint8_t shift_bits, const int edge_clf, const uint8_t ccso_bo_only, |
272 | 0 | int plane) { |
273 | 0 | const CommonModeInfoParams *const mi_params = &cm->mi_params; |
274 | 0 | const int y_end = AVMMIN(pic_height - y, blk_size_y); |
275 | 0 | const int x_end = AVMMIN(pic_width - x, blk_size_x); |
276 | 0 | int min_b_size_x = (1 << MI_SIZE_LOG2) >> y_uv_hscale; |
277 | 0 | int min_b_size_y = (1 << MI_SIZE_LOG2) >> y_uv_vscale; |
278 | |
|
279 | 0 | for (int y_start = 0; y_start < y_end; y_start += min_b_size_y) { |
280 | 0 | const int y_pos = y_start; |
281 | 0 | for (int x_start = 0; x_start < x_end; x_start += min_b_size_x) { |
282 | 0 | const int x_pos = x + x_start; |
283 | 0 | const int this_mi_row = |
284 | 0 | ((tile_row_start + y_pos + y) << y_uv_vscale) >> MI_SIZE_LOG2; |
285 | 0 | const int this_mi_col = |
286 | 0 | ((tile_col_start + x_pos) << y_uv_hscale) >> MI_SIZE_LOG2; |
287 | |
|
288 | 0 | MB_MODE_INFO **this_mbmi_ptr = mi_params->mi_grid_base + |
289 | 0 | this_mi_row * mi_params->mi_stride + |
290 | 0 | this_mi_col; |
291 | 0 | MB_MODE_INFO **this_mbmi = |
292 | 0 | get_mi_location_from_collocated_mi(cm, this_mbmi_ptr, plane); |
293 | |
|
294 | 0 | const int is_lossless = |
295 | 0 | cm->features.lossless_segment[this_mbmi[0]->segment_id]; |
296 | 0 | if (!is_lossless) { |
297 | 0 | int j_max = AVMMIN(x_pos + min_b_size_x, x + x_start + x_end); |
298 | 0 | int i_max = AVMMIN(y_pos + min_b_size_y, y_end); |
299 | 0 | for (int i_pos_4x4 = y_pos; i_pos_4x4 < i_max; i_pos_4x4++) { |
300 | 0 | for (int j_pos_4x4 = x_pos; j_pos_4x4 < j_max; j_pos_4x4++) { |
301 | 0 | if (!ccso_bo_only) { |
302 | 0 | cal_filter_support( |
303 | 0 | src_cls, |
304 | 0 | &src_y[(i_pos_4x4 << y_uv_vscale) * src_y_stride + |
305 | 0 | (j_pos_4x4 << y_uv_hscale)], |
306 | 0 | thr, neg_thr, src_loc, edge_clf); |
307 | 0 | } else { |
308 | 0 | src_cls[0] = 0; |
309 | 0 | src_cls[1] = 0; |
310 | 0 | } |
311 | 0 | const int band_num = |
312 | 0 | isSingleBand ? 0 |
313 | 0 | : src_y[(i_pos_4x4 << y_uv_vscale) * src_y_stride + |
314 | 0 | (j_pos_4x4 << y_uv_hscale)] >> |
315 | 0 | shift_bits; |
316 | 0 | const int lut_idx_ext = |
317 | 0 | (band_num << 4) + (src_cls[0] << 2) + src_cls[1]; |
318 | 0 | const int offset_val = offset_buf[lut_idx_ext]; |
319 | 0 | dst_yuv[i_pos_4x4 * dst_stride + j_pos_4x4] = |
320 | 0 | clamp(offset_val + dst_yuv[i_pos_4x4 * dst_stride + j_pos_4x4], |
321 | 0 | 0, max_val); |
322 | 0 | } |
323 | 0 | } |
324 | 0 | } |
325 | 0 | } |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | // Apply CCSO for each process block row |
330 | | void av2_apply_ccso_filter_for_row(AV2_COMMON *cm, MACROBLOCKD *xd, |
331 | | const uint16_t *src_y, uint16_t *dst_yuv, |
332 | | int *src_loc, int *src_cls, int blk_row, |
333 | | int thr, int blk_size, int blk_size_proc, |
334 | | int blk_log2_x, int blk_log2_y, |
335 | | int unit_log2_x, int unit_log2_y, |
336 | 0 | int plane) { |
337 | 0 | const CommonModeInfoParams *const mi_params = &cm->mi_params; |
338 | 0 | const int src_y_stride = xd->plane[0].dst.width + (CCSO_PADDING_SIZE << 1); |
339 | 0 | const uint8_t max_band_log2 = cm->ccso_info.max_band_log2[plane]; |
340 | 0 | const int dst_stride = xd->plane[plane].dst.stride; |
341 | 0 | const int pic_height = xd->plane[plane].dst.height; |
342 | 0 | const int pic_width = xd->plane[plane].dst.width; |
343 | 0 | const int neg_thr = thr * -1; |
344 | 0 | const bool is_single_band = !max_band_log2; |
345 | 0 | const uint8_t shift_bits = cm->seq_params.bit_depth - max_band_log2; |
346 | 0 | const int max_val = (1 << cm->seq_params.bit_depth) - 1; |
347 | 0 | const int edge_clf = cm->ccso_info.edge_clf[plane]; |
348 | 0 | const int y_uv_hscale = xd->plane[plane].subsampling_x; |
349 | 0 | const int y_uv_vscale = xd->plane[plane].subsampling_y; |
350 | |
|
351 | 0 | for (int blk_col = 0; blk_col < pic_width; blk_col += blk_size_proc) { |
352 | 0 | const int ccso_blk_idx = |
353 | 0 | (blk_size >> MI_SIZE_LOG2) * (blk_row >> blk_log2_y) * |
354 | 0 | mi_params->mi_stride + |
355 | 0 | (blk_size >> MI_SIZE_LOG2) * (blk_col >> blk_log2_x); |
356 | 0 | const bool use_ccso = |
357 | 0 | (plane == 0) ? mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_y |
358 | 0 | : (plane == 1) ? mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_u |
359 | 0 | : mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_v; |
360 | 0 | if (!use_ccso) continue; |
361 | | |
362 | | // FPU level skip |
363 | 0 | const int x_mbmi = (blk_col >> unit_log2_x) << unit_log2_x; |
364 | 0 | const int y_mbmi = (blk_row >> unit_log2_y) << unit_log2_y; |
365 | 0 | const int mbmi_idx = |
366 | 0 | get_mi_grid_idx(mi_params, y_mbmi >> (MI_SIZE_LOG2 - y_uv_vscale), |
367 | 0 | x_mbmi >> (MI_SIZE_LOG2 - y_uv_hscale)); |
368 | 0 | const int use_ccso_local = |
369 | 0 | mi_params->mi_grid_base[mbmi_idx]->local_ccso_blk_flag; |
370 | 0 | if (!use_ccso_local) continue; |
371 | | |
372 | 0 | if (cm->bru.enabled && |
373 | 0 | mi_params->mi_grid_base[mbmi_idx]->sb_active_mode != BRU_ACTIVE_SB) { |
374 | 0 | avm_internal_error( |
375 | 0 | &cm->error, AVM_CODEC_ERROR, |
376 | 0 | "Invalid BRU activity in CCSO: only active SB can be filtered"); |
377 | 0 | return; |
378 | 0 | } |
379 | 0 | if (cm->bridge_frame_info.is_bridge_frame) { |
380 | 0 | avm_internal_error( |
381 | 0 | &cm->error, AVM_CODEC_ERROR, |
382 | 0 | "Invalid Bridge frame activity in CCSO: can not be filtered"); |
383 | 0 | return; |
384 | 0 | } |
385 | | |
386 | 0 | if (cm->features.has_lossless_segment) { |
387 | 0 | ccso_filter_block_hbd_wo_buf_4x4_c( |
388 | 0 | cm, src_y, dst_yuv, 0, 0, blk_col, blk_row, pic_width, pic_height, |
389 | 0 | src_cls, cm->ccso_info.filter_offset[plane], src_y_stride, dst_stride, |
390 | 0 | y_uv_hscale, y_uv_vscale, thr, neg_thr, src_loc, max_val, |
391 | 0 | blk_size_proc, blk_size_proc, is_single_band, shift_bits, edge_clf, |
392 | 0 | cm->ccso_info.ccso_bo_only[plane], plane); |
393 | 0 | } else { |
394 | 0 | if (cm->ccso_info.ccso_bo_only[plane]) { |
395 | 0 | ccso_filter_block_hbd_wo_buf_bo_only( |
396 | 0 | src_y, dst_yuv, blk_col, blk_row, pic_width, pic_height, |
397 | 0 | cm->ccso_info.filter_offset[plane], src_y_stride, dst_stride, |
398 | 0 | y_uv_hscale, y_uv_vscale, max_val, blk_size_proc, blk_size_proc, |
399 | 0 | is_single_band, shift_bits); |
400 | 0 | } else { |
401 | 0 | ccso_filter_block_hbd_wo_buf( |
402 | 0 | src_y, dst_yuv, blk_col, blk_row, pic_width, pic_height, src_cls, |
403 | 0 | cm->ccso_info.filter_offset[plane], src_y_stride, dst_stride, |
404 | 0 | y_uv_hscale, y_uv_vscale, thr, neg_thr, src_loc, max_val, |
405 | 0 | blk_size_proc, blk_size_proc, is_single_band, shift_bits, edge_clf, |
406 | 0 | 0); |
407 | 0 | } |
408 | 0 | } |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | | /* Apply CCSO on luma or chroma component when single or multiple bands are |
413 | | * applied */ |
414 | | void apply_ccso_filter(AV2_COMMON *cm, MACROBLOCKD *xd, int plane, |
415 | | const uint16_t *src_y, uint16_t *dst_yuv, int dst_stride, |
416 | | int proc_unit_log2, uint16_t thr, uint8_t filter_sup, |
417 | 17 | uint8_t max_band_log2, int edge_clf) { |
418 | 17 | const int ccso_ext_stride = xd->plane[0].dst.width + (CCSO_PADDING_SIZE << 1); |
419 | 17 | const int pic_height = xd->plane[plane].dst.height; |
420 | 17 | int src_cls[2]; |
421 | 17 | int src_loc[2]; |
422 | 17 | const int y_uv_hscale = xd->plane[plane].subsampling_x; |
423 | 17 | const int y_uv_vscale = xd->plane[plane].subsampling_y; |
424 | 17 | derive_ccso_sample_pos(src_loc, ccso_ext_stride, filter_sup); |
425 | 17 | const int ccso_blk_size = get_ccso_unit_size_log2_adaptive_tile( |
426 | 17 | cm, cm->mib_size_log2 + MI_SIZE_LOG2, CCSO_BLK_SIZE); |
427 | 17 | const int blk_log2 = ccso_blk_size; |
428 | 17 | const int blk_size = 1 << blk_log2; |
429 | 17 | const int blk_log2_x = blk_log2 - y_uv_hscale; |
430 | 17 | const int blk_log2_y = blk_log2 - y_uv_vscale; |
431 | 17 | src_y += CCSO_PADDING_SIZE * ccso_ext_stride + CCSO_PADDING_SIZE; |
432 | 17 | const int unit_log2_x = AVMMIN(proc_unit_log2, blk_log2_x); |
433 | 17 | const int unit_log2_y = AVMMIN(proc_unit_log2, blk_log2_y); |
434 | 17 | const CommonModeInfoParams *const mi_params = &cm->mi_params; |
435 | 17 | const uint8_t shift_bits = cm->seq_params.bit_depth - max_band_log2; |
436 | 17 | const bool is_single_band = !max_band_log2; |
437 | 17 | const int max_val = (1 << cm->seq_params.bit_depth) - 1; |
438 | 17 | const int neg_thr = thr * -1; |
439 | 17 | const int unit_size_x = 1 << unit_log2_x; |
440 | 17 | const int unit_size_y = 1 << unit_log2_y; |
441 | 17 | const int blk_size_x = 1 << blk_log2_x; |
442 | 17 | const int blk_size_y = 1 << blk_log2_y; |
443 | 17 | if (cm->seq_params.disable_loopfilters_across_tiles) { |
444 | 17 | int tile_rows = cm->tiles.rows; |
445 | 17 | int tile_cols = cm->tiles.cols; |
446 | 17 | TileInfo tile_info_y; |
447 | 17 | AV2PixelRect tile_rect_y; |
448 | 34 | for (int tile_row = 0; tile_row < tile_rows; ++tile_row) { |
449 | 17 | int tile_height = 0; |
450 | 34 | for (int tile_col = 0; tile_col < tile_cols; ++tile_col) { |
451 | 17 | av2_tile_init(&tile_info_y, cm, tile_row, tile_col); |
452 | 17 | tile_rect_y = av2_get_tile_rect_ccso(&tile_info_y, cm, xd, 0); |
453 | | |
454 | 17 | int tile_row_start = tile_rect_y.top; |
455 | 17 | int tile_col_start = tile_rect_y.left; |
456 | 17 | int tile_row_end = tile_rect_y.bottom; |
457 | 17 | int tile_col_end = tile_rect_y.right; |
458 | 17 | int tile_width = tile_col_end - tile_col_start; |
459 | 17 | tile_height = tile_row_end - tile_row_start; |
460 | 17 | const int ccso_ext_tile_stride = tile_width + (CCSO_PADDING_SIZE << 1); |
461 | 17 | derive_ccso_sample_pos(src_loc, ccso_ext_tile_stride, filter_sup); |
462 | | |
463 | 17 | uint16_t *ext_rec_tile_y = NULL; |
464 | | // const uint16_t *rec_y = cm->cur_frame->buf.y_buffer; |
465 | 17 | ext_rec_tile_y = avm_malloc(sizeof(*ext_rec_tile_y) * |
466 | 17 | (tile_height + (CCSO_PADDING_SIZE << 1)) * |
467 | 17 | (tile_width + (CCSO_PADDING_SIZE << 1))); |
468 | 7.32k | for (int r = 0; r < tile_height; ++r) { |
469 | 240k | for (int c = 0; c < tile_width; ++c) { |
470 | 233k | ext_rec_tile_y[(r + CCSO_PADDING_SIZE) * ccso_ext_tile_stride + c + |
471 | 233k | CCSO_PADDING_SIZE] = |
472 | 233k | src_y[(r + tile_row_start) * ccso_ext_stride + |
473 | 233k | (c + tile_col_start)]; |
474 | 233k | } |
475 | 7.30k | } |
476 | 17 | extend_ccso_tile_border(tile_height, tile_width, ccso_ext_tile_stride, |
477 | 17 | ext_rec_tile_y, CCSO_PADDING_SIZE); |
478 | | |
479 | 17 | uint16_t *src_tile_y = ext_rec_tile_y; |
480 | 17 | src_tile_y += |
481 | 17 | CCSO_PADDING_SIZE * ccso_ext_tile_stride + CCSO_PADDING_SIZE; |
482 | 17 | if (plane != 0) { |
483 | 15 | TileInfo tile_info_uv; |
484 | 15 | AV2PixelRect tile_rect_uv; |
485 | 15 | av2_tile_init(&tile_info_uv, cm, tile_row, tile_col); |
486 | 15 | tile_rect_uv = av2_get_tile_rect_ccso(&tile_info_uv, cm, xd, 1); |
487 | 15 | tile_row_start = tile_rect_uv.top; |
488 | 15 | tile_col_start = tile_rect_uv.left; |
489 | 15 | tile_row_end = tile_rect_uv.bottom; |
490 | 15 | tile_col_end = tile_rect_uv.right; |
491 | 15 | tile_width = tile_col_end - tile_col_start; |
492 | 15 | tile_height = tile_row_end - tile_row_start; |
493 | 15 | } |
494 | 17 | uint16_t *dst_tile_yuv = dst_yuv + tile_col_start; |
495 | 146 | for (int frame_pxl_y = tile_row_start; frame_pxl_y < tile_row_end; |
496 | 129 | frame_pxl_y += blk_size_y) { |
497 | 258 | for (int frame_pxl_x = tile_col_start; frame_pxl_x < tile_col_end; |
498 | 129 | frame_pxl_x += blk_size_x) { |
499 | | // int x = frame_pxl_x; |
500 | | // int y = frame_pxl_y; |
501 | 129 | int tile_pxl_x = frame_pxl_x - tile_col_start; |
502 | 129 | int tile_pxl_y = frame_pxl_y - tile_row_start; |
503 | | |
504 | 129 | const int ccso_blk_idx = |
505 | 129 | (blk_size >> MI_SIZE_LOG2) * (frame_pxl_y >> blk_log2_y) * |
506 | 129 | mi_params->mi_stride + |
507 | 129 | (blk_size >> MI_SIZE_LOG2) * (frame_pxl_x >> blk_log2_x); |
508 | 129 | const bool use_ccso = |
509 | 129 | (plane == 0) ? mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_y |
510 | 129 | : (plane == 1) |
511 | 127 | ? mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_u |
512 | 127 | : mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_v; |
513 | 129 | if (!use_ccso) continue; |
514 | 62 | const uint16_t *src_unit_y = src_tile_y; |
515 | 62 | uint16_t *dst_unit_yuv = dst_tile_yuv; |
516 | 62 | const int y_end = AVMMIN(tile_row_end - frame_pxl_y, blk_size_y); |
517 | 62 | const int x_end = AVMMIN(tile_col_end - frame_pxl_x, blk_size_x); |
518 | 124 | for (int unit_y = 0; unit_y < y_end; unit_y += unit_size_y) { |
519 | 124 | for (int unit_x = 0; unit_x < x_end; unit_x += unit_size_x) { |
520 | | // FPU level skip |
521 | 62 | const int mbmi_idx = get_mi_grid_idx( |
522 | 62 | mi_params, |
523 | 62 | (frame_pxl_y + unit_y) >> (MI_SIZE_LOG2 - y_uv_vscale), |
524 | 62 | (frame_pxl_x + unit_x) >> (MI_SIZE_LOG2 - y_uv_hscale)); |
525 | 62 | const int use_ccso_local = |
526 | 62 | mi_params->mi_grid_base[mbmi_idx]->local_ccso_blk_flag; |
527 | 62 | if (!use_ccso_local) { |
528 | 0 | continue; |
529 | 0 | } |
530 | 62 | if (cm->bru.enabled && |
531 | 0 | mi_params->mi_grid_base[mbmi_idx]->sb_active_mode != |
532 | 0 | BRU_ACTIVE_SB) { |
533 | 0 | avm_internal_error(&cm->error, AVM_CODEC_ERROR, |
534 | 0 | "Invalid BRU activity in CCSO: only " |
535 | 0 | "active SB can be filtered"); |
536 | 0 | return; |
537 | 0 | } |
538 | 62 | if (cm->features.has_lossless_segment) { |
539 | 0 | ccso_filter_block_hbd_wo_buf_4x4_c( |
540 | 0 | cm, src_unit_y, dst_unit_yuv, tile_col_start, |
541 | 0 | tile_row_start, tile_pxl_x + unit_x, tile_pxl_y + unit_y, |
542 | 0 | tile_width, tile_height, src_cls, |
543 | 0 | cm->ccso_info.filter_offset[plane], ccso_ext_tile_stride, |
544 | 0 | dst_stride, y_uv_hscale, y_uv_vscale, thr, neg_thr, |
545 | 0 | src_loc, max_val, unit_size_x, unit_size_y, |
546 | 0 | is_single_band, shift_bits, edge_clf, |
547 | 0 | cm->ccso_info.ccso_bo_only[plane], plane); |
548 | 62 | } else { |
549 | 62 | if (cm->ccso_info.ccso_bo_only[plane]) { |
550 | 34 | ccso_filter_block_hbd_wo_buf_c( |
551 | 34 | src_unit_y, dst_unit_yuv, tile_pxl_x + unit_x, |
552 | 34 | tile_pxl_y + unit_y, tile_width, tile_height, src_cls, |
553 | 34 | cm->ccso_info.filter_offset[plane], |
554 | 34 | ccso_ext_tile_stride, dst_stride, y_uv_hscale, |
555 | 34 | y_uv_vscale, thr, neg_thr, src_loc, max_val, |
556 | 34 | unit_size_x, unit_size_y, is_single_band, shift_bits, |
557 | 34 | edge_clf, cm->ccso_info.ccso_bo_only[plane]); |
558 | 34 | } else { |
559 | 28 | ccso_filter_block_hbd_wo_buf( |
560 | 28 | src_unit_y, dst_unit_yuv, tile_pxl_x + unit_x, |
561 | 28 | tile_pxl_y + unit_y, tile_width, tile_height, src_cls, |
562 | 28 | cm->ccso_info.filter_offset[plane], |
563 | 28 | ccso_ext_tile_stride, dst_stride, y_uv_hscale, |
564 | 28 | y_uv_vscale, thr, neg_thr, src_loc, max_val, |
565 | 28 | unit_size_x, unit_size_y, is_single_band, shift_bits, |
566 | 28 | edge_clf, 0); |
567 | 28 | } |
568 | 62 | } |
569 | 62 | } |
570 | 62 | dst_unit_yuv += (dst_stride << unit_log2_y); |
571 | 62 | src_unit_y += |
572 | 62 | (ccso_ext_tile_stride << (unit_log2_y + y_uv_vscale)); |
573 | 62 | } |
574 | 62 | } |
575 | 129 | dst_tile_yuv += (dst_stride << blk_log2_y); |
576 | 129 | src_tile_y += (ccso_ext_tile_stride << (blk_log2_y + y_uv_vscale)); |
577 | 129 | } |
578 | 17 | avm_free(ext_rec_tile_y); |
579 | 17 | } |
580 | 17 | dst_yuv += (dst_stride * tile_height); |
581 | | // src_y += (ccso_ext_stride * (cm->tiles.height << (MI_SIZE_LOG2))); |
582 | 17 | } |
583 | 17 | return; |
584 | 17 | } |
585 | 0 | const int blk_log2_proc = CCSO_PROC_BLK_LOG2; |
586 | 0 | const int blk_size_proc = 1 << blk_log2_proc; |
587 | 0 | for (int blk_row = 0; blk_row < pic_height; blk_row += blk_size_proc) { |
588 | 0 | av2_apply_ccso_filter_for_row( |
589 | 0 | cm, xd, src_y, dst_yuv, src_loc, src_cls, blk_row, thr, blk_size, |
590 | 0 | blk_size_proc, blk_log2_x, blk_log2_y, unit_log2_x, unit_log2_y, plane); |
591 | 0 | dst_yuv += dst_stride << blk_log2_proc; |
592 | 0 | src_y += ccso_ext_stride << (blk_log2_proc + y_uv_vscale); |
593 | 0 | } |
594 | 0 | } |
595 | | |
596 | | /* Apply CCSO for one frame */ |
597 | | void ccso_frame(YV12_BUFFER_CONFIG *frame, AV2_COMMON *cm, MACROBLOCKD *xd, |
598 | 9 | uint16_t *ext_rec_y) { |
599 | 9 | const int num_planes = av2_num_planes(cm); |
600 | 9 | av2_setup_dst_planes(xd->plane, frame, 0, 0, 0, num_planes, NULL); |
601 | | |
602 | 36 | for (int plane = 0; plane < num_planes; plane++) { |
603 | 27 | const int dst_stride = xd->plane[plane].dst.stride; |
604 | 27 | const uint16_t quant_step_size = quant_sz[cm->ccso_info.scale_idx[plane]] |
605 | 27 | [cm->ccso_info.quant_idx[plane]]; |
606 | 27 | if (cm->ccso_info.ccso_enable[plane]) { |
607 | 17 | apply_ccso_filter( |
608 | 17 | cm, xd, plane, ext_rec_y, &(xd->plane[plane].dst.buf)[0], dst_stride, |
609 | 17 | cm->mib_size_log2 - |
610 | 17 | AVMMAX(xd->plane[plane].subsampling_x, |
611 | 17 | xd->plane[plane].subsampling_y) + |
612 | 17 | MI_SIZE_LOG2, |
613 | 17 | quant_step_size, cm->ccso_info.ext_filter_support[plane], |
614 | 17 | cm->ccso_info.max_band_log2[plane], cm->ccso_info.edge_clf[plane]); |
615 | 17 | } |
616 | 27 | } |
617 | 9 | } |
618 | | |
619 | | // This function is to copy ccso filter parameters between frames when |
620 | | // ccso_reuse is true. |
621 | | void av2_copy_ccso_filters(CcsoInfo *to, CcsoInfo *from, int plane, |
622 | 2.05k | bool frame_level, bool block_level, int sb_count) { |
623 | 2.05k | if (frame_level) { |
624 | 2.05k | memcpy(to->filter_offset[plane], from->filter_offset[plane], |
625 | 2.05k | sizeof(to->filter_offset[plane])); |
626 | 2.05k | to->quant_idx[plane] = from->quant_idx[plane]; |
627 | 2.05k | to->ext_filter_support[plane] = from->ext_filter_support[plane]; |
628 | 2.05k | to->edge_clf[plane] = from->edge_clf[plane]; |
629 | 2.05k | to->ccso_bo_only[plane] = from->ccso_bo_only[plane]; |
630 | 2.05k | to->max_band_log2[plane] = from->max_band_log2[plane]; |
631 | 2.05k | to->scale_idx[plane] = from->scale_idx[plane]; |
632 | 2.05k | } |
633 | | |
634 | 2.05k | if (block_level) { |
635 | 0 | if (to->sb_filter_control[plane]) { |
636 | 0 | memcpy(to->sb_filter_control[plane], from->sb_filter_control[plane], |
637 | 0 | sizeof(*from->sb_filter_control[plane]) * sb_count); |
638 | 0 | } |
639 | 0 | } |
640 | | |
641 | 2.05k | to->ccso_enable[plane] = from->ccso_enable[plane]; |
642 | 2.05k | } |