|
|
nono 1.5.0
//
// nono
// Copyright (C) 2024 nono project
// Licensed under nono-license.txt
//
//
// AVX2
//
#include "accel_avx2.h"
#include "videoctlr.h"
#include <x86intrin.h>
// ホスト CPU、OS が AVX2 をサポートしていれば true を返す。
bool
DetectAVX2()
{
uint32 ebx;
uint32 ecx;
uint32 edx;
// CPUID(eax=0x07, ecx=0) で ebx の bit5 が立っていれば、
// CPU は AVX2 をサポートしている。
__asm__ __volatile__(
"cpuid"
: // output
"=b" (ebx),
"=c" (ecx),
"=d" (edx)
: // input
"a" (0x7),
"c" (0x0)
);
bool has_avx2 = ebx & (1U << 5);
// CPUID(eax=0x01) で ecx の bit27 (OSXSAVE) が立っていれば、
// OS が XSAVE を有効にしている。
__asm__ __volatile__(
"cpuid"
: // output
"=b" (ebx),
"=c" (ecx),
"=d" (edx)
: // input
"a" (0x1)
);
bool has_osxsave = ecx & (1U << 27);
if (has_avx2 && has_osxsave) {
// XSAVE で保存するレジスタセットは XGETBV(0) で読み出せる
// XCR0 レジスタに入っていて、
// これの bit1 が SSE(XMM?)、bit2 が AVX(YMM?) のようだ。
uint32 xcr0;
__asm__ __volatile__(
"xgetbv"
: // output
"=a" (xcr0)
: // input
"c" (0)
: // used
"edx"
);
if ((xcr0 & 6) == 6) {
return true;
}
}
return false;
}
// コントラスト (1-254) を適用。dst サイズでクリップする。
// (ちなみに _gen は 409 usec)
/*static*/ void
VideoCtlrDevice::RenderContrast_avx2(BitmapRGBX& dst, const BitmapRGBX& src,
uint32 contrast)
{
// 8ビットのまま、筆算の掛け算と固定小数点の要領で、
// ビットが立っている桁だけ右シフトしたものを足していく。
// 例えば $b7(%1011'0111) を %1001'0000/256 (= 0.562) 倍する場合、
// 除数の
// bit7=%1 なので $b7 >> 1 = %0101'1011
// bit4=%1 なので $b7 >> 4 = %0000'1011
// +
// ----------
// 結果は %0110'0110 (= $66)
//
// 立っているビット数が増えると演算回数で不利になるので、5 ビット
// 以上立っている場合は被除数を反転して計算して元の値から引く。
bool use_sub;
uint n = __builtin_popcount(contrast);
if (n <= 4) {
use_sub = false;
} else {
contrast = (uint32)-contrast;
use_sub = true;
}
std::array<int, 4> shift_count;
__m256i shift_mask[4];
n = 0;
for (int i = 0; i < 8; i++) {
if ((contrast & (0x80 >> i)) != 0) {
shift_count[n] = i + 1;
shift_mask[n] = _mm256_set1_epi8(0xffU >> shift_count[n]);
n++;
}
}
// 今のところ幅は 8ピクセルで割り切れる。
uint width = dst.GetWidth();
for (uint y = 0, yend = dst.GetHeight(); y < yend; y++) {
const uint32 *s32 = (const uint32 *)src.GetRowPtr(y);
uint32 *d32 = (uint32 *)dst.GetRowPtr(y);
uint32 *d32end = d32 + width;
for (; d32 < d32end; ) {
// 8ピクセルずつ処理する。
// 101 usec
__m256i a = _mm256_load_si256((const __m256i *)s32);
s32 += 8;
__m256i r;
// AVX には 8 ビットのシフト演算がないので、16 ビット
// 単位で右シフトして、右にはみ出た分をマスクする…。
auto b0 = _mm256_srli_epi16(a, shift_count[0]);
r = _mm256_and_si256(b0, shift_mask[0]);
if (n >= 2) {
auto b1 = _mm256_srli_epi16(a, shift_count[1]);
auto c1 = _mm256_and_si256(b1, shift_mask[1]);
r = _mm256_add_epi8(r, c1);
if (n >= 3) {
auto b2 = _mm256_srli_epi16(a, shift_count[2]);
auto c2 = _mm256_and_si256(b2, shift_mask[2]);
r = _mm256_add_epi8(r, c2);
if (n >= 4) {
auto b3 = _mm256_srli_epi16(a, shift_count[3]);
auto c3 = _mm256_and_si256(b3, shift_mask[3]);
r = _mm256_add_epi8(r, c3);
}
}
}
if (use_sub) {
r = _mm256_sub_epi8(a, r);
}
_mm256_store_si256((__m256i *)d32, r);
d32 += 8;
}
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.