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