Annotation of nono/vm/accel_avx2.cpp, revision 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.