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

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"
1.1.1.5   root       14: #include <algorithm>
                     15: #include <array>
                     16: 
                     17: // v[] がいずれかでも true なら true を返す。
                     18: static bool
                     19: AnyOf(const std::vector<bool>& v)
                     20: {
                     21:        return std::any_of(v.begin(), v.end(), [](bool x) { return x; });
                     22: }
1.1       root       23: 
                     24: // コンストラクタ
1.1.1.2   root       25: PlaneVRAMDevice::PlaneVRAMDevice(int width_, int height_)
                     26:        : inherited(OBJ_PLANEVRAM)
1.1       root       27: {
1.1.1.2   root       28:        // ポカ避け。継承クラス側が設定する。
1.1       root       29:        nplane = -1;
                     30: 
                     31:        composite.Create(width_, height_);
                     32: 
1.1.1.5   root       33:        dirty.line.resize(height_);
                     34:        pending.line.resize(height_);
1.1       root       35: }
                     36: 
1.1.1.6 ! root       37: // 初期化
        !            38: bool
        !            39: PlaneVRAMDevice::Init()
1.1       root       40: {
1.1.1.6 ! root       41:        // レンダリング時に使うテーブルを事前に計算。
        !            42:        //
1.1       root       43:        // %abcdefgh の8ビットを
                     44:        // リトルエンディアンホストでは
                     45:        // %0000000h'0000000g'0000000f'0000000e'0000000d'0000000c'0000000b'0000000a
                     46:        // ビッグエンディアンホストでは
                     47:        // %0000000a'0000000b'0000000c'0000000d'0000000e'0000000f'0000000g'0000000h
                     48:        // の64ビットに伸張する。
                     49:        // (8ビットを各バイトの最下位ビットに対応させる)
1.1.1.6 ! root       50:        constexpr uint num = 256;
        !            51:        deptable.reset(new(std::nothrow) uint64[num]);
        !            52:        if ((bool)deptable == false) {
        !            53:                warnx("Cannot allocate %zu bytes at %s",
        !            54:                        sizeof(uint64) * num, __method__);
        !            55:                return false;
        !            56:        }
        !            57:        for (uint i = 0; i < num; i++) {
1.1       root       58:                uint64 res = 0;
                     59:                for (int bit = 0; bit < 8; bit++) {
                     60:                        int n = bit * 8;
                     61: #if BYTE_ORDER == LITTLE_ENDIAN
                     62:                        n = 56 - n;
                     63: #endif
                     64:                        res |= (uint64)((i >> bit) & 1) << n;
                     65:                }
                     66:                deptable[i] = res;
                     67:        }
1.1.1.6 ! root       68: 
        !            69:        return true;
        !            70: }
        !            71: 
        !            72: // デストラクタ
        !            73: PlaneVRAMDevice::~PlaneVRAMDevice()
        !            74: {
        !            75: }
        !            76: 
        !            77: // リセット
        !            78: void
        !            79: PlaneVRAMDevice::ResetHard(bool poweron)
        !            80: {
        !            81:        // XXX ここ?
        !            82:        Invalidate();
1.1       root       83: }
                     84: 
                     85: // 全画面の更新フラグを立てる
                     86: void
                     87: PlaneVRAMDevice::Invalidate()
                     88: {
1.1.1.5   root       89:        std::fill(dirty.line.begin(), dirty.line.end(), 1);
1.1       root       90: }
                     91: 
1.1.1.5   root       92: // VRAM には更新がないが再レンダリングが必要。
                     93: // パレット変更、スクロール等。VM スレッドから呼ばれる。
1.1       root       94: void
1.1.1.5   root       95: PlaneVRAMDevice::Invalidate2()
1.1       root       96: {
1.1.1.5   root       97:        dirty.invalidate2 = true;
1.1       root       98: }
                     99: 
1.1.1.5   root      100: // 画面更新する必要があるか。
                    101: // dirty から pending を更新してみて、更新が一つでもあれば true を返す。
                    102: // VM スレッドで呼ばれる。
1.1.1.4   root      103: bool
1.1.1.5   root      104: PlaneVRAMDevice::Snap()
1.1       root      105: {
1.1.1.5   root      106:        const uint height = composite.GetHeight();
                    107:        bool update_needed;
1.1.1.4   root      108: 
1.1.1.5   root      109:        // 更新 (dirty) があれば pending に重ねる。
                    110:        {
                    111:                std::unique_lock<std::mutex> lock(mtx);
                    112:                for (int y = 0; y < height; y++) {
                    113:                        // std::vector<bool> には operator|=() がない。
                    114:                        if (__predict_false(dirty.line[y])) {
                    115:                                pending.line[y] = true;
                    116:                                dirty.line[y] = false;
                    117:                        }
1.1       root      118:                }
1.1.1.5   root      119: 
                    120:                if (__predict_false(dirty.invalidate2)) {
                    121:                        pending.invalidate2 = true;
                    122:                        dirty.invalidate2 = false;
                    123:                }
                    124: 
                    125:                update_needed = AnyOf(pending.line) || pending.invalidate2;
1.1       root      126:        }
1.1.1.5   root      127: 
                    128:        return update_needed;
1.1       root      129: }
                    130: 
                    131: // 画面合成。
                    132: // レンダラスレッドから呼ばれる。
1.1.1.5   root      133: // dst を更新すれば true を返す。
1.1       root      134: bool
1.1.1.5   root      135: PlaneVRAMDevice::Render(BitmapRGBX& dst)
1.1       root      136: {
                    137:        bool updated = false;
                    138: 
1.1.1.5   root      139:        // ローカルにコピー。
                    140:        ModifyInfo modified;
                    141:        {
                    142:                std::unique_lock<std::mutex> lock(mtx);
                    143:                modified.line = pending.line;
                    144:                std::fill(pending.line.begin(), pending.line.end(), false);
                    145:                modified.invalidate2 = pending.invalidate2;
                    146:                pending.invalidate2 = false;
                    147:        }
                    148: 
                    149:        // VRAM に更新があれば composite を更新。
                    150:        if (AnyOf(modified.line)) {
                    151:                RenderVRAMToComposite(modified.line);
1.1       root      152:                updated = true;
                    153:        }
                    154: 
1.1.1.5   root      155:        // composite に更新があるか(updated)、
                    156:        // 無条件に更新するか(modified.invalidate2) なら dst を更新。
                    157:        if (updated || modified.invalidate2) {
                    158:                RenderCompositeToRGBX(dst, modified);
1.1       root      159:                updated = true;
                    160:        }
                    161: 
                    162:        return updated;
                    163: }
                    164: 
                    165: // VRAM から composite 画面を合成する。
                    166: // レンダラスレッドから呼ばれる。
                    167: void
1.1.1.5   root      168: PlaneVRAMDevice::RenderVRAMToComposite(const std::vector<bool>& modified)
1.1       root      169: {
1.1.1.4   root      170:        const uint width  = composite.GetWidth();
                    171:        const uint height = composite.GetHeight();
                    172:        const uint planesz = (width / 8) * height;
1.1       root      173: 
1.1.1.2   root      174:        // 1bpp はパレット的にはほぼ 4bpp と同じ扱いになる。
                    175:        const int nplane4 = (nplane == 1) ? 4 : nplane;
                    176: 
1.1       root      177:        // テキスト合成画面を描く
1.1.1.4   root      178:        for (uint y = 0; y < height; y++) {
1.1.1.5   root      179:                bool mod = modified[y];
                    180:                if (mod == false) {
1.1.1.2   root      181:                        continue;
                    182:                }
                    183: 
                    184:                const uint32 *src = (const uint32 *)&mem[0];
                    185:                src += y * width / 32;
1.1.1.3   root      186:                uint64 *dst = (uint64 *)composite.GetRowPtr(y);
1.1.1.2   root      187: 
1.1.1.4   root      188:                for (uint x = 0; x < width / 32; x++) {
1.1.1.2   root      189:                        std::array<uint32, 8> data;
                    190: 
                    191:                        // VRAM はホストエンディアンに関係なく 32bit で読み込んだ
                    192:                        // 時の MSB が左端ピクセル。
                    193:                        // ここでは左側から 8ピクセル(8ビット)ずつ 4回処理する
                    194:                        // のを最下位バイトから順に右シフトで行いたいので、
                    195:                        // (バイト内のビット順は維持したまま) バイトスワップする。
                    196:                        //
                    197:                        //          31     24  23     16  15      8   7      0
                    198:                        //        +----------+----------+----------+----------+
                    199:                        // VRAM   |+00 .. +07|+08 .. +15|+16 .. +23|+24 .. +31|
                    200:                        //        +----------+----------+----------+----------+
                    201:                        //  ↓
                    202:                        //        +----------+----------+----------+----------+
                    203:                        // data[] |+24 .. +31|+16 .. +23|+08 .. +15|+00 .. +07|
                    204:                        //        +----------+----------+----------+----------+
                    205:                        if (__predict_false(nplane == 1)) {
                    206:                                // LUNA 1bpp の場合残りの3プレーンは ff で埋める。
                    207:                                // 実際にも存在しないプレーンは ff が読み出せて
                    208:                                // カラーインデックスは $e か $f の二択になるので。
                    209:                                data[0] = bswap32(src[0]);
                    210:                                data[1] = 0xffffffff;
                    211:                                data[2] = 0xffffffff;
                    212:                                data[3] = 0xffffffff;
                    213:                        } else {
1.1.1.4   root      214:                                uint offset = planesz / sizeof(uint32);
1.1.1.2   root      215:                                for (int plane = 0; plane < nplane; plane++) {
                    216:                                        data[plane] = bswap32(src[plane * offset]);
                    217:                                }
1.1       root      218:                        }
1.1.1.2   root      219:                        src++;
1.1       root      220: 
1.1.1.2   root      221:                        // data[] の下位 8bit ずつを取り出して deptable[] で変換。
                    222:                        // p0..p3 は1バイト x 8個。
                    223:                        //
                    224:                        // data[] = %xxxxxxxx'xxxxxxxx'xxxxxxxx'ABCDEFGH
                    225:                        // ↓
                    226:                        // (リトルエンディアンホストの場合)
                    227:                        // p.H = %0000000H'0000000G'0000000F'0000000E
                    228:                        // p.L = %0000000D'0000000C'0000000B'0000000A
                    229:                        for (int b = 0; b < 4; b++) {
                    230:                                uint64 cc = 0;
                    231: 
                    232:                                for (int plane = 0; plane < nplane4; plane++) {
                    233:                                        uint64 p;
                    234:                                        p = deptable[data[plane] & 0xff];
                    235:                                        p <<= plane;
                    236:                                        cc |= p;
                    237:                                        data[plane] >>= 8;
1.1       root      238:                                }
1.1.1.2   root      239: 
                    240:                                // cc (64bit = 8バイト) は各バイトが各ピクセルの
                    241:                                // カラーインデックスになっているので
                    242:                                // BitmapI8 に 8バイト一気に書き込める。
                    243:                                // (エンディアンの影響は deptable[] で処理してある)
                    244:                                *dst++ = cc;
1.1       root      245:                        }
                    246:                }
                    247:        }
                    248: }
                    249: 
1.1.1.3   root      250: // composite 画面とパレット情報から RGBX 画面を合成する。
1.1.1.5   root      251: // modified は composite 上での変更箇所を示している。
1.1       root      252: // レンダラスレッドから呼ばれる。
                    253: void
1.1.1.3   root      254: PlaneVRAMDevice::RenderCompositeToRGBX(BitmapRGBX& dst,
1.1.1.5   root      255:        const ModifyInfo& modified)
1.1       root      256: {
1.1.1.4   root      257:        const uint width  = dst.GetWidth();
                    258:        const uint height = dst.GetHeight();
1.1       root      259: 
1.1.1.2   root      260:        // view_mod は表示領域 view における更新ラスタ
1.1       root      261:        BitmapI8 view(width, height);
1.1.1.2   root      262:        std::vector<uint8> view_mod(height);
1.1       root      263: 
                    264:        // この view に対する modify を計算する
1.1.1.5   root      265:        if (__predict_false(modified.invalidate2)) {
1.1.1.2   root      266:                std::fill(view_mod.begin(), view_mod.end(), 1);
1.1       root      267:        } else {
                    268:                // Y scroll 方向の modify 計算
1.1.1.2   root      269:                int sy = yscroll;
                    270:                for (int y = 0; y < height; y++, sy++) {
                    271:                        if (sy >= composite.GetHeight()) {
                    272:                                sy -= composite.GetHeight();
1.1       root      273:                        }
1.1.1.5   root      274:                        view_mod[y] = modified.line[sy];
1.1       root      275:                }
                    276: 
1.1.1.2   root      277:                // 右がはみ出す場合は、はみ出した先のラスタも加味する。
                    278:                if (xscroll + view.GetWidth() > composite.GetWidth()) {
                    279:                        sy = yscroll;
                    280:                        for (int y = 0; y < height; y++, sy++) {
                    281:                                if (sy >= composite.GetHeight()) {
                    282:                                        sy -= composite.GetHeight();
                    283:                                }
                    284:                                int sy2 = GetWrapY(sy);
1.1.1.5   root      285:                                view_mod[y] |= modified.line[sy2];
1.1       root      286:                        }
                    287:                }
                    288:        }
                    289: 
                    290:        // composite からスクロールを加味した表示領域 view を作る
1.1.1.2   root      291:        int sy = yscroll;
                    292:        for (int y = 0; y < height; y++, sy++) {
                    293:                if (sy >= composite.GetHeight()) {
                    294:                        sy -= composite.GetHeight();
                    295:                }
                    296:                if (view_mod[y] == 0) {
                    297:                        continue;
                    298:                }
1.1       root      299: 
1.1.1.3   root      300:                uint8 *v = view.GetRowPtr(y);
1.1.1.2   root      301:                uint8 *c = composite.GetPtr(xscroll, sy);
                    302:                if (xscroll + view.GetWidth() <= composite.GetWidth()) {
                    303:                        memcpy(v, c, view.GetWidth());
                    304:                } else {
                    305:                        // はみ出しの折り返し
                    306:                        int len1 = composite.GetWidth() - xscroll;
                    307:                        memcpy(v, c, len1);
                    308:                        v += len1;
                    309:                        int sy2 = GetWrapY(sy);
1.1.1.3   root      310:                        c = composite.GetRowPtr(sy2);
1.1.1.2   root      311:                        int len2 = view.GetWidth() - len1;
                    312:                        memcpy(v, c, len2);
1.1       root      313:                }
                    314:        }
                    315: 
1.1.1.3   root      316:        // view (I8) を Bitmap (RGBX) に展開する
1.1.1.4   root      317:        dst.DrawBitmapI8Raster(view, palette, &view_mod[0]);
1.1       root      318: }

unix.superglobalmegacorp.com

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