From eb7d42dfe88efaa9939b155ca5016ec40c992268 Mon Sep 17 00:00:00 2001 From: Silent Date: Fri, 30 Aug 2019 23:55:40 +0200 Subject: [PATCH 1/6] LogWidget: Replace QTextEdit with QPlainTextEdit for better performance QTextEdit is heavy, similar in functionality to WordPad, while QPlainTextEdit is lightweight like Notepad. Qt documentation recommends using QPlainTextEdit for log viewers, and it also allows to set automatic cutoff of oldest messages beyond a fixed point, which we now set to MAX_LOG_LINES (5000) --- Source/Core/DolphinQt/Config/LogWidget.cpp | 29 +++++++++++++--------- Source/Core/DolphinQt/Config/LogWidget.h | 7 ++---- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Source/Core/DolphinQt/Config/LogWidget.cpp b/Source/Core/DolphinQt/Config/LogWidget.cpp index 2b5f4dfdc4..e3567353b0 100644 --- a/Source/Core/DolphinQt/Config/LogWidget.cpp +++ b/Source/Core/DolphinQt/Config/LogWidget.cpp @@ -8,12 +8,13 @@ #include #include #include -#include +#include +#include #include #include -#include #include -#include + +#include "Core/ConfigManager.h" #include "Common/FileUtil.h" #include "Common/StringUtil.h" @@ -25,8 +26,10 @@ // Delay in ms between calls of UpdateLog() constexpr int UPDATE_LOG_DELAY = 100; +// Maximum number of lines to show in log viewer +constexpr int MAX_LOG_LINES = 5000; // Maximum lines to process at a time -constexpr int MAX_LOG_LINES = 200; +constexpr size_t MAX_LOG_LINES_TO_UPDATE = 200; // Timestamp length constexpr int TIMESTAMP_LENGTH = 10; @@ -75,9 +78,9 @@ void LogWidget::UpdateLog() int old_horizontal = hscroll->value(); int old_vertical = vscroll->value(); - for (int i = 0; !m_log_queue.empty() && i < MAX_LOG_LINES; i++) + for (size_t i = 0; !m_log_queue.empty() && i < MAX_LOG_LINES_TO_UPDATE; i++) { - m_log_text->append(m_log_queue.front()); + m_log_text->appendHtml(m_log_queue.front()); m_log_queue.pop(); } @@ -115,8 +118,7 @@ void LogWidget::UpdateFont() void LogWidget::CreateWidgets() { // Log - m_tab_log = new QWidget; - m_log_text = new QTextEdit; + m_log_text = new QPlainTextEdit; m_log_wrap = new QCheckBox(tr("Word Wrap")); m_log_font = new QComboBox; m_log_clear = new QPushButton(tr("Clear")); @@ -124,7 +126,6 @@ void LogWidget::CreateWidgets() m_log_font->addItems({tr("Default Font"), tr("Monospaced Font"), tr("Selected Font")}); auto* log_layout = new QGridLayout; - m_tab_log->setLayout(log_layout); log_layout->addWidget(m_log_wrap, 0, 0); log_layout->addWidget(m_log_font, 0, 1); log_layout->addWidget(m_log_clear, 0, 2); @@ -136,6 +137,8 @@ void LogWidget::CreateWidgets() setWidget(widget); m_log_text->setReadOnly(true); + m_log_text->setUndoRedoEnabled(false); + m_log_text->setMaximumBlockCount(MAX_LOG_LINES); QPalette palette = m_log_text->palette(); palette.setColor(QPalette::Base, Qt::black); @@ -145,7 +148,7 @@ void LogWidget::CreateWidgets() void LogWidget::ConnectWidgets() { - connect(m_log_clear, &QPushButton::clicked, m_log_text, &QTextEdit::clear); + connect(m_log_clear, &QPushButton::clicked, m_log_text, &QPlainTextEdit::clear); connect(m_log_wrap, &QCheckBox::toggled, this, &LogWidget::SaveSettings); connect(m_log_font, static_cast(&QComboBox::currentIndexChanged), this, &LogWidget::SaveSettings); @@ -163,7 +166,8 @@ void LogWidget::LoadSettings() // Log - Wrap Lines m_log_wrap->setChecked(settings.value(QStringLiteral("logging/wraplines")).toBool()); - m_log_text->setLineWrapMode(m_log_wrap->isChecked() ? QTextEdit::WidgetWidth : QTextEdit::NoWrap); + m_log_text->setLineWrapMode(m_log_wrap->isChecked() ? QPlainTextEdit::WidgetWidth : + QPlainTextEdit::NoWrap); // Log - Font Selection // Currently "Debugger Font" is not supported as there is no Qt Debugger, defaulting to Monospace @@ -180,7 +184,8 @@ void LogWidget::SaveSettings() // Log - Wrap Lines settings.setValue(QStringLiteral("logging/wraplines"), m_log_wrap->isChecked()); - m_log_text->setLineWrapMode(m_log_wrap->isChecked() ? QTextEdit::WidgetWidth : QTextEdit::NoWrap); + m_log_text->setLineWrapMode(m_log_wrap->isChecked() ? QPlainTextEdit::WidgetWidth : + QPlainTextEdit::NoWrap); // Log - Font Selection settings.setValue(QStringLiteral("logging/font"), m_log_font->currentIndex()); diff --git a/Source/Core/DolphinQt/Config/LogWidget.h b/Source/Core/DolphinQt/Config/LogWidget.h index d116d8d02b..d8956cca1d 100644 --- a/Source/Core/DolphinQt/Config/LogWidget.h +++ b/Source/Core/DolphinQt/Config/LogWidget.h @@ -14,9 +14,8 @@ class QCheckBox; class QCloseEvent; class QComboBox; +class QPlainTextEdit; class QPushButton; -class QVBoxLayout; -class QTextEdit; class QTimer; class LogWidget final : public QDockWidget, LogListener @@ -43,9 +42,7 @@ private: QCheckBox* m_log_wrap; QComboBox* m_log_font; QPushButton* m_log_clear; - QVBoxLayout* m_main_layout; - QTextEdit* m_log_text; - QWidget* m_tab_log; + QPlainTextEdit* m_log_text; QTimer* m_timer; From 7cec8601f191a68745e8b58256328e0a59655420 Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 31 Aug 2019 00:05:01 +0200 Subject: [PATCH 2/6] LogWidget: Remove scrollbar tampering code (doesn't seem to do anything), clear text queue on clearing log --- Source/Core/DolphinQt/Config/LogWidget.cpp | 30 ++++------------------ 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/Source/Core/DolphinQt/Config/LogWidget.cpp b/Source/Core/DolphinQt/Config/LogWidget.cpp index e3567353b0..e274183606 100644 --- a/Source/Core/DolphinQt/Config/LogWidget.cpp +++ b/Source/Core/DolphinQt/Config/LogWidget.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include "Core/ConfigManager.h" @@ -68,32 +67,11 @@ void LogWidget::UpdateLog() if (m_log_queue.empty()) return; - auto* vscroll = m_log_text->verticalScrollBar(); - auto* hscroll = m_log_text->horizontalScrollBar(); - - // If the vertical scrollbar is within 50 units of the maximum value, count it as being at the - // bottom - bool vscroll_bottom = vscroll->maximum() - vscroll->value() < 50; - - int old_horizontal = hscroll->value(); - int old_vertical = vscroll->value(); - for (size_t i = 0; !m_log_queue.empty() && i < MAX_LOG_LINES_TO_UPDATE; i++) { m_log_text->appendHtml(m_log_queue.front()); m_log_queue.pop(); } - - if (hscroll->value() != old_horizontal) - hscroll->setValue(old_horizontal); - - if (vscroll->value() != old_vertical) - { - if (vscroll_bottom) - vscroll->setValue(vscroll->maximum()); - else - vscroll->setValue(old_vertical); - } } void LogWidget::UpdateFont() @@ -148,13 +126,15 @@ void LogWidget::CreateWidgets() void LogWidget::ConnectWidgets() { - connect(m_log_clear, &QPushButton::clicked, m_log_text, &QPlainTextEdit::clear); + connect(m_log_clear, &QPushButton::clicked, [this] { + m_log_text->clear(); + m_log_queue = {}; + }); connect(m_log_wrap, &QCheckBox::toggled, this, &LogWidget::SaveSettings); connect(m_log_font, static_cast(&QComboBox::currentIndexChanged), this, &LogWidget::SaveSettings); connect(this, &QDockWidget::topLevelChanged, this, &LogWidget::SaveSettings); - connect(&Settings::Instance(), &Settings::LogVisibilityChanged, this, - [this](bool visible) { setHidden(!visible); }); + connect(&Settings::Instance(), &Settings::LogVisibilityChanged, this, &LogWidget::setVisible); } void LogWidget::LoadSettings() From e746d95d01bc7e36de82f66be9d082ceba8be446 Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 31 Aug 2019 00:09:47 +0200 Subject: [PATCH 3/6] LogWidget: Stop update timer when log window is invisible so it doesn't continuously update in the background --- Source/Core/DolphinQt/Config/LogWidget.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Core/DolphinQt/Config/LogWidget.cpp b/Source/Core/DolphinQt/Config/LogWidget.cpp index e274183606..63b98a5ef3 100644 --- a/Source/Core/DolphinQt/Config/LogWidget.cpp +++ b/Source/Core/DolphinQt/Config/LogWidget.cpp @@ -46,7 +46,12 @@ LogWidget::LogWidget(QWidget* parent) : QDockWidget(parent), m_timer(new QTimer( ConnectWidgets(); connect(m_timer, &QTimer::timeout, this, &LogWidget::UpdateLog); - m_timer->start(UPDATE_LOG_DELAY); + connect(this, &QDockWidget::visibilityChanged, [this](bool visible) { + if (visible) + m_timer->start(UPDATE_LOG_DELAY); + else + m_timer->stop(); + }); connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &LogWidget::UpdateFont); From b088fc37d5de88b122c3e440319b421de9bee209 Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 31 Aug 2019 00:15:03 +0200 Subject: [PATCH 4/6] LogWidget: Do not use QueueOnObject to construct log queue, instead store a std::string constructed from string_view and convert to QString just before appending --- Source/Core/DolphinQt/Config/LogWidget.cpp | 104 +++++++++++---------- Source/Core/DolphinQt/Config/LogWidget.h | 5 +- 2 files changed, 60 insertions(+), 49 deletions(-) diff --git a/Source/Core/DolphinQt/Config/LogWidget.cpp b/Source/Core/DolphinQt/Config/LogWidget.cpp index 63b98a5ef3..743d2b187e 100644 --- a/Source/Core/DolphinQt/Config/LogWidget.cpp +++ b/Source/Core/DolphinQt/Config/LogWidget.cpp @@ -15,12 +15,6 @@ #include "Core/ConfigManager.h" -#include "Common/FileUtil.h" -#include "Common/StringUtil.h" - -#include "Core/ConfigManager.h" - -#include "DolphinQt/QtUtils/QueueOnObject.h" #include "DolphinQt/Settings.h" // Delay in ms between calls of UpdateLog() @@ -30,7 +24,13 @@ constexpr int MAX_LOG_LINES = 5000; // Maximum lines to process at a time constexpr size_t MAX_LOG_LINES_TO_UPDATE = 200; // Timestamp length -constexpr int TIMESTAMP_LENGTH = 10; +constexpr size_t TIMESTAMP_LENGTH = 10; + +// A helper function to construct QString from std::string_view in one line +static QString QStringFromStringView(std::string_view str) +{ + return QString::fromUtf8(str.data(), static_cast(str.size())); +} LogWidget::LogWidget(QWidget* parent) : QDockWidget(parent), m_timer(new QTimer(this)) { @@ -67,15 +67,50 @@ LogWidget::~LogWidget() void LogWidget::UpdateLog() { - std::lock_guard lock(m_log_mutex); - - if (m_log_queue.empty()) - return; - - for (size_t i = 0; !m_log_queue.empty() && i < MAX_LOG_LINES_TO_UPDATE; i++) + std::vector elements_to_push; { - m_log_text->appendHtml(m_log_queue.front()); - m_log_queue.pop(); + std::lock_guard lock(m_log_mutex); + if (m_log_queue.empty()) + return; + + elements_to_push.reserve(std::min(MAX_LOG_LINES_TO_UPDATE, m_log_queue.size())); + + for (size_t i = 0; !m_log_queue.empty() && i < MAX_LOG_LINES_TO_UPDATE; i++) + { + elements_to_push.push_back(std::move(m_log_queue.front())); + m_log_queue.pop(); + } + } + + for (auto& line : elements_to_push) + { + const char* color = "white"; + switch (std::get(line)) + { + case LogTypes::LOG_LEVELS::LERROR: + color = "red"; + break; + case LogTypes::LOG_LEVELS::LWARNING: + color = "yellow"; + break; + case LogTypes::LOG_LEVELS::LNOTICE: + color = "lime"; + break; + case LogTypes::LOG_LEVELS::LINFO: + color = "cyan"; + break; + case LogTypes::LOG_LEVELS::LDEBUG: + color = "lightgrey"; + break; + } + + const std::string_view str_view(std::get(line)); + + m_log_text->appendHtml( + QStringLiteral("%1 %3") + .arg(QStringFromStringView(str_view.substr(0, TIMESTAMP_LENGTH)), + QString::fromUtf8(color), + QStringFromStringView(str_view.substr(TIMESTAMP_LENGTH)).toHtmlEscaped())); } } @@ -179,40 +214,13 @@ void LogWidget::SaveSettings() void LogWidget::Log(LogTypes::LOG_LEVELS level, const char* text) { - // The text has to be copied here as it will be deallocated after this method has returned - std::string str(text); + size_t text_length = strlen(text); + while (text_length > 0 && text[text_length - 1] == '\n') + text_length--; - QueueOnObject(this, [this, level, str]() mutable { - std::lock_guard lock(m_log_mutex); - - const char* color = "white"; - - switch (level) - { - case LogTypes::LOG_LEVELS::LERROR: - color = "red"; - break; - case LogTypes::LOG_LEVELS::LWARNING: - color = "yellow"; - break; - case LogTypes::LOG_LEVELS::LNOTICE: - color = "lime"; - break; - case LogTypes::LOG_LEVELS::LINFO: - color = "cyan"; - break; - case LogTypes::LOG_LEVELS::LDEBUG: - color = "lightgrey"; - break; - } - - StringPopBackIf(&str, '\n'); - m_log_queue.push( - QStringLiteral("%1 %3") - .arg(QString::fromStdString(str.substr(0, TIMESTAMP_LENGTH)), - QString::fromStdString(color), - QString::fromStdString(str.substr(TIMESTAMP_LENGTH)).toHtmlEscaped())); - }); + std::lock_guard lock(m_log_mutex); + m_log_queue.emplace(std::piecewise_construct, std::forward_as_tuple(text, text_length), + std::forward_as_tuple(level)); } void LogWidget::closeEvent(QCloseEvent*) diff --git a/Source/Core/DolphinQt/Config/LogWidget.h b/Source/Core/DolphinQt/Config/LogWidget.h index d8956cca1d..9c7694b273 100644 --- a/Source/Core/DolphinQt/Config/LogWidget.h +++ b/Source/Core/DolphinQt/Config/LogWidget.h @@ -8,6 +8,7 @@ #include #include +#include #include "Common/Logging/LogManager.h" @@ -46,6 +47,8 @@ private: QTimer* m_timer; + using LogEntry = std::pair; + std::mutex m_log_mutex; - std::queue m_log_queue; + std::queue m_log_queue; }; From b3969e91d901ae6c30c9fc12c7e457658817903c Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 31 Aug 2019 20:16:16 +0200 Subject: [PATCH 5/6] FixedSizeQueue: Bugfixes and improvements - Fixed a bug where pushing items over queue's size left it in a corrupted state - For non-trivial types, have clear() and pop() run destructors - Added emplace(args...) - Added empty() FixedSizeQueue has semantics of a circular buffer, so pushing items continuously is expected to keep overwriting oldest elements gracefully. Tests have been updated to verify correctness of a previously bugged behaviour and to verify correctness of destructing non-trivial types --- Source/Core/Common/FixedSizeQueue.h | 44 +++++++--- .../UnitTests/Common/FixedSizeQueueTest.cpp | 82 +++++++++++++++++++ 2 files changed, 114 insertions(+), 12 deletions(-) diff --git a/Source/Core/Common/FixedSizeQueue.h b/Source/Core/Common/FixedSizeQueue.h index 267b3d8c4b..149ee9846b 100644 --- a/Source/Core/Common/FixedSizeQueue.h +++ b/Source/Core/Common/FixedSizeQueue.h @@ -6,6 +6,7 @@ #include #include +#include #include // STL-look-a-like interface, but name is mixed case to distinguish it clearly from the @@ -19,6 +20,9 @@ class FixedSizeQueue public: void clear() { + if constexpr (!std::is_trivial_v) + storage = {}; + head = 0; tail = 0; count = 0; @@ -26,31 +30,47 @@ public: void push(T t) { + if (count == N) + head = (head + 1) % N; + else + count++; + storage[tail] = std::move(t); - tail++; - if (tail == N) - tail = 0; - count++; + tail = (tail + 1) % N; + } + + template + void emplace(Args&&... args) + { + if (count == N) + head = (head + 1) % N; + else + count++; + + storage[tail] = T(std::forward(args)...); + tail = (tail + 1) % N; } void pop() { - head++; - if (head == N) - head = 0; + if constexpr (!std::is_trivial_v) + storage[head] = {}; + + head = (head + 1) % N; count--; } T pop_front() { - T& temp = storage[head]; + T temp = std::move(front()); pop(); - return std::move(temp); + return temp; } - T& front() { return storage[head]; } - const T& front() const { return storage[head]; } - size_t size() const { return count; } + T& front() noexcept { return storage[head]; } + const T& front() const noexcept { return storage[head]; } + size_t size() const noexcept { return count; } + bool empty() const noexcept { return size() == 0; } private: std::array storage; diff --git a/Source/UnitTests/Common/FixedSizeQueueTest.cpp b/Source/UnitTests/Common/FixedSizeQueueTest.cpp index 1461ad672f..a6f6d7e833 100644 --- a/Source/UnitTests/Common/FixedSizeQueueTest.cpp +++ b/Source/UnitTests/Common/FixedSizeQueueTest.cpp @@ -31,3 +31,85 @@ TEST(FixedSizeQueue, Simple) EXPECT_EQ(0u, q.size()); } + +TEST(FixedSizeQueue, RingBuffer) +{ + // Testing if queue works when used as a ring buffer + FixedSizeQueue q; + + EXPECT_EQ(0u, q.size()); + + q.push(0); + q.push(1); + q.push(2); + q.push(3); + q.push(4); + q.push(5); + + EXPECT_EQ(5u, q.size()); + EXPECT_EQ(1, q.pop_front()); + + EXPECT_EQ(4u, q.size()); +} + +// Local classes cannot have static fields, +// therefore this has to be declared in global scope. +class NonTrivialTypeTestData +{ +public: + static inline int num_objects = 0; + static inline int total_constructed = 0; + static inline int default_constructed = 0; + static inline int total_destructed = 0; + + NonTrivialTypeTestData() + { + num_objects++; + total_constructed++; + default_constructed++; + } + + NonTrivialTypeTestData(int /*val*/) + { + num_objects++; + total_constructed++; + } + + ~NonTrivialTypeTestData() + { + num_objects--; + total_destructed++; + } +}; + +TEST(FixedSizeQueue, NonTrivialTypes) +{ + // Testing if construction/destruction of non-trivial types happens as expected + FixedSizeQueue q; + + EXPECT_EQ(0u, q.size()); + + EXPECT_EQ(2, NonTrivialTypeTestData::num_objects); + EXPECT_EQ(2, NonTrivialTypeTestData::total_constructed); + EXPECT_EQ(2, NonTrivialTypeTestData::default_constructed); + EXPECT_EQ(0, NonTrivialTypeTestData::total_destructed); + + q.emplace(4); + q.emplace(6); + q.emplace(8); + + EXPECT_EQ(2, NonTrivialTypeTestData::num_objects); + EXPECT_EQ(2 + 3, NonTrivialTypeTestData::total_constructed); + EXPECT_EQ(2, NonTrivialTypeTestData::default_constructed); + EXPECT_EQ(3, NonTrivialTypeTestData::total_destructed); + EXPECT_EQ(2u, q.size()); + + q.pop(); + q.pop(); + + EXPECT_EQ(2, NonTrivialTypeTestData::num_objects); + EXPECT_EQ(2 + 3 + 2, NonTrivialTypeTestData::total_constructed); + EXPECT_EQ(2 + 2, NonTrivialTypeTestData::default_constructed); + EXPECT_EQ(3 + 2, NonTrivialTypeTestData::total_destructed); + EXPECT_EQ(0u, q.size()); +} From 6bfa4fa64306d2fc882282ea147f2b22afd4a9cb Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 31 Aug 2019 20:27:15 +0200 Subject: [PATCH 6/6] LogWidget: Use FixedSizeQueue for a log messages buffer Messages buffer is intended to be of a fixed capacity (MAX_LOG_LINES), which cannot be achieved by std::queue unless we manually pop() extra elements. std::queue uses std::deque internally which most likely results in allocations performed continuously. FixedSizeQueue keeps a single buffer during its entire lifetime, avoiding any allocations except the ones performed by stored objects. --- Source/Core/DolphinQt/Config/LogWidget.cpp | 19 +++++++------------ Source/Core/DolphinQt/Config/LogWidget.h | 7 +++++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Source/Core/DolphinQt/Config/LogWidget.cpp b/Source/Core/DolphinQt/Config/LogWidget.cpp index 743d2b187e..d73841f26a 100644 --- a/Source/Core/DolphinQt/Config/LogWidget.cpp +++ b/Source/Core/DolphinQt/Config/LogWidget.cpp @@ -19,8 +19,6 @@ // Delay in ms between calls of UpdateLog() constexpr int UPDATE_LOG_DELAY = 100; -// Maximum number of lines to show in log viewer -constexpr int MAX_LOG_LINES = 5000; // Maximum lines to process at a time constexpr size_t MAX_LOG_LINES_TO_UPDATE = 200; // Timestamp length @@ -70,16 +68,13 @@ void LogWidget::UpdateLog() std::vector elements_to_push; { std::lock_guard lock(m_log_mutex); - if (m_log_queue.empty()) + if (m_log_ring_buffer.empty()) return; - elements_to_push.reserve(std::min(MAX_LOG_LINES_TO_UPDATE, m_log_queue.size())); + elements_to_push.reserve(std::min(MAX_LOG_LINES_TO_UPDATE, m_log_ring_buffer.size())); - for (size_t i = 0; !m_log_queue.empty() && i < MAX_LOG_LINES_TO_UPDATE; i++) - { - elements_to_push.push_back(std::move(m_log_queue.front())); - m_log_queue.pop(); - } + for (size_t i = 0; !m_log_ring_buffer.empty() && i < MAX_LOG_LINES_TO_UPDATE; i++) + elements_to_push.push_back(std::move(m_log_ring_buffer.pop_front())); } for (auto& line : elements_to_push) @@ -168,7 +163,7 @@ void LogWidget::ConnectWidgets() { connect(m_log_clear, &QPushButton::clicked, [this] { m_log_text->clear(); - m_log_queue = {}; + m_log_ring_buffer.clear(); }); connect(m_log_wrap, &QCheckBox::toggled, this, &LogWidget::SaveSettings); connect(m_log_font, static_cast(&QComboBox::currentIndexChanged), this, @@ -219,8 +214,8 @@ void LogWidget::Log(LogTypes::LOG_LEVELS level, const char* text) text_length--; std::lock_guard lock(m_log_mutex); - m_log_queue.emplace(std::piecewise_construct, std::forward_as_tuple(text, text_length), - std::forward_as_tuple(level)); + m_log_ring_buffer.emplace(std::piecewise_construct, std::forward_as_tuple(text, text_length), + std::forward_as_tuple(level)); } void LogWidget::closeEvent(QCloseEvent*) diff --git a/Source/Core/DolphinQt/Config/LogWidget.h b/Source/Core/DolphinQt/Config/LogWidget.h index 9c7694b273..a56bd3df4b 100644 --- a/Source/Core/DolphinQt/Config/LogWidget.h +++ b/Source/Core/DolphinQt/Config/LogWidget.h @@ -7,9 +7,9 @@ #include #include -#include #include +#include "Common/FixedSizeQueue.h" #include "Common/Logging/LogManager.h" class QCheckBox; @@ -49,6 +49,9 @@ private: using LogEntry = std::pair; + // Maximum number of lines to show in log viewer + static constexpr int MAX_LOG_LINES = 5000; + std::mutex m_log_mutex; - std::queue m_log_queue; + FixedSizeQueue m_log_ring_buffer; };