diff options
Diffstat (limited to 'src')
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; |