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