diff options
55 files changed, 1153 insertions, 882 deletions
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index a3983b27e..9814b06dd 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -2,7 +2,8 @@ QToolTip { border: 1px solid #76797C; background-color: #5A7566; color: white; - padding: 0px; /*remove padding, for fix combobox tooltip.*/ + /*remove padding, for fix combobox tooltip.*/ + padding: 0; opacity: 200; } @@ -13,7 +14,7 @@ QWidget { selection-color: #eff0f1; background-clip: border; border-image: none; - border: 0px transparent black; + border: 0; outline: 0; } @@ -27,10 +28,10 @@ QWidget:item:selected { } QCheckBox { - spacing: 5px; + spacing: 6px; outline: none; color: #eff0f1; - margin-bottom: 2px; + margin: 0 2px 1px 0; } QCheckBox:disabled { @@ -163,7 +164,7 @@ QMenuBar::item:selected { } QMenuBar::item:pressed { - border: 1px solid #76797C; + border: 1px solid #18465d; background-color: #3daee9; color: #eff0f1; margin-bottom: -1px; @@ -171,9 +172,9 @@ QMenuBar::item:pressed { } QMenu { - border: 1px solid #76797C; + border: 1px solid #434242; + padding: 2px; color: #eff0f1; - margin: 2px; } QMenu::icon { @@ -190,11 +191,21 @@ QMenu::item:selected { color: #eff0f1; } -QMenu::separator { - height: 2px; - background: #76797C; - margin-left: 10px; - margin-right: 5px; +QMenu::item:disabled { + color: #54575B; +} + +QMenu::item:disabled:hover, +QMenu::item:disabled:selected { + background-color: #393e43; + color: #666; +} + +QMenu::separator, +QMenuBar::separator { + height: 1px; + background-color: #54575B; + margin: 2px 4px 2px 40px; } QMenu::indicator { @@ -203,10 +214,7 @@ QMenu::indicator { height: 18px; } - -/* non-exclusive indicator = check box style indicator - (see QActionGroup::setExclusive) */ - +/* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */ QMenu::indicator:non-exclusive:unchecked { image: url(:/qss_icons/rc/checkbox_unchecked.png); } @@ -223,9 +231,7 @@ QMenu::indicator:non-exclusive:checked:selected { image: url(:/qss_icons/rc/checkbox_checked_disabled.png); } - /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ - QMenu::indicator:exclusive:unchecked { image: url(:/qss_icons/rc/radio_unchecked.png); } @@ -243,12 +249,12 @@ QMenu::indicator:exclusive:checked:selected { } QMenu::right-arrow { - margin: 5px; + margin-right: 10px; image: url(:/qss_icons/rc/right_arrow.png) } QWidget:disabled { - color: #454545; + color: #4f515b; background-color: #31363b; } @@ -259,23 +265,30 @@ QAbstractItemView { border-radius: 2px; } -QWidget:focus, -QMenuBar:focus { +QAbstractItemView:disabled, +QAbstractItemView:read-only { + alternate-background-color: #232629; +} + +QWidget:focus { border: 1px solid #3daee9; } QTabWidget:focus, QCheckBox:focus, QRadioButton:focus, -QSlider:focus { +QSlider:focus, +QTreeView:focus, +QMenu:focus, +QMenuBar:focus, +QTabBar:focus { border: none; } QLineEdit { background-color: #232629; padding: 5px; - border-style: solid; - border: 1px solid #76797C; + border: 1px solid #54575B; border-radius: 2px; color: #eff0f1; } @@ -285,9 +298,10 @@ QAbstractItemView QLineEdit { } QGroupBox { - border: 1px solid #76797C; + border: 1px solid #54575B; border-radius: 2px; - margin-top: 20px; + margin-top: 12px; + padding-top: 2px; } QGroupBox::title { @@ -295,12 +309,12 @@ QGroupBox::title { subcontrol-position: top center; padding-left: 10px; padding-right: 10px; - padding-top: 10px; + padding-top: 2px; } QAbstractScrollArea { border-radius: 2px; - border: 1px solid #76797C; + border: 1px solid #54575B; background-color: transparent; } @@ -319,7 +333,7 @@ QScrollBar::handle:horizontal { } QScrollBar::add-line:horizontal { - margin: 0px 3px 0px 3px; + margin: 0 3px; border-image: url(:/qss_icons/rc/right_arrow_disabled.png); width: 10px; height: 10px; @@ -328,7 +342,7 @@ QScrollBar::add-line:horizontal { } QScrollBar::sub-line:horizontal { - margin: 0px 3px 0px 3px; + margin: 0 3px; border-image: url(:/qss_icons/rc/left_arrow_disabled.png); height: 10px; width: 10px; @@ -379,7 +393,7 @@ QScrollBar::handle:vertical { } QScrollBar::sub-line:vertical { - margin: 3px 0px 3px 0px; + margin: 3px 0; border-image: url(:/qss_icons/rc/up_arrow_disabled.png); height: 10px; width: 10px; @@ -388,7 +402,7 @@ QScrollBar::sub-line:vertical { } QScrollBar::add-line:vertical { - margin: 3px 0px 3px 0px; + margin: 3px 0; border-image: url(:/qss_icons/rc/down_arrow_disabled.png); height: 10px; width: 10px; @@ -427,15 +441,14 @@ QScrollBar::sub-page:vertical { QTextEdit { background-color: #232629; color: #eff0f1; - border: 1px solid #76797C; + border: 1px solid #54575B; } QPlainTextEdit { background-color: #232629; - ; color: #eff0f1; border-radius: 2px; - border: 1px solid #76797C; + border: 1px solid #54575B; } QHeaderView::section { @@ -467,15 +480,6 @@ QMainWindow::separator:hover { spacing: 2px; } -QMenu::separator { - height: 1px; - background-color: #76797C; - color: white; - padding-left: 4px; - margin-left: 10px; - margin-right: 5px; -} - QFrame { border-radius: 2px; border: 1px solid #76797C; @@ -518,25 +522,19 @@ QToolButton#qt_toolbar_ext_button { QPushButton { color: #eff0f1; - background-color: #31363b; border-width: 1px; - border-color: #76797C; + border-color: #54575B; border-style: solid; - padding: 5px; + padding: 6px 4px; border-radius: 2px; outline: none; + min-width: 100px; + background-color: #232629; } QPushButton:disabled { background-color: #31363b; - border-width: 1px; border-color: #454545; - border-style: solid; - padding-top: 5px; - padding-bottom: 5px; - padding-left: 10px; - padding-right: 10px; - border-radius: 2px; color: #454545; } @@ -553,11 +551,11 @@ QPushButton:pressed { QComboBox { selection-background-color: #3daee9; - border-style: solid; - border: 1px solid #76797C; + border: 1px solid #54575B; border-radius: 2px; - padding: 5px; + padding: 4px 6px; min-width: 75px; + background-color: #232629; } QPushButton:checked { @@ -571,8 +569,7 @@ QAbstractSpinBox:hover, QLineEdit:hover, QTextEdit:hover, QPlainTextEdit:hover, -QAbstractView:hover, -QTreeView:hover { +QAbstractView:hover { border: 1px solid #3daee9; color: #eff0f1; } @@ -591,6 +588,7 @@ QComboBox QAbstractItemView { QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; + left: -6px; width: 15px; border-left-width: 0px; border-left-color: darkgray; @@ -610,8 +608,8 @@ QComboBox::down-arrow:focus { } QAbstractSpinBox { - padding: 5px; - border: 1px solid #76797C; + padding: 4px 6px; + border: 1px solid #54575B; background-color: #232629; color: #eff0f1; border-radius: 2px; @@ -622,12 +620,14 @@ QAbstractSpinBox:up-button { background-color: transparent; subcontrol-origin: border; subcontrol-position: center right; + left: -6px; } QAbstractSpinBox:down-button { background-color: transparent; subcontrol-origin: border; subcontrol-position: center left; + right: -6px; } QAbstractSpinBox::up-arrow, @@ -654,22 +654,27 @@ QAbstractSpinBox::down-arrow:hover { image: url(:/qss_icons/rc/down_arrow.png); } -QLabel { - border: 0px solid black; +QLabel, +QTabWidget { + border: 0; } QTabWidget { - border: 0px transparent black; + padding-top: 1px; } QTabWidget::pane { border: 1px solid #76797C; padding: 5px; - margin: 0px; + position: absolute; + top: -1px; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; } QTabWidget::tab-bar { - /* left: 5px; move to the right by 5px */ + overflow: visible; } QTabBar { @@ -677,10 +682,6 @@ QTabBar { border-radius: 3px; } -QTabBar:focus { - border: 0px transparent black; -} - QTabBar::close-button { image: url(:/qss_icons/rc/close.png); background: transparent; @@ -696,36 +697,33 @@ QTabBar::close-button:pressed { background: transparent; } - /* TOP TABS */ - QTabBar::tab:top { color: #eff0f1; - border: 1px solid #76797C; - border-bottom: 2px transparent; - background-color: #31363b; - padding: 4px 16px 2px; - min-width: 38px; + border: 1px solid #54575B; + background-color: #2a2f33; + padding: 4px 16px 5px; + min-width: 36px; border-top-left-radius: 2px; border-top-right-radius: 2px; } QTabBar::tab:top:selected { - color: #eff0f1; - background-color: #54575B; - border: 1px solid #76797C; - border-bottom: 2px solid #3daee9; - border-top-left-radius: 2px; - border-top-right-radius: 2px; + border-color: #76797C; + background-color: #31363b; + border-bottom-color: #31363b; +} + +QTabBar::tab:top:!selected { + margin-top: 1px; + border-bottom-color: #76797C; } QTabBar::tab:top:!selected:hover { background-color: #3daee9; } - /* BOTTOM TABS */ - QTabBar::tab:bottom { color: #eff0f1; border: 1px solid #76797C; @@ -750,9 +748,7 @@ QTabBar::tab:bottom:!selected:hover { background-color: #3daee9; } - /* LEFT TABS */ - QTabBar::tab:left { color: #eff0f1; border: 1px solid #76797C; @@ -777,9 +773,7 @@ QTabBar::tab:left:!selected:hover { background-color: #3daee9; } - /* RIGHT TABS */ - QTabBar::tab:right { color: #eff0f1; border: 1px solid #76797C; @@ -847,7 +841,7 @@ QDockWidget::float-button:pressed { QTreeView, QListView { - border: 1px solid #76797C; + border: 1px solid #54575B; background-color: #232629; } @@ -978,8 +972,8 @@ QSlider::handle:vertical { } QToolButton { - background-color: transparent; - border: 1px transparent #76797C; + background-color: #232629; + border: 1px solid #54575B; border-radius: 2px; margin: 3px; padding: 5px; @@ -988,7 +982,6 @@ QToolButton { QToolButton[popupMode="1"] { /* only for MenuButtonPopup */ padding-right: 20px; - /* make way for the popup button */ border: 1px #76797C; border-radius: 5px; } @@ -996,7 +989,6 @@ QToolButton[popupMode="1"] { QToolButton[popupMode="2"] { /* only for InstantPopup */ padding-right: 10px; - /* make way for the popup button */ border: 1px #76797C; } @@ -1015,19 +1007,14 @@ QToolButton::menu-button:pressed { padding: 5px; } - /* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */ - QToolButton::menu-indicator { image: url(:/qss_icons/rc/down_arrow.png); top: -7px; left: -2px; - /* shift it a bit */ } - /* the subcontrols below are used only in the MenuButtonPopup mode */ - QToolButton::menu-button { border: 1px transparent #76797C; border-top-right-radius: 6px; @@ -1052,14 +1039,22 @@ QPushButton::menu-indicator { } QTableView { - border: 1px solid #76797C; + border: 1px solid #54575B; gridline-color: #31363b; background-color: #232629; } +QTreeView:disabled { + background-color: #1f2225; +} + QTableView, QHeaderView { - border-radius: 0px; + border-radius: 0; +} + +QListView:focus { + border-color: #54575B; } QTableView::item:pressed, @@ -1088,7 +1083,7 @@ QHeaderView::section { background-color: #232629; color: #eff0f1; padding: 0 5px; - border: 1px solid #403F3F; + border: 1px solid #434242; border-bottom: 0; border-radius: 0px; text-align: center; @@ -1118,9 +1113,7 @@ QHeaderView::section:checked { background-color: #334e5e; } - -/* style the sort indicator */ - +/* sort indicator */ QHeaderView::down-arrow { image: url(:/qss_icons/rc/down_arrow.png); } @@ -1150,14 +1143,13 @@ QToolBox::tab { } QToolBox::tab:selected { - /* italicize selected tabs */ font: italic; background-color: #31363b; border-color: #3daee9; } QStatusBar::item { - border: 0px transparent dark; + border: 0; } QFrame[height="3"], @@ -1194,7 +1186,6 @@ QProgressBar::chunk { QDateEdit { selection-background-color: #3daee9; - border-style: solid; border: 1px solid #3375A3; border-radius: 2px; padding: 1px; @@ -1218,7 +1209,7 @@ QDateEdit::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 15px; - border-left-width: 0px; + border-left-width: 0; border-left-color: darkgray; border-left-style: solid; border-top-right-radius: 3px; @@ -1234,3 +1225,14 @@ QDateEdit::down-arrow:hover, QDateEdit::down-arrow:focus { image: url(:/qss_icons/rc/down_arrow.png); } + +QComboBox:disabled, +QPushButton:disabled, +QAbstractSpinBox:disabled, +QDateEdit:disabled, +QLineEdit:disabled, +QTextEdit:disabled, +QToolButton:disabled, +QPlainTextEdit:disabled { + background-color: #2b2e31; +} diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 8f2591d53..04bc3128f 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -120,7 +120,7 @@ private: duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); entry.log_class = log_class; entry.log_level = log_level; - entry.filename = Common::TrimSourcePath(filename); + entry.filename = filename; entry.line_num = line_nr; entry.function = function; entry.message = std::move(message); diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index fca0267a1..fc338c70d 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -23,7 +23,7 @@ struct Entry { std::chrono::microseconds timestamp; Class log_class; Level log_level; - std::string filename; + const char* filename; unsigned int line_num; std::string function; std::string message; diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 259708116..13a4f1e30 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -9,6 +9,15 @@ namespace Log { +// trims up to and including the last of ../, ..\, src/, src\ in a string +constexpr const char* TrimSourcePath(std::string_view source) { + const auto rfind = [source](const std::string_view match) { + return source.rfind(match) == source.npos ? 0 : (source.rfind(match) + match.size()); + }; + auto idx = std::max({rfind("src/"), rfind("src\\"), rfind("../"), rfind("..\\")}); + return source.data() + idx; +} + /// Specifies the severity or level of detail of the log message. enum class Level : u8 { Trace, ///< Extremely detailed and repetitive debugging information that is likely to @@ -141,24 +150,24 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig #ifdef _DEBUG #define LOG_TRACE(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \ - __func__, __VA_ARGS__) + ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, \ + ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) #else #define LOG_TRACE(log_class, fmt, ...) (void(0)) #endif #define LOG_DEBUG(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \ - __func__, __VA_ARGS__) + ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, \ + ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) #define LOG_INFO(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \ - __func__, __VA_ARGS__) + ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, \ + ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) #define LOG_WARNING(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \ - __func__, __VA_ARGS__) + ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, \ + ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) #define LOG_ERROR(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \ - __func__, __VA_ARGS__) + ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, \ + ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) #define LOG_CRITICAL(log_class, ...) \ - ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \ - __func__, __VA_ARGS__) + ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, \ + ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 959f278aa..84883a1d3 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -223,26 +223,4 @@ std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buff return std::u16string(buffer.begin(), buffer.begin() + len); } -const char* TrimSourcePath(const char* path, const char* root) { - const char* p = path; - - while (*p != '\0') { - const char* next_slash = p; - while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') { - ++next_slash; - } - - bool is_src = Common::ComparePartialString(p, next_slash, root); - p = next_slash; - - if (*p != '\0') { - ++p; - } - if (is_src) { - path = p; - } - } - return path; -} - } // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1a3647a67..d342cafe0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -15,14 +15,14 @@ add_library(core STATIC constants.h core.cpp core.h - core_cpu.cpp - core_cpu.h + core_manager.cpp + core_manager.h core_timing.cpp core_timing.h core_timing_util.cpp core_timing_util.h - cpu_core_manager.cpp - cpu_core_manager.h + cpu_manager.cpp + cpu_manager.h crypto/aes_util.cpp crypto/aes_util.h crypto/encryption_layer.cpp @@ -158,6 +158,8 @@ add_library(core STATIC hle/kernel/mutex.h hle/kernel/object.cpp hle/kernel/object.h + hle/kernel/physical_core.cpp + hle/kernel/physical_core.h hle/kernel/process.cpp hle/kernel/process.h hle/kernel/process_capability.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index e825c0526..791640a3a 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -10,11 +10,12 @@ #include "common/microprofile.h" #include "core/arm/dynarmic/arm_dynarmic.h" #include "core/core.h" -#include "core/core_cpu.h" +#include "core/core_manager.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/svc.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" @@ -87,7 +88,7 @@ public: if (GDBStub::IsServerEnabled()) { parent.jit->HaltExecution(); parent.SetPC(pc); - Kernel::Thread* thread = Kernel::GetCurrentThread(); + Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread(); parent.SaveContext(thread->GetContext()); GDBStub::Break(); GDBStub::SendTrap(thread, 5); diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index abd59ff4b..94570e520 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -2,10 +2,24 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#ifdef ARCHITECTURE_x86_64 +#include "core/arm/dynarmic/arm_dynarmic.h" +#endif #include "core/arm/exclusive_monitor.h" +#include "core/memory.h" namespace Core { ExclusiveMonitor::~ExclusiveMonitor() = default; +std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, + std::size_t num_cores) { +#ifdef ARCHITECTURE_x86_64 + return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores); +#else + // TODO(merry): Passthrough exclusive monitor + return nullptr; +#endif +} + } // namespace Core diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index f59aca667..4ef418b90 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h @@ -4,8 +4,14 @@ #pragma once +#include <memory> + #include "common/common_types.h" +namespace Memory { +class Memory; +} + namespace Core { class ExclusiveMonitor { @@ -22,4 +28,7 @@ public: virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0; }; +std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, + std::size_t num_cores); + } // namespace Core diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 48182c99a..f99ad5802 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -9,6 +9,7 @@ #include "core/arm/unicorn/arm_unicorn.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/svc.h" namespace Core { @@ -177,7 +178,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); } - Kernel::Thread* thread = Kernel::GetCurrentThread(); + Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread(); SaveContext(thread->GetContext()); if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { last_bkpt_hit = false; diff --git a/src/core/core.cpp b/src/core/core.cpp index d697b80ef..c53d122be 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -11,9 +11,9 @@ #include "common/string_util.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" -#include "core/core_cpu.h" +#include "core/core_manager.h" #include "core/core_timing.h" -#include "core/cpu_core_manager.h" +#include "core/cpu_manager.h" #include "core/file_sys/bis_factory.h" #include "core/file_sys/card_image.h" #include "core/file_sys/mode.h" @@ -28,6 +28,7 @@ #include "core/hardware_interrupt_manager.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" @@ -113,16 +114,25 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, struct System::Impl { explicit Impl(System& system) : kernel{system}, fs_controller{system}, memory{system}, - cpu_core_manager{system}, reporter{system}, applet_manager{system} {} + cpu_manager{system}, reporter{system}, applet_manager{system} {} - Cpu& CurrentCpuCore() { - return cpu_core_manager.GetCurrentCore(); + CoreManager& CurrentCoreManager() { + return cpu_manager.GetCurrentCoreManager(); + } + + Kernel::PhysicalCore& CurrentPhysicalCore() { + const auto index = cpu_manager.GetActiveCoreIndex(); + return kernel.PhysicalCore(index); + } + + Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { + return kernel.PhysicalCore(index); } ResultStatus RunLoop(bool tight_loop) { status = ResultStatus::Success; - cpu_core_manager.RunLoop(tight_loop); + cpu_manager.RunLoop(tight_loop); return status; } @@ -131,8 +141,8 @@ struct System::Impl { LOG_DEBUG(HW_Memory, "initialized OK"); core_timing.Initialize(); - cpu_core_manager.Initialize(); kernel.Initialize(); + cpu_manager.Initialize(); const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now().time_since_epoch()); @@ -205,7 +215,6 @@ struct System::Impl { // Main process has been loaded and been made current. // Begin GPU and CPU execution. gpu_core->Start(); - cpu_core_manager.StartThreads(); // Initialize cheat engine if (cheat_engine) { @@ -272,7 +281,7 @@ struct System::Impl { gpu_core.reset(); // Close all CPU/threading state - cpu_core_manager.Shutdown(); + cpu_manager.Shutdown(); // Shutdown kernel and core timing kernel.Shutdown(); @@ -342,7 +351,7 @@ struct System::Impl { std::unique_ptr<Tegra::GPU> gpu_core; std::unique_ptr<Hardware::InterruptManager> interrupt_manager; Memory::Memory memory; - CpuCoreManager cpu_core_manager; + CpuManager cpu_manager; bool is_powered_on = false; bool exit_lock = false; @@ -377,12 +386,12 @@ struct System::Impl { System::System() : impl{std::make_unique<Impl>(*this)} {} System::~System() = default; -Cpu& System::CurrentCpuCore() { - return impl->CurrentCpuCore(); +CoreManager& System::CurrentCoreManager() { + return impl->CurrentCoreManager(); } -const Cpu& System::CurrentCpuCore() const { - return impl->CurrentCpuCore(); +const CoreManager& System::CurrentCoreManager() const { + return impl->CurrentCoreManager(); } System::ResultStatus System::RunLoop(bool tight_loop) { @@ -394,7 +403,7 @@ System::ResultStatus System::SingleStep() { } void System::InvalidateCpuInstructionCaches() { - impl->cpu_core_manager.InvalidateAllInstructionCaches(); + impl->kernel.InvalidateAllInstructionCaches(); } System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { @@ -406,13 +415,11 @@ bool System::IsPoweredOn() const { } void System::PrepareReschedule() { - CurrentCpuCore().PrepareReschedule(); + impl->CurrentPhysicalCore().Stop(); } void System::PrepareReschedule(const u32 core_index) { - if (core_index < GlobalScheduler().CpuCoresCount()) { - CpuCore(core_index).PrepareReschedule(); - } + impl->kernel.PrepareReschedule(core_index); } PerfStatsResults System::GetAndResetPerfStats() { @@ -428,31 +435,31 @@ const TelemetrySession& System::TelemetrySession() const { } ARM_Interface& System::CurrentArmInterface() { - return CurrentCpuCore().ArmInterface(); + return impl->CurrentPhysicalCore().ArmInterface(); } const ARM_Interface& System::CurrentArmInterface() const { - return CurrentCpuCore().ArmInterface(); + return impl->CurrentPhysicalCore().ArmInterface(); } std::size_t System::CurrentCoreIndex() const { - return CurrentCpuCore().CoreIndex(); + return impl->cpu_manager.GetActiveCoreIndex(); } Kernel::Scheduler& System::CurrentScheduler() { - return CurrentCpuCore().Scheduler(); + return impl->CurrentPhysicalCore().Scheduler(); } const Kernel::Scheduler& System::CurrentScheduler() const { - return CurrentCpuCore().Scheduler(); + return impl->CurrentPhysicalCore().Scheduler(); } Kernel::Scheduler& System::Scheduler(std::size_t core_index) { - return CpuCore(core_index).Scheduler(); + return impl->GetPhysicalCore(core_index).Scheduler(); } const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { - return CpuCore(core_index).Scheduler(); + return impl->GetPhysicalCore(core_index).Scheduler(); } /// Gets the global scheduler @@ -474,28 +481,28 @@ const Kernel::Process* System::CurrentProcess() const { } ARM_Interface& System::ArmInterface(std::size_t core_index) { - return CpuCore(core_index).ArmInterface(); + return impl->GetPhysicalCore(core_index).ArmInterface(); } const ARM_Interface& System::ArmInterface(std::size_t core_index) const { - return CpuCore(core_index).ArmInterface(); + return impl->GetPhysicalCore(core_index).ArmInterface(); } -Cpu& System::CpuCore(std::size_t core_index) { - return impl->cpu_core_manager.GetCore(core_index); +CoreManager& System::GetCoreManager(std::size_t core_index) { + return impl->cpu_manager.GetCoreManager(core_index); } -const Cpu& System::CpuCore(std::size_t core_index) const { +const CoreManager& System::GetCoreManager(std::size_t core_index) const { ASSERT(core_index < NUM_CPU_CORES); - return impl->cpu_core_manager.GetCore(core_index); + return impl->cpu_manager.GetCoreManager(core_index); } ExclusiveMonitor& System::Monitor() { - return impl->cpu_core_manager.GetExclusiveMonitor(); + return impl->kernel.GetExclusiveMonitor(); } const ExclusiveMonitor& System::Monitor() const { - return impl->cpu_core_manager.GetExclusiveMonitor(); + return impl->kernel.GetExclusiveMonitor(); } Memory::Memory& System::Memory() { diff --git a/src/core/core.h b/src/core/core.h index e240c5c58..e69d68fcf 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -93,7 +93,7 @@ class Memory; namespace Core { class ARM_Interface; -class Cpu; +class CoreManager; class ExclusiveMonitor; class FrameLimiter; class PerfStats; @@ -218,10 +218,10 @@ public: const ARM_Interface& ArmInterface(std::size_t core_index) const; /// Gets a CPU interface to the CPU core with the specified index - Cpu& CpuCore(std::size_t core_index); + CoreManager& GetCoreManager(std::size_t core_index); /// Gets a CPU interface to the CPU core with the specified index - const Cpu& CpuCore(std::size_t core_index) const; + const CoreManager& GetCoreManager(std::size_t core_index) const; /// Gets a reference to the exclusive monitor ExclusiveMonitor& Monitor(); @@ -364,10 +364,10 @@ private: System(); /// Returns the currently running CPU core - Cpu& CurrentCpuCore(); + CoreManager& CurrentCoreManager(); /// Returns the currently running CPU core - const Cpu& CurrentCpuCore() const; + const CoreManager& CurrentCoreManager() const; /** * Initialize the emulated system. diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp deleted file mode 100644 index 630cd4feb..000000000 --- a/src/core/core_cpu.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <condition_variable> -#include <mutex> - -#include "common/logging/log.h" -#ifdef ARCHITECTURE_x86_64 -#include "core/arm/dynarmic/arm_dynarmic.h" -#endif -#include "core/arm/exclusive_monitor.h" -#include "core/arm/unicorn/arm_unicorn.h" -#include "core/core.h" -#include "core/core_cpu.h" -#include "core/core_timing.h" -#include "core/hle/kernel/scheduler.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/lock.h" -#include "core/settings.h" - -namespace Core { - -void CpuBarrier::NotifyEnd() { - std::unique_lock lock{mutex}; - end = true; - condition.notify_all(); -} - -bool CpuBarrier::Rendezvous() { - if (!Settings::values.use_multi_core) { - // Meaningless when running in single-core mode - return true; - } - - if (!end) { - std::unique_lock lock{mutex}; - - --cores_waiting; - if (!cores_waiting) { - cores_waiting = NUM_CPU_CORES; - condition.notify_all(); - return true; - } - - condition.wait(lock); - return true; - } - - return false; -} - -Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, - std::size_t core_index) - : cpu_barrier{cpu_barrier}, global_scheduler{system.GlobalScheduler()}, - core_timing{system.CoreTiming()}, core_index{core_index} { -#ifdef ARCHITECTURE_x86_64 - arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); -#else - arm_interface = std::make_unique<ARM_Unicorn>(system); - LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); -#endif - - scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index); -} - -Cpu::~Cpu() = default; - -std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor( - [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) { -#ifdef ARCHITECTURE_x86_64 - return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores); -#else - // TODO(merry): Passthrough exclusive monitor - return nullptr; -#endif -} - -void Cpu::RunLoop(bool tight_loop) { - // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step - if (!cpu_barrier.Rendezvous()) { - // If rendezvous failed, session has been killed - return; - } - - Reschedule(); - - // If we don't have a currently active thread then don't execute instructions, - // instead advance to the next event and try to yield to the next thread - if (Kernel::GetCurrentThread() == nullptr) { - LOG_TRACE(Core, "Core-{} idling", core_index); - core_timing.Idle(); - } else { - if (tight_loop) { - arm_interface->Run(); - } else { - arm_interface->Step(); - } - // We are stopping a run, exclusive state must be cleared - arm_interface->ClearExclusiveState(); - } - core_timing.Advance(); - - Reschedule(); -} - -void Cpu::SingleStep() { - return RunLoop(false); -} - -void Cpu::PrepareReschedule() { - arm_interface->PrepareReschedule(); -} - -void Cpu::Reschedule() { - // Lock the global kernel mutex when we manipulate the HLE state - std::lock_guard lock(HLE::g_hle_lock); - - global_scheduler.SelectThread(core_index); - scheduler->TryDoContextSwitch(); -} - -void Cpu::Shutdown() { - scheduler->Shutdown(); -} - -} // namespace Core diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h deleted file mode 100644 index 78f5021a2..000000000 --- a/src/core/core_cpu.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <atomic> -#include <condition_variable> -#include <cstddef> -#include <memory> -#include <mutex> -#include "common/common_types.h" - -namespace Kernel { -class GlobalScheduler; -class Scheduler; -} // namespace Kernel - -namespace Core { -class System; -} - -namespace Core::Timing { -class CoreTiming; -} - -namespace Memory { -class Memory; -} - -namespace Core { - -class ARM_Interface; -class ExclusiveMonitor; - -constexpr unsigned NUM_CPU_CORES{4}; - -class CpuBarrier { -public: - bool IsAlive() const { - return !end; - } - - void NotifyEnd(); - - bool Rendezvous(); - -private: - unsigned cores_waiting{NUM_CPU_CORES}; - std::mutex mutex; - std::condition_variable condition; - std::atomic<bool> end{}; -}; - -class Cpu { -public: - Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, - std::size_t core_index); - ~Cpu(); - - void RunLoop(bool tight_loop = true); - - void SingleStep(); - - void PrepareReschedule(); - - ARM_Interface& ArmInterface() { - return *arm_interface; - } - - const ARM_Interface& ArmInterface() const { - return *arm_interface; - } - - Kernel::Scheduler& Scheduler() { - return *scheduler; - } - - const Kernel::Scheduler& Scheduler() const { - return *scheduler; - } - - bool IsMainCore() const { - return core_index == 0; - } - - std::size_t CoreIndex() const { - return core_index; - } - - void Shutdown(); - - /** - * Creates an exclusive monitor to handle exclusive reads/writes. - * - * @param memory The current memory subsystem that the monitor may wish - * to keep track of. - * - * @param num_cores The number of cores to assume about the CPU. - * - * @returns The constructed exclusive monitor instance, or nullptr if the current - * CPU backend is unable to use an exclusive monitor. - */ - static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, - std::size_t num_cores); - -private: - void Reschedule(); - - std::unique_ptr<ARM_Interface> arm_interface; - CpuBarrier& cpu_barrier; - Kernel::GlobalScheduler& global_scheduler; - std::unique_ptr<Kernel::Scheduler> scheduler; - Timing::CoreTiming& core_timing; - - std::atomic<bool> reschedule_pending = false; - std::size_t core_index; -}; - -} // namespace Core diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp new file mode 100644 index 000000000..8eacf92dd --- /dev/null +++ b/src/core/core_manager.cpp @@ -0,0 +1,70 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <condition_variable> +#include <mutex> + +#include "common/logging/log.h" +#ifdef ARCHITECTURE_x86_64 +#include "core/arm/dynarmic/arm_dynarmic.h" +#endif +#include "core/arm/exclusive_monitor.h" +#include "core/arm/unicorn/arm_unicorn.h" +#include "core/core.h" +#include "core/core_manager.h" +#include "core/core_timing.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" +#include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/lock.h" +#include "core/settings.h" + +namespace Core { + +CoreManager::CoreManager(System& system, std::size_t core_index) + : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore( + core_index)}, + core_timing{system.CoreTiming()}, core_index{core_index} {} + +CoreManager::~CoreManager() = default; + +void CoreManager::RunLoop(bool tight_loop) { + Reschedule(); + + // If we don't have a currently active thread then don't execute instructions, + // instead advance to the next event and try to yield to the next thread + if (Kernel::GetCurrentThread() == nullptr) { + LOG_TRACE(Core, "Core-{} idling", core_index); + core_timing.Idle(); + } else { + if (tight_loop) { + physical_core.Run(); + } else { + physical_core.Step(); + } + } + core_timing.Advance(); + + Reschedule(); +} + +void CoreManager::SingleStep() { + return RunLoop(false); +} + +void CoreManager::PrepareReschedule() { + physical_core.Stop(); +} + +void CoreManager::Reschedule() { + // Lock the global kernel mutex when we manipulate the HLE state + std::lock_guard lock(HLE::g_hle_lock); + + global_scheduler.SelectThread(core_index); + + physical_core.Scheduler().TryDoContextSwitch(); +} + +} // namespace Core diff --git a/src/core/core_manager.h b/src/core/core_manager.h new file mode 100644 index 000000000..b14e723d7 --- /dev/null +++ b/src/core/core_manager.h @@ -0,0 +1,63 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <atomic> +#include <cstddef> +#include <memory> +#include "common/common_types.h" + +namespace Kernel { +class GlobalScheduler; +class PhysicalCore; +} // namespace Kernel + +namespace Core { +class System; +} + +namespace Core::Timing { +class CoreTiming; +} + +namespace Memory { +class Memory; +} + +namespace Core { + +constexpr unsigned NUM_CPU_CORES{4}; + +class CoreManager { +public: + CoreManager(System& system, std::size_t core_index); + ~CoreManager(); + + void RunLoop(bool tight_loop = true); + + void SingleStep(); + + void PrepareReschedule(); + + bool IsMainCore() const { + return core_index == 0; + } + + std::size_t CoreIndex() const { + return core_index; + } + +private: + void Reschedule(); + + Kernel::GlobalScheduler& global_scheduler; + Kernel::PhysicalCore& physical_core; + Timing::CoreTiming& core_timing; + + std::atomic<bool> reschedule_pending = false; + std::size_t core_index; +}; + +} // namespace Core diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp deleted file mode 100644 index f04a34133..000000000 --- a/src/core/cpu_core_manager.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/assert.h" -#include "core/arm/exclusive_monitor.h" -#include "core/core.h" -#include "core/core_cpu.h" -#include "core/core_timing.h" -#include "core/cpu_core_manager.h" -#include "core/gdbstub/gdbstub.h" -#include "core/settings.h" - -namespace Core { -namespace { -void RunCpuCore(const System& system, Cpu& cpu_state) { - while (system.IsPoweredOn()) { - cpu_state.RunLoop(true); - } -} -} // Anonymous namespace - -CpuCoreManager::CpuCoreManager(System& system) : system{system} {} -CpuCoreManager::~CpuCoreManager() = default; - -void CpuCoreManager::Initialize() { - barrier = std::make_unique<CpuBarrier>(); - exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size()); - - for (std::size_t index = 0; index < cores.size(); ++index) { - cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); - } -} - -void CpuCoreManager::StartThreads() { - // Create threads for CPU cores 1-3, and build thread_to_cpu map - // CPU core 0 is run on the main thread - thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); - if (!Settings::values.use_multi_core) { - return; - } - - for (std::size_t index = 0; index < core_threads.size(); ++index) { - core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system), - std::ref(*cores[index + 1])); - thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get(); - } -} - -void CpuCoreManager::Shutdown() { - barrier->NotifyEnd(); - if (Settings::values.use_multi_core) { - for (auto& thread : core_threads) { - thread->join(); - thread.reset(); - } - } - - thread_to_cpu.clear(); - for (auto& cpu_core : cores) { - cpu_core->Shutdown(); - cpu_core.reset(); - } - - exclusive_monitor.reset(); - barrier.reset(); -} - -Cpu& CpuCoreManager::GetCore(std::size_t index) { - return *cores.at(index); -} - -const Cpu& CpuCoreManager::GetCore(std::size_t index) const { - return *cores.at(index); -} - -ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() { - return *exclusive_monitor; -} - -const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const { - return *exclusive_monitor; -} - -Cpu& CpuCoreManager::GetCurrentCore() { - if (Settings::values.use_multi_core) { - const auto& search = thread_to_cpu.find(std::this_thread::get_id()); - ASSERT(search != thread_to_cpu.end()); - ASSERT(search->second); - return *search->second; - } - - // Otherwise, use single-threaded mode active_core variable - return *cores[active_core]; -} - -const Cpu& CpuCoreManager::GetCurrentCore() const { - if (Settings::values.use_multi_core) { - const auto& search = thread_to_cpu.find(std::this_thread::get_id()); - ASSERT(search != thread_to_cpu.end()); - ASSERT(search->second); - return *search->second; - } - - // Otherwise, use single-threaded mode active_core variable - return *cores[active_core]; -} - -void CpuCoreManager::RunLoop(bool tight_loop) { - // Update thread_to_cpu in case Core 0 is run from a different host thread - thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); - - if (GDBStub::IsServerEnabled()) { - GDBStub::HandlePacket(); - - // If the loop is halted and we want to step, use a tiny (1) number of instructions to - // execute. Otherwise, get out of the loop function. - if (GDBStub::GetCpuHaltFlag()) { - if (GDBStub::GetCpuStepFlag()) { - tight_loop = false; - } else { - return; - } - } - } - - auto& core_timing = system.CoreTiming(); - core_timing.ResetRun(); - bool keep_running{}; - do { - keep_running = false; - for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { - core_timing.SwitchContext(active_core); - if (core_timing.CanCurrentContextRun()) { - cores[active_core]->RunLoop(tight_loop); - } - keep_running |= core_timing.CanCurrentContextRun(); - } - } while (keep_running); - - if (GDBStub::IsServerEnabled()) { - GDBStub::SetCpuStepFlag(false); - } -} - -void CpuCoreManager::InvalidateAllInstructionCaches() { - for (auto& cpu : cores) { - cpu->ArmInterface().ClearInstructionCache(); - } -} - -} // namespace Core diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h deleted file mode 100644 index 2cbbf8216..000000000 --- a/src/core/cpu_core_manager.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <map> -#include <memory> -#include <thread> - -namespace Core { - -class Cpu; -class CpuBarrier; -class ExclusiveMonitor; -class System; - -class CpuCoreManager { -public: - explicit CpuCoreManager(System& system); - CpuCoreManager(const CpuCoreManager&) = delete; - CpuCoreManager(CpuCoreManager&&) = delete; - - ~CpuCoreManager(); - - CpuCoreManager& operator=(const CpuCoreManager&) = delete; - CpuCoreManager& operator=(CpuCoreManager&&) = delete; - - void Initialize(); - void StartThreads(); - void Shutdown(); - - Cpu& GetCore(std::size_t index); - const Cpu& GetCore(std::size_t index) const; - - Cpu& GetCurrentCore(); - const Cpu& GetCurrentCore() const; - - ExclusiveMonitor& GetExclusiveMonitor(); - const ExclusiveMonitor& GetExclusiveMonitor() const; - - void RunLoop(bool tight_loop); - - void InvalidateAllInstructionCaches(); - -private: - static constexpr std::size_t NUM_CPU_CORES = 4; - - std::unique_ptr<ExclusiveMonitor> exclusive_monitor; - std::unique_ptr<CpuBarrier> barrier; - std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores; - std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads; - std::size_t active_core{}; ///< Active core, only used in single thread mode - - /// Map of guest threads to CPU cores - std::map<std::thread::id, Cpu*> thread_to_cpu; - - System& system; -}; - -} // namespace Core diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp new file mode 100644 index 000000000..70ddbdcca --- /dev/null +++ b/src/core/cpu_manager.cpp @@ -0,0 +1,81 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/arm/exclusive_monitor.h" +#include "core/core.h" +#include "core/core_manager.h" +#include "core/core_timing.h" +#include "core/cpu_manager.h" +#include "core/gdbstub/gdbstub.h" + +namespace Core { + +CpuManager::CpuManager(System& system) : system{system} {} +CpuManager::~CpuManager() = default; + +void CpuManager::Initialize() { + for (std::size_t index = 0; index < core_managers.size(); ++index) { + core_managers[index] = std::make_unique<CoreManager>(system, index); + } +} + +void CpuManager::Shutdown() { + for (auto& cpu_core : core_managers) { + cpu_core.reset(); + } +} + +CoreManager& CpuManager::GetCoreManager(std::size_t index) { + return *core_managers.at(index); +} + +const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { + return *core_managers.at(index); +} + +CoreManager& CpuManager::GetCurrentCoreManager() { + // Otherwise, use single-threaded mode active_core variable + return *core_managers[active_core]; +} + +const CoreManager& CpuManager::GetCurrentCoreManager() const { + // Otherwise, use single-threaded mode active_core variable + return *core_managers[active_core]; +} + +void CpuManager::RunLoop(bool tight_loop) { + if (GDBStub::IsServerEnabled()) { + GDBStub::HandlePacket(); + + // If the loop is halted and we want to step, use a tiny (1) number of instructions to + // execute. Otherwise, get out of the loop function. + if (GDBStub::GetCpuHaltFlag()) { + if (GDBStub::GetCpuStepFlag()) { + tight_loop = false; + } else { + return; + } + } + } + + auto& core_timing = system.CoreTiming(); + core_timing.ResetRun(); + bool keep_running{}; + do { + keep_running = false; + for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { + core_timing.SwitchContext(active_core); + if (core_timing.CanCurrentContextRun()) { + core_managers[active_core]->RunLoop(tight_loop); + } + keep_running |= core_timing.CanCurrentContextRun(); + } + } while (keep_running); + + if (GDBStub::IsServerEnabled()) { + GDBStub::SetCpuStepFlag(false); + } +} + +} // namespace Core diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h new file mode 100644 index 000000000..feb619e1b --- /dev/null +++ b/src/core/cpu_manager.h @@ -0,0 +1,50 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <memory> + +namespace Core { + +class CoreManager; +class System; + +class CpuManager { +public: + explicit CpuManager(System& system); + CpuManager(const CpuManager&) = delete; + CpuManager(CpuManager&&) = delete; + + ~CpuManager(); + + CpuManager& operator=(const CpuManager&) = delete; + CpuManager& operator=(CpuManager&&) = delete; + + void Initialize(); + void Shutdown(); + + CoreManager& GetCoreManager(std::size_t index); + const CoreManager& GetCoreManager(std::size_t index) const; + + CoreManager& GetCurrentCoreManager(); + const CoreManager& GetCurrentCoreManager() const; + + std::size_t GetActiveCoreIndex() const { + return active_core; + } + + void RunLoop(bool tight_loop); + +private: + static constexpr std::size_t NUM_CPU_CORES = 4; + + std::array<std::unique_ptr<CoreManager>, NUM_CPU_CORES> core_managers; + std::size_t active_core{}; ///< Active core, only used in single thread mode + + System& system; +}; + +} // namespace Core diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 7c11d7546..2b098b7c6 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -15,6 +15,13 @@ namespace Input { +enum class AnalogDirection : u8 { + RIGHT, + LEFT, + UP, + DOWN, +}; + /// An abstract class template for an input device (a button, an analog input, etc.). template <typename StatusType> class InputDevice { @@ -23,6 +30,9 @@ public: virtual StatusType GetStatus() const { return {}; } + virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { + return {}; + } }; /// An abstract class template for a factory that can create input devices. diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 37cb28848..67e95999d 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -35,7 +35,7 @@ #include "common/swap.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/core_cpu.h" +#include "core/core_manager.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index db189c8e3..2ea3dcb61 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -8,7 +8,6 @@ #include "common/assert.h" #include "common/common_types.h" #include "core/core.h" -#include "core/core_cpu.h" #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/scheduler.h" diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1d0783bd3..edd4c4259 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -3,13 +3,15 @@ // Refer to the license.txt file included. #include <atomic> +#include <functional> #include <memory> #include <mutex> #include <utility> #include "common/assert.h" #include "common/logging/log.h" - +#include "core/arm/arm_interface.h" +#include "core/arm/exclusive_monitor.h" #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" @@ -17,6 +19,7 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/scheduler.h" @@ -98,6 +101,7 @@ struct KernelCore::Impl { void Initialize(KernelCore& kernel) { Shutdown(); + InitializePhysicalCores(); InitializeSystemResourceLimit(kernel); InitializeThreads(); InitializePreemption(); @@ -121,6 +125,21 @@ struct KernelCore::Impl { global_scheduler.Shutdown(); named_ports.clear(); + + for (auto& core : cores) { + core.Shutdown(); + } + cores.clear(); + + exclusive_monitor.reset(); + } + + void InitializePhysicalCores() { + exclusive_monitor = + Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount()); + for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) { + cores.emplace_back(system, i, *exclusive_monitor); + } } // Creates the default system resource limit @@ -186,6 +205,9 @@ struct KernelCore::Impl { /// the ConnectToPort SVC. NamedPortTable named_ports; + std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; + std::vector<Kernel::PhysicalCore> cores; + // System context Core::System& system; }; @@ -240,6 +262,34 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const { return impl->global_scheduler; } +Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { + return impl->cores[id]; +} + +const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { + return impl->cores[id]; +} + +Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { + return *impl->exclusive_monitor; +} + +const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { + return *impl->exclusive_monitor; +} + +void KernelCore::InvalidateAllInstructionCaches() { + for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) { + PhysicalCore(i).ArmInterface().ClearInstructionCache(); + } +} + +void KernelCore::PrepareReschedule(std::size_t id) { + if (id < impl->global_scheduler.CpuCoresCount()) { + impl->cores[id].Stop(); + } +} + void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { impl->named_ports.emplace(std::move(name), std::move(port)); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 3bf0068ed..fccffaf3a 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -11,8 +11,9 @@ #include "core/hle/kernel/object.h" namespace Core { +class ExclusiveMonitor; class System; -} +} // namespace Core namespace Core::Timing { class CoreTiming; @@ -25,6 +26,7 @@ class AddressArbiter; class ClientPort; class GlobalScheduler; class HandleTable; +class PhysicalCore; class Process; class ResourceLimit; class Thread; @@ -84,6 +86,21 @@ public: /// Gets the sole instance of the global scheduler const Kernel::GlobalScheduler& GlobalScheduler() const; + /// Gets the an instance of the respective physical CPU core. + Kernel::PhysicalCore& PhysicalCore(std::size_t id); + + /// Gets the an instance of the respective physical CPU core. + const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; + + /// Stops execution of 'id' core, in order to reschedule a new thread. + void PrepareReschedule(std::size_t id); + + Core::ExclusiveMonitor& GetExclusiveMonitor(); + + const Core::ExclusiveMonitor& GetExclusiveMonitor() const; + + void InvalidateAllInstructionCaches(); + /// Adds a port to the named port table void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port); diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp new file mode 100644 index 000000000..9303dd273 --- /dev/null +++ b/src/core/hle/kernel/physical_core.cpp @@ -0,0 +1,51 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/arm/arm_interface.h" +#ifdef ARCHITECTURE_x86_64 +#include "core/arm/dynarmic/arm_dynarmic.h" +#endif +#include "core/arm/exclusive_monitor.h" +#include "core/arm/unicorn/arm_unicorn.h" +#include "core/core.h" +#include "core/hle/kernel/physical_core.h" +#include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, + Core::ExclusiveMonitor& exclusive_monitor) + : core_index{id} { +#ifdef ARCHITECTURE_x86_64 + arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index); +#else + arm_interface = std::make_shared<Core::ARM_Unicorn>(system); + LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); +#endif + + scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index); +} + +PhysicalCore::~PhysicalCore() = default; + +void PhysicalCore::Run() { + arm_interface->Run(); + arm_interface->ClearExclusiveState(); +} + +void PhysicalCore::Step() { + arm_interface->Step(); +} + +void PhysicalCore::Stop() { + arm_interface->PrepareReschedule(); +} + +void PhysicalCore::Shutdown() { + scheduler->Shutdown(); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h new file mode 100644 index 000000000..4c32c0f1b --- /dev/null +++ b/src/core/hle/kernel/physical_core.h @@ -0,0 +1,77 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstddef> +#include <memory> + +namespace Kernel { +class Scheduler; +} // namespace Kernel + +namespace Core { +class ARM_Interface; +class ExclusiveMonitor; +class System; +} // namespace Core + +namespace Kernel { + +class PhysicalCore { +public: + PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor); + ~PhysicalCore(); + + PhysicalCore(const PhysicalCore&) = delete; + PhysicalCore& operator=(const PhysicalCore&) = delete; + + PhysicalCore(PhysicalCore&&) = default; + PhysicalCore& operator=(PhysicalCore&&) = default; + + /// Execute current jit state + void Run(); + /// Execute a single instruction in current jit. + void Step(); + /// Stop JIT execution/exit + void Stop(); + + // Shutdown this physical core. + void Shutdown(); + + Core::ARM_Interface& ArmInterface() { + return *arm_interface; + } + + const Core::ARM_Interface& ArmInterface() const { + return *arm_interface; + } + + bool IsMainCore() const { + return core_index == 0; + } + + bool IsSystemCore() const { + return core_index == 3; + } + + std::size_t CoreIndex() const { + return core_index; + } + + Kernel::Scheduler& Scheduler() { + return *scheduler; + } + + const Kernel::Scheduler& Scheduler() const { + return *scheduler; + } + +private: + std::size_t core_index; + std::unique_ptr<Core::ARM_Interface> arm_interface; + std::unique_ptr<Kernel::Scheduler> scheduler; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index d36fcd7d9..eb196a690 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -14,7 +14,6 @@ #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/core_cpu.h" #include "core/core_timing.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index dbcdb0b88..1d99bf7a2 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -15,7 +15,7 @@ #include "common/string_util.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" -#include "core/core_cpu.h" +#include "core/core_manager.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hle/kernel/address_arbiter.h" diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e84e5ce0d..e965b5b04 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -13,7 +13,6 @@ #include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/core_cpu.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hle/kernel/errors.h" @@ -356,7 +355,7 @@ void Thread::SetActivity(ThreadActivity value) { // Set status if not waiting if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { SetStatus(ThreadStatus::Paused); - Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); + kernel.PrepareReschedule(processor_id); } } else if (status == ThreadStatus::Paused) { // Ready to reschedule diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index 745f2c4e8..a0c806e8f 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -7,7 +7,6 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" -#include "core/core_cpu.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" @@ -96,7 +95,7 @@ void WaitObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { } if (resume) { thread->ResumeFromWait(); - Core::System::GetInstance().PrepareReschedule(thread->GetProcessorID()); + kernel.PrepareReschedule(thread->GetProcessorID()); } } diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 4d952adc0..15c09f04c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -250,6 +250,10 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { auto& rstick_entry = npad_pad_states[controller_idx].r_stick; const auto& button_state = buttons[controller_idx]; const auto& analog_state = sticks[controller_idx]; + const auto [stick_l_x_f, stick_l_y_f] = + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); + const auto [stick_r_x_f, stick_r_y_f] = + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); using namespace Settings::NativeButton; pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); @@ -270,23 +274,32 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); - - pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick_right.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( + Input::AnalogDirection::RIGHT)); + pad_state.l_stick_left.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( + Input::AnalogDirection::LEFT)); + pad_state.l_stick_up.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( + Input::AnalogDirection::UP)); + pad_state.l_stick_down.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( + Input::AnalogDirection::DOWN)); + + pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); + pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); + pad_state.r_stick_right.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); + pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); - const auto [stick_l_x_f, stick_l_y_f] = - analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); - const auto [stick_r_x_f, stick_r_y_f] = - analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 884ad173b..f67fab2f9 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -42,6 +42,26 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) { rb.Push<u32>(0); // bsd errno } +void BSD::Select(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + +void BSD::Bind(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + void BSD::Connect(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service, "(STUBBED) called"); @@ -52,6 +72,26 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) { rb.Push<u32>(0); // bsd errno } +void BSD::Listen(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + +void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + void BSD::SendTo(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service, "(STUBBED) called"); @@ -80,7 +120,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) { {2, &BSD::Socket, "Socket"}, {3, nullptr, "SocketExempt"}, {4, nullptr, "Open"}, - {5, nullptr, "Select"}, + {5, &BSD::Select, "Select"}, {6, nullptr, "Poll"}, {7, nullptr, "Sysctl"}, {8, nullptr, "Recv"}, @@ -88,15 +128,15 @@ BSD::BSD(const char* name) : ServiceFramework(name) { {10, nullptr, "Send"}, {11, &BSD::SendTo, "SendTo"}, {12, nullptr, "Accept"}, - {13, nullptr, "Bind"}, + {13, &BSD::Bind, "Bind"}, {14, &BSD::Connect, "Connect"}, {15, nullptr, "GetPeerName"}, {16, nullptr, "GetSockName"}, {17, nullptr, "GetSockOpt"}, - {18, nullptr, "Listen"}, + {18, &BSD::Listen, "Listen"}, {19, nullptr, "Ioctl"}, {20, nullptr, "Fcntl"}, - {21, nullptr, "SetSockOpt"}, + {21, &BSD::SetSockOpt, "SetSockOpt"}, {22, nullptr, "Shutdown"}, {23, nullptr, "ShutdownAllSockets"}, {24, nullptr, "Write"}, diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 0fe0e65c6..3098e3baf 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -18,7 +18,11 @@ private: void RegisterClient(Kernel::HLERequestContext& ctx); void StartMonitoring(Kernel::HLERequestContext& ctx); void Socket(Kernel::HLERequestContext& ctx); + void Select(Kernel::HLERequestContext& ctx); + void Bind(Kernel::HLERequestContext& ctx); void Connect(Kernel::HLERequestContext& ctx); + void Listen(Kernel::HLERequestContext& ctx); + void SetSockOpt(Kernel::HLERequestContext& ctx); void SendTo(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d2e9d278f..a2e0c0bd2 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -342,6 +342,22 @@ public: return std::make_tuple<float, float>(0.0f, 0.0f); } + bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { + const auto [x, y] = GetStatus(); + const float directional_deadzone = 0.4f; + switch (direction) { + case Input::AnalogDirection::RIGHT: + return x > directional_deadzone; + case Input::AnalogDirection::LEFT: + return x < -directional_deadzone; + case Input::AnalogDirection::UP: + return y > directional_deadzone; + case Input::AnalogDirection::DOWN: + return y < -directional_deadzone; + } + return false; + } + private: std::shared_ptr<SDLJoystick> joystick; const int axis_x; diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 6f98bd827..f443ec0fe 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -227,6 +227,28 @@ enum class AtomicOp : u64 { Exch = 8, }; +enum class GlobalAtomicOp : u64 { + Add = 0, + Min = 1, + Max = 2, + Inc = 3, + Dec = 4, + And = 5, + Or = 6, + Xor = 7, + Exch = 8, + SafeAdd = 10, +}; + +enum class GlobalAtomicType : u64 { + U32 = 0, + S32 = 1, + U64 = 2, + F32_FTZ_RN = 3, + F16x2_FTZ_RN = 4, + S64 = 5, +}; + enum class UniformType : u64 { UnsignedByte = 0, SignedByte = 1, @@ -958,6 +980,12 @@ union Instruction { } stg; union { + BitField<52, 4, GlobalAtomicOp> operation; + BitField<49, 3, GlobalAtomicType> type; + BitField<28, 20, s64> offset; + } atom; + + union { BitField<52, 4, AtomicOp> operation; BitField<28, 2, AtomicType> type; BitField<30, 22, s64> offset; @@ -1690,6 +1718,7 @@ public: ST_S, ST, // Store in generic memory STG, // Store in global memory + ATOM, // Atomic operation on global memory ATOMS, // Atomic operation on shared memory AL2P, // Transforms attribute memory into physical memory TEX, @@ -1994,6 +2023,7 @@ private: INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), INST("101-------------", Id::ST, Type::Memory, "ST"), INST("1110111011011---", Id::STG, Type::Memory, "STG"), + INST("11101101--------", Id::ATOM, Type::Memory, "ATOM"), INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"), INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), INST("110000----111---", Id::TEX, Type::Texture, "TEX"), diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index cb1a5f35c..4735000b5 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1040,7 +1040,6 @@ private: } return {{"gl_ViewportIndex", Type::Int}}; case 3: - UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader"); return {{"gl_PointSize", Type::Float}}; } return {}; @@ -1885,10 +1884,7 @@ private: template <const std::string_view& opname, Type type> Expression Atomic(Operation operation) { - ASSERT(stage == ShaderType::Compute); - auto& smem = std::get<SmemNode>(*operation[0]); - - return {fmt::format("atomic{}(smem[{} >> 2], {})", opname, Visit(smem.GetAddress()).AsInt(), + return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), Visit(operation[1]).As(type)), type}; } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index e95eb069e..d4b81cd87 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -176,6 +176,19 @@ GLint GetSwizzleSource(SwizzleSource source) { return GL_NONE; } +GLenum GetComponent(PixelFormat format, bool is_first) { + switch (format) { + case PixelFormat::Z24S8: + case PixelFormat::Z32FS8: + return is_first ? GL_DEPTH_COMPONENT : GL_STENCIL_INDEX; + case PixelFormat::S8Z24: + return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT; + default: + UNREACHABLE(); + return GL_DEPTH_COMPONENT; + } +} + void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { if (params.IsBuffer()) { return; @@ -184,7 +197,7 @@ void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.num_levels - 1); + glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(params.num_levels - 1)); if (params.num_levels == 1) { glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f); } @@ -416,11 +429,21 @@ void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_sou if (new_swizzle == swizzle) return; swizzle = new_swizzle; - const std::array<GLint, 4> gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), - GetSwizzleSource(z_source), - GetSwizzleSource(w_source)}; + const std::array gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), + GetSwizzleSource(z_source), GetSwizzleSource(w_source)}; const GLuint handle = GetTexture(); - glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); + const PixelFormat format = surface.GetSurfaceParams().pixel_format; + switch (format) { + case PixelFormat::Z24S8: + case PixelFormat::Z32FS8: + case PixelFormat::S8Z24: + glTextureParameteri(handle, GL_DEPTH_STENCIL_TEXTURE_MODE, + GetComponent(format, x_source == SwizzleSource::R)); + break; + default: + glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); + break; + } } OGLTextureView CachedSurfaceView::CreateTextureView() const { @@ -529,8 +552,11 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view, const Common::Rectangle<u32>& dst_rect = copy_config.dst_rect; const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear; - glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, - dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, + glBlitFramebuffer(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.top), + static_cast<GLint>(src_rect.right), static_cast<GLint>(src_rect.bottom), + static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.top), + static_cast<GLint>(dst_rect.right), static_cast<GLint>(dst_rect.bottom), + buffers, is_linear && (buffers == GL_COLOR_BUFFER_BIT) ? GL_LINEAR : GL_NEAREST); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 48e23d4cd..7ddf7d3ee 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -325,9 +325,6 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { specialization.tessellation.primitive = fixed_state.tessellation.primitive; specialization.tessellation.spacing = fixed_state.tessellation.spacing; specialization.tessellation.clockwise = fixed_state.tessellation.clockwise; - for (const auto& rt : key.renderpass_params.color_attachments) { - specialization.enabled_rendertargets.set(rt.index); - } SPIRVProgram program; std::vector<vk::DescriptorSetLayoutBinding> bindings; diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 36d928fab..24a658dce 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -543,11 +543,10 @@ private: return; } - for (u32 rt = 0; rt < static_cast<u32>(frag_colors.size()); ++rt) { - if (!specialization.enabled_rendertargets[rt]) { + for (u32 rt = 0; rt < static_cast<u32>(std::size(frag_colors)); ++rt) { + if (!IsRenderTargetEnabled(rt)) { continue; } - const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output)); Name(id, fmt::format("frag_color{}", rt)); Decorate(id, spv::Decoration::Location, rt); @@ -862,6 +861,15 @@ private: return binding; } + bool IsRenderTargetEnabled(u32 rt) const { + for (u32 component = 0; component < 4; ++component) { + if (header.ps.IsColorComponentOutputEnabled(rt, component)) { + return true; + } + } + return false; + } + bool IsInputAttributeArray() const { return stage == ShaderType::TesselationControl || stage == ShaderType::TesselationEval || stage == ShaderType::Geometry; @@ -1130,15 +1138,7 @@ private: } if (const auto gmem = std::get_if<GmemNode>(&*node)) { - const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); - const Id real = AsUint(Visit(gmem->GetRealAddress())); - const Id base = AsUint(Visit(gmem->GetBaseAddress())); - - Id offset = OpISub(t_uint, real, base); - offset = OpUDiv(t_uint, offset, Constant(t_uint, 4U)); - return {OpLoad(t_float, - OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0U), offset)), - Type::Float}; + return {OpLoad(t_uint, GetGlobalMemoryPointer(*gmem)), Type::Uint}; } if (const auto lmem = std::get_if<LmemNode>(&*node)) { @@ -1149,10 +1149,7 @@ private: } if (const auto smem = std::get_if<SmemNode>(&*node)) { - Id address = AsUint(Visit(smem->GetAddress())); - address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); - const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address); - return {OpLoad(t_uint, pointer), Type::Uint}; + return {OpLoad(t_uint, GetSharedMemoryPointer(*smem)), Type::Uint}; } if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) { @@ -1346,20 +1343,10 @@ private: target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float}; } else if (const auto smem = std::get_if<SmemNode>(&*dest)) { - ASSERT(stage == ShaderType::Compute); - Id address = AsUint(Visit(smem->GetAddress())); - address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); - target = {OpAccessChain(t_smem_uint, shared_memory, address), Type::Uint}; + target = {GetSharedMemoryPointer(*smem), Type::Uint}; } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { - const Id real = AsUint(Visit(gmem->GetRealAddress())); - const Id base = AsUint(Visit(gmem->GetBaseAddress())); - const Id diff = OpISub(t_uint, real, base); - const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2)); - - const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); - target = {OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0), offset), - Type::Float}; + target = {GetGlobalMemoryPointer(*gmem), Type::Uint}; } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) { target = {custom_variables.at(cv->GetIndex()), Type::Float}; @@ -1814,11 +1801,16 @@ private: return {}; } - Expression UAtomicAdd(Operation operation) { - const auto& smem = std::get<SmemNode>(*operation[0]); - Id address = AsUint(Visit(smem.GetAddress())); - address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); - const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address); + Expression AtomicAdd(Operation operation) { + Id pointer; + if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { + pointer = GetSharedMemoryPointer(*smem); + } else if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) { + pointer = GetGlobalMemoryPointer(*gmem); + } else { + UNREACHABLE(); + return {Constant(t_uint, 0), Type::Uint}; + } const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); const Id semantics = Constant(t_uint, 0U); @@ -1907,19 +1899,14 @@ private: // rendertargets/components are skipped in the register assignment. u32 current_reg = 0; for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { - if (!specialization.enabled_rendertargets[rt]) { - // Skip rendertargets that are not enabled - continue; - } // TODO(Subv): Figure out how dual-source blending is configured in the Switch. for (u32 component = 0; component < 4; ++component) { - const Id pointer = AccessElement(t_out_float, frag_colors.at(rt), component); - if (header.ps.IsColorComponentOutputEnabled(rt, component)) { - OpStore(pointer, SafeGetRegister(current_reg)); - ++current_reg; - } else { - OpStore(pointer, component == 3 ? v_float_one : v_float_zero); + if (!header.ps.IsColorComponentOutputEnabled(rt, component)) { + continue; } + const Id pointer = AccessElement(t_out_float, frag_colors[rt], component); + OpStore(pointer, SafeGetRegister(current_reg)); + ++current_reg; } } if (header.ps.omap.depth) { @@ -2258,6 +2245,22 @@ private: return {}; } + Id GetGlobalMemoryPointer(const GmemNode& gmem) { + const Id real = AsUint(Visit(gmem.GetRealAddress())); + const Id base = AsUint(Visit(gmem.GetBaseAddress())); + const Id diff = OpISub(t_uint, real, base); + const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2)); + const Id buffer = global_buffers.at(gmem.GetDescriptor()); + return OpAccessChain(t_gmem_uint, buffer, Constant(t_uint, 0), offset); + } + + Id GetSharedMemoryPointer(const SmemNode& smem) { + ASSERT(stage == ShaderType::Compute); + Id address = AsUint(Visit(smem.GetAddress())); + address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); + return OpAccessChain(t_smem_uint, shared_memory, address); + } + static constexpr std::array operation_decompilers = { &SPIRVDecompiler::Assign, @@ -2404,7 +2407,7 @@ private: &SPIRVDecompiler::AtomicImageXor, &SPIRVDecompiler::AtomicImageExchange, - &SPIRVDecompiler::UAtomicAdd, + &SPIRVDecompiler::AtomicAdd, &SPIRVDecompiler::Branch, &SPIRVDecompiler::BranchIndirect, @@ -2500,9 +2503,9 @@ private: Id t_smem_uint{}; - const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float); + const Id t_gmem_uint = TypePointer(spv::StorageClass::StorageBuffer, t_uint); const Id t_gmem_array = - Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4U), "GmemArray"); + Name(Decorate(TypeRuntimeArray(t_uint), spv::Decoration::ArrayStride, 4U), "GmemArray"); const Id t_gmem_struct = MemberDecorate( Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h index 10794be1c..f5dc14d9e 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h @@ -102,9 +102,6 @@ struct Specialization final { Maxwell::TessellationSpacing spacing{}; bool clockwise{}; } tessellation; - - // Fragment specific - std::bitset<8> enabled_rendertargets; }; // Old gcc versions don't consider this trivially copyable. // static_assert(std::is_trivially_copyable_v<Specialization>); diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index 7591a715f..b5fbc4d58 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp @@ -19,9 +19,12 @@ namespace VideoCommon::Shader { using Tegra::Shader::AtomicOp; using Tegra::Shader::AtomicType; using Tegra::Shader::Attribute; +using Tegra::Shader::GlobalAtomicOp; +using Tegra::Shader::GlobalAtomicType; using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; using Tegra::Shader::Register; +using Tegra::Shader::StoreType; namespace { @@ -61,6 +64,27 @@ u32 GetMemorySize(Tegra::Shader::UniformType uniform_type) { } } +Node ExtractUnaligned(Node value, Node address, u32 mask, u32 size) { + Node offset = Operation(OperationCode::UBitwiseAnd, address, Immediate(mask)); + offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3)); + return Operation(OperationCode::UBitfieldExtract, std::move(value), std::move(offset), + Immediate(size)); +} + +Node InsertUnaligned(Node dest, Node value, Node address, u32 mask, u32 size) { + Node offset = Operation(OperationCode::UBitwiseAnd, std::move(address), Immediate(mask)); + offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3)); + return Operation(OperationCode::UBitfieldInsert, std::move(dest), std::move(value), + std::move(offset), Immediate(size)); +} + +Node Sign16Extend(Node value) { + Node sign = Operation(OperationCode::UBitwiseAnd, value, Immediate(1U << 15)); + Node is_sign = Operation(OperationCode::LogicalUEqual, std::move(sign), Immediate(1U << 15)); + Node extend = Operation(OperationCode::Select, is_sign, Immediate(0xFFFF0000), Immediate(0)); + return Operation(OperationCode::UBitwiseOr, std::move(value), std::move(extend)); +} + } // Anonymous namespace u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { @@ -136,26 +160,31 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown)); [[fallthrough]]; case OpCode::Id::LD_S: { - const auto GetMemory = [&](s32 offset) { + const auto GetAddress = [&](s32 offset) { ASSERT(offset % 4 == 0); const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset); - const Node address = Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), - immediate_offset); - return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(address) - : GetLocalMemory(address); + return Operation(OperationCode::IAdd, GetRegister(instr.gpr8), immediate_offset); + }; + const auto GetMemory = [&](s32 offset) { + return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(GetAddress(offset)) + : GetLocalMemory(GetAddress(offset)); }; switch (instr.ldst_sl.type.Value()) { - case Tegra::Shader::StoreType::Bits32: - case Tegra::Shader::StoreType::Bits64: - case Tegra::Shader::StoreType::Bits128: { - const u32 count = [&]() { + case StoreType::Signed16: + SetRegister(bb, instr.gpr0, + Sign16Extend(ExtractUnaligned(GetMemory(0), GetAddress(0), 0b10, 16))); + break; + case StoreType::Bits32: + case StoreType::Bits64: + case StoreType::Bits128: { + const u32 count = [&] { switch (instr.ldst_sl.type.Value()) { - case Tegra::Shader::StoreType::Bits32: + case StoreType::Bits32: return 1; - case Tegra::Shader::StoreType::Bits64: + case StoreType::Bits64: return 2; - case Tegra::Shader::StoreType::Bits128: + case StoreType::Bits128: return 4; default: UNREACHABLE(); @@ -212,12 +241,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { // To handle unaligned loads get the bytes used to dereference global memory and extract // those bytes from the loaded u32. if (IsUnaligned(type)) { - Node mask = Immediate(GetUnalignedMask(type)); - Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask)); - offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3)); - - gmem = Operation(OperationCode::UBitfieldExtract, std::move(gmem), - std::move(offset), Immediate(size)); + gmem = ExtractUnaligned(gmem, real_address, GetUnalignedMask(type), size); } SetTemporary(bb, i, gmem); @@ -269,21 +293,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate); }; - const auto set_memory = opcode->get().GetId() == OpCode::Id::ST_L - ? &ShaderIR::SetLocalMemory - : &ShaderIR::SetSharedMemory; + const bool is_local = opcode->get().GetId() == OpCode::Id::ST_L; + const auto set_memory = is_local ? &ShaderIR::SetLocalMemory : &ShaderIR::SetSharedMemory; + const auto get_memory = is_local ? &ShaderIR::GetLocalMemory : &ShaderIR::GetSharedMemory; switch (instr.ldst_sl.type.Value()) { - case Tegra::Shader::StoreType::Bits128: + case StoreType::Bits128: (this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3)); (this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2)); [[fallthrough]]; - case Tegra::Shader::StoreType::Bits64: + case StoreType::Bits64: (this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1)); [[fallthrough]]; - case Tegra::Shader::StoreType::Bits32: + case StoreType::Bits32: (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0)); break; + case StoreType::Signed16: { + Node address = GetAddress(0); + Node memory = (this->*get_memory)(address); + (this->*set_memory)( + bb, address, InsertUnaligned(memory, GetRegister(instr.gpr0), address, 0b10, 16)); + break; + } default: UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(), static_cast<u32>(instr.ldst_sl.type.Value())); @@ -323,18 +354,32 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { Node value = GetRegister(instr.gpr0.Value() + i); if (IsUnaligned(type)) { - Node mask = Immediate(GetUnalignedMask(type)); - Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask)); - offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3)); - - value = Operation(OperationCode::UBitfieldInsert, gmem, std::move(value), offset, - Immediate(size)); + const u32 mask = GetUnalignedMask(type); + value = InsertUnaligned(gmem, std::move(value), real_address, mask, size); } bb.push_back(Operation(OperationCode::Assign, gmem, value)); } break; } + case OpCode::Id::ATOM: { + UNIMPLEMENTED_IF_MSG(instr.atom.operation != GlobalAtomicOp::Add, "operation={}", + static_cast<int>(instr.atom.operation.Value())); + UNIMPLEMENTED_IF_MSG(instr.atom.type != GlobalAtomicType::S32, "type={}", + static_cast<int>(instr.atom.type.Value())); + + const auto [real_address, base_address, descriptor] = + TrackGlobalMemory(bb, instr, true, true); + if (!real_address || !base_address) { + // Tracking failed, skip atomic. + break; + } + + Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor); + Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20)); + SetRegister(bb, instr.gpr0, std::move(value)); + break; + } case OpCode::Id::ATOMS: { UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", static_cast<int>(instr.atoms.operation.Value())); @@ -348,7 +393,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { Node memory = GetSharedMemory(std::move(address)); Node data = GetRegister(instr.gpr20); - Node value = Operation(OperationCode::UAtomicAdd, std::move(memory), std::move(data)); + Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data)); SetRegister(bb, instr.gpr0, std::move(value)); break; } diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index 53a551d27..5f83403db 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -162,7 +162,7 @@ enum class OperationCode { AtomicImageXor, /// (MetaImage, int[N] coords) -> void AtomicImageExchange, /// (MetaImage, int[N] coords) -> void - UAtomicAdd, /// (smem, uint) -> uint + AtomicAdd, /// (memory, {u}int) -> {u}int Branch, /// (uint branch_target) -> void BranchIndirect, /// (uint branch_target) -> void diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 829268b4c..84469b7ba 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp @@ -135,7 +135,7 @@ std::vector<CopyParams> SurfaceBaseImpl::BreakDownLayered(const SurfaceParams& i for (u32 level = 0; level < mipmaps; level++) { const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level); const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level); - result.emplace_back(width, height, layer, level); + result.emplace_back(0, 0, layer, 0, 0, layer, level, level, width, height, 1); } } return result; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 11ae1e66e..a3fb91d29 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -36,9 +36,6 @@ add_executable(yuzu configuration/configure_filesystem.cpp configuration/configure_filesystem.h configuration/configure_filesystem.ui - configuration/configure_gamelist.cpp - configuration/configure_gamelist.h - configuration/configure_gamelist.ui configuration/configure_general.cpp configuration/configure_general.h configuration/configure_general.ui @@ -75,6 +72,9 @@ add_executable(yuzu configuration/configure_touchscreen_advanced.cpp configuration/configure_touchscreen_advanced.h configuration/configure_touchscreen_advanced.ui + configuration/configure_ui.cpp + configuration/configure_ui.h + configuration/configure_ui.ui configuration/configure_web.cpp configuration/configure_web.h configuration/configure_web.ui diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 372427ae2..67b990f1a 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -48,7 +48,7 @@ <string>General</string> </attribute> </widget> - <widget class="ConfigureGameList" name="gameListTab"> + <widget class="ConfigureUi" name="uiTab"> <attribute name="title"> <string>Game List</string> </attribute> @@ -166,9 +166,9 @@ <container>1</container> </customwidget> <customwidget> - <class>ConfigureGameList</class> + <class>ConfigureUi</class> <extends>QWidget</extends> - <header>configuration/configure_gamelist.h</header> + <header>configuration/configure_ui.h</header> <container>1</container> </customwidget> <customwidget> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 8497eaa14..db3b19352 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -34,7 +34,7 @@ void ConfigureDialog::SetConfiguration() {} void ConfigureDialog::ApplyConfiguration() { ui->generalTab->ApplyConfiguration(); - ui->gameListTab->ApplyConfiguration(); + ui->uiTab->ApplyConfiguration(); ui->systemTab->ApplyConfiguration(); ui->profileManagerTab->ApplyConfiguration(); ui->filesystemTab->applyConfiguration(); @@ -74,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>); void ConfigureDialog::PopulateSelectionList() { const std::array<std::pair<QString, QList<QWidget*>>, 5> items{ - {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, + {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, {tr("Graphics"), {ui->graphicsTab}}, {tr("Audio"), {ui->audioTab}}, @@ -108,7 +108,7 @@ void ConfigureDialog::UpdateVisibleTabs() { {ui->audioTab, tr("Audio")}, {ui->debugTab, tr("Debug")}, {ui->webTab, tr("Web")}, - {ui->gameListTab, tr("Game List")}, + {ui->uiTab, tr("UI")}, {ui->filesystemTab, tr("Filesystem")}, {ui->serviceTab, tr("Services")}, }; diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 34e1d7fea..5ef927114 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -15,11 +15,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) ui->setupUi(this); - for (const auto& theme : UISettings::themes) { - ui->theme_combobox->addItem(QString::fromUtf8(theme.first), - QString::fromUtf8(theme.second)); - } - SetConfiguration(); connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); @@ -30,7 +25,6 @@ ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::SetConfiguration() { ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); - ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); @@ -41,8 +35,6 @@ void ConfigureGeneral::SetConfiguration() { void ConfigureGeneral::ApplyConfiguration() { UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); - UISettings::values.theme = - ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 26b3486ff..857119bb3 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -65,39 +65,12 @@ </property> </widget> </item> - <item> - <widget class="QCheckBox" name="toggle_background_pause"> - <property name="text"> - <string>Pause emulation when in background</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="theme_group_box"> - <property name="title"> - <string>Theme</string> - </property> - <layout class="QHBoxLayout" name="theme_qhbox_layout"> - <item> - <layout class="QVBoxLayout" name="theme_qvbox_layout"> <item> - <layout class="QHBoxLayout" name="theme_qhbox_layout_2"> - <item> - <widget class="QLabel" name="theme_label"> - <property name="text"> - <string>Theme:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="theme_combobox"/> - </item> - </layout> + <widget class="QCheckBox" name="toggle_background_pause"> + <property name="text"> + <string>Pause emulation when in background</string> + </property> + </widget> </item> </layout> </item> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 67c9a7c6d..96dec50e2 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -236,6 +236,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i widget->setVisible(false); analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; + analog_map_deadzone = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; + analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { auto* const button = button_map[button_id]; @@ -326,6 +328,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i InputCommon::Polling::DeviceType::Analog); } }); + connect(analog_map_deadzone[analog_id], &QSlider::valueChanged, [=] { + const float deadzone = analog_map_deadzone[analog_id]->value() / 100.0f; + analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1").arg(deadzone)); + analogs_param[analog_id].Set("deadzone", deadzone); + }); } connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); @@ -484,7 +491,7 @@ void ConfigureInputPlayer::ClearAll() { continue; } - analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); + analogs_param[analog_id].Clear(); } } @@ -508,6 +515,23 @@ void ConfigureInputPlayer::UpdateButtonLabels() { AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); } analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); + + auto& param = analogs_param[analog_id]; + auto* const analog_deadzone_slider = analog_map_deadzone[analog_id]; + auto* const analog_deadzone_label = analog_map_deadzone_label[analog_id]; + + if (param.Has("engine") && param.Get("engine", "") == "sdl") { + if (!param.Has("deadzone")) { + param.Set("deadzone", 0.1f); + } + + analog_deadzone_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100)); + analog_deadzone_slider->setVisible(true); + analog_deadzone_label->setVisible(true); + } else { + analog_deadzone_slider->setVisible(false); + analog_deadzone_label->setVisible(false); + } } } diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index c66027651..045704e47 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -97,6 +97,8 @@ private: /// Analog inputs are also represented each with a single button, used to configure with an /// actual analog stick std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; + std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone; + std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label; static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 42db020be..1556481d0 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -170,6 +170,44 @@ </item> </layout> </item> + <item row="4" column="0" colspan="2"> + <layout class="QVBoxLayout" name="sliderRStickDeadzoneVerticalLayout"> + <item> + <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout"> + <item> + <widget class="QLabel" name="labelRStickDeadzone"> + <property name="text"> + <string>Deadzone: 0</string> + </property> + <property name="alignment"> + <enum>Qt::AlignHCenter</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSlider" name="sliderRStickDeadzone"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + <item row="5" column="0"> + <spacer name="RStick_verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> </layout> </widget> </item> @@ -745,6 +783,47 @@ </item> </layout> </item> + <item row="5" column="1" colspan="2"> + <layout class="QVBoxLayout" name="sliderLStickDeadzoneVerticalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout"> + <item> + <widget class="QLabel" name="labelLStickDeadzone"> + <property name="text"> + <string>Deadzone: 0</string> + </property> + <property name="alignment"> + <enum>Qt::AlignHCenter</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QSlider" name="sliderLStickDeadzone"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + <item row="6" column="1"> + <spacer name="LStick_verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> </layout> </widget> </item> diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_ui.cpp index e43e84d39..c4a84cc67 100644 --- a/src/yuzu/configuration/configure_gamelist.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -7,8 +7,8 @@ #include "common/common_types.h" #include "core/settings.h" -#include "ui_configure_gamelist.h" -#include "yuzu/configuration/configure_gamelist.h" +#include "ui_configure_ui.h" +#include "yuzu/configuration/configure_ui.h" #include "yuzu/uisettings.h" namespace { @@ -26,35 +26,40 @@ constexpr std::array row_text_names{ }; } // Anonymous namespace -ConfigureGameList::ConfigureGameList(QWidget* parent) - : QWidget(parent), ui(new Ui::ConfigureGameList) { +ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) { ui->setupUi(this); + for (const auto& theme : UISettings::themes) { + ui->theme_combobox->addItem(QString::fromUtf8(theme.first), + QString::fromUtf8(theme.second)); + } + InitializeIconSizeComboBox(); InitializeRowComboBoxes(); SetConfiguration(); // Force game list reload if any of the relevant settings are changed. - connect(ui->show_unknown, &QCheckBox::stateChanged, this, - &ConfigureGameList::RequestGameListUpdate); + connect(ui->show_unknown, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureGameList::RequestGameListUpdate); + &ConfigureUi::RequestGameListUpdate); connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureGameList::RequestGameListUpdate); + &ConfigureUi::RequestGameListUpdate); connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, - &ConfigureGameList::RequestGameListUpdate); + &ConfigureUi::RequestGameListUpdate); // Update text ComboBoxes after user interaction. connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated), - [=]() { ConfigureGameList::UpdateSecondRowComboBox(); }); + [=]() { ConfigureUi::UpdateSecondRowComboBox(); }); connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated), - [=]() { ConfigureGameList::UpdateFirstRowComboBox(); }); + [=]() { ConfigureUi::UpdateFirstRowComboBox(); }); } -ConfigureGameList::~ConfigureGameList() = default; +ConfigureUi::~ConfigureUi() = default; -void ConfigureGameList::ApplyConfiguration() { +void ConfigureUi::ApplyConfiguration() { + UISettings::values.theme = + ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); UISettings::values.show_unknown = ui->show_unknown->isChecked(); UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); @@ -63,18 +68,19 @@ void ConfigureGameList::ApplyConfiguration() { Settings::Apply(); } -void ConfigureGameList::RequestGameListUpdate() { +void ConfigureUi::RequestGameListUpdate() { UISettings::values.is_game_list_reload_pending.exchange(true); } -void ConfigureGameList::SetConfiguration() { +void ConfigureUi::SetConfiguration() { + ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); ui->show_unknown->setChecked(UISettings::values.show_unknown); ui->show_add_ons->setChecked(UISettings::values.show_add_ons); ui->icon_size_combobox->setCurrentIndex( ui->icon_size_combobox->findData(UISettings::values.icon_size)); } -void ConfigureGameList::changeEvent(QEvent* event) { +void ConfigureUi::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); } @@ -82,7 +88,7 @@ void ConfigureGameList::changeEvent(QEvent* event) { QWidget::changeEvent(event); } -void ConfigureGameList::RetranslateUI() { +void ConfigureUi::RetranslateUI() { ui->retranslateUi(this); for (int i = 0; i < ui->icon_size_combobox->count(); i++) { @@ -97,18 +103,18 @@ void ConfigureGameList::RetranslateUI() { } } -void ConfigureGameList::InitializeIconSizeComboBox() { +void ConfigureUi::InitializeIconSizeComboBox() { for (const auto& size : default_icon_sizes) { ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first); } } -void ConfigureGameList::InitializeRowComboBoxes() { +void ConfigureUi::InitializeRowComboBoxes() { UpdateFirstRowComboBox(true); UpdateSecondRowComboBox(true); } -void ConfigureGameList::UpdateFirstRowComboBox(bool init) { +void ConfigureUi::UpdateFirstRowComboBox(bool init) { const int currentIndex = init ? UISettings::values.row_1_text_id : ui->row_1_text_combobox->findData(ui->row_1_text_combobox->currentData()); @@ -127,7 +133,7 @@ void ConfigureGameList::UpdateFirstRowComboBox(bool init) { ui->row_1_text_combobox->findData(ui->row_2_text_combobox->currentData())); } -void ConfigureGameList::UpdateSecondRowComboBox(bool init) { +void ConfigureUi::UpdateSecondRowComboBox(bool init) { const int currentIndex = init ? UISettings::values.row_2_text_id : ui->row_2_text_combobox->findData(ui->row_2_text_combobox->currentData()); diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_ui.h index ecd3fa174..d471afe99 100644 --- a/src/yuzu/configuration/configure_gamelist.h +++ b/src/yuzu/configuration/configure_ui.h @@ -8,15 +8,15 @@ #include <QWidget> namespace Ui { -class ConfigureGameList; +class ConfigureUi; } -class ConfigureGameList : public QWidget { +class ConfigureUi : public QWidget { Q_OBJECT public: - explicit ConfigureGameList(QWidget* parent = nullptr); - ~ConfigureGameList() override; + explicit ConfigureUi(QWidget* parent = nullptr); + ~ConfigureUi() override; void ApplyConfiguration(); @@ -34,5 +34,5 @@ private: void UpdateFirstRowComboBox(bool init = false); void UpdateSecondRowComboBox(bool init = false); - std::unique_ptr<Ui::ConfigureGameList> ui; + std::unique_ptr<Ui::ConfigureUi> ui; }; diff --git a/src/yuzu/configuration/configure_gamelist.ui b/src/yuzu/configuration/configure_ui.ui index 7a69377e7..aa36bd112 100644 --- a/src/yuzu/configuration/configure_gamelist.ui +++ b/src/yuzu/configuration/configure_ui.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ConfigureGameList</class> - <widget class="QWidget" name="ConfigureGameList"> + <class>ConfigureUi</class> + <widget class="QWidget" name="ConfigureUi"> <property name="geometry"> <rect> <x>0</x> @@ -21,7 +21,34 @@ <property name="title"> <string>General</string> </property> - <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="theme_label"> + <property name="text"> + <string>Theme:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="theme_combobox"/> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="GameListGroupBox"> + <property name="title"> + <string>Game List</string> + </property> + <layout class="QHBoxLayout" name="GameListHorizontalLayout"> <item> <layout class="QVBoxLayout" name="GeneralVerticalLayout"> <item> @@ -38,19 +65,6 @@ </property> </widget> </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="IconSizeGroupBox"> - <property name="title"> - <string>Icon Size</string> - </property> - <layout class="QHBoxLayout" name="icon_size_qhbox_layout"> - <item> - <layout class="QVBoxLayout" name="icon_size_qvbox_layout"> <item> <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> <item> @@ -65,19 +79,6 @@ </item> </layout> </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="RowGroupBox"> - <property name="title"> - <string>Row Text</string> - </property> - <layout class="QHBoxLayout" name="RowHorizontalLayout"> - <item> - <layout class="QVBoxLayout" name="RowVerticalLayout"> <item> <layout class="QHBoxLayout" name="row_1_qhbox_layout"> <item> |