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