|
|
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:
1.1.1.6 root 7: //
8: // レンダラ
9: //
10:
1.1.1.4 root 11: #include "renderer.h"
1.1.1.11 root 12: #include "console.h"
13: #include "monitor.h"
1.1.1.9 root 14: #include "planevram.h"
1.1.1.10 root 15: #include "scheduler.h"
1.1.1.8 root 16: #include "syncer.h"
1.1.1.6 root 17: #include "uimessage.h"
1.1.1.9 root 18: #include "videoctlr.h"
1.1 root 19:
1.1.1.10 root 20: #if 0
21: #define PERFREND
22: #include "stopwatch.h"
23: static Stopwatch sw_notify;
1.1.1.14! root 24: static uint64 last_report_rtime;
1.1.1.10 root 25: static uint64 notify_time;
26: static uint notify_count;
27: static uint64 render_time;
28: static uint64 stretch_time;
29: static uint64 convert_time;
30: static uint convert_count;
31: #endif
32:
33: // 電源オフ時の画面色 (電源オンで黒なのとは区別したいのであえて灰色)
34: static const Color ScreenOffColor(0x70, 0x70, 0x70);
35:
1.1 root 36: // コンストラクタ
37: Renderer::Renderer()
1.1.1.8 root 38: : inherited(OBJ_RENDERER)
1.1 root 39: {
1.1.1.11 root 40: monitor = gMonitorManager->Regist(ID_MONITOR_RENDERER, this);
41: monitor->func = ToMonitorCallback(&Renderer::MonitorUpdate);
42: monitor->SetSize(42, 10);
1.1 root 43: }
44:
45: // デストラクタ
46: Renderer::~Renderer()
47: {
1.1.1.6 root 48: TerminateThread();
1.1.1.8 root 49: }
50:
51: // 初期化
52: bool
53: Renderer::Init()
54: {
55: syncer = GetSyncer();
1.1.1.14! root 56: uimessage = gMainApp.GetUIMessage();
1.1.1.8 root 57:
1.1.1.10 root 58: // GUI ならレンダラ有効。
59: if (gMainApp.IsGUI()) {
60: enable = true;
61: bitmap.Create(width, height);
62: // 初期値 (どこでやる?)
63: bitmap.Fill(ScreenOffColor);
64: }
65:
1.1.1.8 root 66: return true;
1.1 root 67: }
68:
1.1.1.6 root 69: // リセット
70: void
71: Renderer::ResetHard(bool poweron)
1.1 root 72: {
1.1.1.8 root 73: next_refresh_rtime = syncer->GetRealTime();
1.1 root 74:
1.1.1.6 root 75: // 統計情報をクリア
1.1.1.10 root 76: uint64 now = scheduler->GetVirtTime();
77: stat_input_count = 0;
78: last_input_time = now;
79: ma_input_span.Clear();
80: stat_render_count = 0;
81: stat_noupdate_count = 0;
82: stat_nobitmap_count = 0;
83: stat_output_count = 0;
84: last_output_time = now;
85: ma_output_span.Clear();
1.1.1.14! root 86: #if defined(PERFREND)
! 87: last_report_rtime = syncer->GetRealTime();
! 88: #endif
1.1 root 89: }
90:
1.1.1.6 root 91: // 電源オフ
92: void
1.1 root 93: Renderer::PowerOff()
94: {
1.1.1.6 root 95: if (enable) {
1.1.1.11 root 96: #if defined(PERFREND)
97: sw_notify.Restart();
98: #endif
1.1 root 99: // レンダリングスレッドに指示
1.1.1.14! root 100: std::lock_guard<std::mutex> lock(mtx);
1.1.1.10 root 101: request |= REQ_SCREENOFF;
1.1 root 102: cv.notify_one();
103: }
104: }
105:
1.1.1.11 root 106: // 画面合成処理 (VM スレッドで呼ばれる)。
1.1 root 107: // 今は一画面単位で合成を行なっている。ラスター云々は未対応。
108: void
1.1.1.10 root 109: Renderer::NotifyRender()
1.1 root 110: {
111: // CLI 版なら何もしない。
1.1.1.6 root 112: if (enable == false) {
1.1 root 113: return;
114: }
115:
1.1.1.10 root 116: // 統計情報
117: stat_input_count++;
118: uint64 now = scheduler->GetVirtTime();
119: ma_input_span.EnqueueForce(now - last_input_time);
120: last_input_time = now;
121:
122: #if defined(PERFREND)
1.1.1.11 root 123: sw_notify.Restart();
1.1.1.10 root 124: #endif
1.1.1.6 root 125:
1.1.1.11 root 126: // レンダラスレッドに描画指示を出すだけ。
127: {
1.1.1.14! root 128: std::lock_guard<std::mutex> lock(mtx);
1.1.1.11 root 129: request |= REQ_RENDER;
130: cv.notify_one();
1.1.1.6 root 131: }
1.1 root 132: }
133:
134: // レンダリングスレッド
135: void
136: Renderer::ThreadRun()
137: {
1.1.1.10 root 138: if (enable) {
139: SetThreadAffinityHint(AffinityClass::Heavy);
140: }
141:
142: int64 delay = 0;
1.1 root 143: for (;;) {
144: uint32 req;
145:
146: // 何か起きるまで待つ
147: {
148: std::unique_lock<std::mutex> lock(mtx);
1.1.1.10 root 149: if (delay == 0) {
150: cv.wait(lock, [&] { return (request != 0); });
151: } else {
152: cv.wait_for(lock, std::chrono::nanoseconds(delay),
153: [&] { return (request != 0); });
154: if (request == 0) {
155: request = REQ_POST;
156: }
157: }
1.1 root 158: req = request;
159: request = 0;
160: }
161:
162: if ((req & REQ_TERMINATE)) {
163: // 終了するので、他のフラグは無視
164: break;
1.1.1.4 root 165: }
1.1 root 166:
1.1.1.10 root 167: // req に基づいて処理。
168: int64 t = DoRender(req);
1.1.1.14! root 169: #if defined(PERFREND)
! 170: PrintPerf();
! 171: #endif
1.1.1.10 root 172: if (t == 0 || (req & REQ_POST)) {
173: // UI に即通知。
174:
175: // 統計情報
176: stat_output_count++;
177: uint64 now = syncer->GetRealTime();
178: ma_output_span.EnqueueForce(now - last_output_time);
179: last_output_time = now;
180:
1.1.1.14! root 181: uimessage->Post(UIMessage::RENDER);
1.1.1.10 root 182: delay = 0;
183: } else if (t > 0) {
184: // 指定時間後に UI に通知。
185: delay = t;
1.1.1.4 root 186: }
1.1.1.10 root 187: }
188: }
1.1 root 189:
1.1.1.10 root 190: // req に基づいて描画する。
191: // UI への描画通知を遅延させる時間を返す (即通知する場合は 0)。
192: // UI に通知しない場合は -1 を返す。
193: int64
194: Renderer::DoRender(uint32 req)
195: {
196: // 1. REND_NORMAL なら電源オン時の通常描画指示、
197: // REND_POWEROFF なら電源オン→オフ遷移時の描画指示。
198: // 2. REND_RESIZE なら valid = false にして、表示ビットマップをリサイズ。
199: // 3. 必要なら bitmap を拡大縮小。
200: // 4. bitmap24 に描画して valid = true;
1.1.1.6 root 201:
1.1.1.10 root 202: #if defined(PERFREND)
203: Stopwatch sw;
204: #endif
205: bool updated = false;
206:
207: if ((req & REQ_RENDER)) {
208: // 通常描画
209:
210: #if defined(PERFREND)
211: sw_notify.Stop();
212: notify_time += sw_notify.Elapsed();
213: notify_count++;
214:
1.1.1.11 root 215: sw.Restart();
1.1.1.10 root 216: #endif
1.1.1.11 root 217: // レンダラを駆動する。
218: updated = RenderMD();
1.1.1.10 root 219: #if defined(PERFREND)
1.1.1.11 root 220: sw.Stop();
221: render_time += sw.Elapsed();
1.1.1.10 root 222: #endif
1.1.1.11 root 223: stat_render_count++;
1.1 root 224: }
1.1.1.10 root 225: if ((req & REQ_SCREENOFF)) {
226: // 電源オン→オフ時の描画
227: bitmap.Fill(ScreenOffColor);
228: updated = true;
229: }
230:
231: if ((req & REQ_RESIZE)) {
232: // 表示ビットマップのリサイズ要求
233:
234: bitmap24_valid = false;
1.1.1.13 root 235: bitmap24.reset();
236: try {
237: bitmap24.reset(new BitmapRGB(new_viewwidth, new_viewheight));
238: } catch (...) { }
1.1.1.10 root 239: if ((bool)bitmap24 == false) {
240: putlog(0, "Could not allocate BitmapRGB(%u, %u)",
241: new_viewwidth, new_viewheight);
242: return -1;
243: }
244: viewrect = Rect(0, 0, new_viewwidth, new_viewheight);
245: updated = true;
246: }
247:
248: // 更新箇所がなければここで終わり。
249: if (updated == false) {
250: stat_noupdate_count++;
251: return -1;
252: }
253:
254: // 出力ビットマップが用意できてなければここで終わり。
255: if (__predict_false((bool)bitmap24 == false)) {
256: stat_nobitmap_count++;
257: return -1;
258: }
259:
260: // 等倍表示でなければ拡大縮小。
261: #if defined(PERFREND)
262: sw.Restart();
263: #endif
264: BitmapRGBX scaled_bitmap;
265: BitmapRGBX *src_bitmap;
266: if (bitmap.GetWidth() == bitmap24->GetWidth() &&
267: bitmap.GetHeight() == bitmap24->GetHeight())
268: {
269: src_bitmap = &bitmap;
270: } else {
271: scaled_bitmap.Create(viewrect.w, viewrect.h);
272: scaled_bitmap.DrawBitmapStretch(viewrect, bitmap);
273: src_bitmap = &scaled_bitmap;
274: }
275: #if defined(PERFREND)
276: sw.Stop();
277: stretch_time += sw.Elapsed();
278: #endif
279:
280: // wxWidgets 用の RGBX→RGB 変換。
281: #if defined(PERFREND)
282: sw.Restart();
283: #endif
284: src_bitmap->ConvertToRGB(*bitmap24);
285: bitmap24_valid = true;
286: #if defined(PERFREND)
287: sw.Stop();
288: convert_time += sw.Elapsed();
1.1.1.14! root 289: convert_count++;
1.1.1.10 root 290: #endif
291:
292: if ((req & (REQ_SCREENOFF | REQ_RESIZE))) {
293: // 電源オン→オフ遷移かリサイズ要求時は即通知。
294: return 0;
295: }
296:
297: // 通常描画のみならここで通知を律速する。
298: uint64 rtime = syncer->GetRealTime();
299: if (rtime < next_refresh_rtime) {
300: return next_refresh_rtime - rtime;
301: }
302: next_refresh_rtime = rtime + refresh_interval;
303: return 0;
1.1 root 304: }
305:
1.1.1.14! root 306: #if defined(PERFREND)
! 307: void
! 308: Renderer::PrintPerf()
! 309: {
! 310: uint64 now = syncer->GetRealTime();
! 311: if (now - last_report_rtime < 5_sec) {
! 312: return;
! 313: }
! 314:
! 315: double rate = 0;
! 316: uint nt = 0;
! 317: uint rt = 0;
! 318: uint st = 0;
! 319: uint ct = 0;
! 320: if (notify_count != 0) {
! 321: rate = (double)notify_count * 1_sec / (now - last_report_rtime);
! 322: nt = notify_time / notify_count / 1000U;
! 323: rt = render_time / notify_count / 1000U;
! 324: }
! 325: if (convert_count != 0) {
! 326: st = stretch_time / convert_count / 1000U;
! 327: ct = convert_time / convert_count / 1000U;
! 328: }
! 329:
! 330: printf(
! 331: "%5.1f calls/s: Notify %u us, Render %u us, Stretch %u us, Convert %u us\n",
! 332: rate, nt, rt, st, ct);
! 333:
! 334: notify_time = 0;
! 335: render_time = 0;
! 336: stretch_time = 0;
! 337: convert_time = 0;
! 338: notify_count = 0;
! 339: convert_count = 0;
! 340: last_report_rtime = now;
! 341: }
! 342: #endif // PERFREND
! 343:
1.1.1.6 root 344: // スレッドの終了を指示
1.1.1.2 root 345: void
346: Renderer::Terminate()
347: {
1.1.1.14! root 348: std::lock_guard<std::mutex> lock(mtx);
1.1.1.2 root 349: request |= REQ_TERMINATE;
350: cv.notify_one();
351: }
352:
1.1.1.10 root 353: // リサイズ指示
354: void
355: Renderer::ResizeView(uint width_, uint height_)
356: {
357: assert(enable);
358:
359: new_viewwidth = width_;
360: new_viewheight = height_;
361:
1.1.1.14! root 362: std::lock_guard<std::mutex> lock(mtx);
1.1.1.10 root 363: request |= REQ_RESIZE;
364: cv.notify_one();
365: }
366:
1.1.1.6 root 367: // 描画更新(通知)間隔を設定
368: void
369: Renderer::SetRefreshInterval(uint64 t)
370: {
371: refresh_interval = t;
372: }
373:
374: // モニタ更新
375: void
376: Renderer::MonitorUpdate(Monitor *, TextScreen& screen)
377: {
378: int x;
379: int y;
1.1.1.14! root 380: uint64 ma;
1.1.1.6 root 381:
382: screen.Clear();
383:
1.1.1.10 root 384: x = 16;
1.1.1.6 root 385: y = 0;
386:
1.1.1.10 root 387: screen.Puts(0, y++, "<Statistics>");
1.1.1.6 root 388:
1.1.1.10 root 389: screen.Puts(0, y, "Input Count");
390: screen.Print(x, y++, "%26s", format_number(stat_input_count).c_str());
391: screen.Puts(0, y, "Input Frequency(VT)");
1.1.1.14! root 392: ma = ma_input_span.GetMovingAverage();
1.1.1.10 root 393: screen.Print(x + 18, y, "%5.1f Hz",
394: ma == 0 ? 0.0 : (double)1000'000'000 / ma);
1.1.1.6 root 395: y++;
396:
1.1.1.10 root 397: y++;
398: screen.Puts(0, y, "RenderMD Count");
399: screen.Print(x, y++, "%26s", format_number(stat_render_count).c_str());
400: screen.Puts(0, y, "NoUpdate Count");
401: screen.Print(x, y++, "%26s", format_number(stat_noupdate_count).c_str());
402: screen.Puts(0, y, "NoBitmap Count");
403: screen.Print(x, y++, "%26s", format_number(stat_nobitmap_count).c_str());
1.1.1.9 root 404:
1.1.1.10 root 405: y++;
406: screen.Puts(0, y, "Output Count");
407: screen.Print(x, y++, "%26s", format_number(stat_output_count).c_str());
408: screen.Puts(0, y, "Output Frequency(RT)");
1.1.1.14! root 409: ma = ma_output_span.GetMovingAverage();
1.1.1.10 root 410: screen.Print(x + 18, y, "%5.1f Hz",
411: ma == 0 ? 0.0 : (double)1000'000'000 / ma);
412: y++;
1.1.1.9 root 413: }
414:
1.1.1.6 root 415:
1.1 root 416: //
417: // X68030 レンダラ
418: //
419:
420: // コンストラクタ
421: X68030Renderer::X68030Renderer()
422: {
423: // XXX とりあえず
424: width = 768;
425: height = 512;
1.1.1.9 root 426:
1.1.1.10 root 427: bitmap.Create(width, height);
1.1 root 428: }
429:
430: // デストラクタ
431: X68030Renderer::~X68030Renderer()
432: {
433: }
434:
1.1.1.9 root 435: // 初期化
436: bool
437: X68030Renderer::Init()
438: {
439: if (inherited::Init() == false) {
440: return false;
441: }
442:
443: videoctlr = GetVideoCtlrDevice();
444:
445: return true;
446: }
447:
1.1 root 448: // 画面合成
449: bool
1.1.1.10 root 450: X68030Renderer::RenderMD()
1.1 root 451: {
1.1.1.11 root 452: bool updated = videoctlr->Render(bitmap);
1.1.1.9 root 453: return updated;
1.1 root 454: }
455:
1.1.1.6 root 456:
1.1 root 457: //
458: // LUNA レンダラ
459: //
460:
461: // コンストラクタ
462: LunaRenderer::LunaRenderer()
463: {
464: width = 1280;
465: height = 1024;
1.1.1.9 root 466:
1.1.1.10 root 467: bitmap.Create(width, height);
1.1 root 468: }
469:
470: // デストラクタ
471: LunaRenderer::~LunaRenderer()
472: {
473: }
474:
1.1.1.9 root 475: // 初期化
476: bool
477: LunaRenderer::Init()
478: {
479: if (inherited::Init() == false) {
480: return false;
481: }
482:
483: planevram = GetPlaneVRAMDevice();
484:
485: return true;
486: }
487:
1.1 root 488: // 画面合成
489: bool
1.1.1.10 root 490: LunaRenderer::RenderMD()
1.1 root 491: {
1.1.1.11 root 492: bool updated = planevram->Render(bitmap);
1.1.1.9 root 493: return updated;
1.1.1.8 root 494: }
495:
496:
497: //
1.1.1.11 root 498: // コンソールレンダラ
1.1.1.8 root 499: //
500:
501: // コンストラクタ
1.1.1.11 root 502: ConsoleRenderer::ConsoleRenderer()
1.1.1.8 root 503: {
1.1.1.11 root 504: // 視認性のためフチを追加。
505: width = 640 + Padding * 2;
506: height = 480 + Padding * 2;
507:
508: bitmap.Create(width, height);
1.1.1.8 root 509: }
510:
511: // デストラクタ
1.1.1.11 root 512: ConsoleRenderer::~ConsoleRenderer()
1.1.1.8 root 513: {
514: }
515:
1.1.1.11 root 516: // 初期化
1.1.1.10 root 517: bool
1.1.1.11 root 518: ConsoleRenderer::Init()
1.1.1.10 root 519: {
1.1.1.11 root 520: if (inherited::Init() == false) {
521: return false;
522: }
1.1.1.10 root 523:
1.1.1.11 root 524: console = GetConsoleDevice();
525:
526: return true;
1.1.1.10 root 527: }
528:
1.1.1.8 root 529: // 画面合成
530: bool
1.1.1.11 root 531: ConsoleRenderer::RenderMD()
1.1.1.8 root 532: {
1.1.1.11 root 533: bool updated = console->Render(bitmap);
534: return updated;
1.1.1.6 root 535: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.