FFmpeg
Loading...
Searching...
No Matches
hevc_pred.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026 Jun Zhao <barryjzhao@tencent.com>
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include <string.h>
22#include "checkasm.h"
26
27static const uint32_t pixel_mask[3] = { 0xffffffff, 0x01ff01ff, 0x03ff03ff };
28
29#define SIZEOF_PIXEL ((bit_depth + 7) / 8)
30#define PRED_SIZE 128 /* Increased to 4 * MAX_TB_SIZE to accommodate C code reads */
31
32#define randomize_ref_buffers() \
33 do { \
34 uint32_t mask = pixel_mask[bit_depth - 8]; \
35 for (int i = -4; i < PRED_SIZE; i += 4) { \
36 uint32_t r = rnd() & mask; \
37 AV_WN32A(top + i, r); \
38 AV_WN32A(left + i, r); \
39 } \
40 } while (0)
41
43 uint8_t *top, uint8_t *left, int bit_depth)
44{
45 const char *const block_name[] = { "4x4", "8x8", "16x16", "32x32" };
46 const int block_size[] = { 4, 8, 16, 32 };
47 int log2_size;
48
49 PIXEL_RECT(buf0, 64, 64);
50 PIXEL_RECT(buf1, 64, 64);
51
52 declare_func(void, uint8_t *src, const uint8_t *top,
53 const uint8_t *left, ptrdiff_t stride,
54 int log2_size, int c_idx);
55
56 /* Test all 4 sizes: 4x4, 8x8, 16x16, 32x32 */
57 for (log2_size = 2; log2_size <= 5; log2_size++) {
58 int size = block_size[log2_size - 2];
59
60 if (check_func(h->pred_dc, "hevc_pred_dc_%s_%d",
61 block_name[log2_size - 2], bit_depth)) {
62 /* Test with c_idx=0 (luma, with edge smoothing for size < 32) */
64 CLEAR_PIXEL_RECT(buf0);
65 CLEAR_PIXEL_RECT(buf1);
66 call_ref(buf0, top, left, buf0_stride, log2_size, 0);
67 call_new(buf1, top, left, buf1_stride, log2_size, 0);
68 checkasm_check_pixel_padded(buf0, buf0_stride,
69 buf1, buf1_stride,
70 size, size, "dst");
71
72 /* Test with c_idx=1 (chroma, no edge smoothing) */
74 CLEAR_PIXEL_RECT(buf0);
75 CLEAR_PIXEL_RECT(buf1);
76 call_ref(buf0, top, left, buf0_stride, log2_size, 1);
77 call_new(buf1, top, left, buf1_stride, log2_size, 1);
78 checkasm_check_pixel_padded(buf0, buf0_stride,
79 buf1, buf1_stride,
80 size, size, "dst");
81
82 bench_new(buf1, top, left, buf1_stride, log2_size, 0);
83 }
84 }
85}
86
88 uint8_t *top, uint8_t *left, int bit_depth)
89{
90 const char *const block_name[] = { "4x4", "8x8", "16x16", "32x32" };
91 const int block_size[] = { 4, 8, 16, 32 };
92 int i;
93
94 PIXEL_RECT(buf0, 64, 64);
95 PIXEL_RECT(buf1, 64, 64);
96
97 declare_func(void, uint8_t *src, const uint8_t *top,
98 const uint8_t *left, ptrdiff_t stride);
99
100 /* Test all 4 sizes: 4x4, 8x8, 16x16, 32x32 */
101 for (i = 0; i < 4; i++) {
102 int size = block_size[i];
103
104 if (check_func(h->pred_planar[i], "hevc_pred_planar_%s_%d",
105 block_name[i], bit_depth)) {
107 CLEAR_PIXEL_RECT(buf0);
108 CLEAR_PIXEL_RECT(buf1);
109 call_ref(buf0, top, left, buf0_stride);
110 call_new(buf1, top, left, buf1_stride);
111 checkasm_check_pixel_padded(buf0, buf0_stride,
112 buf1, buf1_stride,
113 size, size, "dst");
114
115 bench_new(buf1, top, left, buf1_stride);
116 }
117 }
118}
119
120/*
121 * Angular prediction modes are divided into categories:
122 *
123 * Mode 10: Horizontal pure copy (H pure)
124 * Mode 26: Vertical pure copy (V pure)
125 * Modes 2-9: Horizontal positive angle (H pos) - uses left reference
126 * Modes 11-17: Horizontal negative angle (H neg) - needs reference extension
127 * Modes 18-25: Vertical negative angle (V neg) - needs reference extension
128 * Modes 27-34: Vertical positive angle (V pos) - uses top reference
129 *
130 * Each category has 4 NEON functions for 4x4, 8x8, 16x16, 32x32 sizes.
131 */
133 uint8_t *top, uint8_t *left, int bit_depth)
134{
135 const char *const block_name[] = { "4x4", "8x8", "16x16", "32x32" };
136 const int block_size[] = { 4, 8, 16, 32 };
137 int i, mode;
138
139 PIXEL_RECT(buf0, 64, 64);
140 PIXEL_RECT(buf1, 64, 64);
141
142 declare_func(void, uint8_t *src, const uint8_t *top,
143 const uint8_t *left, ptrdiff_t stride, int c_idx, int mode);
144
145 /* Test all 4 sizes */
146 for (i = 0; i < 4; i++) {
147 int size = block_size[i];
148
149 /* Test all 33 angular modes (2-34) */
150 for (mode = 2; mode <= 34; mode++) {
151 const char *mode_category;
152
153 /* Determine mode category for descriptive test name */
154 if (mode == 10)
155 mode_category = "Hpure";
156 else if (mode == 26)
157 mode_category = "Vpure";
158 else if (mode >= 2 && mode <= 9)
159 mode_category = "Hpos";
160 else if (mode >= 11 && mode <= 17)
161 mode_category = "Hneg";
162 else if (mode >= 18 && mode <= 25)
163 mode_category = "Vneg";
164 else /* mode >= 27 && mode <= 34 */
165 mode_category = "Vpos";
166
167 if (check_func(h->pred_angular[i],
168 "hevc_pred_angular_%s_%s_mode%d_%d",
169 block_name[i], mode_category, mode, bit_depth)) {
170 /* Test with c_idx=0 (luma) */
172 CLEAR_PIXEL_RECT(buf0);
173 CLEAR_PIXEL_RECT(buf1);
174 call_ref(buf0, top, left, buf0_stride, 0, mode);
175 call_new(buf1, top, left, buf1_stride, 0, mode);
176 checkasm_check_pixel_padded(buf0, buf0_stride,
177 buf1, buf1_stride,
178 size, size, "dst");
179
180 /* Test with c_idx=1 (chroma) for modes 10/26 to cover
181 * the edge filtering skip path */
182 if (mode == 10 || mode == 26) {
184 CLEAR_PIXEL_RECT(buf0);
185 CLEAR_PIXEL_RECT(buf1);
186 call_ref(buf0, top, left, buf0_stride, 1, mode);
187 call_new(buf1, top, left, buf1_stride, 1, mode);
188 checkasm_check_pixel_padded(buf0, buf0_stride,
189 buf1, buf1_stride,
190 size, size, "dst");
191 }
192
193 bench_new(buf1, top, left, buf1_stride, 0, mode);
194 }
195 }
196 }
197}
198
200 uint8_t *top, uint8_t *left, int bit_depth)
201{
202 const char *const block_name[] = { "8x8", "16x16", "32x32" };
203 const int block_size[] = { 8, 16, 32 };
204 int i;
205
206 /* 3-tap filter: out[i] = (in[i+1] + 2*in[i] + in[i-1] + 2) >> 2
207 * Filters 2*size-1 samples (indices 0..2*size-2) plus corner [-1].
208 * Output: filtered_left[-1..2*size-1] and filtered_top[-1..2*size-1] */
209 declare_func(void, uint8_t *filtered_left, uint8_t *filtered_top,
210 const uint8_t *left, const uint8_t *top, int size);
211
212 for (i = 0; i < 3; i++) {
213 int size = block_size[i];
214 int n = 2 * size;
215
216 if (check_func(h->ref_filter_3tap[i],
217 "hevc_ref_filter_3tap_%s_%d",
218 block_name[i], bit_depth)) {
219 /* Allocate output buffers with space for [-1] indexing.
220 * Need n+1 elements: indices [-1..n-1] = n+1 pixels.
221 * Use (n+1)*SIZEOF_PIXEL bytes starting at offset SIZEOF_PIXEL. */
222 LOCAL_ALIGNED_32(uint8_t, fl_ref_buf, [PRED_SIZE + 16]);
223 LOCAL_ALIGNED_32(uint8_t, fl_new_buf, [PRED_SIZE + 16]);
224 LOCAL_ALIGNED_32(uint8_t, ft_ref_buf, [PRED_SIZE + 16]);
225 LOCAL_ALIGNED_32(uint8_t, ft_new_buf, [PRED_SIZE + 16]);
226 uint8_t *fl_ref = fl_ref_buf + 8;
227 uint8_t *fl_new = fl_new_buf + 8;
228 uint8_t *ft_ref = ft_ref_buf + 8;
229 uint8_t *ft_new = ft_new_buf + 8;
230
232 /* Clear output buffers so comparison is clean */
233 memset(fl_ref_buf, 0, PRED_SIZE + 16);
234 memset(fl_new_buf, 0, PRED_SIZE + 16);
235 memset(ft_ref_buf, 0, PRED_SIZE + 16);
236 memset(ft_new_buf, 0, PRED_SIZE + 16);
237
238 call_ref(fl_ref, ft_ref, left, top, size);
239 call_new(fl_new, ft_new, left, top, size);
240
241 /* Compare filtered_left[-1..2*size-1] and filtered_top[-1..2*size-1] */
242 if (memcmp(fl_ref - SIZEOF_PIXEL, fl_new - SIZEOF_PIXEL,
243 (n + 1) * SIZEOF_PIXEL))
244 fail();
245 if (memcmp(ft_ref - SIZEOF_PIXEL, ft_new - SIZEOF_PIXEL,
246 (n + 1) * SIZEOF_PIXEL))
247 fail();
248
249 bench_new(fl_new, ft_new, left, top, size);
250 }
251 }
252}
253
255 uint8_t *top, uint8_t *left,
256 int bit_depth)
257{
258 /* Strong intra smoothing: only 32x32 luma.
259 * Interpolates top into filtered_top[0..62], sets filtered_top[-1] and [63].
260 * Modifies left[0..62] in-place. */
261 declare_func(void, uint8_t *filtered_top, uint8_t *left,
262 const uint8_t *top);
263
264 if (check_func(h->ref_filter_strong,
265 "hevc_ref_filter_strong_%d", bit_depth)) {
266 LOCAL_ALIGNED_32(uint8_t, ft_ref_buf, [PRED_SIZE + 16]);
267 LOCAL_ALIGNED_32(uint8_t, ft_new_buf, [PRED_SIZE + 16]);
268 LOCAL_ALIGNED_32(uint8_t, left_ref_buf, [PRED_SIZE + 16]);
269 LOCAL_ALIGNED_32(uint8_t, left_new_buf, [PRED_SIZE + 16]);
270 uint8_t *ft_ref = ft_ref_buf + 8;
271 uint8_t *ft_new = ft_new_buf + 8;
272 uint8_t *left_ref = left_ref_buf + 8;
273 uint8_t *left_new = left_new_buf + 8;
274
276 memset(ft_ref_buf, 0, PRED_SIZE + 16);
277 memset(ft_new_buf, 0, PRED_SIZE + 16);
278
279 /* Copy left so both ref and new start with the same input
280 * (left is modified in-place) */
281 memcpy(left_ref_buf, left - 8, PRED_SIZE + 16);
282 memcpy(left_new_buf, left - 8, PRED_SIZE + 16);
283
284 call_ref(ft_ref, left_ref, top);
285 call_new(ft_new, left_new, top);
286
287 /* Compare filtered_top[-1..63] = 65 pixels */
288 if (memcmp(ft_ref - SIZEOF_PIXEL, ft_new - SIZEOF_PIXEL,
289 65 * SIZEOF_PIXEL))
290 fail();
291
292 /* Compare left[-1..63] = 65 pixels (left[-1] is unchanged,
293 * left[0..62] are modified, left[63] is unchanged) */
294 if (memcmp(left_ref - SIZEOF_PIXEL, left_new - SIZEOF_PIXEL,
295 65 * SIZEOF_PIXEL))
296 fail();
297
298 bench_new(ft_new, left_new, top);
299 }
300}
301
303{
304 LOCAL_ALIGNED_32(uint8_t, top_buf, [PRED_SIZE + 16]);
305 LOCAL_ALIGNED_32(uint8_t, left_buf, [PRED_SIZE + 16]);
306 /* Add offset of 8 bytes to allow negative indexing (top[-1], left[-1]) */
307 uint8_t *top = top_buf + 8;
308 uint8_t *left = left_buf + 8;
309 int bit_depth;
310
311 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
313
315 check_pred_dc(&h, top, left, bit_depth);
316 }
317 report("pred_dc");
318
319 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
321
324 }
325 report("pred_planar");
326
327 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
329
332 }
333 report("pred_angular");
334
335 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
337
340 }
341 report("ref_filter_3tap");
342
343 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
345
348 }
349 report("ref_filter_strong");
350}
static void bit_depth(AudioStatsContext *s, const uint64_t *const mask, uint8_t *depth)
Definition af_astats.c:246
static int BS_FUNC left(const BSCTX *bc)
Return the number of the bits left in a buffer.
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define checkasm_check_pixel_padded(...)
Definition checkasm.h:166
#define CLEAR_PIXEL_RECT(name)
Definition checkasm.h:151
#define PIXEL_RECT(name, w, h)
Definition checkasm.h:144
#define declare_func
Definition test.h:489
#define fail
Definition test.h:479
#define bench_new
Definition test.h:487
#define check_func
Definition test.h:481
#define call_new
Definition test.h:486
#define call_ref
Definition test.h:485
#define report
Definition test.h:480
static void check_pred_planar(HEVCPredContext *h, uint8_t *top, uint8_t *left, int bit_depth)
Definition hevc_pred.c:87
static void check_pred_angular(HEVCPredContext *h, uint8_t *top, uint8_t *left, int bit_depth)
Definition hevc_pred.c:132
#define PRED_SIZE
Definition hevc_pred.c:30
static void check_ref_filter_strong(HEVCPredContext *h, uint8_t *top, uint8_t *left, int bit_depth)
Definition hevc_pred.c:254
static void check_ref_filter_3tap(HEVCPredContext *h, uint8_t *top, uint8_t *left, int bit_depth)
Definition hevc_pred.c:199
void checkasm_check_hevc_pred(void)
Definition hevc_pred.c:302
static void check_pred_dc(HEVCPredContext *h, uint8_t *top, uint8_t *left, int bit_depth)
Definition hevc_pred.c:42
#define randomize_ref_buffers()
Definition hevc_pred.c:32
#define LOCAL_ALIGNED_32(t, v,...)
void ff_hevc_pred_init(HEVCPredContext *hpc, int bit_depth)
Definition pred.c:43
Definition swscale.c:71
#define stride
#define SIZEOF_PIXEL
Definition h264chroma.c:28
static const uint32_t pixel_mask[5]
Definition h264dsp.c:31
#define src
Definition vp8dsp.c:248
int size