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