|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.