summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/citra/citra.cpp38
-rw-r--r--src/citra_qt/CMakeLists.txt15
-rw-r--r--src/citra_qt/config.cpp81
-rw-r--r--src/citra_qt/configure.ui97
-rw-r--r--src/citra_qt/configure_debug.cpp31
-rw-r--r--src/citra_qt/configure_debug.h29
-rw-r--r--src/citra_qt/configure_debug.ui102
-rw-r--r--src/citra_qt/configure_dialog.cpp29
-rw-r--r--src/citra_qt/configure_dialog.h29
-rw-r--r--src/citra_qt/configure_general.cpp37
-rw-r--r--src/citra_qt/configure_general.h29
-rw-r--r--src/citra_qt/configure_general.ui166
-rw-r--r--src/citra_qt/game_list.cpp13
-rw-r--r--src/citra_qt/game_list.h4
-rw-r--r--src/citra_qt/hotkeys.cpp54
-rw-r--r--src/citra_qt/hotkeys.h8
-rw-r--r--src/citra_qt/hotkeys.ui47
-rw-r--r--src/citra_qt/main.cpp164
-rw-r--r--src/citra_qt/main.h8
-rw-r--r--src/citra_qt/main.ui51
-rw-r--r--src/citra_qt/ui_settings.cpp11
-rw-r--r--src/citra_qt/ui_settings.h47
-rw-r--r--src/common/file_util.cpp58
-rw-r--r--src/common/file_util.h35
-rw-r--r--src/common/thread.h46
-rw-r--r--src/common/x64/emitter.cpp28
-rw-r--r--src/common/x64/emitter.h2
-rw-r--r--src/core/hle/config_mem.cpp7
-rw-r--r--src/core/hle/hle.cpp2
-rw-r--r--src/core/hle/service/soc_u.cpp100
-rw-r--r--src/core/hle/shared_page.cpp3
-rw-r--r--src/core/hle/shared_page.h6
-rw-r--r--src/core/hw/y2r.cpp2
-rw-r--r--src/core/loader/3dsx.cpp6
-rw-r--r--src/core/loader/ncch.cpp4
-rw-r--r--src/core/settings.cpp14
-rw-r--r--src/core/settings.h2
-rw-r--r--src/video_core/command_processor.cpp23
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp48
-rw-r--r--src/video_core/debug_utils/debug_utils.h21
-rw-r--r--src/video_core/primitive_assembly.cpp2
-rw-r--r--src/video_core/rasterizer.cpp99
-rw-r--r--src/video_core/shader/shader.cpp34
-rw-r--r--src/video_core/shader/shader.h3
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp302
-rw-r--r--src/video_core/shader/shader_jit_x64.h58
46 files changed, 1326 insertions, 669 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 3a1fbe3f7..b4501eb2e 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -20,6 +20,7 @@
#include "common/logging/log.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
+#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "core/settings.h"
@@ -34,11 +35,17 @@
#include "video_core/video_core.h"
-static void PrintHelp()
+static void PrintHelp(const char *argv0)
{
- std::cout << "Usage: citra [options] <filename>" << std::endl;
- std::cout << "--help, -h Display this information" << std::endl;
- std::cout << "--gdbport, -g number Enable gdb stub on port number" << std::endl;
+ std::cout << "Usage: " << argv0 << " [options] <filename>\n"
+ "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
+ "-h, --help Display this help and exit\n"
+ "-v, --version Output version information and exit\n";
+}
+
+static void PrintVersion()
+{
+ std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
}
/// Application entry point
@@ -51,18 +58,16 @@ int main(int argc, char **argv) {
std::string boot_filename;
static struct option long_options[] = {
- { "help", no_argument, 0, 'h' },
{ "gdbport", required_argument, 0, 'g' },
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'v' },
{ 0, 0, 0, 0 }
};
while (optind < argc) {
- char arg = getopt_long(argc, argv, ":hg:", long_options, &option_index);
+ char arg = getopt_long(argc, argv, "g:hv", long_options, &option_index);
if (arg != -1) {
switch (arg) {
- case 'h':
- PrintHelp();
- return 0;
case 'g':
errno = 0;
gdb_port = strtoul(optarg, &endarg, 0);
@@ -73,6 +78,12 @@ int main(int argc, char **argv) {
exit(1);
}
break;
+ case 'h':
+ PrintHelp(argv[0]);
+ return 0;
+ case 'v':
+ PrintVersion();
+ return 0;
}
} else {
boot_filename = argv[optind];
@@ -93,14 +104,13 @@ int main(int argc, char **argv) {
log_filter.ParseFilterString(Settings::values.log_filter);
- GDBStub::ToggleServer(use_gdbstub);
- GDBStub::SetServerPort(gdb_port);
+ // Apply the command line arguments
+ Settings::values.gdbstub_port = gdb_port;
+ Settings::values.use_gdbstub = use_gdbstub;
+ Settings::Apply();
std::unique_ptr<EmuWindow_SDL2> emu_window = std::make_unique<EmuWindow_SDL2>();
- VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer;
- VideoCore::g_shader_jit_enabled = Settings::values.use_shader_jit;
-
System::Init(emu_window.get());
SCOPE_EXIT({ System::Shutdown(); });
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 9b3eb2cd6..6660d9879 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -17,12 +17,16 @@ set(SRCS
debugger/profiler.cpp
debugger/ramview.cpp
debugger/registers.cpp
- game_list.cpp
util/spinbox.cpp
util/util.cpp
bootmanager.cpp
+ configure_debug.cpp
+ configure_dialog.cpp
+ configure_general.cpp
+ game_list.cpp
hotkeys.cpp
main.cpp
+ ui_settings.cpp
citra-qt.rc
Info.plist
)
@@ -44,12 +48,16 @@ set(HEADERS
debugger/profiler.h
debugger/ramview.h
debugger/registers.h
- game_list.h
util/spinbox.h
util/util.h
bootmanager.h
+ configure_debug.h
+ configure_dialog.h
+ configure_general.h
+ game_list.h
hotkeys.h
main.h
+ ui_settings.h
version.h
)
@@ -59,6 +67,9 @@ set(UIS
debugger/disassembler.ui
debugger/profiler.ui
debugger/registers.ui
+ configure.ui
+ configure_debug.ui
+ configure_general.ui
hotkeys.ui
main.ui
)
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 8e247ff5c..e363be38a 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -7,12 +7,12 @@
#include <QStringList>
#include "citra_qt/config.h"
+#include "citra_qt/ui_settings.h"
#include "common/file_util.h"
#include "core/settings.h"
Config::Config() {
-
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini";
FileUtil::CreateFullPath(qt_config_loc);
@@ -67,6 +67,51 @@ void Config::ReadValues() {
Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
qt_config->endGroup();
+
+ qt_config->beginGroup("UI");
+
+ qt_config->beginGroup("UILayout");
+ UISettings::values.geometry = qt_config->value("geometry").toByteArray();
+ UISettings::values.state = qt_config->value("state").toByteArray();
+ UISettings::values.renderwindow_geometry = qt_config->value("geometryRenderWindow").toByteArray();
+ UISettings::values.gamelist_header_state = qt_config->value("gameListHeaderState").toByteArray();
+ UISettings::values.microprofile_geometry = qt_config->value("microProfileDialogGeometry").toByteArray();
+ UISettings::values.microprofile_visible = qt_config->value("microProfileDialogVisible", false).toBool();
+ qt_config->endGroup();
+
+ qt_config->beginGroup("Paths");
+ UISettings::values.roms_path = qt_config->value("romsPath").toString();
+ UISettings::values.symbols_path = qt_config->value("symbolsPath").toString();
+ UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString();
+ UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool();
+ UISettings::values.recent_files = qt_config->value("recentFiles").toStringList();
+ qt_config->endGroup();
+
+ qt_config->beginGroup("Shortcuts");
+ QStringList groups = qt_config->childGroups();
+ for (auto group : groups) {
+ qt_config->beginGroup(group);
+
+ QStringList hotkeys = qt_config->childGroups();
+ for (auto hotkey : hotkeys) {
+ qt_config->beginGroup(hotkey);
+ UISettings::values.shortcuts.emplace_back(
+ UISettings::Shortcut(group + "/" + hotkey,
+ UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(),
+ qt_config->value("Context").toInt())));
+ qt_config->endGroup();
+ }
+
+ qt_config->endGroup();
+ }
+ qt_config->endGroup();
+
+ UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
+ UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
+ UISettings::values.confirm_before_closing = qt_config->value("confirmClose",true).toBool();
+ UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
+
+ qt_config->endGroup();
}
void Config::SaveValues() {
@@ -107,10 +152,44 @@ void Config::SaveValues() {
qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
qt_config->endGroup();
+
+ qt_config->beginGroup("UI");
+
+ qt_config->beginGroup("UILayout");
+ qt_config->setValue("geometry", UISettings::values.geometry);
+ qt_config->setValue("state", UISettings::values.state);
+ qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry);
+ qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state);
+ qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry);
+ qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible);
+ qt_config->endGroup();
+
+ qt_config->beginGroup("Paths");
+ qt_config->setValue("romsPath", UISettings::values.roms_path);
+ qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
+ qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
+ qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
+ qt_config->setValue("recentFiles", UISettings::values.recent_files);
+ qt_config->endGroup();
+
+ qt_config->beginGroup("Shortcuts");
+ for (auto shortcut : UISettings::values.shortcuts ) {
+ qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first);
+ qt_config->setValue(shortcut.first + "/Context", shortcut.second.second);
+ }
+ qt_config->endGroup();
+
+ qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode);
+ qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar);
+ qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
+ qt_config->setValue("firstStart", UISettings::values.first_start);
+
+ qt_config->endGroup();
}
void Config::Reload() {
ReadValues();
+ Settings::Apply();
}
void Config::Save() {
diff --git a/src/citra_qt/configure.ui b/src/citra_qt/configure.ui
new file mode 100644
index 000000000..6ae056ff9
--- /dev/null
+++ b/src/citra_qt/configure.ui
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureDialog</class>
+ <widget class="QDialog" name="ConfigureDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>441</width>
+ <height>501</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Citra Configuration</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="ConfigureGeneral" name="generalTab">
+ <attribute name="title">
+ <string>General</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="inputTab">
+ <attribute name="title">
+ <string>Input</string>
+ </attribute>
+ </widget>
+ <widget class="ConfigureDebug" name="debugTab">
+ <attribute name="title">
+ <string>Debug</string>
+ </attribute>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ConfigureGeneral</class>
+ <extends>QWidget</extends>
+ <header>configure_general.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>ConfigureDebug</class>
+ <extends>QWidget</extends>
+ <header>configure_debug.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>220</x>
+ <y>380</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>220</x>
+ <y>200</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>220</x>
+ <y>380</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>220</x>
+ <y>200</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/citra_qt/configure_debug.cpp b/src/citra_qt/configure_debug.cpp
new file mode 100644
index 000000000..dc3d7b906
--- /dev/null
+++ b/src/citra_qt/configure_debug.cpp
@@ -0,0 +1,31 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citra_qt/configure_debug.h"
+#include "ui_configure_debug.h"
+
+#include "core/settings.h"
+
+ConfigureDebug::ConfigureDebug(QWidget *parent) :
+ QWidget(parent),
+ ui(new Ui::ConfigureDebug)
+{
+ ui->setupUi(this);
+ this->setConfiguration();
+}
+
+ConfigureDebug::~ConfigureDebug() {
+}
+
+void ConfigureDebug::setConfiguration() {
+ ui->toogle_gdbstub->setChecked(Settings::values.use_gdbstub);
+ ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
+ ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
+}
+
+void ConfigureDebug::applyConfiguration() {
+ Settings::values.use_gdbstub = ui->toogle_gdbstub->isChecked();
+ Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
+ Settings::Apply();
+}
diff --git a/src/citra_qt/configure_debug.h b/src/citra_qt/configure_debug.h
new file mode 100644
index 000000000..ab58ebbdc
--- /dev/null
+++ b/src/citra_qt/configure_debug.h
@@ -0,0 +1,29 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+
+namespace Ui {
+class ConfigureDebug;
+}
+
+class ConfigureDebug : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ConfigureDebug(QWidget *parent = nullptr);
+ ~ConfigureDebug();
+
+ void applyConfiguration();
+
+private:
+ void setConfiguration();
+
+private:
+ std::unique_ptr<Ui::ConfigureDebug> ui;
+};
diff --git a/src/citra_qt/configure_debug.ui b/src/citra_qt/configure_debug.ui
new file mode 100644
index 000000000..3ba7f44da
--- /dev/null
+++ b/src/citra_qt/configure_debug.ui
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureDebug</class>
+ <widget class="QWidget" name="ConfigureDebug">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>GDB</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QCheckBox" name="toogle_gdbstub">
+ <property name="text">
+ <string>Enable GDB Stub</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="gdbport_spinbox">
+ <property name="maximum">
+ <number>65536</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>toogle_gdbstub</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>gdbport_spinbox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>84</x>
+ <y>157</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>342</x>
+ <y>158</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/citra_qt/configure_dialog.cpp b/src/citra_qt/configure_dialog.cpp
new file mode 100644
index 000000000..87c26c715
--- /dev/null
+++ b/src/citra_qt/configure_dialog.cpp
@@ -0,0 +1,29 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citra_qt/config.h"
+#include "citra_qt/configure_dialog.h"
+#include "ui_configure.h"
+
+
+#include "core/settings.h"
+
+ConfigureDialog::ConfigureDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::ConfigureDialog)
+{
+ ui->setupUi(this);
+ this->setConfiguration();
+}
+
+ConfigureDialog::~ConfigureDialog() {
+}
+
+void ConfigureDialog::setConfiguration() {
+}
+
+void ConfigureDialog::applyConfiguration() {
+ ui->generalTab->applyConfiguration();
+ ui->debugTab->applyConfiguration();
+}
diff --git a/src/citra_qt/configure_dialog.h b/src/citra_qt/configure_dialog.h
new file mode 100644
index 000000000..89020eeb4
--- /dev/null
+++ b/src/citra_qt/configure_dialog.h
@@ -0,0 +1,29 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QDialog>
+
+namespace Ui {
+class ConfigureDialog;
+}
+
+class ConfigureDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ConfigureDialog(QWidget *parent = nullptr);
+ ~ConfigureDialog();
+
+ void applyConfiguration();
+
+private:
+ void setConfiguration();
+
+private:
+ std::unique_ptr<Ui::ConfigureDialog> ui;
+};
diff --git a/src/citra_qt/configure_general.cpp b/src/citra_qt/configure_general.cpp
new file mode 100644
index 000000000..a27d0d26c
--- /dev/null
+++ b/src/citra_qt/configure_general.cpp
@@ -0,0 +1,37 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "citra_qt/configure_general.h"
+#include "citra_qt/ui_settings.h"
+#include "ui_configure_general.h"
+
+#include "core/settings.h"
+
+ConfigureGeneral::ConfigureGeneral(QWidget *parent) :
+ QWidget(parent),
+ ui(new Ui::ConfigureGeneral)
+{
+ ui->setupUi(this);
+ this->setConfiguration();
+}
+
+ConfigureGeneral::~ConfigureGeneral() {
+}
+
+void ConfigureGeneral::setConfiguration() {
+ ui->toogle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
+ ui->toogle_check_exit->setChecked(UISettings::values.confirm_before_closing);
+ ui->region_combobox->setCurrentIndex(Settings::values.region_value);
+ ui->toogle_hw_renderer->setChecked(Settings::values.use_hw_renderer);
+ ui->toogle_shader_jit->setChecked(Settings::values.use_shader_jit);
+}
+
+void ConfigureGeneral::applyConfiguration() {
+ UISettings::values.gamedir_deepscan = ui->toogle_deepscan->isChecked();
+ UISettings::values.confirm_before_closing = ui->toogle_check_exit->isChecked();
+ Settings::values.region_value = ui->region_combobox->currentIndex();
+ Settings::values.use_hw_renderer = ui->toogle_hw_renderer->isChecked();
+ Settings::values.use_shader_jit = ui->toogle_shader_jit->isChecked();
+ Settings::Apply();
+}
diff --git a/src/citra_qt/configure_general.h b/src/citra_qt/configure_general.h
new file mode 100644
index 000000000..a6c68e62d
--- /dev/null
+++ b/src/citra_qt/configure_general.h
@@ -0,0 +1,29 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+
+namespace Ui {
+class ConfigureGeneral;
+}
+
+class ConfigureGeneral : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ConfigureGeneral(QWidget *parent = nullptr);
+ ~ConfigureGeneral();
+
+ void applyConfiguration();
+
+private:
+ void setConfiguration();
+
+private:
+ std::unique_ptr<Ui::ConfigureGeneral> ui;
+};
diff --git a/src/citra_qt/configure_general.ui b/src/citra_qt/configure_general.ui
new file mode 100644
index 000000000..47184c5c6
--- /dev/null
+++ b/src/citra_qt/configure_general.ui
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureGeneral</class>
+ <widget class="QWidget" name="ConfigureGeneral">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>300</width>
+ <height>377</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>General</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="toogle_deepscan">
+ <property name="text">
+ <string>Recursive scan for game folder</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="toogle_check_exit">
+ <property name="text">
+ <string>Confirm exit while emulation is running</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Emulation</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Region:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="region_combobox">
+ <item>
+ <property name="text">
+ <string notr="true">JPN</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">USA</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">EUR</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">AUS</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">CHN</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">KOR</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">TWN</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Performance</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QCheckBox" name="toogle_hw_renderer">
+ <property name="text">
+ <string>Enable hardware renderer</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="toogle_shader_jit">
+ <property name="text">
+ <string>Enable shader JIT</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Hotkeys</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="GHotkeysDialog" name="widget" native="true"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>GHotkeysDialog</class>
+ <extends>QWidget</extends>
+ <header>hotkeys.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index ffcab1f03..d14532102 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -8,6 +8,7 @@
#include "game_list.h"
#include "game_list_p.h"
+#include "ui_settings.h"
#include "core/loader/loader.h"
@@ -100,19 +101,15 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
current_worker = std::move(worker);
}
-void GameList::SaveInterfaceLayout(QSettings& settings)
+void GameList::SaveInterfaceLayout()
{
- settings.beginGroup("UILayout");
- settings.setValue("gameListHeaderState", tree_view->header()->saveState());
- settings.endGroup();
+ UISettings::values.gamelist_header_state = tree_view->header()->saveState();
}
-void GameList::LoadInterfaceLayout(QSettings& settings)
+void GameList::LoadInterfaceLayout()
{
auto header = tree_view->header();
- settings.beginGroup("UILayout");
- header->restoreState(settings.value("gameListHeaderState").toByteArray());
- settings.endGroup();
+ header->restoreState(UISettings::values.gamelist_header_state);
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
}
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index 0950d9622..48febdc60 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -31,8 +31,8 @@ public:
void PopulateAsync(const QString& dir_path, bool deep_scan);
- void SaveInterfaceLayout(QSettings& settings);
- void LoadInterfaceLayout(QSettings& settings);
+ void SaveInterfaceLayout();
+ void LoadInterfaceLayout();
public slots:
void AddEntry(QList<QStandardItem*> entry_items);
diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp
index ed6b12fc4..41f95c63d 100644
--- a/src/citra_qt/hotkeys.cpp
+++ b/src/citra_qt/hotkeys.cpp
@@ -4,11 +4,12 @@
#include <map>
+#include <QtGlobal>
#include <QKeySequence>
-#include <QSettings>
#include <QShortcut>
#include "citra_qt/hotkeys.h"
+#include "citra_qt/ui_settings.h"
struct Hotkey
{
@@ -24,54 +25,39 @@ typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
HotkeyGroupMap hotkey_groups;
-void SaveHotkeys(QSettings& settings)
+void SaveHotkeys()
{
- settings.beginGroup("Shortcuts");
-
+ UISettings::values.shortcuts.clear();
for (auto group : hotkey_groups)
{
- settings.beginGroup(group.first);
for (auto hotkey : group.second)
{
- settings.beginGroup(hotkey.first);
- settings.setValue(QString("KeySeq"), hotkey.second.keyseq.toString());
- settings.setValue(QString("Context"), hotkey.second.context);
- settings.endGroup();
+ UISettings::values.shortcuts.emplace_back(
+ UISettings::Shortcut(group.first + "/" + hotkey.first,
+ UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
+ hotkey.second.context)));
}
- settings.endGroup();
}
- settings.endGroup();
}
-void LoadHotkeys(QSettings& settings)
+void LoadHotkeys()
{
- settings.beginGroup("Shortcuts");
-
// Make sure NOT to use a reference here because it would become invalid once we call beginGroup()
- QStringList groups = settings.childGroups();
- for (auto group : groups)
+ for (auto shortcut : UISettings::values.shortcuts)
{
- settings.beginGroup(group);
+ QStringList cat = shortcut.first.split("/");
+ Q_ASSERT(cat.size() >= 2);
- QStringList hotkeys = settings.childGroups();
- for (auto hotkey : hotkeys)
+ // RegisterHotkey assigns default keybindings, so use old values as default parameters
+ Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
+ if (!shortcut.second.first.isEmpty())
{
- settings.beginGroup(hotkey);
-
- // RegisterHotkey assigns default keybindings, so use old values as default parameters
- Hotkey& hk = hotkey_groups[group][hotkey];
- hk.keyseq = QKeySequence::fromString(settings.value("KeySeq", hk.keyseq.toString()).toString());
- hk.context = (Qt::ShortcutContext)settings.value("Context", hk.context).toInt();
- if (hk.shortcut)
- hk.shortcut->setKey(hk.keyseq);
-
- settings.endGroup();
+ hk.keyseq = QKeySequence::fromString(shortcut.second.first);
+ hk.context = (Qt::ShortcutContext)shortcut.second.second;
}
-
- settings.endGroup();
+ if (hk.shortcut)
+ hk.shortcut->setKey(hk.keyseq);
}
-
- settings.endGroup();
}
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, Qt::ShortcutContext default_context)
@@ -94,7 +80,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
}
-GHotkeysDialog::GHotkeysDialog(QWidget* parent): QDialog(parent)
+GHotkeysDialog::GHotkeysDialog(QWidget* parent): QWidget(parent)
{
ui.setupUi(this);
diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h
index 2fe635882..38aa5f012 100644
--- a/src/citra_qt/hotkeys.h
+++ b/src/citra_qt/hotkeys.h
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#pragma once
+
#include "ui_hotkeys.h"
class QDialog;
@@ -33,16 +35,16 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
*
* @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a settings group will be created to store the key sequence and the hotkey context.
*/
-void SaveHotkeys(QSettings& settings);
+void SaveHotkeys();
/**
* Loads hotkeys from the settings file.
*
* @note Yet unregistered hotkeys which are present in the settings will automatically be registered.
*/
-void LoadHotkeys(QSettings& settings);
+void LoadHotkeys();
-class GHotkeysDialog : public QDialog
+class GHotkeysDialog : public QWidget
{
Q_OBJECT
diff --git a/src/citra_qt/hotkeys.ui b/src/citra_qt/hotkeys.ui
index 38a9a14d1..050fe064e 100644
--- a/src/citra_qt/hotkeys.ui
+++ b/src/citra_qt/hotkeys.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>hotkeys</class>
- <widget class="QDialog" name="hotkeys">
+ <widget class="QWidget" name="hotkeys">
<property name="geometry">
<rect>
<x>0</x>
@@ -39,51 +39,8 @@
</column>
</widget>
</item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
- </property>
- </widget>
- </item>
</layout>
</widget>
<resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>hotkeys</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>248</x>
- <y>254</y>
- </hint>
- <hint type="destinationlabel">
- <x>157</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>hotkeys</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>316</x>
- <y>260</y>
- </hint>
- <hint type="destinationlabel">
- <x>286</x>
- <y>274</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <connections/>
</ui>
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index ca0ae6f7b..2ca1e51f6 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -14,9 +14,11 @@
#include "citra_qt/bootmanager.h"
#include "citra_qt/config.h"
+#include "citra_qt/configure_dialog.h"
#include "citra_qt/game_list.h"
#include "citra_qt/hotkeys.h"
#include "citra_qt/main.h"
+#include "citra_qt/ui_settings.h"
// Debugger
#include "citra_qt/debugger/callstack.h"
@@ -50,12 +52,10 @@
#include "video_core/video_core.h"
-GMainWindow::GMainWindow() : emu_thread(nullptr)
+GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr)
{
Pica::g_debug_context = Pica::DebugContext::Construct();
- Config config;
-
ui.setupUi(this);
statusBar()->hide();
@@ -133,33 +133,18 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
setGeometry(x, y, w, h);
// Restore UI state
- QSettings settings;
-
- settings.beginGroup("UILayout");
- 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());
- settings.endGroup();
-
- game_list->LoadInterfaceLayout(settings);
-
- ui.action_Use_Gdbstub->setChecked(Settings::values.use_gdbstub);
- SetGdbstubEnabled(ui.action_Use_Gdbstub->isChecked());
-
- GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
-
- ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer);
- SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked());
+ restoreGeometry(UISettings::values.geometry);
+ restoreState(UISettings::values.state);
+ render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
+ microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry);
+ microProfileDialog->setVisible(UISettings::values.microprofile_visible);
- ui.action_Use_Shader_JIT->setChecked(Settings::values.use_shader_jit);
- SetShaderJITEnabled(ui.action_Use_Shader_JIT->isChecked());
+ game_list->LoadInterfaceLayout();
- ui.action_Single_Window_Mode->setChecked(settings.value("singleWindowMode", true).toBool());
+ ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
ToggleWindowMode();
- ui.actionDisplay_widget_title_bars->setChecked(settings.value("displayTitleBars", true).toBool());
+ ui.actionDisplay_widget_title_bars->setChecked(UISettings::values.display_titlebar);
OnDisplayTitleBars(ui.actionDisplay_widget_title_bars->isChecked());
// Prepare actions for recent files
@@ -172,21 +157,16 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
}
UpdateRecentFiles();
- confirm_before_closing = settings.value("confirmClose", true).toBool();
-
// Setup connections
- connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)));
- connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()));
+ connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), Qt::DirectConnection);
+ connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure()));
+ connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()),Qt::DirectConnection);
connect(ui.action_Load_Symbol_Map, SIGNAL(triggered()), this, SLOT(OnMenuLoadSymbolMap()));
connect(ui.action_Select_Game_List_Root, SIGNAL(triggered()), this, SLOT(OnMenuSelectGameListRoot()));
connect(ui.action_Start, SIGNAL(triggered()), this, SLOT(OnStartGame()));
connect(ui.action_Pause, SIGNAL(triggered()), this, SLOT(OnPauseGame()));
connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
- connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool)));
- connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool)));
- connect(ui.action_Use_Gdbstub, SIGNAL(triggered(bool)), this, SLOT(SetGdbstubEnabled(bool)));
connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
- connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*)));
connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
@@ -201,7 +181,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
// Setup hotkeys
RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
RegisterHotkey("Main Window", "Start Emulation");
- LoadHotkeys(settings);
+ LoadHotkeys();
connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile()));
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame()));
@@ -211,7 +191,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
show();
- game_list->PopulateAsync(settings.value("gameListRootDir", ".").toString(), settings.value("gameListDeepScan", false).toBool());
+ game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
QStringList args = QApplication::arguments();
if (args.length() >= 2) {
@@ -375,32 +355,24 @@ void GMainWindow::ShutdownGame() {
emulation_running = false;
}
-void GMainWindow::StoreRecentFile(const std::string& filename)
-{
- QSettings settings;
- QStringList recent_files = settings.value("recentFiles").toStringList();
- recent_files.prepend(QString::fromStdString(filename));
- recent_files.removeDuplicates();
- while (recent_files.size() > max_recent_files_item) {
- recent_files.removeLast();
+void GMainWindow::StoreRecentFile(const std::string& filename) {
+ UISettings::values.recent_files.prepend(QString::fromStdString(filename));
+ UISettings::values.recent_files.removeDuplicates();
+ while (UISettings::values.recent_files.size() > max_recent_files_item) {
+ UISettings::values.recent_files.removeLast();
}
- settings.setValue("recentFiles", recent_files);
-
UpdateRecentFiles();
}
void GMainWindow::UpdateRecentFiles() {
- QSettings settings;
- QStringList recent_files = settings.value("recentFiles").toStringList();
-
- unsigned int num_recent_files = std::min(recent_files.size(), static_cast<int>(max_recent_files_item));
+ unsigned int num_recent_files = std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
for (unsigned int i = 0; i < num_recent_files; i++) {
- QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(recent_files[i]).fileName());
+ QString text = QString("&%1. %2").arg(i + 1).arg(QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text);
- actions_recent_files[i]->setData(recent_files[i]);
- actions_recent_files[i]->setToolTip(recent_files[i]);
+ actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
+ actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]);
actions_recent_files[i]->setVisible(true);
}
@@ -421,36 +393,28 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
}
void GMainWindow::OnMenuLoadFile() {
- QSettings settings;
- QString rom_path = settings.value("romsPath", QString()).toString();
-
- QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), rom_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
+ QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
if (!filename.isEmpty()) {
- settings.setValue("romsPath", QFileInfo(filename).path());
+ UISettings::values.roms_path = QFileInfo(filename).path();
BootGame(filename.toStdString());
}
}
void GMainWindow::OnMenuLoadSymbolMap() {
- QSettings settings;
- QString symbol_path = settings.value("symbolsPath", QString()).toString();
-
- QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), symbol_path, tr("Symbol map (*)"));
+ QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), UISettings::values.symbols_path, tr("Symbol map (*)"));
if (!filename.isEmpty()) {
- settings.setValue("symbolsPath", QFileInfo(filename).path());
+ UISettings::values.symbols_path = QFileInfo(filename).path();
LoadSymbolMap(filename.toStdString());
}
}
void GMainWindow::OnMenuSelectGameListRoot() {
- QSettings settings;
-
QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
if (!dir_path.isEmpty()) {
- settings.setValue("gameListRootDir", dir_path);
- game_list->PopulateAsync(dir_path, settings.value("gameListDeepScan").toBool());
+ UISettings::values.gamedir = dir_path;
+ game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan);
}
}
@@ -466,10 +430,7 @@ void GMainWindow::OnMenuRecentFile() {
// Display an error message and remove the file from the list.
QMessageBox::information(this, tr("File not found"), tr("File \"%1\" not found").arg(filename));
- QSettings settings;
- QStringList recent_files = settings.value("recentFiles").toStringList();
- recent_files.removeOne(filename);
- settings.setValue("recentFiles", recent_files);
+ UISettings::values.recent_files.removeOne(filename);
UpdateRecentFiles();
}
}
@@ -496,31 +457,6 @@ void GMainWindow::OnStopGame() {
ShutdownGame();
}
-void GMainWindow::OnOpenHotkeysDialog() {
- GHotkeysDialog dialog(this);
- dialog.exec();
-}
-
-void GMainWindow::SetHardwareRendererEnabled(bool enabled) {
- VideoCore::g_hw_renderer_enabled = enabled;
-
- Config config;
- Settings::values.use_hw_renderer = enabled;
- config.Save();
-}
-
-void GMainWindow::SetGdbstubEnabled(bool enabled) {
- GDBStub::ToggleServer(enabled);
-}
-
-void GMainWindow::SetShaderJITEnabled(bool enabled) {
- VideoCore::g_shader_jit_enabled = enabled;
-
- Config config;
- Settings::values.use_shader_jit = enabled;
- config.Save();
-}
-
void GMainWindow::ToggleWindowMode() {
if (ui.action_Single_Window_Mode->isChecked()) {
// Render in the main window...
@@ -547,11 +483,17 @@ void GMainWindow::ToggleWindowMode() {
}
void GMainWindow::OnConfigure() {
- //GControllerConfigDialog* dialog = new GControllerConfigDialog(controller_ports, this);
+ ConfigureDialog configureDialog(this);
+ auto result = configureDialog.exec();
+ if (result == QDialog::Accepted)
+ {
+ configureDialog.applyConfiguration();
+ config->Save();
+ }
}
bool GMainWindow::ConfirmClose() {
- if (emu_thread == nullptr || !confirm_before_closing)
+ if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true;
auto answer = QMessageBox::question(this, tr("Citra"),
@@ -566,23 +508,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
return;
}
- // Save window layout
- QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Citra team", "Citra");
+ UISettings::values.geometry = saveGeometry();
+ UISettings::values.state = saveState();
+ UISettings::values.renderwindow_geometry = render_window->saveGeometry();
+ UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry();
+ UISettings::values.microprofile_visible = microProfileDialog->isVisible();
- settings.beginGroup("UILayout");
- 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.endGroup();
+ UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked();
+ UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked();
+ UISettings::values.first_start = false;
- settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked());
- settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked());
- settings.setValue("firstStart", false);
- settings.setValue("confirmClose", confirm_before_closing);
- game_list->SaveInterfaceLayout(settings);
- SaveHotkeys(settings);
+ game_list->SaveInterfaceLayout();
+ SaveHotkeys();
// Shutdown session if the emu thread is active...
if (emu_thread != nullptr)
@@ -607,7 +544,6 @@ int main(int argc, char* argv[]) {
});
// Init settings params
- QSettings::setDefaultFormat(QSettings::IniFormat);
QCoreApplication::setOrganizationName("Citra team");
QCoreApplication::setApplicationName("Citra");
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 6e4e56689..477db5c5c 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -10,6 +10,7 @@
#include "ui_main.h"
+class Config;
class GameList;
class GImageInfo;
class GRenderWindow;
@@ -104,12 +105,8 @@ private slots:
/// Called whenever a user selects the "File->Select Game List Root" menu item
void OnMenuSelectGameListRoot();
void OnMenuRecentFile();
- void OnOpenHotkeysDialog();
void OnConfigure();
void OnDisplayTitleBars(bool);
- void SetHardwareRendererEnabled(bool);
- void SetGdbstubEnabled(bool);
- void SetShaderJITEnabled(bool);
void ToggleWindowMode();
private:
@@ -118,6 +115,8 @@ private:
GRenderWindow* render_window;
GameList* game_list;
+ std::unique_ptr<Config> config;
+
// Whether emulation is currently running in Citra.
bool emulation_running = false;
std::unique_ptr<EmuThread> emu_thread;
@@ -131,7 +130,6 @@ private:
GPUCommandListWidget* graphicsCommandsWidget;
QAction* actions_recent_files[max_recent_files_item];
- bool confirm_before_closing;
};
#endif // _CITRA_QT_MAIN_HXX_
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index 1e8a07cfb..441e0b81e 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -45,7 +45,7 @@
<x>0</x>
<y>0</y>
<width>1081</width>
- <height>22</height>
+ <height>19</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@@ -73,9 +73,6 @@
<addaction name="action_Pause"/>
<addaction name="action_Stop"/>
<addaction name="separator"/>
- <addaction name="action_Use_Hardware_Renderer"/>
- <addaction name="action_Use_Shader_JIT"/>
- <addaction name="action_Use_Gdbstub"/>
<addaction name="action_Configure"/>
</widget>
<widget class="QMenu" name="menu_View">
@@ -84,7 +81,6 @@
</property>
<addaction name="action_Single_Window_Mode"/>
<addaction name="actionDisplay_widget_title_bars"/>
- <addaction name="action_Hotkeys"/>
</widget>
<widget class="QMenu" name="menu_Help">
<property name="title">
@@ -150,35 +146,6 @@
<string>Single Window Mode</string>
</property>
</action>
- <action name="action_Hotkeys">
- <property name="text">
- <string>Configure &amp;Hotkeys ...</string>
- </property>
- </action>
- <action name="action_Use_Hardware_Renderer">
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="text">
- <string>Use Hardware Renderer</string>
- </property>
- </action>
- <action name="action_Use_Shader_JIT">
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="text">
- <string>Use Shader JIT</string>
- </property>
- </action>
- <action name="action_Use_Gdbstub">
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="text">
- <string>Use Gdbstub</string>
- </property>
- </action>
<action name="action_Configure">
<property name="text">
<string>Configure ...</string>
@@ -220,22 +187,6 @@
</hints>
</connection>
<connection>
- <sender>action_Configure</sender>
- <signal>triggered()</signal>
- <receiver>MainWindow</receiver>
- <slot>OnConfigure()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>-1</x>
- <y>-1</y>
- </hint>
- <hint type="destinationlabel">
- <x>540</x>
- <y>364</y>
- </hint>
- </hints>
- </connection>
- <connection>
<sender>actionDisplay_widget_title_bars</sender>
<signal>triggered(bool)</signal>
<receiver>MainWindow</receiver>
diff --git a/src/citra_qt/ui_settings.cpp b/src/citra_qt/ui_settings.cpp
new file mode 100644
index 000000000..5f2215899
--- /dev/null
+++ b/src/citra_qt/ui_settings.cpp
@@ -0,0 +1,11 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "ui_settings.h"
+
+namespace UISettings {
+
+Values values = {};
+
+}
diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h
new file mode 100644
index 000000000..62db4a73e
--- /dev/null
+++ b/src/citra_qt/ui_settings.h
@@ -0,0 +1,47 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QByteArray>
+#include <QStringList>
+#include <QString>
+
+#include <vector>
+
+namespace UISettings {
+
+using ContextualShortcut = std::pair<QString, int> ;
+using Shortcut = std::pair<QString, ContextualShortcut>;
+
+struct Values {
+ QByteArray geometry;
+ QByteArray state;
+
+ QByteArray renderwindow_geometry;
+
+ QByteArray gamelist_header_state;
+
+ QByteArray microprofile_geometry;
+ bool microprofile_visible;
+
+ bool single_window_mode;
+ bool display_titlebar;
+
+ bool confirm_before_closing;
+ bool first_start;
+
+ QString roms_path;
+ QString symbols_path;
+ QString gamedir;
+ bool gamedir_deepscan;
+ QStringList recent_files;
+
+ // Shortcut name <Shortcut, context>
+ std::vector<Shortcut> shortcuts;
+};
+
+extern Values values;
+
+}
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 9ada09f8a..6e2867658 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -69,9 +69,10 @@ static void StripTailDirSlashes(std::string &fname)
{
if (fname.length() > 1)
{
- size_t i = fname.length() - 1;
- while (fname[i] == DIR_SEP_CHR)
- fname[i--] = '\0';
+ size_t i = fname.length();
+ while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
+ --i;
+ fname.resize(i);
}
return;
}
@@ -85,6 +86,10 @@ bool Exists(const std::string &filename)
StripTailDirSlashes(copy);
#ifdef _WIN32
+ // Windows needs a slash to identify a driver root
+ if (copy.size() != 0 && copy.back() == ':')
+ copy += DIR_SEP_CHR;
+
int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
#else
int result = stat64(copy.c_str(), &file_info);
@@ -102,6 +107,10 @@ bool IsDirectory(const std::string &filename)
StripTailDirSlashes(copy);
#ifdef _WIN32
+ // Windows needs a slash to identify a driver root
+ if (copy.size() != 0 && copy.back() == ':')
+ copy += DIR_SEP_CHR;
+
int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
#else
int result = stat64(copy.c_str(), &file_info);
@@ -824,13 +833,12 @@ size_t WriteStringToFile(bool text_file, const std::string &str, const char *fil
size_t ReadFileToString(bool text_file, const char *filename, std::string &str)
{
- FileUtil::IOFile file(filename, text_file ? "r" : "rb");
- auto const f = file.GetHandle();
+ IOFile file(filename, text_file ? "r" : "rb");
- if (!f)
+ if (!file)
return false;
- str.resize(static_cast<u32>(GetSize(f)));
+ str.resize(static_cast<u32>(file.GetSize()));
return file.ReadArray(&str[0], str.size());
}
@@ -877,15 +885,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
}
IOFile::IOFile()
- : m_file(nullptr), m_good(true)
-{}
-
-IOFile::IOFile(std::FILE* file)
- : m_file(file), m_good(true)
-{}
+{
+}
IOFile::IOFile(const std::string& filename, const char openmode[])
- : m_file(nullptr), m_good(true)
{
Open(filename, openmode);
}
@@ -896,7 +899,6 @@ IOFile::~IOFile()
}
IOFile::IOFile(IOFile&& other)
- : m_file(nullptr), m_good(true)
{
Swap(other);
}
@@ -935,26 +937,12 @@ bool IOFile::Close()
return m_good;
}
-std::FILE* IOFile::ReleaseHandle()
-{
- std::FILE* const ret = m_file;
- m_file = nullptr;
- return ret;
-}
-
-void IOFile::SetHandle(std::FILE* file)
-{
- Close();
- Clear();
- m_file = file;
-}
-
-u64 IOFile::GetSize()
+u64 IOFile::GetSize() const
{
if (IsOpen())
return FileUtil::GetSize(m_file);
- else
- return 0;
+
+ return 0;
}
bool IOFile::Seek(s64 off, int origin)
@@ -965,12 +953,12 @@ bool IOFile::Seek(s64 off, int origin)
return m_good;
}
-u64 IOFile::Tell()
+u64 IOFile::Tell() const
{
if (IsOpen())
return ftello(m_file);
- else
- return -1;
+
+ return -1;
}
bool IOFile::Flush()
diff --git a/src/common/file_util.h b/src/common/file_util.h
index a85121aa6..b54a9fb72 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -14,6 +14,10 @@
#include "common/common_types.h"
+#ifdef _MSC_VER
+#include "common/string_util.h"
+#endif
+
// User directory indices for GetUserPath
enum {
D_USER_IDX,
@@ -172,7 +176,6 @@ class IOFile : public NonCopyable
{
public:
IOFile();
- IOFile(std::FILE* file);
IOFile(const std::string& filename, const char openmode[]);
~IOFile();
@@ -188,6 +191,9 @@ public:
template <typename T>
size_t ReadArray(T* data, size_t length)
{
+ static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects");
+ static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects");
+
if (!IsOpen()) {
m_good = false;
return -1;
@@ -203,9 +209,8 @@ public:
template <typename T>
size_t WriteArray(const T* data, size_t length)
{
- static_assert(std::is_standard_layout<T>::value, "Given array does not consist of standard layout objects");
- // TODO: gcc 4.8 does not support is_trivially_copyable, but we really should check for it here.
- //static_assert(std::is_trivially_copyable<T>::value, "Given array does not consist of trivially copyable objects");
+ static_assert(std::is_standard_layout<T>(), "Given array does not consist of standard layout objects");
+ static_assert(std::is_trivially_copyable<T>(), "Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
m_good = false;
@@ -235,32 +240,24 @@ public:
return WriteArray(&object, 1);
}
- bool IsOpen() { return nullptr != m_file; }
+ bool IsOpen() const { return nullptr != m_file; }
// m_good is set to false when a read, write or other function fails
- bool IsGood() { return m_good; }
- operator void*() { return m_good ? m_file : nullptr; }
-
- std::FILE* ReleaseHandle();
-
- std::FILE* GetHandle() { return m_file; }
-
- void SetHandle(std::FILE* file);
+ bool IsGood() const { return m_good; }
+ explicit operator bool() const { return IsGood(); }
bool Seek(s64 off, int origin);
- u64 Tell();
- u64 GetSize();
+ u64 Tell() const;
+ u64 GetSize() const;
bool Resize(u64 size);
bool Flush();
// clear error state
void Clear() { m_good = true; std::clearerr(m_file); }
- std::FILE* m_file;
- bool m_good;
private:
- IOFile(IOFile&);
- IOFile& operator=(IOFile& other);
+ std::FILE* m_file = nullptr;
+ bool m_good = true;
};
} // namespace
diff --git a/src/common/thread.h b/src/common/thread.h
index 8255ee6d3..bbfa8befa 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -30,8 +30,7 @@
# endif
#endif
-namespace Common
-{
+namespace Common {
int CurrentThreadId();
@@ -43,55 +42,55 @@ public:
Event() : is_set(false) {}
void Set() {
- std::lock_guard<std::mutex> lk(m_mutex);
+ std::lock_guard<std::mutex> lk(mutex);
if (!is_set) {
is_set = true;
- m_condvar.notify_one();
+ condvar.notify_one();
}
}
void Wait() {
- std::unique_lock<std::mutex> lk(m_mutex);
- m_condvar.wait(lk, [&]{ return is_set; });
+ std::unique_lock<std::mutex> lk(mutex);
+ condvar.wait(lk, [&]{ return is_set; });
is_set = false;
}
void Reset() {
- std::unique_lock<std::mutex> lk(m_mutex);
+ std::unique_lock<std::mutex> lk(mutex);
// no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration
is_set = false;
}
private:
bool is_set;
- std::condition_variable m_condvar;
- std::mutex m_mutex;
+ std::condition_variable condvar;
+ std::mutex mutex;
};
class Barrier {
public:
- Barrier(size_t count) : m_count(count), m_waiting(0) {}
+ explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {}
/// Blocks until all "count" threads have called Sync()
void Sync() {
- std::unique_lock<std::mutex> lk(m_mutex);
+ std::unique_lock<std::mutex> lk(mutex);
+ const size_t current_generation = generation;
- // TODO: broken when next round of Sync()s
- // is entered before all waiting threads return from the notify_all
-
- if (++m_waiting == m_count) {
- m_waiting = 0;
- m_condvar.notify_all();
+ if (++waiting == count) {
+ generation++;
+ waiting = 0;
+ condvar.notify_all();
} else {
- m_condvar.wait(lk, [&]{ return m_waiting == 0; });
+ condvar.wait(lk, [this, current_generation]{ return current_generation != generation; });
}
}
private:
- std::condition_variable m_condvar;
- std::mutex m_mutex;
- const size_t m_count;
- size_t m_waiting;
+ std::condition_variable condvar;
+ std::mutex mutex;
+ const size_t count;
+ size_t waiting;
+ size_t generation; // Incremented once each time the barrier is used
};
void SleepCurrentThread(int ms);
@@ -100,8 +99,7 @@ void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
// Use this function during a spin-wait to make the current thread
// relax while another thread is working. This may be more efficient
// than using events because event functions use kernel calls.
-inline void YieldCPU()
-{
+inline void YieldCPU() {
std::this_thread::yield();
}
diff --git a/src/common/x64/emitter.cpp b/src/common/x64/emitter.cpp
index 1dcf2416c..5662f7f86 100644
--- a/src/common/x64/emitter.cpp
+++ b/src/common/x64/emitter.cpp
@@ -455,6 +455,18 @@ void XEmitter::CALL(const void* fnptr)
Write32(u32(distance));
}
+FixupBranch XEmitter::CALL()
+{
+ FixupBranch branch;
+ branch.type = 1;
+ branch.ptr = code + 5;
+
+ Write8(0xE8);
+ Write32(0);
+
+ return branch;
+}
+
FixupBranch XEmitter::J(bool force5bytes)
{
FixupBranch branch;
@@ -531,6 +543,22 @@ void XEmitter::SetJumpTarget(const FixupBranch& branch)
}
}
+void XEmitter::SetJumpTarget(const FixupBranch& branch, const u8* target)
+{
+ if (branch.type == 0)
+ {
+ s64 distance = (s64)(target - branch.ptr);
+ ASSERT_MSG(distance >= -0x80 && distance < 0x80, "Jump target too far away, needs force5Bytes = true");
+ branch.ptr[-1] = (u8)(s8)distance;
+ }
+ else if (branch.type == 1)
+ {
+ s64 distance = (s64)(target - branch.ptr);
+ ASSERT_MSG(distance >= -0x80000000LL && distance < 0x80000000LL, "Jump target too far away, needs indirect register");
+ ((s32*)branch.ptr)[-1] = (s32)distance;
+ }
+}
+
//Single byte opcodes
//There is no PUSHAD/POPAD in 64-bit mode.
void XEmitter::INT3() {Write8(0xCC);}
diff --git a/src/common/x64/emitter.h b/src/common/x64/emitter.h
index 7c6548fb5..a33724146 100644
--- a/src/common/x64/emitter.h
+++ b/src/common/x64/emitter.h
@@ -425,12 +425,14 @@ public:
#undef CALL
#endif
void CALL(const void* fnptr);
+ FixupBranch CALL();
void CALLptr(OpArg arg);
FixupBranch J_CC(CCFlags conditionCode, bool force5bytes = false);
void J_CC(CCFlags conditionCode, const u8* addr, bool force5Bytes = false);
void SetJumpTarget(const FixupBranch& branch);
+ void SetJumpTarget(const FixupBranch& branch, const u8* target);
void SETcc(CCFlags flag, OpArg dest);
// Note: CMOV brings small if any benefit on current cpus.
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index b1a72dc0c..ccd73cfcb 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -3,13 +3,6 @@
// Refer to the license.txt file included.
#include <cstring>
-
-#include "common/assert.h"
-#include "common/common_types.h"
-#include "common/common_funcs.h"
-
-#include "core/core.h"
-#include "core/memory.h"
#include "core/hle/config_mem.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index 331b1b22a..e545de3b5 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -8,8 +8,6 @@
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/hle.h"
-#include "core/hle/config_mem.h"
-#include "core/hle/shared_page.h"
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index ff0af8f12..d3e5d4bca 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -151,6 +151,34 @@ static int TranslateError(int error) {
return error;
}
+/// Holds the translation from system network socket options to 3DS network socket options
+/// Note: -1 = No effect/unavailable
+static const std::unordered_map<int, int> sockopt_map = { {
+ { 0x0004, SO_REUSEADDR },
+ { 0x0080, -1 },
+ { 0x0100, -1 },
+ { 0x1001, SO_SNDBUF },
+ { 0x1002, SO_RCVBUF },
+ { 0x1003, -1 },
+#ifdef _WIN32
+ /// Unsupported in WinSock2
+ { 0x1004, -1 },
+#else
+ { 0x1004, SO_RCVLOWAT },
+#endif
+ { 0x1008, SO_TYPE },
+ { 0x1009, SO_ERROR },
+}};
+
+/// Converts a socket option from 3ds-specific to platform-specific
+static int TranslateSockOpt(int console_opt_name) {
+ auto found = sockopt_map.find(console_opt_name);
+ if (found != sockopt_map.end()) {
+ return found->second;
+ }
+ return console_opt_name;
+}
+
/// Holds information about a particular socket
struct SocketHolder {
u32 socket_fd; ///< The socket descriptor
@@ -568,7 +596,7 @@ static void RecvFrom(Service::Interface* self) {
socklen_t src_addr_len = sizeof(src_addr);
int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
- if (buffer_parameters.output_src_address_buffer != 0) {
+ if (ret >= 0 && buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) {
CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer));
*ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
}
@@ -724,6 +752,72 @@ static void ShutdownSockets(Service::Interface* self) {
cmd_buffer[1] = 0;
}
+static void GetSockOpt(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ u32 level = cmd_buffer[2];
+ int optname = TranslateSockOpt(cmd_buffer[3]);
+ socklen_t optlen = (socklen_t)cmd_buffer[4];
+
+ int ret = -1;
+ int err = 0;
+
+ if(optname < 0) {
+#ifdef _WIN32
+ err = WSAEINVAL;
+#else
+ err = EINVAL;
+#endif
+ } else {
+ // 0x100 = static buffer offset (bytes)
+ // + 0x4 = 2nd pointer (u32) position
+ // >> 2 = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
+ char* optval = reinterpret_cast<char *>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+
+ ret = ::getsockopt(socket_handle, level, optname, optval, &optlen);
+ err = 0;
+ if (ret == SOCKET_ERROR_VALUE) {
+ err = TranslateError(GET_ERRNO);
+ }
+ }
+
+ cmd_buffer[0] = IPC::MakeHeader(0x11, 4, 2);
+ cmd_buffer[1] = ret;
+ cmd_buffer[2] = err;
+ cmd_buffer[3] = optlen;
+}
+
+static void SetSockOpt(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 socket_handle = cmd_buffer[1];
+ u32 level = cmd_buffer[2];
+ int optname = TranslateSockOpt(cmd_buffer[3]);
+
+ int ret = -1;
+ int err = 0;
+
+ if(optname < 0) {
+#ifdef _WIN32
+ err = WSAEINVAL;
+#else
+ err = EINVAL;
+#endif
+ } else {
+ socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
+ const char* optval = reinterpret_cast<const char *>(Memory::GetPointer(cmd_buffer[8]));
+
+ ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
+ err = 0;
+ if (ret == SOCKET_ERROR_VALUE) {
+ err = TranslateError(GET_ERRNO);
+ }
+ }
+
+ cmd_buffer[0] = IPC::MakeHeader(0x12, 4, 4);
+ cmd_buffer[1] = ret;
+ cmd_buffer[2] = err;
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010044, InitializeSockets, "InitializeSockets"},
{0x000200C2, Socket, "Socket"},
@@ -741,8 +835,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000E00C2, nullptr, "GetHostByAddr"},
{0x000F0106, nullptr, "GetAddrInfo"},
{0x00100102, nullptr, "GetNameInfo"},
- {0x00110102, nullptr, "GetSockOpt"},
- {0x00120104, nullptr, "SetSockOpt"},
+ {0x00110102, GetSockOpt, "GetSockOpt"},
+ {0x00120104, SetSockOpt, "SetSockOpt"},
{0x001300C2, Fcntl, "Fcntl"},
{0x00140084, Poll, "Poll"},
{0x00150042, nullptr, "SockAtMark"},
diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp
index 50c5bc01b..2a1caeaac 100644
--- a/src/core/hle/shared_page.cpp
+++ b/src/core/hle/shared_page.cpp
@@ -16,6 +16,9 @@ void Init() {
std::memset(&shared_page, 0, sizeof(shared_page));
shared_page.running_hw = 0x1; // product
+
+ // Some games wait until this value becomes 0x1, before asking running_hw
+ shared_page.unknown_value = 0x1;
}
} // namespace
diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h
index 379bb7b63..35a07c685 100644
--- a/src/core/hle/shared_page.h
+++ b/src/core/hle/shared_page.h
@@ -39,12 +39,14 @@ struct SharedPageDef {
DateTime date_time_0; // 20
DateTime date_time_1; // 40
u8 wifi_macaddr[6]; // 60
- u8 wifi_unknown1; // 66
+ u8 wifi_link_level; // 66
u8 wifi_unknown2; // 67
INSERT_PADDING_BYTES(0x80 - 0x68); // 68
float_le sliderstate_3d; // 80
u8 ledstate_3d; // 84
- INSERT_PADDING_BYTES(0xA0 - 0x85); // 85
+ INSERT_PADDING_BYTES(1); // 85
+ u8 unknown_value; // 86
+ INSERT_PADDING_BYTES(0xA0 - 0x87); // 87
u64_le menu_title_id; // A0
u64_le active_menu_title_id; // A8
INSERT_PADDING_BYTES(0x1000 - 0xB0); // B0
diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp
index 48c45564f..083391e83 100644
--- a/src/core/hw/y2r.cpp
+++ b/src/core/hw/y2r.cpp
@@ -261,7 +261,7 @@ void PerformConversion(ConversionConfiguration& cvt) {
ASSERT(cvt.block_alignment != BlockAlignment::Block8x8 || cvt.input_lines % 8 == 0);
// Tiles per row
size_t num_tiles = cvt.input_line_width / 8;
- ASSERT(num_tiles < MAX_TILES);
+ ASSERT(num_tiles <= MAX_TILES);
// Buffer used as a CDMA source/target.
std::unique_ptr<u8[]> data_buffer(new u8[cvt.input_line_width * 8 * 4]);
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 8eed6a50a..5fb3b9e2b 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -10,13 +10,9 @@
#include "core/file_sys/archive_romfs.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
-#include "core/hle/service/fs/archive.h"
-#include "core/loader/elf.h"
-#include "core/loader/ncch.h"
+#include "core/loader/3dsx.h"
#include "core/memory.h"
-#include "3dsx.h"
-
namespace Loader {
/*
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index e63cab33f..a4b47ef8c 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -174,7 +174,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
return ResultStatus::Error;
LOG_DEBUG(Loader, "%d sections:", kMaxSections);
- // Iterate through the ExeFs archive until we find the .code file...
+ // Iterate through the ExeFs archive until we find a section with the specified name...
for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
const auto& section = exefs_header.section[section_number];
@@ -186,7 +186,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
file.Seek(section_offset, SEEK_SET);
- if (is_compressed) {
+ if (strcmp(section.name, ".code") == 0 && is_compressed) {
// Section is compressed, read compressed .code section...
std::unique_ptr<u8[]> temp_buffer;
try {
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 8a14f75aa..1aa26fbd2 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -4,8 +4,22 @@
#include "settings.h"
+#include "core/gdbstub/gdbstub.h"
+
+#include "video_core/video_core.h"
+
namespace Settings {
Values values = {};
+void Apply() {
+
+ GDBStub::SetServerPort(static_cast<u32>(values.gdbstub_port));
+ GDBStub::ToggleServer(values.use_gdbstub);
+
+ VideoCore::g_hw_renderer_enabled = values.use_hw_renderer;
+ VideoCore::g_shader_jit_enabled = values.use_shader_jit;
+
}
+
+} // namespace
diff --git a/src/core/settings.h b/src/core/settings.h
index 97ddcdff9..4933a516d 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -67,4 +67,6 @@ struct Values {
u16 gdbstub_port;
} extern values;
+void Apply();
+
}
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 028b59348..3abe79c09 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -140,7 +140,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
immediate_attribute_id = 0;
Shader::UnitState<false> shader_unit;
- Shader::Setup(shader_unit);
+ Shader::Setup();
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, static_cast<void*>(&immediate_input));
@@ -249,10 +249,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8);
bool index_u16 = index_info.format != 0;
-#if PICA_DUMP_GEOMETRY
- DebugUtils::GeometryDumper geometry_dumper;
- PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex> dumping_primitive_assembler(regs.triangle_topology.Value());
-#endif
PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler;
if (g_debug_context) {
@@ -304,7 +300,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
vertex_cache_ids.fill(-1);
Shader::UnitState<false> shader_unit;
- Shader::Setup(shader_unit);
+ Shader::Setup();
for (unsigned int index = 0; index < regs.num_vertices; ++index)
{
@@ -388,17 +384,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input);
-#if PICA_DUMP_GEOMETRY
- // NOTE: When dumping geometry, we simply assume that the first input attribute
- // corresponds to the position for now.
- DebugUtils::GeometryDumper::Vertex dumped_vertex = {
- input.attr[0][0].ToFloat32(), input.attr[0][1].ToFloat32(), input.attr[0][2].ToFloat32()
- };
- using namespace std::placeholders;
- dumping_primitive_assembler.SubmitVertex(dumped_vertex,
- std::bind(&DebugUtils::GeometryDumper::AddTriangle,
- &geometry_dumper, _1, _2, _3));
-#endif
// Send to vertex shader
output = Shader::Run(shader_unit, input, attribute_config.GetNumTotalAttributes());
@@ -424,10 +409,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
range.second, range.first);
}
-#if PICA_DUMP_GEOMETRY
- geometry_dumper.Dump();
-#endif
-
break;
}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index bac6d69c7..c3a9c9598 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -85,35 +85,6 @@ std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
namespace DebugUtils {
-void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) {
- vertices.push_back(v0);
- vertices.push_back(v1);
- vertices.push_back(v2);
-
- int num_vertices = (int)vertices.size();
- faces.push_back({{ num_vertices-3, num_vertices-2, num_vertices-1 }});
-}
-
-void GeometryDumper::Dump() {
- static int index = 0;
- std::string filename = std::string("geometry_dump") + std::to_string(++index) + ".obj";
-
- std::ofstream file(filename);
-
- for (const auto& vertex : vertices) {
- file << "v " << vertex.pos[0]
- << " " << vertex.pos[1]
- << " " << vertex.pos[2] << std::endl;
- }
-
- for (const Face& face : faces) {
- file << "f " << 1+face.index[0]
- << " " << 1+face.index[1]
- << " " << 1+face.index[2] << std::endl;
- }
-}
-
-
void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes)
{
struct StuffToWrite {
@@ -315,7 +286,7 @@ void StartPicaTracing()
}
std::lock_guard<std::mutex> lock(pica_trace_mutex);
- pica_trace = std::unique_ptr<PicaTrace>(new PicaTrace);
+ pica_trace = std::make_unique<PicaTrace>();
is_pica_tracing = true;
}
@@ -615,6 +586,21 @@ TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config,
return info;
}
+#ifdef HAVE_PNG
+// Adapter functions to libpng to write/flush to File::IOFile instances.
+static void WriteIOFile(png_structp png_ptr, png_bytep data, png_size_t length) {
+ auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr));
+ if (!fp->WriteBytes(data, length))
+ png_error(png_ptr, "Failed to write to output PNG file.");
+}
+
+static void FlushIOFile(png_structp png_ptr) {
+ auto* fp = static_cast<FileUtil::IOFile*>(png_get_io_ptr(png_ptr));
+ if (!fp->Flush())
+ png_error(png_ptr, "Failed to flush to output PNG file.");
+}
+#endif
+
void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
#ifndef HAVE_PNG
return;
@@ -658,7 +644,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
goto finalise;
}
- png_init_io(png_ptr, fp.GetHandle());
+ png_set_write_fn(png_ptr, static_cast<void*>(&fp), WriteIOFile, FlushIOFile);
// Write header (8 bit color depth)
png_set_IHDR(png_ptr, info_ptr, texture_config.width, texture_config.height,
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index 795160a32..7df941619 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -158,30 +158,9 @@ extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this g
namespace DebugUtils {
-#define PICA_DUMP_GEOMETRY 0
#define PICA_DUMP_TEXTURES 0
#define PICA_LOG_TEV 0
-// Simple utility class for dumping geometry data to an OBJ file
-class GeometryDumper {
-public:
- struct Vertex {
- std::array<float,3> pos;
- };
-
- void AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2);
-
- void Dump();
-
-private:
- struct Face {
- int index[3];
- };
-
- std::vector<Vertex> vertices;
- std::vector<Face> faces;
-};
-
void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes);
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp
index 0061690f1..ff3e2b862 100644
--- a/src/video_core/primitive_assembly.cpp
+++ b/src/video_core/primitive_assembly.cpp
@@ -68,7 +68,5 @@ void PrimitiveAssembler<VertexType>::Reconfigure(Regs::TriangleTopology topology
// explicitly instantiate use cases
template
struct PrimitiveAssembler<Shader::OutputVertex>;
-template
-struct PrimitiveAssembler<DebugUtils::GeometryDumper::Vertex>;
} // namespace
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index 5b9ed7c64..0434ad05a 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -923,92 +923,72 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
if (output_merger.alphablend_enable) {
auto params = output_merger.alpha_blending;
- auto LookupFactorRGB = [&](Regs::BlendFactor factor) -> Math::Vec3<u8> {
+ auto LookupFactor = [&](unsigned channel, Regs::BlendFactor factor) -> u8 {
+ DEBUG_ASSERT(channel < 4);
+
+ const Math::Vec4<u8> blend_const = {
+ static_cast<u8>(output_merger.blend_const.r),
+ static_cast<u8>(output_merger.blend_const.g),
+ static_cast<u8>(output_merger.blend_const.b),
+ static_cast<u8>(output_merger.blend_const.a)
+ };
+
switch (factor) {
- case Regs::BlendFactor::Zero :
- return Math::Vec3<u8>(0, 0, 0);
+ case Regs::BlendFactor::Zero:
+ return 0;
- case Regs::BlendFactor::One :
- return Math::Vec3<u8>(255, 255, 255);
+ case Regs::BlendFactor::One:
+ return 255;
case Regs::BlendFactor::SourceColor:
- return combiner_output.rgb();
+ return combiner_output[channel];
case Regs::BlendFactor::OneMinusSourceColor:
- return Math::Vec3<u8>(255 - combiner_output.r(), 255 - combiner_output.g(), 255 - combiner_output.b());
+ return 255 - combiner_output[channel];
case Regs::BlendFactor::DestColor:
- return dest.rgb();
+ return dest[channel];
case Regs::BlendFactor::OneMinusDestColor:
- return Math::Vec3<u8>(255 - dest.r(), 255 - dest.g(), 255 - dest.b());
+ return 255 - dest[channel];
case Regs::BlendFactor::SourceAlpha:
- return Math::Vec3<u8>(combiner_output.a(), combiner_output.a(), combiner_output.a());
+ return combiner_output.a();
case Regs::BlendFactor::OneMinusSourceAlpha:
- return Math::Vec3<u8>(255 - combiner_output.a(), 255 - combiner_output.a(), 255 - combiner_output.a());
+ return 255 - combiner_output.a();
case Regs::BlendFactor::DestAlpha:
- return Math::Vec3<u8>(dest.a(), dest.a(), dest.a());
+ return dest.a();
case Regs::BlendFactor::OneMinusDestAlpha:
- return Math::Vec3<u8>(255 - dest.a(), 255 - dest.a(), 255 - dest.a());
+ return 255 - dest.a();
case Regs::BlendFactor::ConstantColor:
- return Math::Vec3<u8>(output_merger.blend_const.r, output_merger.blend_const.g, output_merger.blend_const.b);
+ return blend_const[channel];
case Regs::BlendFactor::OneMinusConstantColor:
- return Math::Vec3<u8>(255 - output_merger.blend_const.r, 255 - output_merger.blend_const.g, 255 - output_merger.blend_const.b);
+ return 255 - blend_const[channel];
case Regs::BlendFactor::ConstantAlpha:
- return Math::Vec3<u8>(output_merger.blend_const.a, output_merger.blend_const.a, output_merger.blend_const.a);
+ return blend_const.a();
case Regs::BlendFactor::OneMinusConstantAlpha:
- return Math::Vec3<u8>(255 - output_merger.blend_const.a, 255 - output_merger.blend_const.a, 255 - output_merger.blend_const.a);
-
- default:
- LOG_CRITICAL(HW_GPU, "Unknown color blend factor %x", factor);
- UNIMPLEMENTED();
- break;
- }
-
- return {};
- };
-
- auto LookupFactorA = [&](Regs::BlendFactor factor) -> u8 {
- switch (factor) {
- case Regs::BlendFactor::Zero:
- return 0;
-
- case Regs::BlendFactor::One:
- return 255;
-
- case Regs::BlendFactor::SourceAlpha:
- return combiner_output.a();
-
- case Regs::BlendFactor::OneMinusSourceAlpha:
- return 255 - combiner_output.a();
+ return 255 - blend_const.a();
- case Regs::BlendFactor::DestAlpha:
- return dest.a();
-
- case Regs::BlendFactor::OneMinusDestAlpha:
- return 255 - dest.a();
-
- case Regs::BlendFactor::ConstantAlpha:
- return output_merger.blend_const.a;
-
- case Regs::BlendFactor::OneMinusConstantAlpha:
- return 255 - output_merger.blend_const.a;
+ case Regs::BlendFactor::SourceAlphaSaturate:
+ // Returns 1.0 for the alpha channel
+ if (channel == 3)
+ return 255;
+ return std::min(combiner_output.a(), static_cast<u8>(255 - dest.a()));
default:
- LOG_CRITICAL(HW_GPU, "Unknown alpha blend factor %x", factor);
+ LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", factor);
UNIMPLEMENTED();
break;
}
- return {};
+ return combiner_output[channel];
};
static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
@@ -1060,10 +1040,15 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
MathUtil::Clamp(result.a(), 0, 255));
};
- auto srcfactor = Math::MakeVec(LookupFactorRGB(params.factor_source_rgb),
- LookupFactorA(params.factor_source_a));
- auto dstfactor = Math::MakeVec(LookupFactorRGB(params.factor_dest_rgb),
- LookupFactorA(params.factor_dest_a));
+ auto srcfactor = Math::MakeVec(LookupFactor(0, params.factor_source_rgb),
+ LookupFactor(1, params.factor_source_rgb),
+ LookupFactor(2, params.factor_source_rgb),
+ LookupFactor(3, params.factor_source_a));
+
+ auto dstfactor = Math::MakeVec(LookupFactor(0, params.factor_dest_rgb),
+ LookupFactor(1, params.factor_dest_rgb),
+ LookupFactor(2, params.factor_dest_rgb),
+ LookupFactor(3, params.factor_dest_a));
blend_output = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb);
blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a();
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index 78d295c76..75301accd 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -28,36 +28,24 @@ namespace Pica {
namespace Shader {
#ifdef ARCHITECTURE_x86_64
-static std::unordered_map<u64, CompiledShader*> shader_map;
-static JitCompiler jit;
-static CompiledShader* jit_shader;
-
-static void ClearCache() {
- shader_map.clear();
- jit.Clear();
- LOG_INFO(HW_GPU, "Shader JIT cache cleared");
-}
+static std::unordered_map<u64, std::unique_ptr<JitShader>> shader_map;
+static const JitShader* jit_shader;
#endif // ARCHITECTURE_x86_64
-void Setup(UnitState<false>& state) {
+void Setup() {
#ifdef ARCHITECTURE_x86_64
if (VideoCore::g_shader_jit_enabled) {
u64 cache_key = (Common::ComputeHash64(&g_state.vs.program_code, sizeof(g_state.vs.program_code)) ^
- Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data)) ^
- g_state.regs.vs.main_offset);
+ Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data)));
auto iter = shader_map.find(cache_key);
if (iter != shader_map.end()) {
- jit_shader = iter->second;
+ jit_shader = iter->second.get();
} else {
- // Check if remaining JIT code space is enough for at least one more (massive) shader
- if (jit.GetSpaceLeft() < jit_shader_size) {
- // If not, clear the cache of all previously compiled shaders
- ClearCache();
- }
-
- jit_shader = jit.Compile();
- shader_map.emplace(cache_key, jit_shader);
+ auto shader = std::make_unique<JitShader>();
+ shader->Compile();
+ jit_shader = shader.get();
+ shader_map[cache_key] = std::move(shader);
}
}
#endif // ARCHITECTURE_x86_64
@@ -65,7 +53,7 @@ void Setup(UnitState<false>& state) {
void Shutdown() {
#ifdef ARCHITECTURE_x86_64
- ClearCache();
+ shader_map.clear();
#endif // ARCHITECTURE_x86_64
}
@@ -109,7 +97,7 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
#ifdef ARCHITECTURE_x86_64
if (VideoCore::g_shader_jit_enabled)
- jit_shader(&state.registers);
+ jit_shader->Run(&state.registers, g_state.regs.vs.main_offset);
else
RunInterpreter(state);
#else
diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h
index 7af8f1fa1..9c5bd97bd 100644
--- a/src/video_core/shader/shader.h
+++ b/src/video_core/shader/shader.h
@@ -339,9 +339,8 @@ struct UnitState {
/**
* Performs any shader unit setup that only needs to happen once per shader (as opposed to once per
* vertex, which would happen within the `Run` function).
- * @param state Shader unit state, must be setup per shader and per shader unit
*/
-void Setup(UnitState<false>& state);
+void Setup();
/// Performs any cleanup when the emulator is shutdown
void Shutdown();
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp
index dffe051ef..b47d3beda 100644
--- a/src/video_core/shader/shader_jit_x64.cpp
+++ b/src/video_core/shader/shader_jit_x64.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <smmintrin.h>
#include "common/x64/abi.h"
@@ -19,73 +20,73 @@ namespace Shader {
using namespace Gen;
-typedef void (JitCompiler::*JitFunction)(Instruction instr);
+typedef void (JitShader::*JitFunction)(Instruction instr);
const JitFunction instr_table[64] = {
- &JitCompiler::Compile_ADD, // add
- &JitCompiler::Compile_DP3, // dp3
- &JitCompiler::Compile_DP4, // dp4
- &JitCompiler::Compile_DPH, // dph
+ &JitShader::Compile_ADD, // add
+ &JitShader::Compile_DP3, // dp3
+ &JitShader::Compile_DP4, // dp4
+ &JitShader::Compile_DPH, // dph
nullptr, // unknown
- &JitCompiler::Compile_EX2, // ex2
- &JitCompiler::Compile_LG2, // lg2
+ &JitShader::Compile_EX2, // ex2
+ &JitShader::Compile_LG2, // lg2
nullptr, // unknown
- &JitCompiler::Compile_MUL, // mul
- &JitCompiler::Compile_SGE, // sge
- &JitCompiler::Compile_SLT, // slt
- &JitCompiler::Compile_FLR, // flr
- &JitCompiler::Compile_MAX, // max
- &JitCompiler::Compile_MIN, // min
- &JitCompiler::Compile_RCP, // rcp
- &JitCompiler::Compile_RSQ, // rsq
+ &JitShader::Compile_MUL, // mul
+ &JitShader::Compile_SGE, // sge
+ &JitShader::Compile_SLT, // slt
+ &JitShader::Compile_FLR, // flr
+ &JitShader::Compile_MAX, // max
+ &JitShader::Compile_MIN, // min
+ &JitShader::Compile_RCP, // rcp
+ &JitShader::Compile_RSQ, // rsq
nullptr, // unknown
nullptr, // unknown
- &JitCompiler::Compile_MOVA, // mova
- &JitCompiler::Compile_MOV, // mov
+ &JitShader::Compile_MOVA, // mova
+ &JitShader::Compile_MOV, // mov
nullptr, // unknown
nullptr, // unknown
nullptr, // unknown
nullptr, // unknown
- &JitCompiler::Compile_DPH, // dphi
+ &JitShader::Compile_DPH, // dphi
nullptr, // unknown
- &JitCompiler::Compile_SGE, // sgei
- &JitCompiler::Compile_SLT, // slti
+ &JitShader::Compile_SGE, // sgei
+ &JitShader::Compile_SLT, // slti
nullptr, // unknown
nullptr, // unknown
nullptr, // unknown
nullptr, // unknown
nullptr, // unknown
- &JitCompiler::Compile_NOP, // nop
- &JitCompiler::Compile_END, // end
+ &JitShader::Compile_NOP, // nop
+ &JitShader::Compile_END, // end
nullptr, // break
- &JitCompiler::Compile_CALL, // call
- &JitCompiler::Compile_CALLC, // callc
- &JitCompiler::Compile_CALLU, // callu
- &JitCompiler::Compile_IF, // ifu
- &JitCompiler::Compile_IF, // ifc
- &JitCompiler::Compile_LOOP, // loop
+ &JitShader::Compile_CALL, // call
+ &JitShader::Compile_CALLC, // callc
+ &JitShader::Compile_CALLU, // callu
+ &JitShader::Compile_IF, // ifu
+ &JitShader::Compile_IF, // ifc
+ &JitShader::Compile_LOOP, // loop
nullptr, // emit
nullptr, // sete
- &JitCompiler::Compile_JMP, // jmpc
- &JitCompiler::Compile_JMP, // jmpu
- &JitCompiler::Compile_CMP, // cmp
- &JitCompiler::Compile_CMP, // cmp
- &JitCompiler::Compile_MAD, // madi
- &JitCompiler::Compile_MAD, // madi
- &JitCompiler::Compile_MAD, // madi
- &JitCompiler::Compile_MAD, // madi
- &JitCompiler::Compile_MAD, // madi
- &JitCompiler::Compile_MAD, // madi
- &JitCompiler::Compile_MAD, // madi
- &JitCompiler::Compile_MAD, // madi
- &JitCompiler::Compile_MAD, // mad
- &JitCompiler::Compile_MAD, // mad
- &JitCompiler::Compile_MAD, // mad
- &JitCompiler::Compile_MAD, // mad
- &JitCompiler::Compile_MAD, // mad
- &JitCompiler::Compile_MAD, // mad
- &JitCompiler::Compile_MAD, // mad
- &JitCompiler::Compile_MAD, // mad
+ &JitShader::Compile_JMP, // jmpc
+ &JitShader::Compile_JMP, // jmpu
+ &JitShader::Compile_CMP, // cmp
+ &JitShader::Compile_CMP, // cmp
+ &JitShader::Compile_MAD, // madi
+ &JitShader::Compile_MAD, // madi
+ &JitShader::Compile_MAD, // madi
+ &JitShader::Compile_MAD, // madi
+ &JitShader::Compile_MAD, // madi
+ &JitShader::Compile_MAD, // madi
+ &JitShader::Compile_MAD, // madi
+ &JitShader::Compile_MAD, // madi
+ &JitShader::Compile_MAD, // mad
+ &JitShader::Compile_MAD, // mad
+ &JitShader::Compile_MAD, // mad
+ &JitShader::Compile_MAD, // mad
+ &JitShader::Compile_MAD, // mad
+ &JitShader::Compile_MAD, // mad
+ &JitShader::Compile_MAD, // mad
+ &JitShader::Compile_MAD, // mad
};
// The following is used to alias some commonly used registers. Generally, RAX-RDX and XMM0-XMM3 can
@@ -138,13 +139,32 @@ static const u8 NO_SRC_REG_SWIZZLE = 0x1b;
static const u8 NO_DEST_REG_MASK = 0xf;
/**
+ * Get the vertex shader instruction for a given offset in the current shader program
+ * @param offset Offset in the current shader program of the instruction
+ * @return Instruction at the specified offset
+ */
+static Instruction GetVertexShaderInstruction(size_t offset) {
+ return { g_state.vs.program_code[offset] };
+}
+
+static void LogCritical(const char* msg) {
+ LOG_CRITICAL(HW_GPU, msg);
+}
+
+void JitShader::Compile_Assert(bool condition, const char* msg) {
+ if (!condition) {
+ ABI_CallFunctionP(reinterpret_cast<const void*>(LogCritical), const_cast<char*>(msg));
+ }
+}
+
+/**
* Loads and swizzles a source register into the specified XMM register.
* @param instr VS instruction, used for determining how to load the source register
* @param src_num Number indicating which source register to load (1 = src1, 2 = src2, 3 = src3)
* @param src_reg SourceRegister object corresponding to the source register to load
* @param dest Destination XMM register to store the loaded, swizzled source register
*/
-void JitCompiler::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, X64Reg dest) {
+void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, X64Reg dest) {
X64Reg src_ptr;
size_t src_offset;
@@ -216,7 +236,7 @@ void JitCompiler::Compile_SwizzleSrc(Instruction instr, unsigned src_num, Source
}
}
-void JitCompiler::Compile_DestEnable(Instruction instr,X64Reg src) {
+void JitShader::Compile_DestEnable(Instruction instr,X64Reg src) {
DestRegister dest;
unsigned operand_desc_id;
if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD ||
@@ -263,7 +283,7 @@ void JitCompiler::Compile_DestEnable(Instruction instr,X64Reg src) {
}
}
-void JitCompiler::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) {
+void JitShader::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) {
MOVAPS(scratch, R(src1));
CMPPS(scratch, R(src2), CMP_ORD);
@@ -276,7 +296,7 @@ void JitCompiler::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::
ANDPS(src1, R(scratch));
}
-void JitCompiler::Compile_EvaluateCondition(Instruction instr) {
+void JitShader::Compile_EvaluateCondition(Instruction instr) {
// Note: NXOR is used below to check for equality
switch (instr.flow_control.op) {
case Instruction::FlowControlType::Or:
@@ -307,23 +327,23 @@ void JitCompiler::Compile_EvaluateCondition(Instruction instr) {
}
}
-void JitCompiler::Compile_UniformCondition(Instruction instr) {
+void JitShader::Compile_UniformCondition(Instruction instr) {
int offset = offsetof(decltype(g_state.vs.uniforms), b) + (instr.flow_control.bool_uniform_id * sizeof(bool));
CMP(sizeof(bool) * 8, MDisp(UNIFORMS, offset), Imm8(0));
}
-BitSet32 JitCompiler::PersistentCallerSavedRegs() {
+BitSet32 JitShader::PersistentCallerSavedRegs() {
return persistent_regs & ABI_ALL_CALLER_SAVED;
}
-void JitCompiler::Compile_ADD(Instruction instr) {
+void JitShader::Compile_ADD(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
ADDPS(SRC1, R(SRC2));
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_DP3(Instruction instr) {
+void JitShader::Compile_DP3(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
@@ -342,7 +362,7 @@ void JitCompiler::Compile_DP3(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_DP4(Instruction instr) {
+void JitShader::Compile_DP4(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
@@ -359,7 +379,7 @@ void JitCompiler::Compile_DP4(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_DPH(Instruction instr) {
+void JitShader::Compile_DPH(Instruction instr) {
if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::DPHI) {
Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
@@ -391,7 +411,7 @@ void JitCompiler::Compile_DPH(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_EX2(Instruction instr) {
+void JitShader::Compile_EX2(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
MOVSS(XMM0, R(SRC1));
@@ -404,7 +424,7 @@ void JitCompiler::Compile_EX2(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_LG2(Instruction instr) {
+void JitShader::Compile_LG2(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
MOVSS(XMM0, R(SRC1));
@@ -417,14 +437,14 @@ void JitCompiler::Compile_LG2(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_MUL(Instruction instr) {
+void JitShader::Compile_MUL(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
Compile_SanitizedMul(SRC1, SRC2, SCRATCH);
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_SGE(Instruction instr) {
+void JitShader::Compile_SGE(Instruction instr) {
if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SGEI) {
Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
@@ -439,7 +459,7 @@ void JitCompiler::Compile_SGE(Instruction instr) {
Compile_DestEnable(instr, SRC2);
}
-void JitCompiler::Compile_SLT(Instruction instr) {
+void JitShader::Compile_SLT(Instruction instr) {
if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::SLTI) {
Compile_SwizzleSrc(instr, 1, instr.common.src1i, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2i, SRC2);
@@ -454,7 +474,7 @@ void JitCompiler::Compile_SLT(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_FLR(Instruction instr) {
+void JitShader::Compile_FLR(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
if (Common::GetCPUCaps().sse4_1) {
@@ -467,7 +487,7 @@ void JitCompiler::Compile_FLR(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_MAX(Instruction instr) {
+void JitShader::Compile_MAX(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
// SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned.
@@ -475,7 +495,7 @@ void JitCompiler::Compile_MAX(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_MIN(Instruction instr) {
+void JitShader::Compile_MIN(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2);
// SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned.
@@ -483,7 +503,7 @@ void JitCompiler::Compile_MIN(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_MOVA(Instruction instr) {
+void JitShader::Compile_MOVA(Instruction instr) {
SwizzlePattern swiz = { g_state.vs.swizzle_data[instr.common.operand_desc_id] };
if (!swiz.DestComponentEnabled(0) && !swiz.DestComponentEnabled(1)) {
@@ -528,12 +548,12 @@ void JitCompiler::Compile_MOVA(Instruction instr) {
}
}
-void JitCompiler::Compile_MOV(Instruction instr) {
+void JitShader::Compile_MOV(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_RCP(Instruction instr) {
+void JitShader::Compile_RCP(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
// TODO(bunnei): RCPSS is a pretty rough approximation, this might cause problems if Pica
@@ -544,7 +564,7 @@ void JitCompiler::Compile_RCP(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_RSQ(Instruction instr) {
+void JitShader::Compile_RSQ(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1);
// TODO(bunnei): RSQRTSS is a pretty rough approximation, this might cause problems if Pica
@@ -555,36 +575,41 @@ void JitCompiler::Compile_RSQ(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_NOP(Instruction instr) {
+void JitShader::Compile_NOP(Instruction instr) {
}
-void JitCompiler::Compile_END(Instruction instr) {
+void JitShader::Compile_END(Instruction instr) {
ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
RET();
}
-void JitCompiler::Compile_CALL(Instruction instr) {
- unsigned offset = instr.flow_control.dest_offset;
- while (offset < (instr.flow_control.dest_offset + instr.flow_control.num_instructions)) {
- Compile_NextInstr(&offset);
- }
+void JitShader::Compile_CALL(Instruction instr) {
+ // Push offset of the return
+ PUSH(64, Imm32(instr.flow_control.dest_offset + instr.flow_control.num_instructions));
+
+ // Call the subroutine
+ FixupBranch b = CALL();
+ fixup_branches.push_back({ b, instr.flow_control.dest_offset });
+
+ // Skip over the return offset that's on the stack
+ ADD(64, R(RSP), Imm32(8));
}
-void JitCompiler::Compile_CALLC(Instruction instr) {
+void JitShader::Compile_CALLC(Instruction instr) {
Compile_EvaluateCondition(instr);
FixupBranch b = J_CC(CC_Z, true);
Compile_CALL(instr);
SetJumpTarget(b);
}
-void JitCompiler::Compile_CALLU(Instruction instr) {
+void JitShader::Compile_CALLU(Instruction instr) {
Compile_UniformCondition(instr);
FixupBranch b = J_CC(CC_Z, true);
Compile_CALL(instr);
SetJumpTarget(b);
}
-void JitCompiler::Compile_CMP(Instruction instr) {
+void JitShader::Compile_CMP(Instruction instr) {
using Op = Instruction::Common::CompareOpType::Op;
Op op_x = instr.common.compare_op.x;
Op op_y = instr.common.compare_op.y;
@@ -627,7 +652,7 @@ void JitCompiler::Compile_CMP(Instruction instr) {
SHR(64, R(COND1), Imm8(63));
}
-void JitCompiler::Compile_MAD(Instruction instr) {
+void JitShader::Compile_MAD(Instruction instr) {
Compile_SwizzleSrc(instr, 1, instr.mad.src1, SRC1);
if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) {
@@ -644,9 +669,8 @@ void JitCompiler::Compile_MAD(Instruction instr) {
Compile_DestEnable(instr, SRC1);
}
-void JitCompiler::Compile_IF(Instruction instr) {
- ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards if-statements (%d -> %d) not supported",
- *offset_ptr, instr.flow_control.dest_offset.Value());
+void JitShader::Compile_IF(Instruction instr) {
+ Compile_Assert(instr.flow_control.dest_offset >= program_counter, "Backwards if-statements not supported");
// Evaluate the "IF" condition
if (instr.opcode.Value() == OpCode::Id::IFU) {
@@ -676,10 +700,9 @@ void JitCompiler::Compile_IF(Instruction instr) {
SetJumpTarget(b2);
}
-void JitCompiler::Compile_LOOP(Instruction instr) {
- ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards loops (%d -> %d) not supported",
- *offset_ptr, instr.flow_control.dest_offset.Value());
- ASSERT_MSG(!looping, "Nested loops not supported");
+void JitShader::Compile_LOOP(Instruction instr) {
+ Compile_Assert(instr.flow_control.dest_offset >= program_counter, "Backwards loops not supported");
+ Compile_Assert(!looping, "Nested loops not supported");
looping = true;
@@ -705,10 +728,7 @@ void JitCompiler::Compile_LOOP(Instruction instr) {
looping = false;
}
-void JitCompiler::Compile_JMP(Instruction instr) {
- ASSERT_MSG(instr.flow_control.dest_offset > *offset_ptr, "Backwards jumps (%d -> %d) not supported",
- *offset_ptr, instr.flow_control.dest_offset.Value());
-
+void JitShader::Compile_JMP(Instruction instr) {
if (instr.opcode.Value() == OpCode::Id::JMPC)
Compile_EvaluateCondition(instr);
else if (instr.opcode.Value() == OpCode::Id::JMPU)
@@ -718,30 +738,38 @@ void JitCompiler::Compile_JMP(Instruction instr) {
bool inverted_condition = (instr.opcode.Value() == OpCode::Id::JMPU) &&
(instr.flow_control.num_instructions & 1);
+
FixupBranch b = J_CC(inverted_condition ? CC_Z : CC_NZ, true);
+ fixup_branches.push_back({ b, instr.flow_control.dest_offset });
+}
- Compile_Block(instr.flow_control.dest_offset);
+void JitShader::Compile_Block(unsigned end) {
+ while (program_counter < end) {
+ Compile_NextInstr();
+ }
+}
+
+void JitShader::Compile_Return() {
+ // Peek return offset on the stack and check if we're at that offset
+ MOV(64, R(RAX), MDisp(RSP, 8));
+ CMP(32, R(RAX), Imm32(program_counter));
+ // If so, jump back to before CALL
+ FixupBranch b = J_CC(CC_NZ, true);
+ RET();
SetJumpTarget(b);
}
-void JitCompiler::Compile_Block(unsigned end) {
- // Save current offset pointer
- unsigned* prev_offset_ptr = offset_ptr;
- unsigned offset = *prev_offset_ptr;
+void JitShader::Compile_NextInstr() {
+ if (std::binary_search(return_offsets.begin(), return_offsets.end(), program_counter)) {
+ Compile_Return();
+ }
- while (offset < end)
- Compile_NextInstr(&offset);
+ ASSERT_MSG(code_ptr[program_counter] == nullptr, "Tried to compile already compiled shader location!");
+ code_ptr[program_counter] = GetCodePtr();
- // Restore current offset pointer
- offset_ptr = prev_offset_ptr;
- *offset_ptr = offset;
-}
+ Instruction instr = GetVertexShaderInstruction(program_counter++);
-void JitCompiler::Compile_NextInstr(unsigned* offset) {
- offset_ptr = offset;
-
- Instruction instr = *(Instruction*)&g_state.vs.program_code[(*offset_ptr)++];
OpCode::Id opcode = instr.opcode.Value();
auto instr_func = instr_table[static_cast<unsigned>(opcode)];
@@ -755,9 +783,35 @@ void JitCompiler::Compile_NextInstr(unsigned* offset) {
}
}
-CompiledShader* JitCompiler::Compile() {
- const u8* start = GetCodePtr();
- unsigned offset = g_state.regs.vs.main_offset;
+void JitShader::FindReturnOffsets() {
+ return_offsets.clear();
+
+ for (size_t offset = 0; offset < g_state.vs.program_code.size(); ++offset) {
+ Instruction instr = GetVertexShaderInstruction(offset);
+
+ switch (instr.opcode.Value()) {
+ case OpCode::Id::CALL:
+ case OpCode::Id::CALLC:
+ case OpCode::Id::CALLU:
+ return_offsets.push_back(instr.flow_control.dest_offset + instr.flow_control.num_instructions);
+ break;
+ }
+ }
+
+ // Sort for efficient binary search later
+ std::sort(return_offsets.begin(), return_offsets.end());
+}
+
+void JitShader::Compile() {
+ // Reset flow control state
+ program = (CompiledShader*)GetCodePtr();
+ program_counter = 0;
+ looping = false;
+ code_ptr.fill(nullptr);
+ fixup_branches.clear();
+
+ // Find all `CALL` instructions and identify return locations
+ FindReturnOffsets();
// The stack pointer is 8 modulo 16 at the entry of a procedure
ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
@@ -780,21 +834,31 @@ CompiledShader* JitCompiler::Compile() {
MOV(PTRBITS, R(RAX), ImmPtr(&neg));
MOVAPS(NEGBIT, MatR(RAX));
- looping = false;
+ // Jump to start of the shader program
+ JMPptr(R(ABI_PARAM2));
+
+ // Compile entire program
+ Compile_Block(static_cast<unsigned>(g_state.vs.program_code.size()));
- while (offset < g_state.vs.program_code.size()) {
- Compile_NextInstr(&offset);
+ // Set the target for any incomplete branches now that the entire shader program has been emitted
+ for (const auto& branch : fixup_branches) {
+ SetJumpTarget(branch.first, code_ptr[branch.second]);
}
- return (CompiledShader*)start;
-}
+ // Free memory that's no longer needed
+ return_offsets.clear();
+ return_offsets.shrink_to_fit();
+ fixup_branches.clear();
+ fixup_branches.shrink_to_fit();
+
+ uintptr_t size = reinterpret_cast<uintptr_t>(GetCodePtr()) - reinterpret_cast<uintptr_t>(program);
+ ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!");
-JitCompiler::JitCompiler() {
- AllocCodeSpace(jit_cache_size);
+ LOG_DEBUG(HW_GPU, "Compiled shader size=%d", size);
}
-void JitCompiler::Clear() {
- ClearCodeSpace();
+JitShader::JitShader() {
+ AllocCodeSpace(MAX_SHADER_SIZE);
}
} // namespace Shader
diff --git a/src/video_core/shader/shader_jit_x64.h b/src/video_core/shader/shader_jit_x64.h
index 5357c964b..cd6280ade 100644
--- a/src/video_core/shader/shader_jit_x64.h
+++ b/src/video_core/shader/shader_jit_x64.h
@@ -4,6 +4,9 @@
#pragma once
+#include <utility>
+#include <vector>
+
#include <nihstro/shader_bytecode.h>
#include "common/x64/emitter.h"
@@ -19,24 +22,22 @@ namespace Pica {
namespace Shader {
-/// Memory needed to be available to compile the next shader (otherwise, clear the cache)
-constexpr size_t jit_shader_size = 1024 * 512;
-/// Memory allocated for the JIT code space cache
-constexpr size_t jit_cache_size = 1024 * 1024 * 8;
-
-using CompiledShader = void(void* registers);
+/// Memory allocated for each compiled shader (64Kb)
+constexpr size_t MAX_SHADER_SIZE = 1024 * 64;
/**
* This class implements the shader JIT compiler. It recompiles a Pica shader program into x86_64
* code that can be executed on the host machine directly.
*/
-class JitCompiler : public Gen::XCodeBlock {
+class JitShader : public Gen::XCodeBlock {
public:
- JitCompiler();
+ JitShader();
- CompiledShader* Compile();
+ void Run(void* registers, unsigned offset) const {
+ program(registers, code_ptr[offset]);
+ }
- void Clear();
+ void Compile();
void Compile_ADD(Instruction instr);
void Compile_DP3(Instruction instr);
@@ -66,8 +67,9 @@ public:
void Compile_MAD(Instruction instr);
private:
+
void Compile_Block(unsigned end);
- void Compile_NextInstr(unsigned* offset);
+ void Compile_NextInstr();
void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, Gen::X64Reg dest);
void Compile_DestEnable(Instruction instr, Gen::X64Reg dest);
@@ -81,13 +83,39 @@ private:
void Compile_EvaluateCondition(Instruction instr);
void Compile_UniformCondition(Instruction instr);
+ /**
+ * Emits the code to conditionally return from a subroutine envoked by the `CALL` instruction.
+ */
+ void Compile_Return();
+
BitSet32 PersistentCallerSavedRegs();
- /// Pointer to the variable that stores the current Pica code offset. Used to handle nested code blocks.
- unsigned* offset_ptr = nullptr;
+ /**
+ * Assertion evaluated at compile-time, but only triggered if executed at runtime.
+ * @param msg Message to be logged if the assertion fails.
+ */
+ void Compile_Assert(bool condition, const char* msg);
+
+ /**
+ * Analyzes the entire shader program for `CALL` instructions before emitting any code,
+ * identifying the locations where a return needs to be inserted.
+ */
+ void FindReturnOffsets();
+
+ /// Mapping of Pica VS instructions to pointers in the emitted code
+ std::array<const u8*, 1024> code_ptr;
+
+ /// Offsets in code where a return needs to be inserted
+ std::vector<unsigned> return_offsets;
+
+ unsigned program_counter = 0; ///< Offset of the next instruction to decode
+ bool looping = false; ///< True if compiling a loop, used to check for nested loops
+
+ /// Branches that need to be fixed up once the entire shader program is compiled
+ std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches;
- /// Set to true if currently in a loop, used to check for the existence of nested loops
- bool looping = false;
+ using CompiledShader = void(void* registers, const u8* start_addr);
+ CompiledShader* program = nullptr;
};
} // Shader