GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 0.0% 0 / 0 / 2601
Functions: 0.0% 0 / 0 / 100
Branches: 0.0% 0 / 0 / 542

filters/intel/resample_avx512.hpp
Line Branch Exec Source
1 // AviSynth+. Copyright 2026- AviSynth+ Project
2 // https://avs-plus.net
3 // http://avisynth.nl
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
17 // http://www.gnu.org/copyleft/gpl.html .
18 //
19 // Linking Avisynth statically or dynamically with other modules is making a
20 // combined work based on Avisynth. Thus, the terms and conditions of the GNU
21 // General Public License cover the whole combination.
22 //
23 // As a special exception, the copyright holders of Avisynth give you
24 // permission to link Avisynth with independent modules that communicate with
25 // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
26 // terms of these independent modules, and to copy and distribute the
27 // resulting combined work under terms of your choice, provided that
28 // every copy of the combined work is accompanied by a complete copy of
29 // the source code of Avisynth (the version of Avisynth used to produce the
30 // combined work), being distributed under the terms of the GNU General
31 // Public License plus this exception. An independent module is a module
32 // which is not derived from or based on Avisynth, such as 3rd-party filters,
33 // import and export plugins, or graphical user interfaces.
34
35 /*
36
37 This is a common source cpp include file (not header) for multi-arch AVX512 functions.
38 Functions here are static, they will be compiled into each translation unit including this file.
39
40 */
41
42 // Original function needed avx512vbmi feature flag, but we want to support also base AVX512 without VBMI.
43 // We use _mm512_permutex2var_epi8_SIMUL<UseVBMI> and _mm512_maskz_permutex2var_epi8_SIMUL<UseVBMI>
44 // Thus both Base AVX512 and ICL level arch is supported.
45 // We are using two separated source modules and include this hpp file templated
46 // with UseVBMI/UseVNNI
47
48 // Notes:
49 // As of January 2026, Visual Studio 2026 ships with clang-cl (LLVM 20.1.8).
50 // - This version typically avoids using VNNI vpdpwssd instructions, opting instead for separate madd and add operations.
51 // - Masked permute operations are not optimized: for example, instead of using masked permutex2var_epi8,
52 // it performs a basic permutex2var_epi8 followed by an "and" with a pre-loaded zmm mask.
53 // These behaviors result in slower code compared to MSVC builds, which utilize these instructions more efficiently.
54 // These optimization issues are resolved in LLVM 21 (e.g., Intel C++ Compiler 2025.3).
55
56 // helper function for simulating _mm512_permutex2var_epi8 when VBMI is not available
57 // The MSB bit (128) zeroing effect is _not_ considered here, the indices must be all positive and within 0-127 range.
58 // Helper for _mm512_permutex2var_epi8_SIMUL: gather 32 bytes from [a,b] using precomputed word_idx and shift_amt.
59 // Extracted from lambda to ensure MSVC inlines it (lambdas are not reliably inlined by MSVC).
60 // Accepts precomputed word_idx (target_idx>>1) and shift_amt ((target_idx<<3)&8) so callers outside
61 // the y-loop can hoist these invariants, avoiding recomputation every row.
62 // Returns 32 16-bit words with gathered bytes in low 8 bits (high 8 bits cleared).
63 // Returning __m512i instead of __m256i lets callers feed unpacklo/hi_epi16 directly,
64 // avoiding the cvtepu8_epi16 expansion round-trip that MSVC emits as vpmovwb+vpmovzxbw.
65 AVS_FORCEINLINE static __m512i _permutex2var_epi8_sim_get32(__m512i word_idx, __m512i shift_amt, __m512i a, __m512i b)
66 {
67 __m512i words = _mm512_permutex2var_epi16(a, word_idx, b);
68 // vpsrlvw: avoids k-register RAW dependency; MSVC compiles test_epi16_mask+mask_srli as
69 // vptestmw->k1->vpsrlw{k1}, creating a read-after-write through the mask unit (higher latency)
70 words = _mm512_srlv_epi16(words, shift_amt);
71 // clear high byte so the caller can use unpacklo/hi_epi16 without cvtepu8_epi16
72 return _mm512_and_si512(words, _mm512_set1_epi16(0x00FF));
73 }
74
75 template<bool UseVBMI>
76 AVS_FORCEINLINE static __m512i _mm512_permutex2var_epi8_SIMUL(__m512i a, __m512i idx, __m512i b) {
77 if constexpr (UseVBMI) {
78 return _mm512_permutex2var_epi8(a, idx, b);
79 }
80 else {
81 const __m512i c_8 = _mm512_set1_epi16(8);
82
83 __m512i idx_lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(idx));
84 __m512i idx_hi = _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(idx, 1));
85
86 __m512i wi_lo = _mm512_srli_epi16(idx_lo, 1);
87 __m512i sa_lo = _mm512_and_si512(_mm512_slli_epi16(idx_lo, 3), c_8);
88 __m512i wi_hi = _mm512_srli_epi16(idx_hi, 1);
89 __m512i sa_hi = _mm512_and_si512(_mm512_slli_epi16(idx_hi, 3), c_8);
90
91 __m256i res_0_31 = _mm512_cvtepi16_epi8(_permutex2var_epi8_sim_get32(wi_lo, sa_lo, a, b));
92 __m256i res_32_63 = _mm512_cvtepi16_epi8(_permutex2var_epi8_sim_get32(wi_hi, sa_hi, a, b));
93
94 return _mm512_inserti64x4(_mm512_castsi256_si512(res_0_31), res_32_63, 1);
95 }
96 }
97
98 template<bool UseVBMI>
99 AVS_FORCEINLINE static __m512i _mm512_maskz_permutex2var_epi8_SIMUL(
100 __mmask64 k,
101 __m512i a,
102 __m512i idx,
103 __m512i b)
104 {
105 if constexpr (UseVBMI) {
106 return _mm512_maskz_permutex2var_epi8(k, a, idx, b);
107 }
108 else {
109 // 1. Run the base simulation to get the permuted bytes
110 // Note: Using your existing logic to get the full 512-bit permuted result
111 __m512i res = _mm512_permutex2var_epi8_SIMUL<false>(a, idx, b);
112
113 // 2. Apply the zero-mask using AVX-512BW
114 // _mm512_maskz_mov_epi8(k, src, src) returns 'src' where k[i]==1, and 0 where k[i]==0
115 return _mm512_maskz_mov_epi8(k, res);
116 }
117 }
118
119 // H-Float-Resampler: 16 pixels, filter size 4, transpose 4x (4x_m128) to 4x_m512
120 // Transposes a 4x4 matrix of 4-float vectors (16x16 float matrix effectively).
121 // Input/Output: Four 512-bit vectors (16 floats each) passed by reference.
122 AVS_FORCEINLINE static void _MM_TRANSPOSE16_LANE4_PS(__m512& row0, __m512& row1, __m512& row2, __m512& row3) {
123 // Stage 1: Interleave 32-bit (float) elements within 128-bit chunks (lanes)
124 // t0 = (r0_lo, r1_lo) | t1 = (r0_hi, r1_hi)
125 // t2 = (r2_lo, r3_lo) | t3 = (r2_hi, r3_hi)
126 __m512 t0 = _mm512_unpacklo_ps(row0, row1);
127 __m512 t1 = _mm512_unpackhi_ps(row0, row1);
128 __m512 t2 = _mm512_unpacklo_ps(row2, row3);
129 __m512 t3 = _mm512_unpackhi_ps(row2, row3);
130
131 // Stage 2: Shuffle 128-bit chunks (lanes) to complete the transpose
132 // We use _mm512_shuffle_ps which shuffles 64-bit blocks across the 512-bit register.
133 // _MM_SHUFFLE(w, z, y, x) applies to the 64-bit pairs (4 floats) within each 128-bit lane.
134 // Result: row0 = columns 0, 1, 2, 3
135 row0 = _mm512_shuffle_ps(t0, t2, _MM_SHUFFLE(1, 0, 1, 0));
136 // Result: row1 = columns 4, 5, 6, 7
137 row1 = _mm512_shuffle_ps(t0, t2, _MM_SHUFFLE(3, 2, 3, 2));
138 // Result: row2 = columns 8, 9, 10, 11
139 row2 = _mm512_shuffle_ps(t1, t3, _MM_SHUFFLE(1, 0, 1, 0));
140 // Result: row3 = columns 12, 13, 14, 15
141 row3 = _mm512_shuffle_ps(t1, t3, _MM_SHUFFLE(3, 2, 3, 2));
142 }
143
144 // H-float-resampler: 16 pixels, filter size 8, transpose 8x (2x_m256) to 8x_m512
145 // Transposes an 8x8 matrix of 2-float vectors (16x16 float matrix).
146 // Input/Output: Eight 512-bit vectors (16 floats each) passed by reference.
147 AVS_FORCEINLINE static void _MM_TRANSPOSE8x16_PS(
148 __m512& r0, __m512& r1, __m512& r2, __m512& r3,
149 __m512& r4, __m512& r5, __m512& r6, __m512& r7)
150 {
151 // --- Stage 1: Unpack 32-bit (Pairs of rows) ---
152 __m512 t0 = _mm512_unpacklo_ps(r0, r1);
153 __m512 t1 = _mm512_unpackhi_ps(r0, r1);
154 __m512 t2 = _mm512_unpacklo_ps(r2, r3);
155 __m512 t3 = _mm512_unpackhi_ps(r2, r3);
156 __m512 t4 = _mm512_unpacklo_ps(r4, r5);
157 __m512 t5 = _mm512_unpackhi_ps(r4, r5);
158 __m512 t6 = _mm512_unpacklo_ps(r6, r7);
159 __m512 t7 = _mm512_unpackhi_ps(r6, r7);
160
161 // --- Stage 2: Unpack 64-bit (Quads of rows) ---
162 // Uses _mm512_unpacklo/hi_pd for 64-bit (double) to interleave pairs of __m512 floats
163 __m512 u0 = _mm512_castpd_ps(_mm512_unpacklo_pd(_mm512_castps_pd(t0), _mm512_castps_pd(t2)));
164 __m512 u1 = _mm512_castpd_ps(_mm512_unpackhi_pd(_mm512_castps_pd(t0), _mm512_castps_pd(t2)));
165 __m512 u2 = _mm512_castpd_ps(_mm512_unpacklo_pd(_mm512_castps_pd(t1), _mm512_castps_pd(t3)));
166 __m512 u3 = _mm512_castpd_ps(_mm512_unpackhi_pd(_mm512_castps_pd(t1), _mm512_castps_pd(t3)));
167 __m512 u4 = _mm512_castpd_ps(_mm512_unpacklo_pd(_mm512_castps_pd(t4), _mm512_castps_pd(t6)));
168 __m512 u5 = _mm512_castpd_ps(_mm512_unpackhi_pd(_mm512_castps_pd(t4), _mm512_castps_pd(t6)));
169 __m512 u6 = _mm512_castpd_ps(_mm512_unpacklo_pd(_mm512_castps_pd(t5), _mm512_castps_pd(t7)));
170 __m512 u7 = _mm512_castpd_ps(_mm512_unpackhi_pd(_mm512_castps_pd(t5), _mm512_castps_pd(t7)));
171
172 // --- Stage 3: Shuffle 128-bit lanes (Octets of rows) ---
173 // _mm512_shuffle_f32x4 shuffles the 128-bit (f32x4) sub-vectors within and between two __m512 vectors.
174 // 0x88 = (10001000)_2: selects lane 0 from first input and lane 0 from second input for lo/hi 256 bits.
175 // 0xDD = (11011101)_2: selects lane 3 from first input and lane 3 from second input for lo/hi 256 bits.
176 __m512 v0 = _mm512_shuffle_f32x4(u0, u4, 0x88); // Col 0, 4 (interleaved)
177 __m512 v1 = _mm512_shuffle_f32x4(u0, u4, 0xDD); // Col 1, 5 (interleaved)
178 __m512 v2 = _mm512_shuffle_f32x4(u1, u5, 0x88); // Col 2, 6 (interleaved)
179 __m512 v3 = _mm512_shuffle_f32x4(u1, u5, 0xDD); // Col 3, 7 (interleaved)
180 __m512 v4 = _mm512_shuffle_f32x4(u2, u6, 0x88); // Col 8, 12 (interleaved)
181 __m512 v5 = _mm512_shuffle_f32x4(u2, u6, 0xDD); // Col 9, 13 (interleaved)
182 __m512 v6 = _mm512_shuffle_f32x4(u3, u7, 0x88); // Col 10, 14 (interleaved)
183 __m512 v7 = _mm512_shuffle_f32x4(u3, u7, 0xDD); // Col 11, 15 (interleaved)
184
185 // --- Stage 4: Permute to Linearize Indices ---
186 // Corrects the order of the 128-bit lanes to linearize the columns.
187 // The columns are currently: (0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15)
188 // The required order is: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
189 __m512i idx = _mm512_setr_epi32(
190 0, 4, 1, 5, /* Lane 0: Rows 0, 1, 2, 3 */
191 2, 6, 3, 7, /* Lane 1: Rows 4, 5, 6, 7 */
192 8, 12, 9, 13, /* Lane 2: Rows 8, 9, 10, 11 */
193 10, 14, 11, 15 /* Lane 3: Rows 12, 13, 14, 15 */
194 );
195
196 // --- Final Assignment with Correct Mapping ---
197 // Maps the permuted vector components back to the original row variables (now columns).
198 r0 = _mm512_permutexvar_ps(idx, v0); // Col 0
199 r1 = _mm512_permutexvar_ps(idx, v2); // Col 1
200 r2 = _mm512_permutexvar_ps(idx, v4); // Col 2
201 r3 = _mm512_permutexvar_ps(idx, v6); // Col 3
202 r4 = _mm512_permutexvar_ps(idx, v1); // Col 4
203 r5 = _mm512_permutexvar_ps(idx, v3); // Col 5
204 r6 = _mm512_permutexvar_ps(idx, v5); // Col 6
205 r7 = _mm512_permutexvar_ps(idx, v7); // Col 7
206 }
207
208 // Loads two 256-bit float vectors from registers (__m256) into a single 512-bit register.
209 // Equivalent to _mm512_insertf32x8(_mm512_castps256_ps512(lo), hi, 1)
210 AVS_FORCEINLINE static __m512 _mm512_insert_2_m256(__m256 lo, __m256 hi) {
211 return _mm512_insertf32x8(_mm512_castps256_ps512(lo), hi, 1);
212 }
213
214 // Loads four 128-bit float vectors (unaligned) into a single 512-bit register.
215 AVS_FORCEINLINE static __m512 _mm512_loadu_4_m128(
216 /* __m128 const* */ const float* addr1,
217 /* __m128 const* */ const float* addr2,
218 /* __m128 const* */ const float* addr3,
219 /* __m128 const* */ const float* addr4)
220 {
221 // The cast is needed for the first insertion to make the target a 512-bit register
222 __m512 v = _mm512_castps128_ps512(_mm_loadu_ps(addr1));
223 v = _mm512_insertf32x4(v, _mm_loadu_ps(addr2), 1);
224 v = _mm512_insertf32x4(v, _mm_loadu_ps(addr3), 2);
225 v = _mm512_insertf32x4(v, _mm_loadu_ps(addr4), 3);
226 return v;
227 }
228
229 // Loads four 128-bit float vectors (aligned) into a single 512-bit register.
230 AVS_FORCEINLINE static __m512 _mm512_load_4_m128(
231 /* __m128 const* */ const float* addr1,
232 /* __m128 const* */ const float* addr2,
233 /* __m128 const* */ const float* addr3,
234 /* __m128 const* */ const float* addr4)
235 {
236 // The cast is needed for the first insertion to make the target a 512-bit register
237 __m512 v = _mm512_castps128_ps512(_mm_load_ps(addr1));
238 v = _mm512_insertf32x4(v, _mm_load_ps(addr2), 1);
239 v = _mm512_insertf32x4(v, _mm_load_ps(addr3), 2);
240 v = _mm512_insertf32x4(v, _mm_load_ps(addr4), 3);
241 return v;
242 }
243
244 // Loads two 256 - bit unaligned integer vectors from registers(__m256i) into a single 512i register.
245 AVS_FORCEINLINE static __m512i _mm512i_loadu_2_m256i(
246 const __m256i* addr1,
247 const __m256i* addr2)
248 {
249 return _mm512_inserti64x4(_mm512_castsi256_si512(_mm256_loadu_si256(addr1)), _mm256_loadu_si256(addr2), 1);
250 }
251
252 // Loads two 256 - bit aligned integer vectors from registers(__m256) into a single 512 - bit register.
253 AVS_FORCEINLINE static __m512i _mm512i_load_2_m256i(
254 const __m256i* addr1,
255 const __m256i* addr2)
256 {
257 return _mm512_inserti64x4(_mm512_castsi256_si512(_mm256_load_si256(addr1)), _mm256_load_si256(addr2), 1);
258 }
259
260 // Integers
261 // Loads four 128-bit integer vectors (unaligned) into a single 512-bit integer register.
262 AVS_FORCEINLINE static __m512i _mm512i_loadu_4_m128i(
263 const __m128i* addr1,
264 const __m128i* addr2,
265 const __m128i* addr3,
266 const __m128i* addr4)
267 {
268 // The cast is needed for the first insertion to make the target a 512-bit register
269 __m512i v = _mm512_castsi128_si512(_mm_loadu_si128(addr1));
270 v = _mm512_inserti32x4(v, _mm_loadu_si128(addr2), 1);
271 v = _mm512_inserti32x4(v, _mm_loadu_si128(addr3), 2);
272 v = _mm512_inserti32x4(v, _mm_loadu_si128(addr4), 3);
273 return v;
274 }
275
276 // filter size up to 4
277 // 64 target uint8_t pixels at a time
278 // 128-byte source loads (128 uint8_t pixels)
279 // maximum permute index is 128 for _mm512_permutex2var_epi8 (uint8_t)
280
281 // filter size up to 8
282 // 64 target uint8_t pixels at a time
283 // 128-byte source loads (128 uint8_t pixels)
284 // maximum permute index is 128 for _mm512_permutex2var_epi8(uint8_t)
285
286 // filter size up to 8
287 // 64 target uint8_t pixels at a time in 2 groups of 32 to support longer source loading to each group to support lower downsample ratios
288 // support /2 downsample ratios for resizers with no-resize kernel size of 4 (or support of 2 ?) (Bicubic, Bilinear, and others, also SinPowResize (?))
289 // 2 groups of 128-byte source loads (128 uint8_t pixels)
290 // maximum permute index is 128 for _mm512_permutex2var_epi8 (uint8_t)
291
292 // filter size up to 16
293 // 32 target uint8_t pixels at a time
294 // 128-byte source loads (128 uint8_t pixels)
295 // maximum permute index is 128 for _mm512_permutex2var_epi8 (uint8_t)
296 // expect to support all upsampling ratios up to filter support of 8 (or 7..6 ?) and some downsampling ratios with filter support up to 3 (with downsample ratios from 0.5 or a bit lower)
297
298 // uint8_t "mp" versions
299
300 // filter size up to 4
301 // 64 target uint8_t pixels at a time
302 // 127-byte source loads (127 uint8_t pixels)
303 // maximum permute index is 127 for _mm512_maskz_permutex2var_epi8 (uint8_t)
304 // more premutex version to create 8->16bit converted and low-hi unpacked registers in single permutex instruction
305
306 // filter size up to 4
307 // 64 target uint8_t pixels at a time
308 // 127-byte source loads (127 uint8_t pixels)
309 // maximum permute index is 127 for _mm512_maskz_permutex2var_epi8 (uint8_t)
310 // more premutex version to create 8->16bit converted and low-hi unpacked registers in single permutex instruction
311 template<bool UseVNNI>
312 void resize_h_planar_uint8_avx512_permutex_vstripe_mpz_ks4_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
313 {
314 constexpr int PIXELS_AT_A_TIME = 64;
315
316 // 'source_overread_beyond_targetx' indicates if the filter kernel can read beyond the target width.
317 // we load 2x64 source bytes at a time, so ensure safe overread if needed.
318 // Our main loop processes calculates for 64 target pixels at a time.
319 // Inside that, we load 128 source bytes (2x64) to be able to permutex from that.
320 // This we have to check at each mod-PIXELS_AT_A_TIME boundary, the allowance of 128-byte source load.
321 const int width_safe_mod = (program->safelimit_128_pixels_each64th_target.overread_possible ? program->safelimit_128_pixels_each64th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
322
323 // Preconditions:
324 assert(program->filter_size_real <= 4); // We preload all relevant coefficients (up to 4) before the height loop.
325
326 // 'target_size_alignment' ensures we can safely access coefficients using offsets like
327 // 'filter_size * 15' when processing 16 H pixels at a time
328 // 'filter_size * 63' when processing 64 H pixels at a time
329 assert(program->target_size_alignment >= 64); // Adjusted for 64 pixels (is it enough for uint8 ?)
330
331 assert(FRAME_ALIGN >= 64); // Good for 64x8 bit pixels
332
333 // Ensure that coefficient loading beyond the valid target size is safe for 4x4 float loads.
334 // We load 4x16bit coeffs at a time
335 assert(program->filter_size_alignment >= 4);
336
337 const int max_scanlines = program->max_scanlines;
338
339 __m512i rounder = _mm512_set1_epi32(1 << (FPScale8bits - 1));
340
341 // Vertical stripe loop for L2 cache optimization
342 for (int y_from = 0; y_from < height; y_from += max_scanlines)
343 {
344 int y_to = std::min(y_from + max_scanlines, height);
345
346 // Reset current_coeff for the start of the stripe (points to start of row's coeffs)
347 // const short* AVS_RESTRICT current_coeff = program->pixel_coefficient;
348 // Pre-transposed coefficients for AVX512, stored in the program structure, ready for direct use in the main loop without needing to transpose on the fly.
349 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
350
351 int x = 0;
352
353 // Lambda to handle both safe (fast) and unsafe (masked/partial) loading paths
354 auto do_h_integer_core = [&](auto partial_load) {
355
356 // prepare coefs in transposed V-form
357 // TODO: make storage in transposed form, 64 x uint16 transposition looks too slow
358 __m512i one_epi16 = _mm512_set1_epi16(1);
359
360 // load coeffs from prepared
361 const __m512i coef_r0r1_0_31lo = _mm512_load_si512(current_coeff_SIMD + 0);
362 const __m512i coef_r0r1_0_31hi = _mm512_load_si512(current_coeff_SIMD + 1); // in count of __m512i
363
364 const __m512i coef_r0r1_32_63lo = _mm512_load_si512(current_coeff_SIMD + 2);
365 const __m512i coef_r0r1_32_63hi = _mm512_load_si512(current_coeff_SIMD + 3);
366
367 const __m512i coef_r2r3_0_31lo = _mm512_load_si512(current_coeff_SIMD + 4);
368 const __m512i coef_r2r3_0_31hi = _mm512_load_si512(current_coeff_SIMD + 5);
369
370 const __m512i coef_r2r3_32_63lo = _mm512_load_si512(current_coeff_SIMD + 6);
371 const __m512i coef_r2r3_32_63hi = _mm512_load_si512(current_coeff_SIMD + 7);
372
373 // convert resampling program in H-form into permuting indexes for src transposition in V-form
374 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x])); // 16 offsets
375 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16])); // 16 offsets
376 __m512i perm_0_32_47 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 32])); // 16 offsets
377 __m512i perm_0_48_63 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 48])); // 16 offsets
378
379 int iStart = program->pixel_offset[x];
380 __m512i m512i_Start = _mm512_set1_epi32(iStart);
381
382 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, m512i_Start);
383 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, m512i_Start);
384 perm_0_32_47 = _mm512_sub_epi32(perm_0_32_47, m512i_Start);
385 perm_0_48_63 = _mm512_sub_epi32(perm_0_48_63, m512i_Start);
386
387 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
388 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
389 __m256i m256i_perm_0_32_47 = _mm512_cvtepi32_epi16(perm_0_32_47);
390 __m256i m256i_perm_0_48_63 = _mm512_cvtepi32_epi16(perm_0_48_63);
391
392 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
393 __m512i perm_0_32_63 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_32_47), m256i_perm_0_48_63, 1);
394
395 // Taps are contiguous (0, 1, 2, 3), so we increment perm indexes by 1.
396 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
397 __m512i perm_2_0_31 = _mm512_add_epi16(perm_1_0_31, one_epi16);
398 __m512i perm_3_0_31 = _mm512_add_epi16(perm_2_0_31, one_epi16);
399
400 __m512i perm_1_32_63 = _mm512_add_epi16(perm_0_32_63, one_epi16);
401 __m512i perm_2_32_63 = _mm512_add_epi16(perm_1_32_63, one_epi16);
402 __m512i perm_3_32_63 = _mm512_add_epi16(perm_2_32_63, one_epi16);
403
404 const __m512i perm_r0r1_0_31lo = _mm512_unpacklo_epi16(perm_0_0_31, perm_1_0_31);
405 const __m512i perm_r0r1_0_31hi = _mm512_unpackhi_epi16(perm_0_0_31, perm_1_0_31);
406
407 const __m512i perm_r0r1_32_63lo = _mm512_unpacklo_epi16(perm_0_32_63, perm_1_32_63);
408 const __m512i perm_r0r1_32_63hi = _mm512_unpackhi_epi16(perm_0_32_63, perm_1_32_63);
409
410 const __m512i perm_r2r3_0_31lo = _mm512_unpacklo_epi16(perm_2_0_31, perm_3_0_31);
411 const __m512i perm_r2r3_0_31hi = _mm512_unpackhi_epi16(perm_2_0_31, perm_3_0_31);
412
413 const __m512i perm_r2r3_32_63lo = _mm512_unpacklo_epi16(perm_2_32_63, perm_3_32_63);
414 const __m512i perm_r2r3_32_63hi = _mm512_unpackhi_epi16(perm_2_32_63, perm_3_32_63);
415
416 __m512i wi_r0r1_0_31lo={},sa_r0r1_0_31lo={},wi_r0r1_0_31hi={},sa_r0r1_0_31hi={};
417 __m512i wi_r0r1_32_63lo={},sa_r0r1_32_63lo={},wi_r0r1_32_63hi={},sa_r0r1_32_63hi={};
418 __m512i wi_r2r3_0_31lo={},sa_r2r3_0_31lo={},wi_r2r3_0_31hi={},sa_r2r3_0_31hi={};
419 __m512i wi_r2r3_32_63lo={},sa_r2r3_32_63lo={},wi_r2r3_32_63hi={},sa_r2r3_32_63hi={};
420 if constexpr (!UseVNNI) {
421 const __m512i c_8w = _mm512_set1_epi16(8);
422 auto make_wi_sa = [&](__m512i p, __m512i& wi, __m512i& sa) {
423 wi = _mm512_srli_epi16(p, 1);
424 sa = _mm512_and_si512(_mm512_slli_epi16(p, 3), c_8w);
425 };
426 make_wi_sa(perm_r0r1_0_31lo, wi_r0r1_0_31lo, sa_r0r1_0_31lo);
427 make_wi_sa(perm_r0r1_0_31hi, wi_r0r1_0_31hi, sa_r0r1_0_31hi);
428 make_wi_sa(perm_r0r1_32_63lo, wi_r0r1_32_63lo, sa_r0r1_32_63lo);
429 make_wi_sa(perm_r0r1_32_63hi, wi_r0r1_32_63hi, sa_r0r1_32_63hi);
430 make_wi_sa(perm_r2r3_0_31lo, wi_r2r3_0_31lo, sa_r2r3_0_31lo);
431 make_wi_sa(perm_r2r3_0_31hi, wi_r2r3_0_31hi, sa_r2r3_0_31hi);
432 make_wi_sa(perm_r2r3_32_63lo, wi_r2r3_32_63lo, sa_r2r3_32_63lo);
433 make_wi_sa(perm_r2r3_32_63hi, wi_r2r3_32_63hi, sa_r2r3_32_63hi);
434 }
435
436 uint8_t* AVS_RESTRICT dst_ptr = dst8 + x + y_from * dst_pitch;
437 const uint8_t* src_ptr = src8 + iStart + y_from * src_pitch; // all permute offsets relative to this start offset
438
439 // Calculate remaining pixels for bounds checking in partial_load mode. 1..128 remaining pixels possible.
440 const int remaining = program->source_size - iStart;
441 __mmask64 k1 = _bzhi_u64(~0ULL, remaining); // _bzhi_u64 creates a mask with the lower N bits set. If N >= 64, it returns all ones (~0ULL).
442 __mmask64 k2 = _bzhi_u64(~0ULL, std::max(0, remaining - 64));
443
444 // mask is used to zero out every odd byte, so that the result of the permute is a
445 // vector of zero-extended 8-bit values in 16-bit lanes, preparing 8-bit data for 16-bit FMA operations in AVX512.
446 const __mmask64 k_zh8 = 0x5555555555555555ULL;
447
448 for (int y = y_from; y < y_to; y++)
449 {
450 // 8 coeffs + 8 permute_idx + 2 src + 4 temporal + 1 rounder ~= 23 regs (permute2var overwrite first source - really may be more needed)
451 __m512i data_src, data_src2;
452
453 if constexpr (partial_load) {
454 // Safe masked loads for the image edge
455 data_src = _mm512_maskz_loadu_epi8(k1, src_ptr);
456 data_src2 = _mm512_maskz_loadu_epi8(k2, src_ptr + 64);
457 }
458 else {
459 // Fast unaligned loads for the safe zone
460 data_src = _mm512_loadu_si512(src_ptr);
461 data_src2 = _mm512_loadu_si512(src_ptr + 64);
462 }
463
464 __m512i src_r0r1_0_31lo, src_r0r1_0_31hi, src_r0r1_32_63lo, src_r0r1_32_63hi;
465 __m512i src_r2r3_0_31lo, src_r2r3_0_31hi, src_r2r3_32_63lo, src_r2r3_32_63hi;
466 if constexpr (UseVNNI) {
467 src_r0r1_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_r0r1_0_31lo, data_src2);
468 src_r0r1_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_r0r1_0_31hi, data_src2);
469 src_r0r1_32_63lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_r0r1_32_63lo, data_src2);
470 src_r0r1_32_63hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_r0r1_32_63hi, data_src2);
471 src_r2r3_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_r2r3_0_31lo, data_src2);
472 src_r2r3_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_r2r3_0_31hi, data_src2);
473 src_r2r3_32_63lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_r2r3_32_63lo, data_src2);
474 src_r2r3_32_63hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_r2r3_32_63hi, data_src2);
475 } else {
476 src_r0r1_0_31lo = _permutex2var_epi8_sim_get32(wi_r0r1_0_31lo, sa_r0r1_0_31lo, data_src, data_src2);
477 src_r0r1_0_31hi = _permutex2var_epi8_sim_get32(wi_r0r1_0_31hi, sa_r0r1_0_31hi, data_src, data_src2);
478 src_r0r1_32_63lo = _permutex2var_epi8_sim_get32(wi_r0r1_32_63lo, sa_r0r1_32_63lo, data_src, data_src2);
479 src_r0r1_32_63hi = _permutex2var_epi8_sim_get32(wi_r0r1_32_63hi, sa_r0r1_32_63hi, data_src, data_src2);
480 src_r2r3_0_31lo = _permutex2var_epi8_sim_get32(wi_r2r3_0_31lo, sa_r2r3_0_31lo, data_src, data_src2);
481 src_r2r3_0_31hi = _permutex2var_epi8_sim_get32(wi_r2r3_0_31hi, sa_r2r3_0_31hi, data_src, data_src2);
482 src_r2r3_32_63lo = _permutex2var_epi8_sim_get32(wi_r2r3_32_63lo, sa_r2r3_32_63lo, data_src, data_src2);
483 src_r2r3_32_63hi = _permutex2var_epi8_sim_get32(wi_r2r3_32_63hi, sa_r2r3_32_63hi, data_src, data_src2);
484 }
485
486 __m512i result_0_31lo, result_0_31hi;
487 __m512i result_32_63lo, result_32_63hi;
488
489 if constexpr (UseVNNI)
490 {
491 result_0_31lo = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31lo, coef_r0r1_0_31lo);
492 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r2r3_0_31lo, coef_r2r3_0_31lo);
493
494 result_0_31hi = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31hi, coef_r0r1_0_31hi);
495 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r2r3_0_31hi, coef_r2r3_0_31hi);
496
497 result_32_63lo = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63lo, coef_r0r1_32_63lo);
498 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r2r3_32_63lo, coef_r2r3_32_63lo);
499
500 result_32_63hi = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63hi, coef_r0r1_32_63hi);
501 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r2r3_32_63hi, coef_r2r3_32_63hi);
502 }
503 else
504 {
505 // making FMA in 32bits accs as in AVX256 V-resize
506 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, coef_r0r1_0_31lo), _mm512_madd_epi16(src_r2r3_0_31lo, coef_r2r3_0_31lo));
507 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, coef_r0r1_0_31hi), _mm512_madd_epi16(src_r2r3_0_31hi, coef_r2r3_0_31hi));
508
509 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63lo, coef_r0r1_32_63lo), _mm512_madd_epi16(src_r2r3_32_63lo, coef_r2r3_32_63lo));
510 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63hi, coef_r0r1_32_63hi), _mm512_madd_epi16(src_r2r3_32_63hi, coef_r2r3_32_63hi));
511
512 // rounding
513 result_0_31lo = _mm512_add_epi32(result_0_31lo, rounder);
514 result_0_31hi = _mm512_add_epi32(result_0_31hi, rounder);
515 result_32_63lo = _mm512_add_epi32(result_32_63lo, rounder);
516 result_32_63hi = _mm512_add_epi32(result_32_63hi, rounder);
517 }
518
519 // scaling down
520 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale8bits);
521 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale8bits);
522 result_32_63lo = _mm512_srai_epi32(result_32_63lo, FPScale8bits);
523 result_32_63hi = _mm512_srai_epi32(result_32_63hi, FPScale8bits);
524
525 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
526 __m512i result_32_63_int16 = _mm512_packus_epi32(result_32_63lo, result_32_63hi);
527
528 // cast is enough, no need to _mm512_zextsi256_si512
529 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr),
530 _mm512_inserti64x4(_mm512_castsi256_si512(_mm512_cvtusepi16_epi8(result_0_31_int16)), _mm512_cvtusepi16_epi8(result_32_63_int16), 1));
531
532 dst_ptr += dst_pitch;
533 src_ptr += src_pitch;
534 }
535
536 // current_coeff += filter_size * PIXELS_AT_A_TIME;
537 current_coeff_SIMD += 8; // in number of __m512i
538
539 };
540
541 // Process the 'safe zone' where direct full unaligned loads are acceptable.
542 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
543 {
544 do_h_integer_core(std::false_type{});
545 }
546
547 // Process the potentially 'unsafe zone' near the image edge, using safe masked loading.
548 for (; x < width; x += PIXELS_AT_A_TIME)
549 {
550 do_h_integer_core(std::true_type{});
551 }
552 }
553 }
554
555 // filter size up to 8
556 // 64 target uint8_t pixels at a time
557 // 128-byte source loads (128 uint8_t pixels)
558 // maximum permute index is 128 for _mm512_maskz_permutex2var_epi8 (uint8_t)
559 // support VNNI and madd FMA
560
561 // filter size up to 16
562 // 32 target uint8_t pixels at a time
563 // 128-byte source loads (128 uint8_t pixels)
564 // maximum permute index is 128 for _mm512_maskz_permutex2var_epi8 (uint8_t)
565
566 // uint16_t
567
568 // filter size up to 4
569 // 32 target uint16_t pixels at a time
570 // 128-byte source loads (64 uint16_t pixels)
571 // maximum permute index is 64 for _mm512_permutex2var_epi16 (uint16_t)
572 // making lo-hi unpacking with single permutex2var operation and optional VNNI FMA
573
574 // filter size up to 8
575 // 64 target uint16_t pixels at a time in 4 groups of 16
576 // 128-byte source loads (64 uint16_t pixels), 2 groups of 128 byte source loads for each 16 output samples,
577 // expect to support /2 downsize (used in ConvertTo42x with default bicubic too)
578 // maximum permute index is 64 for _mm512_permutex2var_epi16 (uint16_t)
579 // support VNNI and madd FMA
580 // checker: (16/*iSamplesInTheGroup*/, 64/*permutex_index_diff_limit*/, 8/*kernel_size*/))
581
582 template <bool AdvancePerm, bool UseVNNI>
583 AVS_FORCEINLINE static void process_row_pair(
584 // 1. Accumulators (Modified across calls)
585 __m512i& res_lo, __m512i& res_hi,
586 __m512i& res_32_63lo, __m512i& res_32_63hi,
587 // 2. Permutation Vectors (Modified across calls)
588 __m512i& p_lo, __m512i& p_hi,
589 __m512i& p_32_lo, __m512i& p_32_hi,
590 // 3. Coefficients (Read-only)
591 const __m512i& c_lo, const __m512i& c_hi,
592 const __m512i& c_32_63lo, const __m512i& c_32_63hi,
593 // 4. Source Data and Constants
594 const __m512i& d0_15, const __m512i& d16_31,
595 const __m512i& d32_47, const __m512i& d48_63,
596 const __m512i& d2_0_15, const __m512i& d2_16_31,
597 const __m512i& d2_32_47, const __m512i& d2_48_63,
598 const __mmask32 &k_hi, const __m512i &v_two)
599 {
600 // Generate source vectors via permutations
601 auto src_lo = _mm512_mask_blend_epi16(k_hi,
602 _mm512_permutex2var_epi16(d0_15, p_lo, d2_0_15),
603 _mm512_permutex2var_epi16(d16_31, p_lo, d2_16_31));
604
605 auto src_hi = _mm512_mask_blend_epi16(k_hi,
606 _mm512_permutex2var_epi16(d0_15, p_hi, d2_0_15),
607 _mm512_permutex2var_epi16(d16_31, p_hi, d2_16_31));
608
609 auto src_32_63lo = _mm512_mask_blend_epi16(k_hi,
610 _mm512_permutex2var_epi16(d32_47, p_32_lo, d2_32_47),
611 _mm512_permutex2var_epi16(d48_63, p_32_lo, d2_48_63));
612
613 auto src_32_63hi = _mm512_mask_blend_epi16(k_hi,
614 _mm512_permutex2var_epi16(d32_47, p_32_hi, d2_32_47),
615 _mm512_permutex2var_epi16(d48_63, p_32_hi, d2_48_63));
616
617 // Accumulate results
618 if constexpr (UseVNNI) {
619 res_lo = _mm512_dpwssd_epi32(res_lo, src_lo, c_lo);
620 res_hi = _mm512_dpwssd_epi32(res_hi, src_hi, c_hi);
621 res_32_63lo = _mm512_dpwssd_epi32(res_32_63lo, src_32_63lo, c_32_63lo);
622 res_32_63hi = _mm512_dpwssd_epi32(res_32_63hi, src_32_63hi, c_32_63hi);
623 }
624 else {
625 res_lo = _mm512_add_epi32(res_lo, _mm512_madd_epi16(src_lo, c_lo));
626 res_hi = _mm512_add_epi32(res_hi, _mm512_madd_epi16(src_hi, c_hi));
627 res_32_63lo = _mm512_add_epi32(res_32_63lo, _mm512_madd_epi16(src_32_63lo, c_32_63lo));
628 res_32_63hi = _mm512_add_epi32(res_32_63hi, _mm512_madd_epi16(src_32_63hi, c_32_63hi));
629 }
630
631 if constexpr (AdvancePerm) {
632 p_lo = _mm512_add_epi16(p_lo, v_two);
633 p_hi = _mm512_add_epi16(p_hi, v_two);
634 p_32_lo = _mm512_add_epi16(p_32_lo, v_two);
635 p_32_hi = _mm512_add_epi16(p_32_hi, v_two);
636 }
637 }
638
639 // filter size up to 4
640 // 64 target uint16_t pixels at a time in 2 groups of 32
641 // 128-byte source loads (64 uint16_t pixels)
642 // maximum permute index is 64 for _mm512_permutex2var_epi16 (uint16_t)
643 // making lo-hi unpacking with single permutex2var operation and optional VNNI FMA
644
645 // filter size up to 8
646 // 64 target uint16_t pixels at a time in 2 groups of 32
647 // 128-byte source loads (64 uint16_t pixels)
648 // maximum permute index is 64 for _mm512_permutex2var_epi16 (uint16_t)
649 // support VNNI and madd FMA
650
651 // filter size up to 8
652 // 32 target uint16_t pixels at a time
653 // 128-byte source loads (64 uint16_t pixels), 2 groups of 128 byte source loads for each 16 output samples, expect to support /2 downsize (used in ConvertTo42x with default bicubic too)
654 // maximum permute index is 64 for _mm512_permutex2var_epi16 (uint16_t)
655 // support VNNI and madd FMA
656 // checker function is program->resize_h_planar_gather_permutex_vstripe_check(16/*iSamplesInTheGroup*/, 64/*permutex_index_diff_limit*/, 8/*kernel_size*/))
657
658 // filter size up to 8
659 // 32 target uint16_t pixels at a time
660 // 128-byte source loads (64 uint16_t pixels)
661 // maximum permute index is 64 for _mm512_permutex2var_epi16 (uint16_t)
662 // support VNNI and madd FMA
663
664 // filter size up to 16
665 // 32 target uint16_t pixels at a time
666 // 128-byte source loads (64 uint16_t pixels)
667 // maximum permute index is 64 for _mm512_permutex2var_epi16 (uint16_t)
668 // support VNNI and madd FMA
669
670 // filter size up to 8, pretransposed coefficients
671 // 64 target uint8_t pixels at a time
672 // 128-byte source loads (2x64 uint8_t pixels)
673 template<bool UseVNNI>
674 void resize_h_planar_uint8_avx512_permutex_vstripe_mpz_ks8_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
675 {
676 constexpr int PIXELS_AT_A_TIME = 64;
677
678 const int width_safe_mod = (program->safelimit_128_pixels_each64th_target.overread_possible ? program->safelimit_128_pixels_each64th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
679
680 assert(program->filter_size_real <= 8);
681 assert(program->target_size_alignment >= 64);
682 assert(FRAME_ALIGN >= 64);
683 assert(program->filter_size_alignment >= 8);
684
685 const int max_scanlines = program->max_scanlines;
686
687 __m512i rounder = _mm512_set1_epi32(1 << (FPScale8bits - 1));
688
689 for (int y_from = 0; y_from < height; y_from += max_scanlines)
690 {
691 int y_to = std::min(y_from + max_scanlines, height);
692
693 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
694
695 int x = 0;
696
697 auto do_h_integer_core = [&](auto partial_load) {
698 __m512i one_epi16 = _mm512_set1_epi16(1);
699
700 const __m512i coef_r0r1_0_31lo = _mm512_load_si512(current_coeff_SIMD + 0);
701 const __m512i coef_r0r1_0_31hi = _mm512_load_si512(current_coeff_SIMD + 1);
702 const __m512i coef_r0r1_32_63lo = _mm512_load_si512(current_coeff_SIMD + 2);
703 const __m512i coef_r0r1_32_63hi = _mm512_load_si512(current_coeff_SIMD + 3);
704 const __m512i coef_r2r3_0_31lo = _mm512_load_si512(current_coeff_SIMD + 4);
705 const __m512i coef_r2r3_0_31hi = _mm512_load_si512(current_coeff_SIMD + 5);
706 const __m512i coef_r2r3_32_63lo = _mm512_load_si512(current_coeff_SIMD + 6);
707 const __m512i coef_r2r3_32_63hi = _mm512_load_si512(current_coeff_SIMD + 7);
708 const __m512i coef_r4r5_0_31lo = _mm512_load_si512(current_coeff_SIMD + 8);
709 const __m512i coef_r4r5_0_31hi = _mm512_load_si512(current_coeff_SIMD + 9);
710 const __m512i coef_r4r5_32_63lo = _mm512_load_si512(current_coeff_SIMD + 10);
711 const __m512i coef_r4r5_32_63hi = _mm512_load_si512(current_coeff_SIMD + 11);
712 const __m512i coef_r6r7_0_31lo = _mm512_load_si512(current_coeff_SIMD + 12);
713 const __m512i coef_r6r7_0_31hi = _mm512_load_si512(current_coeff_SIMD + 13);
714 const __m512i coef_r6r7_32_63lo = _mm512_load_si512(current_coeff_SIMD + 14);
715 const __m512i coef_r6r7_32_63hi = _mm512_load_si512(current_coeff_SIMD + 15);
716
717 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
718 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
719 __m512i perm_0_32_47 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 32]));
720 __m512i perm_0_48_63 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 48]));
721
722 int iStart = program->pixel_offset[x];
723 __m512i m512i_Start = _mm512_set1_epi32(iStart);
724
725 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, m512i_Start);
726 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, m512i_Start);
727 perm_0_32_47 = _mm512_sub_epi32(perm_0_32_47, m512i_Start);
728 perm_0_48_63 = _mm512_sub_epi32(perm_0_48_63, m512i_Start);
729
730 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
731 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
732 __m256i m256i_perm_0_32_47 = _mm512_cvtepi32_epi16(perm_0_32_47);
733 __m256i m256i_perm_0_48_63 = _mm512_cvtepi32_epi16(perm_0_48_63);
734
735 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
736 __m512i perm_0_32_63 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_32_47), m256i_perm_0_48_63, 1);
737
738 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
739 __m512i perm_1_32_63 = _mm512_add_epi16(perm_0_32_63, one_epi16);
740
741 const __m512i perm_r0r1_0_31lo = _mm512_unpacklo_epi16(perm_0_0_31, perm_1_0_31);
742 const __m512i perm_r0r1_0_31hi = _mm512_unpackhi_epi16(perm_0_0_31, perm_1_0_31);
743 const __m512i perm_r0r1_32_63lo = _mm512_unpacklo_epi16(perm_0_32_63, perm_1_32_63);
744 const __m512i perm_r0r1_32_63hi = _mm512_unpackhi_epi16(perm_0_32_63, perm_1_32_63);
745
746 const __m512i two_epi16 = _mm512_set1_epi16(2);
747
748 __m512i wi_r0r1_0_31lo={},sa_r0r1_0_31lo={},wi_r0r1_0_31hi={},sa_r0r1_0_31hi={};
749 __m512i wi_r0r1_32_63lo={},sa_r0r1_32_63lo={},wi_r0r1_32_63hi={},sa_r0r1_32_63hi={};
750 __m512i wi_r2r3_0_31lo={},sa_r2r3_0_31lo={},wi_r2r3_0_31hi={},sa_r2r3_0_31hi={};
751 __m512i wi_r2r3_32_63lo={},sa_r2r3_32_63lo={},wi_r2r3_32_63hi={},sa_r2r3_32_63hi={};
752 __m512i wi_r4r5_0_31lo={},sa_r4r5_0_31lo={},wi_r4r5_0_31hi={},sa_r4r5_0_31hi={};
753 __m512i wi_r4r5_32_63lo={},sa_r4r5_32_63lo={},wi_r4r5_32_63hi={},sa_r4r5_32_63hi={};
754 __m512i wi_r6r7_0_31lo={},sa_r6r7_0_31lo={},wi_r6r7_0_31hi={},sa_r6r7_0_31hi={};
755 __m512i wi_r6r7_32_63lo={},sa_r6r7_32_63lo={},wi_r6r7_32_63hi={},sa_r6r7_32_63hi={};
756 if constexpr (!UseVNNI) {
757 const __m512i c_8w = _mm512_set1_epi16(8);
758 auto make_wi_sa = [&](__m512i p, __m512i& wi, __m512i& sa) {
759 wi = _mm512_srli_epi16(p, 1);
760 sa = _mm512_and_si512(_mm512_slli_epi16(p, 3), c_8w);
761 };
762 __m512i p_lo = perm_r0r1_0_31lo, p_hi = perm_r0r1_0_31hi;
763 __m512i p_lo2 = perm_r0r1_32_63lo, p_hi2 = perm_r0r1_32_63hi;
764 make_wi_sa(p_lo, wi_r0r1_0_31lo, sa_r0r1_0_31lo); make_wi_sa(p_hi, wi_r0r1_0_31hi, sa_r0r1_0_31hi);
765 make_wi_sa(p_lo2, wi_r0r1_32_63lo, sa_r0r1_32_63lo); make_wi_sa(p_hi2, wi_r0r1_32_63hi, sa_r0r1_32_63hi);
766 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
767 p_lo2 = _mm512_add_epi16(p_lo2, two_epi16); p_hi2 = _mm512_add_epi16(p_hi2, two_epi16);
768 make_wi_sa(p_lo, wi_r2r3_0_31lo, sa_r2r3_0_31lo); make_wi_sa(p_hi, wi_r2r3_0_31hi, sa_r2r3_0_31hi);
769 make_wi_sa(p_lo2, wi_r2r3_32_63lo, sa_r2r3_32_63lo); make_wi_sa(p_hi2, wi_r2r3_32_63hi, sa_r2r3_32_63hi);
770 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
771 p_lo2 = _mm512_add_epi16(p_lo2, two_epi16); p_hi2 = _mm512_add_epi16(p_hi2, two_epi16);
772 make_wi_sa(p_lo, wi_r4r5_0_31lo, sa_r4r5_0_31lo); make_wi_sa(p_hi, wi_r4r5_0_31hi, sa_r4r5_0_31hi);
773 make_wi_sa(p_lo2, wi_r4r5_32_63lo, sa_r4r5_32_63lo); make_wi_sa(p_hi2, wi_r4r5_32_63hi, sa_r4r5_32_63hi);
774 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
775 p_lo2 = _mm512_add_epi16(p_lo2, two_epi16); p_hi2 = _mm512_add_epi16(p_hi2, two_epi16);
776 make_wi_sa(p_lo, wi_r6r7_0_31lo, sa_r6r7_0_31lo); make_wi_sa(p_hi, wi_r6r7_0_31hi, sa_r6r7_0_31hi);
777 make_wi_sa(p_lo2, wi_r6r7_32_63lo, sa_r6r7_32_63lo); make_wi_sa(p_hi2, wi_r6r7_32_63hi, sa_r6r7_32_63hi);
778 }
779
780 uint8_t* AVS_RESTRICT dst_ptr = dst8 + x + y_from * dst_pitch;
781 const uint8_t* src_ptr = src8 + iStart + y_from * src_pitch;
782
783 const int remaining = program->source_size - iStart;
784 __mmask64 k1 = _bzhi_u64(~0ULL, remaining);
785 const __mmask64 k2 = _bzhi_u64(~0ULL, std::max(0, remaining - 64));
786 const __mmask64 k_zh8 = 0x5555555555555555ULL;
787
788 for (int y = y_from; y < y_to; y++)
789 {
790 __m512i data_src, data_src2;
791
792 __m512i perm_rNrNp1_0_31lo_w = perm_r0r1_0_31lo;
793 __m512i perm_rNrNp1_0_31hi_w = perm_r0r1_0_31hi;
794 __m512i perm_rNrNp1_32_63lo_w = perm_r0r1_32_63lo;
795 __m512i perm_rNrNp1_32_63hi_w = perm_r0r1_32_63hi;
796
797 if constexpr (partial_load) {
798 data_src = _mm512_maskz_loadu_epi8(k1, src_ptr);
799 data_src2 = _mm512_maskz_loadu_epi8(k2, src_ptr + 64);
800 }
801 else {
802 data_src = _mm512_loadu_si512(src_ptr);
803 data_src2 = _mm512_loadu_si512(src_ptr + 64);
804 }
805
806 // rows 0..1
807 __m512i src_r0r1_0_31lo, src_r0r1_0_31hi, src_r0r1_32_63lo, src_r0r1_32_63hi;
808 if constexpr (UseVNNI) {
809 src_r0r1_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
810 src_r0r1_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
811 src_r0r1_32_63lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_32_63lo_w, data_src2);
812 src_r0r1_32_63hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_32_63hi_w, data_src2);
813 } else {
814 src_r0r1_0_31lo = _permutex2var_epi8_sim_get32(wi_r0r1_0_31lo, sa_r0r1_0_31lo, data_src, data_src2);
815 src_r0r1_0_31hi = _permutex2var_epi8_sim_get32(wi_r0r1_0_31hi, sa_r0r1_0_31hi, data_src, data_src2);
816 src_r0r1_32_63lo = _permutex2var_epi8_sim_get32(wi_r0r1_32_63lo, sa_r0r1_32_63lo, data_src, data_src2);
817 src_r0r1_32_63hi = _permutex2var_epi8_sim_get32(wi_r0r1_32_63hi, sa_r0r1_32_63hi, data_src, data_src2);
818 }
819
820 // for r2r3
821 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
822 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
823 perm_rNrNp1_32_63lo_w = _mm512_add_epi16(perm_rNrNp1_32_63lo_w, two_epi16);
824 perm_rNrNp1_32_63hi_w = _mm512_add_epi16(perm_rNrNp1_32_63hi_w, two_epi16);
825
826 __m512i src_r2r3_0_31lo, src_r2r3_0_31hi, src_r2r3_32_63lo, src_r2r3_32_63hi;
827 if constexpr (UseVNNI) {
828 src_r2r3_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
829 src_r2r3_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
830 src_r2r3_32_63lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_32_63lo_w, data_src2);
831 src_r2r3_32_63hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_32_63hi_w, data_src2);
832 } else {
833 src_r2r3_0_31lo = _permutex2var_epi8_sim_get32(wi_r2r3_0_31lo, sa_r2r3_0_31lo, data_src, data_src2);
834 src_r2r3_0_31hi = _permutex2var_epi8_sim_get32(wi_r2r3_0_31hi, sa_r2r3_0_31hi, data_src, data_src2);
835 src_r2r3_32_63lo = _permutex2var_epi8_sim_get32(wi_r2r3_32_63lo, sa_r2r3_32_63lo, data_src, data_src2);
836 src_r2r3_32_63hi = _permutex2var_epi8_sim_get32(wi_r2r3_32_63hi, sa_r2r3_32_63hi, data_src, data_src2);
837 }
838
839 // for r4r5
840 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
841 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
842 perm_rNrNp1_32_63lo_w = _mm512_add_epi16(perm_rNrNp1_32_63lo_w, two_epi16);
843 perm_rNrNp1_32_63hi_w = _mm512_add_epi16(perm_rNrNp1_32_63hi_w, two_epi16);
844
845 __m512i result_0_31lo, result_0_31hi;
846 __m512i result_32_63lo, result_32_63hi;
847
848 if constexpr (UseVNNI)
849 {
850 result_0_31lo = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31lo, coef_r0r1_0_31lo);
851 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r2r3_0_31lo, coef_r2r3_0_31lo);
852 result_0_31hi = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31hi, coef_r0r1_0_31hi);
853 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r2r3_0_31hi, coef_r2r3_0_31hi);
854 result_32_63lo = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63lo, coef_r0r1_32_63lo);
855 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r2r3_32_63lo, coef_r2r3_32_63lo);
856 result_32_63hi = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63hi, coef_r0r1_32_63hi);
857 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r2r3_32_63hi, coef_r2r3_32_63hi);
858 }
859 else
860 {
861 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, coef_r0r1_0_31lo), _mm512_madd_epi16(src_r2r3_0_31lo, coef_r2r3_0_31lo));
862 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, coef_r0r1_0_31hi), _mm512_madd_epi16(src_r2r3_0_31hi, coef_r2r3_0_31hi));
863 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63lo, coef_r0r1_32_63lo), _mm512_madd_epi16(src_r2r3_32_63lo, coef_r2r3_32_63lo));
864 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63hi, coef_r0r1_32_63hi), _mm512_madd_epi16(src_r2r3_32_63hi, coef_r2r3_32_63hi));
865 }
866
867 // rows 4..5
868 __m512i src_r4r5_0_31lo, src_r4r5_0_31hi, src_r4r5_32_63lo, src_r4r5_32_63hi;
869 if constexpr (UseVNNI) {
870 src_r4r5_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
871 src_r4r5_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
872 src_r4r5_32_63lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_32_63lo_w, data_src2);
873 src_r4r5_32_63hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_32_63hi_w, data_src2);
874 } else {
875 src_r4r5_0_31lo = _permutex2var_epi8_sim_get32(wi_r4r5_0_31lo, sa_r4r5_0_31lo, data_src, data_src2);
876 src_r4r5_0_31hi = _permutex2var_epi8_sim_get32(wi_r4r5_0_31hi, sa_r4r5_0_31hi, data_src, data_src2);
877 src_r4r5_32_63lo = _permutex2var_epi8_sim_get32(wi_r4r5_32_63lo, sa_r4r5_32_63lo, data_src, data_src2);
878 src_r4r5_32_63hi = _permutex2var_epi8_sim_get32(wi_r4r5_32_63hi, sa_r4r5_32_63hi, data_src, data_src2);
879 }
880
881 // for r6r7
882 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
883 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
884 perm_rNrNp1_32_63lo_w = _mm512_add_epi16(perm_rNrNp1_32_63lo_w, two_epi16);
885 perm_rNrNp1_32_63hi_w = _mm512_add_epi16(perm_rNrNp1_32_63hi_w, two_epi16);
886
887 __m512i src_r6r7_0_31lo, src_r6r7_0_31hi, src_r6r7_32_63lo, src_r6r7_32_63hi;
888 if constexpr (UseVNNI) {
889 src_r6r7_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
890 src_r6r7_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
891 src_r6r7_32_63lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_32_63lo_w, data_src2);
892 src_r6r7_32_63hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_32_63hi_w, data_src2);
893 } else {
894 src_r6r7_0_31lo = _permutex2var_epi8_sim_get32(wi_r6r7_0_31lo, sa_r6r7_0_31lo, data_src, data_src2);
895 src_r6r7_0_31hi = _permutex2var_epi8_sim_get32(wi_r6r7_0_31hi, sa_r6r7_0_31hi, data_src, data_src2);
896 src_r6r7_32_63lo = _permutex2var_epi8_sim_get32(wi_r6r7_32_63lo, sa_r6r7_32_63lo, data_src, data_src2);
897 src_r6r7_32_63hi = _permutex2var_epi8_sim_get32(wi_r6r7_32_63hi, sa_r6r7_32_63hi, data_src, data_src2);
898 }
899
900 if constexpr (UseVNNI)
901 {
902 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r4r5_0_31lo, coef_r4r5_0_31lo);
903 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r6r7_0_31lo, coef_r6r7_0_31lo);
904 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r4r5_0_31hi, coef_r4r5_0_31hi);
905 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r6r7_0_31hi, coef_r6r7_0_31hi);
906 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r4r5_32_63lo, coef_r4r5_32_63lo);
907 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r6r7_32_63lo, coef_r6r7_32_63lo);
908 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r4r5_32_63hi, coef_r4r5_32_63hi);
909 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r6r7_32_63hi, coef_r6r7_32_63hi);
910 // rounding VNNI in first FMA already summed
911 }
912 else
913 {
914 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_0_31lo, coef_r4r5_0_31lo), result_0_31lo);
915 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_0_31hi, coef_r4r5_0_31hi), result_0_31hi);
916 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_32_63lo, coef_r4r5_32_63lo), result_32_63lo);
917 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_32_63hi, coef_r4r5_32_63hi), result_32_63hi);
918
919 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_0_31lo, coef_r6r7_0_31lo), result_0_31lo);
920 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_0_31hi, coef_r6r7_0_31hi), result_0_31hi);
921 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_32_63lo, coef_r6r7_32_63lo), result_32_63lo);
922 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_32_63hi, coef_r6r7_32_63hi), result_32_63hi);
923
924 result_0_31lo = _mm512_add_epi32(result_0_31lo, rounder);
925 result_0_31hi = _mm512_add_epi32(result_0_31hi, rounder);
926 result_32_63lo = _mm512_add_epi32(result_32_63lo, rounder);
927 result_32_63hi = _mm512_add_epi32(result_32_63hi, rounder);
928 }
929
930 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale8bits);
931 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale8bits);
932 result_32_63lo = _mm512_srai_epi32(result_32_63lo, FPScale8bits);
933 result_32_63hi = _mm512_srai_epi32(result_32_63hi, FPScale8bits);
934
935 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
936 __m512i result_32_63_int16 = _mm512_packus_epi32(result_32_63lo, result_32_63hi);
937
938 __m256i result_0_31_u8 = _mm512_cvtusepi16_epi8(result_0_31_int16);
939 __m256i result_32_63_u8 = _mm512_cvtusepi16_epi8(result_32_63_int16);
940
941 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr), _mm512_inserti64x4(_mm512_castsi256_si512(result_0_31_u8), result_32_63_u8, 1));
942
943 dst_ptr += dst_pitch;
944 src_ptr += src_pitch;
945 }
946
947 current_coeff_SIMD += 16;
948 };
949
950 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
951 {
952 do_h_integer_core(std::false_type{});
953 }
954
955 for (; x < width; x += PIXELS_AT_A_TIME)
956 {
957 do_h_integer_core(std::true_type{});
958 }
959 }
960 }
961
962 // filter size up to 16, pretransposed coefficients
963 // 32 target uint8_t pixels at a time
964 // 128-byte source loads (2x64 uint8_t pixels)
965 template<bool UseVNNI>
966 void resize_h_planar_uint8_avx512_permutex_vstripe_mpz_ks16_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
967 {
968 constexpr int PIXELS_AT_A_TIME = 32;
969
970 const int width_safe_mod = (program->safelimit_128_pixels_each64th_target.overread_possible ? program->safelimit_128_pixels_each64th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
971
972 assert(program->filter_size_real <= 16);
973 assert(program->target_size_alignment >= 32);
974 assert(FRAME_ALIGN >= 32);
975 assert(program->filter_size_alignment >= 16);
976
977 const int max_scanlines = program->max_scanlines;
978
979 __m512i rounder = _mm512_set1_epi32(1 << (FPScale8bits - 1));
980
981 for (int y_from = 0; y_from < height; y_from += max_scanlines)
982 {
983 int y_to = std::min(y_from + max_scanlines, height);
984
985 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
986
987 int x = 0;
988
989 auto do_h_integer_core = [&](auto partial_load) {
990 __m512i one_epi16 = _mm512_set1_epi16(1);
991
992 const __m512i coef_r0r1_0_31lo = _mm512_load_si512(current_coeff_SIMD + 0);
993 const __m512i coef_r0r1_0_31hi = _mm512_load_si512(current_coeff_SIMD + 1);
994 const __m512i coef_r2r3_0_31lo = _mm512_load_si512(current_coeff_SIMD + 2);
995 const __m512i coef_r2r3_0_31hi = _mm512_load_si512(current_coeff_SIMD + 3);
996 const __m512i coef_r4r5_0_31lo = _mm512_load_si512(current_coeff_SIMD + 4);
997 const __m512i coef_r4r5_0_31hi = _mm512_load_si512(current_coeff_SIMD + 5);
998 const __m512i coef_r6r7_0_31lo = _mm512_load_si512(current_coeff_SIMD + 6);
999 const __m512i coef_r6r7_0_31hi = _mm512_load_si512(current_coeff_SIMD + 7);
1000 const __m512i coef_r8r9_0_31lo = _mm512_load_si512(current_coeff_SIMD + 8);
1001 const __m512i coef_r8r9_0_31hi = _mm512_load_si512(current_coeff_SIMD + 9);
1002 const __m512i coef_r10r11_0_31lo = _mm512_load_si512(current_coeff_SIMD + 10);
1003 const __m512i coef_r10r11_0_31hi = _mm512_load_si512(current_coeff_SIMD + 11);
1004 const __m512i coef_r12r13_0_31lo = _mm512_load_si512(current_coeff_SIMD + 12);
1005 const __m512i coef_r12r13_0_31hi = _mm512_load_si512(current_coeff_SIMD + 13);
1006 const __m512i coef_r14r15_0_31lo = _mm512_load_si512(current_coeff_SIMD + 14);
1007 const __m512i coef_r14r15_0_31hi = _mm512_load_si512(current_coeff_SIMD + 15);
1008
1009 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
1010 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
1011
1012 int iStart = program->pixel_offset[x];
1013 __m512i m512i_Start = _mm512_set1_epi32(iStart);
1014
1015 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, m512i_Start);
1016 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, m512i_Start);
1017
1018 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
1019 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
1020
1021 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
1022 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
1023
1024 const __m512i perm_r0r1_0_31lo = _mm512_unpacklo_epi16(perm_0_0_31, perm_1_0_31);
1025 const __m512i perm_r0r1_0_31hi = _mm512_unpackhi_epi16(perm_0_0_31, perm_1_0_31);
1026
1027 const __m512i two_epi16 = _mm512_set1_epi16(2);
1028
1029 // BASE path: precompute word-index (wi) and shift-amount (sa) for each tap-pair perm.
1030 // perm_rNrNp1 holds 32 x 16-bit byte addresses; wi = addr>>1 selects the 16-bit word in
1031 // {data_src, data_src2}, sa = (addr<<3)&8 selects high (1) or low (0) byte within that word.
1032 // Precomputing here eliminates ~12 instructions/gather (cvtepu8+extract+slli+srli+maskz_mov)
1033 // from the hot y-loop, leaving only the 3-instruction sim_get32 core per tap-pair.
1034 __m512i wi_r0r1_lo={},sa_r0r1_lo={},wi_r0r1_hi={},sa_r0r1_hi={};
1035 __m512i wi_r2r3_lo={},sa_r2r3_lo={},wi_r2r3_hi={},sa_r2r3_hi={};
1036 __m512i wi_r4r5_lo={},sa_r4r5_lo={},wi_r4r5_hi={},sa_r4r5_hi={};
1037 __m512i wi_r6r7_lo={},sa_r6r7_lo={},wi_r6r7_hi={},sa_r6r7_hi={};
1038 __m512i wi_r8r9_lo={},sa_r8r9_lo={},wi_r8r9_hi={},sa_r8r9_hi={};
1039 __m512i wi_r10r11_lo={},sa_r10r11_lo={},wi_r10r11_hi={},sa_r10r11_hi={};
1040 __m512i wi_r12r13_lo={},sa_r12r13_lo={},wi_r12r13_hi={},sa_r12r13_hi={};
1041 __m512i wi_r14r15_lo={},sa_r14r15_lo={},wi_r14r15_hi={},sa_r14r15_hi={};
1042 if constexpr (!UseVNNI) {
1043 const __m512i c_8w = _mm512_set1_epi16(8);
1044 auto make_wi_sa = [&](__m512i p, __m512i& wi, __m512i& sa) {
1045 wi = _mm512_srli_epi16(p, 1);
1046 sa = _mm512_and_si512(_mm512_slli_epi16(p, 3), c_8w);
1047 };
1048 __m512i p_lo = perm_r0r1_0_31lo, p_hi = perm_r0r1_0_31hi;
1049 make_wi_sa(p_lo, wi_r0r1_lo, sa_r0r1_lo); make_wi_sa(p_hi, wi_r0r1_hi, sa_r0r1_hi);
1050 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
1051 make_wi_sa(p_lo, wi_r2r3_lo, sa_r2r3_lo); make_wi_sa(p_hi, wi_r2r3_hi, sa_r2r3_hi);
1052 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
1053 make_wi_sa(p_lo, wi_r4r5_lo, sa_r4r5_lo); make_wi_sa(p_hi, wi_r4r5_hi, sa_r4r5_hi);
1054 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
1055 make_wi_sa(p_lo, wi_r6r7_lo, sa_r6r7_lo); make_wi_sa(p_hi, wi_r6r7_hi, sa_r6r7_hi);
1056 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
1057 make_wi_sa(p_lo, wi_r8r9_lo, sa_r8r9_lo); make_wi_sa(p_hi, wi_r8r9_hi, sa_r8r9_hi);
1058 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
1059 make_wi_sa(p_lo, wi_r10r11_lo, sa_r10r11_lo); make_wi_sa(p_hi, wi_r10r11_hi, sa_r10r11_hi);
1060 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
1061 make_wi_sa(p_lo, wi_r12r13_lo, sa_r12r13_lo); make_wi_sa(p_hi, wi_r12r13_hi, sa_r12r13_hi);
1062 p_lo = _mm512_add_epi16(p_lo, two_epi16); p_hi = _mm512_add_epi16(p_hi, two_epi16);
1063 make_wi_sa(p_lo, wi_r14r15_lo, sa_r14r15_lo); make_wi_sa(p_hi, wi_r14r15_hi, sa_r14r15_hi);
1064 }
1065
1066 uint8_t* AVS_RESTRICT dst_ptr = dst8 + x + y_from * dst_pitch;
1067 const uint8_t* src_ptr = src8 + iStart + y_from * src_pitch;
1068
1069 const int remaining = program->source_size - iStart;
1070 __mmask64 k1 = _bzhi_u64(~0ULL, remaining);
1071 const __mmask64 k2 = _bzhi_u64(~0ULL, std::max(0, remaining - 64));
1072 const __mmask64 k_zh8 = 0x5555555555555555ULL;
1073
1074 for (int y = y_from; y < y_to; y++)
1075 {
1076 __m512i data_src, data_src2;
1077
1078 __m512i perm_rNrNp1_0_31lo_w = perm_r0r1_0_31lo;
1079 __m512i perm_rNrNp1_0_31hi_w = perm_r0r1_0_31hi;
1080
1081 if constexpr (partial_load) {
1082 data_src = _mm512_maskz_loadu_epi8(k1, src_ptr);
1083 data_src2 = _mm512_maskz_loadu_epi8(k2, src_ptr + 64);
1084 }
1085 else {
1086 data_src = _mm512_loadu_si512(src_ptr);
1087 data_src2 = _mm512_loadu_si512(src_ptr + 64);
1088 }
1089
1090 // rows 0..1
1091 __m512i src_r0r1_0_31lo, src_r0r1_0_31hi;
1092 if constexpr (UseVNNI) {
1093 src_r0r1_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
1094 src_r0r1_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
1095 } else {
1096 src_r0r1_0_31lo = _permutex2var_epi8_sim_get32(wi_r0r1_lo, sa_r0r1_lo, data_src, data_src2);
1097 src_r0r1_0_31hi = _permutex2var_epi8_sim_get32(wi_r0r1_hi, sa_r0r1_hi, data_src, data_src2);
1098 }
1099
1100 // for r2r3
1101 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
1102 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
1103
1104 __m512i src_r2r3_0_31lo, src_r2r3_0_31hi;
1105 if constexpr (UseVNNI) {
1106 src_r2r3_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
1107 src_r2r3_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
1108 } else {
1109 src_r2r3_0_31lo = _permutex2var_epi8_sim_get32(wi_r2r3_lo, sa_r2r3_lo, data_src, data_src2);
1110 src_r2r3_0_31hi = _permutex2var_epi8_sim_get32(wi_r2r3_hi, sa_r2r3_hi, data_src, data_src2);
1111 }
1112
1113 // for r4r5
1114 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
1115 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
1116
1117 __m512i result_0_31lo, result_0_31hi;
1118
1119 if constexpr (UseVNNI)
1120 {
1121 result_0_31lo = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31lo, coef_r0r1_0_31lo);
1122 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r2r3_0_31lo, coef_r2r3_0_31lo);
1123 result_0_31hi = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31hi, coef_r0r1_0_31hi);
1124 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r2r3_0_31hi, coef_r2r3_0_31hi);
1125 }
1126 else
1127 {
1128 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, coef_r0r1_0_31lo), _mm512_madd_epi16(src_r2r3_0_31lo, coef_r2r3_0_31lo));
1129 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, coef_r0r1_0_31hi), _mm512_madd_epi16(src_r2r3_0_31hi, coef_r2r3_0_31hi));
1130 }
1131
1132 // rows 4..5
1133 __m512i src_r4r5_0_31lo, src_r4r5_0_31hi;
1134 if constexpr (UseVNNI) {
1135 src_r4r5_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
1136 src_r4r5_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
1137 } else {
1138 src_r4r5_0_31lo = _permutex2var_epi8_sim_get32(wi_r4r5_lo, sa_r4r5_lo, data_src, data_src2);
1139 src_r4r5_0_31hi = _permutex2var_epi8_sim_get32(wi_r4r5_hi, sa_r4r5_hi, data_src, data_src2);
1140 }
1141
1142 // for r6r7
1143 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
1144 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
1145
1146 __m512i src_r6r7_0_31lo, src_r6r7_0_31hi;
1147 if constexpr (UseVNNI) {
1148 src_r6r7_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
1149 src_r6r7_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
1150 } else {
1151 src_r6r7_0_31lo = _permutex2var_epi8_sim_get32(wi_r6r7_lo, sa_r6r7_lo, data_src, data_src2);
1152 src_r6r7_0_31hi = _permutex2var_epi8_sim_get32(wi_r6r7_hi, sa_r6r7_hi, data_src, data_src2);
1153 }
1154
1155 // for r8r9
1156 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
1157 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
1158
1159 if constexpr (UseVNNI)
1160 {
1161 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r4r5_0_31lo, coef_r4r5_0_31lo);
1162 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r6r7_0_31lo, coef_r6r7_0_31lo);
1163 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r4r5_0_31hi, coef_r4r5_0_31hi);
1164 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r6r7_0_31hi, coef_r6r7_0_31hi);
1165 }
1166 else
1167 {
1168 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_0_31lo, coef_r4r5_0_31lo), result_0_31lo);
1169 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_0_31hi, coef_r4r5_0_31hi), result_0_31hi);
1170 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_0_31lo, coef_r6r7_0_31lo), result_0_31lo);
1171 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_0_31hi, coef_r6r7_0_31hi), result_0_31hi);
1172 }
1173
1174 // rows 8..9
1175 __m512i src_r8r9_0_31lo, src_r8r9_0_31hi;
1176 if constexpr (UseVNNI) {
1177 src_r8r9_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
1178 src_r8r9_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
1179 } else {
1180 src_r8r9_0_31lo = _permutex2var_epi8_sim_get32(wi_r8r9_lo, sa_r8r9_lo, data_src, data_src2);
1181 src_r8r9_0_31hi = _permutex2var_epi8_sim_get32(wi_r8r9_hi, sa_r8r9_hi, data_src, data_src2);
1182 }
1183
1184 // for r10r11
1185 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
1186 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
1187
1188 __m512i src_r10r11_0_31lo, src_r10r11_0_31hi;
1189 if constexpr (UseVNNI) {
1190 src_r10r11_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
1191 src_r10r11_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
1192 } else {
1193 src_r10r11_0_31lo = _permutex2var_epi8_sim_get32(wi_r10r11_lo, sa_r10r11_lo, data_src, data_src2);
1194 src_r10r11_0_31hi = _permutex2var_epi8_sim_get32(wi_r10r11_hi, sa_r10r11_hi, data_src, data_src2);
1195 }
1196
1197 // for r12r13
1198 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
1199 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
1200
1201 if constexpr (UseVNNI)
1202 {
1203 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r8r9_0_31lo, coef_r8r9_0_31lo);
1204 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r10r11_0_31lo, coef_r10r11_0_31lo);
1205 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r8r9_0_31hi, coef_r8r9_0_31hi);
1206 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r10r11_0_31hi, coef_r10r11_0_31hi);
1207 }
1208 else
1209 {
1210 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r8r9_0_31lo, coef_r8r9_0_31lo), result_0_31lo);
1211 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r8r9_0_31hi, coef_r8r9_0_31hi), result_0_31hi);
1212 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r10r11_0_31lo, coef_r10r11_0_31lo), result_0_31lo);
1213 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r10r11_0_31hi, coef_r10r11_0_31hi), result_0_31hi);
1214 }
1215
1216 // rows 12..13
1217 __m512i src_r12r13_0_31lo, src_r12r13_0_31hi;
1218 if constexpr (UseVNNI) {
1219 src_r12r13_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
1220 src_r12r13_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
1221 } else {
1222 src_r12r13_0_31lo = _permutex2var_epi8_sim_get32(wi_r12r13_lo, sa_r12r13_lo, data_src, data_src2);
1223 src_r12r13_0_31hi = _permutex2var_epi8_sim_get32(wi_r12r13_hi, sa_r12r13_hi, data_src, data_src2);
1224 }
1225
1226 // for r14r15
1227 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
1228 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
1229
1230 __m512i src_r14r15_0_31lo, src_r14r15_0_31hi;
1231 if constexpr (UseVNNI) {
1232 src_r14r15_0_31lo = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31lo_w, data_src2);
1233 src_r14r15_0_31hi = _mm512_maskz_permutex2var_epi8(k_zh8, data_src, perm_rNrNp1_0_31hi_w, data_src2);
1234 } else {
1235 src_r14r15_0_31lo = _permutex2var_epi8_sim_get32(wi_r14r15_lo, sa_r14r15_lo, data_src, data_src2);
1236 src_r14r15_0_31hi = _permutex2var_epi8_sim_get32(wi_r14r15_hi, sa_r14r15_hi, data_src, data_src2);
1237 }
1238
1239 if constexpr (UseVNNI)
1240 {
1241 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r12r13_0_31lo, coef_r12r13_0_31lo);
1242 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r14r15_0_31lo, coef_r14r15_0_31lo);
1243 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r12r13_0_31hi, coef_r12r13_0_31hi);
1244 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r14r15_0_31hi, coef_r14r15_0_31hi);
1245 // rounding VNNI in first FMA already summed
1246 }
1247 else
1248 {
1249 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r12r13_0_31lo, coef_r12r13_0_31lo), result_0_31lo);
1250 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r12r13_0_31hi, coef_r12r13_0_31hi), result_0_31hi);
1251 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r14r15_0_31lo, coef_r14r15_0_31lo), result_0_31lo);
1252 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r14r15_0_31hi, coef_r14r15_0_31hi), result_0_31hi);
1253
1254 result_0_31lo = _mm512_add_epi32(result_0_31lo, rounder);
1255 result_0_31hi = _mm512_add_epi32(result_0_31hi, rounder);
1256 }
1257
1258 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale8bits);
1259 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale8bits);
1260
1261 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
1262
1263 __m256i result_0_31_u8 = _mm512_cvtusepi16_epi8(result_0_31_int16);
1264
1265 _mm256_stream_si256(reinterpret_cast<__m256i*>(dst_ptr), result_0_31_u8);
1266
1267 dst_ptr += dst_pitch;
1268 src_ptr += src_pitch;
1269 }
1270
1271 current_coeff_SIMD += 16;
1272 };
1273
1274 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
1275 {
1276 do_h_integer_core(std::false_type{});
1277 }
1278
1279 for (; x < width; x += PIXELS_AT_A_TIME)
1280 {
1281 do_h_integer_core(std::true_type{});
1282 }
1283 }
1284 }
1285
1286 // filter size up to 64, pretransposed coefficients
1287 // 64 target uint8_t pixels at a time in 2 groups of 32
1288 // 2 groups of 128-byte source loads
1289 template<bool UseVNNI>
1290 void resize_h_planar_uint8_avx512_permutex_vstripe_mpz_2s32_ks64_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
1291 {
1292 int filter_size_real = program->filter_size_real;
1293 if ((filter_size_real / 2 * 2) != filter_size_real) filter_size_real++;
1294
1295 constexpr int PIXELS_AT_A_TIME = 64;
1296
1297 const int width_safe_mod = (program->safelimit_128_pixels_each64th_target.overread_possible ? program->safelimit_128_pixels_each64th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
1298
1299 assert(program->filter_size_real <= 64);
1300 assert(program->target_size_alignment >= 64);
1301 assert(FRAME_ALIGN >= 64);
1302 assert(program->filter_size_alignment >= 8);
1303
1304 const int max_scanlines = program->max_scanlines;
1305
1306 __m512i rounder = _mm512_set1_epi32(1 << (FPScale8bits - 1));
1307
1308 for (int y_from = 0; y_from < height; y_from += max_scanlines)
1309 {
1310 int y_to = std::min(y_from + max_scanlines, height);
1311
1312 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
1313
1314 int x = 0;
1315
1316 auto do_h_integer_core = [&](auto partial_load) {
1317
1318 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
1319 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
1320 __m512i perm_0_32_47 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 32]));
1321 __m512i perm_0_48_63 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 48]));
1322
1323 int iStart = program->pixel_offset[x];
1324 int iStart_2 = program->pixel_offset[x + 32];
1325 __m512i m512i_Start = _mm512_set1_epi32(iStart);
1326 __m512i m512i_Start_2 = _mm512_set1_epi32(iStart_2);
1327
1328 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, m512i_Start);
1329 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, m512i_Start);
1330 perm_0_32_47 = _mm512_sub_epi32(perm_0_32_47, m512i_Start_2);
1331 perm_0_48_63 = _mm512_sub_epi32(perm_0_48_63, m512i_Start_2);
1332
1333 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
1334 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
1335 __m256i m256i_perm_0_32_47 = _mm512_cvtepi32_epi16(perm_0_32_47);
1336 __m256i m256i_perm_0_48_63 = _mm512_cvtepi32_epi16(perm_0_48_63);
1337
1338 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
1339 __m512i perm_0_32_63 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_32_47), m256i_perm_0_48_63, 1);
1340
1341 __m512i one_epi16 = _mm512_set1_epi16(1);
1342 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
1343 __m512i perm_1_32_63 = _mm512_add_epi16(perm_0_32_63, one_epi16);
1344
1345 const __m512i two_epi16 = _mm512_set1_epi16(2);
1346
1347 // BASE: precompute wi/sa outside y-loop. perm vectors are epi16 (32 16-bit byte indices).
1348 // Each kr step the perm advances by +2 bytes = +1 word, so wi += 1 and sa is invariant.
1349 // perm_1 = perm_0 + 1: wi_p1[i] = wi_p0[i] + (perm_0[i] & 1), sa_p1 = sa_p0 ^ 8.
1350 __m512i wi_g1_p0_base = {}, wi_g2_p0_base = {}, wi_g1_p1_base = {}, wi_g2_p1_base = {};
1351 __m512i sa_g1_p0 = {}, sa_g2_p0 = {}, sa_g1_p1 = {}, sa_g2_p1 = {};
1352 if constexpr (!UseVNNI) {
1353 const __m512i c_8w = _mm512_set1_epi16(8);
1354 sa_g1_p0 = _mm512_and_si512(_mm512_slli_epi16(perm_0_0_31, 3), c_8w);
1355 wi_g1_p0_base = _mm512_srli_epi16(perm_0_0_31, 1);
1356 sa_g1_p1 = _mm512_xor_si512(sa_g1_p0, c_8w);
1357 wi_g1_p1_base = _mm512_add_epi16(wi_g1_p0_base, _mm512_srli_epi16(sa_g1_p0, 3));
1358 sa_g2_p0 = _mm512_and_si512(_mm512_slli_epi16(perm_0_32_63, 3), c_8w);
1359 wi_g2_p0_base = _mm512_srli_epi16(perm_0_32_63, 1);
1360 sa_g2_p1 = _mm512_xor_si512(sa_g2_p0, c_8w);
1361 wi_g2_p1_base = _mm512_add_epi16(wi_g2_p0_base, _mm512_srli_epi16(sa_g2_p0, 3));
1362 }
1363
1364 uint8_t* AVS_RESTRICT dst_ptr = dst8 + x + y_from * dst_pitch;
1365 const uint8_t* src_ptr = src8 + iStart + y_from * src_pitch;
1366 const uint8_t* src_ptr_2 = src8 + iStart_2 + y_from * src_pitch;
1367
1368 const int remaining = program->source_size - iStart;
1369 const int remaining_2 = program->source_size - iStart_2;
1370 const __mmask64 k1 = _bzhi_u64(~0ULL, remaining);
1371 const __mmask64 k2 = _bzhi_u64(~0ULL, std::max(0, remaining - 64));
1372 const __mmask64 k1_2 = _bzhi_u64(~0ULL, remaining_2);
1373 const __mmask64 k2_2 = _bzhi_u64(~0ULL, std::max(0, remaining_2 - 64));
1374 const __mmask64 k_zh8 = 0x5555555555555555ULL;
1375
1376 for (int y = y_from; y < y_to; y++)
1377 {
1378 __m512i data_src, data_src2, data_src_2, data_src2_2;
1379
1380 // VBMI: reset working perm copies per row; BASE: reset running wi counters per row
1381 __m512i perm_0_0_31w = {}, perm_0_32_63w = {}, perm_1_0_31w = {}, perm_1_32_63w = {};
1382 __m512i wi_g1_p0 = {}, wi_g2_p0 = {}, wi_g1_p1 = {}, wi_g2_p1 = {};
1383 if constexpr (UseVNNI) {
1384 perm_0_0_31w = perm_0_0_31;
1385 perm_0_32_63w = perm_0_32_63;
1386 perm_1_0_31w = perm_1_0_31;
1387 perm_1_32_63w = perm_1_32_63;
1388 } else {
1389 wi_g1_p0 = wi_g1_p0_base;
1390 wi_g2_p0 = wi_g2_p0_base;
1391 wi_g1_p1 = wi_g1_p1_base;
1392 wi_g2_p1 = wi_g2_p1_base;
1393 }
1394
1395 if constexpr (partial_load) {
1396 data_src = _mm512_maskz_loadu_epi8(k1, src_ptr);
1397 data_src2 = _mm512_maskz_loadu_epi8(k2, src_ptr + 64);
1398 data_src_2 = _mm512_maskz_loadu_epi8(k1_2, src_ptr_2);
1399 data_src2_2 = _mm512_maskz_loadu_epi8(k2_2, src_ptr_2 + 64);
1400 }
1401 else {
1402 data_src = _mm512_loadu_si512(src_ptr);
1403 data_src2 = _mm512_loadu_si512(src_ptr + 64);
1404 data_src_2 = _mm512_loadu_si512(src_ptr_2);
1405 data_src2_2 = _mm512_loadu_si512(src_ptr_2 + 64);
1406 }
1407
1408 __m512i result_0_31lo = rounder;
1409 __m512i result_0_31hi = rounder;
1410 __m512i result_32_63lo = rounder;
1411 __m512i result_32_63hi = rounder;
1412
1413 const __m512i* current_coeff_SIMDw = current_coeff_SIMD;
1414
1415 for (int kr = 0; kr < filter_size_real; kr += 2)
1416 {
1417 __m512i src_r0_0_31, src_r0_32_63, src_r1_0_31, src_r1_32_63;
1418 if constexpr (UseVNNI) {
1419 src_r0_0_31 = _mm512_maskz_permutex2var_epi8_SIMUL<true>(k_zh8, data_src, perm_0_0_31w, data_src2);
1420 src_r0_32_63 = _mm512_maskz_permutex2var_epi8_SIMUL<true>(k_zh8, data_src_2, perm_0_32_63w, data_src2_2);
1421 src_r1_0_31 = _mm512_maskz_permutex2var_epi8_SIMUL<true>(k_zh8, data_src, perm_1_0_31w, data_src2);
1422 src_r1_32_63 = _mm512_maskz_permutex2var_epi8_SIMUL<true>(k_zh8, data_src_2, perm_1_32_63w, data_src2_2);
1423 perm_0_0_31w = _mm512_add_epi16(perm_0_0_31w, two_epi16);
1424 perm_0_32_63w = _mm512_add_epi16(perm_0_32_63w, two_epi16);
1425 perm_1_0_31w = _mm512_add_epi16(perm_1_0_31w, two_epi16);
1426 perm_1_32_63w = _mm512_add_epi16(perm_1_32_63w, two_epi16);
1427 } else {
1428 // BASE: sim_get32 reads word wi (already indexed into [data_src | data_src2])
1429 // and shifts by sa to extract the target byte; avoids full SIMUL recomputation
1430 src_r0_0_31 = _permutex2var_epi8_sim_get32(wi_g1_p0, sa_g1_p0, data_src, data_src2);
1431 src_r0_32_63 = _permutex2var_epi8_sim_get32(wi_g2_p0, sa_g2_p0, data_src_2, data_src2_2);
1432 src_r1_0_31 = _permutex2var_epi8_sim_get32(wi_g1_p1, sa_g1_p1, data_src, data_src2);
1433 src_r1_32_63 = _permutex2var_epi8_sim_get32(wi_g2_p1, sa_g2_p1, data_src_2, data_src2_2);
1434 wi_g1_p0 = _mm512_add_epi16(wi_g1_p0, one_epi16);
1435 wi_g2_p0 = _mm512_add_epi16(wi_g2_p0, one_epi16);
1436 wi_g1_p1 = _mm512_add_epi16(wi_g1_p1, one_epi16);
1437 wi_g2_p1 = _mm512_add_epi16(wi_g2_p1, one_epi16);
1438 }
1439
1440 __m512i src_r0r1_0_31lo = _mm512_unpacklo_epi16(src_r0_0_31, src_r1_0_31);
1441 __m512i src_r0r1_0_31hi = _mm512_unpackhi_epi16(src_r0_0_31, src_r1_0_31);
1442 __m512i src_r0r1_32_63lo = _mm512_unpacklo_epi16(src_r0_32_63, src_r1_32_63);
1443 __m512i src_r0r1_32_63hi = _mm512_unpackhi_epi16(src_r0_32_63, src_r1_32_63);
1444
1445 if constexpr (UseVNNI)
1446 {
1447 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r0r1_0_31lo, _mm512_load_si512(current_coeff_SIMDw + 0));
1448 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r0r1_0_31hi, _mm512_load_si512(current_coeff_SIMDw + 1));
1449 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r0r1_32_63lo, _mm512_load_si512(current_coeff_SIMDw + 2));
1450 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r0r1_32_63hi, _mm512_load_si512(current_coeff_SIMDw + 3));
1451 }
1452 else
1453 {
1454 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, _mm512_load_si512(current_coeff_SIMDw + 0)), result_0_31lo);
1455 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, _mm512_load_si512(current_coeff_SIMDw + 1)), result_0_31hi);
1456 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63lo, _mm512_load_si512(current_coeff_SIMDw + 2)), result_32_63lo);
1457 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63hi, _mm512_load_si512(current_coeff_SIMDw + 3)), result_32_63hi);
1458 }
1459
1460 current_coeff_SIMDw += 4;
1461 }
1462
1463 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale8bits);
1464 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale8bits);
1465 result_32_63lo = _mm512_srai_epi32(result_32_63lo, FPScale8bits);
1466 result_32_63hi = _mm512_srai_epi32(result_32_63hi, FPScale8bits);
1467
1468 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
1469 __m512i result_32_63_int16 = _mm512_packus_epi32(result_32_63lo, result_32_63hi);
1470
1471 __m256i result_0_31_u8 = _mm512_cvtusepi16_epi8(result_0_31_int16);
1472 __m256i result_32_63_u8 = _mm512_cvtusepi16_epi8(result_32_63_int16);
1473
1474 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr), _mm512_inserti64x4(_mm512_castsi256_si512(result_0_31_u8), result_32_63_u8, 1));
1475
1476 dst_ptr += dst_pitch;
1477 src_ptr += src_pitch;
1478 src_ptr_2 += src_pitch;
1479 }
1480
1481 current_coeff_SIMD += filter_size_real * 2;
1482 };
1483
1484 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
1485 {
1486 do_h_integer_core(std::false_type{});
1487 }
1488
1489 for (; x < width; x += PIXELS_AT_A_TIME)
1490 {
1491 do_h_integer_core(std::true_type{});
1492 }
1493 }
1494 }
1495
1496 // filter size 8, pretransposed coefficients
1497 // 64 target uint8_t pixels at a time in 2 groups of 32
1498 // 2 groups of 128-byte source loads
1499 template<bool UseVNNI>
1500 void resize_h_planar_uint8_avx512_permutex_vstripe_2s32_ks8_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
1501 {
1502 constexpr int PIXELS_AT_A_TIME = 64;
1503
1504 const int width_safe_mod = (program->safelimit_128_pixels_each64th_target.overread_possible ? program->safelimit_128_pixels_each64th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
1505
1506 const int max_scanlines = program->max_scanlines;
1507
1508 __m512i rounder = _mm512_set1_epi32(1 << (FPScale8bits - 1));
1509
1510 for (int y_from = 0; y_from < height; y_from += max_scanlines)
1511 {
1512 int y_to = std::min(y_from + max_scanlines, height);
1513
1514 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
1515
1516 int x = 0;
1517
1518 auto do_h_integer_core = [&](auto partial_load) {
1519 const __m512i coef_r0r1_0_31lo = _mm512_load_si512(current_coeff_SIMD + 0);
1520 const __m512i coef_r0r1_0_31hi = _mm512_load_si512(current_coeff_SIMD + 1);
1521 const __m512i coef_r0r1_32_63lo = _mm512_load_si512(current_coeff_SIMD + 2);
1522 const __m512i coef_r0r1_32_63hi = _mm512_load_si512(current_coeff_SIMD + 3);
1523 const __m512i coef_r2r3_0_31lo = _mm512_load_si512(current_coeff_SIMD + 4);
1524 const __m512i coef_r2r3_0_31hi = _mm512_load_si512(current_coeff_SIMD + 5);
1525 const __m512i coef_r2r3_32_63lo = _mm512_load_si512(current_coeff_SIMD + 6);
1526 const __m512i coef_r2r3_32_63hi = _mm512_load_si512(current_coeff_SIMD + 7);
1527 const __m512i coef_r4r5_0_31lo = _mm512_load_si512(current_coeff_SIMD + 8);
1528 const __m512i coef_r4r5_0_31hi = _mm512_load_si512(current_coeff_SIMD + 9);
1529 const __m512i coef_r4r5_32_63lo = _mm512_load_si512(current_coeff_SIMD + 10);
1530 const __m512i coef_r4r5_32_63hi = _mm512_load_si512(current_coeff_SIMD + 11);
1531 const __m512i coef_r6r7_0_31lo = _mm512_load_si512(current_coeff_SIMD + 12);
1532 const __m512i coef_r6r7_0_31hi = _mm512_load_si512(current_coeff_SIMD + 13);
1533 const __m512i coef_r6r7_32_63lo = _mm512_load_si512(current_coeff_SIMD + 14);
1534 const __m512i coef_r6r7_32_63hi = _mm512_load_si512(current_coeff_SIMD + 15);
1535
1536 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
1537 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
1538 __m512i perm_0_32_47 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 32]));
1539 __m512i perm_0_48_63 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 48]));
1540
1541 int iStart = program->pixel_offset[x];
1542 int iStart_2 = program->pixel_offset[x + 32];
1543 __m512i m512i_Start = _mm512_set1_epi32(iStart);
1544 __m512i m512i_Start_2 = _mm512_set1_epi32(iStart_2);
1545
1546 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, m512i_Start);
1547 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, m512i_Start);
1548 perm_0_32_47 = _mm512_sub_epi32(perm_0_32_47, m512i_Start_2);
1549 perm_0_48_63 = _mm512_sub_epi32(perm_0_48_63, m512i_Start_2);
1550
1551 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
1552 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
1553 __m256i m256i_perm_0_32_47 = _mm512_cvtepi32_epi16(perm_0_32_47);
1554 __m256i m256i_perm_0_48_63 = _mm512_cvtepi32_epi16(perm_0_48_63);
1555
1556 __m128i mm128i_perm_0_0_15 = _mm256_cvtepi16_epi8(m256i_perm_0_0_15);
1557 __m128i mm128i_perm_0_16_31 = _mm256_cvtepi16_epi8(m256i_perm_0_16_31);
1558 __m128i mm128i_perm_0_32_47 = _mm256_cvtepi16_epi8(m256i_perm_0_32_47);
1559 __m128i mm128i_perm_0_48_63 = _mm256_cvtepi16_epi8(m256i_perm_0_48_63);
1560
1561 __m512i perm_0 = _mm512_inserti32x4(_mm512_castsi128_si512(mm128i_perm_0_0_15), mm128i_perm_0_16_31, 1);
1562 perm_0 = _mm512_inserti32x4(perm_0, mm128i_perm_0_32_47, 2);
1563 perm_0 = _mm512_inserti32x4(perm_0, mm128i_perm_0_48_63, 3);
1564
1565 uint8_t* AVS_RESTRICT dst_ptr = dst8 + x + y_from * dst_pitch;
1566 const uint8_t* src_ptr = src8 + iStart + y_from * src_pitch;
1567 const uint8_t* src_ptr_2 = src8 + iStart_2 + y_from * src_pitch;
1568
1569 const int remaining = program->source_size - iStart;
1570 const int remaining_2 = program->source_size - iStart_2;
1571 const __mmask64 k1 = _bzhi_u64(~0ULL, remaining);
1572 const __mmask64 k2 = _bzhi_u64(~0ULL, std::max(0, remaining - 64));
1573 const __mmask64 k1_2 = _bzhi_u64(~0ULL, remaining_2);
1574 const __mmask64 k2_2 = _bzhi_u64(~0ULL, std::max(0, remaining_2 - 64));
1575
1576 // perm_0+N for each kernel tap — loop-invariant, precomputed for both VBMI and BASE paths
1577 const __m512i pw_1 = _mm512_add_epi8(perm_0, _mm512_set1_epi8(1));
1578 const __m512i pw_2 = _mm512_add_epi8(perm_0, _mm512_set1_epi8(2));
1579 const __m512i pw_3 = _mm512_add_epi8(perm_0, _mm512_set1_epi8(3));
1580 const __m512i pw_4 = _mm512_add_epi8(perm_0, _mm512_set1_epi8(4));
1581 const __m512i pw_5 = _mm512_add_epi8(perm_0, _mm512_set1_epi8(5));
1582 const __m512i pw_6 = _mm512_add_epi8(perm_0, _mm512_set1_epi8(6));
1583 const __m512i pw_7 = _mm512_add_epi8(perm_0, _mm512_set1_epi8(7));
1584
1585 // BASE path only: precompute word_idx and shift_amt from each perm vector.
1586 // MSVC fails to hoist these from the y-loop even though perm_0 is invariant.
1587 __m512i wi_lo_0 = {}, sa_lo_0 = {}, wi_hi_0 = {}, sa_hi_0 = {};
1588 __m512i wi_lo_1 = {}, sa_lo_1 = {}, wi_hi_1 = {}, sa_hi_1 = {};
1589 __m512i wi_lo_2 = {}, sa_lo_2 = {}, wi_hi_2 = {}, sa_hi_2 = {};
1590 __m512i wi_lo_3 = {}, sa_lo_3 = {}, wi_hi_3 = {}, sa_hi_3 = {};
1591 __m512i wi_lo_4 = {}, sa_lo_4 = {}, wi_hi_4 = {}, sa_hi_4 = {};
1592 __m512i wi_lo_5 = {}, sa_lo_5 = {}, wi_hi_5 = {}, sa_hi_5 = {};
1593 __m512i wi_lo_6 = {}, sa_lo_6 = {}, wi_hi_6 = {}, sa_hi_6 = {};
1594 __m512i wi_lo_7 = {}, sa_lo_7 = {}, wi_hi_7 = {}, sa_hi_7 = {};
1595 if constexpr (!UseVNNI) {
1596 const __m512i c_8 = _mm512_set1_epi16(8);
1597 auto make_wi_sa = [&](const __m512i pw, __m512i &wi_lo, __m512i &sa_lo, __m512i &wi_hi, __m512i &sa_hi) {
1598 __m512i lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(pw));
1599 __m512i hi = _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(pw, 1));
1600 wi_lo = _mm512_srli_epi16(lo, 1);
1601 sa_lo = _mm512_and_si512(_mm512_slli_epi16(lo, 3), c_8);
1602 wi_hi = _mm512_srli_epi16(hi, 1);
1603 sa_hi = _mm512_and_si512(_mm512_slli_epi16(hi, 3), c_8);
1604 };
1605 make_wi_sa(perm_0, wi_lo_0, sa_lo_0, wi_hi_0, sa_hi_0);
1606 make_wi_sa(pw_1, wi_lo_1, sa_lo_1, wi_hi_1, sa_hi_1);
1607 make_wi_sa(pw_2, wi_lo_2, sa_lo_2, wi_hi_2, sa_hi_2);
1608 make_wi_sa(pw_3, wi_lo_3, sa_lo_3, wi_hi_3, sa_hi_3);
1609 make_wi_sa(pw_4, wi_lo_4, sa_lo_4, wi_hi_4, sa_hi_4);
1610 make_wi_sa(pw_5, wi_lo_5, sa_lo_5, wi_hi_5, sa_hi_5);
1611 make_wi_sa(pw_6, wi_lo_6, sa_lo_6, wi_hi_6, sa_hi_6);
1612 make_wi_sa(pw_7, wi_lo_7, sa_lo_7, wi_hi_7, sa_hi_7);
1613 }
1614
1615 // 512-bit vpermi2b + vextracti64x4 + vpunpcklw/hi saturated port 5 on Ice Lake
1616 // (56 port-5 uops/iter vs BASE's 32), costing ~8% vs the BASE simulation path.
1617 // Combined 256-bit indices eliminate extract+unpack, matching BASE at 32 port-5 uops.
1618 // Coefficient layout is unchanged; unpackbw+unpackqdq construction replicates the word
1619 // order of unpacklo/hi_epi16. Dual-AVX-512-pipe CPUs (Sapphire Rapids+) may not have
1620 // suffered this bottleneck due to higher port-5 throughput.
1621 __m256i comb_01_g1lo={}, comb_01_g1hi={}, comb_01_g2lo={}, comb_01_g2hi={};
1622 __m256i comb_23_g1lo={}, comb_23_g1hi={}, comb_23_g2lo={}, comb_23_g2hi={};
1623 __m256i comb_45_g1lo={}, comb_45_g1hi={}, comb_45_g2lo={}, comb_45_g2hi={};
1624 __m256i comb_67_g1lo={}, comb_67_g1hi={}, comb_67_g2lo={}, comb_67_g2hi={};
1625 if constexpr (UseVNNI) {
1626 auto make_combined = [&](__m256i pa, __m256i pb, __m256i& clo, __m256i& chi) {
1627 __m256i t0 = _mm256_unpacklo_epi8(pa, pb);
1628 __m256i t1 = _mm256_unpackhi_epi8(pa, pb);
1629 clo = _mm256_unpacklo_epi64(t0, t1);
1630 chi = _mm256_unpackhi_epi64(t0, t1);
1631 };
1632 __m256i p0g1 = _mm512_castsi512_si256(perm_0);
1633 __m256i p0g2 = _mm512_extracti64x4_epi64(perm_0, 1);
1634 // pw_N lower half is a free register alias; upper half = p0g2+N avoids 7 extra extracts
1635 make_combined(p0g1, _mm512_castsi512_si256(pw_1), comb_01_g1lo, comb_01_g1hi);
1636 make_combined(p0g2, _mm256_add_epi8(p0g2, _mm256_set1_epi8(1)), comb_01_g2lo, comb_01_g2hi);
1637 make_combined(_mm512_castsi512_si256(pw_2), _mm512_castsi512_si256(pw_3), comb_23_g1lo, comb_23_g1hi);
1638 make_combined(_mm256_add_epi8(p0g2, _mm256_set1_epi8(2)), _mm256_add_epi8(p0g2, _mm256_set1_epi8(3)), comb_23_g2lo, comb_23_g2hi);
1639 make_combined(_mm512_castsi512_si256(pw_4), _mm512_castsi512_si256(pw_5), comb_45_g1lo, comb_45_g1hi);
1640 make_combined(_mm256_add_epi8(p0g2, _mm256_set1_epi8(4)), _mm256_add_epi8(p0g2, _mm256_set1_epi8(5)), comb_45_g2lo, comb_45_g2hi);
1641 make_combined(_mm512_castsi512_si256(pw_6), _mm512_castsi512_si256(pw_7), comb_67_g1lo, comb_67_g1hi);
1642 make_combined(_mm256_add_epi8(p0g2, _mm256_set1_epi8(6)), _mm256_add_epi8(p0g2, _mm256_set1_epi8(7)), comb_67_g2lo, comb_67_g2hi);
1643 }
1644
1645 for (int y = y_from; y < y_to; y++)
1646 {
1647 __m512i data_src, data_src2, data_src_2, data_src2_2;
1648
1649 if constexpr (partial_load) {
1650 data_src = _mm512_maskz_loadu_epi8(k1, src_ptr);
1651 data_src2 = _mm512_maskz_loadu_epi8(k2, src_ptr + 64);
1652 data_src_2 = _mm512_maskz_loadu_epi8(k1_2, src_ptr_2);
1653 data_src2_2 = _mm512_maskz_loadu_epi8(k2_2, src_ptr_2 + 64);
1654 }
1655 else {
1656 data_src = _mm512_loadu_si512(src_ptr);
1657 data_src2 = _mm512_loadu_si512(src_ptr + 64);
1658 data_src_2 = _mm512_loadu_si512(src_ptr_2);
1659 data_src2_2 = _mm512_loadu_si512(src_ptr_2 + 64);
1660 }
1661
1662 __m512i src_r0r1_0_31lo, src_r0r1_0_31hi, src_r0r1_32_63lo, src_r0r1_32_63hi;
1663 __m512i src_r2r3_0_31lo, src_r2r3_0_31hi, src_r2r3_32_63lo, src_r2r3_32_63hi;
1664
1665 if constexpr (UseVNNI) {
1666 // 512-bit vpermi2b keeps the full 128-byte source window (256-bit vpermi2b only
1667 // addresses 64 bytes, corrupting pixels whose index falls in data_src[32..63]).
1668 // castsi256_si512 on index = free (upper 32 index bytes are undefined but only
1669 // affect output bytes 32-63 which are discarded by castsi512_si256 = also free).
1670 src_r0r1_0_31lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src, _mm512_castsi256_si512(comb_01_g1lo), data_src2)));
1671 src_r0r1_0_31hi = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src, _mm512_castsi256_si512(comb_01_g1hi), data_src2)));
1672 src_r0r1_32_63lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src_2, _mm512_castsi256_si512(comb_01_g2lo), data_src2_2)));
1673 src_r0r1_32_63hi = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src_2, _mm512_castsi256_si512(comb_01_g2hi), data_src2_2)));
1674 src_r2r3_0_31lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src, _mm512_castsi256_si512(comb_23_g1lo), data_src2)));
1675 src_r2r3_0_31hi = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src, _mm512_castsi256_si512(comb_23_g1hi), data_src2)));
1676 src_r2r3_32_63lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src_2, _mm512_castsi256_si512(comb_23_g2lo), data_src2_2)));
1677 src_r2r3_32_63hi = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src_2, _mm512_castsi256_si512(comb_23_g2hi), data_src2_2)));
1678 } else {
1679 // BASE: sim_get32 returns 32 16-bit words with gathered bytes in low 8 bits;
1680 // feed unpacklo/hi directly — avoids cvtepu8_epi16 round-trip
1681 __m512i d0l = _permutex2var_epi8_sim_get32(wi_lo_0, sa_lo_0, data_src, data_src2);
1682 __m512i d0h = _permutex2var_epi8_sim_get32(wi_hi_0, sa_hi_0, data_src_2, data_src2_2);
1683 __m512i d1l = _permutex2var_epi8_sim_get32(wi_lo_1, sa_lo_1, data_src, data_src2);
1684 __m512i d1h = _permutex2var_epi8_sim_get32(wi_hi_1, sa_hi_1, data_src_2, data_src2_2);
1685 __m512i d2l = _permutex2var_epi8_sim_get32(wi_lo_2, sa_lo_2, data_src, data_src2);
1686 __m512i d2h = _permutex2var_epi8_sim_get32(wi_hi_2, sa_hi_2, data_src_2, data_src2_2);
1687 __m512i d3l = _permutex2var_epi8_sim_get32(wi_lo_3, sa_lo_3, data_src, data_src2);
1688 __m512i d3h = _permutex2var_epi8_sim_get32(wi_hi_3, sa_hi_3, data_src_2, data_src2_2);
1689 src_r0r1_0_31lo = _mm512_unpacklo_epi16(d0l, d1l);
1690 src_r0r1_0_31hi = _mm512_unpackhi_epi16(d0l, d1l);
1691 src_r0r1_32_63lo = _mm512_unpacklo_epi16(d0h, d1h);
1692 src_r0r1_32_63hi = _mm512_unpackhi_epi16(d0h, d1h);
1693 src_r2r3_0_31lo = _mm512_unpacklo_epi16(d2l, d3l);
1694 src_r2r3_0_31hi = _mm512_unpackhi_epi16(d2l, d3l);
1695 src_r2r3_32_63lo = _mm512_unpacklo_epi16(d2h, d3h);
1696 src_r2r3_32_63hi = _mm512_unpackhi_epi16(d2h, d3h);
1697 }
1698
1699 __m512i result_0_31lo, result_0_31hi, result_32_63lo, result_32_63hi;
1700
1701 if constexpr (UseVNNI)
1702 {
1703 result_0_31lo = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31lo, coef_r0r1_0_31lo);
1704 result_0_31hi = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31hi, coef_r0r1_0_31hi);
1705 result_32_63lo = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63lo, coef_r0r1_32_63lo);
1706 result_32_63hi = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63hi, coef_r0r1_32_63hi);
1707 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r2r3_0_31lo, coef_r2r3_0_31lo);
1708 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r2r3_0_31hi, coef_r2r3_0_31hi);
1709 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r2r3_32_63lo, coef_r2r3_32_63lo);
1710 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r2r3_32_63hi, coef_r2r3_32_63hi);
1711 }
1712 else
1713 {
1714 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, coef_r0r1_0_31lo), _mm512_madd_epi16(src_r2r3_0_31lo, coef_r2r3_0_31lo));
1715 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, coef_r0r1_0_31hi), _mm512_madd_epi16(src_r2r3_0_31hi, coef_r2r3_0_31hi));
1716 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63lo, coef_r0r1_32_63lo), _mm512_madd_epi16(src_r2r3_32_63lo, coef_r2r3_32_63lo));
1717 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63hi, coef_r0r1_32_63hi), _mm512_madd_epi16(src_r2r3_32_63hi, coef_r2r3_32_63hi));
1718 }
1719
1720 __m512i src_r4r5_0_31lo, src_r4r5_0_31hi, src_r4r5_32_63lo, src_r4r5_32_63hi;
1721 __m512i src_r6r7_0_31lo, src_r6r7_0_31hi, src_r6r7_32_63lo, src_r6r7_32_63hi;
1722
1723 if constexpr (UseVNNI) {
1724 src_r4r5_0_31lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src, _mm512_castsi256_si512(comb_45_g1lo), data_src2)));
1725 src_r4r5_0_31hi = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src, _mm512_castsi256_si512(comb_45_g1hi), data_src2)));
1726 src_r4r5_32_63lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src_2, _mm512_castsi256_si512(comb_45_g2lo), data_src2_2)));
1727 src_r4r5_32_63hi = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src_2, _mm512_castsi256_si512(comb_45_g2hi), data_src2_2)));
1728 src_r6r7_0_31lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src, _mm512_castsi256_si512(comb_67_g1lo), data_src2)));
1729 src_r6r7_0_31hi = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src, _mm512_castsi256_si512(comb_67_g1hi), data_src2)));
1730 src_r6r7_32_63lo = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src_2, _mm512_castsi256_si512(comb_67_g2lo), data_src2_2)));
1731 src_r6r7_32_63hi = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(_mm512_permutex2var_epi8(data_src_2, _mm512_castsi256_si512(comb_67_g2hi), data_src2_2)));
1732 } else {
1733 __m512i d4l = _permutex2var_epi8_sim_get32(wi_lo_4, sa_lo_4, data_src, data_src2);
1734 __m512i d4h = _permutex2var_epi8_sim_get32(wi_hi_4, sa_hi_4, data_src_2, data_src2_2);
1735 __m512i d5l = _permutex2var_epi8_sim_get32(wi_lo_5, sa_lo_5, data_src, data_src2);
1736 __m512i d5h = _permutex2var_epi8_sim_get32(wi_hi_5, sa_hi_5, data_src_2, data_src2_2);
1737 __m512i d6l = _permutex2var_epi8_sim_get32(wi_lo_6, sa_lo_6, data_src, data_src2);
1738 __m512i d6h = _permutex2var_epi8_sim_get32(wi_hi_6, sa_hi_6, data_src_2, data_src2_2);
1739 __m512i d7l = _permutex2var_epi8_sim_get32(wi_lo_7, sa_lo_7, data_src, data_src2);
1740 __m512i d7h = _permutex2var_epi8_sim_get32(wi_hi_7, sa_hi_7, data_src_2, data_src2_2);
1741 src_r4r5_0_31lo = _mm512_unpacklo_epi16(d4l, d5l);
1742 src_r4r5_0_31hi = _mm512_unpackhi_epi16(d4l, d5l);
1743 src_r4r5_32_63lo = _mm512_unpacklo_epi16(d4h, d5h);
1744 src_r4r5_32_63hi = _mm512_unpackhi_epi16(d4h, d5h);
1745 src_r6r7_0_31lo = _mm512_unpacklo_epi16(d6l, d7l);
1746 src_r6r7_0_31hi = _mm512_unpackhi_epi16(d6l, d7l);
1747 src_r6r7_32_63lo = _mm512_unpacklo_epi16(d6h, d7h);
1748 src_r6r7_32_63hi = _mm512_unpackhi_epi16(d6h, d7h);
1749 }
1750
1751 if constexpr (UseVNNI)
1752 {
1753 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r4r5_0_31lo, coef_r4r5_0_31lo);
1754 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r4r5_0_31hi, coef_r4r5_0_31hi);
1755 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r4r5_32_63lo, coef_r4r5_32_63lo);
1756 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r4r5_32_63hi, coef_r4r5_32_63hi);
1757 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r6r7_0_31lo, coef_r6r7_0_31lo);
1758 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r6r7_0_31hi, coef_r6r7_0_31hi);
1759 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r6r7_32_63lo, coef_r6r7_32_63lo);
1760 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r6r7_32_63hi, coef_r6r7_32_63hi);
1761 }
1762 else
1763 {
1764 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_0_31lo, coef_r4r5_0_31lo), result_0_31lo);
1765 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_0_31hi, coef_r4r5_0_31hi), result_0_31hi);
1766 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_32_63lo, coef_r4r5_32_63lo), result_32_63lo);
1767 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_32_63hi, coef_r4r5_32_63hi), result_32_63hi);
1768 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_0_31lo, coef_r6r7_0_31lo), result_0_31lo);
1769 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_0_31hi, coef_r6r7_0_31hi), result_0_31hi);
1770 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_32_63lo, coef_r6r7_32_63lo), result_32_63lo);
1771 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_32_63hi, coef_r6r7_32_63hi), result_32_63hi);
1772
1773 result_0_31lo = _mm512_add_epi32(result_0_31lo, rounder);
1774 result_0_31hi = _mm512_add_epi32(result_0_31hi, rounder);
1775 result_32_63lo = _mm512_add_epi32(result_32_63lo, rounder);
1776 result_32_63hi = _mm512_add_epi32(result_32_63hi, rounder);
1777 }
1778
1779 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale8bits);
1780 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale8bits);
1781 result_32_63lo = _mm512_srai_epi32(result_32_63lo, FPScale8bits);
1782 result_32_63hi = _mm512_srai_epi32(result_32_63hi, FPScale8bits);
1783
1784 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
1785 __m512i result_32_63_int16 = _mm512_packus_epi32(result_32_63lo, result_32_63hi);
1786
1787 __m256i result_0_31_u8 = _mm512_cvtusepi16_epi8(result_0_31_int16);
1788 __m256i result_32_63_u8 = _mm512_cvtusepi16_epi8(result_32_63_int16);
1789
1790 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr), _mm512_inserti64x4(_mm512_castsi256_si512(result_0_31_u8), result_32_63_u8, 1));
1791
1792 dst_ptr += dst_pitch;
1793 src_ptr += src_pitch;
1794 src_ptr_2 += src_pitch;
1795 }
1796
1797 current_coeff_SIMD += 16;
1798 };
1799
1800 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
1801 {
1802 do_h_integer_core(std::false_type{});
1803 }
1804
1805 for (; x < width; x += PIXELS_AT_A_TIME)
1806 {
1807 do_h_integer_core(std::true_type{});
1808 }
1809 }
1810 }
1811
1812 // filter size up to 4, pretransposed coefficients
1813 // 64 target uint16_t pixels at a time in 2 groups of 32
1814 template<bool lessthan16bit, bool UseVNNI>
1815 void resize_h_planar_uint16_avx512_permutex_vstripe_mp_2s32_ks4_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
1816 {
1817 const uint16_t* src = (uint16_t*)src8;
1818 uint16_t* AVS_RESTRICT dst = (uint16_t* AVS_RESTRICT)dst8;
1819 dst_pitch = dst_pitch / sizeof(uint16_t);
1820 src_pitch = src_pitch / sizeof(uint16_t);
1821
1822 constexpr int PIXELS_AT_A_TIME = 64;
1823
1824 const int width_safe_mod = (program->safelimit_64_pixels_each32th_target.overread_possible ? program->safelimit_64_pixels_each32th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
1825
1826 assert(program->filter_size_real <= 4);
1827 assert(program->target_size_alignment >= 64);
1828 assert(FRAME_ALIGN >= 64);
1829 assert(program->filter_size_alignment >= 4);
1830
1831 const int max_scanlines = program->max_scanlines;
1832
1833 const __m512i shifttosigned = _mm512_set1_epi16(-32768);
1834 const __m512i shiftfromsigned = _mm512_set1_epi32(32768 << FPScale16bits);
1835
1836 const int limit = (1 << bits_per_pixel) - 1;
1837 __m512i clamp_limit = _mm512_set1_epi16((short)limit);
1838 __m512i rounder = _mm512_set1_epi32(1 << (FPScale16bits - 1));
1839
1840 for (int y_from = 0; y_from < height; y_from += max_scanlines)
1841 {
1842 int y_to = std::min(y_from + max_scanlines, height);
1843
1844 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
1845
1846 int x = 0;
1847
1848 auto do_h_integer_core = [&](auto partial_load) {
1849 __m512i one_epi16 = _mm512_set1_epi16(1);
1850
1851 const __m512i coef_r0r1_0_31lo = _mm512_load_si512(current_coeff_SIMD + 0);
1852 const __m512i coef_r0r1_0_31hi = _mm512_load_si512(current_coeff_SIMD + 1);
1853 const __m512i coef_r0r1_32_63lo = _mm512_load_si512(current_coeff_SIMD + 2);
1854 const __m512i coef_r0r1_32_63hi = _mm512_load_si512(current_coeff_SIMD + 3);
1855 const __m512i coef_r2r3_0_31lo = _mm512_load_si512(current_coeff_SIMD + 4);
1856 const __m512i coef_r2r3_0_31hi = _mm512_load_si512(current_coeff_SIMD + 5);
1857 const __m512i coef_r2r3_32_63lo = _mm512_load_si512(current_coeff_SIMD + 6);
1858 const __m512i coef_r2r3_32_63hi = _mm512_load_si512(current_coeff_SIMD + 7);
1859
1860 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
1861 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
1862 __m512i perm_0_32_47 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 32]));
1863 __m512i perm_0_48_63 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 48]));
1864
1865 int iStart_0_31 = program->pixel_offset[x];
1866 int iStart_32_63 = program->pixel_offset[x + 32];
1867 __m512i m512i_Start_0_31 = _mm512_set1_epi32(iStart_0_31);
1868 __m512i m512i_Start_32_63 = _mm512_set1_epi32(iStart_32_63);
1869
1870 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, m512i_Start_0_31);
1871 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, m512i_Start_0_31);
1872 perm_0_32_47 = _mm512_sub_epi32(perm_0_32_47, m512i_Start_32_63);
1873 perm_0_48_63 = _mm512_sub_epi32(perm_0_48_63, m512i_Start_32_63);
1874
1875 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
1876 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
1877 __m256i m256i_perm_0_32_47 = _mm512_cvtepi32_epi16(perm_0_32_47);
1878 __m256i m256i_perm_0_48_63 = _mm512_cvtepi32_epi16(perm_0_48_63);
1879
1880 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
1881 __m512i perm_0_32_63 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_32_47), m256i_perm_0_48_63, 1);
1882
1883 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
1884 __m512i perm_1_32_63 = _mm512_add_epi16(perm_0_32_63, one_epi16);
1885
1886 const __m512i perm_r0r1_0_31lo = _mm512_unpacklo_epi16(perm_0_0_31, perm_1_0_31);
1887 const __m512i perm_r0r1_0_31hi = _mm512_unpackhi_epi16(perm_0_0_31, perm_1_0_31);
1888 const __m512i perm_r0r1_32_63lo = _mm512_unpacklo_epi16(perm_0_32_63, perm_1_32_63);
1889 const __m512i perm_r0r1_32_63hi = _mm512_unpackhi_epi16(perm_0_32_63, perm_1_32_63);
1890
1891 const __m512i two_epi16 = _mm512_set1_epi16(2);
1892
1893 uint16_t* AVS_RESTRICT dst_ptr = dst + x + y_from * dst_pitch;
1894 const uint16_t* src_ptr_0_31 = src + iStart_0_31 + y_from * src_pitch;
1895 const uint16_t* src_ptr_32_63 = src + iStart_32_63 + y_from * src_pitch;
1896
1897 const int remaining_0_31 = program->source_size - iStart_0_31;
1898 const __mmask32 k1_0_31 = _bzhi_u32(~0UL, remaining_0_31);
1899 const __mmask32 k2_0_31 = _bzhi_u32(~0UL, remaining_0_31 - 32);
1900 const int remaining_32_63 = program->source_size - iStart_32_63;
1901 const __mmask32 k1_32_63 = _bzhi_u32(~0UL, remaining_32_63);
1902 const __mmask32 k2_32_63 = _bzhi_u32(~0UL, remaining_32_63 - 32);
1903
1904 for (int y = y_from; y < y_to; y++)
1905 {
1906 __m512i data_src_0_31, data_src2_0_31, data_src_32_63, data_src2_32_63;
1907
1908 __m512i perm_rNrNp1_0_31lo = perm_r0r1_0_31lo;
1909 __m512i perm_rNrNp1_0_31hi = perm_r0r1_0_31hi;
1910 __m512i perm_rNrNp1_32_63lo = perm_r0r1_32_63lo;
1911 __m512i perm_rNrNp1_32_63hi = perm_r0r1_32_63hi;
1912
1913 if constexpr (partial_load) {
1914 data_src_0_31 = _mm512_maskz_loadu_epi16(k1_0_31, src_ptr_0_31);
1915 data_src2_0_31 = _mm512_maskz_loadu_epi16(k2_0_31, src_ptr_0_31 + 32);
1916 data_src_32_63 = _mm512_maskz_loadu_epi16(k1_32_63, src_ptr_32_63);
1917 data_src2_32_63 = _mm512_maskz_loadu_epi16(k2_32_63, src_ptr_32_63 + 32);
1918 }
1919 else {
1920 data_src_0_31 = _mm512_loadu_si512(src_ptr_0_31);
1921 data_src2_0_31 = _mm512_loadu_si512(src_ptr_0_31 + 32);
1922 data_src_32_63 = _mm512_loadu_si512(src_ptr_32_63);
1923 data_src2_32_63 = _mm512_loadu_si512(src_ptr_32_63 + 32);
1924 }
1925
1926 __m512i src_r0r1_0_31lo = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31lo, data_src2_0_31);
1927 __m512i src_r0r1_0_31hi = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31hi, data_src2_0_31);
1928 __m512i src_r0r1_32_63lo = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63lo, data_src2_32_63);
1929 __m512i src_r0r1_32_63hi = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63hi, data_src2_32_63);
1930
1931 // for r2r3
1932 perm_rNrNp1_0_31lo = _mm512_add_epi16(perm_rNrNp1_0_31lo, two_epi16);
1933 perm_rNrNp1_0_31hi = _mm512_add_epi16(perm_rNrNp1_0_31hi, two_epi16);
1934 perm_rNrNp1_32_63lo = _mm512_add_epi16(perm_rNrNp1_32_63lo, two_epi16);
1935 perm_rNrNp1_32_63hi = _mm512_add_epi16(perm_rNrNp1_32_63hi, two_epi16);
1936
1937 if constexpr (!lessthan16bit) {
1938 src_r0r1_0_31lo = _mm512_add_epi16(src_r0r1_0_31lo, shifttosigned);
1939 src_r0r1_0_31hi = _mm512_add_epi16(src_r0r1_0_31hi, shifttosigned);
1940 src_r0r1_32_63lo = _mm512_add_epi16(src_r0r1_32_63lo, shifttosigned);
1941 src_r0r1_32_63hi = _mm512_add_epi16(src_r0r1_32_63hi, shifttosigned);
1942 }
1943
1944 __m512i result_0_31lo, result_0_31hi, result_32_63lo, result_32_63hi;
1945
1946 if constexpr (UseVNNI)
1947 {
1948 result_0_31lo = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31lo, coef_r0r1_0_31lo);
1949 result_0_31hi = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31hi, coef_r0r1_0_31hi);
1950 result_32_63lo = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63lo, coef_r0r1_32_63lo);
1951 result_32_63hi = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63hi, coef_r0r1_32_63hi);
1952 }
1953 else
1954 {
1955 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, coef_r0r1_0_31lo), rounder);
1956 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, coef_r0r1_0_31hi), rounder);
1957 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63lo, coef_r0r1_32_63lo), rounder);
1958 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63hi, coef_r0r1_32_63hi), rounder);
1959 }
1960
1961 __m512i src_r2r3_0_31lo = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31lo, data_src2_0_31);
1962 __m512i src_r2r3_0_31hi = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31hi, data_src2_0_31);
1963 __m512i src_r2r3_32_63lo = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63lo, data_src2_32_63);
1964 __m512i src_r2r3_32_63hi = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63hi, data_src2_32_63);
1965
1966 if constexpr (!lessthan16bit) {
1967 src_r2r3_0_31lo = _mm512_add_epi16(src_r2r3_0_31lo, shifttosigned);
1968 src_r2r3_0_31hi = _mm512_add_epi16(src_r2r3_0_31hi, shifttosigned);
1969 src_r2r3_32_63lo = _mm512_add_epi16(src_r2r3_32_63lo, shifttosigned);
1970 src_r2r3_32_63hi = _mm512_add_epi16(src_r2r3_32_63hi, shifttosigned);
1971 }
1972
1973 if constexpr (UseVNNI)
1974 {
1975 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r2r3_0_31lo, coef_r2r3_0_31lo);
1976 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r2r3_0_31hi, coef_r2r3_0_31hi);
1977 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r2r3_32_63lo, coef_r2r3_32_63lo);
1978 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r2r3_32_63hi, coef_r2r3_32_63hi);
1979 }
1980 else
1981 {
1982 result_0_31lo = _mm512_add_epi32(result_0_31lo, _mm512_madd_epi16(src_r2r3_0_31lo, coef_r2r3_0_31lo));
1983 result_0_31hi = _mm512_add_epi32(result_0_31hi, _mm512_madd_epi16(src_r2r3_0_31hi, coef_r2r3_0_31hi));
1984 result_32_63lo = _mm512_add_epi32(result_32_63lo, _mm512_madd_epi16(src_r2r3_32_63lo, coef_r2r3_32_63lo));
1985 result_32_63hi = _mm512_add_epi32(result_32_63hi, _mm512_madd_epi16(src_r2r3_32_63hi, coef_r2r3_32_63hi));
1986 }
1987
1988 if constexpr (!lessthan16bit) {
1989 result_0_31lo = _mm512_add_epi32(result_0_31lo, shiftfromsigned);
1990 result_0_31hi = _mm512_add_epi32(result_0_31hi, shiftfromsigned);
1991 result_32_63lo = _mm512_add_epi32(result_32_63lo, shiftfromsigned);
1992 result_32_63hi = _mm512_add_epi32(result_32_63hi, shiftfromsigned);
1993 }
1994
1995 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale16bits);
1996 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale16bits);
1997 result_32_63lo = _mm512_srai_epi32(result_32_63lo, FPScale16bits);
1998 result_32_63hi = _mm512_srai_epi32(result_32_63hi, FPScale16bits);
1999
2000 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
2001 __m512i result_32_63_int16 = _mm512_packus_epi32(result_32_63lo, result_32_63hi);
2002
2003 if constexpr (lessthan16bit) {
2004 result_0_31_int16 = _mm512_min_epu16(result_0_31_int16, clamp_limit);
2005 result_32_63_int16 = _mm512_min_epu16(result_32_63_int16, clamp_limit);
2006 }
2007
2008 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr), result_0_31_int16);
2009
2010 const int w_mod32 = width / 32 * 32;
2011 if (x < (w_mod32 - 32))
2012 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr + 32), result_32_63_int16);
2013
2014 dst_ptr += dst_pitch;
2015 src_ptr_0_31 += src_pitch;
2016 src_ptr_32_63 += src_pitch;
2017 }
2018
2019 current_coeff_SIMD += 8;
2020 };
2021
2022 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
2023 {
2024 do_h_integer_core(std::false_type{});
2025 }
2026
2027 for (; x < width; x += PIXELS_AT_A_TIME)
2028 {
2029 do_h_integer_core(std::true_type{});
2030 }
2031 }
2032 }
2033
2034 template<bool lessthan16bit, bool UseVNNI>
2035 void resize_h_planar_uint16_avx512_permutex_vstripe_mp_2s32_ks8_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
2036 {
2037 const uint16_t* src = (uint16_t*)src8;
2038 uint16_t* AVS_RESTRICT dst = (uint16_t* AVS_RESTRICT)dst8;
2039 dst_pitch = dst_pitch / sizeof(uint16_t);
2040 src_pitch = src_pitch / sizeof(uint16_t);
2041
2042 constexpr int PIXELS_AT_A_TIME = 64;
2043
2044 const int width_safe_mod = (program->safelimit_64_pixels_each32th_target.overread_possible ? program->safelimit_64_pixels_each32th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
2045
2046 const int max_scanlines = program->max_scanlines;
2047
2048 const __m512i shifttosigned = _mm512_set1_epi16(-32768);
2049 const __m512i shiftfromsigned = _mm512_set1_epi32(32768 << FPScale16bits);
2050
2051 const int limit = (1 << bits_per_pixel) - 1;
2052 __m512i clamp_limit = _mm512_set1_epi16((short)limit);
2053 __m512i rounder = _mm512_set1_epi32(1 << (FPScale16bits - 1));
2054
2055 for (int y_from = 0; y_from < height; y_from += max_scanlines)
2056 {
2057 int y_to = std::min(y_from + max_scanlines, height);
2058
2059 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
2060
2061 int x = 0;
2062
2063 auto do_h_integer_core = [&](auto partial_load) {
2064 __m512i one_epi16 = _mm512_set1_epi16(1);
2065
2066 const __m512i coef_r0r1_0_31lo = _mm512_load_si512(current_coeff_SIMD + 0);
2067 const __m512i coef_r0r1_0_31hi = _mm512_load_si512(current_coeff_SIMD + 1);
2068 const __m512i coef_r0r1_32_63lo = _mm512_load_si512(current_coeff_SIMD + 2);
2069 const __m512i coef_r0r1_32_63hi = _mm512_load_si512(current_coeff_SIMD + 3);
2070 const __m512i coef_r2r3_0_31lo = _mm512_load_si512(current_coeff_SIMD + 4);
2071 const __m512i coef_r2r3_0_31hi = _mm512_load_si512(current_coeff_SIMD + 5);
2072 const __m512i coef_r2r3_32_63lo = _mm512_load_si512(current_coeff_SIMD + 6);
2073 const __m512i coef_r2r3_32_63hi = _mm512_load_si512(current_coeff_SIMD + 7);
2074 const __m512i coef_r4r5_0_31lo = _mm512_load_si512(current_coeff_SIMD + 8);
2075 const __m512i coef_r4r5_0_31hi = _mm512_load_si512(current_coeff_SIMD + 9);
2076 const __m512i coef_r4r5_32_63lo = _mm512_load_si512(current_coeff_SIMD + 10);
2077 const __m512i coef_r4r5_32_63hi = _mm512_load_si512(current_coeff_SIMD + 11);
2078 const __m512i coef_r6r7_0_31lo = _mm512_load_si512(current_coeff_SIMD + 12);
2079 const __m512i coef_r6r7_0_31hi = _mm512_load_si512(current_coeff_SIMD + 13);
2080 const __m512i coef_r6r7_32_63lo = _mm512_load_si512(current_coeff_SIMD + 14);
2081 const __m512i coef_r6r7_32_63hi = _mm512_load_si512(current_coeff_SIMD + 15);
2082
2083 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
2084 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
2085 __m512i perm_0_32_47 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 32]));
2086 __m512i perm_0_48_63 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 48]));
2087
2088 int iStart_0_31 = program->pixel_offset[x];
2089 int iStart_32_63 = program->pixel_offset[x + 32];
2090 __m512i m512i_Start_0_31 = _mm512_set1_epi32(iStart_0_31);
2091 __m512i m512i_Start_32_63 = _mm512_set1_epi32(iStart_32_63);
2092
2093 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, m512i_Start_0_31);
2094 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, m512i_Start_0_31);
2095 perm_0_32_47 = _mm512_sub_epi32(perm_0_32_47, m512i_Start_32_63);
2096 perm_0_48_63 = _mm512_sub_epi32(perm_0_48_63, m512i_Start_32_63);
2097
2098 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
2099 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
2100 __m256i m256i_perm_0_32_47 = _mm512_cvtepi32_epi16(perm_0_32_47);
2101 __m256i m256i_perm_0_48_63 = _mm512_cvtepi32_epi16(perm_0_48_63);
2102
2103 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
2104 __m512i perm_0_32_63 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_32_47), m256i_perm_0_48_63, 1);
2105
2106 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
2107 __m512i perm_1_32_63 = _mm512_add_epi16(perm_0_32_63, one_epi16);
2108
2109 const __m512i perm_r0r1_0_31lo = _mm512_unpacklo_epi16(perm_0_0_31, perm_1_0_31);
2110 const __m512i perm_r0r1_0_31hi = _mm512_unpackhi_epi16(perm_0_0_31, perm_1_0_31);
2111 const __m512i perm_r0r1_32_63lo = _mm512_unpacklo_epi16(perm_0_32_63, perm_1_32_63);
2112 const __m512i perm_r0r1_32_63hi = _mm512_unpackhi_epi16(perm_0_32_63, perm_1_32_63);
2113
2114 const __m512i two_epi16 = _mm512_set1_epi16(2);
2115
2116 uint16_t* AVS_RESTRICT dst_ptr = dst + x + y_from * dst_pitch;
2117 const uint16_t* src_ptr_0_31 = src + iStart_0_31 + y_from * src_pitch;
2118 const uint16_t* src_ptr_32_63 = src + iStart_32_63 + y_from * src_pitch;
2119
2120 const int remaining_0_31 = program->source_size - iStart_0_31;
2121 const __mmask32 k1_0_31 = _bzhi_u32(~0UL, remaining_0_31);
2122 const __mmask32 k2_0_31 = _bzhi_u32(~0UL, remaining_0_31 - 32);
2123 const int remaining_32_63 = program->source_size - iStart_32_63;
2124 const __mmask32 k1_32_63 = _bzhi_u32(~0UL, remaining_32_63);
2125 const __mmask32 k2_32_63 = _bzhi_u32(~0UL, remaining_32_63 - 32);
2126
2127 for (int y = y_from; y < y_to; y++)
2128 {
2129 __m512i data_src_0_31, data_src2_0_31, data_src_32_63, data_src2_32_63;
2130
2131 __m512i perm_rNrNp1_0_31lo = perm_r0r1_0_31lo;
2132 __m512i perm_rNrNp1_0_31hi = perm_r0r1_0_31hi;
2133 __m512i perm_rNrNp1_32_63lo = perm_r0r1_32_63lo;
2134 __m512i perm_rNrNp1_32_63hi = perm_r0r1_32_63hi;
2135
2136 if constexpr (partial_load) {
2137 data_src_0_31 = _mm512_maskz_loadu_epi16(k1_0_31, src_ptr_0_31);
2138 data_src2_0_31 = _mm512_maskz_loadu_epi16(k2_0_31, src_ptr_0_31 + 32);
2139 data_src_32_63 = _mm512_maskz_loadu_epi16(k1_32_63, src_ptr_32_63);
2140 data_src2_32_63 = _mm512_maskz_loadu_epi16(k2_32_63, src_ptr_32_63 + 32);
2141 }
2142 else {
2143 data_src_0_31 = _mm512_loadu_si512(src_ptr_0_31);
2144 data_src2_0_31 = _mm512_loadu_si512(src_ptr_0_31 + 32);
2145 data_src_32_63 = _mm512_loadu_si512(src_ptr_32_63);
2146 data_src2_32_63 = _mm512_loadu_si512(src_ptr_32_63 + 32);
2147 }
2148
2149 __m512i result_0_31lo, result_0_31hi, result_32_63lo, result_32_63hi;
2150
2151 __m512i src_r0r1_0_31lo = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31lo, data_src2_0_31);
2152 __m512i src_r0r1_0_31hi = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31hi, data_src2_0_31);
2153 __m512i src_r0r1_32_63lo = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63lo, data_src2_32_63);
2154 __m512i src_r0r1_32_63hi = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63hi, data_src2_32_63);
2155
2156 perm_rNrNp1_0_31lo = _mm512_add_epi16(perm_rNrNp1_0_31lo, two_epi16);
2157 perm_rNrNp1_0_31hi = _mm512_add_epi16(perm_rNrNp1_0_31hi, two_epi16);
2158 perm_rNrNp1_32_63lo = _mm512_add_epi16(perm_rNrNp1_32_63lo, two_epi16);
2159 perm_rNrNp1_32_63hi = _mm512_add_epi16(perm_rNrNp1_32_63hi, two_epi16);
2160
2161 if constexpr (!lessthan16bit) {
2162 src_r0r1_0_31lo = _mm512_add_epi16(src_r0r1_0_31lo, shifttosigned);
2163 src_r0r1_0_31hi = _mm512_add_epi16(src_r0r1_0_31hi, shifttosigned);
2164 src_r0r1_32_63lo = _mm512_add_epi16(src_r0r1_32_63lo, shifttosigned);
2165 src_r0r1_32_63hi = _mm512_add_epi16(src_r0r1_32_63hi, shifttosigned);
2166 }
2167
2168 if constexpr (UseVNNI)
2169 {
2170 result_0_31lo = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31lo, coef_r0r1_0_31lo);
2171 result_0_31hi = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31hi, coef_r0r1_0_31hi);
2172 result_32_63lo = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63lo, coef_r0r1_32_63lo);
2173 result_32_63hi = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63hi, coef_r0r1_32_63hi);
2174 }
2175 else
2176 {
2177 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, coef_r0r1_0_31lo), rounder);
2178 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, coef_r0r1_0_31hi), rounder);
2179 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63lo, coef_r0r1_32_63lo), rounder);
2180 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63hi, coef_r0r1_32_63hi), rounder);
2181 }
2182
2183 __m512i src_r2r3_0_31lo = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31lo, data_src2_0_31);
2184 __m512i src_r2r3_0_31hi = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31hi, data_src2_0_31);
2185 __m512i src_r2r3_32_63lo = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63lo, data_src2_32_63);
2186 __m512i src_r2r3_32_63hi = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63hi, data_src2_32_63);
2187
2188 perm_rNrNp1_0_31lo = _mm512_add_epi16(perm_rNrNp1_0_31lo, two_epi16);
2189 perm_rNrNp1_0_31hi = _mm512_add_epi16(perm_rNrNp1_0_31hi, two_epi16);
2190 perm_rNrNp1_32_63lo = _mm512_add_epi16(perm_rNrNp1_32_63lo, two_epi16);
2191 perm_rNrNp1_32_63hi = _mm512_add_epi16(perm_rNrNp1_32_63hi, two_epi16);
2192
2193 if constexpr (!lessthan16bit) {
2194 src_r2r3_0_31lo = _mm512_add_epi16(src_r2r3_0_31lo, shifttosigned);
2195 src_r2r3_0_31hi = _mm512_add_epi16(src_r2r3_0_31hi, shifttosigned);
2196 src_r2r3_32_63lo = _mm512_add_epi16(src_r2r3_32_63lo, shifttosigned);
2197 src_r2r3_32_63hi = _mm512_add_epi16(src_r2r3_32_63hi, shifttosigned);
2198 }
2199
2200 if constexpr (UseVNNI)
2201 {
2202 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r2r3_0_31lo, coef_r2r3_0_31lo);
2203 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r2r3_0_31hi, coef_r2r3_0_31hi);
2204 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r2r3_32_63lo, coef_r2r3_32_63lo);
2205 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r2r3_32_63hi, coef_r2r3_32_63hi);
2206 }
2207 else
2208 {
2209 result_0_31lo = _mm512_add_epi32(result_0_31lo, _mm512_madd_epi16(src_r2r3_0_31lo, coef_r2r3_0_31lo));
2210 result_0_31hi = _mm512_add_epi32(result_0_31hi, _mm512_madd_epi16(src_r2r3_0_31hi, coef_r2r3_0_31hi));
2211 result_32_63lo = _mm512_add_epi32(result_32_63lo, _mm512_madd_epi16(src_r2r3_32_63lo, coef_r2r3_32_63lo));
2212 result_32_63hi = _mm512_add_epi32(result_32_63hi, _mm512_madd_epi16(src_r2r3_32_63hi, coef_r2r3_32_63hi));
2213 }
2214
2215 __m512i src_r4r5_0_31lo = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31lo, data_src2_0_31);
2216 __m512i src_r4r5_0_31hi = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31hi, data_src2_0_31);
2217 __m512i src_r4r5_32_63lo = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63lo, data_src2_32_63);
2218 __m512i src_r4r5_32_63hi = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63hi, data_src2_32_63);
2219
2220 perm_rNrNp1_0_31lo = _mm512_add_epi16(perm_rNrNp1_0_31lo, two_epi16);
2221 perm_rNrNp1_0_31hi = _mm512_add_epi16(perm_rNrNp1_0_31hi, two_epi16);
2222 perm_rNrNp1_32_63lo = _mm512_add_epi16(perm_rNrNp1_32_63lo, two_epi16);
2223 perm_rNrNp1_32_63hi = _mm512_add_epi16(perm_rNrNp1_32_63hi, two_epi16);
2224
2225 if constexpr (!lessthan16bit) {
2226 src_r4r5_0_31lo = _mm512_add_epi16(src_r4r5_0_31lo, shifttosigned);
2227 src_r4r5_0_31hi = _mm512_add_epi16(src_r4r5_0_31hi, shifttosigned);
2228 src_r4r5_32_63lo = _mm512_add_epi16(src_r4r5_32_63lo, shifttosigned);
2229 src_r4r5_32_63hi = _mm512_add_epi16(src_r4r5_32_63hi, shifttosigned);
2230 }
2231
2232 if constexpr (UseVNNI)
2233 {
2234 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r4r5_0_31lo, coef_r4r5_0_31lo);
2235 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r4r5_0_31hi, coef_r4r5_0_31hi);
2236 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r4r5_32_63lo, coef_r4r5_32_63lo);
2237 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r4r5_32_63hi, coef_r4r5_32_63hi);
2238 }
2239 else
2240 {
2241 result_0_31lo = _mm512_add_epi32(result_0_31lo, _mm512_madd_epi16(src_r4r5_0_31lo, coef_r4r5_0_31lo));
2242 result_0_31hi = _mm512_add_epi32(result_0_31hi, _mm512_madd_epi16(src_r4r5_0_31hi, coef_r4r5_0_31hi));
2243 result_32_63lo = _mm512_add_epi32(result_32_63lo, _mm512_madd_epi16(src_r4r5_32_63lo, coef_r4r5_32_63lo));
2244 result_32_63hi = _mm512_add_epi32(result_32_63hi, _mm512_madd_epi16(src_r4r5_32_63hi, coef_r4r5_32_63hi));
2245 }
2246
2247 __m512i src_r6r7_0_31lo = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31lo, data_src2_0_31);
2248 __m512i src_r6r7_0_31hi = _mm512_permutex2var_epi16(data_src_0_31, perm_rNrNp1_0_31hi, data_src2_0_31);
2249 __m512i src_r6r7_32_63lo = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63lo, data_src2_32_63);
2250 __m512i src_r6r7_32_63hi = _mm512_permutex2var_epi16(data_src_32_63, perm_rNrNp1_32_63hi, data_src2_32_63);
2251
2252 if constexpr (!lessthan16bit) {
2253 src_r6r7_0_31lo = _mm512_add_epi16(src_r6r7_0_31lo, shifttosigned);
2254 src_r6r7_0_31hi = _mm512_add_epi16(src_r6r7_0_31hi, shifttosigned);
2255 src_r6r7_32_63lo = _mm512_add_epi16(src_r6r7_32_63lo, shifttosigned);
2256 src_r6r7_32_63hi = _mm512_add_epi16(src_r6r7_32_63hi, shifttosigned);
2257 }
2258
2259 if constexpr (UseVNNI)
2260 {
2261 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r6r7_0_31lo, coef_r6r7_0_31lo);
2262 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r6r7_0_31hi, coef_r6r7_0_31hi);
2263 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r6r7_32_63lo, coef_r6r7_32_63lo);
2264 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r6r7_32_63hi, coef_r6r7_32_63hi);
2265 }
2266 else
2267 {
2268 result_0_31lo = _mm512_add_epi32(result_0_31lo, _mm512_madd_epi16(src_r6r7_0_31lo, coef_r6r7_0_31lo));
2269 result_0_31hi = _mm512_add_epi32(result_0_31hi, _mm512_madd_epi16(src_r6r7_0_31hi, coef_r6r7_0_31hi));
2270 result_32_63lo = _mm512_add_epi32(result_32_63lo, _mm512_madd_epi16(src_r6r7_32_63lo, coef_r6r7_32_63lo));
2271 result_32_63hi = _mm512_add_epi32(result_32_63hi, _mm512_madd_epi16(src_r6r7_32_63hi, coef_r6r7_32_63hi));
2272 }
2273
2274 if constexpr (!lessthan16bit) {
2275 result_0_31lo = _mm512_add_epi32(result_0_31lo, shiftfromsigned);
2276 result_0_31hi = _mm512_add_epi32(result_0_31hi, shiftfromsigned);
2277 result_32_63lo = _mm512_add_epi32(result_32_63lo, shiftfromsigned);
2278 result_32_63hi = _mm512_add_epi32(result_32_63hi, shiftfromsigned);
2279 }
2280
2281 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale16bits);
2282 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale16bits);
2283 result_32_63lo = _mm512_srai_epi32(result_32_63lo, FPScale16bits);
2284 result_32_63hi = _mm512_srai_epi32(result_32_63hi, FPScale16bits);
2285
2286 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
2287 __m512i result_32_63_int16 = _mm512_packus_epi32(result_32_63lo, result_32_63hi);
2288
2289 if constexpr (lessthan16bit) {
2290 result_0_31_int16 = _mm512_min_epu16(result_0_31_int16, clamp_limit);
2291 result_32_63_int16 = _mm512_min_epu16(result_32_63_int16, clamp_limit);
2292 }
2293
2294 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr), result_0_31_int16);
2295
2296 const int w_mod32 = width / 32 * 32;
2297 if (x < (w_mod32 - 32))
2298 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr + 32), result_32_63_int16);
2299
2300 dst_ptr += dst_pitch;
2301 src_ptr_0_31 += src_pitch;
2302 src_ptr_32_63 += src_pitch;
2303 }
2304
2305 current_coeff_SIMD += 16;
2306 };
2307
2308 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
2309 {
2310 do_h_integer_core(std::false_type{});
2311 }
2312
2313 for (; x < width; x += PIXELS_AT_A_TIME)
2314 {
2315 do_h_integer_core(std::true_type{});
2316 }
2317 }
2318 }
2319
2320 template<bool lessthan16bit, bool UseVNNI>
2321 void resize_h_planar_uint16_avx512_permutex_vstripe_mp_4s16_ks8_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
2322 {
2323 const uint16_t* src = (uint16_t*)src8;
2324 uint16_t* AVS_RESTRICT dst = (uint16_t* AVS_RESTRICT)dst8;
2325 dst_pitch = dst_pitch / sizeof(uint16_t);
2326 src_pitch = src_pitch / sizeof(uint16_t);
2327
2328 constexpr int PIXELS_AT_A_TIME = 64;
2329
2330 const int width_safe_mod = (program->safelimit_64_pixels_each32th_target.overread_possible ? program->safelimit_64_pixels_each32th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
2331
2332 const int max_scanlines = program->max_scanlines;
2333
2334 const __m512i shifttosigned = _mm512_set1_epi16(-32768);
2335 const __m512i shiftfromsigned = _mm512_set1_epi32(32768 << FPScale16bits);
2336
2337 const int limit = (1 << bits_per_pixel) - 1;
2338 __m512i clamp_limit = _mm512_set1_epi16((short)limit);
2339 __m512i rounder = _mm512_set1_epi32(1 << (FPScale16bits - 1));
2340
2341 for (int y_from = 0; y_from < height; y_from += max_scanlines)
2342 {
2343 int y_to = std::min(y_from + max_scanlines, height);
2344
2345 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
2346
2347 int x = 0;
2348
2349 auto do_h_integer_core = [&](auto partial_load) {
2350 __m512i one_epi16 = _mm512_set1_epi16(1);
2351
2352 const __m512i coef_r0r1_0_31lo = _mm512_load_si512(current_coeff_SIMD + 0);
2353 const __m512i coef_r0r1_0_31hi = _mm512_load_si512(current_coeff_SIMD + 1);
2354 const __m512i coef_r0r1_32_63lo = _mm512_load_si512(current_coeff_SIMD + 2);
2355 const __m512i coef_r0r1_32_63hi = _mm512_load_si512(current_coeff_SIMD + 3);
2356 const __m512i coef_r2r3_0_31lo = _mm512_load_si512(current_coeff_SIMD + 4);
2357 const __m512i coef_r2r3_0_31hi = _mm512_load_si512(current_coeff_SIMD + 5);
2358 const __m512i coef_r2r3_32_63lo = _mm512_load_si512(current_coeff_SIMD + 6);
2359 const __m512i coef_r2r3_32_63hi = _mm512_load_si512(current_coeff_SIMD + 7);
2360 const __m512i coef_r4r5_0_31lo = _mm512_load_si512(current_coeff_SIMD + 8);
2361 const __m512i coef_r4r5_0_31hi = _mm512_load_si512(current_coeff_SIMD + 9);
2362 const __m512i coef_r4r5_32_63lo = _mm512_load_si512(current_coeff_SIMD + 10);
2363 const __m512i coef_r4r5_32_63hi = _mm512_load_si512(current_coeff_SIMD + 11);
2364 const __m512i coef_r6r7_0_31lo = _mm512_load_si512(current_coeff_SIMD + 12);
2365 const __m512i coef_r6r7_0_31hi = _mm512_load_si512(current_coeff_SIMD + 13);
2366 const __m512i coef_r6r7_32_63lo = _mm512_load_si512(current_coeff_SIMD + 14);
2367 const __m512i coef_r6r7_32_63hi = _mm512_load_si512(current_coeff_SIMD + 15);
2368
2369 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
2370 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
2371 __m512i perm_0_32_47 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 32]));
2372 __m512i perm_0_48_63 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 48]));
2373
2374 int iStart_0_15 = program->pixel_offset[x];
2375 int iStart_16_31 = program->pixel_offset[x + 16];
2376 int iStart_32_47 = program->pixel_offset[x + 32];
2377 int iStart_48_63 = program->pixel_offset[x + 48];
2378
2379 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, _mm512_set1_epi32(iStart_0_15));
2380 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, _mm512_set1_epi32(iStart_16_31));
2381 perm_0_32_47 = _mm512_sub_epi32(perm_0_32_47, _mm512_set1_epi32(iStart_32_47));
2382 perm_0_48_63 = _mm512_sub_epi32(perm_0_48_63, _mm512_set1_epi32(iStart_48_63));
2383
2384 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
2385 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
2386 __m256i m256i_perm_0_32_47 = _mm512_cvtepi32_epi16(perm_0_32_47);
2387 __m256i m256i_perm_0_48_63 = _mm512_cvtepi32_epi16(perm_0_48_63);
2388
2389 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
2390 __m512i perm_0_32_63 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_32_47), m256i_perm_0_48_63, 1);
2391
2392 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
2393 __m512i perm_1_32_63 = _mm512_add_epi16(perm_0_32_63, one_epi16);
2394
2395 const __m512i perm_r0r1_0_31lo = _mm512_unpacklo_epi16(perm_0_0_31, perm_1_0_31);
2396 const __m512i perm_r0r1_0_31hi = _mm512_unpackhi_epi16(perm_0_0_31, perm_1_0_31);
2397 const __m512i perm_r0r1_32_63lo = _mm512_unpacklo_epi16(perm_0_32_63, perm_1_32_63);
2398 const __m512i perm_r0r1_32_63hi = _mm512_unpackhi_epi16(perm_0_32_63, perm_1_32_63);
2399
2400 const __m512i two_epi16 = _mm512_set1_epi16(2);
2401 const __mmask32 k_high = 0xFFFF0000;
2402
2403 uint16_t* AVS_RESTRICT dst_ptr = dst + x + y_from * dst_pitch;
2404 const uint16_t* src_ptr_0_15 = src + iStart_0_15 + y_from * src_pitch;
2405 const uint16_t* src_ptr_16_31 = src + iStart_16_31 + y_from * src_pitch;
2406 const uint16_t* src_ptr_32_47 = src + iStart_32_47 + y_from * src_pitch;
2407 const uint16_t* src_ptr_48_63 = src + iStart_48_63 + y_from * src_pitch;
2408
2409 const int remaining_0_15 = program->source_size - iStart_0_15;
2410 const __mmask32 k1_0_15 = _bzhi_u32(~0UL, remaining_0_15);
2411 const __mmask32 k2_0_15 = _bzhi_u32(~0UL, remaining_0_15 - 32);
2412 const int remaining_16_31 = program->source_size - iStart_16_31;
2413 const __mmask32 k1_16_31 = _bzhi_u32(~0UL, remaining_16_31);
2414 const __mmask32 k2_16_31 = _bzhi_u32(~0UL, remaining_16_31 - 32);
2415 const int remaining_32_47 = program->source_size - iStart_32_47;
2416 const __mmask32 k1_32_47 = _bzhi_u32(~0UL, remaining_32_47);
2417 const __mmask32 k2_32_47 = _bzhi_u32(~0UL, remaining_32_47 - 32);
2418 const int remaining_48_63 = program->source_size - iStart_48_63;
2419 const __mmask32 k1_48_63 = _bzhi_u32(~0UL, remaining_48_63);
2420 const __mmask32 k2_48_63 = _bzhi_u32(~0UL, remaining_48_63 - 32);
2421
2422 for (int y = y_from; y < y_to; y++)
2423 {
2424 __m512i data_src_0_15, data_src2_0_15;
2425 __m512i data_src_16_31, data_src2_16_31;
2426 __m512i data_src_32_47, data_src2_32_47;
2427 __m512i data_src_48_63, data_src2_48_63;
2428
2429 __m512i perm_rNrNp1_0_31lo = perm_r0r1_0_31lo;
2430 __m512i perm_rNrNp1_0_31hi = perm_r0r1_0_31hi;
2431 __m512i perm_rNrNp1_32_63lo = perm_r0r1_32_63lo;
2432 __m512i perm_rNrNp1_32_63hi = perm_r0r1_32_63hi;
2433
2434 if constexpr (partial_load) {
2435 data_src_0_15 = _mm512_maskz_loadu_epi16(k1_0_15, src_ptr_0_15);
2436 data_src_16_31 = _mm512_maskz_loadu_epi16(k1_16_31, src_ptr_16_31);
2437 data_src_32_47 = _mm512_maskz_loadu_epi16(k1_32_47, src_ptr_32_47);
2438 data_src_48_63 = _mm512_maskz_loadu_epi16(k1_48_63, src_ptr_48_63);
2439 data_src2_0_15 = _mm512_maskz_loadu_epi16(k2_0_15, src_ptr_0_15 + 32);
2440 data_src2_16_31 = _mm512_maskz_loadu_epi16(k2_16_31, src_ptr_16_31 + 32);
2441 data_src2_32_47 = _mm512_maskz_loadu_epi16(k2_32_47, src_ptr_32_47 + 32);
2442 data_src2_48_63 = _mm512_maskz_loadu_epi16(k2_48_63, src_ptr_48_63 + 32);
2443 }
2444 else {
2445 data_src_0_15 = _mm512_loadu_si512(src_ptr_0_15);
2446 data_src_16_31 = _mm512_loadu_si512(src_ptr_16_31);
2447 data_src_32_47 = _mm512_loadu_si512(src_ptr_32_47);
2448 data_src_48_63 = _mm512_loadu_si512(src_ptr_48_63);
2449 data_src2_0_15 = _mm512_loadu_si512(src_ptr_0_15 + 32);
2450 data_src2_16_31 = _mm512_loadu_si512(src_ptr_16_31 + 32);
2451 data_src2_32_47 = _mm512_loadu_si512(src_ptr_32_47 + 32);
2452 data_src2_48_63 = _mm512_loadu_si512(src_ptr_48_63 + 32);
2453 }
2454
2455 __m512i result_0_31lo, result_0_31hi, result_32_63lo, result_32_63hi;
2456
2457 __m512i src_r0r1_0_31lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31lo, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31lo, data_src2_16_31));
2458 __m512i src_r0r1_0_31hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31hi, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31hi, data_src2_16_31));
2459 __m512i src_r0r1_32_63lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63lo, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63lo, data_src2_48_63));
2460 __m512i src_r0r1_32_63hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63hi, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63hi, data_src2_48_63));
2461
2462 perm_rNrNp1_0_31lo = _mm512_add_epi16(perm_rNrNp1_0_31lo, two_epi16);
2463 perm_rNrNp1_0_31hi = _mm512_add_epi16(perm_rNrNp1_0_31hi, two_epi16);
2464 perm_rNrNp1_32_63lo = _mm512_add_epi16(perm_rNrNp1_32_63lo, two_epi16);
2465 perm_rNrNp1_32_63hi = _mm512_add_epi16(perm_rNrNp1_32_63hi, two_epi16);
2466
2467 if constexpr (!lessthan16bit) {
2468 src_r0r1_0_31lo = _mm512_add_epi16(src_r0r1_0_31lo, shifttosigned);
2469 src_r0r1_0_31hi = _mm512_add_epi16(src_r0r1_0_31hi, shifttosigned);
2470 src_r0r1_32_63lo = _mm512_add_epi16(src_r0r1_32_63lo, shifttosigned);
2471 src_r0r1_32_63hi = _mm512_add_epi16(src_r0r1_32_63hi, shifttosigned);
2472 }
2473
2474 if constexpr (UseVNNI)
2475 {
2476 result_0_31lo = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31lo, coef_r0r1_0_31lo);
2477 result_0_31hi = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31hi, coef_r0r1_0_31hi);
2478 result_32_63lo = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63lo, coef_r0r1_32_63lo);
2479 result_32_63hi = _mm512_dpwssd_epi32(rounder, src_r0r1_32_63hi, coef_r0r1_32_63hi);
2480 }
2481 else
2482 {
2483 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, coef_r0r1_0_31lo), rounder);
2484 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, coef_r0r1_0_31hi), rounder);
2485 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63lo, coef_r0r1_32_63lo), rounder);
2486 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63hi, coef_r0r1_32_63hi), rounder);
2487 }
2488
2489 __m512i src_r2r3_0_31lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31lo, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31lo, data_src2_16_31));
2490 __m512i src_r2r3_0_31hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31hi, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31hi, data_src2_16_31));
2491 __m512i src_r2r3_32_63lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63lo, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63lo, data_src2_48_63));
2492 __m512i src_r2r3_32_63hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63hi, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63hi, data_src2_48_63));
2493
2494 perm_rNrNp1_0_31lo = _mm512_add_epi16(perm_rNrNp1_0_31lo, two_epi16);
2495 perm_rNrNp1_0_31hi = _mm512_add_epi16(perm_rNrNp1_0_31hi, two_epi16);
2496 perm_rNrNp1_32_63lo = _mm512_add_epi16(perm_rNrNp1_32_63lo, two_epi16);
2497 perm_rNrNp1_32_63hi = _mm512_add_epi16(perm_rNrNp1_32_63hi, two_epi16);
2498
2499 if constexpr (!lessthan16bit) {
2500 src_r2r3_0_31lo = _mm512_add_epi16(src_r2r3_0_31lo, shifttosigned);
2501 src_r2r3_0_31hi = _mm512_add_epi16(src_r2r3_0_31hi, shifttosigned);
2502 src_r2r3_32_63lo = _mm512_add_epi16(src_r2r3_32_63lo, shifttosigned);
2503 src_r2r3_32_63hi = _mm512_add_epi16(src_r2r3_32_63hi, shifttosigned);
2504 }
2505
2506 if constexpr (UseVNNI)
2507 {
2508 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r2r3_0_31lo, coef_r2r3_0_31lo);
2509 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r2r3_0_31hi, coef_r2r3_0_31hi);
2510 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r2r3_32_63lo, coef_r2r3_32_63lo);
2511 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r2r3_32_63hi, coef_r2r3_32_63hi);
2512 }
2513 else
2514 {
2515 result_0_31lo = _mm512_add_epi32(result_0_31lo, _mm512_madd_epi16(src_r2r3_0_31lo, coef_r2r3_0_31lo));
2516 result_0_31hi = _mm512_add_epi32(result_0_31hi, _mm512_madd_epi16(src_r2r3_0_31hi, coef_r2r3_0_31hi));
2517 result_32_63lo = _mm512_add_epi32(result_32_63lo, _mm512_madd_epi16(src_r2r3_32_63lo, coef_r2r3_32_63lo));
2518 result_32_63hi = _mm512_add_epi32(result_32_63hi, _mm512_madd_epi16(src_r2r3_32_63hi, coef_r2r3_32_63hi));
2519 }
2520
2521 __m512i src_r4r5_0_31lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31lo, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31lo, data_src2_16_31));
2522 __m512i src_r4r5_0_31hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31hi, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31hi, data_src2_16_31));
2523 __m512i src_r4r5_32_63lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63lo, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63lo, data_src2_48_63));
2524 __m512i src_r4r5_32_63hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63hi, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63hi, data_src2_48_63));
2525
2526 perm_rNrNp1_0_31lo = _mm512_add_epi16(perm_rNrNp1_0_31lo, two_epi16);
2527 perm_rNrNp1_0_31hi = _mm512_add_epi16(perm_rNrNp1_0_31hi, two_epi16);
2528 perm_rNrNp1_32_63lo = _mm512_add_epi16(perm_rNrNp1_32_63lo, two_epi16);
2529 perm_rNrNp1_32_63hi = _mm512_add_epi16(perm_rNrNp1_32_63hi, two_epi16);
2530
2531 if constexpr (!lessthan16bit) {
2532 src_r4r5_0_31lo = _mm512_add_epi16(src_r4r5_0_31lo, shifttosigned);
2533 src_r4r5_0_31hi = _mm512_add_epi16(src_r4r5_0_31hi, shifttosigned);
2534 src_r4r5_32_63lo = _mm512_add_epi16(src_r4r5_32_63lo, shifttosigned);
2535 src_r4r5_32_63hi = _mm512_add_epi16(src_r4r5_32_63hi, shifttosigned);
2536 }
2537
2538 if constexpr (UseVNNI)
2539 {
2540 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r4r5_0_31lo, coef_r4r5_0_31lo);
2541 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r4r5_0_31hi, coef_r4r5_0_31hi);
2542 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r4r5_32_63lo, coef_r4r5_32_63lo);
2543 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r4r5_32_63hi, coef_r4r5_32_63hi);
2544 }
2545 else
2546 {
2547 result_0_31lo = _mm512_add_epi32(result_0_31lo, _mm512_madd_epi16(src_r4r5_0_31lo, coef_r4r5_0_31lo));
2548 result_0_31hi = _mm512_add_epi32(result_0_31hi, _mm512_madd_epi16(src_r4r5_0_31hi, coef_r4r5_0_31hi));
2549 result_32_63lo = _mm512_add_epi32(result_32_63lo, _mm512_madd_epi16(src_r4r5_32_63lo, coef_r4r5_32_63lo));
2550 result_32_63hi = _mm512_add_epi32(result_32_63hi, _mm512_madd_epi16(src_r4r5_32_63hi, coef_r4r5_32_63hi));
2551 }
2552
2553 __m512i src_r6r7_0_31lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31lo, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31lo, data_src2_16_31));
2554 __m512i src_r6r7_0_31hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31hi, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31hi, data_src2_16_31));
2555 __m512i src_r6r7_32_63lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63lo, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63lo, data_src2_48_63));
2556 __m512i src_r6r7_32_63hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63hi, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63hi, data_src2_48_63));
2557
2558 if constexpr (!lessthan16bit) {
2559 src_r6r7_0_31lo = _mm512_add_epi16(src_r6r7_0_31lo, shifttosigned);
2560 src_r6r7_0_31hi = _mm512_add_epi16(src_r6r7_0_31hi, shifttosigned);
2561 src_r6r7_32_63lo = _mm512_add_epi16(src_r6r7_32_63lo, shifttosigned);
2562 src_r6r7_32_63hi = _mm512_add_epi16(src_r6r7_32_63hi, shifttosigned);
2563 }
2564
2565 if constexpr (UseVNNI)
2566 {
2567 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r6r7_0_31lo, coef_r6r7_0_31lo);
2568 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r6r7_0_31hi, coef_r6r7_0_31hi);
2569 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r6r7_32_63lo, coef_r6r7_32_63lo);
2570 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r6r7_32_63hi, coef_r6r7_32_63hi);
2571 }
2572 else
2573 {
2574 result_0_31lo = _mm512_add_epi32(result_0_31lo, _mm512_madd_epi16(src_r6r7_0_31lo, coef_r6r7_0_31lo));
2575 result_0_31hi = _mm512_add_epi32(result_0_31hi, _mm512_madd_epi16(src_r6r7_0_31hi, coef_r6r7_0_31hi));
2576 result_32_63lo = _mm512_add_epi32(result_32_63lo, _mm512_madd_epi16(src_r6r7_32_63lo, coef_r6r7_32_63lo));
2577 result_32_63hi = _mm512_add_epi32(result_32_63hi, _mm512_madd_epi16(src_r6r7_32_63hi, coef_r6r7_32_63hi));
2578 }
2579
2580 if constexpr (!lessthan16bit) {
2581 result_0_31lo = _mm512_add_epi32(result_0_31lo, shiftfromsigned);
2582 result_0_31hi = _mm512_add_epi32(result_0_31hi, shiftfromsigned);
2583 result_32_63lo = _mm512_add_epi32(result_32_63lo, shiftfromsigned);
2584 result_32_63hi = _mm512_add_epi32(result_32_63hi, shiftfromsigned);
2585 }
2586
2587 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale16bits);
2588 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale16bits);
2589 result_32_63lo = _mm512_srai_epi32(result_32_63lo, FPScale16bits);
2590 result_32_63hi = _mm512_srai_epi32(result_32_63hi, FPScale16bits);
2591
2592 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
2593 __m512i result_32_63_int16 = _mm512_packus_epi32(result_32_63lo, result_32_63hi);
2594
2595 if constexpr (lessthan16bit) {
2596 result_0_31_int16 = _mm512_min_epu16(result_0_31_int16, clamp_limit);
2597 result_32_63_int16 = _mm512_min_epu16(result_32_63_int16, clamp_limit);
2598 }
2599
2600 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr), result_0_31_int16);
2601
2602 const int w_mod32 = width / 32 * 32;
2603 if (x < (w_mod32 - 32))
2604 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr + 32), result_32_63_int16);
2605
2606 dst_ptr += dst_pitch;
2607 src_ptr_0_15 += src_pitch;
2608 src_ptr_16_31 += src_pitch;
2609 src_ptr_32_47 += src_pitch;
2610 src_ptr_48_63 += src_pitch;
2611 }
2612
2613 current_coeff_SIMD += 16;
2614 };
2615
2616 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
2617 {
2618 do_h_integer_core(std::false_type{});
2619 }
2620
2621 for (; x < width; x += PIXELS_AT_A_TIME)
2622 {
2623 do_h_integer_core(std::true_type{});
2624 }
2625 }
2626 }
2627
2628 template<bool lessthan16bit, bool UseVNNI>
2629 void resize_h_planar_uint16_avx512_permutex_vstripe_mp_ks16_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
2630 {
2631 const uint16_t* src = (uint16_t*)src8;
2632 uint16_t* AVS_RESTRICT dst = (uint16_t* AVS_RESTRICT)dst8;
2633 dst_pitch = dst_pitch / sizeof(uint16_t);
2634 src_pitch = src_pitch / sizeof(uint16_t);
2635
2636 constexpr int PIXELS_AT_A_TIME = 32;
2637
2638 const int width_safe_mod = (program->safelimit_64_pixels_each32th_target.overread_possible ? program->safelimit_64_pixels_each32th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
2639
2640 const int max_scanlines = program->max_scanlines;
2641
2642 const __m512i shifttosigned = _mm512_set1_epi16(-32768);
2643 const __m512i shiftfromsigned = _mm512_set1_epi32(32768 << FPScale16bits);
2644
2645 const int limit = (1 << bits_per_pixel) - 1;
2646 __m512i clamp_limit = _mm512_set1_epi16((short)limit);
2647 __m512i rounder = _mm512_set1_epi32(1 << (FPScale16bits - 1));
2648
2649 for (int y_from = 0; y_from < height; y_from += max_scanlines)
2650 {
2651 int y_to = std::min(y_from + max_scanlines, height);
2652
2653 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
2654
2655 int x = 0;
2656
2657 auto do_h_integer_core = [&](auto partial_load) {
2658 __m512i one_epi16 = _mm512_set1_epi16(1);
2659
2660 const __m512i coef_r0r1_0_31lo = _mm512_load_si512(current_coeff_SIMD + 0);
2661 const __m512i coef_r0r1_0_31hi = _mm512_load_si512(current_coeff_SIMD + 1);
2662 const __m512i coef_r2r3_0_31lo = _mm512_load_si512(current_coeff_SIMD + 2);
2663 const __m512i coef_r2r3_0_31hi = _mm512_load_si512(current_coeff_SIMD + 3);
2664 const __m512i coef_r4r5_0_31lo = _mm512_load_si512(current_coeff_SIMD + 4);
2665 const __m512i coef_r4r5_0_31hi = _mm512_load_si512(current_coeff_SIMD + 5);
2666 const __m512i coef_r6r7_0_31lo = _mm512_load_si512(current_coeff_SIMD + 6);
2667 const __m512i coef_r6r7_0_31hi = _mm512_load_si512(current_coeff_SIMD + 7);
2668 const __m512i coef_r8r9_0_31lo = _mm512_load_si512(current_coeff_SIMD + 8);
2669 const __m512i coef_r8r9_0_31hi = _mm512_load_si512(current_coeff_SIMD + 9);
2670 const __m512i coef_r10r11_0_31lo = _mm512_load_si512(current_coeff_SIMD + 10);
2671 const __m512i coef_r10r11_0_31hi = _mm512_load_si512(current_coeff_SIMD + 11);
2672 const __m512i coef_r12r13_0_31lo = _mm512_load_si512(current_coeff_SIMD + 12);
2673 const __m512i coef_r12r13_0_31hi = _mm512_load_si512(current_coeff_SIMD + 13);
2674 const __m512i coef_r14r15_0_31lo = _mm512_load_si512(current_coeff_SIMD + 14);
2675 const __m512i coef_r14r15_0_31hi = _mm512_load_si512(current_coeff_SIMD + 15);
2676
2677 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
2678 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
2679
2680 int iStart = program->pixel_offset[x];
2681 __m512i m512i_Start = _mm512_set1_epi32(iStart);
2682
2683 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, m512i_Start);
2684 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, m512i_Start);
2685
2686 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
2687 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
2688
2689 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
2690 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
2691
2692 const __m512i perm_r0r1_0_31lo = _mm512_unpacklo_epi16(perm_0_0_31, perm_1_0_31);
2693 const __m512i perm_r0r1_0_31hi = _mm512_unpackhi_epi16(perm_0_0_31, perm_1_0_31);
2694
2695 const __m512i two_epi16 = _mm512_set1_epi16(2);
2696
2697 uint16_t* AVS_RESTRICT dst_ptr = dst + x + y_from * dst_pitch;
2698 const uint16_t* src_ptr = src + iStart + y_from * src_pitch;
2699
2700 const int remaining = program->source_size - iStart;
2701 const __mmask32 k1 = _bzhi_u32(~0UL, remaining);
2702 const __mmask32 k2 = _bzhi_u32(~0UL, remaining - 32);
2703
2704 for (int y = y_from; y < y_to; y++)
2705 {
2706 __m512i data_src, data_src2;
2707
2708 __m512i perm_rNrNp1_0_31lo_w = perm_r0r1_0_31lo;
2709 __m512i perm_rNrNp1_0_31hi_w = perm_r0r1_0_31hi;
2710
2711 if constexpr (partial_load) {
2712 data_src = _mm512_maskz_loadu_epi16(k1, src_ptr);
2713 data_src2 = _mm512_maskz_loadu_epi16(k2, src_ptr + 32);
2714 }
2715 else {
2716 data_src = _mm512_loadu_si512(src_ptr);
2717 data_src2 = _mm512_loadu_si512(src_ptr + 32);
2718 }
2719
2720 __m512i src_r0r1_0_31lo = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31lo_w, data_src2);
2721 __m512i src_r0r1_0_31hi = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31hi_w, data_src2);
2722
2723 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
2724 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
2725
2726 __m512i src_r2r3_0_31lo = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31lo_w, data_src2);
2727 __m512i src_r2r3_0_31hi = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31hi_w, data_src2);
2728
2729 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
2730 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
2731
2732 __m512i result_0_31lo, result_0_31hi;
2733
2734 if constexpr (!lessthan16bit) {
2735 src_r0r1_0_31lo = _mm512_add_epi16(src_r0r1_0_31lo, shifttosigned);
2736 src_r0r1_0_31hi = _mm512_add_epi16(src_r0r1_0_31hi, shifttosigned);
2737 src_r2r3_0_31lo = _mm512_add_epi16(src_r2r3_0_31lo, shifttosigned);
2738 src_r2r3_0_31hi = _mm512_add_epi16(src_r2r3_0_31hi, shifttosigned);
2739 }
2740
2741 if constexpr (UseVNNI)
2742 {
2743 result_0_31lo = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31lo, coef_r0r1_0_31lo);
2744 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r2r3_0_31lo, coef_r2r3_0_31lo);
2745 result_0_31hi = _mm512_dpwssd_epi32(rounder, src_r0r1_0_31hi, coef_r0r1_0_31hi);
2746 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r2r3_0_31hi, coef_r2r3_0_31hi);
2747 }
2748 else
2749 {
2750 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, coef_r0r1_0_31lo), _mm512_madd_epi16(src_r2r3_0_31lo, coef_r2r3_0_31lo));
2751 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, coef_r0r1_0_31hi), _mm512_madd_epi16(src_r2r3_0_31hi, coef_r2r3_0_31hi));
2752 }
2753
2754 __m512i src_r4r5_0_31lo = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31lo_w, data_src2);
2755 __m512i src_r4r5_0_31hi = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31hi_w, data_src2);
2756
2757 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
2758 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
2759
2760 __m512i src_r6r7_0_31lo = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31lo_w, data_src2);
2761 __m512i src_r6r7_0_31hi = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31hi_w, data_src2);
2762
2763 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
2764 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
2765
2766 if constexpr (!lessthan16bit) {
2767 src_r4r5_0_31lo = _mm512_add_epi16(src_r4r5_0_31lo, shifttosigned);
2768 src_r4r5_0_31hi = _mm512_add_epi16(src_r4r5_0_31hi, shifttosigned);
2769 src_r6r7_0_31lo = _mm512_add_epi16(src_r6r7_0_31lo, shifttosigned);
2770 src_r6r7_0_31hi = _mm512_add_epi16(src_r6r7_0_31hi, shifttosigned);
2771 }
2772
2773 if constexpr (UseVNNI)
2774 {
2775 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r4r5_0_31lo, coef_r4r5_0_31lo);
2776 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r6r7_0_31lo, coef_r6r7_0_31lo);
2777 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r4r5_0_31hi, coef_r4r5_0_31hi);
2778 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r6r7_0_31hi, coef_r6r7_0_31hi);
2779 }
2780 else
2781 {
2782 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_0_31lo, coef_r4r5_0_31lo), result_0_31lo);
2783 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r4r5_0_31hi, coef_r4r5_0_31hi), result_0_31hi);
2784 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_0_31lo, coef_r6r7_0_31lo), result_0_31lo);
2785 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r6r7_0_31hi, coef_r6r7_0_31hi), result_0_31hi);
2786 }
2787
2788 __m512i src_r8r9_0_31lo = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31lo_w, data_src2);
2789 __m512i src_r8r9_0_31hi = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31hi_w, data_src2);
2790
2791 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
2792 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
2793
2794 __m512i src_r10r11_0_31lo = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31lo_w, data_src2);
2795 __m512i src_r10r11_0_31hi = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31hi_w, data_src2);
2796
2797 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
2798 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
2799
2800 if constexpr (!lessthan16bit) {
2801 src_r8r9_0_31lo = _mm512_add_epi16(src_r8r9_0_31lo, shifttosigned);
2802 src_r8r9_0_31hi = _mm512_add_epi16(src_r8r9_0_31hi, shifttosigned);
2803 src_r10r11_0_31lo = _mm512_add_epi16(src_r10r11_0_31lo, shifttosigned);
2804 src_r10r11_0_31hi = _mm512_add_epi16(src_r10r11_0_31hi, shifttosigned);
2805 }
2806
2807 if constexpr (UseVNNI)
2808 {
2809 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r8r9_0_31lo, coef_r8r9_0_31lo);
2810 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r10r11_0_31lo, coef_r10r11_0_31lo);
2811 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r8r9_0_31hi, coef_r8r9_0_31hi);
2812 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r10r11_0_31hi, coef_r10r11_0_31hi);
2813 }
2814 else
2815 {
2816 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r8r9_0_31lo, coef_r8r9_0_31lo), result_0_31lo);
2817 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r8r9_0_31hi, coef_r8r9_0_31hi), result_0_31hi);
2818 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r10r11_0_31lo, coef_r10r11_0_31lo), result_0_31lo);
2819 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r10r11_0_31hi, coef_r10r11_0_31hi), result_0_31hi);
2820 }
2821
2822 __m512i src_r12r13_0_31lo = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31lo_w, data_src2);
2823 __m512i src_r12r13_0_31hi = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31hi_w, data_src2);
2824
2825 perm_rNrNp1_0_31lo_w = _mm512_add_epi16(perm_rNrNp1_0_31lo_w, two_epi16);
2826 perm_rNrNp1_0_31hi_w = _mm512_add_epi16(perm_rNrNp1_0_31hi_w, two_epi16);
2827
2828 __m512i src_r14r15_0_31lo = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31lo_w, data_src2);
2829 __m512i src_r14r15_0_31hi = _mm512_permutex2var_epi16(data_src, perm_rNrNp1_0_31hi_w, data_src2);
2830
2831 if constexpr (!lessthan16bit) {
2832 src_r12r13_0_31lo = _mm512_add_epi16(src_r12r13_0_31lo, shifttosigned);
2833 src_r12r13_0_31hi = _mm512_add_epi16(src_r12r13_0_31hi, shifttosigned);
2834 src_r14r15_0_31lo = _mm512_add_epi16(src_r14r15_0_31lo, shifttosigned);
2835 src_r14r15_0_31hi = _mm512_add_epi16(src_r14r15_0_31hi, shifttosigned);
2836 }
2837
2838 if constexpr (UseVNNI)
2839 {
2840 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r12r13_0_31lo, coef_r12r13_0_31lo);
2841 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r14r15_0_31lo, coef_r14r15_0_31lo);
2842 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r12r13_0_31hi, coef_r12r13_0_31hi);
2843 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r14r15_0_31hi, coef_r14r15_0_31hi);
2844 }
2845 else
2846 {
2847 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r12r13_0_31lo, coef_r12r13_0_31lo), result_0_31lo);
2848 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r12r13_0_31hi, coef_r12r13_0_31hi), result_0_31hi);
2849 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r14r15_0_31lo, coef_r14r15_0_31lo), result_0_31lo);
2850 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r14r15_0_31hi, coef_r14r15_0_31hi), result_0_31hi);
2851
2852 result_0_31lo = _mm512_add_epi32(result_0_31lo, rounder);
2853 result_0_31hi = _mm512_add_epi32(result_0_31hi, rounder);
2854 }
2855
2856 if constexpr (!lessthan16bit) {
2857 result_0_31lo = _mm512_add_epi32(result_0_31lo, shiftfromsigned);
2858 result_0_31hi = _mm512_add_epi32(result_0_31hi, shiftfromsigned);
2859 }
2860
2861 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale16bits);
2862 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale16bits);
2863
2864 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
2865
2866 if constexpr (lessthan16bit) {
2867 result_0_31_int16 = _mm512_min_epu16(result_0_31_int16, clamp_limit);
2868 }
2869
2870 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr), result_0_31_int16);
2871
2872 dst_ptr += dst_pitch;
2873 src_ptr += src_pitch;
2874 }
2875
2876 current_coeff_SIMD += 16;
2877 };
2878
2879 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
2880 {
2881 do_h_integer_core(std::false_type{});
2882 }
2883
2884 for (; x < width; x += PIXELS_AT_A_TIME)
2885 {
2886 do_h_integer_core(std::true_type{});
2887 }
2888 }
2889 }
2890
2891 template<bool lessthan16bit, bool UseVNNI>
2892 void resize_h_planar_uint16_avx512_permutex_vstripe_mp_4s16_ks48_pretransposed_coeffs_internal(BYTE* dst8, const BYTE* src8, int dst_pitch, int src_pitch, ResamplingProgram* program, int width, int height, int bits_per_pixel)
2893 {
2894 int filter_size_real = program->filter_size_real;
2895 if ((filter_size_real / 2 * 2) != filter_size_real) filter_size_real++;
2896
2897 const uint16_t* src = (uint16_t*)src8;
2898 uint16_t* AVS_RESTRICT dst = (uint16_t* AVS_RESTRICT)dst8;
2899 dst_pitch = dst_pitch / sizeof(uint16_t);
2900 src_pitch = src_pitch / sizeof(uint16_t);
2901
2902 constexpr int PIXELS_AT_A_TIME = 64;
2903
2904 const int width_safe_mod = (program->safelimit_64_pixels_each32th_target.overread_possible ? program->safelimit_64_pixels_each32th_target.source_overread_beyond_targetx : width) / PIXELS_AT_A_TIME * PIXELS_AT_A_TIME;
2905
2906 const int max_scanlines = program->max_scanlines;
2907
2908 const __m512i shifttosigned = _mm512_set1_epi16(-32768);
2909 const __m512i shiftfromsigned = _mm512_set1_epi32(32768 << FPScale16bits);
2910
2911 const int limit = (1 << bits_per_pixel) - 1;
2912 __m512i clamp_limit = _mm512_set1_epi16((short)limit);
2913 __m512i rounder = _mm512_set1_epi32(1 << (FPScale16bits - 1));
2914
2915 for (int y_from = 0; y_from < height; y_from += max_scanlines)
2916 {
2917 int y_to = std::min(y_from + max_scanlines, height);
2918
2919 const __m512i* AVS_RESTRICT current_coeff_SIMD = (__m512i*)program->pixel_coefficient_AVX512_H;
2920
2921 int x = 0;
2922
2923 auto do_h_integer_core = [&](auto partial_load) {
2924 __m512i one_epi16 = _mm512_set1_epi16(1);
2925
2926 __m512i perm_0_0_15 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x]));
2927 __m512i perm_0_16_31 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 16]));
2928 __m512i perm_0_32_47 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 32]));
2929 __m512i perm_0_48_63 = _mm512_loadu_si512((__m512i*)(&program->pixel_offset[x + 48]));
2930
2931 int iStart_0_15 = program->pixel_offset[x];
2932 int iStart_16_31 = program->pixel_offset[x + 16];
2933 int iStart_32_47 = program->pixel_offset[x + 32];
2934 int iStart_48_63 = program->pixel_offset[x + 48];
2935
2936 perm_0_0_15 = _mm512_sub_epi32(perm_0_0_15, _mm512_set1_epi32(iStart_0_15));
2937 perm_0_16_31 = _mm512_sub_epi32(perm_0_16_31, _mm512_set1_epi32(iStart_16_31));
2938 perm_0_32_47 = _mm512_sub_epi32(perm_0_32_47, _mm512_set1_epi32(iStart_32_47));
2939 perm_0_48_63 = _mm512_sub_epi32(perm_0_48_63, _mm512_set1_epi32(iStart_48_63));
2940
2941 __m256i m256i_perm_0_0_15 = _mm512_cvtepi32_epi16(perm_0_0_15);
2942 __m256i m256i_perm_0_16_31 = _mm512_cvtepi32_epi16(perm_0_16_31);
2943 __m256i m256i_perm_0_32_47 = _mm512_cvtepi32_epi16(perm_0_32_47);
2944 __m256i m256i_perm_0_48_63 = _mm512_cvtepi32_epi16(perm_0_48_63);
2945
2946 __m512i perm_0_0_31 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_0_15), m256i_perm_0_16_31, 1);
2947 __m512i perm_0_32_63 = _mm512_inserti64x4(_mm512_castsi256_si512(m256i_perm_0_32_47), m256i_perm_0_48_63, 1);
2948
2949 __m512i perm_1_0_31 = _mm512_add_epi16(perm_0_0_31, one_epi16);
2950 __m512i perm_1_32_63 = _mm512_add_epi16(perm_0_32_63, one_epi16);
2951
2952 const __m512i perm_r0r1_0_31lo = _mm512_unpacklo_epi16(perm_0_0_31, perm_1_0_31);
2953 const __m512i perm_r0r1_0_31hi = _mm512_unpackhi_epi16(perm_0_0_31, perm_1_0_31);
2954 const __m512i perm_r0r1_32_63lo = _mm512_unpacklo_epi16(perm_0_32_63, perm_1_32_63);
2955 const __m512i perm_r0r1_32_63hi = _mm512_unpackhi_epi16(perm_0_32_63, perm_1_32_63);
2956
2957 const __m512i two_epi16 = _mm512_set1_epi16(2);
2958 const __mmask32 k_high = 0xFFFF0000;
2959
2960 uint16_t* AVS_RESTRICT dst_ptr = dst + x + y_from * dst_pitch;
2961 const uint16_t* src_ptr_0_15 = src + iStart_0_15 + y_from * src_pitch;
2962 const uint16_t* src_ptr_16_31 = src + iStart_16_31 + y_from * src_pitch;
2963 const uint16_t* src_ptr_32_47 = src + iStart_32_47 + y_from * src_pitch;
2964 const uint16_t* src_ptr_48_63 = src + iStart_48_63 + y_from * src_pitch;
2965
2966 const int remaining_0_15 = program->source_size - iStart_0_15;
2967 const __mmask32 k1_0_15 = _bzhi_u32(~0UL, remaining_0_15);
2968 const __mmask32 k2_0_15 = _bzhi_u32(~0UL, remaining_0_15 - 32);
2969 const int remaining_16_31 = program->source_size - iStart_16_31;
2970 const __mmask32 k1_16_31 = _bzhi_u32(~0UL, remaining_16_31);
2971 const __mmask32 k2_16_31 = _bzhi_u32(~0UL, remaining_16_31 - 32);
2972 const int remaining_32_47 = program->source_size - iStart_32_47;
2973 const __mmask32 k1_32_47 = _bzhi_u32(~0UL, remaining_32_47);
2974 const __mmask32 k2_32_47 = _bzhi_u32(~0UL, remaining_32_47 - 32);
2975 const int remaining_48_63 = program->source_size - iStart_48_63;
2976 const __mmask32 k1_48_63 = _bzhi_u32(~0UL, remaining_48_63);
2977 const __mmask32 k2_48_63 = _bzhi_u32(~0UL, remaining_48_63 - 32);
2978
2979 for (int y = y_from; y < y_to; y++)
2980 {
2981 __m512i data_src_0_15, data_src2_0_15;
2982 __m512i data_src_16_31, data_src2_16_31;
2983 __m512i data_src_32_47, data_src2_32_47;
2984 __m512i data_src_48_63, data_src2_48_63;
2985
2986 __m512i perm_rNrNp1_0_31lo = perm_r0r1_0_31lo;
2987 __m512i perm_rNrNp1_0_31hi = perm_r0r1_0_31hi;
2988 __m512i perm_rNrNp1_32_63lo = perm_r0r1_32_63lo;
2989 __m512i perm_rNrNp1_32_63hi = perm_r0r1_32_63hi;
2990
2991 if constexpr (partial_load) {
2992 data_src_0_15 = _mm512_maskz_loadu_epi16(k1_0_15, src_ptr_0_15);
2993 data_src_16_31 = _mm512_maskz_loadu_epi16(k1_16_31, src_ptr_16_31);
2994 data_src_32_47 = _mm512_maskz_loadu_epi16(k1_32_47, src_ptr_32_47);
2995 data_src_48_63 = _mm512_maskz_loadu_epi16(k1_48_63, src_ptr_48_63);
2996 data_src2_0_15 = _mm512_maskz_loadu_epi16(k2_0_15, src_ptr_0_15 + 32);
2997 data_src2_16_31 = _mm512_maskz_loadu_epi16(k2_16_31, src_ptr_16_31 + 32);
2998 data_src2_32_47 = _mm512_maskz_loadu_epi16(k2_32_47, src_ptr_32_47 + 32);
2999 data_src2_48_63 = _mm512_maskz_loadu_epi16(k2_48_63, src_ptr_48_63 + 32);
3000 }
3001 else {
3002 data_src_0_15 = _mm512_loadu_si512(src_ptr_0_15);
3003 data_src_16_31 = _mm512_loadu_si512(src_ptr_16_31);
3004 data_src_32_47 = _mm512_loadu_si512(src_ptr_32_47);
3005 data_src_48_63 = _mm512_loadu_si512(src_ptr_48_63);
3006 data_src2_0_15 = _mm512_loadu_si512(src_ptr_0_15 + 32);
3007 data_src2_16_31 = _mm512_loadu_si512(src_ptr_16_31 + 32);
3008 data_src2_32_47 = _mm512_loadu_si512(src_ptr_32_47 + 32);
3009 data_src2_48_63 = _mm512_loadu_si512(src_ptr_48_63 + 32);
3010 }
3011
3012 __m512i result_0_31lo = rounder;
3013 __m512i result_0_31hi = rounder;
3014 __m512i result_32_63lo = rounder;
3015 __m512i result_32_63hi = rounder;
3016
3017 const __m512i* current_coeff_SIMDw = current_coeff_SIMD;
3018
3019 for (int kr = 0; kr < filter_size_real; kr += 2)
3020 {
3021 __m512i src_r0r1_0_31lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31lo, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31lo, data_src2_16_31));
3022 __m512i src_r0r1_0_31hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_0_15, perm_rNrNp1_0_31hi, data_src2_0_15), _mm512_permutex2var_epi16(data_src_16_31, perm_rNrNp1_0_31hi, data_src2_16_31));
3023 __m512i src_r0r1_32_63lo = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63lo, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63lo, data_src2_48_63));
3024 __m512i src_r0r1_32_63hi = _mm512_mask_blend_epi16(k_high, _mm512_permutex2var_epi16(data_src_32_47, perm_rNrNp1_32_63hi, data_src2_32_47), _mm512_permutex2var_epi16(data_src_48_63, perm_rNrNp1_32_63hi, data_src2_48_63));
3025
3026 perm_rNrNp1_0_31lo = _mm512_add_epi16(perm_rNrNp1_0_31lo, two_epi16);
3027 perm_rNrNp1_0_31hi = _mm512_add_epi16(perm_rNrNp1_0_31hi, two_epi16);
3028 perm_rNrNp1_32_63lo = _mm512_add_epi16(perm_rNrNp1_32_63lo, two_epi16);
3029 perm_rNrNp1_32_63hi = _mm512_add_epi16(perm_rNrNp1_32_63hi, two_epi16);
3030
3031 if constexpr (!lessthan16bit) {
3032 src_r0r1_0_31lo = _mm512_add_epi16(src_r0r1_0_31lo, shifttosigned);
3033 src_r0r1_0_31hi = _mm512_add_epi16(src_r0r1_0_31hi, shifttosigned);
3034 src_r0r1_32_63lo = _mm512_add_epi16(src_r0r1_32_63lo, shifttosigned);
3035 src_r0r1_32_63hi = _mm512_add_epi16(src_r0r1_32_63hi, shifttosigned);
3036 }
3037
3038 if constexpr (UseVNNI)
3039 {
3040 result_0_31lo = _mm512_dpwssd_epi32(result_0_31lo, src_r0r1_0_31lo, _mm512_load_si512(current_coeff_SIMDw + 0));
3041 result_0_31hi = _mm512_dpwssd_epi32(result_0_31hi, src_r0r1_0_31hi, _mm512_load_si512(current_coeff_SIMDw + 1));
3042 result_32_63lo = _mm512_dpwssd_epi32(result_32_63lo, src_r0r1_32_63lo, _mm512_load_si512(current_coeff_SIMDw + 2));
3043 result_32_63hi = _mm512_dpwssd_epi32(result_32_63hi, src_r0r1_32_63hi, _mm512_load_si512(current_coeff_SIMDw + 3));
3044 }
3045 else
3046 {
3047 result_0_31lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31lo, _mm512_load_si512(current_coeff_SIMDw + 0)), result_0_31lo);
3048 result_0_31hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_0_31hi, _mm512_load_si512(current_coeff_SIMDw + 1)), result_0_31hi);
3049 result_32_63lo = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63lo, _mm512_load_si512(current_coeff_SIMDw + 2)), result_32_63lo);
3050 result_32_63hi = _mm512_add_epi32(_mm512_madd_epi16(src_r0r1_32_63hi, _mm512_load_si512(current_coeff_SIMDw + 3)), result_32_63hi);
3051 }
3052
3053 current_coeff_SIMDw += 4;
3054 }
3055
3056 if constexpr (!lessthan16bit) {
3057 result_0_31lo = _mm512_add_epi32(result_0_31lo, shiftfromsigned);
3058 result_0_31hi = _mm512_add_epi32(result_0_31hi, shiftfromsigned);
3059 result_32_63lo = _mm512_add_epi32(result_32_63lo, shiftfromsigned);
3060 result_32_63hi = _mm512_add_epi32(result_32_63hi, shiftfromsigned);
3061 }
3062
3063 result_0_31lo = _mm512_srai_epi32(result_0_31lo, FPScale16bits);
3064 result_0_31hi = _mm512_srai_epi32(result_0_31hi, FPScale16bits);
3065 result_32_63lo = _mm512_srai_epi32(result_32_63lo, FPScale16bits);
3066 result_32_63hi = _mm512_srai_epi32(result_32_63hi, FPScale16bits);
3067
3068 __m512i result_0_31_int16 = _mm512_packus_epi32(result_0_31lo, result_0_31hi);
3069 __m512i result_32_63_int16 = _mm512_packus_epi32(result_32_63lo, result_32_63hi);
3070
3071 if constexpr (lessthan16bit) {
3072 result_0_31_int16 = _mm512_min_epu16(result_0_31_int16, clamp_limit);
3073 result_32_63_int16 = _mm512_min_epu16(result_32_63_int16, clamp_limit);
3074 }
3075
3076 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr), result_0_31_int16);
3077
3078 const int w_mod32 = width / 32 * 32;
3079 if (x < (w_mod32 - 32))
3080 _mm512_stream_si512(reinterpret_cast<__m512i*>(dst_ptr + 32), result_32_63_int16);
3081
3082 dst_ptr += dst_pitch;
3083 src_ptr_0_15 += src_pitch;
3084 src_ptr_16_31 += src_pitch;
3085 src_ptr_32_47 += src_pitch;
3086 src_ptr_48_63 += src_pitch;
3087 }
3088
3089 current_coeff_SIMD += filter_size_real * 2;
3090 };
3091
3092 for (; x < width_safe_mod; x += PIXELS_AT_A_TIME)
3093 {
3094 do_h_integer_core(std::false_type{});
3095 }
3096
3097 for (; x < width; x += PIXELS_AT_A_TIME)
3098 {
3099 do_h_integer_core(std::true_type{});
3100 }
3101 }
3102 }
3103