| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- // Copyright 2015 Citra Emulator Project
- // Licensed under GPLv2 or any later version
- // Refer to the license.txt file included.
- #include <QMouseEvent>
- #include <QPainter>
- #include <QString>
- #include "citra_qt/debugger/profiler.h"
- #include "citra_qt/util/util.h"
- #include "common/microprofile.h"
- #include "common/profiler_reporting.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;
- static QVariant GetDataForColumn(int col, const AggregatedDuration& duration)
- {
- static auto duration_to_float = [](Duration dur) -> float {
- using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
- return std::chrono::duration_cast<FloatMs>(dur).count();
- };
- switch (col) {
- case 1: return duration_to_float(duration.avg);
- case 2: return duration_to_float(duration.min);
- case 3: return duration_to_float(duration.max);
- default: return QVariant();
- }
- }
- static const TimingCategoryInfo* GetCategoryInfo(int id)
- {
- const auto& categories = GetProfilingManager().GetTimingCategoriesInfo();
- if ((size_t)id >= categories.size()) {
- return nullptr;
- } else {
- return &categories[id];
- }
- }
- ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent)
- {
- updateProfilingInfo();
- const auto& categories = GetProfilingManager().GetTimingCategoriesInfo();
- results.time_per_category.resize(categories.size());
- }
- QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const
- {
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
- switch (section) {
- case 0: return tr("Category");
- case 1: return tr("Avg");
- case 2: return tr("Min");
- case 3: return tr("Max");
- }
- }
- return QVariant();
- }
- QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const
- {
- return createIndex(row, column);
- }
- QModelIndex ProfilerModel::parent(const QModelIndex& child) const
- {
- return QModelIndex();
- }
- int ProfilerModel::columnCount(const QModelIndex& parent) const
- {
- return 4;
- }
- int ProfilerModel::rowCount(const QModelIndex& parent) const
- {
- if (parent.isValid()) {
- return 0;
- } else {
- return static_cast<int>(results.time_per_category.size() + 2);
- }
- }
- QVariant ProfilerModel::data(const QModelIndex& index, int role) const
- {
- if (role == Qt::DisplayRole) {
- if (index.row() == 0) {
- if (index.column() == 0) {
- return tr("Frame");
- } else {
- return GetDataForColumn(index.column(), results.frame_time);
- }
- } else if (index.row() == 1) {
- if (index.column() == 0) {
- return tr("Frame (with swapping)");
- } else {
- return GetDataForColumn(index.column(), results.interframe_time);
- }
- } else {
- if (index.column() == 0) {
- const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2);
- return info != nullptr ? QString(info->name) : QVariant();
- } else {
- if (index.row() - 2 < (int)results.time_per_category.size()) {
- return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]);
- } else {
- return QVariant();
- }
- }
- }
- }
- return QVariant();
- }
- void ProfilerModel::updateProfilingInfo()
- {
- results = GetTimingResultsAggregator()->GetAggregatedResults();
- emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3));
- }
- ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent)
- {
- ui.setupUi(this);
- model = new ProfilerModel(this);
- ui.treeView->setModel(model);
- connect(this, SIGNAL(visibilityChanged(bool)), SLOT(setProfilingInfoUpdateEnabled(bool)));
- connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo()));
- }
- void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable)
- {
- if (enable) {
- update_timer.start(100);
- model->updateProfilingInfo();
- } else {
- update_timer.stop();
- }
- }
- class MicroProfileWidget : public QWidget {
- public:
- MicroProfileWidget(QWidget* parent = nullptr);
- 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();
- }
|