diff options
Diffstat (limited to 'src/citra_qt')
-rw-r--r-- | src/citra_qt/bootmanager.cpp | 5 | ||||
-rw-r--r-- | src/citra_qt/debugger/profiler.cpp | 202 | ||||
-rw-r--r-- | src/citra_qt/debugger/profiler.h | 17 | ||||
-rw-r--r-- | src/citra_qt/main.cpp | 14 | ||||
-rw-r--r-- | src/citra_qt/main.h | 2 |
5 files changed, 240 insertions, 0 deletions
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index a96fbea5f..f8aacb527 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -14,6 +14,7 @@ #include "common/string_util.h" #include "common/scm_rev.h" #include "common/key_map.h" +#include "common/microprofile.h" #include "core/core.h" #include "core/settings.h" @@ -37,6 +38,8 @@ EmuThread::EmuThread(GRenderWindow* render_window) : void EmuThread::run() { render_window->MakeCurrent(); + MicroProfileOnThreadCreate("EmuThread"); + stop_run = false; // holds whether the cpu was running during the last iteration, @@ -69,6 +72,8 @@ void EmuThread::run() { } } + MicroProfileOnThreadExit(); + render_window->moveContext(); } diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 89b28c2f4..5261d4836 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp @@ -2,9 +2,21 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QMouseEvent> +#include <QPainter> +#include <QString> + #include "profiler.h" +#include "citra_qt/util/util.h" + #include "common/profiler_reporting.h" +#include "common/microprofile.h" + +// Include the implementation of the UI in this file. This isn't in microprofile.cpp because the +// non-Qt frontends don't need it (and don't implement the UI drawing hooks either). +#define MICROPROFILEUI_IMPL 1 +#include "common/microprofileui.h" using namespace Common::Profiling; @@ -136,3 +148,193 @@ void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) update_timer.stop(); } } + +class MicroProfileWidget : public QWidget { +public: + MicroProfileWidget(QWidget* parent = 0); + +protected: + void paintEvent(QPaintEvent* ev) override; + void showEvent(QShowEvent* ev) override; + void hideEvent(QHideEvent* ev) override; + + void mouseMoveEvent(QMouseEvent* ev) override; + void mousePressEvent(QMouseEvent* ev) override; + void mouseReleaseEvent(QMouseEvent* ev) override; + void wheelEvent(QWheelEvent* ev) override; + + void keyPressEvent(QKeyEvent* ev) override; + void keyReleaseEvent(QKeyEvent* ev) override; + +private: + /// This timer is used to redraw the widget's contents continuously. To save resources, it only + /// runs while the widget is visible. + QTimer update_timer; +}; + +MicroProfileDialog::MicroProfileDialog(QWidget* parent) + : QWidget(parent, Qt::Dialog) +{ + setObjectName("MicroProfile"); + setWindowTitle(tr("MicroProfile")); + resize(1000, 600); + // Remove the "?" button from the titlebar and enable the maximize button + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint | Qt::WindowMaximizeButtonHint); + + MicroProfileWidget* widget = new MicroProfileWidget(this); + + QLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(widget); + setLayout(layout); + + // Configure focus so that widget is focusable and the dialog automatically forwards focus to it. + setFocusProxy(widget); + widget->setFocusPolicy(Qt::StrongFocus); + widget->setFocus(); +} + +QAction* MicroProfileDialog::toggleViewAction() { + if (toggle_view_action == nullptr) { + toggle_view_action = new QAction(windowTitle(), this); + toggle_view_action->setCheckable(true); + toggle_view_action->setChecked(isVisible()); + connect(toggle_view_action, SIGNAL(toggled(bool)), SLOT(setVisible(bool))); + } + + return toggle_view_action; +} + +void MicroProfileDialog::showEvent(QShowEvent* ev) { + if (toggle_view_action) { + toggle_view_action->setChecked(isVisible()); + } + QWidget::showEvent(ev); +} + +void MicroProfileDialog::hideEvent(QHideEvent* ev) { + if (toggle_view_action) { + toggle_view_action->setChecked(isVisible()); + } + QWidget::hideEvent(ev); +} + +/// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the +/// QPainter available inside the drawing callbacks. +static QPainter* mp_painter = nullptr; + +MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) { + // Send mouse motion events even when not dragging. + setMouseTracking(true); + + MicroProfileSetDisplayMode(1); // Timers screen + MicroProfileInitUI(); + + connect(&update_timer, SIGNAL(timeout()), SLOT(update())); +} + +void MicroProfileWidget::paintEvent(QPaintEvent* ev) { + QPainter painter(this); + + painter.setBackground(Qt::black); + painter.eraseRect(rect()); + + QFont font = GetMonospaceFont(); + font.setPixelSize(MICROPROFILE_TEXT_HEIGHT); + painter.setFont(font); + + mp_painter = &painter; + MicroProfileDraw(rect().width(), rect().height()); + mp_painter = nullptr; +} + +void MicroProfileWidget::showEvent(QShowEvent* ev) { + update_timer.start(15); // ~60 Hz + QWidget::showEvent(ev); +} + +void MicroProfileWidget::hideEvent(QHideEvent* ev) { + update_timer.stop(); + QWidget::hideEvent(ev); +} + +void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) { + MicroProfileMousePosition(ev->x(), ev->y(), 0); + ev->accept(); +} + +void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) { + MicroProfileMousePosition(ev->x(), ev->y(), 0); + MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); + ev->accept(); +} + +void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { + MicroProfileMousePosition(ev->x(), ev->y(), 0); + MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); + ev->accept(); +} + +void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { + MicroProfileMousePosition(ev->x(), ev->y(), ev->delta() / 120); + ev->accept(); +} + +void MicroProfileWidget::keyPressEvent(QKeyEvent* ev) { + if (ev->key() == Qt::Key_Control) { + // Inform MicroProfile that the user is holding Ctrl. + MicroProfileModKey(1); + } + QWidget::keyPressEvent(ev); +} + +void MicroProfileWidget::keyReleaseEvent(QKeyEvent* ev) { + if (ev->key() == Qt::Key_Control) { + MicroProfileModKey(0); + } + QWidget::keyReleaseEvent(ev); +} + +// These functions are called by MicroProfileDraw to draw the interface elements on the screen. + +void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 text_length) { + // hex_color does not include an alpha, so it must be assumed to be 255 + mp_painter->setPen(QColor::fromRgb(hex_color)); + + // It's impossible to draw a string using a monospaced font with a fixed width per cell in a + // way that's reliable across different platforms and fonts as far as I (yuriks) can tell, so + // draw each character individually in order to precisely control the text advance. + for (u32 i = 0; i < text_length; ++i) { + // Position the text baseline 1 pixel above the bottom of the text cell, this gives nice + // vertical alignment of text for a wide range of tested fonts. + mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QChar(text[i])); + x += MICROPROFILE_TEXT_WIDTH + 1; + } +} + +void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color, MicroProfileBoxType type) { + QColor color = QColor::fromRgba(hex_color); + QBrush brush = color; + if (type == MicroProfileBoxTypeBar) { + QLinearGradient gradient(left, top, left, bottom); + gradient.setColorAt(0.f, color.lighter(125)); + gradient.setColorAt(1.f, color.darker(125)); + brush = gradient; + } + mp_painter->fillRect(left, top, right - left, bottom - top, brush); +} + +void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color) { + // Temporary vector used to convert between the float array and QPointF. Marked static to reuse + // the allocation across calls. + static std::vector<QPointF> point_buf; + + for (u32 i = 0; i < vertices_length; ++i) { + point_buf.emplace_back(vertices[i*2 + 0], vertices[i*2 + 1]); + } + + // hex_color does not include an alpha, so it must be assumed to be 255 + mp_painter->setPen(QColor::fromRgb(hex_color)); + mp_painter->drawPolyline(point_buf.data(), vertices_length); + point_buf.clear(); +} diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h index fabf279b8..2199eaef1 100644 --- a/src/citra_qt/debugger/profiler.h +++ b/src/citra_qt/debugger/profiler.h @@ -48,3 +48,20 @@ private: QTimer update_timer; }; + +class MicroProfileDialog : public QWidget { + Q_OBJECT + +public: + MicroProfileDialog(QWidget* parent = 0); + + /// Returns a QAction that can be used to toggle visibility of this dialog. + QAction* toggleViewAction(); + +protected: + void showEvent(QShowEvent* ev) override; + void hideEvent(QHideEvent* ev) override; + +private: + QAction* toggle_view_action = nullptr; +}; diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 8bf2a3e13..7fb1b0dcb 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -17,6 +17,7 @@ #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/make_unique.h" +#include "common/microprofile.h" #include "common/platform.h" #include "common/scm_rev.h" #include "common/scope_exit.h" @@ -64,6 +65,9 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); profilerWidget->hide(); + microProfileDialog = new MicroProfileDialog(this); + microProfileDialog->hide(); + disasmWidget = new DisassemblerWidget(this, emu_thread.get()); addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); disasmWidget->hide(); @@ -102,6 +106,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); debug_menu->addAction(profilerWidget->toggleViewAction()); + debug_menu->addAction(microProfileDialog->toggleViewAction()); debug_menu->addAction(disasmWidget->toggleViewAction()); debug_menu->addAction(registersWidget->toggleViewAction()); debug_menu->addAction(callstackWidget->toggleViewAction()); @@ -128,6 +133,8 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) restoreGeometry(settings.value("geometry").toByteArray()); restoreState(settings.value("state").toByteArray()); render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray()); + microProfileDialog->restoreGeometry(settings.value("microProfileDialogGeometry").toByteArray()); + microProfileDialog->setVisible(settings.value("microProfileDialogVisible").toBool()); ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer); SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked()); @@ -434,6 +441,8 @@ void GMainWindow::closeEvent(QCloseEvent* event) { settings.setValue("geometry", saveGeometry()); settings.setValue("state", saveState()); settings.setValue("geometryRenderWindow", render_window->saveGeometry()); + settings.setValue("microProfileDialogGeometry", microProfileDialog->saveGeometry()); + settings.setValue("microProfileDialogVisible", microProfileDialog->isVisible()); settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked()); settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked()); settings.setValue("firstStart", false); @@ -456,6 +465,11 @@ int main(int argc, char* argv[]) { Log::Filter log_filter(Log::Level::Info); Log::SetFilter(&log_filter); + MicroProfileOnThreadCreate("Frontend"); + SCOPE_EXIT({ + MicroProfileShutdown(); + }); + // Init settings params QSettings::setDefaultFormat(QSettings::IniFormat); QCoreApplication::setOrganizationName("Citra team"); diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 6f1292295..32523fded 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -14,6 +14,7 @@ class GImageInfo; class GRenderWindow; class EmuThread; class ProfilerWidget; +class MicroProfileDialog; class DisassemblerWidget; class RegistersWidget; class CallstackWidget; @@ -104,6 +105,7 @@ private: std::unique_ptr<EmuThread> emu_thread; ProfilerWidget* profilerWidget; + MicroProfileDialog* microProfileDialog; DisassemblerWidget* disasmWidget; RegistersWidget* registersWidget; CallstackWidget* callstackWidget; |