summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/citra.cpp7
-rw-r--r--src/citra_qt/CMakeLists.txt2
-rw-r--r--src/citra_qt/bootmanager.cpp5
-rw-r--r--src/citra_qt/debugger/graphics.cpp4
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp6
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp4
-rw-r--r--src/citra_qt/debugger/profiler.cpp202
-rw-r--r--src/citra_qt/debugger/profiler.h17
-rw-r--r--src/citra_qt/main.cpp14
-rw-r--r--src/citra_qt/main.h2
-rw-r--r--src/citra_qt/util/util.cpp13
-rw-r--r--src/citra_qt/util/util.h10
-rw-r--r--src/common/CMakeLists.txt3
-rw-r--r--src/common/microprofile.cpp7
-rw-r--r--src/common/microprofile.h25
-rw-r--r--src/common/microprofileui.h16
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp103
-rw-r--r--src/core/hle/service/gsp_gpu.cpp5
-rw-r--r--src/core/hle/svc.cpp4
-rw-r--r--src/core/hw/gpu.cpp8
-rw-r--r--src/video_core/command_processor.cpp4
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp8
-rw-r--r--src/video_core/rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h4
-rw-r--r--src/video_core/shader/shader.cpp5
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp8
28 files changed, 450 insertions, 76 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index d6fcb66a5..46f4a07c9 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -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;
}
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 0c0515054..a82e8a85b 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -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
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 @@
#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();
}
diff --git a/src/citra_qt/debugger/graphics.cpp b/src/citra_qt/debugger/graphics.cpp
index 7424671f1..7d15028f0 100644
--- a/src/citra_qt/debugger/graphics.cpp
+++ b/src/citra_qt/debugger/graphics.cpp
@@ -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);
}
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index 35a3140b2..025434687 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -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);
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
index 0c17edee0..1d9a00e89 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -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
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 @@
// 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();
+}
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:
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;
+};
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 @@
#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");
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;
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;
diff --git a/src/citra_qt/util/util.cpp b/src/citra_qt/util/util.cpp
new file mode 100644
index 000000000..2cb939af1
--- /dev/null
+++ b/src/citra_qt/util/util.cpp
@@ -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;
+}
diff --git a/src/citra_qt/util/util.h b/src/citra_qt/util/util.h
new file mode 100644
index 000000000..98a944047
--- /dev/null
+++ b/src/citra_qt/util/util.h
@@ -0,0 +1,10 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QFont>
+
+/// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc.
+QFont GetMonospaceFont();
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index e743a026d..7f3712efa 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -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
diff --git a/src/common/microprofile.cpp b/src/common/microprofile.cpp
new file mode 100644
index 000000000..ee25dd37f
--- /dev/null
+++ b/src/common/microprofile.cpp
@@ -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"
diff --git a/src/common/microprofile.h b/src/common/microprofile.h
new file mode 100644
index 000000000..9eb6016a8
--- /dev/null
+++ b/src/common/microprofile.h
@@ -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
diff --git a/src/common/microprofileui.h b/src/common/microprofileui.h
new file mode 100644
index 000000000..97c369bd9
--- /dev/null
+++ b/src/common/microprofileui.h
@@ -0,0 +1,16 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/microprofile.h"
+
+// Customized Citra settings.
+// This file wraps the MicroProfile header so that these are consistent everywhere.
+#define MICROPROFILE_TEXT_WIDTH 6
+#define MICROPROFILE_TEXT_HEIGHT 12
+#define MICROPROFILE_HELP_ALT "Right-Click"
+#define MICROPROFILE_HELP_MOD "Ctrl"
+
+#include <microprofileui.h>
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 422e80b50..0fddb07a0 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -9,6 +9,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
+#include "common/microprofile.h"
#include "common/profiler.h"
#include "core/memory.h"
@@ -48,65 +49,47 @@ enum {
typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper);
-static int CondPassed(ARMul_State* cpu, unsigned int cond) {
- const u32 NFLAG = cpu->NFlag;
- const u32 ZFLAG = cpu->ZFlag;
- const u32 CFLAG = cpu->CFlag;
- const u32 VFLAG = cpu->VFlag;
-
- int temp = 0;
+static bool CondPassed(ARMul_State* cpu, unsigned int cond) {
+ const bool n_flag = cpu->NFlag != 0;
+ const bool z_flag = cpu->ZFlag != 0;
+ const bool c_flag = cpu->CFlag != 0;
+ const bool v_flag = cpu->VFlag != 0;
switch (cond) {
- case 0x0:
- temp = ZFLAG;
- break;
- case 0x1: // NE
- temp = !ZFLAG;
- break;
- case 0x2: // CS
- temp = CFLAG;
- break;
- case 0x3: // CC
- temp = !CFLAG;
- break;
- case 0x4: // MI
- temp = NFLAG;
- break;
- case 0x5: // PL
- temp = !NFLAG;
- break;
- case 0x6: // VS
- temp = VFLAG;
- break;
- case 0x7: // VC
- temp = !VFLAG;
- break;
- case 0x8: // HI
- temp = (CFLAG && !ZFLAG);
- break;
- case 0x9: // LS
- temp = (!CFLAG || ZFLAG);
- break;
- case 0xa: // GE
- temp = ((!NFLAG && !VFLAG) || (NFLAG && VFLAG));
- break;
- case 0xb: // LT
- temp = ((NFLAG && !VFLAG) || (!NFLAG && VFLAG));
- break;
- case 0xc: // GT
- temp = ((!NFLAG && !VFLAG && !ZFLAG) || (NFLAG && VFLAG && !ZFLAG));
- break;
- case 0xd: // LE
- temp = ((NFLAG && !VFLAG) || (!NFLAG && VFLAG)) || ZFLAG;
- break;
- case 0xe: // AL
- temp = 1;
- break;
- case 0xf:
- temp = 1;
- break;
- }
- return temp;
+ case ConditionCode::EQ:
+ return z_flag;
+ case ConditionCode::NE:
+ return !z_flag;
+ case ConditionCode::CS:
+ return c_flag;
+ case ConditionCode::CC:
+ return !c_flag;
+ case ConditionCode::MI:
+ return n_flag;
+ case ConditionCode::PL:
+ return !n_flag;
+ case ConditionCode::VS:
+ return v_flag;
+ case ConditionCode::VC:
+ return !v_flag;
+ case ConditionCode::HI:
+ return (c_flag && !z_flag);
+ case ConditionCode::LS:
+ return (!c_flag || z_flag);
+ case ConditionCode::GE:
+ return (n_flag == v_flag);
+ case ConditionCode::LT:
+ return (n_flag != v_flag);
+ case ConditionCode::GT:
+ return (!z_flag && (n_flag == v_flag));
+ case ConditionCode::LE:
+ return (z_flag || (n_flag != v_flag));
+ case ConditionCode::AL:
+ case ConditionCode::NV: // Unconditional
+ return true;
+ }
+
+ return false;
}
static unsigned int DPO(Immediate)(ARMul_State* cpu, unsigned int sht_oper) {
@@ -3522,8 +3505,11 @@ enum {
FETCH_EXCEPTION
};
+MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64));
+
static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
Common::Profiling::ScopeTimer timer_decode(profile_decode);
+ MICROPROFILE_SCOPE(DynCom_Decode);
// Decode instruction, get index
// Allocate memory and init InsCream
@@ -3588,8 +3574,11 @@ static int clz(unsigned int x) {
return n;
}
+MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0));
+
unsigned InterpreterMainLoop(ARMul_State* cpu) {
Common::Profiling::ScopeTimer timer_execute(profile_execute);
+ MICROPROFILE_SCOPE(DynCom_Execute);
#undef RM
#undef RS
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index fde508a13..c3d0d28a5 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/bit_field.h"
+#include "common/microprofile.h"
#include "core/memory.h"
#include "core/hle/kernel/event.h"
@@ -229,6 +230,10 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
if (Pica::g_debug_context)
Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr);
+
+ if (screen_id == 0) {
+ MicroProfileFlip();
+ }
}
/**
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 89ac45a6f..19f750d72 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -5,6 +5,7 @@
#include <map>
#include "common/logging/log.h"
+#include "common/microprofile.h"
#include "common/profiler.h"
#include "common/string_util.h"
#include "common/symbols.h"
@@ -969,8 +970,11 @@ static const FunctionDef* GetSVCInfo(u32 func_num) {
return &SVC_Table[func_num];
}
+MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
+
void CallSVC(u32 immediate) {
Common::Profiling::ScopeTimer timer_svc(profiler_svc);
+ MICROPROFILE_SCOPE(Kernel_SVC);
const FunctionDef* info = GetSVCInfo(immediate);
if (info) {
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 68ae38289..bc7bde903 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -9,6 +9,7 @@
#include "common/color.h"
#include "common/common_types.h"
#include "common/logging/log.h"
+#include "common/microprofile.h"
#include "common/vector_math.h"
#include "core/settings.h"
@@ -85,6 +86,9 @@ static Math::Vec4<u8> DecodePixel(Regs::PixelFormat input_format, const u8* src_
}
}
+MICROPROFILE_DEFINE(GPU_DisplayTransfer, "GPU", "DisplayTransfer", MP_RGB(100, 100, 255));
+MICROPROFILE_DEFINE(GPU_CmdlistProcessing, "GPU", "Cmdlist Processing", MP_RGB(100, 255, 100));
+
template <typename T>
inline void Write(u32 addr, const T data) {
addr -= HW::VADDR_GPU;
@@ -150,6 +154,8 @@ inline void Write(u32 addr, const T data) {
case GPU_REG_INDEX(display_transfer_config.trigger):
{
+ MICROPROFILE_SCOPE(GPU_DisplayTransfer);
+
const auto& config = g_regs.display_transfer_config;
if (config.trigger & 1) {
@@ -344,6 +350,8 @@ inline void Write(u32 addr, const T data) {
const auto& config = g_regs.command_processor_config;
if (config.trigger & 1)
{
+ MICROPROFILE_SCOPE(GPU_CmdlistProcessing);
+
u32* buffer = (u32*)Memory::GetPhysicalPointer(config.GetPhysicalAddress());
if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index d82e20f86..a78985510 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -4,6 +4,7 @@
#include <boost/range/algorithm/fill.hpp>
+#include "common/microprofile.h"
#include "common/profiler.h"
#include "core/hle/service/gsp_gpu.h"
@@ -43,6 +44,8 @@ static const u32 expand_bits_to_bytes[] = {
0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff
};
+MICROPROFILE_DEFINE(GPU_Drawing, "GPU", "Drawing", MP_RGB(50, 50, 240));
+
static void WritePicaReg(u32 id, u32 value, u32 mask) {
auto& regs = g_state.regs;
@@ -126,6 +129,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
case PICA_REG_INDEX(trigger_draw_indexed):
{
Common::Profiling::ScopeTimer scope_timer(category_drawing);
+ MICROPROFILE_SCOPE(GPU_Drawing);
#if PICA_LOG_TEV
DebugUtils::DumpTevStageConfig(regs.GetTevStages());
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 8ad77f0c8..059445f7d 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -25,6 +25,8 @@
#include "common/math_util.h"
#include "common/vector_math.h"
+#include "core/settings.h"
+
#include "video_core/pica.h"
#include "video_core/renderer_base.h"
#include "video_core/utils.h"
@@ -45,8 +47,10 @@ void DebugContext::OnEvent(Event event, void* data) {
{
std::unique_lock<std::mutex> lock(breakpoint_mutex);
- // Commit the hardware renderer's framebuffer so it will show on debug widgets
- VideoCore::g_renderer->hw_rasterizer->CommitFramebuffer();
+ if (Settings::values.use_hw_renderer) {
+ // Commit the hardware renderer's framebuffer so it will show on debug widgets
+ VideoCore::g_renderer->hw_rasterizer->CommitFramebuffer();
+ }
// TODO: Should stop the CPU thread here once we multithread emulation.
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index b83798b0f..4a159da8e 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -7,6 +7,7 @@
#include "common/color.h"
#include "common/common_types.h"
#include "common/math_util.h"
+#include "common/microprofile.h"
#include "common/profiler.h"
#include "core/hw/gpu.h"
@@ -267,6 +268,7 @@ static int SignedArea (const Math::Vec2<Fix12P4>& vtx1,
};
static Common::Profiling::TimingCategory rasterization_category("Rasterization");
+MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240));
/**
* Helper function for ProcessTriangle with the "reversed" flag to allow for implementing
@@ -279,6 +281,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
{
const auto& regs = g_state.regs;
Common::Profiling::ScopeTimer timer(rasterization_category);
+ MICROPROFILE_SCOPE(GPU_Rasterization);
// vertex positions in rasterizer coordinates
static auto FloatToFix = [](float24 flt) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 9f1552adf..f0ccc2397 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -7,6 +7,7 @@
#include "common/color.h"
#include "common/math_util.h"
+#include "common/microprofile.h"
#include "common/profiler.h"
#include "core/hw/gpu.h"
@@ -230,8 +231,8 @@ void RasterizerOpenGL::DrawTriangles() {
u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format)
* regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
- res_cache.NotifyFlush(cur_fb_color_addr, cur_fb_color_size);
- res_cache.NotifyFlush(cur_fb_depth_addr, cur_fb_depth_size);
+ res_cache.NotifyFlush(cur_fb_color_addr, cur_fb_color_size, true);
+ res_cache.NotifyFlush(cur_fb_depth_addr, cur_fb_depth_size, true);
}
void RasterizerOpenGL::CommitFramebuffer() {
@@ -777,12 +778,16 @@ void RasterizerOpenGL::SyncDrawState() {
state.Apply();
}
+MICROPROFILE_DEFINE(OpenGL_FramebufferReload, "OpenGL", "FB Reload", MP_RGB(70, 70, 200));
+
void RasterizerOpenGL::ReloadColorBuffer() {
u8* color_buffer = Memory::GetPhysicalPointer(Pica::g_state.regs.framebuffer.GetColorBufferPhysicalAddress());
if (color_buffer == nullptr)
return;
+ MICROPROFILE_SCOPE(OpenGL_FramebufferReload);
+
u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format);
std::unique_ptr<u8[]> temp_fb_color_buffer(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]);
@@ -822,6 +827,8 @@ void RasterizerOpenGL::ReloadDepthBuffer() {
if (depth_buffer == nullptr)
return;
+ MICROPROFILE_SCOPE(OpenGL_FramebufferReload);
+
u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format);
// OpenGL needs 4 bpp alignment for D24
@@ -868,6 +875,7 @@ void RasterizerOpenGL::ReloadDepthBuffer() {
}
Common::Profiling::TimingCategory buffer_commit_category("Framebuffer Commit");
+MICROPROFILE_DEFINE(OpenGL_FramebufferCommit, "OpenGL", "FB Commit", MP_RGB(70, 70, 200));
void RasterizerOpenGL::CommitColorBuffer() {
if (last_fb_color_addr != 0) {
@@ -875,6 +883,7 @@ void RasterizerOpenGL::CommitColorBuffer() {
if (color_buffer != nullptr) {
Common::Profiling::ScopeTimer timer(buffer_commit_category);
+ MICROPROFILE_SCOPE(OpenGL_FramebufferCommit);
u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format);
@@ -911,6 +920,7 @@ void RasterizerOpenGL::CommitDepthBuffer() {
if (depth_buffer != nullptr) {
Common::Profiling::ScopeTimer timer(buffer_commit_category);
+ MICROPROFILE_SCOPE(OpenGL_FramebufferCommit);
u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 70f0ba5f1..1e38c2e6d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -2,8 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/hash.h"
#include "common/make_unique.h"
#include "common/math_util.h"
+#include "common/microprofile.h"
#include "common/vector_math.h"
#include "core/memory.h"
@@ -16,15 +18,18 @@ RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
FullFlush();
}
+MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
+
void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) {
PAddr texture_addr = config.config.GetPhysicalAddress();
-
const auto cached_texture = texture_cache.find(texture_addr);
if (cached_texture != texture_cache.end()) {
state.texture_units[texture_unit].texture_2d = cached_texture->second->texture.handle;
state.Apply();
} else {
+ MICROPROFILE_SCOPE(OpenGL_TextureUpload);
+
std::unique_ptr<CachedTexture> new_texture = Common::make_unique<CachedTexture>();
new_texture->texture.Create();
@@ -46,12 +51,14 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
}
const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format);
+ u8* texture_src_data = Memory::GetPhysicalPointer(texture_addr);
new_texture->width = info.width;
new_texture->height = info.height;
- new_texture->size = info.width * info.height * Pica::Regs::NibblesPerPixel(info.format);
+ new_texture->size = info.stride * info.height;
+ new_texture->addr = texture_addr;
+ new_texture->hash = Common::ComputeHash64(texture_src_data, new_texture->size);
- u8* texture_src_data = Memory::GetPhysicalPointer(texture_addr);
std::unique_ptr<Math::Vec4<u8>[]> temp_texture_buffer_rgba(new Math::Vec4<u8>[info.width * info.height]);
for (int y = 0; y < info.height; ++y) {
@@ -66,12 +73,18 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
}
}
-void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size) {
+void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash) {
// Flush any texture that falls in the flushed region
// TODO: Optimize by also inserting upper bound (addr + size) of each texture into the same map and also narrow using lower_bound
auto cache_upper_bound = texture_cache.upper_bound(addr + size);
+
for (auto it = texture_cache.begin(); it != cache_upper_bound;) {
- if (MathUtil::IntervalsIntersect(addr, size, it->first, it->second->size)) {
+ const auto& info = *it->second;
+
+ // Flush the texture only if the memory region intersects and a change is detected
+ if (MathUtil::IntervalsIntersect(addr, size, info.addr, info.size) &&
+ (ignore_hash || info.hash != Common::ComputeHash64(Memory::GetPhysicalPointer(info.addr), info.size))) {
+
it = texture_cache.erase(it);
} else {
++it;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 96f3a925c..d8f9edf59 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -19,7 +19,7 @@ public:
void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config);
/// Flush any cached resource that touches the flushed region
- void NotifyFlush(PAddr addr, u32 size);
+ void NotifyFlush(PAddr addr, u32 size, bool ignore_hash = false);
/// Flush all cached OpenGL resources tracked by this cache manager
void FullFlush();
@@ -30,6 +30,8 @@ private:
GLuint width;
GLuint height;
u32 size;
+ u64 hash;
+ PAddr addr;
};
std::map<PAddr, std::unique_ptr<CachedTexture>> texture_cache;
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index 4e9836c80..f89117521 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -9,6 +9,7 @@
#include "common/hash.h"
#include "common/make_unique.h"
+#include "common/microprofile.h"
#include "common/profiler.h"
#include "video_core/debug_utils/debug_utils.h"
@@ -51,15 +52,19 @@ void Setup(UnitState<false>& state) {
}
void Shutdown() {
+#ifdef ARCHITECTURE_x86_64
shader_map.clear();
+#endif // ARCHITECTURE_x86_64
}
static Common::Profiling::TimingCategory shader_category("Vertex Shader");
+MICROPROFILE_DEFINE(GPU_VertexShader, "GPU", "Vertex Shader", MP_RGB(50, 50, 240));
OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attributes) {
auto& config = g_state.regs.vs;
Common::Profiling::ScopeTimer timer(shader_category);
+ MICROPROFILE_SCOPE(GPU_VertexShader);
state.program_counter = config.main_offset;
state.debug.max_offset = 0;
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp
index c8a669b51..d3cfe109e 100644
--- a/src/video_core/shader/shader_jit_x64.cpp
+++ b/src/video_core/shader/shader_jit_x64.cpp
@@ -493,8 +493,8 @@ void JitCompiler::Compile_MOVA(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
- // Convert floats to integers (only care about X and Y components)
- CVTPS2DQ(SRC1, R(SRC1));
+ // Convert floats to integers using truncation (only care about X and Y components)
+ CVTTPS2DQ(SRC1, R(SRC1));
// Get result
MOVQ_xmm(R(RAX), SRC1);
@@ -768,12 +768,12 @@ CompiledShader* JitCompiler::Compile() {
// Used to set a register to one
static const __m128 one = { 1.f, 1.f, 1.f, 1.f };
MOV(PTRBITS, R(RAX), ImmPtr(&one));
- MOVAPS(ONE, MDisp(RAX, 0));
+ MOVAPS(ONE, MatR(RAX));
// Used to negate registers
static const __m128 neg = { -0.f, -0.f, -0.f, -0.f };
MOV(PTRBITS, R(RAX), ImmPtr(&neg));
- MOVAPS(NEGBIT, MDisp(RAX, 0));
+ MOVAPS(NEGBIT, MatR(RAX));
looping = false;