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

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: 
1.1.1.3 ! root       72: // コントラスト (1-254) を適用。dst サイズでクリップする。
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: {
                     78:        // 8ビットのまま、筆算の掛け算と固定小数点の要領で、
                     79:        // ビットが立っている桁だけ右シフトしたものを足していく。
                     80:        // 例えば $b7(%1011'0111) を %1001'0000/256 (= 0.562) 倍する場合、
                     81:        // 除数の
1.1.1.2   root       82:        // bit7=%1 なので $b7 >> 1 = %0101'1011
                     83:        // bit4=%1 なので $b7 >> 4 = %0000'1011
                     84:        //                          +
                     85:        //                           ----------
                     86:        // 結果は                    %0110'0110 (= $66)
1.1       root       87:        //
                     88:        // 立っているビット数が増えると演算回数で不利になるので、5 ビット
                     89:        // 以上立っている場合は被除数を反転して計算して元の値から引く。
                     90:        bool use_sub;
1.1.1.2   root       91:        uint n = __builtin_popcount(contrast);
1.1       root       92:        if (n <= 4) {
                     93:                use_sub = false;
                     94:        } else {
                     95:                contrast = (uint32)-contrast;
                     96:                use_sub = true;
                     97:        }
                     98:        std::array<int, 4> shift_count;
                     99:        __m256i shift_mask[4];
                    100:        n = 0;
                    101:        for (int i = 0; i < 8; i++) {
                    102:                if ((contrast & (0x80 >> i)) != 0) {
                    103:                        shift_count[n] = i + 1;
                    104:                        shift_mask[n] = _mm256_set1_epi8(0xffU >> shift_count[n]);
                    105:                        n++;
                    106:                }
                    107:        }
1.1.1.2   root      108: 
1.1.1.3 ! root      109:        // 今のところ幅は 8ピクセルで割り切れる。
        !           110:        uint width = dst.GetWidth();
        !           111:        for (uint y = 0, yend = dst.GetHeight(); y < yend; y++) {
        !           112:                const uint32 *s32 = (const uint32 *)src.GetRowPtr(y);
        !           113:                uint32 *d32 = (uint32 *)dst.GetRowPtr(y);
        !           114:                uint32 *d32end = d32 + width;
        !           115:                for (; d32 < d32end; ) {
        !           116:                        // 8ピクセルずつ処理する。
        !           117:                        // 101 usec
        !           118: 
        !           119:                        __m256i a = _mm256_load_si256((const __m256i *)s32);
        !           120:                        s32 += 8;
        !           121: 
        !           122:                        __m256i r;
        !           123:                        // AVX には 8 ビットのシフト演算がないので、16 ビット
        !           124:                        // 単位で右シフトして、右にはみ出た分をマスクする…。
        !           125:                        auto b0 = _mm256_srli_epi16(a, shift_count[0]);
        !           126:                        r = _mm256_and_si256(b0, shift_mask[0]);
        !           127: 
        !           128:                        if (n >= 2) {
        !           129:                                auto b1 = _mm256_srli_epi16(a, shift_count[1]);
        !           130:                                auto c1 = _mm256_and_si256(b1, shift_mask[1]);
        !           131:                                r = _mm256_add_epi8(r, c1);
        !           132: 
        !           133:                                if (n >= 3) {
        !           134:                                        auto b2 = _mm256_srli_epi16(a, shift_count[2]);
        !           135:                                        auto c2 = _mm256_and_si256(b2, shift_mask[2]);
        !           136:                                        r = _mm256_add_epi8(r, c2);
        !           137: 
        !           138:                                        if (n >= 4) {
        !           139:                                                auto b3 = _mm256_srli_epi16(a, shift_count[3]);
        !           140:                                                auto c3 = _mm256_and_si256(b3, shift_mask[3]);
        !           141:                                                r = _mm256_add_epi8(r, c3);
        !           142:                                        }
1.1       root      143:                                }
                    144:                        }
1.1.1.3 ! root      145:                        if (use_sub) {
        !           146:                                r = _mm256_sub_epi8(a, r);
        !           147:                        }
1.1.1.2   root      148: 
1.1.1.3 ! root      149:                        _mm256_store_si256((__m256i *)d32, r);
        !           150:                        d32 += 8;
        !           151:                }
1.1       root      152:        }
                    153: }

unix.superglobalmegacorp.com

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