diff options
Diffstat (limited to 'src/citra_qt/debugger/profiler.cpp')
| -rw-r--r-- | src/citra_qt/debugger/profiler.cpp | 202 |
1 files changed, 202 insertions, 0 deletions
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 | } | ||