--- nono/vm/renderer.cpp 2026/04/29 17:05:28 1.1.1.10 +++ nono/vm/renderer.cpp 2026/04/29 17:05:59 1.1.1.16 @@ -5,10 +5,12 @@ // // -// レンダラ +// ビデオレンダラ // #include "renderer.h" +#include "console.h" +#include "monitor.h" #include "planevram.h" #include "scheduler.h" #include "syncer.h" @@ -18,14 +20,13 @@ #if 0 #define PERFREND #include "stopwatch.h" -static uint64 check_time; -static uint check_count; static Stopwatch sw_notify; -static uint64 notify_time; +static uint64 last_report_rtime; +static uint64 notify_nsec; static uint notify_count; -static uint64 render_time; -static uint64 stretch_time; -static uint64 convert_time; +static uint64 render_nsec; +static uint64 stretch_nsec; +static uint64 convert_nsec; static uint convert_count; #endif @@ -33,36 +34,28 @@ static uint convert_count; static const Color ScreenOffColor(0x70, 0x70, 0x70); // コンストラクタ -Renderer::Renderer() - : inherited(OBJ_RENDERER) +VideoRenderer::VideoRenderer() + : inherited(OBJ_VIDEO_RENDERER) { + AddAlias("Renderer"); + + monitor = gMonitorManager->Regist(ID_MONITOR_RENDERER, this); + monitor->SetCallback(&VideoRenderer::MonitorScreen); + monitor->SetSize(42, 10); } // デストラクタ -Renderer::~Renderer() +VideoRenderer::~VideoRenderer() { TerminateThread(); } -// モニタを初期化する (継承先コンストラクタから必要なら呼ばれる) -void -Renderer::InitMonitor() -{ - monitor.func = ToMonitorCallback(&Renderer::MonitorUpdate); - monitor.SetSize(42, 16); - monitor.Regist(ID_MONITOR_RENDERER); -} - // 初期化 bool -Renderer::Init() +VideoRenderer::Init() { - if (inherited::Init() == false) { - return false; - } - - scheduler = GetScheduler(); syncer = GetSyncer(); + uimessage = gMainApp.GetUIMessage(); // GUI ならレンダラ有効。 if (gMainApp.IsGUI()) { @@ -77,7 +70,7 @@ Renderer::Init() // リセット void -Renderer::ResetHard(bool poweron) +VideoRenderer::ResetHard(bool poweron) { next_refresh_rtime = syncer->GetRealTime(); @@ -86,34 +79,36 @@ Renderer::ResetHard(bool poweron) stat_input_count = 0; last_input_time = now; ma_input_span.Clear(); - stat_enqueue_count = 0; - stat_enqueue_full = 0; stat_render_count = 0; stat_noupdate_count = 0; stat_nobitmap_count = 0; stat_output_count = 0; last_output_time = now; ma_output_span.Clear(); - last_mod_lines = 0; - total_invalidate2 = 0; +#if defined(PERFREND) + last_report_rtime = syncer->GetRealTime(); +#endif } // 電源オフ void -Renderer::PowerOff() +VideoRenderer::PowerOff() { if (enable) { +#if defined(PERFREND) + sw_notify.Restart(); +#endif // レンダリングスレッドに指示 - std::unique_lock lock(mtx); + std::lock_guard lock(mtx); request |= REQ_SCREENOFF; cv.notify_one(); } } -// 画面合成処理 (VM スレッドで CRTC/CRTC2 から呼ばれる)。 +// 画面合成処理 (VM スレッドで呼ばれる)。 // 今は一画面単位で合成を行なっている。ラスター云々は未対応。 void -Renderer::NotifyRender() +VideoRenderer::NotifyRender() { // CLI 版なら何もしない。 if (enable == false) { @@ -126,56 +121,21 @@ Renderer::NotifyRender() ma_input_span.EnqueueForce(now - last_input_time); last_input_time = now; - // 更新されていればレンダラに引き渡す #if defined(PERFREND) - Stopwatch sw; - sw.Start(); + sw_notify.Restart(); #endif - ModifyInfo mod; - bool dirty = GetModifyMD(&mod); - if (__predict_false(invalidate2 == true)) { - mod.invalidate2 = true; - } - if (dirty || mod.invalidate2) { - if (modq.Enqueue(mod)) { -#if defined(PERFREND) - // 更新チェックしてキューに入れるまでにかかる VM スレッド側時間 - sw.Stop(); - check_time += sw.Elapsed(); - check_count++; -#endif - // 更新情報を引き取ったのでクリアする - ClearDirtyMD(); - invalidate2 = false; - - stat_enqueue_count++; - -#if defined(PERFREND) - // スレッドをまたぐのにかかる時間 - sw_notify.Restart(); -#endif - // 描画指示。 - std::unique_lock lock(mtx); - request |= REQ_RENDER; - cv.notify_one(); - } else { - stat_enqueue_full++; - } + // レンダラスレッドに描画指示を出すだけ。 + { + std::lock_guard lock(mtx); + request |= REQ_RENDER; + cv.notify_one(); } } -// 全画面更新の指示 (パレット変更やスクロールなど VRAM の変更を伴わないもの)。 -// ここでは無効化だけ受け付けておいて、実際の処理はレンダリングの際に行う。 -void -Renderer::Invalidate2() -{ - invalidate2 = true; -} - // レンダリングスレッド void -Renderer::ThreadRun() +VideoRenderer::ThreadRun() { if (enable) { SetThreadAffinityHint(AffinityClass::Heavy); @@ -208,6 +168,9 @@ Renderer::ThreadRun() // req に基づいて処理。 int64 t = DoRender(req); +#if defined(PERFREND) + PrintPerf(); +#endif if (t == 0 || (req & REQ_POST)) { // UI に即通知。 @@ -217,7 +180,7 @@ Renderer::ThreadRun() ma_output_span.EnqueueForce(now - last_output_time); last_output_time = now; - UIMessage::Post(UIMessage::RENDER); + uimessage->Post(UIMessage::RENDER); delay = 0; } else if (t > 0) { // 指定時間後に UI に通知。 @@ -230,7 +193,7 @@ Renderer::ThreadRun() // UI への描画通知を遅延させる時間を返す (即通知する場合は 0)。 // UI に通知しない場合は -1 を返す。 int64 -Renderer::DoRender(uint32 req) +VideoRenderer::DoRender(uint32 req) { // 1. REND_NORMAL なら電源オン時の通常描画指示、 // REND_POWEROFF なら電源オン→オフ遷移時の描画指示。 @@ -248,28 +211,18 @@ Renderer::DoRender(uint32 req) #if defined(PERFREND) sw_notify.Stop(); - notify_time += sw_notify.Elapsed(); + notify_nsec += sw_notify.Elapsed_nsec(); notify_count++; -#endif - - if (modq.Dequeue(&modify)) { - // 更新ライン数を数える (統計用) - last_mod_lines = modify.GetDirtyCount(); - if (__predict_false(modify.invalidate2)) { - total_invalidate2++; - } - // レンダラを駆動する。 -#if defined(PERFREND) - sw.Restart(); + sw.Restart(); #endif - updated = RenderMD(); + // レンダラを駆動する。 + updated = RenderMD(); #if defined(PERFREND) - sw.Stop(); - render_time += sw.Elapsed(); + sw.Stop(); + render_nsec += sw.Elapsed_nsec(); #endif - stat_render_count++; - } + stat_render_count++; } if ((req & REQ_SCREENOFF)) { // 電源オン→オフ時の描画 @@ -281,7 +234,10 @@ Renderer::DoRender(uint32 req) // 表示ビットマップのリサイズ要求 bitmap24_valid = false; - bitmap24.reset(new BitmapRGB(new_viewwidth, new_viewheight)); + bitmap24.reset(); + try { + bitmap24.reset(new BitmapRGB(new_viewwidth, new_viewheight)); + } catch (...) { } if ((bool)bitmap24 == false) { putlog(0, "Could not allocate BitmapRGB(%u, %u)", new_viewwidth, new_viewheight); @@ -320,7 +276,7 @@ Renderer::DoRender(uint32 req) } #if defined(PERFREND) sw.Stop(); - stretch_time += sw.Elapsed(); + stretch_nsec += sw.Elapsed_nsec(); #endif // wxWidgets 用の RGBX→RGB 変換。 @@ -331,19 +287,8 @@ Renderer::DoRender(uint32 req) bitmap24_valid = true; #if defined(PERFREND) sw.Stop(); - convert_time += sw.Elapsed(); -#endif - -#if defined(PERFREND) - if (++convert_count % 100 == 0) { - printf("Check %4.2f, Notify %u, Render %u, Stretch %u, Convert %u" - " [usec]\n", - (double)check_time / check_count / 1000, - (uint)(notify_time / notify_count / 1000U), - (uint)(render_time / stat_render_count / 1000U), - (uint)(stretch_time / convert_count / 1000U), - (uint)(convert_time / convert_count / 1000U)); - } + convert_nsec += sw.Elapsed_nsec(); + convert_count++; #endif if ((req & (REQ_SCREENOFF | REQ_RESIZE))) { @@ -360,42 +305,81 @@ Renderer::DoRender(uint32 req) return 0; } +#if defined(PERFREND) +void +VideoRenderer::PrintPerf() +{ + uint64 now = syncer->GetRealTime(); + if (now - last_report_rtime < 5_sec) { + return; + } + + double rate = 0; + uint nt_usec = 0; + uint rt_usec = 0; + uint st_usec = 0; + uint ct_usec = 0; + if (notify_count != 0) { + rate = (double)notify_count * 1_sec / (now - last_report_rtime); + nt_usec = nsec_to_usec(notify_nsec) / notify_count; + rt_usec = nsec_to_usec(render_nsec) / notify_count; + } + if (convert_count != 0) { + st_usec = nsec_to_usec(stretch_nsec) / convert_count; + ct_usec = nsec_to_usec(convert_nsec) / convert_count; + } + + printf( + "%5.1f calls/s: Notify %u us, Render %u us, Stretch %u us, Convert %u us\n", + rate, nt_usec, rt_usec, st_usec, ct_usec); + + notify_nsec = 0; + render_nsec = 0; + stretch_nsec = 0; + convert_nsec = 0; + notify_count = 0; + convert_count = 0; + last_report_rtime = now; +} +#endif // PERFREND + // スレッドの終了を指示 void -Renderer::Terminate() +VideoRenderer::Terminate() { - std::unique_lock lock(mtx); + std::lock_guard lock(mtx); request |= REQ_TERMINATE; cv.notify_one(); } // リサイズ指示 void -Renderer::ResizeView(uint width_, uint height_) +VideoRenderer::ResizeView(uint width_, uint height_) { assert(enable); new_viewwidth = width_; new_viewheight = height_; - std::unique_lock lock(mtx); + std::lock_guard lock(mtx); request |= REQ_RESIZE; cv.notify_one(); } -// 描画更新(通知)間隔を設定 +// 描画更新(通知)間隔を設定。[msec] で指定。 void -Renderer::SetRefreshInterval(uint64 t) +VideoRenderer::SetRefreshInterval(uint64 msec) { - refresh_interval = t; + refresh_interval = msec_to_tsec(msec); } // モニタ更新 void -Renderer::MonitorUpdate(Monitor *, TextScreen& screen) +VideoRenderer::MonitorScreen(Monitor *, TextScreen& screen) { int x; int y; + uint64 ma; screen.Clear(); @@ -406,26 +390,13 @@ Renderer::MonitorUpdate(Monitor *, TextS screen.Puts(0, y, "Input Count"); screen.Print(x, y++, "%26s", format_number(stat_input_count).c_str()); - uint count = ma_input_span.Length(); - uint64 ma = 0; - for (uint i = 0; i < count; i++) { - ma += ma_input_span.Peek(i); - } - if (count != 0) { - ma /= count; - } screen.Puts(0, y, "Input Frequency(VT)"); + ma = ma_input_span.GetMovingAverage(); screen.Print(x + 18, y, "%5.1f Hz", ma == 0 ? 0.0 : (double)1000'000'000 / ma); y++; y++; - screen.Puts(0, y, "Enqueue Count"); - screen.Print(x, y++, "%26s", format_number(stat_enqueue_count).c_str()); - screen.Puts(0, y, "Enqueue Full"); - screen.Print(x, y++, "%26s", format_number(stat_enqueue_full).c_str()); - - y++; screen.Puts(0, y, "RenderMD Count"); screen.Print(x, y++, "%26s", format_number(stat_render_count).c_str()); screen.Puts(0, y, "NoUpdate Count"); @@ -436,24 +407,11 @@ Renderer::MonitorUpdate(Monitor *, TextS y++; screen.Puts(0, y, "Output Count"); screen.Print(x, y++, "%26s", format_number(stat_output_count).c_str()); - count = ma_output_span.Length(); - ma = 0; - for (uint i = 0; i < count; i++) { - ma += ma_output_span.Peek(i); - } - if (count != 0) { - ma /= count; - } screen.Puts(0, y, "Output Frequency(RT)"); + ma = ma_output_span.GetMovingAverage(); screen.Print(x + 18, y, "%5.1f Hz", ma == 0 ? 0.0 : (double)1000'000'000 / ma); y++; - - y++; - screen.Puts(0, y, "Last Mod Lines"); - screen.Print(x, y++, "%26u", last_mod_lines); - screen.Puts(0, y, "Invalidate2"); - screen.Print(x, y++, "%26s", format_number(total_invalidate2).c_str()); } @@ -462,10 +420,8 @@ Renderer::MonitorUpdate(Monitor *, TextS // // コンストラクタ -X68030Renderer::X68030Renderer() +X68030VideoRenderer::X68030VideoRenderer() { - InitMonitor(); - // XXX とりあえず width = 768; height = 512; @@ -474,46 +430,28 @@ X68030Renderer::X68030Renderer() } // デストラクタ -X68030Renderer::~X68030Renderer() +X68030VideoRenderer::~X68030VideoRenderer() { } // 初期化 bool -X68030Renderer::Init() +X68030VideoRenderer::Init() { if (inherited::Init() == false) { return false; } - planevram = GetPlaneVRAMDevice(); videoctlr = GetVideoCtlrDevice(); return true; } -// 更新チェック -bool -X68030Renderer::GetModifyMD(ModifyInfo *mod) const -{ - // 今のところテキスト VRAM しかない - // TODO: videoctlr に移動 - return planevram->GetModify(mod); -} - -// 更新情報クリア -void -X68030Renderer::ClearDirtyMD() -{ - // 今のところテキスト VRAM しかない - planevram->ClearDirty(); -} - // 画面合成 bool -X68030Renderer::RenderMD() +X68030VideoRenderer::RenderMD() { - bool updated = videoctlr->Render(bitmap, modify); + bool updated = videoctlr->Render(bitmap); return updated; } @@ -523,10 +461,8 @@ X68030Renderer::RenderMD() // // コンストラクタ -LunaRenderer::LunaRenderer() +LunaVideoRenderer::LunaVideoRenderer() { - InitMonitor(); - width = 1280; height = 1024; @@ -534,13 +470,13 @@ LunaRenderer::LunaRenderer() } // デストラクタ -LunaRenderer::~LunaRenderer() +LunaVideoRenderer::~LunaVideoRenderer() { } // 初期化 bool -LunaRenderer::Init() +LunaVideoRenderer::Init() { if (inherited::Init() == false) { return false; @@ -551,62 +487,51 @@ LunaRenderer::Init() return true; } -// 更新チェック -bool -LunaRenderer::GetModifyMD(ModifyInfo *mod) const -{ - return planevram->GetModify(mod); -} - -// 更新情報クリア -void -LunaRenderer::ClearDirtyMD() -{ - planevram->ClearDirty(); -} - // 画面合成 bool -LunaRenderer::RenderMD() +LunaVideoRenderer::RenderMD() { - bool updated = planevram->Render(bitmap, modify); + bool updated = planevram->Render(bitmap); return updated; } // -// 何もしないレンダラ +// コンソールレンダラ // // コンストラクタ -NopRenderer::NopRenderer() +ConsoleRenderer::ConsoleRenderer() { - // XXX サポートしてない - width = 640; - height = 480; + // 視認性のためフチを追加。 + width = (80 * 8) + Padding * 2; + height = (24 * 16) + Padding * 2; + + bitmap.Create(width, height); } // デストラクタ -NopRenderer::~NopRenderer() +ConsoleRenderer::~ConsoleRenderer() { } -// 更新チェック +// 初期化 bool -NopRenderer::GetModifyMD(ModifyInfo *) const +ConsoleRenderer::Init() { - return false; -} + if (inherited::Init() == false) { + return false; + } -// 更新情報クリア -void -NopRenderer::ClearDirtyMD() -{ + console = GetConsoleDevice(); + + return true; } // 画面合成 bool -NopRenderer::RenderMD() +ConsoleRenderer::RenderMD() { - return false; + bool updated = console->Render(bitmap); + return updated; }