Annotation of nono/vm/planevram.cpp, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.