|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2022 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // プレーン VRAM (垂直 VRAM)
9: // Lunafb と X68k TVRAM の共通部分
10: //
11:
12: #include "planevram.h"
13: #include "mybswap.h"
14: #if defined(__x86_64__)
15: #include <x86intrin.h>
16: #endif
17:
18: // コンストラクタ
1.1.1.2 ! root 19: PlaneVRAMDevice::PlaneVRAMDevice(int width_, int height_)
! 20: : inherited(OBJ_PLANEVRAM)
1.1 root 21: {
1.1.1.2 ! root 22: // ポカ避け。継承クラス側が設定する。
1.1 root 23: nplane = -1;
24:
25: composite.Create(width_, height_);
26:
1.1.1.2 ! root 27: dirty.resize(height_);
1.1 root 28:
29: // レンダリング時に使うテーブルを事前に計算。
30: InitDeptable();
31: }
32:
33: // デストラクタ
34: PlaneVRAMDevice::~PlaneVRAMDevice()
35: {
36: }
37:
38: // リセット
39: void
40: PlaneVRAMDevice::ResetHard(bool poweron)
41: {
42: // XXX ここ?
43: Invalidate();
44: }
45:
46: // レンダリング時に使うテーブルを事前に計算。
47: void
48: PlaneVRAMDevice::InitDeptable()
49: {
50: deptable.reset(new uint64[256]);
51:
52: // %abcdefgh の8ビットを
53: // リトルエンディアンホストでは
54: // %0000000h'0000000g'0000000f'0000000e'0000000d'0000000c'0000000b'0000000a
55: // ビッグエンディアンホストでは
56: // %0000000a'0000000b'0000000c'0000000d'0000000e'0000000f'0000000g'0000000h
57: // の64ビットに伸張する。
58: // (8ビットを各バイトの最下位ビットに対応させる)
59: for (int i = 0; i < 256; i++) {
60: uint64 res = 0;
61: for (int bit = 0; bit < 8; bit++) {
62: int n = bit * 8;
63: #if BYTE_ORDER == LITTLE_ENDIAN
64: n = 56 - n;
65: #endif
66: res |= (uint64)((i >> bit) & 1) << n;
67: }
68: deptable[i] = res;
69: }
70: }
71:
72: // 全画面の更新フラグを立てる
73: void
74: PlaneVRAMDevice::Invalidate()
75: {
76: std::fill(dirty.begin(), dirty.end(), 1);
77: }
78:
79: // 全画面の更新フラグを下ろす
80: // VM スレッドから呼ばれる。
81: void
82: PlaneVRAMDevice::ClearDirty()
83: {
84: std::fill(dirty.begin(), dirty.end(), 0);
85: }
86:
87: // 更新情報を取得。
88: // VM スレッドから呼ばれる。
89: ModifyInfo
90: PlaneVRAMDevice::GetModify() const
91: {
92: ModifyInfo mod;
1.1.1.2 ! root 93: memcpy(&mod.bits[0], &dirty[0], mod.bits.size());
! 94: // TODO: 互換性のために n_dirty 計算。廃止するかも。
! 95: for (int i = 0; i < dirty.size(); i++) {
! 96: if (dirty[i]) {
! 97: mod.n_dirty++;
1.1 root 98: }
99: }
100:
101: return mod;
102: }
103:
104: // 画面合成。
105: // レンダラスレッドから呼ばれる。
106: bool
107: PlaneVRAMDevice::Render(BitmapRGB& bitmap, const ModifyInfo& modify)
108: {
109: bool updated = false;
110:
111: // VRAM に更新があれば composite を更新
112: if (modify.IsDirty()) {
113: RenderVRAMToComposite(modify);
114: updated = true;
115: }
116:
117: // composite に更新があるか(update)、
118: // 無条件に更新するか(invalidate2) なら bitmap を更新。
119: if (updated || modify.invalidate2) {
120: RenderCompositeToRGB(bitmap, modify);
121: updated = true;
122: }
123:
124: return updated;
125: }
126:
127: // VRAM から composite 画面を合成する。
128: // レンダラスレッドから呼ばれる。
129: void
130: PlaneVRAMDevice::RenderVRAMToComposite(const ModifyInfo& modify)
131: {
132: const int width = composite.GetWidth();
133: const int height = composite.GetHeight();
134: const int planesz = (width / 8) * height;
135:
1.1.1.2 ! root 136: // 1bpp はパレット的にはほぼ 4bpp と同じ扱いになる。
! 137: const int nplane4 = (nplane == 1) ? 4 : nplane;
! 138:
1.1 root 139: // テキスト合成画面を描く
1.1.1.2 ! root 140: for (int y = 0; y < height; y++) {
! 141: uint8 mod = modify.bits[y];
! 142: if (mod == 0) {
! 143: continue;
! 144: }
! 145:
! 146: const uint32 *src = (const uint32 *)&mem[0];
! 147: src += y * width / 32;
! 148: uint64 *dst = (uint64 *)composite.GetPtr(0, y);
! 149:
! 150: for (int x = 0; x < width / 32; x++) {
! 151: std::array<uint32, 8> data;
! 152:
! 153: // VRAM はホストエンディアンに関係なく 32bit で読み込んだ
! 154: // 時の MSB が左端ピクセル。
! 155: // ここでは左側から 8ピクセル(8ビット)ずつ 4回処理する
! 156: // のを最下位バイトから順に右シフトで行いたいので、
! 157: // (バイト内のビット順は維持したまま) バイトスワップする。
! 158: //
! 159: // 31 24 23 16 15 8 7 0
! 160: // +----------+----------+----------+----------+
! 161: // VRAM |+00 .. +07|+08 .. +15|+16 .. +23|+24 .. +31|
! 162: // +----------+----------+----------+----------+
! 163: // ↓
! 164: // +----------+----------+----------+----------+
! 165: // data[] |+24 .. +31|+16 .. +23|+08 .. +15|+00 .. +07|
! 166: // +----------+----------+----------+----------+
! 167: if (__predict_false(nplane == 1)) {
! 168: // LUNA 1bpp の場合残りの3プレーンは ff で埋める。
! 169: // 実際にも存在しないプレーンは ff が読み出せて
! 170: // カラーインデックスは $e か $f の二択になるので。
! 171: data[0] = bswap32(src[0]);
! 172: data[1] = 0xffffffff;
! 173: data[2] = 0xffffffff;
! 174: data[3] = 0xffffffff;
! 175: } else {
! 176: int offset = planesz / sizeof(uint32);
! 177: for (int plane = 0; plane < nplane; plane++) {
! 178: data[plane] = bswap32(src[plane * offset]);
! 179: }
1.1 root 180: }
1.1.1.2 ! root 181: src++;
1.1 root 182:
1.1.1.2 ! root 183: // data[] の下位 8bit ずつを取り出して deptable[] で変換。
! 184: // p0..p3 は1バイト x 8個。
! 185: //
! 186: // data[] = %xxxxxxxx'xxxxxxxx'xxxxxxxx'ABCDEFGH
! 187: // ↓
! 188: // (リトルエンディアンホストの場合)
! 189: // p.H = %0000000H'0000000G'0000000F'0000000E
! 190: // p.L = %0000000D'0000000C'0000000B'0000000A
! 191: for (int b = 0; b < 4; b++) {
! 192: uint64 cc = 0;
! 193:
! 194: for (int plane = 0; plane < nplane4; plane++) {
! 195: uint64 p;
! 196: p = deptable[data[plane] & 0xff];
! 197: p <<= plane;
! 198: cc |= p;
! 199: data[plane] >>= 8;
1.1 root 200: }
1.1.1.2 ! root 201:
! 202: // cc (64bit = 8バイト) は各バイトが各ピクセルの
! 203: // カラーインデックスになっているので
! 204: // BitmapI8 に 8バイト一気に書き込める。
! 205: // (エンディアンの影響は deptable[] で処理してある)
! 206: *dst++ = cc;
1.1 root 207: }
208: }
209: }
210: }
211:
212: // composite 画面とパレット情報から RGB 画面を合成する。
213: // modify は composite 上での変更箇所を示している。
214: // レンダラスレッドから呼ばれる。
215: void
216: PlaneVRAMDevice::RenderCompositeToRGB(BitmapRGB& bitmap,
217: const ModifyInfo& modify)
218: {
219: const int width = bitmap.GetWidth();
220: const int height = bitmap.GetHeight();
221:
1.1.1.2 ! root 222: // view_mod は表示領域 view における更新ラスタ
1.1 root 223: BitmapI8 view(width, height);
1.1.1.2 ! root 224: std::vector<uint8> view_mod(height);
1.1 root 225:
226: // この view に対する modify を計算する
227: if (__predict_false(modify.invalidate2)) {
1.1.1.2 ! root 228: std::fill(view_mod.begin(), view_mod.end(), 1);
1.1 root 229: } else {
230: // Y scroll 方向の modify 計算
1.1.1.2 ! root 231: int sy = yscroll;
! 232: for (int y = 0; y < height; y++, sy++) {
! 233: if (sy >= composite.GetHeight()) {
! 234: sy -= composite.GetHeight();
1.1 root 235: }
236: view_mod[y] = modify.bits[sy];
237: }
238:
1.1.1.2 ! root 239: // 右がはみ出す場合は、はみ出した先のラスタも加味する。
! 240: if (xscroll + view.GetWidth() > composite.GetWidth()) {
! 241: sy = yscroll;
! 242: for (int y = 0; y < height; y++, sy++) {
! 243: if (sy >= composite.GetHeight()) {
! 244: sy -= composite.GetHeight();
! 245: }
! 246: int sy2 = GetWrapY(sy);
! 247: view_mod[y] |= modify.bits[sy2];
1.1 root 248: }
249: }
250: }
251:
252: // composite からスクロールを加味した表示領域 view を作る
1.1.1.2 ! root 253: int sy = yscroll;
! 254: for (int y = 0; y < height; y++, sy++) {
! 255: if (sy >= composite.GetHeight()) {
! 256: sy -= composite.GetHeight();
! 257: }
! 258: if (view_mod[y] == 0) {
! 259: continue;
! 260: }
1.1 root 261:
1.1.1.2 ! root 262: uint8 *v = view.GetPtr(0, y);
! 263: uint8 *c = composite.GetPtr(xscroll, sy);
! 264: if (xscroll + view.GetWidth() <= composite.GetWidth()) {
! 265: memcpy(v, c, view.GetWidth());
! 266: } else {
! 267: // はみ出しの折り返し
! 268: int len1 = composite.GetWidth() - xscroll;
! 269: memcpy(v, c, len1);
! 270: v += len1;
! 271: int sy2 = GetWrapY(sy);
! 272: c = composite.GetPtr(0, sy2);
! 273: int len2 = view.GetWidth() - len1;
! 274: memcpy(v, c, len2);
1.1 root 275: }
276: }
277:
278: // view (I8) を Bitmap (RGB) に展開する
1.1.1.2 ! root 279: for (int y = 0; y < view_mod.size(); y++) {
! 280: if (view_mod[y] == 0) {
! 281: continue;
! 282: }
! 283:
! 284: const uint8 *s8 = view.GetPtr(0, y);
! 285: uint32 *d = (uint32 *)bitmap.GetPtr(0, y);
1.1 root 286:
1.1.1.2 ! root 287: const uint32 *s = (const uint32 *)s8;
! 288: const uint32 *send = (const uint32 *)(s8 + view.GetWidth());
! 289: for (; s < send;) {
! 290: // s は BitmapI8 なのでビッグエンディアン並びに相当
1.1 root 291:
292: #if defined(__AVX2__) && !defined(NO_AVX) // オプション -mavx2 が必要
1.1.1.2 ! root 293: // 35.1msec
1.1 root 294:
1.1.1.2 ! root 295: // 8ピクセルずつ処理する
1.1 root 296:
1.1.1.2 ! root 297: // idx0 = $00000000'00000000'I7I6I5I4'I3I2I1I0
! 298: __m128i idx0 = _mm_loadl_epi64((const __m128i *)s);
! 299: s += 2;
! 300:
! 301: // idx = $000000I7'000000I6'000000I5'000000I4'
! 302: // 000000I3'000000I2'000000I1'000000I0
! 303: __m256i idx = _mm256_cvtepu8_epi32(idx0);
! 304:
! 305: // 8個の 32bit インデックスから XBGR を取得
! 306: // c = $00B7G7R7'00B6G6R6'00B5G5R5'00B4G4R4'
! 307: // 00B3G3R3'00B2G2R2'00B1G1R1'00B0G0R0
! 308: __m256i c = _mm256_i32gather_epi32(
! 309: (const int *)palette, idx, 4);
! 310:
! 311: // 256bit リニアに使えれば下詰めで済んだんだが、
! 312: // 上位 128bit と下位 128bit をまたいでシャッフルは
! 313: // 出来なくて、かつこの後のストアは連続領域への
! 314: // 書き込みになるので、中央でつなげておく。うーん。
! 315: // c = $00000000'B7G7R7B6'G6R6B5G5'R5B4G4R4'
! 316: // B3G3R3B2'G2R2B1G1'R1B0G0R0'00000000
! 317: __m256i shuff = _mm256_set_epi8(
! 318: -1,-1,-1,-1, 14,13,12,10, 9, 8, 6, 5, 4, 2, 1, 0,
! 319: 14,13,12,10, 9, 8, 6, 5, 4, 2, 1, 0, -1,-1,-1,-1
! 320: );
! 321: __m256i res = _mm256_shuffle_epi8(c, shuff);
! 322:
! 323: // 先頭(下位)と末尾(上位)の 32bit ずつを除いたところを
! 324: // 書き出す。
! 325: __m256i mask = _mm256_set_epi32(
! 326: 0, -1, -1, -1, -1, -1, -1, 0
! 327: );
! 328: _mm256_maskstore_epi32((int *)(d - 1), mask, res);
! 329: d += 6;
1.1 root 330: #else
1.1.1.2 ! root 331: // 46.5msec
1.1 root 332:
1.1.1.2 ! root 333: // 4ピクセルずつ処理する
1.1 root 334:
1.1.1.2 ! root 335: uint32 cc4 = *s++;
! 336: // 左側ピクセルが下位バイトになるよう並び替える
! 337: // (le32toh() はビッグエンディアンなら bswap32 の意)
! 338: cc4 = le32toh(cc4);
! 339:
! 340: // 4ピクセルをそれぞれ xBGR32 に変換
! 341: uint32 c0 = palette[ cc4 & 0xff].xbgr;
! 342: uint32 c1 = palette[(cc4 >> 8) & 0xff].xbgr;
! 343: uint32 c2 = palette[(cc4 >> 16) & 0xff].xbgr;
! 344: uint32 c3 = palette[ cc4 >> 24 ].xbgr;
! 345:
! 346: // xBGR32 4つ(16バイト)を RGB24 3つ(12バイト)に変換して
! 347: // ロングワードで書き出す。
! 348: // c0 = $x0B0G0R0
! 349: // c1 = $x1B1G1R1
! 350: // c2 = $x2B2G2R2
! 351: // c3 = $x3B3G3R3
! 352: //
! 353: // d[0] = $00B0G0R0 | $R1000000 = $R1B0G0R0
! 354: // d[1] = $0000B1G1 | $G2R20000 = $G2R2B1G1
! 355: // d[2] = $000000B2 | $B3G3R300 = $B3G3R3B2
! 356:
! 357: // (le32toh() はビッグエンディアンなら bswap32 の意)
! 358: *d++ = le32toh((c0 ) | (c1 << 24));
! 359: *d++ = le32toh((c1 >> 8) | (c2 << 16));
! 360: *d++ = le32toh((c2 >> 16) | (c3 << 8));
1.1 root 361: #endif
362: }
363: }
364: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.