summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/swap.h46
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp30
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp34
-rw-r--r--src/core/file_sys/bis_factory.cpp23
-rw-r--r--src/core/file_sys/sdmc_factory.cpp4
-rw-r--r--src/core/settings.h47
-rw-r--r--src/input_common/CMakeLists.txt3
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp11
-rw-r--r--src/input_common/gcadapter/gc_adapter.h15
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp42
-rw-r--r--src/input_common/gcadapter/gc_poller.h2
-rw-r--r--src/input_common/main.cpp1
-rw-r--r--src/input_common/udp/client.cpp2
-rw-r--r--src/video_core/engines/maxwell_dma.cpp300
-rw-r--r--src/video_core/engines/maxwell_dma.h348
-rw-r--r--src/video_core/texture_cache/surface_params.cpp5
-rw-r--r--src/video_core/texture_cache/surface_params.h2
-rw-r--r--src/video_core/textures/decoders.cpp134
-rw-r--r--src/video_core/textures/decoders.h38
-rw-r--r--src/yuzu/CMakeLists.txt6
-rw-r--r--src/yuzu/configuration/config.cpp88
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure.ui22
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp61
-rw-r--r--src/yuzu/configuration/configure_cpu.h33
-rw-r--r--src/yuzu/configuration/configure_cpu.ui92
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.cpp65
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.h31
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.ui174
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp7
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp27
-rw-r--r--src/yuzu/configuration/configure_filesystem.ui121
-rw-r--r--src/yuzu/configuration/configure_general.cpp2
-rw-r--r--src/yuzu_cmd/config.cpp11
-rw-r--r--src/yuzu_cmd/default_ini.h36
-rw-r--r--src/yuzu_tester/default_ini.h33
38 files changed, 1223 insertions, 684 deletions
diff --git a/src/common/swap.h b/src/common/swap.h
index 71932c2bb..7665942a2 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -17,43 +17,14 @@
#pragma once
-#include <type_traits>
-
#if defined(_MSC_VER)
#include <cstdlib>
#endif
+#include <bit>
#include <cstring>
+#include <type_traits>
#include "common/common_types.h"
-// GCC
-#ifdef __GNUC__
-
-#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
-#define COMMON_LITTLE_ENDIAN 1
-#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN)
-#define COMMON_BIG_ENDIAN 1
-#endif
-
-// LLVM/clang
-#elif defined(__clang__)
-
-#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
-#define COMMON_LITTLE_ENDIAN 1
-#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN)
-#define COMMON_BIG_ENDIAN 1
-#endif
-
-// MSVC
-#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
-
-#define COMMON_LITTLE_ENDIAN 1
-#endif
-
-// Worst case, default to little endian.
-#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN
-#define COMMON_LITTLE_ENDIAN 1
-#endif
-
namespace Common {
#ifdef _MSC_VER
@@ -675,17 +646,8 @@ struct AddEndian<T, SwapTag> {
};
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
-#if COMMON_LITTLE_ENDIAN
-
-using LETag = KeepTag;
-using BETag = SwapTag;
-
-#else
-
-using BETag = KeepTag;
-using LETag = SwapTag;
-
-#endif
+using LETag = std::conditional_t<std::endian::native == std::endian::little, KeepTag, SwapTag>;
+using BETag = std::conditional_t<std::endian::native == std::endian::big, KeepTag, SwapTag>;
// Aliases for LE types
using u16_le = AddEndian<u16, LETag>::type;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 0d4ab95b7..443ca72eb 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -142,10 +142,32 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
// Timing
config.wall_clock_cntpct = uses_wall_clock;
- // Optimizations
- if (Settings::values.disable_cpu_opt) {
- config.enable_optimizations = false;
- config.enable_fast_dispatch = false;
+ // Safe optimizations
+ if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
+ if (!Settings::values.cpuopt_page_tables) {
+ config.page_table = nullptr;
+ }
+ if (!Settings::values.cpuopt_block_linking) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
+ }
+ if (!Settings::values.cpuopt_return_stack_buffer) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
+ }
+ if (!Settings::values.cpuopt_fast_dispatcher) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
+ }
+ if (!Settings::values.cpuopt_context_elimination) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
+ }
+ if (!Settings::values.cpuopt_const_prop) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
+ }
+ if (!Settings::values.cpuopt_misc_ir) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
+ }
+ if (!Settings::values.cpuopt_reduce_misalign_checks) {
+ config.only_detect_misalignment_via_page_table_on_page_boundary = false;
+ }
}
return std::make_unique<Dynarmic::A32::Jit>(config);
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 790981034..a63a04a25 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -191,15 +191,37 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
// Unpredictable instructions
config.define_unpredictable_behaviour = true;
- // Optimizations
- if (Settings::values.disable_cpu_opt) {
- config.enable_optimizations = false;
- config.enable_fast_dispatch = false;
- }
-
// Timing
config.wall_clock_cntpct = uses_wall_clock;
+ // Safe optimizations
+ if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
+ if (!Settings::values.cpuopt_page_tables) {
+ config.page_table = nullptr;
+ }
+ if (!Settings::values.cpuopt_block_linking) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
+ }
+ if (!Settings::values.cpuopt_return_stack_buffer) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
+ }
+ if (!Settings::values.cpuopt_fast_dispatcher) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
+ }
+ if (!Settings::values.cpuopt_context_elimination) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
+ }
+ if (!Settings::values.cpuopt_const_prop) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
+ }
+ if (!Settings::values.cpuopt_misc_ir) {
+ config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
+ }
+ if (!Settings::values.cpuopt_reduce_misalign_checks) {
+ config.only_detect_misalignment_via_page_table_on_page_boundary = false;
+ }
+ }
+
return std::make_shared<Dynarmic::A64::Jit>(config);
}
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 8935a62c3..285277ef8 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -12,6 +12,10 @@
namespace FileSys {
+constexpr u64 NAND_USER_SIZE = 0x680000000; // 26624 MiB
+constexpr u64 NAND_SYSTEM_SIZE = 0xA0000000; // 2560 MiB
+constexpr u64 NAND_TOTAL_SIZE = 0x747C00000; // 29820 MiB
+
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
dump_root(std::move(dump_root_)),
@@ -110,30 +114,29 @@ VirtualDir BISFactory::GetImageDirectory() const {
u64 BISFactory::GetSystemNANDFreeSpace() const {
const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
- if (sys_dir == nullptr)
- return 0;
+ if (sys_dir == nullptr) {
+ return GetSystemNANDTotalSpace();
+ }
return GetSystemNANDTotalSpace() - sys_dir->GetSize();
}
u64 BISFactory::GetSystemNANDTotalSpace() const {
- return static_cast<u64>(Settings::values.nand_system_size);
+ return NAND_SYSTEM_SIZE;
}
u64 BISFactory::GetUserNANDFreeSpace() const {
- const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
- if (usr_dir == nullptr)
- return 0;
-
- return GetUserNANDTotalSpace() - usr_dir->GetSize();
+ // For some reason games such as BioShock 1 checks whether this is exactly 0x680000000 bytes.
+ // Set the free space to be 1 MiB less than the total as a workaround to this issue.
+ return GetUserNANDTotalSpace() - 0x100000;
}
u64 BISFactory::GetUserNANDTotalSpace() const {
- return static_cast<u64>(Settings::values.nand_user_size);
+ return NAND_USER_SIZE;
}
u64 BISFactory::GetFullNANDTotalSpace() const {
- return static_cast<u64>(Settings::values.nand_total_size);
+ return NAND_TOTAL_SIZE;
}
VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index 5113a1ca6..6f732e4d8 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -10,6 +10,8 @@
namespace FileSys {
+constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB
+
SDMCFactory::SDMCFactory(VirtualDir dir_)
: dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
@@ -46,7 +48,7 @@ u64 SDMCFactory::GetSDMCFreeSpace() const {
}
u64 SDMCFactory::GetSDMCTotalSpace() const {
- return static_cast<u64>(Settings::values.sdmc_size);
+ return SDMC_TOTAL_SIZE;
}
} // namespace FileSys
diff --git a/src/core/settings.h b/src/core/settings.h
index 850ca4072..3eb336f75 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -346,31 +346,6 @@ struct TouchscreenInput {
u32 rotation_angle;
};
-enum class NANDTotalSize : u64 {
- S29_1GB = 0x747C00000ULL,
-};
-
-enum class NANDUserSize : u64 {
- S26GB = 0x680000000ULL,
-};
-
-enum class NANDSystemSize : u64 {
- S2_5GB = 0xA0000000,
-};
-
-enum class SDMCSize : u64 {
- S1GB = 0x40000000,
- S2GB = 0x80000000,
- S4GB = 0x100000000ULL,
- S8GB = 0x200000000ULL,
- S16GB = 0x400000000ULL,
- S32GB = 0x800000000ULL,
- S64GB = 0x1000000000ULL,
- S128GB = 0x2000000000ULL,
- S256GB = 0x4000000000ULL,
- S1TB = 0x10000000000ULL,
-};
-
enum class RendererBackend {
OpenGL = 0,
Vulkan = 1,
@@ -382,6 +357,11 @@ enum class GPUAccuracy : u32 {
Extreme = 2,
};
+enum class CPUAccuracy {
+ Accurate = 0,
+ DebugMode = 1,
+};
+
extern bool configuring_global;
template <typename Type>
@@ -427,6 +407,18 @@ struct Values {
// Core
Setting<bool> use_multi_core;
+ // Cpu
+ CPUAccuracy cpu_accuracy;
+
+ bool cpuopt_page_tables;
+ bool cpuopt_block_linking;
+ bool cpuopt_return_stack_buffer;
+ bool cpuopt_fast_dispatcher;
+ bool cpuopt_context_elimination;
+ bool cpuopt_const_prop;
+ bool cpuopt_misc_ir;
+ bool cpuopt_reduce_misalign_checks;
+
// Renderer
Setting<RendererBackend> renderer_backend;
bool renderer_debug;
@@ -491,10 +483,6 @@ struct Values {
bool gamecard_inserted;
bool gamecard_current_game;
std::string gamecard_path;
- NANDTotalSize nand_total_size;
- NANDSystemSize nand_system_size;
- NANDUserSize nand_user_size;
- SDMCSize sdmc_size;
// Debugging
bool record_frame_times;
@@ -505,7 +493,6 @@ struct Values {
bool dump_nso;
bool reporting_services;
bool quest_flag;
- bool disable_cpu_opt;
bool disable_macro_jit;
// Misceallaneous
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 3bd76dd23..317c25bad 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -30,7 +30,8 @@ if(SDL2_FOUND)
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
endif()
-target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES})
+target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR})
+target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index 6d9f4d9eb..38210ffcb 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -4,6 +4,7 @@
#include <chrono>
#include <thread>
+#include <libusb.h>
#include "common/logging/log.h"
#include "input_common/gcadapter/gc_adapter.h"
@@ -33,7 +34,7 @@ Adapter::Adapter() {
}
}
-GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
+GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
GCPadStatus pad = {};
bool get_origin = false;
@@ -198,7 +199,7 @@ void Adapter::StartScanThread() {
}
detect_thread_running = true;
- detect_thread = std::thread([=] { ScanThreadFunc(); });
+ detect_thread = std::thread(&Adapter::ScanThreadFunc, this);
}
void Adapter::StopScanThread() {
@@ -227,7 +228,7 @@ void Adapter::Setup() {
}
if (devices != nullptr) {
- for (std::size_t index = 0; index < device_count; ++index) {
+ for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) {
if (CheckDeviceAccess(devices[index])) {
// GC Adapter found and accessible, registering it
GetGCEndpoint(devices[index]);
@@ -357,11 +358,11 @@ void Adapter::Reset() {
}
}
-bool Adapter::DeviceConnected(int port) {
+bool Adapter::DeviceConnected(std::size_t port) {
return adapter_controllers_status[port] != ControllerTypes::None;
}
-void Adapter::ResetDeviceType(int port) {
+void Adapter::ResetDeviceType(std::size_t port) {
adapter_controllers_status[port] = ControllerTypes::None;
}
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index b1c2a1958..e2cdd6255 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -8,10 +8,13 @@
#include <mutex>
#include <thread>
#include <unordered_map>
-#include <libusb.h>
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
+struct libusb_context;
+struct libusb_device;
+struct libusb_device_handle;
+
namespace GCAdapter {
enum {
@@ -97,6 +100,9 @@ public:
void BeginConfiguration();
void EndConfiguration();
+ /// Returns true if there is a device connected to port
+ bool DeviceConnected(std::size_t port);
+
std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
@@ -104,7 +110,7 @@ public:
const std::array<GCState, 4>& GetPadState() const;
private:
- GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload);
+ GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
void PadToState(const GCPadStatus& pad, GCState& state);
@@ -116,11 +122,8 @@ private:
/// Stop scanning for the adapter
void StopScanThread();
- /// Returns true if there is a device connected to port
- bool DeviceConnected(int port);
-
/// Resets status of device connected to port
- void ResetDeviceType(int port);
+ void ResetDeviceType(std::size_t port);
/// Returns true if we successfully gain access to GC Adapter
bool CheckDeviceAccess(libusb_device* device);
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 385ce8430..b20419ec3 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -6,6 +6,7 @@
#include <list>
#include <mutex>
#include <utility>
+#include "common/assert.h"
#include "common/threadsafe_queue.h"
#include "input_common/gcadapter/gc_adapter.h"
#include "input_common/gcadapter/gc_poller.h"
@@ -20,7 +21,10 @@ public:
~GCButton() override;
bool GetStatus() const override {
- return gcadapter->GetPadState()[port].buttons.at(button);
+ if (gcadapter->DeviceConnected(port)) {
+ return gcadapter->GetPadState()[port].buttons.at(button);
+ }
+ return false;
}
private:
@@ -43,13 +47,17 @@ public:
}
bool GetStatus() const override {
- const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f;
- if (trigger_if_greater) {
- // TODO: Might be worthwile to set a slider for the trigger threshold. It is currently
- // always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
- return axis_value > threshold;
+ if (gcadapter->DeviceConnected(port)) {
+ const float axis_value =
+ (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f;
+ if (trigger_if_greater) {
+ // TODO: Might be worthwile to set a slider for the trigger threshold. It is
+ // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
+ return axis_value > threshold;
+ }
+ return axis_value < -threshold;
}
- return axis_value < -threshold;
+ return false;
}
private:
@@ -94,9 +102,12 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
adapter.get());
}
+
+ UNREACHABLE();
+ return nullptr;
}
-Common::ParamPackage GCButtonFactory::GetNextInput() {
+Common::ParamPackage GCButtonFactory::GetNextInput() const {
Common::ParamPackage params;
GCAdapter::GCPadStatus pad;
auto& queue = adapter->GetPadQueue();
@@ -147,11 +158,14 @@ public:
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {}
float GetAxis(int axis) const {
- std::lock_guard lock{mutex};
- // division is not by a perfect 128 to account for some variance in center location
- // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
- // [20-230]
- return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f;
+ if (gcadapter->DeviceConnected(port)) {
+ std::lock_guard lock{mutex};
+ // division is not by a perfect 128 to account for some variance in center location
+ // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
+ // [20-230]
+ return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f;
+ }
+ return 0.0f;
}
std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
@@ -249,7 +263,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
const u8 axis = static_cast<u8>(pad.axis);
if (analog_x_axis == -1) {
analog_x_axis = axis;
- controller_number = port;
+ controller_number = static_cast<int>(port);
} else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) {
analog_y_axis = axis;
}
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h
index e96af7d51..0527f328f 100644
--- a/src/input_common/gcadapter/gc_poller.h
+++ b/src/input_common/gcadapter/gc_poller.h
@@ -25,7 +25,7 @@ public:
*/
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
- Common::ParamPackage GetNextInput();
+ Common::ParamPackage GetNextInput() const;
/// For device input configuration/polling
void BeginConfiguration();
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index fd0af1019..b9d5d0ec3 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,7 +4,6 @@
#include <memory>
#include <thread>
-#include <libusb.h>
#include "common/param_package.h"
#include "input_common/analog_from_button.h"
#include "input_common/gcadapter/gc_adapter.h"
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index da5227058..e63c73c4f 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -234,7 +234,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
std::function<void(Status)> status_callback,
std::function<void(u16, u16, u16, u16)> data_callback) {
- std::thread([=] {
+ std::thread([=, this] {
constexpr u16 CALIBRATION_THRESHOLD = 100;
u16 min_x{UINT16_MAX};
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 01d7df405..a2d3d7823 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -14,50 +14,45 @@
namespace Tegra::Engines {
+using namespace Texture;
+
MaxwellDMA::MaxwellDMA(Core::System& system, MemoryManager& memory_manager)
: system{system}, memory_manager{memory_manager} {}
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
- ASSERT_MSG(method < Regs::NUM_REGS,
- "Invalid MaxwellDMA register, increase the size of the Regs structure");
+ ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
regs.reg_array[method] = method_argument;
-#define MAXWELLDMA_REG_INDEX(field_name) \
- (offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
-
- switch (method) {
- case MAXWELLDMA_REG_INDEX(exec): {
- HandleCopy();
- break;
- }
+ if (method == offsetof(Regs, launch_dma) / sizeof(u32)) {
+ Launch();
}
-
-#undef MAXWELLDMA_REG_INDEX
}
void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
u32 methods_pending) {
- for (std::size_t i = 0; i < amount; i++) {
+ for (size_t i = 0; i < amount; ++i) {
CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
}
}
-void MaxwellDMA::HandleCopy() {
- LOG_TRACE(HW_GPU, "Requested a DMA copy");
-
- const GPUVAddr source = regs.src_address.Address();
- const GPUVAddr dest = regs.dst_address.Address();
+void MaxwellDMA::Launch() {
+ LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
+ static_cast<GPUVAddr>(regs.offset_out));
// TODO(Subv): Perform more research and implement all features of this engine.
- ASSERT(regs.exec.enable_swizzle == 0);
- ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
- ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
- ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
- ASSERT(regs.dst_params.pos_x == 0);
- ASSERT(regs.dst_params.pos_y == 0);
-
- if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
+ const LaunchDMA& launch = regs.launch_dma;
+ ASSERT(launch.remap_enable == 0);
+ ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
+ ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
+ ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
+ ASSERT(regs.dst_params.origin.x == 0);
+ ASSERT(regs.dst_params.origin.y == 0);
+
+ const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
+ const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
+
+ if (!is_src_pitch && !is_dst_pitch) {
// If both the source and the destination are in block layout, assert.
UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
return;
@@ -66,144 +61,161 @@ void MaxwellDMA::HandleCopy() {
// All copies here update the main memory, so mark all rasterizer states as invalid.
system.GPU().Maxwell3D().OnMemoryWrite();
- if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
- // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
- // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
- // y_count).
- if (!regs.exec.enable_2d) {
- memory_manager.CopyBlock(dest, source, regs.x_count);
- return;
- }
+ if (is_src_pitch && is_dst_pitch) {
+ CopyPitchToPitch();
+ } else {
+ ASSERT(launch.multi_line_enable == 1);
- // If both the source and the destination are in linear layout, perform a line-by-line
- // copy. We're going to take a subrect of size (x_count, y_count) from the source
- // rectangle. There is no need to manually flush/invalidate the regions because
- // CopyBlock does that for us.
- for (u32 line = 0; line < regs.y_count; ++line) {
- const GPUVAddr source_line = source + line * regs.src_pitch;
- const GPUVAddr dest_line = dest + line * regs.dst_pitch;
- memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
+ if (!is_src_pitch && is_dst_pitch) {
+ CopyBlockLinearToPitch();
+ } else {
+ CopyPitchToBlockLinear();
}
- return;
}
+}
- ASSERT(regs.exec.enable_2d == 1);
-
- if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
-
- ASSERT(regs.src_params.BlockDepth() == 0);
- // Optimized path for micro copies.
- if (regs.dst_pitch * regs.y_count < Texture::GetGOBSize() && regs.dst_pitch <= 64) {
- const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
- const std::size_t src_size = Texture::GetGOBSize();
- const std::size_t dst_size = regs.dst_pitch * regs.y_count;
- u32 pos_x = regs.src_params.pos_x;
- u32 pos_y = regs.src_params.pos_y;
- const u64 offset =
- Texture::GetGOBOffset(regs.src_params.size_x, regs.src_params.size_y, pos_x, pos_y,
- regs.src_params.BlockDepth(), bytes_per_pixel);
- const u32 x_in_gob = 64 / bytes_per_pixel;
- pos_x = pos_x % x_in_gob;
- pos_y = pos_y % 8;
-
- if (read_buffer.size() < src_size) {
- read_buffer.resize(src_size);
- }
-
- if (write_buffer.size() < dst_size) {
- write_buffer.resize(dst_size);
- }
-
- if (Settings::IsGPULevelExtreme()) {
- memory_manager.ReadBlock(source + offset, read_buffer.data(), src_size);
- memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
- } else {
- memory_manager.ReadBlockUnsafe(source + offset, read_buffer.data(), src_size);
- memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
- }
-
- Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
- regs.src_params.size_x, bytes_per_pixel, read_buffer.data(),
- write_buffer.data(), regs.src_params.BlockHeight(), pos_x,
- pos_y);
-
- memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
-
- return;
- }
- // If the input is tiled and the output is linear, deswizzle the input and copy it over.
- const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
- const std::size_t src_size = Texture::CalculateSize(
- true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y,
- regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
-
- const std::size_t src_layer_size = Texture::CalculateSize(
- true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y, 1,
- regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
-
- const std::size_t dst_size = regs.dst_pitch * regs.y_count;
+void MaxwellDMA::CopyPitchToPitch() {
+ // When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D
+ // buffer of length `line_length_in`.
+ // Otherwise we copy a 2D image of dimensions (line_length_in, line_count).
+ if (!regs.launch_dma.multi_line_enable) {
+ memory_manager.CopyBlock(regs.offset_out, regs.offset_in, regs.line_length_in);
+ return;
+ }
- if (read_buffer.size() < src_size) {
- read_buffer.resize(src_size);
- }
+ // Perform a line-by-line copy.
+ // We're going to take a subrect of size (line_length_in, line_count) from the source rectangle.
+ // There is no need to manually flush/invalidate the regions because CopyBlock does that for us.
+ for (u32 line = 0; line < regs.line_count; ++line) {
+ const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
+ const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
+ memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
+ }
+}
- if (write_buffer.size() < dst_size) {
- write_buffer.resize(dst_size);
- }
+void MaxwellDMA::CopyBlockLinearToPitch() {
+ ASSERT(regs.src_params.block_size.depth == 0);
- if (Settings::IsGPULevelExtreme()) {
- memory_manager.ReadBlock(source, read_buffer.data(), src_size);
- memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
- } else {
- memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
- memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
- }
+ // Optimized path for micro copies.
+ const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
+ if (dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X) {
+ FastCopyBlockLinearToPitch();
+ return;
+ }
- Texture::UnswizzleSubrect(
- regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel,
- read_buffer.data() + src_layer_size * regs.src_params.pos_z, write_buffer.data(),
- regs.src_params.BlockHeight(), regs.src_params.pos_x, regs.src_params.pos_y);
+ // Deswizzle the input and copy it over.
+ const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
+ const Parameters& src_params = regs.src_params;
+ const u32 width = src_params.width;
+ const u32 height = src_params.height;
+ const u32 depth = src_params.depth;
+ const u32 block_height = src_params.block_size.height;
+ const u32 block_depth = src_params.block_size.depth;
+ const size_t src_size =
+ CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
+ const size_t src_layer_size =
+ CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
+
+ if (read_buffer.size() < src_size) {
+ read_buffer.resize(src_size);
+ }
+ if (write_buffer.size() < dst_size) {
+ write_buffer.resize(dst_size);
+ }
- memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
+ if (Settings::IsGPULevelExtreme()) {
+ memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
+ memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
} else {
- ASSERT(regs.dst_params.BlockDepth() == 0);
+ memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
+ memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
+ }
- const u32 bytes_per_pixel = regs.src_pitch / regs.x_count;
+ UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel,
+ read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(),
+ block_height, src_params.origin.x, src_params.origin.y);
- const std::size_t dst_size = Texture::CalculateSize(
- true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y,
- regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
+ memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
+}
- const std::size_t dst_layer_size = Texture::CalculateSize(
- true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1,
- regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
+void MaxwellDMA::CopyPitchToBlockLinear() {
+ const auto& dst_params = regs.dst_params;
+ const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in;
+ const u32 width = dst_params.width;
+ const u32 height = dst_params.height;
+ const u32 depth = dst_params.depth;
+ const u32 block_height = dst_params.block_size.height;
+ const u32 block_depth = dst_params.block_size.depth;
+ const size_t dst_size =
+ CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
+ const size_t dst_layer_size =
+ CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
+
+ const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
+
+ if (read_buffer.size() < src_size) {
+ read_buffer.resize(src_size);
+ }
+ if (write_buffer.size() < dst_size) {
+ write_buffer.resize(dst_size);
+ }
- const std::size_t src_size = regs.src_pitch * regs.y_count;
+ if (Settings::IsGPULevelExtreme()) {
+ memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
+ memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
+ } else {
+ memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
+ memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
+ }
- if (read_buffer.size() < src_size) {
- read_buffer.resize(src_size);
- }
+ // If the input is linear and the output is tiled, swizzle the input and copy it over.
+ if (regs.dst_params.block_size.depth > 0) {
+ ASSERT(dst_params.layer == 0);
+ SwizzleSliceToVoxel(regs.line_length_in, regs.line_count, regs.pitch_in, width, height,
+ bytes_per_pixel, block_height, block_depth, dst_params.origin.x,
+ dst_params.origin.y, write_buffer.data(), read_buffer.data());
+ } else {
+ SwizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_in, width, bytes_per_pixel,
+ write_buffer.data() + dst_layer_size * dst_params.layer, read_buffer.data(),
+ block_height, dst_params.origin.x, dst_params.origin.y);
+ }
- if (write_buffer.size() < dst_size) {
- write_buffer.resize(dst_size);
- }
+ memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
+}
- if (Settings::IsGPULevelExtreme()) {
- memory_manager.ReadBlock(source, read_buffer.data(), src_size);
- memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
- } else {
- memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
- memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
- }
+void MaxwellDMA::FastCopyBlockLinearToPitch() {
+ const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
+ const size_t src_size = GOB_SIZE;
+ const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
+ u32 pos_x = regs.src_params.origin.x;
+ u32 pos_y = regs.src_params.origin.y;
+ const u64 offset = GetGOBOffset(regs.src_params.width, regs.src_params.height, pos_x, pos_y,
+ regs.src_params.block_size.height, bytes_per_pixel);
+ const u32 x_in_gob = 64 / bytes_per_pixel;
+ pos_x = pos_x % x_in_gob;
+ pos_y = pos_y % 8;
+
+ if (read_buffer.size() < src_size) {
+ read_buffer.resize(src_size);
+ }
- // If the input is linear and the output is tiled, swizzle the input and copy it over.
- Texture::SwizzleSubrect(
- regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, bytes_per_pixel,
- write_buffer.data() + dst_layer_size * regs.dst_params.pos_z, read_buffer.data(),
- regs.dst_params.BlockHeight(), regs.dst_params.pos_x, regs.dst_params.pos_y);
+ if (write_buffer.size() < dst_size) {
+ write_buffer.resize(dst_size);
+ }
- memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
+ if (Settings::IsGPULevelExtreme()) {
+ memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size);
+ memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
+ } else {
+ memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(), src_size);
+ memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
}
+
+ UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width,
+ bytes_per_pixel, read_buffer.data(), write_buffer.data(),
+ regs.src_params.block_size.height, pos_x, pos_y);
+
+ memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 502dd8509..50f445efc 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -24,160 +24,190 @@ class MemoryManager;
namespace Tegra::Engines {
/**
- * This Engine is known as GK104_Copy. Documentation can be found in:
+ * This engine is known as gk104_copy. Documentation can be found in:
+ * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h
* https://github.com/envytools/envytools/blob/master/rnndb/fifo/gk104_copy.xml
*/
class MaxwellDMA final : public EngineInterface {
public:
- explicit MaxwellDMA(Core::System& system, MemoryManager& memory_manager);
- ~MaxwellDMA() = default;
-
- /// Write the value to the register identified by method.
- void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
-
- /// Write multiple values to the register identified by method.
- void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
- u32 methods_pending) override;
+ struct PackedGPUVAddr {
+ u32 upper;
+ u32 lower;
+
+ constexpr operator GPUVAddr() const noexcept {
+ return (static_cast<GPUVAddr>(upper & 0xff) << 32) | lower;
+ }
+ };
+
+ union BlockSize {
+ BitField<0, 4, u32> width;
+ BitField<4, 4, u32> height;
+ BitField<8, 4, u32> depth;
+ BitField<12, 4, u32> gob_height;
+ };
+ static_assert(sizeof(BlockSize) == 4);
+
+ union Origin {
+ BitField<0, 16, u32> x;
+ BitField<16, 16, u32> y;
+ };
+ static_assert(sizeof(Origin) == 4);
+
+ struct Parameters {
+ BlockSize block_size;
+ u32 width;
+ u32 height;
+ u32 depth;
+ u32 layer;
+ Origin origin;
+ };
+ static_assert(sizeof(Parameters) == 24);
+
+ struct Semaphore {
+ PackedGPUVAddr address;
+ u32 payload;
+ };
+ static_assert(sizeof(Semaphore) == 12);
+
+ struct RenderEnable {
+ enum class Mode : u32 {
+ FALSE = 0,
+ TRUE = 1,
+ CONDITIONAL = 2,
+ RENDER_IF_EQUAL = 3,
+ RENDER_IF_NOT_EQUAL = 4,
+ };
- struct Regs {
- static constexpr std::size_t NUM_REGS = 0x1D6;
+ PackedGPUVAddr address;
+ BitField<0, 3, Mode> mode;
+ };
+ static_assert(sizeof(RenderEnable) == 12);
+
+ enum class PhysModeTarget : u32 {
+ LOCAL_FB = 0,
+ COHERENT_SYSMEM = 1,
+ NONCOHERENT_SYSMEM = 2,
+ };
+ using PhysMode = BitField<0, 2, PhysModeTarget>;
+
+ union LaunchDMA {
+ enum class DataTransferType : u32 {
+ NONE = 0,
+ PIPELINED = 1,
+ NON_PIPELINED = 2,
+ };
- struct Parameters {
- union {
- BitField<0, 4, u32> block_depth;
- BitField<4, 4, u32> block_height;
- BitField<8, 4, u32> block_width;
- };
- u32 size_x;
- u32 size_y;
- u32 size_z;
- u32 pos_z;
- union {
- BitField<0, 16, u32> pos_x;
- BitField<16, 16, u32> pos_y;
- };
+ enum class SemaphoreType : u32 {
+ NONE = 0,
+ RELEASE_ONE_WORD_SEMAPHORE = 1,
+ RELEASE_FOUR_WORD_SEMAPHORE = 2,
+ };
- u32 BlockHeight() const {
- return block_height.Value();
- }
+ enum class InterruptType : u32 {
+ NONE = 0,
+ BLOCKING = 1,
+ NON_BLOCKING = 2,
+ };
- u32 BlockDepth() const {
- return block_depth.Value();
- }
+ enum class MemoryLayout : u32 {
+ BLOCKLINEAR = 0,
+ PITCH = 1,
};
- static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
+ enum class Type : u32 {
+ VIRTUAL = 0,
+ PHYSICAL = 1,
+ };
- enum class ComponentMode : u32 {
- Src0 = 0,
- Src1 = 1,
- Src2 = 2,
- Src3 = 3,
- Const0 = 4,
- Const1 = 5,
- Zero = 6,
+ enum class SemaphoreReduction : u32 {
+ IMIN = 0,
+ IMAX = 1,
+ IXOR = 2,
+ IAND = 3,
+ IOR = 4,
+ IADD = 5,
+ INC = 6,
+ DEC = 7,
+ FADD = 0xA,
};
- enum class CopyMode : u32 {
- None = 0,
- Unk1 = 1,
- Unk2 = 2,
+ enum class SemaphoreReductionSign : u32 {
+ SIGNED = 0,
+ UNSIGNED = 1,
};
- enum class QueryMode : u32 {
- None = 0,
- Short = 1,
- Long = 2,
+ enum class BypassL2 : u32 {
+ USE_PTE_SETTING = 0,
+ FORCE_VOLATILE = 1,
};
- enum class QueryIntr : u32 {
- None = 0,
- Block = 1,
- NonBlock = 2,
+ BitField<0, 2, DataTransferType> data_transfer_type;
+ BitField<2, 1, u32> flush_enable;
+ BitField<3, 2, SemaphoreType> semaphore_type;
+ BitField<5, 2, InterruptType> interrupt_type;
+ BitField<7, 1, MemoryLayout> src_memory_layout;
+ BitField<8, 1, MemoryLayout> dst_memory_layout;
+ BitField<9, 1, u32> multi_line_enable;
+ BitField<10, 1, u32> remap_enable;
+ BitField<11, 1, u32> rmwdisable;
+ BitField<12, 1, Type> src_type;
+ BitField<13, 1, Type> dst_type;
+ BitField<14, 4, SemaphoreReduction> semaphore_reduction;
+ BitField<18, 1, SemaphoreReductionSign> semaphore_reduction_sign;
+ BitField<19, 1, u32> reduction_enable;
+ BitField<20, 1, BypassL2> bypass_l2;
+ };
+ static_assert(sizeof(LaunchDMA) == 4);
+
+ struct RemapConst {
+ enum Swizzle : u32 {
+ SRC_X = 0,
+ SRC_Y = 1,
+ SRC_Z = 2,
+ SRC_W = 3,
+ CONST_A = 4,
+ CONST_B = 5,
+ NO_WRITE = 6,
};
- union {
- struct {
- INSERT_UNION_PADDING_WORDS(0xC0);
-
- struct {
- union {
- BitField<0, 2, CopyMode> copy_mode;
- BitField<2, 1, u32> flush;
-
- BitField<3, 2, QueryMode> query_mode;
- BitField<5, 2, QueryIntr> query_intr;
-
- BitField<7, 1, u32> is_src_linear;
- BitField<8, 1, u32> is_dst_linear;
-
- BitField<9, 1, u32> enable_2d;
- BitField<10, 1, u32> enable_swizzle;
- };
- } exec;
-
- INSERT_UNION_PADDING_WORDS(0x3F);
-
- struct {
- u32 address_high;
- u32 address_low;
-
- GPUVAddr Address() const {
- return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
- address_low);
- }
- } src_address;
-
- struct {
- u32 address_high;
- u32 address_low;
-
- GPUVAddr Address() const {
- return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
- address_low);
- }
- } dst_address;
-
- u32 src_pitch;
- u32 dst_pitch;
- u32 x_count;
- u32 y_count;
-
- INSERT_UNION_PADDING_WORDS(0xB8);
-
- u32 const0;
- u32 const1;
- union {
- BitField<0, 4, ComponentMode> component0;
- BitField<4, 4, ComponentMode> component1;
- BitField<8, 4, ComponentMode> component2;
- BitField<12, 4, ComponentMode> component3;
- BitField<16, 2, u32> component_size;
- BitField<20, 3, u32> src_num_components;
- BitField<24, 3, u32> dst_num_components;
-
- u32 SrcBytePerPixel() const {
- return src_num_components.Value() * component_size.Value();
- }
- u32 DstBytePerPixel() const {
- return dst_num_components.Value() * component_size.Value();
- }
- } swizzle_config;
+ PackedGPUVAddr address;
- Parameters dst_params;
+ union {
+ BitField<0, 3, Swizzle> dst_x;
+ BitField<4, 3, Swizzle> dst_y;
+ BitField<8, 3, Swizzle> dst_z;
+ BitField<12, 3, Swizzle> dst_w;
+ BitField<16, 2, u32> component_size_minus_one;
+ BitField<20, 2, u32> num_src_components_minus_one;
+ BitField<24, 2, u32> num_dst_components_minus_one;
+ };
+ };
+ static_assert(sizeof(RemapConst) == 12);
- INSERT_UNION_PADDING_WORDS(1);
+ explicit MaxwellDMA(Core::System& system, MemoryManager& memory_manager);
+ ~MaxwellDMA() = default;
- Parameters src_params;
+ /// Write the value to the register identified by method.
+ void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
- INSERT_UNION_PADDING_WORDS(0x13);
- };
- std::array<u32, NUM_REGS> reg_array;
- };
- } regs{};
+ /// Write multiple values to the register identified by method.
+ void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
+ u32 methods_pending) override;
private:
+ /// Performs the copy from the source buffer to the destination buffer as configured in the
+ /// registers.
+ void Launch();
+
+ void CopyPitchToPitch();
+
+ void CopyBlockLinearToPitch();
+
+ void CopyPitchToBlockLinear();
+
+ void FastCopyBlockLinearToPitch();
+
Core::System& system;
MemoryManager& memory_manager;
@@ -185,28 +215,58 @@ private:
std::vector<u8> read_buffer;
std::vector<u8> write_buffer;
- /// Performs the copy from the source buffer to the destination buffer as configured in the
- /// registers.
- void HandleCopy();
-};
+ static constexpr std::size_t NUM_REGS = 0x800;
+ struct Regs {
+ union {
+ struct {
+ u32 reserved[0x40];
+ u32 nop;
+ u32 reserved01[0xf];
+ u32 pm_trigger;
+ u32 reserved02[0x3f];
+ Semaphore semaphore;
+ u32 reserved03[0x2];
+ RenderEnable render_enable;
+ PhysMode src_phys_mode;
+ PhysMode dst_phys_mode;
+ u32 reserved04[0x26];
+ LaunchDMA launch_dma;
+ u32 reserved05[0x3f];
+ PackedGPUVAddr offset_in;
+ PackedGPUVAddr offset_out;
+ u32 pitch_in;
+ u32 pitch_out;
+ u32 line_length_in;
+ u32 line_count;
+ u32 reserved06[0xb8];
+ RemapConst remap_const;
+ Parameters dst_params;
+ u32 reserved07[0x1];
+ Parameters src_params;
+ u32 reserved08[0x275];
+ u32 pm_trigger_end;
+ u32 reserved09[0x3ba];
+ };
+ std::array<u32, NUM_REGS> reg_array;
+ };
+ } regs{};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
-ASSERT_REG_POSITION(exec, 0xC0);
-ASSERT_REG_POSITION(src_address, 0x100);
-ASSERT_REG_POSITION(dst_address, 0x102);
-ASSERT_REG_POSITION(src_pitch, 0x104);
-ASSERT_REG_POSITION(dst_pitch, 0x105);
-ASSERT_REG_POSITION(x_count, 0x106);
-ASSERT_REG_POSITION(y_count, 0x107);
-ASSERT_REG_POSITION(const0, 0x1C0);
-ASSERT_REG_POSITION(const1, 0x1C1);
-ASSERT_REG_POSITION(swizzle_config, 0x1C2);
-ASSERT_REG_POSITION(dst_params, 0x1C3);
-ASSERT_REG_POSITION(src_params, 0x1CA);
+ ASSERT_REG_POSITION(launch_dma, 0xC0);
+ ASSERT_REG_POSITION(offset_in, 0x100);
+ ASSERT_REG_POSITION(offset_out, 0x102);
+ ASSERT_REG_POSITION(pitch_in, 0x104);
+ ASSERT_REG_POSITION(pitch_out, 0x105);
+ ASSERT_REG_POSITION(line_length_in, 0x106);
+ ASSERT_REG_POSITION(line_count, 0x107);
+ ASSERT_REG_POSITION(remap_const, 0x1C0);
+ ASSERT_REG_POSITION(dst_params, 0x1C3);
+ ASSERT_REG_POSITION(src_params, 0x1CA);
#undef ASSERT_REG_POSITION
+};
} // namespace Tegra::Engines
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index 0b2b2b8c4..921562c1f 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -343,8 +343,7 @@ std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) co
size += GetInnerMipmapMemorySize(level, as_host_size, uncompressed);
}
if (is_tiled && is_layered) {
- return Common::AlignBits(size,
- Tegra::Texture::GetGOBSizeShift() + block_height + block_depth);
+ return Common::AlignBits(size, Tegra::Texture::GOB_SIZE_SHIFT + block_height + block_depth);
}
return size;
}
@@ -418,7 +417,7 @@ std::tuple<u32, u32, u32> SurfaceParams::GetBlockOffsetXYZ(u32 offset) const {
const u32 block_size = GetBlockSize();
const u32 block_index = offset / block_size;
const u32 gob_offset = offset % block_size;
- const u32 gob_index = gob_offset / static_cast<u32>(Tegra::Texture::GetGOBSize());
+ const u32 gob_index = gob_offset / static_cast<u32>(Tegra::Texture::GOB_SIZE);
const u32 x_gob_pixels = 64U / GetBytesPerPixel();
const u32 x_block_pixels = x_gob_pixels << block_width;
const u32 y_block_pixels = 8U << block_height;
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h
index 24957df8d..118aa689e 100644
--- a/src/video_core/texture_cache/surface_params.h
+++ b/src/video_core/texture_cache/surface_params.h
@@ -204,7 +204,7 @@ public:
static std::size_t AlignLayered(const std::size_t out_size, const u32 block_height,
const u32 block_depth) {
return Common::AlignBits(out_size,
- Tegra::Texture::GetGOBSizeShift() + block_height + block_depth);
+ Tegra::Texture::GOB_SIZE_SHIFT + block_height + block_depth);
}
/// Converts a width from a type of surface into another. This helps represent the
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 548e4c3fe..98beabef1 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -6,6 +6,7 @@
#include <cstring>
#include "common/alignment.h"
#include "common/assert.h"
+#include "common/bit_util.h"
#include "video_core/gpu.h"
#include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h"
@@ -37,20 +38,10 @@ struct alignas(64) SwizzleTable {
std::array<std::array<u16, M>, N> values{};
};
-constexpr u32 gob_size_x_shift = 6;
-constexpr u32 gob_size_y_shift = 3;
-constexpr u32 gob_size_z_shift = 0;
-constexpr u32 gob_size_shift = gob_size_x_shift + gob_size_y_shift + gob_size_z_shift;
+constexpr u32 FAST_SWIZZLE_ALIGN = 16;
-constexpr u32 gob_size_x = 1U << gob_size_x_shift;
-constexpr u32 gob_size_y = 1U << gob_size_y_shift;
-constexpr u32 gob_size_z = 1U << gob_size_z_shift;
-constexpr u32 gob_size = 1U << gob_size_shift;
-
-constexpr u32 fast_swizzle_align = 16;
-
-constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>();
-constexpr auto fast_swizzle_table = SwizzleTable<gob_size_y, 4, fast_swizzle_align>();
+constexpr auto LEGACY_SWIZZLE_TABLE = SwizzleTable<GOB_SIZE_X, GOB_SIZE_X, GOB_SIZE_Z>();
+constexpr auto FAST_SWIZZLE_TABLE = SwizzleTable<GOB_SIZE_Y, 4, FAST_SWIZZLE_ALIGN>();
/**
* This function manages ALL the GOBs(Group of Bytes) Inside a single block.
@@ -69,17 +60,17 @@ void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, con
u32 y_address = z_address;
u32 pixel_base = layer_z * z + y_start * stride_x;
for (u32 y = y_start; y < y_end; y++) {
- const auto& table = legacy_swizzle_table[y % gob_size_y];
+ const auto& table = LEGACY_SWIZZLE_TABLE[y % GOB_SIZE_Y];
for (u32 x = x_start; x < x_end; x++) {
- const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
+ const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % GOB_SIZE_X]};
const u32 pixel_index{x * out_bytes_per_pixel + pixel_base};
data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
}
pixel_base += stride_x;
- if ((y + 1) % gob_size_y == 0)
- y_address += gob_size;
+ if ((y + 1) % GOB_SIZE_Y == 0)
+ y_address += GOB_SIZE;
}
z_address += xy_block_size;
}
@@ -104,18 +95,18 @@ void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const
u32 y_address = z_address;
u32 pixel_base = layer_z * z + y_start * stride_x;
for (u32 y = y_start; y < y_end; y++) {
- const auto& table = fast_swizzle_table[y % gob_size_y];
- for (u32 xb = x_startb; xb < x_endb; xb += fast_swizzle_align) {
- const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]};
+ const auto& table = FAST_SWIZZLE_TABLE[y % GOB_SIZE_Y];
+ for (u32 xb = x_startb; xb < x_endb; xb += FAST_SWIZZLE_ALIGN) {
+ const u32 swizzle_offset{y_address + table[(xb / FAST_SWIZZLE_ALIGN) % 4]};
const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
const u32 pixel_index{out_x + pixel_base};
data_ptrs[unswizzle ? 1 : 0] = swizzled_data + swizzle_offset;
data_ptrs[unswizzle ? 0 : 1] = unswizzled_data + pixel_index;
- std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align);
+ std::memcpy(data_ptrs[0], data_ptrs[1], FAST_SWIZZLE_ALIGN);
}
pixel_base += stride_x;
- if ((y + 1) % gob_size_y == 0)
- y_address += gob_size;
+ if ((y + 1) % GOB_SIZE_Y == 0)
+ y_address += GOB_SIZE;
}
z_address += xy_block_size;
}
@@ -138,9 +129,9 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
const u32 stride_x = width * out_bytes_per_pixel;
const u32 layer_z = height * stride_x;
- const u32 gob_elements_x = gob_size_x / bytes_per_pixel;
- constexpr u32 gob_elements_y = gob_size_y;
- constexpr u32 gob_elements_z = gob_size_z;
+ const u32 gob_elements_x = GOB_SIZE_X / bytes_per_pixel;
+ constexpr u32 gob_elements_y = GOB_SIZE_Y;
+ constexpr u32 gob_elements_z = GOB_SIZE_Z;
const u32 block_x_elements = gob_elements_x;
const u32 block_y_elements = gob_elements_y * block_height;
const u32 block_z_elements = gob_elements_z * block_depth;
@@ -148,7 +139,7 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
const u32 blocks_on_x = div_ceil(aligned_width, block_x_elements);
const u32 blocks_on_y = div_ceil(height, block_y_elements);
const u32 blocks_on_z = div_ceil(depth, block_z_elements);
- const u32 xy_block_size = gob_size * block_height;
+ const u32 xy_block_size = GOB_SIZE * block_height;
const u32 block_size = xy_block_size * block_depth;
u32 tile_offset = 0;
for (u32 zb = 0; zb < blocks_on_z; zb++) {
@@ -182,7 +173,7 @@ void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing) {
const u32 block_height_size{1U << block_height};
const u32 block_depth_size{1U << block_depth};
- if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % fast_swizzle_align == 0) {
+ if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % FAST_SWIZZLE_ALIGN == 0) {
SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
bytes_per_pixel, out_bytes_per_pixel, block_height_size,
block_depth_size, width_spacing);
@@ -259,25 +250,26 @@ std::vector<u8> UnswizzleTexture(u8* address, u32 tile_size_x, u32 tile_size_y,
}
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
- u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
+ u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data,
u32 block_height_bit, u32 offset_x, u32 offset_y) {
const u32 block_height = 1U << block_height_bit;
- const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
- gob_size_x};
+ const u32 image_width_in_gobs =
+ (swizzled_width * bytes_per_pixel + (GOB_SIZE_X - 1)) / GOB_SIZE_X;
for (u32 line = 0; line < subrect_height; ++line) {
const u32 dst_y = line + offset_y;
const u32 gob_address_y =
- (dst_y / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
- ((dst_y % (gob_size_y * block_height)) / gob_size_y) * gob_size;
- const auto& table = legacy_swizzle_table[dst_y % gob_size_y];
+ (dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
+ ((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
+ const auto& table = LEGACY_SWIZZLE_TABLE[dst_y % GOB_SIZE_Y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 dst_x = x + offset_x;
const u32 gob_address =
- gob_address_y + (dst_x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
- const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % gob_size_x];
- u8* source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
- u8* dest_addr = swizzled_data + swizzled_offset;
+ gob_address_y + (dst_x * bytes_per_pixel / GOB_SIZE_X) * GOB_SIZE * block_height;
+ const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % GOB_SIZE_X];
+ const u32 unswizzled_offset = line * source_pitch + x * bytes_per_pixel;
+ const u8* const source_line = unswizzled_data + unswizzled_offset;
+ u8* const dest_addr = swizzled_data + swizzled_offset;
std::memcpy(dest_addr, source_line, bytes_per_pixel);
}
}
@@ -289,14 +281,15 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
const u32 block_height = 1U << block_height_bit;
for (u32 line = 0; line < subrect_height; ++line) {
const u32 y2 = line + offset_y;
- const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
- ((y2 % (gob_size_y * block_height)) / gob_size_y) * gob_size;
- const auto& table = legacy_swizzle_table[y2 % gob_size_y];
+ const u32 gob_address_y = (y2 / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height +
+ ((y2 % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
+ const auto& table = LEGACY_SWIZZLE_TABLE[y2 % GOB_SIZE_Y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 x2 = (x + offset_x) * bytes_per_pixel;
- const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
- const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
- u8* dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
+ const u32 gob_address = gob_address_y + (x2 / GOB_SIZE_X) * GOB_SIZE * block_height;
+ const u32 swizzled_offset = gob_address + table[x2 % GOB_SIZE_X];
+ const u32 unswizzled_offset = line * dest_pitch + x * bytes_per_pixel;
+ u8* dest_line = unswizzled_data + unswizzled_offset;
u8* source_addr = swizzled_data + swizzled_offset;
std::memcpy(dest_line, source_addr, bytes_per_pixel);
@@ -304,21 +297,48 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
}
}
+void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
+ u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
+ u32 origin_y, u8* output, const u8* input) {
+ UNIMPLEMENTED_IF(origin_x > 0);
+ UNIMPLEMENTED_IF(origin_y > 0);
+
+ const u32 stride = width * bytes_per_pixel;
+ const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
+ const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
+
+ const u32 block_height_mask = (1U << block_height) - 1;
+ const u32 x_shift = Common::CountTrailingZeroes32(GOB_SIZE << (block_height + block_depth));
+
+ for (u32 line = 0; line < line_count; ++line) {
+ const auto& table = LEGACY_SWIZZLE_TABLE[line % GOB_SIZE_Y];
+ const u32 block_y = line / GOB_SIZE_Y;
+ const u32 dst_offset_y =
+ (block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE;
+ for (u32 x = 0; x < line_length_in; ++x) {
+ const u32 dst_offset =
+ ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X];
+ const u32 src_offset = x * bytes_per_pixel + line * pitch;
+ std::memcpy(output + dst_offset, input + src_offset, bytes_per_pixel);
+ }
+ }
+}
+
void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
const u32 block_height_bit, const std::size_t copy_size, const u8* source_data,
u8* swizzle_data) {
const u32 block_height = 1U << block_height_bit;
- const u32 image_width_in_gobs{(width + gob_size_x - 1) / gob_size_x};
+ const u32 image_width_in_gobs{(width + GOB_SIZE_X - 1) / GOB_SIZE_X};
std::size_t count = 0;
for (std::size_t y = dst_y; y < height && count < copy_size; ++y) {
const std::size_t gob_address_y =
- (y / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
- ((y % (gob_size_y * block_height)) / gob_size_y) * gob_size;
- const auto& table = legacy_swizzle_table[y % gob_size_y];
+ (y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
+ ((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
+ const auto& table = LEGACY_SWIZZLE_TABLE[y % GOB_SIZE_Y];
for (std::size_t x = dst_x; x < width && count < copy_size; ++x) {
const std::size_t gob_address =
- gob_address_y + (x / gob_size_x) * gob_size * block_height;
- const std::size_t swizzled_offset = gob_address + table[x % gob_size_x];
+ gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height;
+ const std::size_t swizzled_offset = gob_address + table[x % GOB_SIZE_X];
const u8* source_line = source_data + count;
u8* dest_addr = swizzle_data + swizzled_offset;
count++;
@@ -373,9 +393,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth) {
if (tiled) {
- const u32 aligned_width = Common::AlignBits(width * bytes_per_pixel, gob_size_x_shift);
- const u32 aligned_height = Common::AlignBits(height, gob_size_y_shift + block_height);
- const u32 aligned_depth = Common::AlignBits(depth, gob_size_z_shift + block_depth);
+ const u32 aligned_width = Common::AlignBits(width * bytes_per_pixel, GOB_SIZE_X_SHIFT);
+ const u32 aligned_height = Common::AlignBits(height, GOB_SIZE_Y_SHIFT + block_height);
+ const u32 aligned_depth = Common::AlignBits(depth, GOB_SIZE_Z_SHIFT + block_depth);
return aligned_width * aligned_height * aligned_depth;
} else {
return width * height * depth * bytes_per_pixel;
@@ -386,14 +406,14 @@ u64 GetGOBOffset(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
u32 bytes_per_pixel) {
auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
const u32 gobs_in_block = 1 << block_height;
- const u32 y_blocks = gob_size_y << block_height;
- const u32 x_per_gob = gob_size_x / bytes_per_pixel;
+ const u32 y_blocks = GOB_SIZE_Y << block_height;
+ const u32 x_per_gob = GOB_SIZE_X / bytes_per_pixel;
const u32 x_blocks = div_ceil(width, x_per_gob);
- const u32 block_size = gob_size * gobs_in_block;
+ const u32 block_size = GOB_SIZE * gobs_in_block;
const u32 stride = block_size * x_blocks;
const u32 base = (dst_y / y_blocks) * stride + (dst_x / x_per_gob) * block_size;
const u32 relative_y = dst_y % y_blocks;
- return base + (relative_y / gob_size_y) * gob_size;
+ return base + (relative_y / GOB_SIZE_Y) * GOB_SIZE;
}
} // namespace Tegra::Texture
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 06f3ebf87..232b696b3 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -10,15 +10,15 @@
namespace Tegra::Texture {
-// GOBSize constant. Calculated by 64 bytes in x multiplied by 8 y coords, represents
-// an small rect of (64/bytes_per_pixel)X8.
-inline std::size_t GetGOBSize() {
- return 512;
-}
+constexpr u32 GOB_SIZE_X = 64;
+constexpr u32 GOB_SIZE_Y = 8;
+constexpr u32 GOB_SIZE_Z = 1;
+constexpr u32 GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z;
-inline std::size_t GetGOBSizeShift() {
- return 9;
-}
+constexpr std::size_t GOB_SIZE_X_SHIFT = 6;
+constexpr std::size_t GOB_SIZE_Y_SHIFT = 3;
+constexpr std::size_t GOB_SIZE_Z_SHIFT = 0;
+constexpr std::size_t GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
/// Unswizzles a swizzled texture without changing its format.
void UnswizzleTexture(u8* unswizzled_data, u8* address, u32 tile_size_x, u32 tile_size_y,
@@ -48,14 +48,32 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height
/// Copies an untiled subrectangle into a tiled surface.
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
- u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
- u32 offset_x, u32 offset_y);
+ u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data,
+ u32 block_height_bit, u32 offset_x, u32 offset_y);
/// Copies a tiled subrectangle into a linear surface.
void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
u32 offset_x, u32 offset_y);
+/// @brief Swizzles a 2D array of pixels into a 3D texture
+/// @param line_length_in Number of pixels per line
+/// @param line_count Number of lines
+/// @param pitch Number of bytes per line
+/// @param width Width of the swizzled texture
+/// @param height Height of the swizzled texture
+/// @param bytes_per_pixel Number of bytes used per pixel
+/// @param block_height Block height shift
+/// @param block_depth Block depth shift
+/// @param origin_x Column offset in pixels of the swizzled texture
+/// @param origin_y Row offset in pixels of the swizzled texture
+/// @param output Pointer to the pixels of the swizzled texture
+/// @param input Pointer to the 2D array of pixels used as input
+/// @pre input and output points to an array large enough to hold the number of bytes used
+void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
+ u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
+ u32 origin_y, u8* output, const u8* input);
+
void SwizzleKepler(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
std::size_t copy_size, const u8* source_data, u8* swizzle_data);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index ff7d9c1fa..a862b2610 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -30,6 +30,12 @@ add_executable(yuzu
configuration/configure_audio.cpp
configuration/configure_audio.h
configuration/configure_audio.ui
+ configuration/configure_cpu.cpp
+ configuration/configure_cpu.h
+ configuration/configure_cpu.ui
+ configuration/configure_cpu_debug.cpp
+ configuration/configure_cpu_debug.h
+ configuration/configure_cpu_debug.ui
configuration/configure_debug.cpp
configuration/configure_debug.h
configuration/configure_debug.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1b2b1b2bb..9e9b38214 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -505,22 +505,6 @@ void Config::ReadDataStorageValues() {
ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
Settings::values.gamecard_path =
ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
- Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
- ReadSetting(QStringLiteral("nand_total_size"),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)))
- .toULongLong());
- Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
- ReadSetting(QStringLiteral("nand_user_size"),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)))
- .toULongLong());
- Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
- ReadSetting(QStringLiteral("nand_system_size"),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)))
- .toULongLong());
- Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
- ReadSetting(QStringLiteral("sdmc_size"),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)))
- .toULongLong());
qt_config->endGroup();
}
@@ -540,8 +524,6 @@ void Config::ReadDebuggingValues() {
Settings::values.reporting_services =
ReadSetting(QStringLiteral("reporting_services"), false).toBool();
Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
- Settings::values.disable_cpu_opt =
- ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool();
Settings::values.disable_macro_jit =
ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool();
@@ -633,6 +615,34 @@ void Config::ReadPathValues() {
qt_config->endGroup();
}
+void Config::ReadCpuValues() {
+ qt_config->beginGroup(QStringLiteral("Cpu"));
+
+ if (global) {
+ Settings::values.cpu_accuracy = static_cast<Settings::CPUAccuracy>(
+ ReadSetting(QStringLiteral("cpu_accuracy"), 0).toInt());
+
+ Settings::values.cpuopt_page_tables =
+ ReadSetting(QStringLiteral("cpuopt_page_tables"), true).toBool();
+ Settings::values.cpuopt_block_linking =
+ ReadSetting(QStringLiteral("cpuopt_block_linking"), true).toBool();
+ Settings::values.cpuopt_return_stack_buffer =
+ ReadSetting(QStringLiteral("cpuopt_return_stack_buffer"), true).toBool();
+ Settings::values.cpuopt_fast_dispatcher =
+ ReadSetting(QStringLiteral("cpuopt_fast_dispatcher"), true).toBool();
+ Settings::values.cpuopt_context_elimination =
+ ReadSetting(QStringLiteral("cpuopt_context_elimination"), true).toBool();
+ Settings::values.cpuopt_const_prop =
+ ReadSetting(QStringLiteral("cpuopt_const_prop"), true).toBool();
+ Settings::values.cpuopt_misc_ir =
+ ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
+ Settings::values.cpuopt_reduce_misalign_checks =
+ ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
+ }
+
+ qt_config->endGroup();
+}
+
void Config::ReadRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
@@ -829,6 +839,7 @@ void Config::ReadValues() {
ReadMiscellaneousValues();
}
ReadCoreValues();
+ ReadCpuValues();
ReadRendererValues();
ReadAudioValues();
ReadSystemValues();
@@ -929,6 +940,7 @@ void Config::SaveValues() {
SaveMiscellaneousValues();
}
SaveCoreValues();
+ SaveCpuValues();
SaveRendererValues();
SaveAudioValues();
SaveSystemValues();
@@ -1006,18 +1018,7 @@ void Config::SaveDataStorageValues() {
false);
WriteSetting(QStringLiteral("gamecard_path"),
QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
- WriteSetting(QStringLiteral("nand_total_size"),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)));
- WriteSetting(QStringLiteral("nand_user_size"),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)));
- WriteSetting(QStringLiteral("nand_system_size"),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)));
- WriteSetting(QStringLiteral("sdmc_size"),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)),
- QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)));
+
qt_config->endGroup();
}
@@ -1033,7 +1034,6 @@ void Config::SaveDebuggingValues() {
WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
- WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false);
WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false);
qt_config->endGroup();
@@ -1097,6 +1097,32 @@ void Config::SavePathValues() {
qt_config->endGroup();
}
+void Config::SaveCpuValues() {
+ qt_config->beginGroup(QStringLiteral("Cpu"));
+
+ if (global) {
+ WriteSetting(QStringLiteral("cpu_accuracy"),
+ static_cast<int>(Settings::values.cpu_accuracy), 0);
+
+ WriteSetting(QStringLiteral("cpuopt_page_tables"), Settings::values.cpuopt_page_tables,
+ true);
+ WriteSetting(QStringLiteral("cpuopt_block_linking"), Settings::values.cpuopt_block_linking,
+ true);
+ WriteSetting(QStringLiteral("cpuopt_return_stack_buffer"),
+ Settings::values.cpuopt_return_stack_buffer, true);
+ WriteSetting(QStringLiteral("cpuopt_fast_dispatcher"),
+ Settings::values.cpuopt_fast_dispatcher, true);
+ WriteSetting(QStringLiteral("cpuopt_context_elimination"),
+ Settings::values.cpuopt_context_elimination, true);
+ WriteSetting(QStringLiteral("cpuopt_const_prop"), Settings::values.cpuopt_const_prop, true);
+ WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
+ WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
+ Settings::values.cpuopt_reduce_misalign_checks, true);
+ }
+
+ qt_config->endGroup();
+}
+
void Config::SaveRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 681f0bca5..8e815f829 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -49,6 +49,7 @@ private:
void ReadDisabledAddOnValues();
void ReadMiscellaneousValues();
void ReadPathValues();
+ void ReadCpuValues();
void ReadRendererValues();
void ReadShortcutValues();
void ReadSystemValues();
@@ -73,6 +74,7 @@ private:
void SaveDisabledAddOnValues();
void SaveMiscellaneousValues();
void SavePathValues();
+ void SaveCpuValues();
void SaveRendererValues();
void SaveShortcutValues();
void SaveSystemValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 9aec1bd09..5f5d8e571 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -78,6 +78,16 @@
<string>Hotkeys</string>
</attribute>
</widget>
+ <widget class="ConfigureCpu" name="cpuTab">
+ <attribute name="title">
+ <string>CPU</string>
+ </attribute>
+ </widget>
+ <widget class="ConfigureCpuDebug" name="cpuDebugTab">
+ <attribute name="title">
+ <string>Debug</string>
+ </attribute>
+ </widget>
<widget class="ConfigureGraphics" name="graphicsTab">
<attribute name="title">
<string>Graphics</string>
@@ -159,6 +169,18 @@
<container>1</container>
</customwidget>
<customwidget>
+ <class>ConfigureCpu</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_cpu.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>ConfigureCpuDebug</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_cpu_debug.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
<class>ConfigureGraphics</class>
<extends>QWidget</extends>
<header>configuration/configure_graphics.h</header>
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
new file mode 100644
index 000000000..7493e5ffb
--- /dev/null
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -0,0 +1,61 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QComboBox>
+#include <QMessageBox>
+
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/settings.h"
+#include "ui_configure_cpu.h"
+#include "yuzu/configuration/configure_cpu.h"
+
+ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureCpu) {
+ ui->setupUi(this);
+
+ SetConfiguration();
+
+ connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
+ &ConfigureCpu::AccuracyUpdated);
+}
+
+ConfigureCpu::~ConfigureCpu() = default;
+
+void ConfigureCpu::SetConfiguration() {
+ const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
+
+ ui->accuracy->setEnabled(runtime_lock);
+ ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy));
+}
+
+void ConfigureCpu::AccuracyUpdated(int index) {
+ if (static_cast<Settings::CPUAccuracy>(index) == Settings::CPUAccuracy::DebugMode) {
+ const auto result = QMessageBox::warning(this, tr("Setting CPU to Debug Mode"),
+ tr("CPU Debug Mode is only intended for developer "
+ "use. Are you sure you want to enable this?"),
+ QMessageBox::Yes | QMessageBox::No);
+ if (result == QMessageBox::No) {
+ ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
+ return;
+ }
+ }
+}
+
+void ConfigureCpu::ApplyConfiguration() {
+ Settings::values.cpu_accuracy =
+ static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
+}
+
+void ConfigureCpu::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QWidget::changeEvent(event);
+}
+
+void ConfigureCpu::RetranslateUI() {
+ ui->retranslateUi(this);
+}
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
new file mode 100644
index 000000000..e4741d3a4
--- /dev/null
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -0,0 +1,33 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+#include "core/settings.h"
+
+namespace Ui {
+class ConfigureCpu;
+}
+
+class ConfigureCpu : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureCpu(QWidget* parent = nullptr);
+ ~ConfigureCpu() override;
+
+ void ApplyConfiguration();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ void AccuracyUpdated(int index);
+
+ void SetConfiguration();
+
+ std::unique_ptr<Ui::ConfigureCpu> ui;
+};
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
new file mode 100644
index 000000000..bf6ea79bb
--- /dev/null
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureCpu</class>
+ <widget class="QWidget" name="ConfigureCpu">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>321</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox">
+ <property name="title">
+ <string>General</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel">
+ <property name="text">
+ <string>Accuracy:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="accuracy">
+ <item>
+ <property name="text">
+ <string>Accurate</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Enable Debug Mode</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel">
+ <property name="wordWrap">
+ <bool>1</bool>
+ </property>
+ <property name="text">
+ <string>We recommend setting accuracy to "Accurate".</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_disable_info">
+ <property name="text">
+ <string>CPU settings are available only when game is not running.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp
new file mode 100644
index 000000000..3385b2cf6
--- /dev/null
+++ b/src/yuzu/configuration/configure_cpu_debug.cpp
@@ -0,0 +1,65 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QComboBox>
+
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/settings.h"
+#include "ui_configure_cpu_debug.h"
+#include "yuzu/configuration/configure_cpu_debug.h"
+
+ConfigureCpuDebug::ConfigureCpuDebug(QWidget* parent)
+ : QWidget(parent), ui(new Ui::ConfigureCpuDebug) {
+ ui->setupUi(this);
+
+ SetConfiguration();
+}
+
+ConfigureCpuDebug::~ConfigureCpuDebug() = default;
+
+void ConfigureCpuDebug::SetConfiguration() {
+ const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
+
+ ui->cpuopt_page_tables->setEnabled(runtime_lock);
+ ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables);
+ ui->cpuopt_block_linking->setEnabled(runtime_lock);
+ ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking);
+ ui->cpuopt_return_stack_buffer->setEnabled(runtime_lock);
+ ui->cpuopt_return_stack_buffer->setChecked(Settings::values.cpuopt_return_stack_buffer);
+ ui->cpuopt_fast_dispatcher->setEnabled(runtime_lock);
+ ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher);
+ ui->cpuopt_context_elimination->setEnabled(runtime_lock);
+ ui->cpuopt_context_elimination->setChecked(Settings::values.cpuopt_context_elimination);
+ ui->cpuopt_const_prop->setEnabled(runtime_lock);
+ ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop);
+ ui->cpuopt_misc_ir->setEnabled(runtime_lock);
+ ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir);
+ ui->cpuopt_reduce_misalign_checks->setEnabled(runtime_lock);
+ ui->cpuopt_reduce_misalign_checks->setChecked(Settings::values.cpuopt_reduce_misalign_checks);
+}
+
+void ConfigureCpuDebug::ApplyConfiguration() {
+ Settings::values.cpuopt_page_tables = ui->cpuopt_page_tables->isChecked();
+ Settings::values.cpuopt_block_linking = ui->cpuopt_block_linking->isChecked();
+ Settings::values.cpuopt_return_stack_buffer = ui->cpuopt_return_stack_buffer->isChecked();
+ Settings::values.cpuopt_fast_dispatcher = ui->cpuopt_fast_dispatcher->isChecked();
+ Settings::values.cpuopt_context_elimination = ui->cpuopt_context_elimination->isChecked();
+ Settings::values.cpuopt_const_prop = ui->cpuopt_const_prop->isChecked();
+ Settings::values.cpuopt_misc_ir = ui->cpuopt_misc_ir->isChecked();
+ Settings::values.cpuopt_reduce_misalign_checks = ui->cpuopt_reduce_misalign_checks->isChecked();
+}
+
+void ConfigureCpuDebug::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QWidget::changeEvent(event);
+}
+
+void ConfigureCpuDebug::RetranslateUI() {
+ ui->retranslateUi(this);
+}
diff --git a/src/yuzu/configuration/configure_cpu_debug.h b/src/yuzu/configuration/configure_cpu_debug.h
new file mode 100644
index 000000000..c9941ef3b
--- /dev/null
+++ b/src/yuzu/configuration/configure_cpu_debug.h
@@ -0,0 +1,31 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+#include "core/settings.h"
+
+namespace Ui {
+class ConfigureCpuDebug;
+}
+
+class ConfigureCpuDebug : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureCpuDebug(QWidget* parent = nullptr);
+ ~ConfigureCpuDebug() override;
+
+ void ApplyConfiguration();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ void SetConfiguration();
+
+ std::unique_ptr<Ui::ConfigureCpuDebug> ui;
+};
diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui
new file mode 100644
index 000000000..a90dc64fe
--- /dev/null
+++ b/src/yuzu/configuration/configure_cpu_debug.ui
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureCpuDebug</class>
+ <widget class="QWidget" name="ConfigureCpuDebug">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>321</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox">
+ <property name="title">
+ <string>Toggle CPU Optimizations</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QLabel">
+ <property name="wordWrap">
+ <bool>1</bool>
+ </property>
+ <property name="text">
+ <string>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is "Debug Mode".
+ &lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_page_tables">
+ <property name="text">
+ <string>Enable inline page tables</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div style="white-space: nowrap"&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style="white-space: nowrap"&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style="white-space: nowrap"&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_block_linking">
+ <property name="text">
+ <string>Enable block linking</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_return_stack_buffer">
+ <property name="text">
+ <string>Enable return stack buffer</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_fast_dispatcher">
+ <property name="text">
+ <string>Enable fast dispatcher</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_context_elimination">
+ <property name="text">
+ <string>Enable context elimination</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_const_prop">
+ <property name="text">
+ <string>Enable constant propagation</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_misc_ir">
+ <property name="text">
+ <string>Enable miscellaneous optimizations</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="cpuopt_reduce_misalign_checks">
+ <property name="text">
+ <string>Enable misalignment check reduction</string>
+ </property>
+ <property name="toolTip">
+ <string>
+ &lt;div style="white-space: nowrap"&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style="white-space: nowrap"&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_disable_info">
+ <property name="text">
+ <string>CPU settings are available only when game is not running.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 2c77441fd..d0e71dd60 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -36,7 +36,6 @@ void ConfigureDebug::SetConfiguration() {
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
ui->reporting_services->setChecked(Settings::values.reporting_services);
ui->quest_flag->setChecked(Settings::values.quest_flag);
- ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt);
ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
@@ -51,7 +50,6 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
Settings::values.reporting_services = ui->reporting_services->isChecked();
Settings::values.quest_flag = ui->quest_flag->isChecked();
- Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
Debugger::ToggleConsole();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 46f0208c6..272bdd6b8 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -228,13 +228,6 @@
</property>
</widget>
</item>
- <item>
- <widget class="QCheckBox" name="disable_cpu_opt">
- <property name="text">
- <string>Disable CPU JIT optimizations</string>
- </property>
- </widget>
- </item>
</layout>
</widget>
</item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 5918e9972..a5afb354f 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -42,6 +42,8 @@ void ConfigureDialog::ApplyConfiguration() {
ui->filesystemTab->applyConfiguration();
ui->inputTab->ApplyConfiguration();
ui->hotkeysTab->ApplyConfiguration(registry);
+ ui->cpuTab->ApplyConfiguration();
+ ui->cpuDebugTab->ApplyConfiguration();
ui->graphicsTab->ApplyConfiguration();
ui->graphicsAdvancedTab->ApplyConfiguration();
ui->audioTab->ApplyConfiguration();
@@ -76,9 +78,10 @@ void ConfigureDialog::RetranslateUI() {
Q_DECLARE_METATYPE(QList<QWidget*>);
void ConfigureDialog::PopulateSelectionList() {
- const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
+ const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
+ {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
{tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
{tr("Audio"), {ui->audioTab}},
{tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
@@ -107,6 +110,8 @@ void ConfigureDialog::UpdateVisibleTabs() {
{ui->profileManagerTab, tr("Profiles")},
{ui->inputTab, tr("Input")},
{ui->hotkeysTab, tr("Hotkeys")},
+ {ui->cpuTab, tr("CPU")},
+ {ui->cpuDebugTab, tr("Debug")},
{ui->graphicsTab, tr("Graphics")},
{ui->graphicsAdvancedTab, tr("Advanced")},
{ui->audioTab, tr("Audio")},
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index 835ee821c..a089f5733 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -11,19 +11,6 @@
#include "yuzu/configuration/configure_filesystem.h"
#include "yuzu/uisettings.h"
-namespace {
-
-template <typename T>
-void SetComboBoxFromData(QComboBox* combo_box, T data) {
- const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data)));
- if (index >= combo_box->count() || index < 0)
- return;
-
- combo_box->setCurrentIndex(index);
-}
-
-} // Anonymous namespace
-
ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) {
ui->setupUi(this);
@@ -73,11 +60,6 @@ void ConfigureFilesystem::setConfiguration() {
ui->cache_game_list->setChecked(UISettings::values.cache_game_list);
- SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size);
- SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size);
- SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size);
- SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size);
-
UpdateEnabledControls();
}
@@ -98,15 +80,6 @@ void ConfigureFilesystem::applyConfiguration() {
Settings::values.dump_nso = ui->dump_nso->isChecked();
UISettings::values.cache_game_list = ui->cache_game_list->isChecked();
-
- Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
- ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong());
- Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
- ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong());
- Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
- ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong());
- Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
- ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong());
}
void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) {
diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui
index 58cd07f52..84bea0600 100644
--- a/src/yuzu/configuration/configure_filesystem.ui
+++ b/src/yuzu/configuration/configure_filesystem.ui
@@ -116,127 +116,6 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="groupBox_3">
- <property name="title">
- <string>Storage Sizes</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="3" column="0">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>SD Card</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>System NAND</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="sysnand_size">
- <item>
- <property name="text">
- <string>2.5 GB</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QComboBox" name="sdmc_size">
- <property name="currentText">
- <string>32 GB</string>
- </property>
- <item>
- <property name="text">
- <string>1 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>2 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>4 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>8 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>16 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>32 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>64 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>128 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>256 GB</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>1 TB</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QComboBox" name="usrnand_size">
- <item>
- <property name="text">
- <string>26 GB</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_6">
- <property name="text">
- <string>User NAND</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_7">
- <property name="text">
- <string>NAND</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="nand_size">
- <item>
- <property name="text">
- <string>29.1 GB</string>
- </property>
- </item>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Patch Manager</string>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 1fb62d1cf..20316c9cc 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -65,6 +65,8 @@ void ConfigureGeneral::ApplyConfiguration() {
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
Qt::Checked);
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
+ }
+ if (Settings::values.use_multi_core.UsingGlobal()) {
Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked());
}
} else {
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 23763144f..7773228c8 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -335,15 +335,6 @@ void Config::ReadValues() {
Settings::values.gamecard_current_game =
sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
- Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger(
- "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB)));
- Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger(
- "Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB)));
- Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
- sdl2_config->GetInteger("Data Storage", "nand_system_size",
- static_cast<long>(Settings::NANDSystemSize::S2_5GB)));
- Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger(
- "Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB)));
// System
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
@@ -437,8 +428,6 @@ void Config::ReadValues() {
Settings::values.reporting_services =
sdl2_config->GetBoolean("Debugging", "reporting_services", false);
Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false);
- Settings::values.disable_cpu_opt =
- sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false);
Settings::values.disable_macro_jit =
sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 45c07ed5d..5bed47fd7 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -97,6 +97,39 @@ udp_pad_index=
# 0 (default): Disabled, 1: Enabled
use_multi_core=
+[Cpu]
+# Enable inline page tables optimization (faster guest memory access)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_page_tables =
+
+# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_block_linking =
+
+# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_return_stack_buffer =
+
+# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_fast_dispatcher =
+
+# Enable context elimination CPU Optimization (reduce host memory use for guest context)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_context_elimination =
+
+# Enable constant propagation CPU optimization (basic IR optimization)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_const_prop =
+
+# Enable miscellaneous CPU optimizations (basic IR optimization)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_misc_ir =
+
+# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_reduce_misalign_checks =
+
[Renderer]
# Which backend API to use.
# 0 (default): OpenGL, 1: Vulkan
@@ -283,9 +316,6 @@ dump_nso=false
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
# false: Retail/Normal Mode (default), true: Kiosk Mode
quest_flag =
-# Determines whether or not JIT CPU optimizations are enabled
-# false: Optimizations Enabled, true: Optimizations Disabled
-disable_cpu_opt =
# Enables/Disables the macro JIT compiler
disable_macro_jit=false
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h
index 41bbbbf60..3eb64e9d7 100644
--- a/src/yuzu_tester/default_ini.h
+++ b/src/yuzu_tester/default_ini.h
@@ -12,6 +12,39 @@ const char* sdl2_config_file = R"(
# 0 (default): Disabled, 1: Enabled
use_multi_core=
+[Cpu]
+# Enable inline page tables optimization (faster guest memory access)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_page_tables =
+
+# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_block_linking =
+
+# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_return_stack_buffer =
+
+# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_fast_dispatcher =
+
+# Enable context elimination CPU Optimization (reduce host memory use for guest context)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_context_elimination =
+
+# Enable constant propagation CPU optimization (basic IR optimization)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_const_prop =
+
+# Enable miscellaneous CPU optimizations (basic IR optimization)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_misc_ir =
+
+# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
+# 0: Disabled, 1 (default): Enabled
+cpuopt_reduce_misalign_checks =
+
[Renderer]
# Whether to use software or hardware rendering.
# 0: Software, 1 (default): Hardware