Merge pull request #1048 from yuriks/microprofile

Integrate the MicroProfile profiling library
This commit is contained in:
Yuri Kunde Schlesner 2015-08-24 18:30:11 -07:00
commit afa2b0205a
30 changed files with 11174 additions and 5 deletions

View file

@ -213,6 +213,7 @@ set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih")
include_directories(${INI_PREFIX})
add_subdirectory(${INI_PREFIX})
include_directories(externals/microprofile)
include_directories(externals/nihstro/include)
if (MSVC)

7
externals/microprofile/README.md vendored Normal file
View file

@ -0,0 +1,7 @@
# microprofile
MicroProfile is a embeddable profiler in a single file, written in C++
It can display profile information in the application, or by generating captures via a minimal built in webserver.
For more information see the project webpage at https://bitbucket.org/jonasmeyer/microprofile

3571
externals/microprofile/microprofile.h vendored Normal file

File diff suppressed because it is too large Load diff

3868
externals/microprofile/microprofile_html.h vendored Normal file

File diff suppressed because it is too large Load diff

3348
externals/microprofile/microprofileui.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,9 @@
#include <thread>
#include <iostream>
// This needs to be included before getopt.h because the latter #defines symbols used by it
#include "common/microprofile.h"
#ifdef _MSC_VER
#include <getopt.h>
#else
@ -59,6 +62,8 @@ int main(int argc, char **argv) {
Log::Filter log_filter(Log::Level::Debug);
Log::SetFilter(&log_filter);
MicroProfileOnThreadCreate("EmuThread");
if (boot_filename.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return -1;
@ -89,5 +94,7 @@ int main(int argc, char **argv) {
delete emu_window;
MicroProfileShutdown();
return 0;
}

View file

@ -18,6 +18,7 @@ set(SRCS
debugger/ramview.cpp
debugger/registers.cpp
util/spinbox.cpp
util/util.cpp
bootmanager.cpp
hotkeys.cpp
main.cpp
@ -42,6 +43,7 @@ set(HEADERS
debugger/ramview.h
debugger/registers.h
util/spinbox.h
util/util.h
bootmanager.h
hotkeys.h
main.h

View file

@ -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();
}

View file

@ -7,6 +7,8 @@
#include <QVBoxLayout>
#include <QDebug>
#include "citra_qt/util/util.h"
extern GraphicsDebugger g_debugger;
GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) : QAbstractListModel(parent), command_count(0)
@ -79,7 +81,7 @@ GPUCommandStreamWidget::GPUCommandStreamWidget(QWidget* parent) : QDockWidget(tr
QListView* command_list = new QListView;
command_list->setModel(command_model);
command_list->setFont(QFont("monospace"));
command_list->setFont(GetMonospaceFont());
setWidget(command_list);
}

View file

@ -14,6 +14,8 @@
#include <QSpinBox>
#include <QComboBox>
#include "citra_qt/util/util.h"
#include "common/vector_math.h"
#include "video_core/debug_utils/debug_utils.h"
@ -303,9 +305,7 @@ GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pi
list_widget = new QTreeView;
list_widget->setModel(model);
QFont font("monospace");
font.setStyleHint(QFont::Monospace); // Automatic fallback to a monospace font on on platforms without a font called "monospace"
list_widget->setFont(font);
list_widget->setFont(GetMonospaceFont());
list_widget->setRootIsDecorated(false);
list_widget->setUniformRowHeights(true);

View file

@ -15,6 +15,8 @@
#include <QSpinBox>
#include <QTreeView>
#include "citra_qt/util/util.h"
#include "video_core/shader/shader.h"
#include "graphics_vertex_shader.h"
@ -245,7 +247,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
}
case Qt::FontRole:
return QFont("monospace");
return GetMonospaceFont();
case Qt::BackgroundRole:
// Highlight instructions which have no debug data associated to them

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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");

View file

@ -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;

View file

@ -0,0 +1,13 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "util.h"
QFont GetMonospaceFont() {
QFont font("monospace");
// Automatic fallback to a monospace font on on platforms without a font called "monospace"
font.setStyleHint(QFont::Monospace);
font.setFixedPitch(true);
return font;
}

8
src/citra_qt/util/util.h Normal file
View file

@ -0,0 +1,8 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QFont>
/// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc.
QFont GetMonospaceFont();

View file

@ -11,6 +11,7 @@ set(SRCS
logging/text_formatter.cpp
logging/backend.cpp
memory_util.cpp
microprofile.cpp
misc.cpp
profiler.cpp
scm_rev.cpp
@ -43,6 +44,8 @@ set(HEADERS
make_unique.h
math_util.h
memory_util.h
microprofile.h
microprofileui.h
platform.h
profiler.h
profiler_reporting.h

View file

@ -0,0 +1,7 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// Includes the MicroProfile implementation in this file for compilation
#define MICROPROFILE_IMPL 1
#include "common/microprofile.h"

25
src/common/microprofile.h Normal file
View file

@ -0,0 +1,25 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
// Customized Citra settings.
// This file wraps the MicroProfile header so that these are consistent everywhere.
#define MICROPROFILE_WEBSERVER 0
#define MICROPROFILE_GPU_TIMERS 0 // TODO: Implement timer queries when we upgrade to OpenGL 3.3
#define MICROPROFILE_CONTEXT_SWITCH_TRACE 0
#define MICROPROFILE_PER_THREAD_BUFFER_SIZE (2048<<12) // 8 MB
#include <microprofile.h>
#define MP_RGB(r, g, b) ((r) << 16 | (g) << 8 | (b) << 0)
// On OS X, some Mach header included by MicroProfile defines these as macros, conflicting with
// identifiers we use.
#ifdef PAGE_SIZE
#undef PAGE_SIZE
#endif
#ifdef PAGE_MASK
#undef PAGE_MASK
#endif

Some files were not shown because too many files have changed in this diff Show more