Annotation of nono/vm/accel_avx2.cpp, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.