filters/intel/turn_avx2.cpp
| 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 | #include "../turn.h" | ||
| 36 | #include "turn_avx2.h" | ||
| 37 | |||
| 38 | #if defined(_MSC_VER) | ||
| 39 | #include <intrin.h> // MSVC | ||
| 40 | #else | ||
| 41 | #include <x86intrin.h> // GCC/MinGW/Clang/LLVM | ||
| 42 | #endif | ||
| 43 | #include <immintrin.h> | ||
| 44 | |||
| 45 | #include <cstdint> | ||
| 46 | |||
| 47 | //------------------------------------------------------------------------------------------------- | ||
| 48 | // 8-bit Transpose Kernels | ||
| 49 | //------------------------------------------------------------------------------------------------- | ||
| 50 | |||
| 51 | // The final winner over 8x8, 16x8 and 8x32. | ||
| 52 | // Transpose 32x8 (input) -> 8x32 (output) | ||
| 53 | // Input: 32 rows of 8 pixels (uint8_t) | ||
| 54 | // Output: 8 rows of 32 pixels | ||
| 55 | static AVS_FORCEINLINE void transpose_32x8x8_avx2(const uint8_t* AVS_RESTRICT srcp, uint8_t* AVS_RESTRICT dstp, const int src_pitch, const int dst_pitch) | ||
| 56 | { | ||
| 57 | // Load 32 rows, 8 bytes each | ||
| 58 | // One 256 bit register contains 4x8 bytes, which are 4 rows apart, so A,E,I,M in reg0, B,F,J,N in reg1, etc. | ||
| 59 | // after reaching the 16th row, it continues with Q,U,Y,c in reg4, R,V,Z,d in reg5, etc. | ||
| 60 | ✗ | auto load_quad_row = [](const uint8_t* base, int pitch, int offset) -> __m256i { | |
| 61 | ✗ | __m128i r0 = _mm_loadu_si64((const __m128i*)(base + (offset + 0 * 4) * pitch)); | |
| 62 | ✗ | __m128i r1 = _mm_loadu_si64((const __m128i*)(base + (offset + 1 * 4) * pitch)); | |
| 63 | ✗ | __m128i r2 = _mm_loadu_si64((const __m128i*)(base + (offset + 2 * 4) * pitch)); | |
| 64 | ✗ | __m128i r3 = _mm_loadu_si64((const __m128i*)(base + (offset + 3 * 4) * pitch)); | |
| 65 | |||
| 66 | ✗ | __m128i r01 = _mm_unpacklo_epi64(r0, r1); | |
| 67 | ✗ | __m128i r23 = _mm_unpacklo_epi64(r2, r3); | |
| 68 | ✗ | return _mm256_inserti128_si256(_mm256_castsi128_si256(r01), r23, 1); | |
| 69 | }; | ||
| 70 | |||
| 71 | // Load 32 rows into 8 registers | ||
| 72 | ✗ | __m256i r0 = load_quad_row(srcp, src_pitch, 0); // Rows 0,4,8,12 | |
| 73 | ✗ | __m256i r1 = load_quad_row(srcp, src_pitch, 1); // Rows 1,5,9,13 | |
| 74 | ✗ | __m256i r2 = load_quad_row(srcp, src_pitch, 2); // Rows 2,6,10,14 | |
| 75 | ✗ | __m256i r3 = load_quad_row(srcp, src_pitch, 3); // Rows 3,7,11,15 | |
| 76 | ✗ | __m256i r4 = load_quad_row(srcp, src_pitch, 16); // Rows 16,20,24,28 | |
| 77 | ✗ | __m256i r5 = load_quad_row(srcp, src_pitch, 17); // Rows 17,21,25,29 | |
| 78 | ✗ | __m256i r6 = load_quad_row(srcp, src_pitch, 18); // Rows 18,22,26,30 | |
| 79 | ✗ | __m256i r7 = load_quad_row(srcp, src_pitch, 19); // Rows 19,23,27,31 | |
| 80 | |||
| 81 | ✗ | __m256i t0 = _mm256_unpacklo_epi8(r0, r1); | |
| 82 | ✗ | __m256i t1 = _mm256_unpackhi_epi8(r0, r1); | |
| 83 | ✗ | __m256i t2 = _mm256_unpacklo_epi8(r2, r3); | |
| 84 | ✗ | __m256i t3 = _mm256_unpackhi_epi8(r2, r3); | |
| 85 | ✗ | __m256i t4 = _mm256_unpacklo_epi8(r4, r5); | |
| 86 | ✗ | __m256i t5 = _mm256_unpackhi_epi8(r4, r5); | |
| 87 | ✗ | __m256i t6 = _mm256_unpacklo_epi8(r6, r7); | |
| 88 | ✗ | __m256i t7 = _mm256_unpackhi_epi8(r6, r7); | |
| 89 | |||
| 90 | ✗ | __m256i u0 = _mm256_unpacklo_epi16(t0, t2); | |
| 91 | ✗ | __m256i u1 = _mm256_unpacklo_epi16(t1, t3); | |
| 92 | ✗ | __m256i u2 = _mm256_unpackhi_epi16(t0, t2); | |
| 93 | ✗ | __m256i u3 = _mm256_unpackhi_epi16(t1, t3); | |
| 94 | ✗ | __m256i u4 = _mm256_unpacklo_epi16(t4, t6); | |
| 95 | ✗ | __m256i u5 = _mm256_unpacklo_epi16(t5, t7); | |
| 96 | ✗ | __m256i u6 = _mm256_unpackhi_epi16(t4, t6); | |
| 97 | ✗ | __m256i u7 = _mm256_unpackhi_epi16(t5, t7); | |
| 98 | |||
| 99 | ✗ | __m256i v0 = _mm256_unpacklo_epi32(u0, u1); | |
| 100 | ✗ | __m256i v1 = _mm256_unpackhi_epi32(u0, u1); | |
| 101 | ✗ | __m256i v2 = _mm256_unpacklo_epi32(u2, u3); | |
| 102 | ✗ | __m256i v3 = _mm256_unpackhi_epi32(u2, u3); | |
| 103 | ✗ | __m256i v4 = _mm256_unpacklo_epi32(u4, u5); | |
| 104 | ✗ | __m256i v5 = _mm256_unpackhi_epi32(u4, u5); | |
| 105 | ✗ | __m256i v6 = _mm256_unpacklo_epi32(u6, u7); | |
| 106 | ✗ | __m256i v7 = _mm256_unpackhi_epi32(u6, u7); | |
| 107 | |||
| 108 | ✗ | __m256i w0 = _mm256_unpacklo_epi64(v0, v4); // AH0, QX0, IP0, Yf0 | |
| 109 | ✗ | __m256i w1 = _mm256_unpackhi_epi64(v0, v4); // AH1, QX1, IP1, Yf1 | |
| 110 | ✗ | __m256i w2 = _mm256_unpacklo_epi64(v1, v5); // AH2, QX2, IP2, Yf2 | |
| 111 | ✗ | __m256i w3 = _mm256_unpackhi_epi64(v1, v5); // AH3, QX3, IP3, Yf3 | |
| 112 | ✗ | __m256i w4 = _mm256_unpacklo_epi64(v2, v6); // AH4, QX4, IP4, Yf4 | |
| 113 | ✗ | __m256i w5 = _mm256_unpackhi_epi64(v2, v6); // AH5, QX5, IP5, Yf5 | |
| 114 | ✗ | __m256i w6 = _mm256_unpacklo_epi64(v3, v7); // AH6, QX6, IP6, Yf6 | |
| 115 | ✗ | __m256i w7 = _mm256_unpackhi_epi64(v3, v7); // AH7, QX7, IP7, Yf7 | |
| 116 | |||
| 117 | // Cross-lane permute to fix AVX2 dual-lane unpack. | ||
| 118 | // Arrange 64-bit blocks into the correct linear order | ||
| 119 | ✗ | const int PERM_CTRL = 0b11'01'10'00; // Standard 0,2,1,3 permute | |
| 120 | ✗ | __m256i out0 = _mm256_permute4x64_epi64(w0, PERM_CTRL); // AH0 | IP0 | QX0 | Yf0: ABCEFGHIJKLMNOPQRTSUVWXYZabcdef_0 ready | |
| 121 | ✗ | __m256i out1 = _mm256_permute4x64_epi64(w1, PERM_CTRL); // AH1 | IP1 | QX1 | Yf1 | |
| 122 | ✗ | __m256i out2 = _mm256_permute4x64_epi64(w2, PERM_CTRL); // AH2 | IP2 | QX2 | Yf2 | |
| 123 | ✗ | __m256i out3 = _mm256_permute4x64_epi64(w3, PERM_CTRL); // AH3 | IP3 | QX3 | Yf3 | |
| 124 | ✗ | __m256i out4 = _mm256_permute4x64_epi64(w4, PERM_CTRL); // AH4 | IP4 | QX4 | Yf4 | |
| 125 | ✗ | __m256i out5 = _mm256_permute4x64_epi64(w5, PERM_CTRL); // AH5 | IP5 | QX5 | Yf5 | |
| 126 | ✗ | __m256i out6 = _mm256_permute4x64_epi64(w6, PERM_CTRL); // AH6 | IP6 | QX6 | Yf6 | |
| 127 | ✗ | __m256i out7 = _mm256_permute4x64_epi64(w7, PERM_CTRL); // AH7 | IP7 | QX7 | Yf7 | |
| 128 | |||
| 129 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + 0 * dst_pitch), out0); | ||
| 130 | ✗ | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + 1 * dst_pitch), out1); | |
| 131 | ✗ | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + 2 * dst_pitch), out2); | |
| 132 | ✗ | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + 3 * dst_pitch), out3); | |
| 133 | ✗ | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + 4 * dst_pitch), out4); | |
| 134 | ✗ | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + 5 * dst_pitch), out5); | |
| 135 | ✗ | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + 6 * dst_pitch), out6); | |
| 136 | ✗ | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + 7 * dst_pitch), out7); | |
| 137 | ✗ | } | |
| 138 | |||
| 139 | 28 | void turn_right_plane_8_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 140 | { | ||
| 141 | // Start at the BOTTOM left of the source | ||
| 142 | 28 | const uint8_t* s0 = srcp + src_pitch * (src_height - 1); | |
| 143 | |||
| 144 | 28 | constexpr int PIXELS_W = 8; // width block size | |
| 145 | 28 | constexpr int PIXELS_H = 32; // height block size | |
| 146 | |||
| 147 | 28 | const int w = src_rowsize & ~(PIXELS_W * sizeof(uint8_t) - 1); | |
| 148 | 28 | const int h = src_height & ~(PIXELS_H - 1); | |
| 149 | 28 | const int simd_width_in_pixels = w / sizeof(uint8_t); | |
| 150 | |||
| 151 |
1/2✗ Branch 88 → 3 not taken.
✓ Branch 88 → 89 taken 28 times.
|
28 | for (int y = 0; y < h; y += PIXELS_H) |
| 152 | { | ||
| 153 | // Destination starts at y offset | ||
| 154 | ✗ | uint8_t* d0 = dstp + y; | |
| 155 | |||
| 156 | ✗ | for (int x = 0; x < w; x += PIXELS_W * sizeof(uint8_t)) | |
| 157 | { | ||
| 158 | // Use negative pitch to load rows bottom-to-top to avoid row reversing after transpose. | ||
| 159 | ✗ | transpose_32x8x8_avx2(s0 + x, d0, -src_pitch, dst_pitch); | |
| 160 | |||
| 161 | ✗ | d0 += PIXELS_W * dst_pitch; | |
| 162 | } | ||
| 163 | ✗ | s0 -= PIXELS_H * src_pitch; | |
| 164 | } | ||
| 165 | |||
| 166 | // Boundary handling fallback | ||
| 167 |
1/2✓ Branch 89 → 90 taken 28 times.
✗ Branch 89 → 91 not taken.
|
28 | if (src_rowsize != w) |
| 168 | { | ||
| 169 | // consider calling the sse2 version when difference is at least 8, since sse2 does 8x8 blocks | ||
| 170 | 28 | turn_right_plane_8_c(srcp + w, dstp + dst_pitch * simd_width_in_pixels, src_rowsize - w, src_height, src_pitch, dst_pitch); | |
| 171 | } | ||
| 172 | |||
| 173 |
1/2✓ Branch 91 → 92 taken 28 times.
✗ Branch 91 → 93 not taken.
|
28 | if (src_height != h) |
| 174 | { | ||
| 175 | 28 | turn_right_plane_8_c(srcp, dstp + h * sizeof(uint8_t), src_rowsize, src_height - h, src_pitch, dst_pitch); | |
| 176 | } | ||
| 177 | 28 | } | |
| 178 | |||
| 179 | 14 | void turn_left_plane_8_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 180 | { | ||
| 181 | 14 | turn_right_plane_8_avx2(srcp + src_pitch * (src_height - 1), dstp + dst_pitch * (src_rowsize / sizeof(uint8_t) - 1), src_rowsize, src_height, -src_pitch, -dst_pitch); | |
| 182 | 14 | } | |
| 183 | |||
| 184 | //------------------------------------------------------------------------------------------------- | ||
| 185 | // 16-bit Transpose Kernels | ||
| 186 | //------------------------------------------------------------------------------------------------- | ||
| 187 | |||
| 188 | // Transpose 8x16 (input) -> 16x8 (output) | ||
| 189 | // Input: 16 rows of 8 pixels (uint16_t) | ||
| 190 | // Output: 8 rows of 16 pixels | ||
| 191 | static AVS_FORCEINLINE void transpose_16x8x16_avx2(const BYTE* AVS_RESTRICT srcp, BYTE* AVS_RESTRICT dstp, const int src_pitch, const int dst_pitch) | ||
| 192 | { | ||
| 193 | // Helper to load 16 bytes from row i (low lane) and row i+8 (high lane) | ||
| 194 | 32 | auto load_dual_row_avx2 = [](const BYTE* base, int pitch, int row_idx) -> __m256i | |
| 195 | { | ||
| 196 | 32 | __m128i lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(base + pitch * row_idx)); | |
| 197 | 64 | __m128i hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(base + pitch * (row_idx + 8))); | |
| 198 | 32 | return _mm256_inserti128_si256(_mm256_castsi128_si256(lo), hi, 1); | |
| 199 | }; | ||
| 200 | |||
| 201 | // Load 16 rows into 8 registers. | ||
| 202 | // Row x in low lane and row x+8 in high lane. | ||
| 203 | 4 | __m256i v0 = load_dual_row_avx2(srcp, src_pitch, 0); // r0 | r8 // A0..A7 | I0..I7 | |
| 204 | 4 | __m256i v1 = load_dual_row_avx2(srcp, src_pitch, 1); // r1 | r9 // B0..B7 | J0..J7 | |
| 205 | 4 | __m256i v2 = load_dual_row_avx2(srcp, src_pitch, 2); // r2 | r10 // C0..C7 | K0..K7 | |
| 206 | 4 | __m256i v3 = load_dual_row_avx2(srcp, src_pitch, 3); // r3 | r11 // D0..D7 | L0..L7 | |
| 207 | 4 | __m256i v4 = load_dual_row_avx2(srcp, src_pitch, 4); // r4 | r12 // E0..E7 | M0..M7 | |
| 208 | 4 | __m256i v5 = load_dual_row_avx2(srcp, src_pitch, 5); // r5 | r13 // F0..F7 | N0..N7 | |
| 209 | 4 | __m256i v6 = load_dual_row_avx2(srcp, src_pitch, 6); // r6 | r14 // G0..G7 | O0..O7 | |
| 210 | 4 | __m256i v7 = load_dual_row_avx2(srcp, src_pitch, 7); // r7 | r15 // H0..H7 | P0..P7 | |
| 211 | |||
| 212 | // Standard 8x8 Transpose Logic (applied to both lanes simultaneously) | ||
| 213 | 4 | __m256i t0 = _mm256_unpacklo_epi16(v0, v1); // A0 B0 A1 B1 A2 B2 A3 B3 | I0 J0 I1 J1 I2 J2 I3 J3 | |
| 214 | 4 | __m256i t1 = _mm256_unpackhi_epi16(v0, v1); // A4 B4 A5 B5 A6 B6 A7 B7 | I4 J4 I5 J5 I6 J6 I7 J7 | |
| 215 | 4 | __m256i t2 = _mm256_unpacklo_epi16(v2, v3); // C0 D0 C1 D1 C2 D2 C3 D3 | K0 L0 K1 L1 K2 L2 K3 L3 | |
| 216 | 4 | __m256i t3 = _mm256_unpackhi_epi16(v2, v3); // C4 D4 C5 D5 C6 D6 C7 D7 | K4 L4 K5 L5 K6 L6 K7 L7 | |
| 217 | 4 | __m256i t4 = _mm256_unpacklo_epi16(v4, v5); // E0 F0 E1 F1 E2 F2 E3 F3 | M0 N0 M1 N1 M2 N2 M3 N3 | |
| 218 | 4 | __m256i t5 = _mm256_unpackhi_epi16(v4, v5); // E4 F4 E5 F5 E6 F6 E7 F7 | M4 N4 M5 N5 M6 N6 M7 N7 | |
| 219 | 4 | __m256i t6 = _mm256_unpacklo_epi16(v6, v7); // G0 H0 G1 H1 G2 H2 G3 H3 | O0 P0 O1 P1 O2 P2 O3 P3 | |
| 220 | 4 | __m256i t7 = _mm256_unpackhi_epi16(v6, v7); // G4 H4 G5 H5 G6 H6 G7 H7 | O4 P4 O5 P5 O6 P6 O7 P7 | |
| 221 | |||
| 222 | 4 | __m256i m0 = _mm256_unpacklo_epi32(t0, t2); // AD0,AD1 | IL0,IL1 | |
| 223 | 4 | __m256i m1 = _mm256_unpackhi_epi32(t0, t2); // AD2,AD3 | IL2,IL3 | |
| 224 | 4 | __m256i m2 = _mm256_unpacklo_epi32(t1, t3); // AD4,AD5 | IL4,IL5 | |
| 225 | 4 | __m256i m3 = _mm256_unpackhi_epi32(t1, t3); // AD6,AD7 | IL6,IL7 | |
| 226 | 4 | __m256i m4 = _mm256_unpacklo_epi32(t4, t6); // EH0,EH1 | MP0,MP1 | |
| 227 | 4 | __m256i m5 = _mm256_unpackhi_epi32(t4, t6); // EH2,EH3 | MP2,MP3 | |
| 228 | 4 | __m256i m6 = _mm256_unpacklo_epi32(t5, t7); // EH4,EH5 | MP4,MP5 | |
| 229 | 4 | __m256i m7 = _mm256_unpackhi_epi32(t5, t7); // EH6,EH7 | MP6,MP7 | |
| 230 | |||
| 231 | 4 | __m256i out0 = _mm256_unpacklo_epi64(m0, m4); // AD0,EH0 | IL0,MP0 | |
| 232 | 4 | __m256i out1 = _mm256_unpackhi_epi64(m0, m4); // AD1,EH1 | IL1,MP1 | |
| 233 | 4 | __m256i out2 = _mm256_unpacklo_epi64(m1, m5); // AD2,EH2 | IL2,MP2 | |
| 234 | 4 | __m256i out3 = _mm256_unpackhi_epi64(m1, m5); // AD3,EH3 | IL3,MP3 | |
| 235 | 4 | __m256i out4 = _mm256_unpacklo_epi64(m2, m6); // AD4,EH4 | IL4,MP4 | |
| 236 | 4 | __m256i out5 = _mm256_unpackhi_epi64(m2, m6); // AD5,EH5 | IL5,MP5 | |
| 237 | 4 | __m256i out6 = _mm256_unpacklo_epi64(m3, m7); // AD6,EH6 | IL6,MP6 | |
| 238 | 4 | __m256i out7 = _mm256_unpackhi_epi64(m3, m7); // AD7,EH7 | IL7,MP7 | |
| 239 | |||
| 240 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 0), out0); | ||
| 241 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 1), out1); | |
| 242 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 2), out2); | |
| 243 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 3), out3); | |
| 244 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 4), out4); | |
| 245 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 5), out5); | |
| 246 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 6), out6); | |
| 247 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 7), out7); | |
| 248 | 4 | } | |
| 249 | |||
| 250 | 8 | void turn_right_plane_16_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 251 | { | ||
| 252 | // source order: bottom to top | ||
| 253 | 8 | const BYTE* s0 = srcp + src_pitch * (src_height - 1); | |
| 254 | |||
| 255 | 8 | constexpr int PIXELS_W = 8; // width block size | |
| 256 | 8 | constexpr int PIXELS_H = 16; // height block size | |
| 257 | |||
| 258 | 8 | const int w = src_rowsize & ~(PIXELS_W * sizeof(uint16_t) - 1); | |
| 259 | 8 | const int h = src_height & ~(PIXELS_H - 1); | |
| 260 | 8 | const int simd_width_in_pixels = w / sizeof(uint16_t); | |
| 261 | |||
| 262 |
2/2✓ Branch 72 → 3 taken 2 times.
✓ Branch 72 → 73 taken 8 times.
|
10 | for (int y = 0; y < h; y += PIXELS_H) |
| 263 | { | ||
| 264 | 2 | BYTE* d0 = dstp + y * sizeof(uint16_t); | |
| 265 | |||
| 266 |
2/2✓ Branch 70 → 4 taken 4 times.
✓ Branch 70 → 71 taken 2 times.
|
6 | for (int x = 0; x < w; x += PIXELS_W * sizeof(uint16_t)) |
| 267 | { | ||
| 268 | // Use negative pitch to load rows bottom-to-top to avoid row reversing after transpose. | ||
| 269 | 4 | transpose_16x8x16_avx2(s0 + x, d0, -src_pitch, dst_pitch); | |
| 270 | 4 | d0 += PIXELS_W * dst_pitch; | |
| 271 | } | ||
| 272 | 2 | s0 -= PIXELS_H * src_pitch; | |
| 273 | } | ||
| 274 | |||
| 275 | // Boundary handling | ||
| 276 |
1/2✓ Branch 73 → 74 taken 8 times.
✗ Branch 73 → 75 not taken.
|
8 | if (src_rowsize != w) |
| 277 | { | ||
| 278 | 8 | turn_right_plane_16_c(srcp + w, dstp + dst_pitch * simd_width_in_pixels, src_rowsize - w, src_height, src_pitch, dst_pitch); | |
| 279 | } | ||
| 280 | |||
| 281 |
1/2✓ Branch 75 → 76 taken 8 times.
✗ Branch 75 → 77 not taken.
|
8 | if (src_height != h) |
| 282 | { | ||
| 283 | 8 | turn_right_plane_16_c(srcp, dstp + h * sizeof(uint16_t), src_rowsize, src_height - h, src_pitch, dst_pitch); | |
| 284 | } | ||
| 285 | 8 | } | |
| 286 | |||
| 287 | 4 | void turn_left_plane_16_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 288 | { | ||
| 289 | 4 | turn_right_plane_16_avx2(srcp + src_pitch * (src_height - 1), dstp + dst_pitch * (src_rowsize / sizeof(uint16_t) - 1), src_rowsize, src_height, -src_pitch, -dst_pitch); | |
| 290 | 4 | } | |
| 291 | |||
| 292 | //------------------------------------------------------------------------------------------------- | ||
| 293 | // 32-bit Transpose Kernels (Float/RGB32) | ||
| 294 | //------------------------------------------------------------------------------------------------- | ||
| 295 | |||
| 296 | static AVS_FORCEINLINE void transpose_8x8_32bit_avx2(const BYTE* AVS_RESTRICT srcp, BYTE* AVS_RESTRICT dstp, int src_pitch, int dst_pitch) | ||
| 297 | { | ||
| 298 | // load 8 rows of 8 pixels (32-bit each) | ||
| 299 | 4 | __m256i r0 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 0)); | |
| 300 | 4 | __m256i r1 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 1)); | |
| 301 | 4 | __m256i r2 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 2)); | |
| 302 | 4 | __m256i r3 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 3)); | |
| 303 | 4 | __m256i r4 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 4)); | |
| 304 | 4 | __m256i r5 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 5)); | |
| 305 | 4 | __m256i r6 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 6)); | |
| 306 | 8 | __m256i r7 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 7)); | |
| 307 | |||
| 308 | 4 | __m256i t0 = _mm256_unpacklo_epi32(r0, r1); | |
| 309 | 4 | __m256i t1 = _mm256_unpackhi_epi32(r0, r1); | |
| 310 | 4 | __m256i t2 = _mm256_unpacklo_epi32(r2, r3); | |
| 311 | 4 | __m256i t3 = _mm256_unpackhi_epi32(r2, r3); | |
| 312 | 4 | __m256i t4 = _mm256_unpacklo_epi32(r4, r5); | |
| 313 | 4 | __m256i t5 = _mm256_unpackhi_epi32(r4, r5); | |
| 314 | 4 | __m256i t6 = _mm256_unpacklo_epi32(r6, r7); | |
| 315 | 4 | __m256i t7 = _mm256_unpackhi_epi32(r6, r7); | |
| 316 | |||
| 317 | 4 | __m256i q0 = _mm256_unpacklo_epi64(t0, t2); | |
| 318 | 4 | __m256i q1 = _mm256_unpackhi_epi64(t0, t2); | |
| 319 | 4 | __m256i q2 = _mm256_unpacklo_epi64(t1, t3); | |
| 320 | 4 | __m256i q3 = _mm256_unpackhi_epi64(t1, t3); | |
| 321 | 4 | __m256i q4 = _mm256_unpacklo_epi64(t4, t6); | |
| 322 | 4 | __m256i q5 = _mm256_unpackhi_epi64(t4, t6); | |
| 323 | 4 | __m256i q6 = _mm256_unpacklo_epi64(t5, t7); | |
| 324 | 4 | __m256i q7 = _mm256_unpackhi_epi64(t5, t7); | |
| 325 | |||
| 326 | // cross-lane permute | ||
| 327 | // out0..3 gets low lane of (rows 0-3) and low lane of (rows 4-7) | ||
| 328 | // out4..7 gets high lane of (rows 0-3) and high lane of (rows 4-7) | ||
| 329 | 4 | __m256i out0 = _mm256_permute2x128_si256(q0, q4, 0x20); | |
| 330 | 4 | __m256i out1 = _mm256_permute2x128_si256(q1, q5, 0x20); | |
| 331 | 4 | __m256i out2 = _mm256_permute2x128_si256(q2, q6, 0x20); | |
| 332 | 4 | __m256i out3 = _mm256_permute2x128_si256(q3, q7, 0x20); | |
| 333 | 4 | __m256i out4 = _mm256_permute2x128_si256(q0, q4, 0x31); | |
| 334 | 4 | __m256i out5 = _mm256_permute2x128_si256(q1, q5, 0x31); | |
| 335 | 4 | __m256i out6 = _mm256_permute2x128_si256(q2, q6, 0x31); | |
| 336 | 4 | __m256i out7 = _mm256_permute2x128_si256(q3, q7, 0x31); | |
| 337 | |||
| 338 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 0), out0); | ||
| 339 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 1), out1); | |
| 340 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 2), out2); | |
| 341 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 3), out3); | |
| 342 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 4), out4); | |
| 343 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 5), out5); | |
| 344 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 6), out6); | |
| 345 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 7), out7); | |
| 346 | 4 | } | |
| 347 | |||
| 348 | // float, rgb32 | ||
| 349 | 4 | void turn_right_plane_32_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 350 | { | ||
| 351 | // source order: bottom to top | ||
| 352 | 4 | const BYTE* s0 = srcp + src_pitch * (src_height - 1); | |
| 353 | |||
| 354 | 4 | constexpr int PIXELS_W = 8; // width block size | |
| 355 | 4 | constexpr int PIXELS_H = 8; // height block size | |
| 356 | |||
| 357 | 4 | const int w = src_rowsize & ~(PIXELS_W * sizeof(uint32_t) - 1); | |
| 358 | 4 | const int h = src_height & ~(PIXELS_H - 1); | |
| 359 | 4 | const int simd_width_in_pixels = w / sizeof(uint32_t); | |
| 360 | |||
| 361 |
2/2✓ Branch 64 → 3 taken 4 times.
✓ Branch 64 → 65 taken 4 times.
|
8 | for (int y = 0; y < h; y += PIXELS_H) |
| 362 | { | ||
| 363 | // Destination grows forward: y index becomes the x-offset in the rotated image | ||
| 364 | 4 | BYTE* d0 = dstp + y * sizeof(uint32_t); | |
| 365 | |||
| 366 |
2/2✓ Branch 62 → 4 taken 4 times.
✓ Branch 62 → 63 taken 4 times.
|
8 | for (int x = 0; x < w; x += PIXELS_W * sizeof(uint32_t)) |
| 367 | { | ||
| 368 | // Use negative pitch to load rows bottom-to-top to avoid row reversing after transpose. | ||
| 369 | 4 | transpose_8x8_32bit_avx2(s0 + x, d0, -src_pitch, dst_pitch); | |
| 370 | 4 | d0 += PIXELS_W * dst_pitch; | |
| 371 | } | ||
| 372 | 4 | s0 -= PIXELS_H * src_pitch; | |
| 373 | } | ||
| 374 | |||
| 375 | // Boundary handling fallbacks | ||
| 376 |
1/2✓ Branch 65 → 66 taken 4 times.
✗ Branch 65 → 67 not taken.
|
4 | if (src_rowsize != w) |
| 377 | { | ||
| 378 | 4 | turn_right_plane_32_c(srcp + w, dstp + dst_pitch * simd_width_in_pixels, src_rowsize - w, src_height, src_pitch, dst_pitch); | |
| 379 | } | ||
| 380 |
1/2✓ Branch 67 → 68 taken 4 times.
✗ Branch 67 → 69 not taken.
|
4 | if (src_height != h) |
| 381 | { | ||
| 382 | 4 | turn_right_plane_32_c(srcp, dstp + h * sizeof(uint32_t), src_rowsize, src_height - h, src_pitch, dst_pitch); | |
| 383 | } | ||
| 384 | 4 | } | |
| 385 | |||
| 386 | 2 | void turn_left_plane_32_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 387 | { | ||
| 388 | 2 | turn_right_plane_32_avx2(srcp + src_pitch * (src_height - 1), dstp + dst_pitch * (src_rowsize / sizeof(float) - 1), src_rowsize, src_height, -src_pitch, -dst_pitch); | |
| 389 | 2 | } | |
| 390 | |||
| 391 | 1 | void turn_left_rgb32_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 392 | { | ||
| 393 | // packed rgb is upside down | ||
| 394 | 1 | turn_right_plane_32_avx2(srcp, dstp, src_rowsize, src_height, src_pitch, dst_pitch); | |
| 395 | 1 | } | |
| 396 | |||
| 397 | 1 | void turn_right_rgb32_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 398 | { | ||
| 399 | // packed rgb is upside down | ||
| 400 | 1 | turn_left_plane_32_avx2(srcp, dstp, src_rowsize, src_height, src_pitch, dst_pitch); | |
| 401 | 1 | } | |
| 402 | |||
| 403 | //------------------------------------------------------------------------------------------------- | ||
| 404 | // 64-bit Transpose Kernels (RGB64) | ||
| 405 | //------------------------------------------------------------------------------------------------- | ||
| 406 | |||
| 407 | static AVS_FORCEINLINE void transpose_4x4_64bit_avx2(const BYTE* AVS_RESTRICT srcp, BYTE* AVS_RESTRICT dstp, int src_pitch, int dst_pitch) | ||
| 408 | { | ||
| 409 | // Load 4 rows (4 pixels each reg = 32 bytes = 1 YMM register) | ||
| 410 | 4 | __m256i r0 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 0)); | |
| 411 | 4 | __m256i r1 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 1)); | |
| 412 | 4 | __m256i r2 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 2)); | |
| 413 | 8 | __m256i r3 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(srcp + src_pitch * 3)); | |
| 414 | |||
| 415 | 4 | __m256i t0 = _mm256_unpacklo_epi64(r0, r1); // [R0_P0, R1_P0 | R0_P2, R1_P2] | |
| 416 | 4 | __m256i t1 = _mm256_unpackhi_epi64(r0, r1); // [R0_P1, R1_P1 | R0_P3, R1_P3] | |
| 417 | 4 | __m256i t2 = _mm256_unpacklo_epi64(r2, r3); // [R2_P0, R3_P0 | R2_P2, R3_P2] | |
| 418 | 4 | __m256i t3 = _mm256_unpackhi_epi64(r2, r3); // [R2_P1, R3_P1 | R2_P3, R3_P3] | |
| 419 | |||
| 420 | // Cross lane permute | ||
| 421 | // Combine low lanes of t0/t2, t1/t3 and high lanes of t0/t2, t1/t3 | ||
| 422 | 4 | __m256i d0 = _mm256_permute2x128_si256(t0, t2, 0x20); // Column 0: [R0-R3]_P0 | |
| 423 | 4 | __m256i d1 = _mm256_permute2x128_si256(t1, t3, 0x20); // Column 1: [R0-R3]_P1 | |
| 424 | 4 | __m256i d2 = _mm256_permute2x128_si256(t0, t2, 0x31); // Column 2: [R0-R3]_P2 | |
| 425 | 4 | __m256i d3 = _mm256_permute2x128_si256(t1, t3, 0x31); // Column 3: [R0-R3]_P3 | |
| 426 | |||
| 427 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 0), d0); | ||
| 428 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 1), d1); | |
| 429 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 2), d2); | |
| 430 | 4 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + dst_pitch * 3), d3); | |
| 431 | 4 | } | |
| 432 | |||
| 433 | 4 | void turn_right_plane_64_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 434 | { | ||
| 435 | // Start at bottom row | ||
| 436 | 4 | const BYTE* s0 = srcp + src_pitch * (src_height - 1); | |
| 437 | |||
| 438 | 4 | constexpr int PIXELS_W = 4; | |
| 439 | 4 | constexpr int PIXELS_H = 4; | |
| 440 | |||
| 441 | 4 | const int w = src_rowsize & ~(PIXELS_W * sizeof(uint64_t) - 1); | |
| 442 | 4 | const int h = src_height & ~(PIXELS_H - 1); | |
| 443 | 4 | const int simd_width_in_pixels = w / sizeof(uint64_t); | |
| 444 | |||
| 445 |
2/2✓ Branch 28 → 3 taken 4 times.
✓ Branch 28 → 29 taken 4 times.
|
8 | for (int y = 0; y < h; y += PIXELS_H) |
| 446 | { | ||
| 447 | // Destination starts at y offset (which is the x-coordinate in turn-right) | ||
| 448 | 4 | BYTE* d0 = dstp + y * sizeof(uint64_t); | |
| 449 | |||
| 450 |
2/2✓ Branch 26 → 4 taken 4 times.
✓ Branch 26 → 27 taken 4 times.
|
8 | for (int x = 0; x < w; x += PIXELS_W * sizeof(uint64_t)) |
| 451 | { | ||
| 452 | // Standard transpose + Negative Pitch = 90 CW Turn | ||
| 453 | 4 | transpose_4x4_64bit_avx2(s0 + x, d0, -src_pitch, dst_pitch); | |
| 454 | 4 | d0 += PIXELS_W * dst_pitch; | |
| 455 | } | ||
| 456 | 4 | s0 -= PIXELS_H * src_pitch; | |
| 457 | } | ||
| 458 | |||
| 459 |
2/2✓ Branch 29 → 30 taken 3 times.
✓ Branch 29 → 31 taken 1 time.
|
4 | if (src_rowsize != w) |
| 460 | { | ||
| 461 | 3 | turn_right_plane_c<uint64_t>(srcp + w, dstp + dst_pitch * simd_width_in_pixels, src_rowsize - w, src_height, src_pitch, dst_pitch); | |
| 462 | } | ||
| 463 |
2/2✓ Branch 31 → 32 taken 2 times.
✓ Branch 31 → 33 taken 2 times.
|
4 | if (src_height != h) |
| 464 | { | ||
| 465 | 2 | turn_right_plane_c<uint64_t>(srcp, dstp + h * sizeof(uint64_t), src_rowsize, src_height - h, src_pitch, dst_pitch); | |
| 466 | } | ||
| 467 | |||
| 468 | 4 | } | |
| 469 | |||
| 470 | 2 | void turn_left_rgb64_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 471 | { | ||
| 472 | // packed rgb is upside down | ||
| 473 | 2 | turn_right_plane_64_avx2(srcp, dstp, src_rowsize, src_height, src_pitch, dst_pitch); | |
| 474 | 2 | } | |
| 475 | |||
| 476 | 2 | void turn_right_rgb64_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 477 | { | ||
| 478 | // packed rgb is upside down | ||
| 479 | 2 | turn_right_plane_64_avx2(srcp + src_pitch * (src_height - 1), dstp + dst_pitch * (src_rowsize / sizeof(uint64_t) - 1), src_rowsize, src_height, -src_pitch, -dst_pitch); | |
| 480 | 2 | } | |
| 481 | |||
| 482 | //------------------------------------------------------------------------------------------------- | ||
| 483 | // Turn 180 (Flip) | ||
| 484 | //------------------------------------------------------------------------------------------------- | ||
| 485 | |||
| 486 | template <typename T> | ||
| 487 | 14 | void turn_180_plane_avx2(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch) | |
| 488 | { | ||
| 489 | 14 | const BYTE* AVS_RESTRICT s0 = srcp; | |
| 490 | 14 | const int row_bytes = src_rowsize; // src_rowsize is the byte width of the row. | |
| 491 | |||
| 492 | // d0 points to the byte after the last pixel (byte) of the last row of dst. | ||
| 493 | // dst_pitch * (src_height - 1) is the start of the last row. | ||
| 494 | // + row_bytes is the address after the last byte of that row. | ||
| 495 | 14 | BYTE* AVS_RESTRICT d0 = dstp + dst_pitch * (src_height - 1) + row_bytes; | |
| 496 | |||
| 497 | 14 | constexpr int vector_bytes = 32; | |
| 498 | 14 | const int aligned_row_bytes = row_bytes & ~(vector_bytes - 1); | |
| 499 | |||
| 500 | // Prepare Shuffle Masks (These masks are correct for byte reversal within lanes) | ||
| 501 | __m256i shuf_mask; // only for 8/16-bit types | ||
| 502 | if constexpr (sizeof(T) == 1) { | ||
| 503 | // Reverse bytes in 16-byte chunks (31..16 and 15..0) | ||
| 504 | 8 | shuf_mask = _mm256_set_epi8( | |
| 505 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | ||
| 506 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 | ||
| 507 | ); | ||
| 508 | } | ||
| 509 | else if constexpr (sizeof(T) == 2) { | ||
| 510 | // Reverse words (16-bit elements) in 16-byte chunks | ||
| 511 | 4 | shuf_mask = _mm256_set_epi8( | |
| 512 | 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, | ||
| 513 | 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14 | ||
| 514 | ); | ||
| 515 | } | ||
| 516 | // For 32/64 we use permute instructions, no shuffle mask needed. | ||
| 517 | |||
| 518 |
8/8void turn_180_plane_avx2<unsigned char>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 18 → 5 taken 30 times.
✓ Branch 18 → 19 taken 8 times.
void turn_180_plane_avx2<unsigned int>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 18 → 3 taken 7 times.
✓ Branch 18 → 19 taken 1 time.
void turn_180_plane_avx2<unsigned long>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 14 → 3 taken 7 times.
✓ Branch 14 → 15 taken 1 time.
void turn_180_plane_avx2<unsigned short>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 18 → 5 taken 15 times.
✓ Branch 18 → 19 taken 4 times.
|
73 | for (int y = 0; y < src_height; ++y) |
| 519 | { | ||
| 520 | // Main AVX2 Loop | ||
| 521 |
8/8void turn_180_plane_avx2<unsigned char>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 12 → 6 taken 9 times.
✓ Branch 12 → 13 taken 30 times.
void turn_180_plane_avx2<unsigned int>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 12 → 4 taken 7 times.
✓ Branch 12 → 13 taken 7 times.
void turn_180_plane_avx2<unsigned long>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 8 → 4 taken 7 times.
✓ Branch 8 → 9 taken 7 times.
void turn_180_plane_avx2<unsigned short>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 12 → 6 taken 7 times.
✓ Branch 12 → 13 taken 15 times.
|
89 | for (int x = 0; x < aligned_row_bytes; x += vector_bytes) |
| 522 | { | ||
| 523 | 53 | __m256i src = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(s0 + x)); | |
| 524 | |||
| 525 | if constexpr (sizeof(T) == 8) { // uint64_t (4 pixels) | ||
| 526 | // Swap 64-bit elements: 3, 2, 1, 0 | ||
| 527 | 7 | src = _mm256_permute4x64_epi64(src, _MM_SHUFFLE(0, 1, 2, 3)); | |
| 528 | } | ||
| 529 | else if constexpr (sizeof(T) == 4) { // uint32_t (8 pixels) | ||
| 530 | // Reverse 32-bit elements: 7, 6, 5, 4, 3, 2, 1, 0 | ||
| 531 | 7 | const __m256i idx = _mm256_set_epi32(0, 1, 2, 3, 4, 5, 6, 7); | |
| 532 | 7 | src = _mm256_permutevar8x32_epi32(src, idx); | |
| 533 | } | ||
| 534 | else { // uint8_t or uint16_t | ||
| 535 | // reverse within 128-bit lanes (using precomputed mask) | ||
| 536 | 16 | src = _mm256_shuffle_epi8(src, shuf_mask); | |
| 537 | // Swap the 128-bit lanes | ||
| 538 | 16 | src = _mm256_permute2x128_si256(src, src, 0x01); | |
| 539 | } | ||
| 540 | |||
| 541 | // Store backwards: d0 points to end, x is byte offset from start. | ||
| 542 | 30 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(d0 - x - vector_bytes), src); | |
| 543 | } | ||
| 544 | |||
| 545 | // fallback for leftovers | ||
| 546 | 59 | int x = aligned_row_bytes; | |
| 547 | 59 | int rem_bytes = row_bytes - x; | |
| 548 | |||
| 549 |
4/8void turn_180_plane_avx2<unsigned char>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 13 → 14 taken 30 times.
✗ Branch 13 → 17 not taken.
void turn_180_plane_avx2<unsigned int>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 13 → 14 taken 7 times.
✗ Branch 13 → 17 not taken.
void turn_180_plane_avx2<unsigned long>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 9 → 10 taken 7 times.
✗ Branch 9 → 13 not taken.
void turn_180_plane_avx2<unsigned short>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 13 → 14 taken 15 times.
✗ Branch 13 → 17 not taken.
|
59 | if (rem_bytes > 0) { |
| 550 | 59 | const BYTE* src_tail = s0 + x; | |
| 551 | |||
| 552 | // Destination pointer: The first pixel of the remainder (src_tail) | ||
| 553 | // must go to the address *just before* where the AVX loop finished writing. | ||
| 554 | // The AVX loop finished writing at: d0 - aligned_row_bytes. | ||
| 555 | // The remaining bytes (rem_bytes) are written to fill the gap. | ||
| 556 | // Start of destination remainder region: d0 - row_bytes | ||
| 557 | 59 | BYTE* dst_rem_start = d0 - row_bytes; | |
| 558 | |||
| 559 | 59 | const int n_pixels = rem_bytes / sizeof(T); | |
| 560 | |||
| 561 | // Pointers for pixel-wise copy and reverse. | ||
| 562 | 59 | const T* s_ptr = reinterpret_cast<const T*>(src_tail); | |
| 563 | |||
| 564 | // d_ptr starts at the last pixel position of the remainder region, | ||
| 565 | // and moves backward to reverse the order. | ||
| 566 | 59 | T* d_ptr = reinterpret_cast<T*>(dst_rem_start + rem_bytes) - 1; | |
| 567 | |||
| 568 |
8/8void turn_180_plane_avx2<unsigned char>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 16 → 15 taken 114 times.
✓ Branch 16 → 17 taken 30 times.
void turn_180_plane_avx2<unsigned int>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 16 → 15 taken 7 times.
✓ Branch 16 → 17 taken 7 times.
void turn_180_plane_avx2<unsigned long>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 12 → 11 taken 7 times.
✓ Branch 12 → 13 taken 7 times.
void turn_180_plane_avx2<unsigned short>(unsigned char const*, unsigned char*, int, int, int, int):
✓ Branch 16 → 15 taken 43 times.
✓ Branch 16 → 17 taken 15 times.
|
230 | for (int k = 0; k < n_pixels; ++k) { |
| 569 | 171 | *d_ptr = s_ptr[k]; | |
| 570 | 171 | d_ptr--; // Move backward | |
| 571 | } | ||
| 572 | } | ||
| 573 | 59 | s0 += src_pitch; | |
| 574 | 59 | d0 -= dst_pitch; | |
| 575 | } | ||
| 576 | 14 | } | |
| 577 | |||
| 578 | // Instantiate templates | ||
| 579 | template void turn_180_plane_avx2<uint8_t>(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch); | ||
| 580 | template void turn_180_plane_avx2<uint16_t>(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch); | ||
| 581 | template void turn_180_plane_avx2<uint32_t>(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch); | ||
| 582 | template void turn_180_plane_avx2<uint64_t>(const BYTE* srcp, BYTE* dstp, int src_rowsize, int src_height, int src_pitch, int dst_pitch); | ||
| 583 |