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