1 // Copyright 2015 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <QAction>
6 #include <QLayout>
7 #include <QMouseEvent>
8 #include <QPainter>
9 #include <QString>
10 #include <QTimer>
11 #include "common/common_types.h"
12 #include "common/microprofile.h"
13 #include "yuzu/debugger/profiler.h"
14 #include "yuzu/util/util.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 #if MICROPROFILE_ENABLED
19 #define MICROPROFILEUI_IMPL 1
20 #include "common/microprofileui.h"
21
22 class MicroProfileWidget : public QWidget {
23 public:
24 MicroProfileWidget(QWidget* parent = nullptr);
25
26 protected:
27 void paintEvent(QPaintEvent* ev) override;
28 void showEvent(QShowEvent* ev) override;
29 void hideEvent(QHideEvent* ev) override;
30
31 void mouseMoveEvent(QMouseEvent* ev) override;
32 void mousePressEvent(QMouseEvent* ev) override;
33 void mouseReleaseEvent(QMouseEvent* ev) override;
34 void wheelEvent(QWheelEvent* ev) override;
35
36 void keyPressEvent(QKeyEvent* ev) override;
37 void keyReleaseEvent(QKeyEvent* ev) override;
38
39 private:
40 /// This timer is used to redraw the widget's contents continuously. To save resources, it only
41 /// runs while the widget is visible.
42 QTimer update_timer;
43 /// Scale the coordinate system appropriately when dpi != 96.
44 qreal x_scale = 1.0, y_scale = 1.0;
45 };
46
47 #endif
48
MicroProfileDialog(QWidget * parent)49 MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) {
50 setObjectName(QStringLiteral("MicroProfile"));
51 setWindowTitle(tr("MicroProfile"));
52 resize(1000, 600);
53 // Remove the "?" button from the titlebar and enable the maximize button
54 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) |
55 Qt::WindowMaximizeButtonHint);
56
57 #if MICROPROFILE_ENABLED
58
59 MicroProfileWidget* widget = new MicroProfileWidget(this);
60
61 QLayout* layout = new QVBoxLayout(this);
62 layout->setContentsMargins(0, 0, 0, 0);
63 layout->addWidget(widget);
64 setLayout(layout);
65
66 // Configure focus so that widget is focusable and the dialog automatically forwards focus to
67 // it.
68 setFocusProxy(widget);
69 widget->setFocusPolicy(Qt::StrongFocus);
70 widget->setFocus();
71 #endif
72 }
73
toggleViewAction()74 QAction* MicroProfileDialog::toggleViewAction() {
75 if (toggle_view_action == nullptr) {
76 toggle_view_action = new QAction(windowTitle(), this);
77 toggle_view_action->setCheckable(true);
78 toggle_view_action->setChecked(isVisible());
79 connect(toggle_view_action, &QAction::toggled, this, &MicroProfileDialog::setVisible);
80 }
81
82 return toggle_view_action;
83 }
84
showEvent(QShowEvent * ev)85 void MicroProfileDialog::showEvent(QShowEvent* ev) {
86 if (toggle_view_action) {
87 toggle_view_action->setChecked(isVisible());
88 }
89 QWidget::showEvent(ev);
90 }
91
hideEvent(QHideEvent * ev)92 void MicroProfileDialog::hideEvent(QHideEvent* ev) {
93 if (toggle_view_action) {
94 toggle_view_action->setChecked(isVisible());
95 }
96 QWidget::hideEvent(ev);
97 }
98
99 #if MICROPROFILE_ENABLED
100
101 /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the
102 /// QPainter available inside the drawing callbacks.
103 static QPainter* mp_painter = nullptr;
104
MicroProfileWidget(QWidget * parent)105 MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
106 // Send mouse motion events even when not dragging.
107 setMouseTracking(true);
108
109 MicroProfileSetDisplayMode(1); // Timers screen
110 MicroProfileInitUI();
111
112 connect(&update_timer, &QTimer::timeout, this, qOverload<>(&MicroProfileWidget::update));
113 }
114
paintEvent(QPaintEvent * ev)115 void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
116 QPainter painter(this);
117
118 // The units used by Microprofile for drawing are based in pixels on a 96 dpi display.
119 x_scale = qreal(painter.device()->logicalDpiX()) / 96.0;
120 y_scale = qreal(painter.device()->logicalDpiY()) / 96.0;
121 painter.scale(x_scale, y_scale);
122
123 painter.setBackground(Qt::black);
124 painter.eraseRect(rect());
125
126 QFont font = GetMonospaceFont();
127 font.setPixelSize(MICROPROFILE_TEXT_HEIGHT);
128 painter.setFont(font);
129
130 mp_painter = &painter;
131 MicroProfileDraw(rect().width() / x_scale, rect().height() / y_scale);
132 mp_painter = nullptr;
133 }
134
showEvent(QShowEvent * ev)135 void MicroProfileWidget::showEvent(QShowEvent* ev) {
136 update_timer.start(15); // ~60 Hz
137 QWidget::showEvent(ev);
138 }
139
hideEvent(QHideEvent * ev)140 void MicroProfileWidget::hideEvent(QHideEvent* ev) {
141 update_timer.stop();
142 QWidget::hideEvent(ev);
143 }
144
mouseMoveEvent(QMouseEvent * ev)145 void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) {
146 MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
147 ev->accept();
148 }
149
mousePressEvent(QMouseEvent * ev)150 void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) {
151 MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
152 MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
153 ev->accept();
154 }
155
mouseReleaseEvent(QMouseEvent * ev)156 void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) {
157 MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
158 MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
159 ev->accept();
160 }
161
wheelEvent(QWheelEvent * ev)162 void MicroProfileWidget::wheelEvent(QWheelEvent* ev) {
163 MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, ev->delta() / 120);
164 ev->accept();
165 }
166
keyPressEvent(QKeyEvent * ev)167 void MicroProfileWidget::keyPressEvent(QKeyEvent* ev) {
168 if (ev->key() == Qt::Key_Control) {
169 // Inform MicroProfile that the user is holding Ctrl.
170 MicroProfileModKey(1);
171 }
172 QWidget::keyPressEvent(ev);
173 }
174
keyReleaseEvent(QKeyEvent * ev)175 void MicroProfileWidget::keyReleaseEvent(QKeyEvent* ev) {
176 if (ev->key() == Qt::Key_Control) {
177 MicroProfileModKey(0);
178 }
179 QWidget::keyReleaseEvent(ev);
180 }
181
182 // These functions are called by MicroProfileDraw to draw the interface elements on the screen.
183
MicroProfileDrawText(int x,int y,u32 hex_color,const char * text,u32 text_length)184 void MicroProfileDrawText(int x, int y, u32 hex_color, const char* text, u32 text_length) {
185 // hex_color does not include an alpha, so it must be assumed to be 255
186 mp_painter->setPen(QColor::fromRgb(hex_color));
187
188 // It's impossible to draw a string using a monospaced font with a fixed width per cell in a
189 // way that's reliable across different platforms and fonts as far as I (yuriks) can tell, so
190 // draw each character individually in order to precisely control the text advance.
191 for (u32 i = 0; i < text_length; ++i) {
192 // Position the text baseline 1 pixel above the bottom of the text cell, this gives nice
193 // vertical alignment of text for a wide range of tested fonts.
194 mp_painter->drawText(x, y + MICROPROFILE_TEXT_HEIGHT - 2, QString{QLatin1Char{text[i]}});
195 x += MICROPROFILE_TEXT_WIDTH + 1;
196 }
197 }
198
MicroProfileDrawBox(int left,int top,int right,int bottom,u32 hex_color,MicroProfileBoxType type)199 void MicroProfileDrawBox(int left, int top, int right, int bottom, u32 hex_color,
200 MicroProfileBoxType type) {
201 QColor color = QColor::fromRgba(hex_color);
202 QBrush brush = color;
203 if (type == MicroProfileBoxTypeBar) {
204 QLinearGradient gradient(left, top, left, bottom);
205 gradient.setColorAt(0.f, color.lighter(125));
206 gradient.setColorAt(1.f, color.darker(125));
207 brush = gradient;
208 }
209 mp_painter->fillRect(left, top, right - left, bottom - top, brush);
210 }
211
MicroProfileDrawLine2D(u32 vertices_length,float * vertices,u32 hex_color)212 void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color) {
213 // Temporary vector used to convert between the float array and QPointF. Marked static to reuse
214 // the allocation across calls.
215 static std::vector<QPointF> point_buf;
216
217 for (u32 i = 0; i < vertices_length; ++i) {
218 point_buf.emplace_back(vertices[i * 2 + 0], vertices[i * 2 + 1]);
219 }
220
221 // hex_color does not include an alpha, so it must be assumed to be 255
222 mp_painter->setPen(QColor::fromRgb(hex_color));
223 mp_painter->drawPolyline(point_buf.data(), vertices_length);
224 point_buf.clear();
225 }
226 #endif
227