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

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 "videoctlr.h"
                     13: #include <x86intrin.h>
                     14: 
                     15: // ホスト CPU、OS が AVX2 をサポートしていれば true を返す。
                     16: bool
                     17: DetectAVX2()
                     18: {
                     19:        uint32 ebx;
                     20:        uint32 ecx;
                     21:        uint32 edx;
                     22: 
                     23:        // CPUID(eax=0x07, ecx=0) で ebx の bit5 が立っていれば、
                     24:        // CPU は AVX2 をサポートしている。
                     25:        __asm__ __volatile__(
                     26:                "cpuid"
                     27:                : // output
                     28:                        "=b" (ebx),
                     29:                        "=c" (ecx),
                     30:                        "=d" (edx)
                     31:                : // input
                     32:                        "a" (0x7),
                     33:                        "c" (0x0)
                     34:        );
                     35:        bool has_avx2 = ebx & (1U << 5);
                     36: 
                     37:        // CPUID(eax=0x01) で ecx の bit27 (OSXSAVE) が立っていれば、
                     38:        // OS が XSAVE を有効にしている。
                     39:        __asm__ __volatile__(
                     40:                "cpuid"
                     41:                : // output
                     42:                        "=b" (ebx),
                     43:                        "=c" (ecx),
                     44:                        "=d" (edx)
                     45:                : // input
                     46:                        "a" (0x1)
                     47:        );
                     48:        bool has_osxsave = ecx & (1U << 27);
                     49: 
                     50:        if (has_avx2 && has_osxsave) {
                     51:                // XSAVE で保存するレジスタセットは XGETBV(0) で読み出せる
                     52:                // XCR0 レジスタに入っていて、
                     53:                // これの bit1 が SSE(XMM?)、bit2 が AVX(YMM?) のようだ。
                     54:                uint32 xcr0;
                     55:                __asm__ __volatile__(
                     56:                        "xgetbv"
                     57:                        : // output
                     58:                                "=a" (xcr0)
                     59:                        : // input
                     60:                                "c" (0)
                     61:                        : // used
                     62:                                "edx"
                     63:                );
                     64:                if ((xcr0 & 6) == 6) {
                     65:                        return true;
                     66:                }
                     67:        }
                     68: 
                     69:        return false;
                     70: }
                     71: 
                     72: // コントラスト (0-254) を適用。
1.1.1.2 ! root       73: // (ちなみに _gen は 409 usec)
1.1       root       74: /*static*/ void
                     75: VideoCtlrDevice::RenderContrast_avx2(BitmapRGBX& dst, const BitmapRGBX& src,
                     76:        uint32 contrast)
                     77: {
1.1.1.2 ! root       78:        // この変換は一次元配列とみなしても行える。
        !            79:        // また必ずテキスト画面全域なので端数の考慮は不要。
        !            80:        uint pxlen = src.GetWidth() * src.GetHeight();
        !            81:        const uint32 *s32 = (const uint32 *)src.GetRowPtr(0);
        !            82:        uint32 *d32 = (uint32 *)dst.GetRowPtr(0);
        !            83:        uint32 *d32end = d32 + pxlen;
        !            84: 
        !            85:        if (__predict_false(contrast == 0)) {
        !            86:                __m256i zero = _mm256_setzero_si256();
        !            87:                for (; d32 < d32end; ) {
        !            88:                        _mm256_store_si256((__m256i *)d32, zero);
        !            89:                        d32 += 8;
        !            90:                }
        !            91:                return;
        !            92:        }
1.1       root       93: 
                     94:        // 8ビットのまま、筆算の掛け算と固定小数点の要領で、
                     95:        // ビットが立っている桁だけ右シフトしたものを足していく。
                     96:        // 例えば $b7(%1011'0111) を %1001'0000/256 (= 0.562) 倍する場合、
                     97:        // 除数の
1.1.1.2 ! root       98:        // bit7=%1 なので $b7 >> 1 = %0101'1011
        !            99:        // bit4=%1 なので $b7 >> 4 = %0000'1011
        !           100:        //                          +
        !           101:        //                           ----------
        !           102:        // 結果は                    %0110'0110 (= $66)
1.1       root      103:        //
                    104:        // 立っているビット数が増えると演算回数で不利になるので、5 ビット
                    105:        // 以上立っている場合は被除数を反転して計算して元の値から引く。
                    106:        bool use_sub;
1.1.1.2 ! root      107:        uint n = __builtin_popcount(contrast);
1.1       root      108:        if (n <= 4) {
                    109:                use_sub = false;
                    110:        } else {
                    111:                contrast = (uint32)-contrast;
                    112:                use_sub = true;
                    113:        }
                    114:        std::array<int, 4> shift_count;
                    115:        __m256i shift_mask[4];
                    116:        n = 0;
                    117:        for (int i = 0; i < 8; i++) {
                    118:                if ((contrast & (0x80 >> i)) != 0) {
                    119:                        shift_count[n] = i + 1;
                    120:                        shift_mask[n] = _mm256_set1_epi8(0xffU >> shift_count[n]);
                    121:                        n++;
                    122:                }
                    123:        }
1.1.1.2 ! root      124: 
        !           125:        for (; d32 < d32end; ) {
        !           126:                // 8ピクセルずつ処理する
        !           127:                // 101 usec
        !           128: 
        !           129:                __m256i a = _mm256_load_si256((const __m256i *)s32);
        !           130:                s32 += 8;
        !           131: 
        !           132:                __m256i r;
        !           133:                // AVX には 8 ビットのシフト演算がないので、16 ビット
        !           134:                // 単位で右シフトして、右にはみ出た分をマスクする…。
        !           135:                auto b0 = _mm256_srli_epi16(a, shift_count[0]);
        !           136:                r = _mm256_and_si256(b0, shift_mask[0]);
        !           137: 
        !           138:                if (n >= 2) {
        !           139:                        auto b1 = _mm256_srli_epi16(a, shift_count[1]);
        !           140:                        auto c1 = _mm256_and_si256(b1, shift_mask[1]);
        !           141:                        r = _mm256_add_epi8(r, c1);
        !           142: 
        !           143:                        if (n >= 3) {
        !           144:                                auto b2 = _mm256_srli_epi16(a, shift_count[2]);
        !           145:                                auto c2 = _mm256_and_si256(b2, shift_mask[2]);
        !           146:                                r = _mm256_add_epi8(r, c2);
        !           147: 
        !           148:                                if (n >= 4) {
        !           149:                                        auto b3 = _mm256_srli_epi16(a, shift_count[3]);
        !           150:                                        auto c3 = _mm256_and_si256(b3, shift_mask[3]);
        !           151:                                        r = _mm256_add_epi8(r, c3);
1.1       root      152:                                }
                    153:                        }
                    154:                }
1.1.1.2 ! root      155:                if (use_sub) {
        !           156:                        r = _mm256_sub_epi8(a, r);
        !           157:                }
        !           158: 
        !           159:                _mm256_store_si256((__m256i *)d32, r);
        !           160:                d32 += 8;
1.1       root      161:        }
                    162: }

unix.superglobalmegacorp.com

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