GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 590 / 0 / 590
Functions: 100.0% 12 / 0 / 12
Branches: 93.8% 60 / 0 / 64

convert/intel/convert_rgb_avx2.cpp
Line Branch Exec Source
1 // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al.
2 // http://avisynth.nl
3
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 <avs/alignment.h>
36 #ifdef _MSC_VER
37 #include <intrin.h>
38 #else
39 #include <x86intrin.h>
40 #endif
41 #include <immintrin.h>
42
43 #ifndef _mm256_set_m128i
44 #define _mm256_set_m128i(v0, v1) _mm256_insertf128_si256(_mm256_castsi128_si256(v1), (v0), 1)
45 #endif
46
47 #ifndef _mm256_set_m128
48 #define _mm256_set_m128(v0, v1) _mm256_insertf128_ps(_mm256_castps128_ps256(v1), (v0), 1)
49 #endif
50
51 #include "convert_rgb_avx2.h"
52
53 // minimum width: 48*2 bytes
54 template<typename pixel_t, bool targetHasAlpha>
55 4 void convert_rgb_to_rgbp_avx2(const BYTE *srcp, BYTE * (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height)
56 {
57 // RGB24: 2x3x16 bytes cycle, 2x16*(RGB) 8bit pixels
58 // RGB48: 2x3x16 bytes cycle, 2x8*(RGB) 16bit pixels
59 // 0123456789ABCDEF 0123456789ABCDEF 0123456789ABCDEF
60 // BGRBGRBGRBGRBGRB GRBGRBGRBGRBGRBG RBGRBGRBGRBGRBGR // 8 bit
61 // B G R B G R B G R B G R B G R B G R B G R B G R // 16 bit
62 // 1111111111112222 2222222233333333 3333444444444444
63
64 4 constexpr int pixels_at_a_time = (sizeof(pixel_t) == 1) ? 32 : 16;
65 4 const int wmod = (width / pixels_at_a_time) * pixels_at_a_time; // 8 pixels for 8 bit, 4 pixels for 16 bit
66 __m256i mask;
67 if constexpr(sizeof(pixel_t) == 1)
68 2 mask = _mm256_set_epi8(15, 14, 13, 12, 11, 8, 5, 2, 10, 7, 4, 1, 9, 6, 3, 0,
69 15, 14, 13, 12, 11, 8, 5, 2, 10, 7, 4, 1, 9, 6, 3, 0); // same for both lanes
70 else
71 2 mask = _mm256_set_epi8(15, 14, 13, 12, 11, 10, 5, 4, 9, 8, 3, 2, 7, 6, 1, 0,
72 15, 14, 13, 12, 11, 10, 5, 4, 9, 8, 3, 2, 7, 6, 1, 0); // same for both lanes
73
74 __m256i max_pixel_value;
75 if constexpr(sizeof(pixel_t) == 1)
76 2 max_pixel_value = _mm256_set1_epi8((char)0xFF);
77 else
78 2 max_pixel_value = _mm256_set1_epi16((short)0xFFFF); // bits_per_pixel is 16
79
80 // read-optimized
81 #define SRC_ADDRESS_ADVANCES
82 #ifdef SRC_ADDRESS_ADVANCES
83 4 srcp -= src_pitch * (height - 1); // source packed RGB is upside down
84 4 dstp[0] += dst_pitch[0] * (height - 1);
85 4 dstp[1] += dst_pitch[1] * (height - 1);
86 4 dstp[2] += dst_pitch[2] * (height - 1);
87 if (targetHasAlpha)
88 2 dstp[3] += dst_pitch[3] * (height - 1);
89 #endif
90
91
8/8
void convert_rgb_to_rgbp_avx2<unsigned char, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 104 → 10 taken 5 times.
✓ Branch 104 → 105 taken 1 time.
void convert_rgb_to_rgbp_avx2<unsigned char, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 103 → 9 taken 5 times.
✓ Branch 103 → 104 taken 1 time.
void convert_rgb_to_rgbp_avx2<unsigned short, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 104 → 10 taken 5 times.
✓ Branch 104 → 105 taken 1 time.
void convert_rgb_to_rgbp_avx2<unsigned short, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 103 → 9 taken 5 times.
✓ Branch 103 → 104 taken 1 time.
24 for (int y = 0; y < height; y++) {
92
8/8
void convert_rgb_to_rgbp_avx2<unsigned char, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 56 → 11 taken 5 times.
✓ Branch 56 → 57 taken 5 times.
void convert_rgb_to_rgbp_avx2<unsigned char, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 55 → 10 taken 5 times.
✓ Branch 55 → 56 taken 5 times.
void convert_rgb_to_rgbp_avx2<unsigned short, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 56 → 11 taken 5 times.
✓ Branch 56 → 57 taken 5 times.
void convert_rgb_to_rgbp_avx2<unsigned short, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 55 → 10 taken 5 times.
✓ Branch 55 → 56 taken 5 times.
40 for (int x = 0; x < wmod; x += pixels_at_a_time) {
93 20 auto BGRA_1_Lo48 = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time));
94 20 auto BGRA_2_Lo48 = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 16));
95 20 auto BGRA_3_Lo48 = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 32));
96
97 20 auto BGRA_1_Hi48 = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 0 + 48));
98 20 auto BGRA_2_Hi48 = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 16 + 48));
99 20 auto BGRA_3_Hi48 = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 32 + 48));
100
101 20 auto BGRA_1 = _mm256_set_m128i(BGRA_1_Hi48, BGRA_1_Lo48);
102 20 auto BGRA_2 = _mm256_set_m128i(BGRA_2_Hi48, BGRA_2_Lo48);
103 40 auto BGRA_3 = _mm256_set_m128i(BGRA_3_Hi48, BGRA_3_Lo48);
104
105 20 auto pack_lo = _mm256_shuffle_epi8(BGRA_1, mask); // 111111111111: BBBBGGGGRRRR and rest: BGRB | BBGGRR and rest: BBGG
106 20 BGRA_1 = _mm256_alignr_epi8(BGRA_2, BGRA_1, 12);
107 20 auto pack_hi = _mm256_shuffle_epi8(BGRA_1, mask); // 222222222222: BBBBGGGGRRRR | BBGGRR
108 20 BGRA_2 = _mm256_alignr_epi8(BGRA_3, BGRA_2, 8);
109 20 auto pack_lo2 = _mm256_shuffle_epi8(BGRA_2, mask); // 333333333333: BBBBGGGGRRRR | BBGGRR
110 20 BGRA_3 = _mm256_srli_si256(BGRA_3, 4); // to use the same mask
111 20 auto pack_hi2 = _mm256_shuffle_epi8(BGRA_3, mask); // 444444444444: BBBBGGGGRRRR | BBGGRR
112
113 20 auto BG1 = _mm256_unpacklo_epi32(pack_lo, pack_hi); // BBBB_lo BBBB_hi GGGG_lo GGGG_hi
114 20 auto BG2 = _mm256_unpacklo_epi32(pack_lo2, pack_hi2); // BBBB_lo BBBB_hi GGGG_lo GGGG_hi
115 20 auto RA1 = _mm256_unpackhi_epi32(pack_lo, pack_hi); // RRRR_lo RRRR_hi AAAA_lo AAAA_hi
116 20 auto RA2 = _mm256_unpackhi_epi32(pack_lo2, pack_hi2); // RRRR_lo RRRR_hi AAAA_lo AAAA_hi
117 20 auto B = _mm256_unpacklo_epi64(BG1, BG2);
118 20 _mm256_stream_si256(reinterpret_cast<__m256i *>(dstp[1] + x * sizeof(pixel_t)), B); // B
119 20 auto G = _mm256_unpackhi_epi64(BG1, BG2);
120 20 _mm256_stream_si256(reinterpret_cast<__m256i *>(dstp[0] + x * sizeof(pixel_t)), G); // G
121 20 auto R = _mm256_unpacklo_epi64(RA1, RA2);
122 20 _mm256_stream_si256(reinterpret_cast<__m256i *>(dstp[2] + x * sizeof(pixel_t)), R); // R
123 if (targetHasAlpha)
124 10 _mm256_stream_si256(reinterpret_cast<__m256i *>(dstp[3] + x * sizeof(pixel_t)), max_pixel_value); // A
125 }
126 // rest, unaligned but simd
127
4/8
void convert_rgb_to_rgbp_avx2<unsigned char, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 57 → 58 taken 5 times.
✗ Branch 57 → 102 not taken.
void convert_rgb_to_rgbp_avx2<unsigned char, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 56 → 57 taken 5 times.
✗ Branch 56 → 102 not taken.
void convert_rgb_to_rgbp_avx2<unsigned short, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 57 → 58 taken 5 times.
✗ Branch 57 → 102 not taken.
void convert_rgb_to_rgbp_avx2<unsigned short, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 56 → 57 taken 5 times.
✗ Branch 56 → 102 not taken.
20 if (wmod != width) {
128 20 size_t x = (width - pixels_at_a_time);
129 20 auto BGRA_1_Lo48 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time));
130 20 auto BGRA_2_Lo48 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 16));
131 20 auto BGRA_3_Lo48 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 32));
132
133 20 auto BGRA_1_Hi48 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 0 + 48));
134 20 auto BGRA_2_Hi48 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 16 + 48));
135 20 auto BGRA_3_Hi48 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(srcp + x * 2 * 48 / pixels_at_a_time + 32 + 48));
136
137 20 auto BGRA_1 = _mm256_set_m128i(BGRA_1_Hi48, BGRA_1_Lo48);
138 20 auto BGRA_2 = _mm256_set_m128i(BGRA_2_Hi48, BGRA_2_Lo48);
139 40 auto BGRA_3 = _mm256_set_m128i(BGRA_3_Hi48, BGRA_3_Lo48);
140
141 20 auto pack_lo = _mm256_shuffle_epi8(BGRA_1, mask); // 111111111111: BBBBGGGGRRRR and rest: BGRB | BBGGRR and rest: BBGG
142 20 BGRA_1 = _mm256_alignr_epi8(BGRA_2, BGRA_1, 12);
143 20 auto pack_hi = _mm256_shuffle_epi8(BGRA_1, mask); // 222222222222: BBBBGGGGRRRR | BBGGRR
144 20 BGRA_2 = _mm256_alignr_epi8(BGRA_3, BGRA_2, 8);
145 20 auto pack_lo2 = _mm256_shuffle_epi8(BGRA_2, mask); // 333333333333: BBBBGGGGRRRR | BBGGRR
146 20 BGRA_3 = _mm256_srli_si256(BGRA_3, 4); // to use the same mask
147 20 auto pack_hi2 = _mm256_shuffle_epi8(BGRA_3, mask); // 444444444444: BBBBGGGGRRRR | BBGGRR
148
149 20 auto BG1 = _mm256_unpacklo_epi32(pack_lo, pack_hi); // BBBB_lo BBBB_hi GGGG_lo GGGG_hi
150 20 auto BG2 = _mm256_unpacklo_epi32(pack_lo2, pack_hi2); // BBBB_lo BBBB_hi GGGG_lo GGGG_hi
151 20 auto RA1 = _mm256_unpackhi_epi32(pack_lo, pack_hi); // RRRR_lo RRRR_hi AAAA_lo AAAA_hi
152 20 auto RA2 = _mm256_unpackhi_epi32(pack_lo2, pack_hi2); // RRRR_lo RRRR_hi AAAA_lo AAAA_hi
153 20 auto B = _mm256_unpacklo_epi64(BG1, BG2);
154 20 _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp[1] + x * sizeof(pixel_t)), B); // B
155 20 auto G = _mm256_unpackhi_epi64(BG1, BG2);
156 20 _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp[0] + x * sizeof(pixel_t)), G); // G
157 20 auto R = _mm256_unpacklo_epi64(RA1, RA2);
158 20 _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp[2] + x * sizeof(pixel_t)), R); // R
159 if (targetHasAlpha)
160 10 _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp[3] + x * sizeof(pixel_t)), max_pixel_value); // A
161 }
162 #ifdef SRC_ADDRESS_ADVANCES
163 20 srcp += src_pitch; // source packed RGB is upside down
164 20 dstp[0] -= dst_pitch[0];
165 20 dstp[1] -= dst_pitch[1];
166 20 dstp[2] -= dst_pitch[2];
167 if (targetHasAlpha)
168 10 dstp[3] -= dst_pitch[3];
169 #else
170 srcp -= src_pitch; // source packed RGB is upside down
171 dstp[0] += dst_pitch[0];
172 dstp[1] += dst_pitch[1];
173 dstp[2] += dst_pitch[2];
174 if (targetHasAlpha)
175 dstp[3] += dst_pitch[3];
176 #endif
177 }
178 #undef SRC_ADDRESS_ADVANCES
179 4 }
180
181 // Instantiate them
182 template void convert_rgb_to_rgbp_avx2<uint8_t, false>(const BYTE *srcp, BYTE * (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height);
183 template void convert_rgb_to_rgbp_avx2<uint8_t, true>(const BYTE *srcp, BYTE * (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height);
184 template void convert_rgb_to_rgbp_avx2<uint16_t, false>(const BYTE *srcp, BYTE * (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height);
185 template void convert_rgb_to_rgbp_avx2<uint16_t, true>(const BYTE *srcp, BYTE * (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height);
186
187 template<typename pixel_t, bool targetHasAlpha>
188 6 void convert_rgba_to_rgbp_avx2(const BYTE* srcp, BYTE* (&dstp)[4],
189 int src_pitch, int(&dst_pitch)[4], int width, int height)
190 {
191 6 const int pixels_per_iter = (sizeof(pixel_t) == 1) ? 16 : 8;
192 // 8-bit: process 16 pixels per loop (64 bytes in -> four 16-byte stores out)
193 // 16-bit: process 8 pixels per loop (64 bytes in -> four 16-byte stores out)
194 __m128i mask128;
195 if constexpr (sizeof(pixel_t) == 1)
196 3 mask128 = _mm_set_epi8(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0);
197 else
198 3 mask128 = _mm_set_epi8(15, 14, 7, 6, 13, 12, 5, 4, 11, 10, 3, 2, 9, 8, 1, 0);
199 6 __m256i vmask = _mm256_broadcastsi128_si256(mask128);
200 // Avisynth's scanline alignment is 64 bytes, so no remainder hanfling is needed for 16 RGB32 or 8 RGB64 pixels
201
8/8
void convert_rgba_to_rgbp_avx2<unsigned char, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 42 → 7 taken 8 times.
✓ Branch 42 → 43 taken 2 times.
void convert_rgba_to_rgbp_avx2<unsigned char, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 43 → 7 taken 5 times.
✓ Branch 43 → 44 taken 1 time.
void convert_rgba_to_rgbp_avx2<unsigned short, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 42 → 7 taken 5 times.
✓ Branch 42 → 43 taken 1 time.
void convert_rgba_to_rgbp_avx2<unsigned short, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 43 → 7 taken 8 times.
✓ Branch 43 → 44 taken 2 times.
32 for (int y = 0; y < height; ++y) {
202
8/8
void convert_rgba_to_rgbp_avx2<unsigned char, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 40 → 8 taken 13 times.
✓ Branch 40 → 41 taken 8 times.
void convert_rgba_to_rgbp_avx2<unsigned char, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 41 → 8 taken 10 times.
✓ Branch 41 → 42 taken 5 times.
void convert_rgba_to_rgbp_avx2<unsigned short, false>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 40 → 8 taken 10 times.
✓ Branch 40 → 41 taken 5 times.
void convert_rgba_to_rgbp_avx2<unsigned short, true>(unsigned char const*, unsigned char* (&) [4], int, int (&) [4], int, int):
✓ Branch 41 → 8 taken 13 times.
✓ Branch 41 → 42 taken 8 times.
72 for (int x = 0; x < width; x += pixels_per_iter) {
203 46 __m256i srcA = _mm256_load_si256(reinterpret_cast<const __m256i*>(srcp + x * 4 * sizeof(pixel_t)));
204 92 __m256i srcB = _mm256_load_si256(reinterpret_cast<const __m256i*>(srcp + (x + pixels_per_iter / 2) * 4 * sizeof(pixel_t)));
205
206 46 __m256i shufA = _mm256_shuffle_epi8(srcA, vmask);
207 46 __m256i shufB = _mm256_shuffle_epi8(srcB, vmask);
208
209 46 __m128i a_lo = _mm256_castsi256_si128(shufA);
210 46 __m128i a_hi = _mm256_extracti128_si256(shufA, 1);
211 46 __m128i b_lo = _mm256_castsi256_si128(shufB);
212 46 __m128i b_hi = _mm256_extracti128_si256(shufB, 1);
213
214 46 __m128i bg_0 = _mm_unpacklo_epi32(a_lo, a_hi);
215 46 __m128i ra_0 = _mm_unpackhi_epi32(a_lo, a_hi);
216 46 __m128i bg_1 = _mm_unpacklo_epi32(b_lo, b_hi);
217 46 __m128i ra_1 = _mm_unpackhi_epi32(b_lo, b_hi);
218
219 46 __m128i chB = _mm_unpacklo_epi64(bg_0, bg_1);
220 46 __m128i chG = _mm_unpackhi_epi64(bg_0, bg_1);
221 46 __m128i chR = _mm_unpacklo_epi64(ra_0, ra_1);
222 46 __m128i chA = _mm_unpackhi_epi64(ra_0, ra_1);
223
224 46 _mm_store_si128(reinterpret_cast<__m128i*>(dstp[1] + x * sizeof(pixel_t)), chB);
225 46 _mm_store_si128(reinterpret_cast<__m128i*>(dstp[0] + x * sizeof(pixel_t)), chG);
226 46 _mm_store_si128(reinterpret_cast<__m128i*>(dstp[2] + x * sizeof(pixel_t)), chR);
227 if constexpr (targetHasAlpha)
228 23 _mm_store_si128(reinterpret_cast<__m128i*>(dstp[3] + x * sizeof(pixel_t)), chA);
229 }
230
231 26 srcp -= src_pitch; // source packed RGB is upside down
232 26 dstp[0] += dst_pitch[0];
233 26 dstp[1] += dst_pitch[1];
234 26 dstp[2] += dst_pitch[2];
235 if constexpr (targetHasAlpha)
236 13 dstp[3] += dst_pitch[3];
237 }
238 6 }
239
240 // Instantiate them
241 template void convert_rgba_to_rgbp_avx2<uint8_t, false>(const BYTE* srcp, BYTE* (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height);
242 template void convert_rgba_to_rgbp_avx2<uint8_t, true>(const BYTE* srcp, BYTE* (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height);
243 template void convert_rgba_to_rgbp_avx2<uint16_t, false>(const BYTE* srcp, BYTE* (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height);
244 template void convert_rgba_to_rgbp_avx2<uint16_t, true>(const BYTE* srcp, BYTE* (&dstp)[4], int src_pitch, int(&dst_pitch)[4], int width, int height);
245
246 // Planar RGB(A) → packed RGBA32/RGBA64 (reverse of convert_rgba_to_rgbp_avx2)
247 //
248 // Main loop: 128 bytes written per iteration
249 // uint8_t: 32 pixels → 4 × _mm256_store_si256
250 // uint16_t: 16 pixels → 4 × _mm256_store_si256
251 // Tail: 64 bytes (one half-iteration, uses SSE2 128-bit stores)
252 // uint8_t: 16 pixels → 4 × _mm_store_si128
253 // uint16_t: 8 pixels → 4 × _mm_store_si128
254 //
255 // Scanline alignment is 64 bytes (Avisynth guarantee), so aligned SIMD
256 // loads/stores are safe. The tail is handled fully in SIMD, potentially
257 // overreading/overwriting into padded bytes (same strategy as SSE2 path).
258
259 template<typename pixel_t, bool hasSrcAlpha>
260 5 void convert_rgbp_to_rgba_avx2(const BYTE* (&srcp)[4], BYTE* dstp, int(&src_pitch)[4], int dst_pitch, int width, int height)
261 {
262 // big: pixels processed per 128-byte iteration; small: 64-byte tail size
263 5 constexpr int big_pixels = (sizeof(pixel_t) == 1) ? 32 : 16;
264 5 constexpr int small_pixels = big_pixels / 2; // 16 (u8) or 8 (u16)
265 5 const int wmod = (width / big_pixels) * big_pixels;
266
267 5 const __m256i transparent256 = _mm256_set1_epi8((char)0xFF);
268 5 const __m128i transparent128 = _mm_set1_epi8((char)0xFF);
269
270
8/8
void convert_rgbp_to_rgba_avx2<unsigned char, false>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 70 → 11 taken 8 times.
✓ Branch 70 → 71 taken 2 times.
void convert_rgbp_to_rgba_avx2<unsigned char, true>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 74 → 11 taken 5 times.
✓ Branch 74 → 75 taken 1 time.
void convert_rgbp_to_rgba_avx2<unsigned short, false>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 70 → 11 taken 5 times.
✓ Branch 70 → 71 taken 1 time.
void convert_rgbp_to_rgba_avx2<unsigned short, true>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 74 → 11 taken 5 times.
✓ Branch 74 → 75 taken 1 time.
28 for (int y = 0; y < height; y++) {
271 // main loop: 128 bytes output per iteration
272
8/8
void convert_rgbp_to_rgba_avx2<unsigned char, false>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 39 → 12 taken 5 times.
✓ Branch 39 → 40 taken 8 times.
void convert_rgbp_to_rgba_avx2<unsigned char, true>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 41 → 12 taken 5 times.
✓ Branch 41 → 42 taken 5 times.
void convert_rgbp_to_rgba_avx2<unsigned short, false>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 39 → 12 taken 5 times.
✓ Branch 39 → 40 taken 5 times.
void convert_rgbp_to_rgba_avx2<unsigned short, true>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 41 → 12 taken 5 times.
✓ Branch 41 → 42 taken 5 times.
43 for (int x = 0; x < wmod; x += big_pixels) {
273 20 __m256i G = _mm256_load_si256(reinterpret_cast<const __m256i*>(srcp[0] + x * sizeof(pixel_t)));
274 20 __m256i B = _mm256_load_si256(reinterpret_cast<const __m256i*>(srcp[1] + x * sizeof(pixel_t)));
275 20 __m256i R = _mm256_load_si256(reinterpret_cast<const __m256i*>(srcp[2] + x * sizeof(pixel_t)));
276 __m256i A;
277 if constexpr (hasSrcAlpha)
278 20 A = _mm256_load_si256(reinterpret_cast<const __m256i*>(srcp[3] + x * sizeof(pixel_t)));
279 else
280 10 A = transparent256;
281
282 __m256i u0, u1, u2, u3;
283 if constexpr (sizeof(pixel_t) == 1) {
284 10 __m256i BG_lo = _mm256_unpacklo_epi8(B, G);
285 10 __m256i BG_hi = _mm256_unpackhi_epi8(B, G);
286 10 __m256i RA_lo = _mm256_unpacklo_epi8(R, A);
287 10 __m256i RA_hi = _mm256_unpackhi_epi8(R, A);
288 10 u0 = _mm256_unpacklo_epi16(BG_lo, RA_lo); // [px 0-3 | px16-19]
289 10 u1 = _mm256_unpackhi_epi16(BG_lo, RA_lo); // [px 4-7 | px20-23]
290 10 u2 = _mm256_unpacklo_epi16(BG_hi, RA_hi); // [px 8-11 | px24-27]
291 10 u3 = _mm256_unpackhi_epi16(BG_hi, RA_hi); // [px12-15 | px28-31]
292 } else {
293 10 __m256i BG_lo = _mm256_unpacklo_epi16(B, G);
294 10 __m256i BG_hi = _mm256_unpackhi_epi16(B, G);
295 10 __m256i RA_lo = _mm256_unpacklo_epi16(R, A);
296 10 __m256i RA_hi = _mm256_unpackhi_epi16(R, A);
297 10 u0 = _mm256_unpacklo_epi32(BG_lo, RA_lo); // [px 0-1 | px 8-9 ]
298 10 u1 = _mm256_unpackhi_epi32(BG_lo, RA_lo); // [px 2-3 | px10-11]
299 10 u2 = _mm256_unpacklo_epi32(BG_hi, RA_hi); // [px 4-5 | px12-13]
300 10 u3 = _mm256_unpackhi_epi32(BG_hi, RA_hi); // [px 6-7 | px14-15]
301 }
302 20 BYTE* d = dstp + x * 4 * sizeof(pixel_t);
303 20 _mm256_store_si256(reinterpret_cast<__m256i*>(d + 0), _mm256_permute2x128_si256(u0, u1, 0x20));
304 20 _mm256_store_si256(reinterpret_cast<__m256i*>(d + 32), _mm256_permute2x128_si256(u2, u3, 0x20));
305 20 _mm256_store_si256(reinterpret_cast<__m256i*>(d + 64), _mm256_permute2x128_si256(u0, u1, 0x31));
306 20 _mm256_store_si256(reinterpret_cast<__m256i*>(d + 96), _mm256_permute2x128_si256(u2, u3, 0x31));
307 }
308
309 // tail: process remaining pixels in small SIMD chunks
310 // (may touch padded bytes beyond logical width)
311
8/8
void convert_rgbp_to_rgba_avx2<unsigned char, false>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 68 → 41 taken 8 times.
✓ Branch 68 → 69 taken 8 times.
void convert_rgbp_to_rgba_avx2<unsigned char, true>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 72 → 43 taken 5 times.
✓ Branch 72 → 73 taken 5 times.
void convert_rgbp_to_rgba_avx2<unsigned short, false>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 68 → 41 taken 5 times.
✓ Branch 68 → 69 taken 5 times.
void convert_rgbp_to_rgba_avx2<unsigned short, true>(unsigned char const* (&) [4], unsigned char*, int (&) [4], int, int, int):
✓ Branch 72 → 43 taken 5 times.
✓ Branch 72 → 73 taken 5 times.
46 for (int tx = wmod; tx < width; tx += small_pixels) {
312 23 __m128i tG = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp[0] + tx * sizeof(pixel_t)));
313 23 __m128i tB = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp[1] + tx * sizeof(pixel_t)));
314 23 __m128i tR = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp[2] + tx * sizeof(pixel_t)));
315 __m128i tA;
316 if constexpr (hasSrcAlpha)
317 10 tA = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp[3] + tx * sizeof(pixel_t)));
318 else
319 13 tA = transparent128;
320
321 23 BYTE* d = dstp + tx * 4 * sizeof(pixel_t);
322 if constexpr (sizeof(pixel_t) == 1) {
323 13 __m128i BG_lo = _mm_unpacklo_epi8(tB, tG);
324 13 __m128i BG_hi = _mm_unpackhi_epi8(tB, tG);
325 13 __m128i RA_lo = _mm_unpacklo_epi8(tR, tA);
326 13 __m128i RA_hi = _mm_unpackhi_epi8(tR, tA);
327 13 _mm_store_si128(reinterpret_cast<__m128i*>(d + 0), _mm_unpacklo_epi16(BG_lo, RA_lo)); // px tx+0..3
328 13 _mm_store_si128(reinterpret_cast<__m128i*>(d + 16), _mm_unpackhi_epi16(BG_lo, RA_lo)); // px tx+4..7
329 13 _mm_store_si128(reinterpret_cast<__m128i*>(d + 32), _mm_unpacklo_epi16(BG_hi, RA_hi)); // px tx+8..11
330 13 _mm_store_si128(reinterpret_cast<__m128i*>(d + 48), _mm_unpackhi_epi16(BG_hi, RA_hi)); // px tx+12..15
331 } else {
332 10 __m128i BG_lo = _mm_unpacklo_epi16(tB, tG);
333 10 __m128i BG_hi = _mm_unpackhi_epi16(tB, tG);
334 10 __m128i RA_lo = _mm_unpacklo_epi16(tR, tA);
335 10 __m128i RA_hi = _mm_unpackhi_epi16(tR, tA);
336 10 _mm_store_si128(reinterpret_cast<__m128i*>(d + 0), _mm_unpacklo_epi32(BG_lo, RA_lo)); // px tx+0..1
337 10 _mm_store_si128(reinterpret_cast<__m128i*>(d + 16), _mm_unpackhi_epi32(BG_lo, RA_lo)); // px tx+2..3
338 10 _mm_store_si128(reinterpret_cast<__m128i*>(d + 32), _mm_unpacklo_epi32(BG_hi, RA_hi)); // px tx+4..5
339 10 _mm_store_si128(reinterpret_cast<__m128i*>(d + 48), _mm_unpackhi_epi32(BG_hi, RA_hi)); // px tx+6..7
340 }
341 }
342
343 23 dstp -= dst_pitch;
344 23 srcp[0] += src_pitch[0];
345 23 srcp[1] += src_pitch[1];
346 23 srcp[2] += src_pitch[2];
347 if constexpr (hasSrcAlpha)
348 10 srcp[3] += src_pitch[3];
349 }
350 5 }
351
352 // Instantiate them
353 template void convert_rgbp_to_rgba_avx2<uint8_t, false>(const BYTE* (&srcp)[4], BYTE* dstp, int(&src_pitch)[4], int dst_pitch, int width, int height);
354 template void convert_rgbp_to_rgba_avx2<uint8_t, true>(const BYTE* (&srcp)[4], BYTE* dstp, int(&src_pitch)[4], int dst_pitch, int width, int height);
355 template void convert_rgbp_to_rgba_avx2<uint16_t, false>(const BYTE* (&srcp)[4], BYTE* dstp, int(&src_pitch)[4], int dst_pitch, int width, int height);
356 template void convert_rgbp_to_rgba_avx2<uint16_t, true>(const BYTE* (&srcp)[4], BYTE* dstp, int(&src_pitch)[4], int dst_pitch, int width, int height);
357