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