|
|
1.1 root 1: //
2: // nono
1.1.1.3 ! root 3: // Copyright (C) 2020 nono project
! 4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: #include "object.h"
8: #include "bitmap.h"
9: #include "bt454.h"
1.1.1.3 ! root 10: #include "mythread.h"
1.1 root 11: #include "renderer.h"
12: #include "tvram.h"
13:
1.1.1.2 root 14: std::unique_ptr<Renderer> gRenderer;
1.1 root 15:
16: static void *renderer_run(void *);
17:
18: // コンストラクタ
19: Renderer::Renderer()
20: {
21: logname = "renderer";
22: devname = "Render";
23: }
24:
25: // デストラクタ
26: Renderer::~Renderer()
27: {
28: // imagebuf の所有権は持っていないので解放不要。
29:
1.1.1.2 root 30: if (thread_created) {
31: Terminate();
32: pthread_join(thread, NULL);
33: }
1.1 root 34: }
35:
36: bool
37: Renderer::Init()
38: {
39: // レンダリングスレッド起動
1.1.1.2 root 40: pthread_create(&thread, NULL, renderer_run, NULL);
41: thread_created = true;
1.1 root 42:
43: return true;
44: }
45:
46: bool
47: Renderer::PowerOff()
48: {
49: if (imagebuf) {
50: // レンダリングスレッドに指示
51: std::unique_lock<std::mutex> lock(mtx);
52: request |= REQ_REND_POWEROFF;
53: cv.notify_one();
54: }
55:
56: return true;
57: }
58:
59: // バッファとコールバックを指定する。
60: // 引数 buf と callback はどちらも NULL であってはならない。
61: // これは画面を必要とする wx 側から呼ばれる。CLI では呼ばれない。
62: void
63: Renderer::SetBuf(uint8 *buf, void (*callback)(void))
64: {
65: assert(buf);
66: assert(callback);
67:
68: imagebuf = buf;
69: refresh_callback = callback;
70: }
71:
72: // 画面合成処理 (CRTC とかから呼ばれる部分)
73: // 今は一画面単位で合成を行なっている。ラスター云々は未対応。
74: // 画面合成を行うかどうかとか、レンダリングスレッドに指示を出すかとか
75: // どこら辺までを呼び出し元とこっちが分担するかは未定。
76: void
77: Renderer::Render()
78: {
79: // CLI 版なら何もしない。
80: if (imagebuf == NULL) {
81: return;
82: }
83:
84: // スケジューラ都合による間引きとかがあればここらへんで評価?
85: // XXX どっちのスレッドでやるか
86:
87: // レンダリングスレッドに指示
88: std::unique_lock<std::mutex> lock(mtx);
89: request |= REQ_REND_NORMAL;
90: cv.notify_one();
91: }
92:
93: // レンダリングスレッドのエントリポイント
94: void *
95: renderer_run(void *dummy)
96: {
1.1.1.3 ! root 97: PTHREAD_SETNAME("Renderer");
1.1 root 98:
99: gRenderer->ThreadRun();
100: return NULL;
101: }
102:
103: // レンダリングスレッド
104: void
105: Renderer::ThreadRun()
106: {
107: for (;;) {
108: uint32 req;
109:
110: // 何か起きるまで待つ
111: {
112: std::unique_lock<std::mutex> lock(mtx);
113: cv.wait(lock, [&] { return (request != 0); });
114: req = request;
115: request = 0;
116: }
117:
118: if ((req & REQ_TERMINATE)) {
119: // 終了するので、他のフラグは無視
120: break;
121:
122: } else if ((req & REQ_REND_POWEROFF)) {
123: // 電源オフ時の描画 (通常描画フラグは無視)
124:
125: // 電源オフされると画面を灰色にする。
126: // 黒でもいいけど、電源オンで黒いままなのと区別したい。
127: memset(imagebuf, 0x70, width * height * BPP);
128: // ウィンドウに通知
129: refresh_callback();
130:
131: } else if ((req & REQ_REND_NORMAL)) {
132: // 通常描画
133:
134: // レンダラを駆動して
135: if (RenderMain()) {
136: // 更新があればウィンドウに通知
137: refresh_callback();
138: }
139: }
140: }
141: }
142:
1.1.1.2 root 143: // スレッドの終了を指示 (スレッドは動いていること)
144: void
145: Renderer::Terminate()
146: {
147: assert(thread_created);
148:
149: std::unique_lock<std::mutex> lock(mtx);
150: request |= REQ_TERMINATE;
151: cv.notify_one();
152: }
153:
1.1 root 154: //
155: // X68030 レンダラ
156: //
157:
158: // コンストラクタ
159: X68030Renderer::X68030Renderer()
160: {
161: // XXX とりあえず
162: width = 768;
163: height = 512;
164: }
165:
166: // デストラクタ
167: X68030Renderer::~X68030Renderer()
168: {
169: }
170:
171: // 画面合成
172: bool
173: X68030Renderer::RenderMain()
174: {
175: assert(imagebuf);
176:
177: // 今はテキスト画面のみを合成バッファに直接描いている
178: return gTVRAM->Render(imagebuf);
179: }
180:
181: //
182: // LUNA レンダラ
183: //
184:
185: // コンストラクタ
186: LunaRenderer::LunaRenderer()
187: {
188: width = 1280;
189: height = 1024;
190: }
191:
192: // デストラクタ
193: LunaRenderer::~LunaRenderer()
194: {
195: }
196:
197: // 画面合成
198: bool
199: LunaRenderer::RenderMain()
200: {
201: const uint8 *pal;
202: const uint32 *plane0;
203: std::atomic<uint8> *dirty;
204: int hscroll;
205: int vscroll;
206: uint8 *dst0;
207: bool updated;
208:
209: assert(imagebuf);
210: dst0 = imagebuf;
211:
212: // パレット取得
213: pal = gBT454->GetHostPalette();
214:
215: // ビットマップ
216: gBitmap->GetRFCNT(&hscroll, &vscroll);
217: const uint8 *mem = gBitmap->GetBuf();
218: dirty = gBitmap->GetDirty();
219:
220: updated = false;
221:
222: // dirty は VRAM への書き込みがあれば常に 1 にする。
223: // レンダリング時に dirty が 1 なら 2 にしてレンダリング。
224: // レンダリング終了時に dirty が 2 なら 0 に戻す。
225: // これによりレンダリング中に VRAM への書き込みがあれば次回描画される。
226:
227: // sy はメモリ上の Y 座標
228: // dy はレンダラ画面の Y 座標 (RFCNT のラスタカウンタを考慮)
229:
230: // XXX とりあえず1プレーン
1.1.1.3 ! root 231: plane0 = (const uint32 *)mem;
1.1 root 232: int sy = vscroll;
233: for (int dy = 0; dy < height; dst0 += width * BPP, dy++, sy++) {
234: sy &= 0x3ff;
235:
236: if (atomic_cas_8(&dirty[sy], 1, 2) == 1) {
237: uint8 *dst = dst0;
238: // sx はメモリ上の X オフセット (ロングワード単位)
239: // dx はレンダラ画面の X オフセット (ロングワード単位)
240: // XXX 水平オフセットが仕様以上の時の挙動は未実装
241:
242: int sx = (sy * 2048 + hscroll) / 32;
243: int shift = (hscroll & 31);
244: for (int dx = 0; dx < width / 32; dx++, sx++) {
245: uint32 p0 = plane0[sx];
246: if (shift) {
247: p0 = (p0 << shift) | (plane0[sx + 1] >> (32 - shift));
248: }
249:
250: // 1ロングワードの横 32bit が横 32dot に相当
251: for (int j = 0; j < 32; j++) {
252: // 1bpp の時 P1,P2,P3 分はプルアップされて %1 に
253: // なっているっぽいので、実質 #14 か #15 ということ。
254: uint c = 14;
255: // カラーコード(パレット番号)を作成
256: if ((p0 & 0x80000000U)) c |= 1;
257: p0 <<= 1;
258:
259: *dst++ = pal[c * 4 + 0];
260: *dst++ = pal[c * 4 + 1];
261: *dst++ = pal[c * 4 + 2];
262: }
263: }
264:
265: // 2の時だけ0に戻す
266: atomic_cas_8(&dirty[sy], 2, 0);
267: updated = true;
268: }
269: }
270:
271: return updated;
272: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.