From cf49b46d64086afe28269631d247916e6efd1ca9 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sun, 8 May 2022 22:43:30 +0200 Subject: [PATCH] Show logs in the debug window Some part of UI code is written by @andrew-app --- CHANGELOG.md | 1 + apps/openmw/mwgui/debugwindow.cpp | 112 ++++++++++++++++-- apps/openmw/mwgui/debugwindow.hpp | 9 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 6 +- components/debug/debugging.cpp | 5 + components/debug/debugging.hpp | 2 + components/debug/debuglog.hpp | 4 +- .../reference/modding/settings/general.rst | 14 +++ files/mygui/openmw_layers.xml | 4 +- files/settings-default.cfg | 3 + 10 files changed, 143 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c64b831bc..a427f1e7c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -143,6 +143,7 @@ Feature #6128: Soft Particles Feature #6161: Refactor Sky to use shaders and GLES/GL3 friendly Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly + Feature #6171: In-game log viewer Feature #6189: Navigation mesh disk cache Feature #6199: Support FBO Rendering Feature #6248: Embedded error marker mesh diff --git a/apps/openmw/mwgui/debugwindow.cpp b/apps/openmw/mwgui/debugwindow.cpp index 728e16b21a..4c71874c1f 100644 --- a/apps/openmw/mwgui/debugwindow.cpp +++ b/apps/openmw/mwgui/debugwindow.cpp @@ -5,6 +5,8 @@ #include #include +#include +#include #ifndef BT_NO_PROFILE @@ -84,28 +86,105 @@ namespace MWGui // Ideas for other tabs: // - Texture / compositor texture viewer - // - Log viewer // - Material editor // - Shader editor + initLogView(); + +#ifndef BT_NO_PROFILE MyGUI::TabItem* item = mTabControl->addItem("Physics Profiler"); mBulletProfilerEdit = item->createWidgetReal ("LogEdit", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Stretch); +#else + mBulletProfilerEdit = nullptr; +#endif } - void DebugWindow::onFrame(float dt) + void DebugWindow::initLogView() { -#ifndef BT_NO_PROFILE - if (!isVisible()) - return; + MyGUI::TabItem* itemLV = mTabControl->addItem("Log Viewer"); + mLogView = itemLV->createWidgetReal + ("LogEdit", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Stretch); + mLogView->setEditReadOnly(true); - static float timer = 0; - timer -= dt; + mLogCircularBuffer.resize(std::max(0, Settings::Manager::getInt64("log buffer size", "General"))); + Debug::setLogListener([this](Debug::Level level, std::string_view prefix, std::string_view msg) + { + if (mLogCircularBuffer.empty()) + return; // Log viewer is disabled. + std::string_view color; + switch (level) + { + case Debug::Error: color = "#FF0000"; break; + case Debug::Warning: color = "#FFFF00"; break; + case Debug::Info: color = "#FFFFFF"; break; + case Debug::Verbose: + case Debug::Debug: color = "#666666"; break; + default: color = "#FFFFFF"; + } + bool bufferOverflow = false; + const int64_t bufSize = mLogCircularBuffer.size(); + auto addChar = [&](char c) + { + mLogCircularBuffer[mLogEndIndex++] = c; + if (mLogEndIndex == bufSize) + mLogEndIndex = 0; + bufferOverflow = bufferOverflow || mLogEndIndex == mLogStartIndex; + }; + auto addShieldedStr = [&](std::string_view s) + { + for (char c : s) + { + addChar(c); + if (c == '#') + addChar(c); + } + }; + for (char c : color) + addChar(c); + addShieldedStr(prefix); + addShieldedStr(msg); + if (bufferOverflow) + mLogStartIndex = (mLogEndIndex + 1) % bufSize; + }); + } - if (timer > 0) + void DebugWindow::updateLogView() + { + if (!mLogView || mLogCircularBuffer.empty() || mLogStartIndex == mLogEndIndex) return; - timer = 1; + if (mLogView->isTextSelection()) + return; // Don't change text while player is trying to copy something + std::string addition; + const int64_t bufSize = mLogCircularBuffer.size(); + { + std::unique_lock lock = Log::lock(); + if (mLogStartIndex < mLogEndIndex) + addition = std::string(mLogCircularBuffer.data() + mLogStartIndex, mLogEndIndex - mLogStartIndex); + else + { + addition = std::string(mLogCircularBuffer.data() + mLogStartIndex, bufSize - mLogStartIndex); + addition.append(mLogCircularBuffer.data(), mLogEndIndex); + } + mLogStartIndex = mLogEndIndex; + } + + size_t scrollPos = mLogView->getVScrollPosition(); + bool scrolledToTheEnd = scrollPos+1 >= mLogView->getVScrollRange(); + int64_t newSizeEstimation = mLogView->getTextLength() + addition.size(); + if (newSizeEstimation > bufSize) + mLogView->eraseText(0, newSizeEstimation - bufSize); + mLogView->addText(addition); + if (scrolledToTheEnd && mLogView->getVScrollRange() > 0) + mLogView->setVScrollPosition(mLogView->getVScrollRange() - 1); + else + mLogView->setVScrollPosition(scrollPos); + } + + void DebugWindow::updateBulletProfile() + { +#ifndef BT_NO_PROFILE std::stringstream stream; bulletDumpAll(stream); @@ -118,4 +197,17 @@ namespace MWGui #endif } -} + void DebugWindow::onFrame(float dt) + { + static float timer = 0; + timer -= dt; + if (timer > 0 || !isVisible()) + return; + timer = 0.25; + + if (mTabControl->getIndexSelected() == 0) + updateLogView(); + else + updateBulletProfile(); + } +} \ No newline at end of file diff --git a/apps/openmw/mwgui/debugwindow.hpp b/apps/openmw/mwgui/debugwindow.hpp index 33647c0789..352a58c50c 100644 --- a/apps/openmw/mwgui/debugwindow.hpp +++ b/apps/openmw/mwgui/debugwindow.hpp @@ -14,8 +14,17 @@ namespace MWGui void onFrame(float dt) override; private: + void initLogView(); + void updateLogView(); + void updateBulletProfile(); + MyGUI::TabControl* mTabControl; + MyGUI::EditBox* mLogView; + std::vector mLogCircularBuffer; + int64_t mLogStartIndex = 0; + int64_t mLogEndIndex = 0; + MyGUI::EditBox* mBulletProfilerEdit; }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3b79b21a7c..95a437ceaa 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -884,6 +884,8 @@ namespace MWGui if (mLocalMapRender) mLocalMapRender->cleanupCameras(); + mDebugWindow->onFrame(frameDuration); + if (!gameRunning) return; @@ -903,8 +905,6 @@ namespace MWGui mHud->onFrame(frameDuration); - mDebugWindow->onFrame(frameDuration); - mPostProcessorHud->onFrame(frameDuration); if (mCharGen) @@ -2061,9 +2061,7 @@ namespace MWGui void WindowManager::toggleDebugWindow() { -#ifndef BT_NO_PROFILE mDebugWindow->setVisible(!mDebugWindow->isVisible()); -#endif } void WindowManager::togglePostProcessorHud() diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index e1bea70954..62d1bc716d 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -61,6 +61,9 @@ namespace Debug } #endif + static LogListener logListener; + void setLogListener(LogListener listener) { logListener = std::move(listener); } + std::streamsize DebugOutputBase::write(const char *str, std::streamsize size) { if (size <= 0) @@ -94,6 +97,8 @@ namespace Debug lineSize++; writeImpl(prefix, prefixSize, level); writeImpl(msg.data(), lineSize, level); + if (logListener) + logListener(level, std::string_view(prefix, prefixSize), std::string_view(msg.data(), lineSize)); msg = msg.substr(lineSize); } diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp index 3e6739c1e1..63c44ae8b4 100644 --- a/components/debug/debugging.hpp +++ b/components/debug/debugging.hpp @@ -133,6 +133,8 @@ namespace Debug }; #endif + using LogListener = std::function; + void setLogListener(LogListener); } diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index 96b9d798e9..bcb282ab3a 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -35,7 +35,7 @@ public: return; // Locks a global lock while the object is alive - mLock = std::unique_lock(sLock); + mLock = lock(); // If the app has no logging system enabled, log level is not specified. // Show all messages without marker - we just use the plain cout in this case. @@ -61,6 +61,8 @@ public: std::cout << std::endl; } + static std::unique_lock lock() { return std::unique_lock(sLock); } + private: const bool mShouldLog; }; diff --git a/docs/source/reference/modding/settings/general.rst b/docs/source/reference/modding/settings/general.rst index ae2448c38b..39045e7733 100644 --- a/docs/source/reference/modding/settings/general.rst +++ b/docs/source/reference/modding/settings/general.rst @@ -86,3 +86,17 @@ since if the country code isn't specified the generic language-code only locale refer to any of the country-specific variants. This setting can only be configured by editing the settings configuration file. + +log buffer size +--------------- + +:Type: integer +:Range: >= 0 +:Default: 65536 + +Buffer size for the in-game log viewer (press F10 to toggle the log viewer). +When the log doesn't fit into the buffer, only the end of the log is visible in the log viewer. +Zero disables the log viewer. + +This setting can only be configured by editing the settings configuration file. + diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 0b70dbb000..750c4cacd3 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -8,14 +8,14 @@ - - + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ed392d44e4..a952a9da19 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -400,6 +400,9 @@ notify on saved screenshot = false # For example "de,en" means German as the first prority and English as a fallback. preferred locales = en +# Buffer size for the in-game log viewer (press F10 to toggle). Zero disables the log viewer. +log buffer size = 65536 + [Shaders] # Force rendering with shaders. By default, only bump-mapped objects will use shaders.