|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2024 nono project ! 4: // Licensed under nono-license.txt ! 5: // ! 6: ! 7: // ! 8: // AVX2 ! 9: // ! 10: ! 11: #include "accel_avx2.h" ! 12: #include "planevram.h" ! 13: #include "renderer.h" ! 14: #include "videoctlr.h" ! 15: #include <x86intrin.h> ! 16: ! 17: // ホスト CPU、OS が AVX2 をサポートしていれば true を返す。 ! 18: bool ! 19: DetectAVX2() ! 20: { ! 21: uint32 ebx; ! 22: uint32 ecx; ! 23: uint32 edx; ! 24: ! 25: // CPUID(eax=0x07, ecx=0) で ebx の bit5 が立っていれば、 ! 26: // CPU は AVX2 をサポートしている。 ! 27: __asm__ __volatile__( ! 28: "cpuid" ! 29: : // output ! 30: "=b" (ebx), ! 31: "=c" (ecx), ! 32: "=d" (edx) ! 33: : // input ! 34: "a" (0x7), ! 35: "c" (0x0) ! 36: ); ! 37: bool has_avx2 = ebx & (1U << 5); ! 38: ! 39: // CPUID(eax=0x01) で ecx の bit27 (OSXSAVE) が立っていれば、 ! 40: // OS が XSAVE を有効にしている。 ! 41: __asm__ __volatile__( ! 42: "cpuid" ! 43: : // output ! 44: "=b" (ebx), ! 45: "=c" (ecx), ! 46: "=d" (edx) ! 47: : // input ! 48: "a" (0x1) ! 49: ); ! 50: bool has_osxsave = ecx & (1U << 27); ! 51: ! 52: if (has_avx2 && has_osxsave) { ! 53: // XSAVE で保存するレジスタセットは XGETBV(0) で読み出せる ! 54: // XCR0 レジスタに入っていて、 ! 55: // これの bit1 が SSE(XMM?)、bit2 が AVX(YMM?) のようだ。 ! 56: uint32 xcr0; ! 57: __asm__ __volatile__( ! 58: "xgetbv" ! 59: : // output ! 60: "=a" (xcr0) ! 61: : // input ! 62: "c" (0) ! 63: : // used ! 64: "edx" ! 65: ); ! 66: if ((xcr0 & 6) == 6) { ! 67: return true; ! 68: } ! 69: } ! 70: ! 71: return false; ! 72: } ! 73: ! 74: // I8 から RGBX への変換。 ! 75: void ! 76: PlaneVRAMDevice::RenderI8toRGBX_avx2(BitmapRGBX& dst, const BitmapI8& view, ! 77: const std::vector<uint8>& view_mod) ! 78: { ! 79: for (int y = 0; y < view_mod.size(); y++) { ! 80: if (view_mod[y] == 0) { ! 81: continue; ! 82: } ! 83: ! 84: const uint8 *s8 = view.GetRowPtr(y); ! 85: uint32 *d = (uint32 *)dst.GetRowPtr(y); ! 86: ! 87: const uint32 *s = (const uint32 *)s8; ! 88: const uint32 *send = (const uint32 *)(s8 + view.GetWidth()); ! 89: for (; s < send;) { ! 90: // 8ピクセルずつ処理する ! 91: ! 92: // s は BitmapI8 なのでビッグエンディアン並びに相当。 ! 93: // idx0 = $00000000'00000000'I7I6I5I4'I3I2I1I0 ! 94: __m128i idx0 = _mm_loadl_epi64((const __m128i *)s); ! 95: s += 2; ! 96: ! 97: // idx = $000000I7'000000I6'000000I5'000000I4' ! 98: // 000000I3'000000I2'000000I1'000000I0 ! 99: __m256i idx = _mm256_cvtepu8_epi32(idx0); ! 100: ! 101: // 8個の 32bit インデックスから XBGR を取得 ! 102: // c = $00B7G7R7'00B6G6R6'00B5G5R5'00B4G4R4' ! 103: // 00B3G3R3'00B2G2R2'00B1G1R1'00B0G0R0 ! 104: __m256i c = _mm256_i32gather_epi32( ! 105: (const int *)palette, idx, 4); ! 106: ! 107: _mm256_storeu_si256((__m256i *)d, c); ! 108: d += 8; ! 109: } ! 110: } ! 111: } ! 112: ! 113: // RGBX から RGB への変換。 ! 114: /*static*/ void ! 115: Renderer::RGBXtoRGB_avx2(BitmapRGB& dst, const BitmapRGBX& src) ! 116: { ! 117: for (int y = 0, yend = src.GetHeight(); y < yend; y++) { ! 118: const uint32 *s = (const uint32 *)src.GetRowPtr(y); ! 119: uint32 *d = (uint32 *)dst.GetRowPtr(y); ! 120: ! 121: const uint32 *send = s + src.GetWidth(); ! 122: for (; s < send; ) { ! 123: // 8ピクセルずつ処理する ! 124: ! 125: // c = $x7B7G7R7'x6B6G6R6'x5B5G5R5'x4B4G4R4' ! 126: // x3B3G3R3'x2B2G2R2'x1B1G1R1'x0B0G0R0 ! 127: __m256i c = _mm256_loadu_si256((const __m256i *)s); ! 128: s += 8; ! 129: ! 130: // 256bit リニアに使えれば下詰めで済んだんだが、 ! 131: // 上位 128bit と下位 128bit をまたいでシャッフルは ! 132: // 出来なくて、かつこの後のストアは連続領域への ! 133: // 書き込みになるので、中央でつなげておく。うーん。 ! 134: // c = $00000000'B7G7R7B6'G6R6B5G5'R5B4G4R4' ! 135: // B3G3R3B2'G2R2B1G1'R1B0G0R0'00000000 ! 136: __m256i shuff = _mm256_set_epi8( ! 137: -1,-1,-1,-1, 14,13,12,10, 9, 8, 6, 5, 4, 2, 1, 0, ! 138: 14,13,12,10, 9, 8, 6, 5, 4, 2, 1, 0, -1,-1,-1,-1 ! 139: ); ! 140: __m256i res = _mm256_shuffle_epi8(c, shuff); ! 141: ! 142: // 先頭(下位)と末尾(上位)の 32bit ずつを除いたところを ! 143: // 書き出す。 ! 144: __m256i mask = _mm256_set_epi32( ! 145: 0, -1, -1, -1, -1, -1, -1, 0 ! 146: ); ! 147: _mm256_maskstore_epi32((int *)(d - 1), mask, res); ! 148: d += 6; ! 149: } ! 150: } ! 151: } ! 152: ! 153: // コントラスト (0-254) を適用。 ! 154: /*static*/ void ! 155: VideoCtlrDevice::RenderContrast_avx2(BitmapRGBX& dst, const BitmapRGBX& src, ! 156: uint32 contrast) ! 157: { ! 158: #define USE_NEW ! 159: ! 160: #if defined(USE_NEW) ! 161: // 8ビットのまま、筆算の掛け算と固定小数点の要領で、 ! 162: // ビットが立っている桁だけ右シフトしたものを足していく。 ! 163: // 例えば $b7(%1011'0111) を %1001'0000/256 (= 0.562) 倍する場合、 ! 164: // 除数の ! 165: // b7=%1 なので $b7 >> 1 = %0101'1011 ! 166: // b4=%1 なので $b7 >> 4 = %0000'1011 ! 167: // : + ! 168: // ---------- ! 169: // 結果は %0110'0110 (= $66) ! 170: // ! 171: // 立っているビット数が増えると演算回数で不利になるので、5 ビット ! 172: // 以上立っている場合は被除数を反転して計算して元の値から引く。 ! 173: int n = contrast == 0 ? 0 : __builtin_popcount(contrast); ! 174: bool use_sub; ! 175: if (n <= 4) { ! 176: use_sub = false; ! 177: } else { ! 178: contrast = (uint32)-contrast; ! 179: use_sub = true; ! 180: } ! 181: std::array<int, 4> shift_count; ! 182: __m256i shift_mask[4]; ! 183: n = 0; ! 184: for (int i = 0; i < 8; i++) { ! 185: if ((contrast & (0x80 >> i)) != 0) { ! 186: shift_count[n] = i + 1; ! 187: shift_mask[n] = _mm256_set1_epi8(0xffU >> shift_count[n]); ! 188: n++; ! 189: } ! 190: } ! 191: #else ! 192: const __m256i vcontrast = _mm256_set1_epi16(contrast); ! 193: const __m256i idxh = _mm256_set_epi32(-1, -1, -1, -1, 7, 6, 3, 2); ! 194: const __m256i idxl = _mm256_set_epi32(-1, -1, -1, -1, 5, 4, 1, 0); ! 195: #endif ! 196: ! 197: for (int y = 0, yend = src.GetHeight(); y < yend; y++) { ! 198: const uint32 *s = (const uint32 *)src.GetRowPtr(y); ! 199: uint32 *d = (uint32 *)dst.GetRowPtr(y); ! 200: ! 201: const uint32 *send = s + src.GetWidth(); ! 202: for (; s < send; ) { ! 203: // 8ピクセルずつ処理する ! 204: ! 205: __m256i a = _mm256_loadu_si256((const __m256i *)s); ! 206: s += 8; ! 207: #if defined(USE_NEW) ! 208: // 144 usec ! 209: ! 210: __m256i r; ! 211: if (__predict_false(n == 0)) { ! 212: r = _mm256_setzero_si256(); ! 213: } else { ! 214: // AVX には 8 ビットのシフト演算がないので、16 ビット ! 215: // 単位で右シフトして、右にはみ出た分をマスクする…。 ! 216: auto b0 = _mm256_srli_epi16(a, shift_count[0]); ! 217: r = _mm256_and_si256(b0, shift_mask[0]); ! 218: ! 219: if (n >= 2) { ! 220: auto b1 = _mm256_srli_epi16(a, shift_count[1]); ! 221: auto c1 = _mm256_and_si256(b1, shift_mask[1]); ! 222: r = _mm256_add_epi8(r, c1); ! 223: ! 224: if (n >= 3) { ! 225: auto b2 = _mm256_srli_epi16(a, shift_count[2]); ! 226: auto c2 = _mm256_and_si256(b2, shift_mask[2]); ! 227: r = _mm256_add_epi8(r, c2); ! 228: ! 229: if (n >= 4) { ! 230: auto b3 = _mm256_srli_epi16(a, shift_count[3]); ! 231: auto c3 = _mm256_and_si256(b3, shift_mask[3]); ! 232: r = _mm256_add_epi8(r, c3); ! 233: } ! 234: } ! 235: } ! 236: } ! 237: if (use_sub) { ! 238: r = _mm256_sub_epi8(a, r); ! 239: } ! 240: #else ! 241: // 146 usec ! 242: ! 243: // a = $x7B7G7R7'x6B6G6R6'x5B5G5R5'x4B4G4R4' ! 244: // x3B3G3R3'x2B2G2R2'x1B1G1R1'x0B0G0R0 ! 245: ! 246: // 上位 128bit を下位 128bit に持っていく必要がどうせあるし、 ! 247: // 最後に packus 一発で元に戻せる位置に permute する。 ! 248: // ah = $...'x7B7G7R7'x6B6G6R6'x3B3G3R3'x2B2G2R2 ! 249: // al = $...'x5B5G5R5'x4B4G4R4'x1B1G1R1'x0B0G0R0 ! 250: __m256i ah = _mm256_permutevar8x32_epi32(a, idxh); ! 251: __m256i al = _mm256_permutevar8x32_epi32(a, idxl); ! 252: ! 253: // bh = $00x700B7'00G700R7'00x600B6'00G600R6' ! 254: // 00x300B3'00G300R3'00x200B2'00G200R2 ! 255: // bl = $00x500B5'00G500R5'00x400B4'00G400R4' ! 256: // 00x100B1'00G100R1'00x000B0'00G000R0 ! 257: __m256i bh = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(ah)); ! 258: __m256i bl = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(al)); ! 259: ! 260: __m256i ch = _mm256_mullo_epi16(bh, vcontrast); ! 261: __m256i cl = _mm256_mullo_epi16(bl, vcontrast); ! 262: ! 263: __m256i sh = _mm256_srli_epi16(ch, 8); ! 264: __m256i sl = _mm256_srli_epi16(cl, 8); ! 265: ! 266: // r = $x7B7G7R7'x6B6G6R6'x5B5G5R5'x4B4G4R4' ! 267: // x3B3G3R3'x2B2G2R2'x1B1G1R1'x0B0G0R0 ! 268: __m256i r = _mm256_packus_epi16(sl, sh); ! 269: #endif ! 270: _mm256_storeu_si256((__m256i *)d, r); ! 271: d += 8; ! 272: } ! 273: } ! 274: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.