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