filters/intel/merge_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 | |||
| 36 | // Avisynth filter: YUV merge / Swap planes | ||
| 37 | // by Klaus Post (kp@interact.dk) | ||
| 38 | // adapted by Richard Berg (avisynth-dev@richardberg.net) | ||
| 39 | // iSSE code by Ian Brabham | ||
| 40 | |||
| 41 | |||
| 42 | // Intrinsics base header + really required extension headers | ||
| 43 | #if defined(_MSC_VER) | ||
| 44 | #include <intrin.h> // MSVC | ||
| 45 | #else | ||
| 46 | #include <x86intrin.h> // GCC/MinGW/Clang/LLVM | ||
| 47 | #endif | ||
| 48 | #include <immintrin.h> | ||
| 49 | |||
| 50 | |||
| 51 | #if !defined(__FMA__) | ||
| 52 | // Assume that all processors that have AVX2 also have FMA3 | ||
| 53 | #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) | ||
| 54 | // Prevent error message in g++ when using FMA intrinsics with avx2: | ||
| 55 | #pragma message "It is recommended to specify also option -mfma when using -mavx2 or higher" | ||
| 56 | #else | ||
| 57 | #define __FMA__ 1 | ||
| 58 | #endif | ||
| 59 | #endif | ||
| 60 | // FMA3 instruction set | ||
| 61 | #if defined(__FMA__) && (defined(__GNUC__) || defined(__clang__)) && !defined(__INTEL_COMPILER) | ||
| 62 | #include <fmaintrin.h> | ||
| 63 | #endif // __FMA__ | ||
| 64 | |||
| 65 | #include "merge_avx2.h" | ||
| 66 | #include <avisynth.h> | ||
| 67 | #include <avs/types.h> | ||
| 68 | #include <cstdint> | ||
| 69 | |||
| 70 | #ifndef _mm256_set_m128i | ||
| 71 | #define _mm256_set_m128i(v0, v1) _mm256_insertf128_si256(_mm256_castsi128_si256(v1), (v0), 1) | ||
| 72 | #endif | ||
| 73 | |||
| 74 | #ifndef _mm256_set_m128 | ||
| 75 | #define _mm256_set_m128(v0, v1) _mm256_insertf128_ps(_mm256_castps128_ps256(v1), (v0), 1) | ||
| 76 | #endif | ||
| 77 | |||
| 78 | /* ----------------------------------- | ||
| 79 | * average_plane | ||
| 80 | * ----------------------------------- | ||
| 81 | */ | ||
| 82 | // VS2017 15.5.1..2 optimizer generates illegal instructions for _mm_stream_load_si128 (vmovntdqa) | ||
| 83 | // just a note, don't use that. | ||
| 84 | // Can be called from Layer or Overlay, so no aligned loads/stores here and prepare for exact width | ||
| 85 | template<typename pixel_t> | ||
| 86 | 15 | void average_plane_avx2(BYTE *p1, const BYTE *p2, int p1_pitch, int p2_pitch, int rowsize, int height) { | |
| 87 | // width is RowSize here | ||
| 88 | 15 | int mod32_width = rowsize / 32 * 32; | |
| 89 | 15 | int mod16_width = rowsize / 16 * 16; | |
| 90 | |||
| 91 |
4/4void average_plane_avx2<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 28 → 3 taken 47 times.
✓ Branch 28 → 29 taken 12 times.
void average_plane_avx2<unsigned short>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 28 → 3 taken 15 times.
✓ Branch 28 → 29 taken 3 times.
|
77 | for(int y = 0; y < height; y++) { |
| 92 |
4/4void average_plane_avx2<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 12 → 4 taken 15 times.
✓ Branch 12 → 13 taken 47 times.
void average_plane_avx2<unsigned short>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 12 → 4 taken 15 times.
✓ Branch 12 → 13 taken 15 times.
|
92 | for(int x = 0; x < mod32_width; x+=32) { |
| 93 | 30 | __m256i src1 = _mm256_loadu_si256(reinterpret_cast<__m256i*>(p1 + x)); | |
| 94 | 60 | __m256i src2 = _mm256_loadu_si256(const_cast<__m256i*>(reinterpret_cast<const __m256i*>(p2+x))); | |
| 95 | __m256i dst; | ||
| 96 | if constexpr(sizeof(pixel_t) == 1) | ||
| 97 | 15 | dst = _mm256_avg_epu8(src1, src2); // 16 pixels | |
| 98 | else // pixel_size == 2 | ||
| 99 | 15 | dst = _mm256_avg_epu16(src1, src2); // 8 pixels | |
| 100 | |||
| 101 | 30 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(p1+x), dst); | |
| 102 | } | ||
| 103 | |||
| 104 |
4/4void average_plane_avx2<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 22 → 14 taken 7 times.
✓ Branch 22 → 23 taken 47 times.
void average_plane_avx2<unsigned short>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 22 → 14 taken 7 times.
✓ Branch 22 → 23 taken 15 times.
|
76 | for(int x = mod32_width; x < mod16_width; x+=16) { |
| 105 | 14 | __m128i src1 = _mm_loadu_si128(reinterpret_cast<__m128i*>(p1 + x)); | |
| 106 | 28 | __m128i src2 = _mm_loadu_si128(const_cast<__m128i*>(reinterpret_cast<const __m128i*>(p2 + x))); | |
| 107 | __m128i dst; | ||
| 108 | if constexpr(sizeof(pixel_t) == 1) | ||
| 109 | 7 | dst = _mm_avg_epu8(src1, src2); // 8 pixels | |
| 110 | else // pixel_size == 2 | ||
| 111 | 7 | dst = _mm_avg_epu16(src1, src2); // 4 pixels | |
| 112 | |||
| 113 | 14 | _mm_storeu_si128(reinterpret_cast<__m128i*>(p1+x), dst); | |
| 114 | } | ||
| 115 | |||
| 116 |
4/4void average_plane_avx2<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 23 → 24 taken 44 times.
✓ Branch 23 → 27 taken 3 times.
void average_plane_avx2<unsigned short>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 23 → 24 taken 12 times.
✓ Branch 23 → 27 taken 3 times.
|
62 | if (mod16_width != rowsize) { |
| 117 |
4/4void average_plane_avx2<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 26 → 25 taken 301 times.
✓ Branch 26 → 27 taken 44 times.
void average_plane_avx2<unsigned short>(unsigned char*, unsigned char const*, int, int, int, int):
✓ Branch 26 → 25 taken 56 times.
✓ Branch 26 → 27 taken 12 times.
|
413 | for (size_t x = mod16_width / sizeof(pixel_t); x < rowsize/sizeof(pixel_t); ++x) { |
| 118 | 357 | reinterpret_cast<pixel_t *>(p1)[x] = (int(reinterpret_cast<pixel_t *>(p1)[x]) + reinterpret_cast<const pixel_t *>(p2)[x] + 1) >> 1; | |
| 119 | } | ||
| 120 | } | ||
| 121 | 62 | p1 += p1_pitch; | |
| 122 | 62 | p2 += p2_pitch; | |
| 123 | } | ||
| 124 | 15 | } | |
| 125 | |||
| 126 | // instantiate to let them access from other modules | ||
| 127 | template void average_plane_avx2<uint8_t>(BYTE *p1, const BYTE *p2, int p1_pitch, int p2_pitch, int rowsize, int height); | ||
| 128 | template void average_plane_avx2<uint16_t>(BYTE *p1, const BYTE *p2, int p1_pitch, int p2_pitch, int rowsize, int height); | ||
| 129 | |||
| 130 | // weighted_merge_planar AVX2 implementations moved to overlay/intel/blend_common_avx2.cpp | ||
| 131 | // (see weighted_merge_avx2 / weighted_merge_float_avx2) | ||
| 132 | |||
| 133 | 3 | void average_plane_avx2_float(BYTE* p1, const BYTE* p2, int p1_pitch, int p2_pitch, int rowsize, int height) { | |
| 134 | 3 | const auto v_half = _mm256_set1_ps(0.5f); | |
| 135 | |||
| 136 | // Process 64 bytes (16 floats) per iteration | ||
| 137 | 3 | const int wMod64 = (rowsize / 64) * 64; | |
| 138 | // Process 32 bytes (8 floats) per iteration (for the remainder) | ||
| 139 | 3 | const int wMod32 = (rowsize / 32) * 32; | |
| 140 | |||
| 141 |
2/2✓ Branch 42 → 5 taken 15 times.
✓ Branch 42 → 43 taken 3 times.
|
18 | for (int y = 0; y < height; y++) { |
| 142 | 15 | int x = 0; | |
| 143 | |||
| 144 | // 16 floats at a time | ||
| 145 |
2/2✓ Branch 25 → 6 taken 15 times.
✓ Branch 25 → 26 taken 15 times.
|
30 | for (; x < wMod64; x += 64) { |
| 146 | // Load 16 floats from p1 and 16 from p2 | ||
| 147 | 15 | auto p1_a = _mm256_loadu_ps(reinterpret_cast<const float*>(p1 + x)); | |
| 148 | 15 | auto p1_b = _mm256_loadu_ps(reinterpret_cast<const float*>(p1 + x + 32)); | |
| 149 | 15 | auto p2_a = _mm256_loadu_ps(reinterpret_cast<const float*>(p2 + x)); | |
| 150 | 30 | auto p2_b = _mm256_loadu_ps(reinterpret_cast<const float*>(p2 + x + 32)); | |
| 151 | |||
| 152 | // result = (p1 + p2) * 0.5 | ||
| 153 | 30 | auto res_a = _mm256_mul_ps(_mm256_add_ps(p1_a, p2_a), v_half); | |
| 154 | 15 | auto res_b = _mm256_mul_ps(_mm256_add_ps(p1_b, p2_b), v_half); | |
| 155 | |||
| 156 | 15 | _mm256_storeu_ps(reinterpret_cast<float*>(p1 + x), res_a); | |
| 157 | 15 | _mm256_storeu_ps(reinterpret_cast<float*>(p1 + x + 32), res_b); | |
| 158 | } | ||
| 159 | |||
| 160 | // 8 floats at a time | ||
| 161 |
1/2✗ Branch 37 → 27 not taken.
✓ Branch 37 → 38 taken 15 times.
|
15 | for (; x < wMod32; x += 32) { |
| 162 | ✗ | auto px1 = _mm256_loadu_ps(reinterpret_cast<const float*>(p1 + x)); | |
| 163 | ✗ | auto px2 = _mm256_loadu_ps(reinterpret_cast<const float*>(p2 + x)); | |
| 164 | ✗ | auto res = _mm256_mul_ps(_mm256_add_ps(px1, px2), v_half); | |
| 165 | ✗ | _mm256_storeu_ps(reinterpret_cast<float*>(p1 + x), res); | |
| 166 | } | ||
| 167 | |||
| 168 | // Scalar tail widths | ||
| 169 |
2/2✓ Branch 40 → 39 taken 40 times.
✓ Branch 40 → 41 taken 15 times.
|
55 | for (int i = x / 4; i < rowsize / 4; i++) { |
| 170 | 40 | reinterpret_cast<float*>(p1)[i] = (reinterpret_cast<float*>(p1)[i] + reinterpret_cast<const float*>(p2)[i]) * 0.5f; | |
| 171 | } | ||
| 172 | |||
| 173 | 15 | p1 += p1_pitch; | |
| 174 | 15 | p2 += p2_pitch; | |
| 175 | } | ||
| 176 | 3 | } | |
| 177 | |||
| 178 |