diff options
Diffstat (limited to '')
| -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 @@ | |||
| 14 | #include "common/string_util.h" | 14 | #include "common/string_util.h" |
| 15 | #include "common/scm_rev.h" | 15 | #include "common/scm_rev.h" |
| 16 | #include "common/key_map.h" | 16 | #include "common/key_map.h" |
| 17 | #include "common/microprofile.h" | ||
| 17 | 18 | ||
| 18 | #include "core/core.h" | 19 | #include "core/core.h" |
| 19 | #include "core/settings.h" | 20 | #include "core/settings.h" |
| @@ -37,6 +38,8 @@ EmuThread::EmuThread(GRenderWindow* render_window) : | |||
| 37 | void EmuThread::run() { | 38 | void EmuThread::run() { |
| 38 | render_window->MakeCurrent(); | 39 | render_window->MakeCurrent(); |
| 39 | 40 | ||
| 41 | MicroProfileOnThreadCreate("EmuThread"); | ||
| 42 | |||
| 40 | stop_run = false; | 43 | stop_run = false; |
| 41 | 44 | ||
| 42 | // holds whether the cpu was running during the last iteration, | 45 | // holds whether the cpu was running during the last iteration, |
| @@ -69,6 +72,8 @@ void EmuThread::run() { | |||
| 69 | } | 72 | } |
| 70 | } | 73 | } |
| 71 | 74 | ||
| 75 | MicroProfileOnThreadExit(); | ||
| 76 | |||
| 72 | render_window->moveContext(); | 77 | render_window->moveContext(); |
| 73 | } | 78 | } |
| 74 | 79 | ||
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 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QMouseEvent> | ||
| 6 | #include <QPainter> | ||
| 7 | #include <QString> | ||
| 8 | |||
| 5 | #include "profiler.h" | 9 | #include "profiler.h" |
| 6 | 10 | ||
| 11 | #include "citra_qt/util/util.h" | ||
| 12 | |||
| 7 | #include "common/profiler_reporting.h" | 13 | #include "common/profiler_reporting.h" |
| 14 | #include "common/microprofile.h" | ||
| 15 | |||
| 16 | // Include the implementation of the UI in this file. This isn't in microprofile.cpp because the | ||
| 17 | // non-Qt frontends don't need it (and don't implement the UI drawing hooks either). | ||
| 18 | #define MICROPROFILEUI_IMPL 1 | ||
| 19 | #include "common/microprofileui.h" | ||
| 8 | 20 | ||
| 9 | using namespace Common::Profiling; | 21 | using namespace Common::Profiling; |
| 10 | 22 | ||
| @@ -136,3 +148,193 @@ void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) | |||
| 136 | update_timer.stop(); | 148 | update_timer.stop(); |
| 137 | } | 149 | } |
| 138 | } | 150 | } |
| 151 | |||
| 152 | class MicroProfileWidget : public QWidget { | ||
| 153 | public: | ||
| 154 | MicroProfileWidget(QWidget* parent = 0); | ||
| 155 | |||
| 156 | protected: | ||
| 157 | void paintEvent(QPaintEvent* ev) override; | ||
| 158 | void showEvent(QShowEvent* ev) override; | ||
| 159 | void hideEvent(QHideEvent* ev) override; | ||
| 160 | |||
| 161 | void mouseMoveEvent(QMouseEvent* ev) override; | ||
| 162 | void mousePressEvent(QMouseEvent* ev) override; | ||
| 163 | void mouseReleaseEvent(QMouseEvent* ev) override; | ||
| 164 | void wheelEvent(QWheelEvent* ev) override; | ||
| 165 | |||
| 166 | void keyPressEvent(QKeyEvent* ev) override; | ||
| 167 | void keyReleaseEvent(QKeyEvent* ev) override; | ||
| 168 | |||
| 169 | private: | ||
| 170 | /// This timer is used to redraw the widget's contents continuously. To save resources, it only | ||
| 171 | /// runs while the widget is visible. | ||
| 172 | QTimer update_timer; | ||
| 173 | }; | ||
| 174 | |||
| 175 | MicroProfileDialog::MicroProfileDialog(QWidget* parent) | ||
| 176 | : QWidget(parent, Qt::Dialog) | ||
| 177 | { | ||
| 178 | setObjectName("MicroProfile"); | ||
| 179 | setWindowTitle(tr("MicroProfile")); | ||
| 180 | resize(1000, 600); | ||
| 181 | // Remove the "?" button from the titlebar and enable the maximize button | ||
| 182 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint | Qt::WindowMaximizeButtonHint); | ||
| 183 | |||
| 184 | MicroProfileWidget* widget = new MicroProfileWidget(this); | ||
| 185 | |||
| 186 | QLayout* layout = new QVBoxLayout(this); | ||
| 187 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 188 | layout->addWidget(widget); | ||
| 189 | setLayout(layout); | ||
| 190 | |||
| 191 | // Configure focus so that widget is focusable and the dialog automatically forwards focus to it. | ||
| 192 | setFocusProxy(widget); | ||
| 193 | widget->setFocusPolicy(Qt::StrongFocus); | ||
| 194 | widget->setFocus(); | ||
| 195 | } | ||
| 196 | |||
| 197 | QAction* MicroProfileDialog::toggleViewAction() { | ||
| 198 | if (toggle_view_action == nullptr) { | ||
| 199 | toggle_view_action = new QAction(windowTitle(), this); | ||
| 200 | toggle_view_action->setCheckable(true); | ||
| 201 | toggle_view_action->setChecked(isVisible()); | ||
| 202 | connect(toggle_view_action, SIGNAL(toggled(bool)), SLOT(setVisible(bool))); | ||
| 203 | } | ||
| 204 | |||
| 205 | return toggle_view_action; | ||
| 206 | } | ||
| 207 | |||
| 208 | void MicroProfileDialog::showEvent(QShowEvent* ev) { | ||
| 209 | if (toggle_view_action) { | ||
| 210 | toggle_view_action->setChecked(isVisible()); | ||
| 211 | } | ||
| 212 | QWidget::showEvent(ev); | ||
| 213 | } | ||
| 214 | |||
| 215 | void MicroProfileDialog::hideEvent(QHideEvent* ev) { | ||
| 216 | if (toggle_view_action) { | ||
| 217 | toggle_view_action->setChecked(isVisible()); | ||
| 218 | } | ||
| 219 | QWidget::hideEvent(ev); | ||
| 220 | } | ||
| 221 | |||
| 222 | /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the | ||
| 223 | /// QPainter available inside the drawing callbacks. | ||
| 224 | static QPainter* mp_painter = nullptr; | ||
| 225 | |||
| 226 | MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) { | ||
| 227 | // Send mouse motion events even when not dragging. | ||
| 228 | setMouseTracking(true); | ||
| 229 | |||
| 230 | MicroProfileSetDisplayMode(1); // Timers screen | ||
| 231 | MicroProfileInitUI(); | ||
| 232 | |||
| 233 | connect(&update_timer, SIGNAL(timeout()), SLOT(update())); | ||
| 234 | } | ||
| 235 | |||
| 236 | void MicroProfileWidget::paintEvent(QPaintEvent* ev) { | ||
| 237 | QPainter painter(this); | ||
| 238 | |||
| 239 | painter.setBackground(Qt::black); | ||
| 240 | painter.eraseRect(rect()); | ||
| 241 | |||
| 242 | QFont font = GetMonospaceFont(); | ||
| 243 | font.setPixelSize(MICROPROFILE_TEXT_HEIGHT); | ||
| 244 | painter.setFont(font); | ||
| 245 | |||
| 246 | mp_painter = &painter; | ||
| 247 | MicroProfileDraw(rect().width(), rect().height()); | ||
| 248 | mp_painter = nullptr; | ||
| 249 | } | ||
| 250 | |||
| 251 | void MicroProfileWidget::showEvent(QShowEvent* ev) { | ||
| 252 | update_timer.start(15); // ~60 Hz | ||
| 253 | QWidget::showEvent(ev); | ||
| 254 | } | ||
| 255 | |||
| 256 | void MicroProfileWidget::hideEvent(QHideEvent* ev) { | ||
| 257 | update_timer.stop(); | ||
| 258 | QWidget::hideEvent(ev); | ||
| 259 | } | ||
| 260 | |||
| 261 | void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) { | ||
| 262 | MicroProfileMousePosition(ev->x(), ev->y(), 0); | ||
| 263 | ev->accept(); | ||
| 264 | } | ||
| 265 | |||
| 266 | void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) { | ||
| 267 | MicroProfileMousePosition(ev->x(), ev->y(), 0); | ||
| 268 | MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); | ||
| 269 | ev->accept(); | ||
| 270 | } | ||
| 271 | |||
| 272 | void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { | ||
| 273 | MicroProfileMousePosition(ev->x(), ev->y(), 0); | ||
| 274 | MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); | ||
| 275 | ev->accept(); | ||
| 276 | } | ||
| 277 | |||
| 278 | void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { | ||
| 279 | MicroProfileMousePosition(ev->x(), ev->y(), ev->delta() / 120); | ||
| 280 | ev->accept(); | ||
| 281 | } | ||
| 282 | |||
| 283 | void MicroProfileWidget::keyPressEvent(QKeyEvent* ev) { | ||
| 284 | if (ev->key() == Qt::Key_Control) { | ||
| 285 | // Inform MicroProfile that the user is holding Ctrl. | ||
| 286 | MicroProfileModKey(1); | ||
| 287 | } | ||
| 288 | QWidget::keyPressEvent(ev); | ||
| 289 | } | ||
| 290 | |||
| 291 | void MicroProfileWidget::keyReleaseEvent(QKeyEvent* ev) { | ||
| 292 | if (ev->key() == Qt::Key_Control) { | ||
| 293 | MicroProfileModKey(0); | ||
| 294 | } | ||
| 295 | QWidget::keyReleaseEvent(ev); | ||
| 296 | } | ||
| 297 | |||
| 298 | // These functions are called by MicroProfileDraw to draw the interface elements on the screen. | ||
| 299 | |||
| 300 | void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 text_length) { | ||
| 301 | // hex_color does not include an alpha, so it must be assumed to be 255 | ||
| 302 | mp_painter->setPen(QColor::fromRgb(hex_color)); | ||
| 303 | |||
| 304 | // It's impossible to draw a string using a monospaced font with a fixed width per cell in a | ||
| 305 | // way that's reliable across different platforms and fonts as far as I (yuriks) can tell, so | ||
| 306 | // draw each character individually in order to precisely control the text advance. | ||
| 307 | for (u32 i = 0; i < text_length; ++i) { | ||
| 308 | // Position the text baseline 1 pixel above the bottom of the text cell, this gives nice | ||
| 309 | // vertical alignment of text for a wide range of tested fonts. | ||
| 310 | mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QChar(text[i])); | ||
| 311 | x += MICROPROFILE_TEXT_WIDTH + 1; | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color, MicroProfileBoxType type) { | ||
| 316 | QColor color = QColor::fromRgba(hex_color); | ||
| 317 | QBrush brush = color; | ||
| 318 | if (type == MicroProfileBoxTypeBar) { | ||
| 319 | QLinearGradient gradient(left, top, left, bottom); | ||
| 320 | gradient.setColorAt(0.f, color.lighter(125)); | ||
| 321 | gradient.setColorAt(1.f, color.darker(125)); | ||
| 322 | brush = gradient; | ||
| 323 | } | ||
| 324 | mp_painter->fillRect(left, top, right - left, bottom - top, brush); | ||
| 325 | } | ||
| 326 | |||
| 327 | void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color) { | ||
| 328 | // Temporary vector used to convert between the float array and QPointF. Marked static to reuse | ||
| 329 | // the allocation across calls. | ||
| 330 | static std::vector<QPointF> point_buf; | ||
| 331 | |||
| 332 | for (u32 i = 0; i < vertices_length; ++i) { | ||
| 333 | point_buf.emplace_back(vertices[i*2 + 0], vertices[i*2 + 1]); | ||
| 334 | } | ||
| 335 | |||
| 336 | // hex_color does not include an alpha, so it must be assumed to be 255 | ||
| 337 | mp_painter->setPen(QColor::fromRgb(hex_color)); | ||
| 338 | mp_painter->drawPolyline(point_buf.data(), vertices_length); | ||
| 339 | point_buf.clear(); | ||
| 340 | } | ||
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: | |||
| 48 | 48 | ||
| 49 | QTimer update_timer; | 49 | QTimer update_timer; |
| 50 | }; | 50 | }; |
| 51 | |||
| 52 | class MicroProfileDialog : public QWidget { | ||
| 53 | Q_OBJECT | ||
| 54 | |||
| 55 | public: | ||
| 56 | MicroProfileDialog(QWidget* parent = 0); | ||
| 57 | |||
| 58 | /// Returns a QAction that can be used to toggle visibility of this dialog. | ||
| 59 | QAction* toggleViewAction(); | ||
| 60 | |||
| 61 | protected: | ||
| 62 | void showEvent(QShowEvent* ev) override; | ||
| 63 | void hideEvent(QHideEvent* ev) override; | ||
| 64 | |||
| 65 | private: | ||
| 66 | QAction* toggle_view_action = nullptr; | ||
| 67 | }; | ||
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 @@ | |||
| 17 | #include "common/logging/backend.h" | 17 | #include "common/logging/backend.h" |
| 18 | #include "common/logging/filter.h" | 18 | #include "common/logging/filter.h" |
| 19 | #include "common/make_unique.h" | 19 | #include "common/make_unique.h" |
| 20 | #include "common/microprofile.h" | ||
| 20 | #include "common/platform.h" | 21 | #include "common/platform.h" |
| 21 | #include "common/scm_rev.h" | 22 | #include "common/scm_rev.h" |
| 22 | #include "common/scope_exit.h" | 23 | #include "common/scope_exit.h" |
| @@ -64,6 +65,9 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | |||
| 64 | addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); | 65 | addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); |
| 65 | profilerWidget->hide(); | 66 | profilerWidget->hide(); |
| 66 | 67 | ||
| 68 | microProfileDialog = new MicroProfileDialog(this); | ||
| 69 | microProfileDialog->hide(); | ||
| 70 | |||
| 67 | disasmWidget = new DisassemblerWidget(this, emu_thread.get()); | 71 | disasmWidget = new DisassemblerWidget(this, emu_thread.get()); |
| 68 | addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); | 72 | addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); |
| 69 | disasmWidget->hide(); | 73 | disasmWidget->hide(); |
| @@ -102,6 +106,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | |||
| 102 | 106 | ||
| 103 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | 107 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); |
| 104 | debug_menu->addAction(profilerWidget->toggleViewAction()); | 108 | debug_menu->addAction(profilerWidget->toggleViewAction()); |
| 109 | debug_menu->addAction(microProfileDialog->toggleViewAction()); | ||
| 105 | debug_menu->addAction(disasmWidget->toggleViewAction()); | 110 | debug_menu->addAction(disasmWidget->toggleViewAction()); |
| 106 | debug_menu->addAction(registersWidget->toggleViewAction()); | 111 | debug_menu->addAction(registersWidget->toggleViewAction()); |
| 107 | debug_menu->addAction(callstackWidget->toggleViewAction()); | 112 | debug_menu->addAction(callstackWidget->toggleViewAction()); |
| @@ -128,6 +133,8 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) | |||
| 128 | restoreGeometry(settings.value("geometry").toByteArray()); | 133 | restoreGeometry(settings.value("geometry").toByteArray()); |
| 129 | restoreState(settings.value("state").toByteArray()); | 134 | restoreState(settings.value("state").toByteArray()); |
| 130 | render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray()); | 135 | render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray()); |
| 136 | microProfileDialog->restoreGeometry(settings.value("microProfileDialogGeometry").toByteArray()); | ||
| 137 | microProfileDialog->setVisible(settings.value("microProfileDialogVisible").toBool()); | ||
| 131 | 138 | ||
| 132 | ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer); | 139 | ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer); |
| 133 | SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked()); | 140 | SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked()); |
| @@ -434,6 +441,8 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 434 | settings.setValue("geometry", saveGeometry()); | 441 | settings.setValue("geometry", saveGeometry()); |
| 435 | settings.setValue("state", saveState()); | 442 | settings.setValue("state", saveState()); |
| 436 | settings.setValue("geometryRenderWindow", render_window->saveGeometry()); | 443 | settings.setValue("geometryRenderWindow", render_window->saveGeometry()); |
| 444 | settings.setValue("microProfileDialogGeometry", microProfileDialog->saveGeometry()); | ||
| 445 | settings.setValue("microProfileDialogVisible", microProfileDialog->isVisible()); | ||
| 437 | settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked()); | 446 | settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked()); |
| 438 | settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked()); | 447 | settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked()); |
| 439 | settings.setValue("firstStart", false); | 448 | settings.setValue("firstStart", false); |
| @@ -456,6 +465,11 @@ int main(int argc, char* argv[]) { | |||
| 456 | Log::Filter log_filter(Log::Level::Info); | 465 | Log::Filter log_filter(Log::Level::Info); |
| 457 | Log::SetFilter(&log_filter); | 466 | Log::SetFilter(&log_filter); |
| 458 | 467 | ||
| 468 | MicroProfileOnThreadCreate("Frontend"); | ||
| 469 | SCOPE_EXIT({ | ||
| 470 | MicroProfileShutdown(); | ||
| 471 | }); | ||
| 472 | |||
| 459 | // Init settings params | 473 | // Init settings params |
| 460 | QSettings::setDefaultFormat(QSettings::IniFormat); | 474 | QSettings::setDefaultFormat(QSettings::IniFormat); |
| 461 | QCoreApplication::setOrganizationName("Citra team"); | 475 | 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; | |||
| 14 | class GRenderWindow; | 14 | class GRenderWindow; |
| 15 | class EmuThread; | 15 | class EmuThread; |
| 16 | class ProfilerWidget; | 16 | class ProfilerWidget; |
| 17 | class MicroProfileDialog; | ||
| 17 | class DisassemblerWidget; | 18 | class DisassemblerWidget; |
| 18 | class RegistersWidget; | 19 | class RegistersWidget; |
| 19 | class CallstackWidget; | 20 | class CallstackWidget; |
| @@ -104,6 +105,7 @@ private: | |||
| 104 | std::unique_ptr<EmuThread> emu_thread; | 105 | std::unique_ptr<EmuThread> emu_thread; |
| 105 | 106 | ||
| 106 | ProfilerWidget* profilerWidget; | 107 | ProfilerWidget* profilerWidget; |
| 108 | MicroProfileDialog* microProfileDialog; | ||
| 107 | DisassemblerWidget* disasmWidget; | 109 | DisassemblerWidget* disasmWidget; |
| 108 | RegistersWidget* registersWidget; | 110 | RegistersWidget* registersWidget; |
| 109 | CallstackWidget* callstackWidget; | 111 | CallstackWidget* callstackWidget; |