|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.