filters/intel/greyscale_sse.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 | |||
| 36 | |||
| 37 | #include "greyscale_sse.h" | ||
| 38 | #include "../convert/convert_matrix.h" | ||
| 39 | |||
| 40 | // Intrinsics base header + really required extension headers | ||
| 41 | #if defined(_MSC_VER) | ||
| 42 | #include <intrin.h> // MSVC | ||
| 43 | #else | ||
| 44 | #include <x86intrin.h> // GCC/MinGW/Clang/LLVM | ||
| 45 | #endif | ||
| 46 | #include <smmintrin.h> // SSE4.1 | ||
| 47 | |||
| 48 | #include <avs/config.h> | ||
| 49 | #include <avs/types.h> | ||
| 50 | #include "avs/minmax.h" | ||
| 51 | |||
| 52 | 3 | void greyscale_yuy2_sse2(BYTE *srcp, size_t /*width*/, size_t height, size_t pitch) { | |
| 53 | 3 | __m128i luma_mask = _mm_set1_epi16(0x00FF); | |
| 54 | 3 | __m128i chroma_value = _mm_set1_epi16((short)0x8000); | |
| 55 | 3 | BYTE* end_point = srcp + pitch * height; | |
| 56 | |||
| 57 |
2/2✓ Branch 19 → 11 taken 63 times.
✓ Branch 19 → 20 taken 3 times.
|
66 | while(srcp < end_point) { |
| 58 | 63 | __m128i src = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp)); | |
| 59 | 63 | src = _mm_and_si128(src, luma_mask); | |
| 60 | 63 | src = _mm_or_si128(src, chroma_value); | |
| 61 | _mm_store_si128(reinterpret_cast<__m128i*>(srcp), src); | ||
| 62 | |||
| 63 | 63 | srcp += 16; | |
| 64 | } | ||
| 65 | 3 | } | |
| 66 | |||
| 67 | 4 | void greyscale_rgb32_sse2(BYTE *srcp, size_t /*width*/, size_t height, size_t pitch, ConversionMatrix &m) { | |
| 68 | 4 | const bool has_offset_rgb = 0 != m.offset_rgb; | |
| 69 | // greyscale RGB is putting pack the calculated pixels to rgb | ||
| 70 | // Limited range input remains limited range output (-offset_rgb is the same as offset_y) | ||
| 71 | |||
| 72 | 8 | __m128i matrix = _mm_set_epi16(0, m.y_r, m.y_g, m.y_b, 0, m.y_r, m.y_g, m.y_b); | |
| 73 | 4 | __m128i zero = _mm_setzero_si128(); | |
| 74 | // .15 frac bit integer arithmetic | ||
| 75 | 4 | int round_mask_and_luma_offset_i = (1 << 14) + (m.offset_y << 15); | |
| 76 | 4 | __m128i round_mask_and_luma_offset = _mm_set1_epi32(round_mask_and_luma_offset_i); // four pixels at a time | |
| 77 | 4 | __m128i alpha_mask = _mm_set1_epi32(0xFF000000); | |
| 78 | |||
| 79 | 4 | __m128i offset_rgb = _mm_set_epi16(0, m.offset_rgb, m.offset_rgb, m.offset_rgb, 0, m.offset_rgb, m.offset_rgb, m.offset_rgb); | |
| 80 | |||
| 81 | 4 | BYTE* end_point = srcp + pitch * height; | |
| 82 | |||
| 83 |
2/2✓ Branch 61 → 17 taken 100 times.
✓ Branch 61 → 62 taken 4 times.
|
104 | while(srcp < end_point) { |
| 84 | 100 | __m128i src = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp)); | |
| 85 | 100 | __m128i alpha = _mm_and_si128(src, alpha_mask); | |
| 86 | 100 | __m128i pixel01 = _mm_unpacklo_epi8(src, zero); | |
| 87 | 100 | __m128i pixel23 = _mm_unpackhi_epi8(src, zero); | |
| 88 | |||
| 89 |
2/2✓ Branch 25 → 26 taken 50 times.
✓ Branch 25 → 31 taken 50 times.
|
100 | if (has_offset_rgb) { |
| 90 | 50 | pixel01 = _mm_add_epi16(pixel01, offset_rgb); | |
| 91 | 50 | pixel23 = _mm_add_epi16(pixel23, offset_rgb); | |
| 92 | } | ||
| 93 | |||
| 94 | 100 | pixel01 = _mm_madd_epi16(pixel01, matrix); | |
| 95 | 100 | pixel23 = _mm_madd_epi16(pixel23, matrix); | |
| 96 | |||
| 97 | 300 | __m128i tmp = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(pixel01), _mm_castsi128_ps(pixel23), _MM_SHUFFLE(3, 1, 3, 1))); // r3*cyr | r2*cyr | r1*cyr | r0*cyr | |
| 98 | 300 | __m128i tmp2 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(pixel01), _mm_castsi128_ps(pixel23), _MM_SHUFFLE(2, 0, 2, 0))); | |
| 99 | |||
| 100 | 100 | tmp = _mm_add_epi32(tmp, tmp2); | |
| 101 | 100 | tmp = _mm_add_epi32(tmp, round_mask_and_luma_offset); | |
| 102 | 100 | tmp = _mm_srli_epi32(tmp, 15); // 0 0 0 p3 | 0 0 0 p2 | 0 0 0 p1 | 0 0 0 p0 the grey scale byte itself | |
| 103 | |||
| 104 | // make r=g=b | ||
| 105 | |||
| 106 | 100 | __m128i result = _mm_or_si128(tmp, _mm_slli_si128(tmp, 1)); // 0 0 p3 p3 | 0 0 p2 p2 | 0 0 p1 p1 | 0 0 p0 p0 | |
| 107 | 200 | result = _mm_or_si128(result, _mm_slli_si128(tmp, 2)); // 0 p3 p3 p3 | 0 p2 p2 p2 | 0 p1 p1 p1 | 0 p0 p0 p0 | |
| 108 | 100 | result = _mm_or_si128(alpha, result); | |
| 109 | |||
| 110 | _mm_store_si128(reinterpret_cast<__m128i*>(srcp), result); | ||
| 111 | |||
| 112 | 100 | srcp += 16; | |
| 113 | } | ||
| 114 | 4 | } | |
| 115 | |||
| 116 | #if defined(GCC) || defined(CLANG) | ||
| 117 | __attribute__((__target__("sse4.1"))) | ||
| 118 | #endif | ||
| 119 | 4 | void greyscale_rgb64_sse41(BYTE *srcp, size_t /*width*/, size_t height, size_t pitch, ConversionMatrix &m) | |
| 120 | { | ||
| 121 | 4 | const bool has_offset_rgb = 0 != m.offset_rgb; | |
| 122 | // greyscale RGB is putting pack the calculated pixels to rgb | ||
| 123 | // Limited range input remains limited range output (-offset_rgb is the same as offset_y) | ||
| 124 | |||
| 125 | 8 | __m128i matrix = _mm_set_epi32(0, m.y_r, m.y_g, m.y_b); | |
| 126 | 4 | __m128i zero = _mm_setzero_si128(); | |
| 127 | // .15 frac bit integer arithmetic | ||
| 128 | 4 | int round_mask_and_luma_offset_i = (1 << 14) + (m.offset_y << 15); | |
| 129 | 4 | __m128i round_mask_and_luma_offset = _mm_set_epi32(0, round_mask_and_luma_offset_i, round_mask_and_luma_offset_i, round_mask_and_luma_offset_i); | |
| 130 | 4 | uint64_t mask64 = 0xFFFF000000000000ull; | |
| 131 | 4 | __m128i alpha_mask = _mm_set_epi32((uint32_t)(mask64 >> 32),(uint32_t)mask64,(uint32_t)(mask64 >> 32),(uint32_t)mask64); | |
| 132 | |||
| 133 | 4 | __m128i offset_rgb = _mm_set_epi32(0, m.offset_rgb, m.offset_rgb, m.offset_rgb); // signed (e.g. -16) if exists, for addition | |
| 134 | |||
| 135 | 4 | BYTE* end_point = srcp + pitch * height; | |
| 136 | |||
| 137 |
2/2✓ Branch 49 → 13 taken 110 times.
✓ Branch 49 → 50 taken 4 times.
|
114 | while(srcp < end_point) { |
| 138 | 110 | __m128i src = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp)); // 2x64bit pixels | |
| 139 | |||
| 140 | 110 | __m128i srclo = _mm_unpacklo_epi16(src, zero); // pixel1 | |
| 141 |
2/2✓ Branch 17 → 18 taken 55 times.
✓ Branch 17 → 21 taken 55 times.
|
110 | if(has_offset_rgb) |
| 142 | 55 | srclo = _mm_add_epi32(srclo, offset_rgb); | |
| 143 | 110 | __m128i mullo = _mm_mullo_epi32(srclo, matrix); // 0, mul_r1, mul_g1, mul_b1 // sse41 | |
| 144 | |||
| 145 | 110 | __m128i srchi = _mm_unpackhi_epi16(src, zero); // pixel2 | |
| 146 |
2/2✓ Branch 25 → 26 taken 55 times.
✓ Branch 25 → 29 taken 55 times.
|
110 | if(has_offset_rgb) |
| 147 | 55 | srchi = _mm_add_epi32(srchi, offset_rgb); | |
| 148 | 110 | __m128i mulhi = _mm_mullo_epi32(srchi, matrix); // 0, mul_r2, mul_g2, mul_b2 // sse41 | |
| 149 | |||
| 150 | 110 | __m128i alpha = _mm_and_si128(src, alpha_mask); // put back later | |
| 151 | |||
| 152 | // ssse3 | ||
| 153 | 110 | __m128i result = _mm_hadd_epi32(mullo, mulhi); // 0+mul_r1 | mul_g1+mul_b1 | 0+mul_r2 | mul_g2+mul_b2 | |
| 154 | 110 | result = _mm_hadd_epi32(result, zero); // 0+mul_r1+mul_g1+mul_b1 | 0+mul_r2+mul_g2+mul_b2 | 0 | 0 | |
| 155 | |||
| 156 | 110 | result = _mm_add_epi32(result, round_mask_and_luma_offset); | |
| 157 | 110 | result = _mm_srli_epi32(result, 15); | |
| 158 | // we have the greyscale value of two pixels as int32 0 0 | 0 0 | 0 p1 | 0 p0 | ||
| 159 | // we need 0 p1 p1 p1 0 p0 p0 p0 | ||
| 160 | |||
| 161 | 220 | __m128i result1 = _mm_or_si128(_mm_slli_si128(result, 2), result); | |
| 162 | // 0 0 | 0 0 | p1 p1 | p0 p0 | ||
| 163 | 110 | result = _mm_unpacklo_epi32(result1, result); | |
| 164 | // 0 p1 | p1 p1 | 0 p0 | p0 p0 | ||
| 165 | |||
| 166 | 110 | result = _mm_or_si128(alpha, result); // put back initial alpha | |
| 167 | |||
| 168 | _mm_store_si128(reinterpret_cast<__m128i*>(srcp), result); | ||
| 169 | |||
| 170 | 110 | srcp += 16; | |
| 171 | } | ||
| 172 | 4 | } | |
| 173 | |||
| 174 | #ifdef X86_32 | ||
| 175 | void greyscale_yuy2_mmx(BYTE *srcp, size_t width, size_t height, size_t pitch) { | ||
| 176 | bool not_mod8 = false; | ||
| 177 | size_t loop_limit = min((pitch / 8) * 8, ((width*4 + 7) / 8) * 8); | ||
| 178 | |||
| 179 | __m64 luma_mask = _mm_set1_pi16(0x00FF); | ||
| 180 | __m64 chroma_value = _mm_set1_pi16((short)0x8000); | ||
| 181 | |||
| 182 | for (size_t y = 0; y < height; ++y) { | ||
| 183 | for (size_t x = 0; x < loop_limit; x+=8) { | ||
| 184 | __m64 src = *reinterpret_cast<const __m64*>(srcp+x); | ||
| 185 | src = _mm_and_si64(src, luma_mask); | ||
| 186 | src = _mm_or_si64(src, chroma_value); | ||
| 187 | *reinterpret_cast<__m64*>(srcp+x) = src; | ||
| 188 | } | ||
| 189 | |||
| 190 | if (loop_limit < width) { | ||
| 191 | __m64 src = *reinterpret_cast<const __m64*>(srcp+width-8); | ||
| 192 | src = _mm_and_si64(src, luma_mask); | ||
| 193 | src = _mm_or_si64(src, chroma_value); | ||
| 194 | *reinterpret_cast<__m64*>(srcp+width-8) = src; | ||
| 195 | } | ||
| 196 | |||
| 197 | srcp += pitch; | ||
| 198 | } | ||
| 199 | _mm_empty(); | ||
| 200 | } | ||
| 201 | |||
| 202 | static AVS_FORCEINLINE __m64 greyscale_rgb32_core_mmx(__m64& src, __m64& alpha_mask, __m64& zero, __m64& matrix, __m64& rgb_offset, __m64& round_mask_and_luma_offset, bool has_offset_rgb) { | ||
| 203 | __m64 alpha = _mm_and_si64(src, alpha_mask); | ||
| 204 | __m64 pixel0 = _mm_unpacklo_pi8(src, zero); | ||
| 205 | __m64 pixel1 = _mm_unpackhi_pi8(src, zero); | ||
| 206 | |||
| 207 | if (has_offset_rgb) { | ||
| 208 | pixel0 = _mm_add_pi16(pixel0, rgb_offset); // single pixel 4x16 bit | ||
| 209 | pixel1 = _mm_add_pi16(pixel1, rgb_offset); // single pixel 4x16 bit | ||
| 210 | } | ||
| 211 | |||
| 212 | pixel0 = _mm_madd_pi16(pixel0, matrix); //a0*0 + r0*cyr | g0*cyg + b0*cyb | ||
| 213 | pixel1 = _mm_madd_pi16(pixel1, matrix); //a1*0 + r1*cyr | g1*cyg + b1*cyb | ||
| 214 | |||
| 215 | __m64 tmp = _mm_unpackhi_pi32(pixel0, pixel1); // r1*cyr | r0*cyr | ||
| 216 | __m64 tmp2 = _mm_unpacklo_pi32(pixel0, pixel1); // g1*cyg + b1*cyb | g0*cyg + b0*cyb | ||
| 217 | |||
| 218 | tmp = _mm_add_pi32(tmp, tmp2); // r1*cyr + g1*cyg + b1*cyb | r0*cyr + g0*cyg + b0*cyb | ||
| 219 | tmp = _mm_add_pi32(tmp, round_mask_and_luma_offset); // r1*cyr + g1*cyg + b1*cyb + 32768 | r0*cyr + g0*cyg + b0*cyb + 32768 | ||
| 220 | tmp = _mm_srli_pi32(tmp, 15); // 0 0 0 p2 | 0 0 0 p1 | ||
| 221 | |||
| 222 | __m64 shifted = _mm_slli_si64(tmp, 8); | ||
| 223 | tmp = _mm_or_si64(tmp, shifted); // 0 0 p2 p2 | 0 0 p1 p1 | ||
| 224 | tmp = _mm_or_si64(tmp, _mm_slli_si64(shifted, 8)); // 0 p2 p2 p2 | 0 p1 p1 p1 | ||
| 225 | return _mm_or_si64(tmp, alpha); | ||
| 226 | } | ||
| 227 | |||
| 228 | void greyscale_rgb32_mmx(BYTE *srcp, size_t width, size_t height, size_t pitch, ConversionMatrix &m) { | ||
| 229 | const bool has_offset_rgb = 0 != m.offset_rgb; | ||
| 230 | |||
| 231 | __m64 matrix = _mm_set_pi16(0, m.y_r, m.y_g, m.y_b); | ||
| 232 | __m64 zero = _mm_setzero_si64(); | ||
| 233 | // .15 frac bit integer arithmetic | ||
| 234 | __m64 round_mask_and_luma_offset = _mm_set1_pi32((1 << 14) + (m.offset_y << 15)); // two pixels at a time | ||
| 235 | __m64 alpha_mask = _mm_set1_pi32(0xFF000000); | ||
| 236 | |||
| 237 | __m64 offset_rgb = _mm_set_pi16(0, m.offset_rgb, m.offset_rgb, m.offset_rgb); // omit alpha. signed (e.g. -16) if exists, for addition | ||
| 238 | |||
| 239 | size_t loop_limit = min((pitch / 8) * 8, ((width*4 + 7) / 8) * 8); | ||
| 240 | |||
| 241 | for (size_t y = 0; y < height; ++y) { | ||
| 242 | for (size_t x = 0; x < loop_limit; x+=8) { | ||
| 243 | __m64 src = *reinterpret_cast<const __m64*>(srcp+x); //pixels 0 and 1 | ||
| 244 | __m64 result = greyscale_rgb32_core_mmx(src, alpha_mask, zero, matrix, offset_rgb, round_mask_and_luma_offset, has_offset_rgb); | ||
| 245 | |||
| 246 | *reinterpret_cast<__m64*>(srcp+x) = result; | ||
| 247 | } | ||
| 248 | |||
| 249 | if (loop_limit < width) { | ||
| 250 | __m64 src = *reinterpret_cast<const __m64*>(srcp+width-8); //pixels 0 and 1 | ||
| 251 | __m64 result = greyscale_rgb32_core_mmx(src, alpha_mask, zero, matrix, offset_rgb, round_mask_and_luma_offset, has_offset_rgb); | ||
| 252 | |||
| 253 | *reinterpret_cast<__m64*>(srcp+width-8) = result; | ||
| 254 | } | ||
| 255 | |||
| 256 | srcp += pitch; | ||
| 257 | } | ||
| 258 | _mm_empty(); | ||
| 259 | } | ||
| 260 | #endif | ||
| 261 | |||
| 262 | |||
| 263 |