Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8b358dba2 | |||
| c65c651b6f | |||
| b3308830b2 | |||
| 18adea343e | |||
| 09f4f3f23b | |||
| a1054a093c | |||
| 099dd0c0d2 | |||
| acbfb0083a | |||
| ea089c012e | |||
| 42f653ab6f | |||
| 26f4e92c1f | |||
| 873d26b335 | |||
| d05675242a | |||
| 72c8a94a6c | |||
| b94e947793 | |||
| 50a7152941 | |||
| 4b4af29f86 | |||
| a95c49e7d0 | |||
| 2897ddb8f5 | |||
| b590e5572d | |||
| f46e1b8ca2 | |||
| 7116a7d28b | |||
| 37a3b89e7a | |||
| 7c88f4de2d | |||
| 62c13e8318 | |||
| 41bbb31af4 | |||
| 524674aafc | |||
| 6af82fd070 | |||
| afb06b1495 | |||
| 118d5fa3b0 | |||
| c68a0d17fc | |||
| c17938f96b | |||
| f58ee3f15f | |||
| 7652543231 | |||
| e773354477 | |||
| abbbdc2bc0 | |||
| 05b7b177f6 | |||
| b89d096652 | |||
| d866916f42 | |||
| da8e0f6571 | |||
| 3fa9702952 | |||
| ae7da0b12d | |||
| 7a13a515d9 | |||
| 133f497f84 | |||
| a9ad983c13 | |||
| 214b9fc9a7 | |||
| 951c61aeaa | |||
| 285b6dbc39 | |||
| eb7d361657 | |||
| 2428214c4b | |||
| af89f7683d | |||
| 667a8ae163 | |||
| 3a89723d97 | |||
| 9ee5c4ec56 | |||
| 5e58271903 | |||
| 8c907c620d | |||
| b84d429c2e | |||
| 9f34be5a61 | |||
| 14ac0c2923 | |||
| 279c7bcc1a | |||
| c7235e67ef | |||
| 840d5520d2 | |||
| 1eec9590c3 | |||
| e18bf4b062 | |||
| 88e1e7f14b | |||
| 091463a429 | |||
| f67605e6aa | |||
| 9a0648ff0a | |||
| c9e4acc4e2 | |||
| 292dfac25e | |||
| 37addf7a94 | |||
| 640fc1418b | |||
| 8a48c4ed1c | |||
| cbc0f0a66e | |||
| 481b210c0d | |||
| 4e7a6639d2 | |||
| a0c7d93b84 | |||
| e0193e2be5 | |||
| 78759d98dc |
+11
-3
@@ -33,6 +33,10 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON)
|
||||
|
||||
option(YUZU_TESTS "Compile tests" ON)
|
||||
|
||||
# Default to a Release build
|
||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||
@@ -166,8 +170,7 @@ macro(yuzu_find_packages)
|
||||
# Capitalization matters here. We need the naming to match the generated paths from Conan
|
||||
set(REQUIRED_LIBS
|
||||
# Cmake Pkg Prefix Version Conan Pkg
|
||||
"Catch2 2.13.7 catch2/2.13.7"
|
||||
"fmt 8.0.1 fmt/8.0.1"
|
||||
"fmt 8.0.1 fmt/8.1.1"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
"nlohmann_json 3.8 nlohmann_json/3.8.0"
|
||||
"ZLIB 1.2 zlib/1.2.11"
|
||||
@@ -175,6 +178,11 @@ macro(yuzu_find_packages)
|
||||
# can't use opus until AVX check is fixed: https://github.com/yuzu-emu/yuzu/pull/4068
|
||||
#"opus 1.3 opus/1.3.1"
|
||||
)
|
||||
if (YUZU_TESTS)
|
||||
list(APPEND REQUIRED_LIBS
|
||||
"Catch2 2.13.7 catch2/2.13.7"
|
||||
)
|
||||
endif()
|
||||
|
||||
foreach(PACKAGE ${REQUIRED_LIBS})
|
||||
string(REGEX REPLACE "[ \t\r\n]+" ";" PACKAGE_SPLIT ${PACKAGE})
|
||||
@@ -229,7 +237,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR YUZU_USE_BUNDLED_BOOST)
|
||||
include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
|
||||
else()
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
|
||||
Vendored
+1004
-930
File diff suppressed because it is too large
Load Diff
Vendored
+353
-312
File diff suppressed because it is too large
Load Diff
Vendored
+347
-306
File diff suppressed because it is too large
Load Diff
Vendored
+353
-312
File diff suppressed because it is too large
Load Diff
Vendored
+371
-330
File diff suppressed because it is too large
Load Diff
Vendored
+360
-319
File diff suppressed because it is too large
Load Diff
Vendored
+422
-373
File diff suppressed because it is too large
Load Diff
Vendored
+348
-307
File diff suppressed because it is too large
Load Diff
Vendored
+348
-307
File diff suppressed because it is too large
Load Diff
Vendored
+353
-312
File diff suppressed because it is too large
Load Diff
Vendored
+353
-312
File diff suppressed because it is too large
Load Diff
Vendored
+353
-312
File diff suppressed because it is too large
Load Diff
Vendored
+491
-444
File diff suppressed because it is too large
Load Diff
Vendored
+352
-311
File diff suppressed because it is too large
Load Diff
Vendored
+348
-307
File diff suppressed because it is too large
Load Diff
Vendored
+352
-311
File diff suppressed because it is too large
Load Diff
Vendored
+401
-360
File diff suppressed because it is too large
Load Diff
Vendored
+352
-311
File diff suppressed because it is too large
Load Diff
Vendored
+348
-307
File diff suppressed because it is too large
Load Diff
Vendored
+396
-355
File diff suppressed because it is too large
Load Diff
Vendored
+3
-7
@@ -13,10 +13,6 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
||||
endif()
|
||||
|
||||
# Catch
|
||||
add_library(catch-single-include INTERFACE)
|
||||
target_include_directories(catch-single-include INTERFACE catch/single_include)
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64)
|
||||
set(DYNARMIC_TESTS OFF)
|
||||
@@ -117,10 +113,10 @@ if (ENABLE_WEB_SERVICE)
|
||||
endif()
|
||||
|
||||
# Opus
|
||||
find_package(opus 1.3)
|
||||
if (NOT opus_FOUND)
|
||||
message(STATUS "opus 1.3 or newer not found, falling back to externals")
|
||||
if (YUZU_USE_BUNDLED_OPUS)
|
||||
add_subdirectory(opus EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
find_package(opus 1.3 REQUIRED)
|
||||
endif()
|
||||
|
||||
# FFMpeg
|
||||
|
||||
Vendored
+1
-1
Submodule externals/dynarmic updated: cce7e4ee5d...1635958d06
+4
-1
@@ -151,7 +151,10 @@ add_subdirectory(audio_core)
|
||||
add_subdirectory(video_core)
|
||||
add_subdirectory(input_common)
|
||||
add_subdirectory(shader_recompiler)
|
||||
add_subdirectory(tests)
|
||||
|
||||
if (YUZU_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
add_subdirectory(yuzu_cmd)
|
||||
|
||||
@@ -85,6 +85,7 @@ add_library(common STATIC
|
||||
logging/backend.h
|
||||
logging/filter.cpp
|
||||
logging/filter.h
|
||||
logging/formatter.h
|
||||
logging/log.h
|
||||
logging/log_entry.h
|
||||
logging/text_formatter.cpp
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
// adapted from https://github.com/fmtlib/fmt/issues/2704
|
||||
// a generic formatter for enum classes
|
||||
#if FMT_VERSION >= 80100
|
||||
template <typename T>
|
||||
struct fmt::formatter<T, std::enable_if_t<std::is_enum_v<T>, char>>
|
||||
: formatter<std::underlying_type_t<T>> {
|
||||
template <typename FormatContext>
|
||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
return fmt::formatter<std::underlying_type_t<T>>::format(
|
||||
static_cast<std::underlying_type_t<T>>(value), ctx);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -7,8 +7,9 @@
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/logging/types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
@@ -597,6 +597,7 @@ struct Values {
|
||||
BasicSetting<std::string> program_args{std::string(), "program_args"};
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
|
||||
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
|
||||
BasicSetting<bool> reporting_services{false, "reporting_services"};
|
||||
BasicSetting<bool> quest_flag{false, "quest_flag"};
|
||||
|
||||
@@ -187,6 +187,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
hle/kernel/k_interrupt_manager.h
|
||||
hle/kernel/k_light_condition_variable.cpp
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
|
||||
@@ -317,6 +317,8 @@ struct System::Impl {
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
gpu_core->NotifyShutdown();
|
||||
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
|
||||
@@ -45,26 +45,26 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
|
||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||
if (parameters.allow_pro_controller) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else if (parameters.allow_dual_joycons) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
|
||||
// Assign left joycons to even player indices and right joycons to odd player indices.
|
||||
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
|
||||
// a right Joycon for Player 2 in 2 Player Assist mode.
|
||||
if (index % 2 == 0) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
}
|
||||
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
|
||||
!Settings::values.use_docked_mode.GetValue()) {
|
||||
// We should *never* reach here under any normal circumstances.
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
|
||||
}
|
||||
|
||||
@@ -351,6 +351,19 @@ void EmulatedController::DisableConfiguration() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::EnableSystemButtons() {
|
||||
system_buttons_enabled = true;
|
||||
}
|
||||
|
||||
void EmulatedController::DisableSystemButtons() {
|
||||
system_buttons_enabled = false;
|
||||
}
|
||||
|
||||
void EmulatedController::ResetSystemButtons() {
|
||||
controller.home_button_state.home.Assign(false);
|
||||
controller.capture_button_state.capture.Assign(false);
|
||||
}
|
||||
|
||||
bool EmulatedController::IsConfiguring() const {
|
||||
return is_configuring;
|
||||
}
|
||||
@@ -389,7 +402,8 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
|
||||
return param.Get("engine", "") == param_.Get("engine", "") &&
|
||||
param.Get("guid", "") == param_.Get("guid", "") &&
|
||||
param.Get("port", 0) == param_.Get("port", 0);
|
||||
param.Get("port", 0) == param_.Get("port", 0) &&
|
||||
param.Get("pad", 0) == param_.Get("pad", 0);
|
||||
});
|
||||
if (devices_it != devices.end()) {
|
||||
continue;
|
||||
@@ -398,6 +412,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
device.Set("engine", param.Get("engine", ""));
|
||||
device.Set("guid", param.Get("guid", ""));
|
||||
device.Set("port", param.Get("port", 0));
|
||||
device.Set("pad", param.Get("pad", 0));
|
||||
devices.push_back(device);
|
||||
}
|
||||
|
||||
@@ -412,7 +427,8 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
|
||||
return param.Get("engine", "") == param_.Get("engine", "") &&
|
||||
param.Get("guid", "") == param_.Get("guid", "") &&
|
||||
param.Get("port", 0) == param_.Get("port", 0);
|
||||
param.Get("port", 0) == param_.Get("port", 0) &&
|
||||
param.Get("pad", 0) == param_.Get("pad", 0);
|
||||
});
|
||||
if (devices_it != devices.end()) {
|
||||
continue;
|
||||
@@ -421,6 +437,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
device.Set("engine", param.Get("engine", ""));
|
||||
device.Set("guid", param.Get("guid", ""));
|
||||
device.Set("port", param.Get("port", 0));
|
||||
device.Set("pad", param.Get("pad", 0));
|
||||
devices.push_back(device);
|
||||
}
|
||||
return devices;
|
||||
@@ -596,7 +613,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
controller.npad_button_state.right_sr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Home:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.home_button_state.home.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Screenshot:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.capture_button_state.capture.Assign(current_status.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -879,15 +905,42 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
if (!is_connected) {
|
||||
return;
|
||||
}
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
Disconnect();
|
||||
if (IsControllerSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Disconnect();
|
||||
|
||||
// Fallback fullkey controllers to Pro controllers
|
||||
if (IsControllerFullkey() && supported_style_tag.fullkey) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
|
||||
SetNpadStyleIndex(NpadStyleIndex::ProController);
|
||||
Connect();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
case NpadStyleIndex::GameCube:
|
||||
case NpadStyleIndex::NES:
|
||||
case NpadStyleIndex::SNES:
|
||||
case NpadStyleIndex::N64:
|
||||
case NpadStyleIndex::SegaGenesis:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerSupported() const {
|
||||
switch (npad_type) {
|
||||
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
return supported_style_tag.fullkey;
|
||||
case NpadStyleIndex::Handheld:
|
||||
@@ -915,9 +968,10 @@ bool EmulatedController::IsControllerSupported() const {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::Connect() {
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
|
||||
void EmulatedController::Connect(bool use_temporary_value) {
|
||||
if (!IsControllerSupported(use_temporary_value)) {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
|
||||
return;
|
||||
}
|
||||
{
|
||||
@@ -1049,6 +1103,20 @@ BatteryValues EmulatedController::GetBatteryValues() const {
|
||||
return controller.battery_values;
|
||||
}
|
||||
|
||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
return controller.home_button_state;
|
||||
}
|
||||
|
||||
CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
return controller.capture_button_state;
|
||||
}
|
||||
|
||||
NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
|
||||
@@ -101,6 +101,8 @@ struct ControllerStatus {
|
||||
VibrationValues vibration_values{};
|
||||
|
||||
// Data for HID serices
|
||||
HomeButtonState home_button_state{};
|
||||
CaptureButtonState capture_button_state{};
|
||||
NpadButtonState npad_button_state{};
|
||||
DebugPadButton debug_pad_button_state{};
|
||||
AnalogSticks analog_stick_state{};
|
||||
@@ -167,8 +169,11 @@ public:
|
||||
*/
|
||||
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
|
||||
|
||||
/// Sets the connected status to true
|
||||
void Connect();
|
||||
/**
|
||||
* Sets the connected status to true
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
*/
|
||||
void Connect(bool use_temporary_value = false);
|
||||
|
||||
/// Sets the connected status to false
|
||||
void Disconnect();
|
||||
@@ -195,6 +200,15 @@ public:
|
||||
/// Returns the emulated controller into normal mode, allowing the modification of the HID state
|
||||
void DisableConfiguration();
|
||||
|
||||
/// Enables Home and Screenshot buttons
|
||||
void EnableSystemButtons();
|
||||
|
||||
/// Disables Home and Screenshot buttons
|
||||
void DisableSystemButtons();
|
||||
|
||||
/// Sets Home and Screenshot buttons to false
|
||||
void ResetSystemButtons();
|
||||
|
||||
/// Returns true if the emulated controller is in configuring mode
|
||||
bool IsConfiguring() const;
|
||||
|
||||
@@ -258,7 +272,13 @@ public:
|
||||
/// Returns the latest battery status from the controller with parameters
|
||||
BatteryValues GetBatteryValues() const;
|
||||
|
||||
/// Returns the latest status of button input for the npad service
|
||||
/// Returns the latest status of button input for the hid::HomeButton service
|
||||
HomeButtonState GetHomeButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the hid::CaptureButton service
|
||||
CaptureButtonState GetCaptureButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the hid::Npad service
|
||||
NpadButtonState GetNpadButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the debug pad service
|
||||
@@ -317,11 +337,18 @@ private:
|
||||
/// Set the params for TAS devices
|
||||
void LoadTASParams();
|
||||
|
||||
/**
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
* @return true if the controller style is fullkey
|
||||
*/
|
||||
bool IsControllerFullkey(bool use_temporary_value = false) const;
|
||||
|
||||
/**
|
||||
* Checks the current controller type against the supported_style_tag
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
* @return true if the controller is supported
|
||||
*/
|
||||
bool IsControllerSupported() const;
|
||||
bool IsControllerSupported(bool use_temporary_value = false) const;
|
||||
|
||||
/**
|
||||
* Updates the button status of the controller
|
||||
@@ -373,6 +400,7 @@ private:
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
bool is_connected{false};
|
||||
bool is_configuring{false};
|
||||
bool system_buttons_enabled{true};
|
||||
f32 motion_sensitivity{0.01f};
|
||||
bool force_update_motion{false};
|
||||
|
||||
|
||||
@@ -378,6 +378,26 @@ struct LedPattern {
|
||||
};
|
||||
};
|
||||
|
||||
struct HomeButtonState {
|
||||
union {
|
||||
u64 raw{};
|
||||
|
||||
// Buttons
|
||||
BitField<0, 1, u64> home;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size.");
|
||||
|
||||
struct CaptureButtonState {
|
||||
union {
|
||||
u64 raw{};
|
||||
|
||||
// Buttons
|
||||
BitField<0, 1, u64> capture;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size.");
|
||||
|
||||
struct NpadButtonState {
|
||||
union {
|
||||
NpadButton raw{};
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Core::HID {
|
||||
MotionInput::MotionInput() {
|
||||
// Initialize PID constants with default values
|
||||
SetPID(0.3f, 0.005f, 0.0f);
|
||||
SetGyroThreshold(0.001f);
|
||||
}
|
||||
|
||||
void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -42,6 +43,11 @@ void GlobalSchedulerContext::PreemptThreads() {
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
const u32 priority = preemption_priorities[core_id];
|
||||
kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
|
||||
|
||||
// Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
|
||||
// in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
|
||||
// interrupts that may have occurred.
|
||||
kernel.PhysicalCore(core_id).Interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel::KInterruptManager {
|
||||
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
||||
auto* process = kernel.CurrentProcess();
|
||||
if (!process) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& scheduler = kernel.Scheduler(core_id);
|
||||
auto& current_thread = *scheduler.GetCurrentThread();
|
||||
|
||||
// If the user disable count is set, we may need to pin the current thread.
|
||||
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Pin the current thread.
|
||||
process->PinCurrentThread(core_id);
|
||||
|
||||
// Set the interrupt flag for the thread.
|
||||
scheduler.GetCurrentThread()->SetInterruptFlag();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel::KInterruptManager
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
namespace KInterruptManager {
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -120,7 +120,7 @@ static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
|
||||
|
||||
enum class KMemoryPermission : u8 {
|
||||
None = 0,
|
||||
Mask = static_cast<u8>(~None),
|
||||
All = static_cast<u8>(~None),
|
||||
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
|
||||
@@ -264,9 +264,9 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
|
||||
ASSERT(heap_last < stack_start || stack_last < heap_start);
|
||||
ASSERT(heap_last < kmap_start || kmap_last < heap_start);
|
||||
|
||||
current_heap_addr = heap_region_start;
|
||||
heap_capacity = 0;
|
||||
physical_memory_usage = 0;
|
||||
current_heap_end = heap_region_start;
|
||||
max_heap_size = 0;
|
||||
mapped_physical_memory_size = 0;
|
||||
memory_pool = pool;
|
||||
|
||||
page_table_impl.Resize(address_space_width, PageBits);
|
||||
@@ -306,7 +306,7 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
|
||||
KMemoryState state{};
|
||||
KMemoryPermission perm{};
|
||||
CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All,
|
||||
KMemoryState::Normal, KMemoryPermission::Mask,
|
||||
KMemoryState::Normal, KMemoryPermission::All,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
@@ -465,7 +465,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
MapPhysicalMemory(page_linked_list, addr, end_addr);
|
||||
|
||||
physical_memory_usage += remaining_size;
|
||||
mapped_physical_memory_size += remaining_size;
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
|
||||
@@ -507,7 +507,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
auto process{system.Kernel().CurrentProcess()};
|
||||
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
|
||||
physical_memory_usage -= mapped_size;
|
||||
mapped_physical_memory_size -= mapped_size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -554,7 +554,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
KMemoryState src_state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
if (IsRegionMapped(dst_addr, size)) {
|
||||
@@ -593,7 +593,7 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
KMemoryState src_state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::None,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
KMemoryPermission dst_perm{};
|
||||
@@ -784,7 +784,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&state, nullptr, &attribute, addr, size,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::Mask,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
|
||||
KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
@@ -806,6 +806,33 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
|
||||
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
|
||||
Svc::MemoryPermission svc_perm) {
|
||||
const size_t num_pages = size / PageSize;
|
||||
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
// Verify we can change the memory permission.
|
||||
KMemoryState old_state;
|
||||
KMemoryPermission old_perm;
|
||||
R_TRY(this->CheckMemoryState(
|
||||
std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size,
|
||||
KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
// Determine new perm.
|
||||
const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
|
||||
R_SUCCEED_IF(old_perm == new_perm);
|
||||
|
||||
// Perform mapping operation.
|
||||
R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
|
||||
|
||||
// Update the blocks.
|
||||
block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -832,61 +859,125 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryA
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
|
||||
ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
heap_capacity = new_heap_capacity;
|
||||
|
||||
// Only process page tables are allowed to set heap size.
|
||||
ASSERT(!this->IsKernel());
|
||||
|
||||
max_heap_size = size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) {
|
||||
ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
|
||||
// Try to perform a reduction in heap, instead of an extension.
|
||||
VAddr cur_address{};
|
||||
std::size_t allocation_size{};
|
||||
{
|
||||
// Lock the table.
|
||||
std::lock_guard lk(page_table_lock);
|
||||
|
||||
if (size > heap_region_end - heap_region_start) {
|
||||
return ResultOutOfMemory;
|
||||
// Validate that setting heap size is possible at all.
|
||||
R_UNLESS(!is_kernel, ResultOutOfMemory);
|
||||
R_UNLESS(size <= static_cast<std::size_t>(heap_region_end - heap_region_start),
|
||||
ResultOutOfMemory);
|
||||
R_UNLESS(size <= max_heap_size, ResultOutOfMemory);
|
||||
|
||||
if (size < GetHeapSize()) {
|
||||
// The size being requested is less than the current size, so we need to free the end of
|
||||
// the heap.
|
||||
|
||||
// Validate memory state.
|
||||
std::size_t num_allocator_blocks;
|
||||
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
|
||||
heap_region_start + size, GetHeapSize() - size,
|
||||
KMemoryState::All, KMemoryState::Normal,
|
||||
KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
// Unmap the end of the heap.
|
||||
const auto num_pages = (GetHeapSize() - size) / PageSize;
|
||||
R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None,
|
||||
OperationType::Unmap));
|
||||
|
||||
// Release the memory from the resource limit.
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit()->Release(
|
||||
LimitableResource::PhysicalMemory, num_pages * PageSize);
|
||||
|
||||
// Apply the memory block update.
|
||||
block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryAttribute::None);
|
||||
|
||||
// Update the current heap end.
|
||||
current_heap_end = heap_region_start + size;
|
||||
|
||||
// Set the output.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
} else if (size == GetHeapSize()) {
|
||||
// The size requested is exactly the current size.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
} else {
|
||||
// We have to allocate memory. Determine how much to allocate and where while the table
|
||||
// is locked.
|
||||
cur_address = current_heap_end;
|
||||
allocation_size = size - GetHeapSize();
|
||||
}
|
||||
}
|
||||
|
||||
const u64 previous_heap_size{GetHeapSize()};
|
||||
// Reserve memory for the heap extension.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
|
||||
allocation_size);
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented");
|
||||
// Allocate pages for the heap extension.
|
||||
KPageLinkedList page_linked_list;
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
|
||||
memory_pool));
|
||||
|
||||
// Increase the heap size
|
||||
// Map the pages.
|
||||
{
|
||||
std::lock_guard lock{page_table_lock};
|
||||
// Lock the table.
|
||||
std::lock_guard lk(page_table_lock);
|
||||
|
||||
const u64 delta{size - previous_heap_size};
|
||||
// Ensure that the heap hasn't changed since we began executing.
|
||||
ASSERT(cur_address == current_heap_end);
|
||||
|
||||
// Reserve memory for the heap extension.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
|
||||
delta);
|
||||
// Check the memory state.
|
||||
std::size_t num_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end,
|
||||
allocation_size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::None, KMemoryAttribute::None));
|
||||
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta);
|
||||
return ResultLimitReached;
|
||||
// Map the pages.
|
||||
const auto num_pages = allocation_size / PageSize;
|
||||
R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
|
||||
// Clear all the newly allocated pages.
|
||||
for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
|
||||
std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0,
|
||||
PageSize);
|
||||
}
|
||||
|
||||
KPageLinkedList page_linked_list;
|
||||
const std::size_t num_pages{delta / PageSize};
|
||||
|
||||
CASCADE_CODE(
|
||||
system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
|
||||
|
||||
if (IsRegionMapped(current_heap_addr, delta)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
|
||||
CASCADE_CODE(
|
||||
Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
|
||||
// Succeeded in allocation, commit the resource reservation
|
||||
// We succeeded, so commit our memory reservation.
|
||||
memory_reservation.Commit();
|
||||
|
||||
block_manager->Update(current_heap_addr, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite);
|
||||
// Apply the memory block update.
|
||||
block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
|
||||
|
||||
current_heap_addr = heap_region_start + size;
|
||||
// Update the current heap end.
|
||||
current_heap_end = heap_region_start + size;
|
||||
|
||||
// Set the output.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
return heap_region_start;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
@@ -978,7 +1069,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
if (const ResultCode result{CheckMemoryState(
|
||||
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
@@ -1031,9 +1122,8 @@ ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
|
||||
|
||||
bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
|
||||
return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::Mask, KMemoryPermission::None,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::None,
|
||||
KMemoryAttribute::IpcAndDeviceMapped)
|
||||
KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)
|
||||
.IsError();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,11 @@ public:
|
||||
KMemoryInfo QueryInfo(VAddr addr);
|
||||
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
|
||||
ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
|
||||
KMemoryAttribute value);
|
||||
ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
|
||||
ResultVal<VAddr> SetHeapSize(std::size_t size);
|
||||
ResultCode SetMaxHeapSize(std::size_t size);
|
||||
ResultCode SetHeapSize(VAddr* out, std::size_t size);
|
||||
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
bool is_map_only, VAddr region_start,
|
||||
std::size_t region_num_pages, KMemoryState state,
|
||||
@@ -182,14 +183,15 @@ public:
|
||||
constexpr VAddr GetAliasCodeRegionSize() const {
|
||||
return alias_code_region_end - alias_code_region_start;
|
||||
}
|
||||
size_t GetNormalMemorySize() {
|
||||
std::lock_guard lk(page_table_lock);
|
||||
return GetHeapSize() + mapped_physical_memory_size;
|
||||
}
|
||||
constexpr std::size_t GetAddressSpaceWidth() const {
|
||||
return address_space_width;
|
||||
}
|
||||
constexpr std::size_t GetHeapSize() {
|
||||
return current_heap_addr - heap_region_start;
|
||||
}
|
||||
constexpr std::size_t GetTotalHeapSize() {
|
||||
return GetHeapSize() + physical_memory_usage;
|
||||
constexpr std::size_t GetHeapSize() const {
|
||||
return current_heap_end - heap_region_start;
|
||||
}
|
||||
constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const {
|
||||
return address_space_start <= address && address + size - 1 <= address_space_end - 1;
|
||||
@@ -269,10 +271,8 @@ private:
|
||||
VAddr code_region_end{};
|
||||
VAddr alias_code_region_start{};
|
||||
VAddr alias_code_region_end{};
|
||||
VAddr current_heap_addr{};
|
||||
|
||||
std::size_t heap_capacity{};
|
||||
std::size_t physical_memory_usage{};
|
||||
std::size_t mapped_physical_memory_size{};
|
||||
std::size_t max_heap_size{};
|
||||
std::size_t max_physical_memory_size{};
|
||||
std::size_t address_space_width{};
|
||||
|
||||
@@ -172,7 +172,7 @@ void KProcess::DecrementThreadCount() {
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryAvailable() const {
|
||||
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
|
||||
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
|
||||
page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size +
|
||||
main_thread_stack_size};
|
||||
if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
|
||||
capacity != pool_size) {
|
||||
@@ -189,7 +189,7 @@ u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
|
||||
}
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryUsed() const {
|
||||
return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() +
|
||||
return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() +
|
||||
GetSystemResourceSize();
|
||||
}
|
||||
|
||||
@@ -220,30 +220,28 @@ bool KProcess::ReleaseUserException(KThread* thread) {
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::PinCurrentThread() {
|
||||
void KProcess::PinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// If the thread isn't terminated, pin it.
|
||||
if (!cur_thread->IsTerminationRequested()) {
|
||||
// Pin it.
|
||||
PinThread(core_id, cur_thread);
|
||||
cur_thread->Pin();
|
||||
cur_thread->Pin(core_id);
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::UnpinCurrentThread() {
|
||||
void KProcess::UnpinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// Unpin it.
|
||||
cur_thread->Unpin();
|
||||
@@ -410,8 +408,8 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
|
||||
resource_limit->Reserve(LimitableResource::Threads, 1);
|
||||
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
|
||||
|
||||
const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size};
|
||||
ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());
|
||||
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
|
||||
ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError());
|
||||
|
||||
ChangeStatus(ProcessStatus::Running);
|
||||
|
||||
|
||||
@@ -345,8 +345,8 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void PinCurrentThread();
|
||||
void UnpinCurrentThread();
|
||||
void PinCurrentThread(s32 core_id);
|
||||
void UnpinCurrentThread(s32 core_id);
|
||||
void UnpinThread(KThread* thread);
|
||||
|
||||
KLightLock& GetStateLock() {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
@@ -53,6 +54,13 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
|
||||
}
|
||||
cores_pending_reschedule &= ~(1ULL << core);
|
||||
}
|
||||
|
||||
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
|
||||
if (kernel.PhysicalCore(core_id).IsInterrupted()) {
|
||||
KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
|
||||
}
|
||||
}
|
||||
|
||||
if (must_context_switch) {
|
||||
auto core_scheduler = kernel.CurrentScheduler();
|
||||
kernel.ExitSVCProfile();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
@@ -26,12 +27,14 @@
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
@@ -50,6 +53,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
|
||||
VAddr entry_point, u64 arg) {
|
||||
context = {};
|
||||
context.cpu_registers[0] = arg;
|
||||
context.cpu_registers[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
|
||||
context.pc = entry_point;
|
||||
context.sp = stack_top;
|
||||
// TODO(merry): Perform a hardware test to determine the below value.
|
||||
@@ -61,6 +65,13 @@ namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ThreadLocalRegion {
|
||||
static constexpr std::size_t MessageBufferSize = 0x100;
|
||||
std::array<u32, MessageBufferSize / sizeof(u32)> message_buffer;
|
||||
std::atomic_uint16_t disable_count;
|
||||
std::atomic_uint16_t interrupt_flag;
|
||||
};
|
||||
|
||||
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
|
||||
public:
|
||||
explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
|
||||
@@ -344,7 +355,7 @@ void KThread::StartTermination() {
|
||||
if (parent != nullptr) {
|
||||
parent->ReleaseUserException(this);
|
||||
if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) {
|
||||
parent->UnpinCurrentThread();
|
||||
parent->UnpinCurrentThread(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +381,7 @@ void KThread::StartTermination() {
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void KThread::Pin() {
|
||||
void KThread::Pin(s32 current_core) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Set ourselves as pinned.
|
||||
@@ -387,7 +398,6 @@ void KThread::Pin() {
|
||||
|
||||
// Bind ourselves to this core.
|
||||
const s32 active_core = GetActiveCore();
|
||||
const s32 current_core = GetCurrentCoreId(kernel);
|
||||
|
||||
SetActiveCore(current_core);
|
||||
physical_ideal_core_id = current_core;
|
||||
@@ -480,6 +490,36 @@ void KThread::Unpin() {
|
||||
}
|
||||
}
|
||||
|
||||
u16 KThread::GetUserDisableCount() const {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
return memory.Read16(tls_address + offsetof(ThreadLocalRegion, disable_count));
|
||||
}
|
||||
|
||||
void KThread::SetInterruptFlag() {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1);
|
||||
}
|
||||
|
||||
void KThread::ClearInterruptFlag() {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0);
|
||||
}
|
||||
|
||||
ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
|
||||
@@ -307,6 +307,10 @@ public:
|
||||
return parent != nullptr;
|
||||
}
|
||||
|
||||
u16 GetUserDisableCount() const;
|
||||
void SetInterruptFlag();
|
||||
void ClearInterruptFlag();
|
||||
|
||||
[[nodiscard]] KThread* GetLockOwner() const {
|
||||
return lock_owner;
|
||||
}
|
||||
@@ -490,7 +494,7 @@ public:
|
||||
this->GetStackParameters().disable_count--;
|
||||
}
|
||||
|
||||
void Pin();
|
||||
void Pin(s32 current_core);
|
||||
|
||||
void Unpin();
|
||||
|
||||
|
||||
+58
-18
@@ -135,24 +135,15 @@ enum class ResourceLimitValueType {
|
||||
} // Anonymous namespace
|
||||
|
||||
/// Set the process heap to a given Size. It can both extend and shrink the heap.
|
||||
static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
|
||||
static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
|
||||
|
||||
// Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
|
||||
if ((heap_size % 0x200000) != 0) {
|
||||
LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
|
||||
heap_size);
|
||||
return ResultInvalidSize;
|
||||
}
|
||||
// Validate size.
|
||||
R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
|
||||
R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
|
||||
|
||||
if (heap_size >= 0x200000000) {
|
||||
LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
|
||||
return ResultInvalidSize;
|
||||
}
|
||||
|
||||
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
|
||||
|
||||
CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size));
|
||||
// Set the heap size.
|
||||
R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -164,6 +155,36 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission::None:
|
||||
case MemoryPermission::Read:
|
||||
case MemoryPermission::ReadWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
|
||||
MemoryPermission perm) {
|
||||
// Validate address / size.
|
||||
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
|
||||
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(size > 0, ResultInvalidSize);
|
||||
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Validate the permission.
|
||||
R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
|
||||
|
||||
// Validate that the region is in range for the current process.
|
||||
auto& page_table = system.Kernel().CurrentProcess()->PageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Set the memory attribute.
|
||||
return page_table.SetMemoryPermission(address, size, perm);
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
|
||||
u32 attribute) {
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
@@ -2006,6 +2027,25 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign
|
||||
count);
|
||||
}
|
||||
|
||||
static void SynchronizePreemptionState(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// If the current thread is pinned, unpin it.
|
||||
KProcess* cur_process = system.Kernel().CurrentProcess();
|
||||
const auto core_id = GetCurrentCoreId(kernel);
|
||||
|
||||
if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
|
||||
// Clear the current thread's interrupt flag.
|
||||
GetCurrentThread(kernel).ClearInterruptFlag();
|
||||
|
||||
// Unpin the current thread.
|
||||
cur_process->UnpinCurrentThread(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
|
||||
s32 value, s32 count) {
|
||||
return SignalToAddress(system, address, signal_type, value, count);
|
||||
@@ -2724,7 +2764,7 @@ static const FunctionDef SVC_Table_32[] = {
|
||||
static const FunctionDef SVC_Table_64[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, nullptr, "SetMemoryPermission"},
|
||||
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
{0x04, SvcWrap64<MapMemory>, "MapMemory"},
|
||||
{0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
|
||||
@@ -2776,7 +2816,7 @@ static const FunctionDef SVC_Table_64[] = {
|
||||
{0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
|
||||
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
|
||||
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
|
||||
{0x36, nullptr, "SynchronizePreemptionState"},
|
||||
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
|
||||
{0x37, nullptr, "Unknown"},
|
||||
{0x38, nullptr, "Unknown"},
|
||||
{0x39, nullptr, "Unknown"},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/literals.h"
|
||||
|
||||
namespace Kernel {
|
||||
using Handle = u32;
|
||||
@@ -12,9 +13,13 @@ using Handle = u32;
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr s32 ArgumentHandleCountMax = 0x40;
|
||||
constexpr u32 HandleWaitMask{1u << 30};
|
||||
|
||||
constexpr inline std::size_t HeapSizeAlignment = 2_MiB;
|
||||
|
||||
constexpr inline Handle InvalidHandle = Handle(0);
|
||||
|
||||
enum PseudoHandle : Handle {
|
||||
|
||||
@@ -249,6 +249,14 @@ void SvcWrap64(Core::System& system) {
|
||||
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
|
||||
}
|
||||
|
||||
// Used by SetMemoryPermission
|
||||
template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<Svc::MemoryPermission>(Param(system, 2)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
// Used by MapSharedMemory
|
||||
template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
|
||||
@@ -186,6 +186,10 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
case QueryType::NativeWindowWidth:
|
||||
case QueryType::NativeWindowHeight:
|
||||
break;
|
||||
case QueryType::NativeWindowMinUndequeuedBuffers:
|
||||
return 0;
|
||||
case QueryType::NativeWindowConsumerUsageBits:
|
||||
return 0;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
|
||||
return 0;
|
||||
|
||||
@@ -57,6 +57,11 @@ public:
|
||||
NativeWindowWidth = 0,
|
||||
NativeWindowHeight = 1,
|
||||
NativeWindowFormat = 2,
|
||||
/// The minimum number of buffers that must remain un-dequeued after a buffer has been
|
||||
/// queued
|
||||
NativeWindowMinUndequeuedBuffers = 3,
|
||||
/// The consumer gralloc usage bits currently set by the consumer
|
||||
NativeWindowConsumerUsageBits = 10,
|
||||
};
|
||||
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
|
||||
|
||||
@@ -100,9 +100,6 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
|
||||
|
||||
LOG_DEBUG(Service, "Opening \"{}\" display", name);
|
||||
|
||||
// TODO(Subv): Currently we only support the Default display.
|
||||
ASSERT(name == "Default");
|
||||
|
||||
const auto itr =
|
||||
std::find_if(displays.begin(), displays.end(),
|
||||
[&](const VI::Display& display) { return display.GetName() == name; });
|
||||
|
||||
@@ -663,6 +663,7 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding() const {
|
||||
{Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||
{Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||
{Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
|
||||
{Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -699,6 +700,7 @@ ButtonBindings SDLDriver::GetNintendoButtonBinding(
|
||||
{Settings::NativeButton::SL, sl_button},
|
||||
{Settings::NativeButton::SR, sr_button},
|
||||
{Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
|
||||
{Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace InputCommon {
|
||||
class SDLJoystick;
|
||||
|
||||
using ButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>;
|
||||
using ZButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ enum class Tas::TasAxis : u8 {
|
||||
};
|
||||
|
||||
// Supported keywords and buttons from a TAS file
|
||||
constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
|
||||
constexpr std::array<std::pair<std::string_view, TasButton>, 18> text_to_tas_button = {
|
||||
std::pair{"KEY_A", TasButton::BUTTON_A},
|
||||
{"KEY_B", TasButton::BUTTON_B},
|
||||
{"KEY_X", TasButton::BUTTON_X},
|
||||
@@ -40,8 +40,9 @@ constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_but
|
||||
{"KEY_DDOWN", TasButton::BUTTON_DOWN},
|
||||
{"KEY_SL", TasButton::BUTTON_SL},
|
||||
{"KEY_SR", TasButton::BUTTON_SR},
|
||||
{"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
|
||||
{"KEY_HOME", TasButton::BUTTON_HOME},
|
||||
// These buttons are disabled to avoid TAS input from activating hotkeys
|
||||
// {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
|
||||
// {"KEY_HOME", TasButton::BUTTON_HOME},
|
||||
{"KEY_ZL", TasButton::TRIGGER_ZL},
|
||||
{"KEY_ZR", TasButton::TRIGGER_ZR},
|
||||
};
|
||||
@@ -105,10 +106,16 @@ void Tas::LoadTasFile(size_t player_index, size_t file_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto num_frames = std::stoi(seg_list[0]);
|
||||
while (frame_no < num_frames) {
|
||||
commands[player_index].emplace_back();
|
||||
frame_no++;
|
||||
try {
|
||||
const auto num_frames = std::stoi(seg_list[0]);
|
||||
while (frame_no < num_frames) {
|
||||
commands[player_index].emplace_back();
|
||||
frame_no++;
|
||||
}
|
||||
} catch (const std::invalid_argument&) {
|
||||
LOG_ERROR(Input, "Invalid argument: '{}' at command {}", seg_list[0], frame_no);
|
||||
} catch (const std::out_of_range&) {
|
||||
LOG_ERROR(Input, "Out of range: '{}' at command {}", seg_list[0], frame_no);
|
||||
}
|
||||
|
||||
TASCommand command = {
|
||||
@@ -233,10 +240,21 @@ TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
|
||||
}
|
||||
}
|
||||
|
||||
const float x = std::stof(seg_list.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seg_list.at(1)) / 32767.0f;
|
||||
if (seg_list.size() < 2) {
|
||||
LOG_ERROR(Input, "Invalid axis data: '{}'", line);
|
||||
return {};
|
||||
}
|
||||
|
||||
return {x, y};
|
||||
try {
|
||||
const float x = std::stof(seg_list.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seg_list.at(1)) / 32767.0f;
|
||||
return {x, y};
|
||||
} catch (const std::invalid_argument&) {
|
||||
LOG_ERROR(Input, "Invalid argument: '{}'", line);
|
||||
} catch (const std::out_of_range&) {
|
||||
LOG_ERROR(Input, "Out of range: '{}'", line);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
u64 Tas::ReadCommandButtons(const std::string& line) const {
|
||||
|
||||
@@ -442,14 +442,22 @@ MotionMapping UDPClient::GetMotionMappingForDevice(const Common::ParamPackage& p
|
||||
}
|
||||
|
||||
MotionMapping mapping = {};
|
||||
Common::ParamPackage motion_params;
|
||||
motion_params.Set("engine", GetEngineName());
|
||||
motion_params.Set("guid", params.Get("guid", ""));
|
||||
motion_params.Set("port", params.Get("port", 0));
|
||||
motion_params.Set("pad", params.Get("pad", 0));
|
||||
motion_params.Set("motion", 0);
|
||||
mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(motion_params));
|
||||
mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(motion_params));
|
||||
Common::ParamPackage left_motion_params;
|
||||
left_motion_params.Set("engine", GetEngineName());
|
||||
left_motion_params.Set("guid", params.Get("guid", ""));
|
||||
left_motion_params.Set("port", params.Get("port", 0));
|
||||
left_motion_params.Set("pad", params.Get("pad", 0));
|
||||
left_motion_params.Set("motion", 0);
|
||||
|
||||
Common::ParamPackage right_motion_params;
|
||||
right_motion_params.Set("engine", GetEngineName());
|
||||
right_motion_params.Set("guid", params.Get("guid", ""));
|
||||
right_motion_params.Set("port", params.Get("port", 0));
|
||||
right_motion_params.Set("pad", params.Get("pad", 0));
|
||||
right_motion_params.Set("motion", 0);
|
||||
|
||||
mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
|
||||
mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_motion_params));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
||||
@@ -298,8 +298,16 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
if (std::abs(value.gyro_x) < 0.6f && std::abs(value.gyro_y) < 0.6f &&
|
||||
std::abs(value.gyro_z) < 0.6f) {
|
||||
bool is_active = false;
|
||||
if (std::abs(value.accel_x) > 1.5f || std::abs(value.accel_y) > 1.5f ||
|
||||
std::abs(value.accel_z) > 1.5f) {
|
||||
is_active = true;
|
||||
}
|
||||
if (std::abs(value.gyro_x) > 0.6f || std::abs(value.gyro_y) > 0.6f ||
|
||||
std::abs(value.gyro_z) > 0.6f) {
|
||||
is_active = true;
|
||||
}
|
||||
if (!is_active) {
|
||||
return;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
|
||||
@@ -86,7 +86,7 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
ctx.Add("MOV.F {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
@@ -112,17 +112,33 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.F {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.S {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex);
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex);
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
@@ -30,8 +31,9 @@ void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value)
|
||||
inst.DestructiveAddUsage(1);
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
|
||||
const auto input{ctx.var_alloc.Consume(value)};
|
||||
const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
|
||||
if (ret != input) {
|
||||
ctx.Add("{}={};", ret, input);
|
||||
ctx.Add("{}={}{};", ret, input, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,39 +102,46 @@ void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf8(ctx, inst, binding, offset, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf8(ctx, inst, binding, offset, "ftoi");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf16(ctx, inst, binding, offset, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf16(ctx, inst, binding, offset, "ftoi");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
|
||||
GetCbuf(ctx, ret, binding, offset, 32);
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
if (offset.IsImmediate()) {
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
const u32 u32_offset{offset.U32()};
|
||||
@@ -145,26 +152,26 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
|
||||
return;
|
||||
}
|
||||
if (u32_offset % 2 == 0) {
|
||||
ctx.AddU32x2("{}=ftou({}[{}].{}{});", inst, cbuf, u32_offset / 16,
|
||||
ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
|
||||
} else {
|
||||
ctx.AddU32x2("{}=uvec2(ftou({}[{}].{}),ftou({}[{}].{}));", inst, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), cbuf, (u32_offset + 4) / 16,
|
||||
OffsetSwizzle(u32_offset + 4));
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
|
||||
u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
|
||||
(u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
if (!ctx.profile.has_gl_component_indexing_bug) {
|
||||
ctx.AddU32x2("{}=uvec2(ftou({}[{}>>4][({}>>2)%4]),ftou({}[({}+4)>>4][(({}+4)>>2)%4]));",
|
||||
inst, cbuf, offset_var, offset_var, cbuf, offset_var, offset_var);
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst,
|
||||
cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var);
|
||||
return;
|
||||
}
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2(ftou({}[{}>>4].{}),ftou({}[({}+4)>>4].{}));", cbuf_offset,
|
||||
swizzle, ret, cbuf, offset_var, "xyzw"[swizzle], cbuf, offset_var,
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset,
|
||||
swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var,
|
||||
"xyzw"[(swizzle + 1) % 4]);
|
||||
}
|
||||
}
|
||||
@@ -221,6 +228,22 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddU32("{}=uint(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddU32("{}=uint(gl_VertexID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
|
||||
@@ -125,11 +125,11 @@ void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& i
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=-({});", inst, value);
|
||||
ctx.AddF32("{}=0.f-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=-({});", inst, value);
|
||||
ctx.AddF64("{}=double(0.)-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
|
||||
@@ -60,6 +60,8 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
|
||||
const IR::Value& offset);
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex);
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
std::string_view vertex);
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
|
||||
|
||||
@@ -87,11 +87,11 @@ void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::strin
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint(-({}));", inst, value);
|
||||
ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=-({});", inst, value);
|
||||
ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
|
||||
@@ -90,7 +90,9 @@ void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value&
|
||||
if (phi_reg == val_reg) {
|
||||
return;
|
||||
}
|
||||
ctx.Add("{}={};", phi_reg, val_reg);
|
||||
const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
|
||||
const auto suffix{needs_workaround ? "?true:false" : ""};
|
||||
ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
|
||||
@@ -428,9 +428,10 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
|
||||
return;
|
||||
}
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
header += fmt::format(
|
||||
"layout(std140,binding={}) uniform {}_cbuf_{}{{vec4 {}_cbuf{}[{}];}};",
|
||||
bindings.uniform_buffer, stage_name, desc.index, stage_name, desc.index, 4 * 1024);
|
||||
const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"};
|
||||
header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};",
|
||||
bindings.uniform_buffer, stage_name, desc.index, cbuf_type,
|
||||
stage_name, desc.index, 4 * 1024);
|
||||
bindings.uniform_buffer += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,6 @@ std::string VarAlloc::AddDefine(IR::Inst& inst, GlslVarType type) {
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
return Representation(inst.Definition<Id>());
|
||||
}
|
||||
|
||||
std::string VarAlloc::Consume(const IR::Value& value) {
|
||||
|
||||
@@ -30,11 +30,20 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702) // Ignore unreachable code warning
|
||||
#endif
|
||||
|
||||
template <auto func, typename... Args>
|
||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
template <typename ArgType>
|
||||
ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, Id>) {
|
||||
|
||||
@@ -355,6 +355,31 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.primitive_id);
|
||||
case IR::Attribute::InstanceId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.instance_id);
|
||||
} else {
|
||||
const Id index{ctx.OpLoad(ctx.U32[1], ctx.instance_index)};
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_instance)};
|
||||
return ctx.OpISub(ctx.U32[1], index, base);
|
||||
}
|
||||
case IR::Attribute::VertexId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.vertex_id);
|
||||
} else {
|
||||
const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
|
||||
return ctx.OpISub(ctx.U32[1], index, base);
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Read U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) {
|
||||
const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)};
|
||||
if (!output) {
|
||||
|
||||
@@ -53,6 +53,7 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
|
||||
Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
|
||||
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
|
||||
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);
|
||||
|
||||
@@ -31,6 +31,8 @@ public:
|
||||
|
||||
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
|
||||
|
||||
virtual void Dump(u64 hash) = 0;
|
||||
|
||||
[[nodiscard]] const ProgramHeader& SPH() const noexcept {
|
||||
return sph;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/logging/formatter.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ OPCODE(GetCbufU32, U32, U32,
|
||||
OPCODE(GetCbufF32, F32, U32, U32, )
|
||||
OPCODE(GetCbufU32x2, U32x2, U32, U32, )
|
||||
OPCODE(GetAttribute, F32, Attribute, U32, )
|
||||
OPCODE(GetAttributeU32, U32, Attribute, U32, )
|
||||
OPCODE(SetAttribute, Void, Attribute, F32, U32, )
|
||||
OPCODE(GetAttributeIndexed, F32, U32, U32, )
|
||||
OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )
|
||||
|
||||
@@ -389,6 +389,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
info.uses_demote_to_helper_invocation = true;
|
||||
break;
|
||||
case IR::Opcode::GetAttribute:
|
||||
case IR::Opcode::GetAttributeU32:
|
||||
info.loads.mask[static_cast<size_t>(inst.Arg(0).Attribute())] = true;
|
||||
break;
|
||||
case IR::Opcode::SetAttribute:
|
||||
|
||||
@@ -505,6 +505,29 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (op == IR::Opcode::BitCastU32F32) {
|
||||
// Workaround for new NVIDIA driver bug, where:
|
||||
// uint attr = ftou(itof(gl_InstanceID));
|
||||
// always returned 0.
|
||||
// We can instead manually optimize this and work around the driver bug:
|
||||
// uint attr = uint(gl_InstanceID);
|
||||
if (arg_inst->GetOpcode() == IR::Opcode::GetAttribute) {
|
||||
const IR::Attribute attr{arg_inst->Arg(0).Attribute()};
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
case IR::Attribute::InstanceId:
|
||||
case IR::Attribute::VertexId:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
// Replace the bitcasts with an integer attribute get
|
||||
inst.ReplaceOpcode(IR::Opcode::GetAttributeU32);
|
||||
inst.SetArg(0, arg_inst->Arg(0));
|
||||
inst.SetArg(1, arg_inst->Arg(1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) {
|
||||
|
||||
@@ -65,6 +65,10 @@ struct Profile {
|
||||
bool has_gl_component_indexing_bug{};
|
||||
/// The precise type qualifier is broken in the fragment stage of some drivers
|
||||
bool has_gl_precise_bug{};
|
||||
/// Some drivers do not properly support floatBitsToUint when used on cbufs
|
||||
bool has_gl_cbuf_ftou_bug{};
|
||||
/// Some drivers poorly optimize boolean variable references
|
||||
bool has_gl_bool_ref_bug{};
|
||||
/// Ignores SPIR-V ordered vs unordered using GLSL semantics
|
||||
bool ignore_nan_fp_comparisons{};
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ add_executable(tests
|
||||
create_target_directory_groups(tests)
|
||||
|
||||
target_link_libraries(tests PRIVATE common core input_common)
|
||||
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
|
||||
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads)
|
||||
|
||||
add_test(NAME tests COMMAND tests)
|
||||
|
||||
+10
-1
@@ -17,7 +17,6 @@
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "video_core/cdma_pusher.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
@@ -312,6 +311,12 @@ struct GPU::Impl {
|
||||
cpu_context->MakeCurrent();
|
||||
}
|
||||
|
||||
void NotifyShutdown() {
|
||||
std::unique_lock lk{sync_mutex};
|
||||
shutting_down.store(true, std::memory_order::relaxed);
|
||||
sync_cv.notify_all();
|
||||
}
|
||||
|
||||
/// Obtain the CPU Context
|
||||
void ObtainContext() {
|
||||
cpu_context->MakeCurrent();
|
||||
@@ -859,6 +864,10 @@ void GPU::Start() {
|
||||
impl->Start();
|
||||
}
|
||||
|
||||
void GPU::NotifyShutdown() {
|
||||
impl->NotifyShutdown();
|
||||
}
|
||||
|
||||
void GPU::ObtainContext() {
|
||||
impl->ObtainContext();
|
||||
}
|
||||
|
||||
@@ -232,6 +232,9 @@ public:
|
||||
/// core timing events.
|
||||
void Start();
|
||||
|
||||
/// Performs any additional necessary steps to shutdown GPU emulation.
|
||||
void NotifyShutdown();
|
||||
|
||||
/// Obtain the CPU Context
|
||||
void ObtainContext();
|
||||
|
||||
|
||||
@@ -73,12 +73,12 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
|
||||
}
|
||||
const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
|
||||
|
||||
for (const auto& map : submapped_ranges) {
|
||||
for (const auto& [map_addr, map_size] : submapped_ranges) {
|
||||
// Flush and invalidate through the GPU interface, to be asynchronous if possible.
|
||||
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map.first);
|
||||
const std::optional<VAddr> cpu_addr = GpuToCpuAddress(map_addr);
|
||||
ASSERT(cpu_addr);
|
||||
|
||||
rasterizer->UnmapMemory(*cpu_addr, map.second);
|
||||
rasterizer->UnmapMemory(*cpu_addr, map_size);
|
||||
}
|
||||
|
||||
UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
|
||||
@@ -265,7 +265,8 @@ size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept {
|
||||
return it->second - (gpu_addr - it->first);
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
|
||||
void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
|
||||
bool is_safe) const {
|
||||
std::size_t remaining_size{size};
|
||||
std::size_t page_index{gpu_src_addr >> page_bits};
|
||||
std::size_t page_offset{gpu_src_addr & page_mask};
|
||||
@@ -273,35 +274,15 @@ void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::siz
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
|
||||
|
||||
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
|
||||
const auto src_addr{*page_addr + page_offset};
|
||||
|
||||
// Flush must happen on the rasterizer interface, such that memory is always synchronous
|
||||
// when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
|
||||
rasterizer->FlushRegion(src_addr, copy_amount);
|
||||
system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
|
||||
}
|
||||
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
|
||||
const std::size_t size) const {
|
||||
std::size_t remaining_size{size};
|
||||
std::size_t page_index{gpu_src_addr >> page_bits};
|
||||
std::size_t page_offset{gpu_src_addr & page_mask};
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
|
||||
|
||||
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
|
||||
const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
|
||||
if (page_addr && *page_addr != 0) {
|
||||
const auto src_addr{*page_addr + page_offset};
|
||||
if (is_safe) {
|
||||
// Flush must happen on the rasterizer interface, such that memory is always
|
||||
// synchronous when it is read (even when in asynchronous GPU mode).
|
||||
// Fixes Dead Cells title menu.
|
||||
rasterizer->FlushRegion(src_addr, copy_amount);
|
||||
}
|
||||
system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
|
||||
} else {
|
||||
std::memset(dest_buffer, 0, copy_amount);
|
||||
@@ -314,7 +295,17 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
|
||||
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
|
||||
ReadBlockImpl(gpu_src_addr, dest_buffer, size, true);
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
|
||||
const std::size_t size) const {
|
||||
ReadBlockImpl(gpu_src_addr, dest_buffer, size, false);
|
||||
}
|
||||
|
||||
void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
|
||||
bool is_safe) {
|
||||
std::size_t remaining_size{size};
|
||||
std::size_t page_index{gpu_dest_addr >> page_bits};
|
||||
std::size_t page_offset{gpu_dest_addr & page_mask};
|
||||
@@ -322,13 +313,15 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, s
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
|
||||
|
||||
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
|
||||
const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
|
||||
if (page_addr && *page_addr != 0) {
|
||||
const auto dest_addr{*page_addr + page_offset};
|
||||
|
||||
// Invalidate must happen on the rasterizer interface, such that memory is always
|
||||
// synchronous when it is written (even when in asynchronous GPU mode).
|
||||
rasterizer->InvalidateRegion(dest_addr, copy_amount);
|
||||
if (is_safe) {
|
||||
// Invalidate must happen on the rasterizer interface, such that memory is always
|
||||
// synchronous when it is written (even when in asynchronous GPU mode).
|
||||
rasterizer->InvalidateRegion(dest_addr, copy_amount);
|
||||
}
|
||||
system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
|
||||
}
|
||||
|
||||
@@ -339,26 +332,13 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, s
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
|
||||
WriteBlockImpl(gpu_dest_addr, src_buffer, size, true);
|
||||
}
|
||||
|
||||
void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
|
||||
std::size_t size) {
|
||||
std::size_t remaining_size{size};
|
||||
std::size_t page_index{gpu_dest_addr >> page_bits};
|
||||
std::size_t page_offset{gpu_dest_addr & page_mask};
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
|
||||
|
||||
if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
|
||||
const auto dest_addr{*page_addr + page_offset};
|
||||
system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
|
||||
}
|
||||
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
WriteBlockImpl(gpu_dest_addr, src_buffer, size, false);
|
||||
}
|
||||
|
||||
void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size) const {
|
||||
@@ -435,15 +415,15 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
|
||||
size_t page_offset{gpu_addr & page_mask};
|
||||
std::optional<std::pair<GPUVAddr, std::size_t>> last_segment{};
|
||||
std::optional<VAddr> old_page_addr{};
|
||||
const auto extend_size = [this, &last_segment, &page_index](std::size_t bytes) {
|
||||
const auto extend_size = [&last_segment, &page_index, &page_offset](std::size_t bytes) {
|
||||
if (!last_segment) {
|
||||
GPUVAddr new_base_addr = page_index << page_bits;
|
||||
const GPUVAddr new_base_addr = (page_index << page_bits) + page_offset;
|
||||
last_segment = {new_base_addr, bytes};
|
||||
} else {
|
||||
last_segment->second += bytes;
|
||||
}
|
||||
};
|
||||
const auto split = [this, &last_segment, &result] {
|
||||
const auto split = [&last_segment, &result] {
|
||||
if (last_segment) {
|
||||
result.push_back(*last_segment);
|
||||
last_segment = std::nullopt;
|
||||
@@ -452,7 +432,7 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
|
||||
while (remaining_size > 0) {
|
||||
const size_t num_bytes{std::min(page_size - page_offset, remaining_size)};
|
||||
const auto page_addr{GpuToCpuAddress(page_index << page_bits)};
|
||||
if (!page_addr) {
|
||||
if (!page_addr || *page_addr == 0) {
|
||||
split();
|
||||
} else if (old_page_addr) {
|
||||
if (*old_page_addr + page_size != *page_addr) {
|
||||
|
||||
@@ -155,6 +155,11 @@ private:
|
||||
|
||||
void FlushRegion(GPUVAddr gpu_addr, size_t size) const;
|
||||
|
||||
void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
|
||||
bool is_safe) const;
|
||||
void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
|
||||
bool is_safe);
|
||||
|
||||
[[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
|
||||
return (gpu_addr >> page_bits) & page_table_mask;
|
||||
}
|
||||
|
||||
@@ -182,17 +182,13 @@ Device::Device() {
|
||||
shader_backend = Settings::ShaderBackend::GLSL;
|
||||
}
|
||||
|
||||
if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia &&
|
||||
!Settings::values.renderer_debug) {
|
||||
if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia) {
|
||||
const std::string_view driver_version = version.substr(13);
|
||||
const int version_major =
|
||||
std::atoi(driver_version.substr(0, driver_version.find(".")).data());
|
||||
|
||||
if (version_major >= 495) {
|
||||
LOG_WARNING(Render_OpenGL, "NVIDIA drivers 495 and later causes significant problems "
|
||||
"with yuzu. Forcing GLASM as a mitigation.");
|
||||
shader_backend = Settings::ShaderBackend::GLASM;
|
||||
use_assembly_shaders = true;
|
||||
has_cbuf_ftou_bug = true;
|
||||
has_bool_ref_bug = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,14 @@ public:
|
||||
return need_fastmath_off;
|
||||
}
|
||||
|
||||
bool HasCbufFtouBug() const {
|
||||
return has_cbuf_ftou_bug;
|
||||
}
|
||||
|
||||
bool HasBoolRefBug() const {
|
||||
return has_bool_ref_bug;
|
||||
}
|
||||
|
||||
Settings::ShaderBackend GetShaderBackend() const {
|
||||
return shader_backend;
|
||||
}
|
||||
@@ -200,6 +208,8 @@ private:
|
||||
bool has_sparse_texture_2{};
|
||||
bool warp_size_potentially_larger_than_guest{};
|
||||
bool need_fastmath_off{};
|
||||
bool has_cbuf_ftou_bug{};
|
||||
bool has_bool_ref_bug{};
|
||||
|
||||
std::string vendor_name;
|
||||
};
|
||||
|
||||
@@ -214,6 +214,8 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
|
||||
.has_broken_fp16_float_controls = false,
|
||||
.has_gl_component_indexing_bug = device.HasComponentIndexingBug(),
|
||||
.has_gl_precise_bug = device.HasPreciseBug(),
|
||||
.has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(),
|
||||
.has_gl_bool_ref_bug = device.HasBoolRefBug(),
|
||||
.ignore_nan_fp_comparisons = true,
|
||||
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
||||
},
|
||||
@@ -423,6 +425,11 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
|
||||
|
||||
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(key.unique_hashes[index]);
|
||||
}
|
||||
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
@@ -509,8 +516,12 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
|
||||
LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
|
||||
|
||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(key.Hash());
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
|
||||
Shader::RuntimeInfo info;
|
||||
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
||||
|
||||
@@ -1047,7 +1047,7 @@ bool Image::ScaleDown(bool ignore) {
|
||||
}
|
||||
|
||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
|
||||
ImageId image_id_, Image& image)
|
||||
ImageId image_id_, Image& image, const SlotVector<Image>&)
|
||||
: VideoCommon::ImageViewBase{info, image.info, image_id_}, views{runtime.null_image_views} {
|
||||
const Device& device = runtime.device;
|
||||
if (True(image.flags & ImageFlagBits::Converted)) {
|
||||
|
||||
@@ -36,6 +36,7 @@ using VideoCommon::ImageViewType;
|
||||
using VideoCommon::NUM_RT;
|
||||
using VideoCommon::Region2D;
|
||||
using VideoCommon::RenderTargets;
|
||||
using VideoCommon::SlotVector;
|
||||
|
||||
struct ImageBufferMap {
|
||||
~ImageBufferMap();
|
||||
@@ -234,7 +235,8 @@ class ImageView : public VideoCommon::ImageViewBase {
|
||||
friend Image;
|
||||
|
||||
public:
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
|
||||
const SlotVector<Image>&);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
|
||||
const VideoCommon::ImageViewInfo&, GPUVAddr);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
|
||||
|
||||
@@ -518,53 +518,6 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkImageView src_view = src_image_view.ColorView();
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkExtent2D extent{
|
||||
.width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
|
||||
.height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
|
||||
};
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
|
||||
this](vk::CommandBuffer cmdbuf) {
|
||||
const VkOffset2D offset{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
};
|
||||
const VkViewport viewport{
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(extent.width),
|
||||
.height = static_cast<float>(extent.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 0.0f,
|
||||
};
|
||||
const VkRect2D scissor{
|
||||
.offset = offset,
|
||||
.extent = extent,
|
||||
};
|
||||
const PushConstants push_constants{
|
||||
.tex_scale = {viewport.width, viewport.height},
|
||||
.tex_offset = {0.0f, 0.0f},
|
||||
};
|
||||
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
|
||||
|
||||
// TODO: Barriers
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
cmdbuf.SetViewport(0, viewport);
|
||||
cmdbuf.SetScissor(0, scissor);
|
||||
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view) {
|
||||
const VkPipelineLayout layout = *two_textures_pipeline_layout;
|
||||
|
||||
@@ -60,9 +60,6 @@ private:
|
||||
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view);
|
||||
|
||||
void ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, u32 up_scale, u32 down_shift);
|
||||
|
||||
void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view);
|
||||
|
||||
|
||||
@@ -517,6 +517,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
|
||||
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(key.unique_hashes[index]);
|
||||
}
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
@@ -613,6 +616,12 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
|
||||
|
||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||
|
||||
// Dump it before error.
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(key.Hash());
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
const std::vector<u32> code{EmitSPIRV(profile, program)};
|
||||
device.SaveShader(code);
|
||||
|
||||
@@ -1476,8 +1476,7 @@ bool Image::NeedsScaleHelper() const {
|
||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
|
||||
ImageId image_id_, Image& image)
|
||||
: VideoCommon::ImageViewBase{info, image.info, image_id_}, device{&runtime.device},
|
||||
src_image{&image}, image_handle{image.Handle()},
|
||||
samples(ConvertSampleCount(image.info.num_samples)) {
|
||||
image_handle{image.Handle()}, samples(ConvertSampleCount(image.info.num_samples)) {
|
||||
using Shader::TextureType;
|
||||
|
||||
const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info);
|
||||
@@ -1560,6 +1559,12 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
}
|
||||
}
|
||||
|
||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
|
||||
ImageId image_id_, Image& image, const SlotVector<Image>& slot_imgs)
|
||||
: ImageView{runtime, info, image_id_, image} {
|
||||
slot_images = &slot_imgs;
|
||||
}
|
||||
|
||||
ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
|
||||
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
|
||||
: VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_},
|
||||
@@ -1616,10 +1621,12 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
|
||||
}
|
||||
|
||||
bool ImageView::IsRescaled() const noexcept {
|
||||
if (!src_image) {
|
||||
if (!slot_images) {
|
||||
return false;
|
||||
}
|
||||
return src_image->IsRescaled();
|
||||
const auto& slots = *slot_images;
|
||||
const auto& src_image = slots[image_id];
|
||||
return src_image.IsRescaled();
|
||||
}
|
||||
|
||||
vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) {
|
||||
|
||||
@@ -23,6 +23,7 @@ using VideoCommon::ImageId;
|
||||
using VideoCommon::NUM_RT;
|
||||
using VideoCommon::Region2D;
|
||||
using VideoCommon::RenderTargets;
|
||||
using VideoCommon::SlotVector;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
|
||||
class ASTCDecoderPass;
|
||||
@@ -172,6 +173,8 @@ private:
|
||||
class ImageView : public VideoCommon::ImageViewBase {
|
||||
public:
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
|
||||
const SlotVector<Image>&);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
|
||||
const VideoCommon::ImageViewInfo&, GPUVAddr);
|
||||
explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&);
|
||||
@@ -228,7 +231,7 @@ private:
|
||||
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask);
|
||||
|
||||
const Device* device = nullptr;
|
||||
const Image* src_image{};
|
||||
const SlotVector<Image>* slot_images = nullptr;
|
||||
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views;
|
||||
std::unique_ptr<StorageViews> storage_views;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "shader_recompiler/environment.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
@@ -57,6 +59,47 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string_view StageToPrefix(Shader::Stage stage) {
|
||||
switch (stage) {
|
||||
case Shader::Stage::VertexB:
|
||||
return "VB";
|
||||
case Shader::Stage::TessellationControl:
|
||||
return "TC";
|
||||
case Shader::Stage::TessellationEval:
|
||||
return "TE";
|
||||
case Shader::Stage::Geometry:
|
||||
return "GS";
|
||||
case Shader::Stage::Fragment:
|
||||
return "FS";
|
||||
case Shader::Stage::Compute:
|
||||
return "CS";
|
||||
case Shader::Stage::VertexA:
|
||||
return "VA";
|
||||
default:
|
||||
return "UK";
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpImpl(u64 hash, const u64* code, u32 read_highest, u32 read_lowest,
|
||||
u32 initial_offset, Shader::Stage stage) {
|
||||
const auto shader_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
|
||||
const auto base_dir{shader_dir / "shaders"};
|
||||
if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories");
|
||||
return;
|
||||
}
|
||||
const auto prefix = StageToPrefix(stage);
|
||||
const auto name{base_dir / fmt::format("{}{:016x}.ash", prefix, hash)};
|
||||
const size_t real_size = read_highest - read_lowest + initial_offset;
|
||||
const size_t padding_needed = ((32 - (real_size % 32)) % 32);
|
||||
std::fstream shader_file(name, std::ios::out | std::ios::binary);
|
||||
const size_t jump_index = initial_offset / sizeof(u64);
|
||||
shader_file.write(reinterpret_cast<const char*>(code + jump_index), real_size);
|
||||
for (size_t i = 0; i < padding_needed; i++) {
|
||||
shader_file.put(0);
|
||||
}
|
||||
}
|
||||
|
||||
GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
|
||||
u32 start_address_)
|
||||
: gpu_memory{&gpu_memory_}, program_base{program_base_} {
|
||||
@@ -128,6 +171,10 @@ u64 GenericEnvironment::CalculateHash() const {
|
||||
return Common::CityHash64(data.get(), size);
|
||||
}
|
||||
|
||||
void GenericEnvironment::Dump(u64 hash) {
|
||||
DumpImpl(hash, code.data(), read_highest, read_lowest, initial_offset, stage);
|
||||
}
|
||||
|
||||
void GenericEnvironment::Serialize(std::ofstream& file) const {
|
||||
const u64 code_size{static_cast<u64>(CachedSize())};
|
||||
const u64 num_texture_types{static_cast<u64>(texture_types.size())};
|
||||
@@ -207,6 +254,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
|
||||
u32 start_address_)
|
||||
: GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} {
|
||||
gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph));
|
||||
initial_offset = sizeof(sph);
|
||||
gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask;
|
||||
switch (program) {
|
||||
case Maxwell::ShaderProgram::VertexA:
|
||||
@@ -323,14 +371,20 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
|
||||
if (stage == Shader::Stage::Compute) {
|
||||
file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
|
||||
.read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
|
||||
initial_offset = 0;
|
||||
} else {
|
||||
file.read(reinterpret_cast<char*>(&sph), sizeof(sph));
|
||||
initial_offset = sizeof(sph);
|
||||
if (stage == Shader::Stage::Geometry) {
|
||||
file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileEnvironment::Dump(u64 hash) {
|
||||
DumpImpl(hash, code.get(), read_highest, read_lowest, initial_offset, stage);
|
||||
}
|
||||
|
||||
u64 FileEnvironment::ReadInstruction(u32 address) {
|
||||
if (address < read_lowest || address > read_highest) {
|
||||
throw Shader::LogicError("Out of bounds address {}", address);
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
|
||||
[[nodiscard]] u64 CalculateHash() const;
|
||||
|
||||
void Dump(u64 hash) override;
|
||||
|
||||
void Serialize(std::ofstream& file) const;
|
||||
|
||||
protected:
|
||||
@@ -82,6 +84,7 @@ protected:
|
||||
|
||||
u32 cached_lowest = std::numeric_limits<u32>::max();
|
||||
u32 cached_highest = 0;
|
||||
u32 initial_offset = 0;
|
||||
|
||||
bool has_unbound_instructions = false;
|
||||
};
|
||||
@@ -149,6 +152,8 @@ public:
|
||||
|
||||
[[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
|
||||
|
||||
void Dump(u64 hash) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<u64[]> code;
|
||||
std::unordered_map<u32, Shader::TextureType> texture_types;
|
||||
@@ -159,6 +164,7 @@ private:
|
||||
u32 texture_bound{};
|
||||
u32 read_lowest{};
|
||||
u32 read_highest{};
|
||||
u32 initial_offset{};
|
||||
};
|
||||
|
||||
void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
|
||||
|
||||
@@ -1376,9 +1376,7 @@ void TextureCache<P>::ForEachSparseSegment(ImageBase& image, Func&& func) {
|
||||
using FuncReturn = typename std::invoke_result<Func, GPUVAddr, VAddr, size_t>::type;
|
||||
static constexpr bool RETURNS_BOOL = std::is_same_v<FuncReturn, bool>;
|
||||
const auto segments = gpu_memory.GetSubmappedRange(image.gpu_addr, image.guest_size_bytes);
|
||||
for (auto& segment : segments) {
|
||||
const auto gpu_addr = segment.first;
|
||||
const auto size = segment.second;
|
||||
for (const auto& [gpu_addr, size] : segments) {
|
||||
std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
@@ -1397,7 +1395,8 @@ ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const Imag
|
||||
if (const ImageViewId image_view_id = image.FindView(info); image_view_id) {
|
||||
return image_view_id;
|
||||
}
|
||||
const ImageViewId image_view_id = slot_image_views.insert(runtime, info, image_id, image);
|
||||
const ImageViewId image_view_id =
|
||||
slot_image_views.insert(runtime, info, image_id, image, slot_images);
|
||||
image.InsertView(info, image_view_id);
|
||||
return image_view_id;
|
||||
}
|
||||
|
||||
@@ -364,14 +364,14 @@ template <u32 GOB_EXTENT>
|
||||
|
||||
[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress2D(
|
||||
const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) {
|
||||
const u32 layer_stride = new_info.layer_stride;
|
||||
const s32 new_size = layer_stride * new_info.resources.layers;
|
||||
const s32 diff = static_cast<s32>(overlap.gpu_addr - gpu_addr);
|
||||
const u64 layer_stride = new_info.layer_stride;
|
||||
const u64 new_size = layer_stride * new_info.resources.layers;
|
||||
const u64 diff = overlap.gpu_addr - gpu_addr;
|
||||
if (diff > new_size) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const s32 base_layer = diff / layer_stride;
|
||||
const s32 mip_offset = diff % layer_stride;
|
||||
const s32 base_layer = static_cast<s32>(diff / layer_stride);
|
||||
const s32 mip_offset = static_cast<s32>(diff % layer_stride);
|
||||
const std::array offsets = CalculateMipLevelOffsets(new_info);
|
||||
const auto end = offsets.begin() + new_info.resources.levels;
|
||||
const auto it = std::find(offsets.begin(), end, static_cast<u32>(mip_offset));
|
||||
|
||||
@@ -33,7 +33,7 @@ void UpdateController(Core::HID::EmulatedController* controller,
|
||||
}
|
||||
controller->SetNpadStyleIndex(controller_type);
|
||||
if (connected) {
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,36 +400,66 @@ void QtControllerSelectorDialog::SetSupportedControllers() {
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
|
||||
const auto npad_style_set = system.HIDCore().GetSupportedStyleTag();
|
||||
auto& pairs = index_controller_type_pairs[player_index];
|
||||
|
||||
pairs.clear();
|
||||
emulated_controllers[player_index]->clear();
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Core::HID::NpadStyleIndex::ProController);
|
||||
emulated_controllers[player_index]->addItem(tr("Pro Controller"));
|
||||
const auto add_item = [&](Core::HID::NpadStyleIndex controller_type,
|
||||
const QString& controller_name) {
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(), controller_type);
|
||||
emulated_controllers[player_index]->addItem(controller_name);
|
||||
};
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Core::HID::NpadStyleIndex::JoyconDual);
|
||||
emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Core::HID::NpadStyleIndex::JoyconLeft);
|
||||
emulated_controllers[player_index]->addItem(tr("Left Joycon"));
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Core::HID::NpadStyleIndex::JoyconRight);
|
||||
emulated_controllers[player_index]->addItem(tr("Right Joycon"));
|
||||
|
||||
if (player_index == 0) {
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Core::HID::NpadStyleIndex::Handheld);
|
||||
emulated_controllers[player_index]->addItem(tr("Handheld"));
|
||||
if (npad_style_set.fullkey == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller"));
|
||||
}
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Core::HID::NpadStyleIndex::GameCube);
|
||||
emulated_controllers[player_index]->addItem(tr("GameCube Controller"));
|
||||
if (npad_style_set.joycon_dual == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons"));
|
||||
}
|
||||
|
||||
if (npad_style_set.joycon_left == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon"));
|
||||
}
|
||||
|
||||
if (npad_style_set.joycon_right == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon"));
|
||||
}
|
||||
|
||||
if (player_index == 0 && npad_style_set.handheld == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld"));
|
||||
}
|
||||
|
||||
if (npad_style_set.gamecube == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller"));
|
||||
}
|
||||
|
||||
// Disable all unsupported controllers
|
||||
if (!Settings::values.enable_all_controllers) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (npad_style_set.palma == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus"));
|
||||
}
|
||||
|
||||
if (npad_style_set.lark == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller"));
|
||||
}
|
||||
|
||||
if (npad_style_set.lucia == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller"));
|
||||
}
|
||||
|
||||
if (npad_style_set.lagoon == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller"));
|
||||
}
|
||||
|
||||
if (npad_style_set.lager == 1) {
|
||||
add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis"));
|
||||
}
|
||||
}
|
||||
|
||||
Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex(
|
||||
|
||||
@@ -66,27 +66,27 @@ const std::array<int, 2> Config::default_stick_mod = {
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 21> Config::default_hotkeys{{
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
|
||||
}};
|
||||
// clang-format on
|
||||
|
||||
@@ -679,7 +679,6 @@ void Config::ReadShortcutValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Shortcuts"));
|
||||
|
||||
for (const auto& [name, group, shortcut] : default_hotkeys) {
|
||||
const auto& [keyseq, context] = shortcut;
|
||||
qt_config->beginGroup(group);
|
||||
qt_config->beginGroup(name);
|
||||
// No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
|
||||
@@ -688,7 +687,10 @@ void Config::ReadShortcutValues() {
|
||||
UISettings::values.shortcuts.push_back(
|
||||
{name,
|
||||
group,
|
||||
{ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
|
||||
{ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
|
||||
ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
|
||||
.toString(),
|
||||
shortcut.context}});
|
||||
qt_config->endGroup();
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1227,8 +1229,10 @@ void Config::SaveShortcutValues() {
|
||||
|
||||
qt_config->beginGroup(group);
|
||||
qt_config->beginGroup(name);
|
||||
WriteSetting(QStringLiteral("KeySeq"), shortcut.first, default_hotkey.first);
|
||||
WriteSetting(QStringLiteral("Context"), shortcut.second, default_hotkey.second);
|
||||
WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq);
|
||||
WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
|
||||
default_hotkey.controller_keyseq);
|
||||
WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
|
||||
qt_config->endGroup();
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
|
||||
ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
|
||||
ui->enable_nsight_aftermath->setEnabled(runtime_lock);
|
||||
ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
|
||||
ui->dump_shaders->setEnabled(runtime_lock);
|
||||
ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
|
||||
ui->disable_macro_jit->setEnabled(runtime_lock);
|
||||
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
|
||||
ui->disable_loop_safety_checks->setEnabled(runtime_lock);
|
||||
@@ -73,6 +75,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
|
||||
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
|
||||
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
|
||||
Settings::values.dump_shaders = ui->dump_shaders->isChecked();
|
||||
Settings::values.disable_shader_loop_safety_checks =
|
||||
ui->disable_loop_safety_checks->isChecked();
|
||||
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
|
||||
|
||||
@@ -105,6 +105,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="dump_shaders">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump Game Shaders</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="disable_macro_jit">
|
||||
<property name="enabled">
|
||||
|
||||
@@ -45,7 +45,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
|
||||
general_tab{std::make_unique<ConfigureGeneral>(system_, this)},
|
||||
graphics_tab{std::make_unique<ConfigureGraphics>(system_, this)},
|
||||
graphics_advanced_tab{std::make_unique<ConfigureGraphicsAdvanced>(system_, this)},
|
||||
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
|
||||
hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)},
|
||||
input_tab{std::make_unique<ConfigureInput>(system_, this)},
|
||||
network_tab{std::make_unique<ConfigureNetwork>(system_, this)},
|
||||
profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user