diff options
Diffstat (limited to 'src')
49 files changed, 692 insertions, 286 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index 47bde5081..e63382e1d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -27,6 +27,8 @@ object InputHandler { 0x054C -> getInputDS5ButtonKey(event.keyCode) 0x057E -> getInputJoyconButtonKey(event.keyCode) 0x1532 -> getInputRazerButtonKey(event.keyCode) + 0x3537 -> getInputRedmagicButtonKey(event.keyCode) + 0x358A -> getInputBackboneLabsButtonKey(event.keyCode) else -> getInputGenericButtonKey(event.keyCode) } @@ -227,6 +229,42 @@ object InputHandler { } } + private fun getInputRedmagicButtonKey(key: Int): Int { + return when (key) { + KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B + KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A + KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y + KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X + KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L + KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R + KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL + KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR + KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L + KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R + KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS + KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS + else -> -1 + } + } + + private fun getInputBackboneLabsButtonKey(key: Int): Int { + return when (key) { + KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B + KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A + KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y + KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X + KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L + KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R + KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL + KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR + KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L + KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R + KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS + KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS + else -> -1 + } + } + private fun getInputGenericButtonKey(key: Int): Int { return when (key) { KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A diff --git a/src/common/settings.h b/src/common/settings.h index 491f0d3e0..6425cd98f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -384,6 +384,8 @@ struct Values { Category::RendererDebug}; // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control bool renderer_amdvlk_depth_bias_workaround{}; + Setting<bool> disable_buffer_reorder{linkage, false, "disable_buffer_reorder", + Category::RendererDebug}; // System SwitchableSetting<Language, true> language_index{linkage, diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index a72df034e..c7b48a58d 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -167,6 +167,11 @@ protected: */ std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const; + /** + * Clip the provided coordinates to be inside the touchscreen area. + */ + std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const; + WindowSystemInfo window_info; bool strict_context_required = false; @@ -181,11 +186,6 @@ private: // By default, ignore this request and do nothing. } - /** - * Clip the provided coordinates to be inside the touchscreen area. - */ - std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const; - Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout u32 client_area_width; ///< Current client width, should be set by window impl. diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index a22015ada..a6e681e15 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -509,9 +509,11 @@ void EmulatedController::ReloadInput() { }); } turbo_button_state = 0; + is_initalized = true; } void EmulatedController::UnloadInput() { + is_initalized = false; for (auto& button : button_devices) { button.reset(); } @@ -1207,6 +1209,9 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { } bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { + if (!is_initalized) { + return false; + } if (device_index >= output_devices.size()) { return false; } @@ -1242,6 +1247,10 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type); const auto& player = Settings::values.players.GetValue()[player_index]; + if (!is_initalized) { + return false; + } + if (!player.vibration_enabled) { return false; } @@ -1261,6 +1270,10 @@ Common::Input::DriverResult EmulatedController::SetPollingMode( EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index); + if (!is_initalized) { + return Common::Input::DriverResult::InvalidHandle; + } + auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)]; auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_output_device = output_devices[3]; @@ -1306,6 +1319,10 @@ bool EmulatedController::SetCameraFormat( Core::IrSensor::ImageTransferProcessorFormat camera_format) { LOG_INFO(Service_HID, "Set camera format {}", camera_format); + if (!is_initalized) { + return false; + } + auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& camera_output_device = output_devices[2]; @@ -1329,6 +1346,11 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) { } bool EmulatedController::HasNfc() const { + + if (!is_initalized) { + return false; + } + const auto& nfc_output_device = output_devices[3]; switch (npad_type) { @@ -1366,6 +1388,10 @@ bool EmulatedController::RemoveNfcHandle() { } bool EmulatedController::StartNfcPolling() { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1377,6 +1403,10 @@ bool EmulatedController::StartNfcPolling() { } bool EmulatedController::StopNfcPolling() { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1388,6 +1418,10 @@ bool EmulatedController::StopNfcPolling() { } bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1400,6 +1434,10 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, Common::Input::MifareRequest& out_data) { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1412,6 +1450,10 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ } bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1423,6 +1465,10 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req } bool EmulatedController::WriteNfc(const std::vector<u8>& data) { + if (!is_initalized) { + return false; + } + auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; auto& nfc_virtual_output_device = output_devices[3]; @@ -1434,6 +1480,10 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) { } void EmulatedController::SetLedPattern() { + if (!is_initalized) { + return; + } + for (auto& device : output_devices) { if (!device) { continue; diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index ea18c2343..d6e20ab66 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -559,6 +559,7 @@ private: NpadStyleTag supported_style_tag{NpadStyleSet::All}; bool is_connected{false}; bool is_configuring{false}; + bool is_initalized{false}; bool system_buttons_enabled{true}; f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard}; u32 turbo_button_state{0}; diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp index b379dadeb..3906c0fa4 100644 --- a/src/core/hle/service/am/applets/applet_cabinet.cpp +++ b/src/core/hle/service/am/applets/applet_cabinet.cpp @@ -122,7 +122,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) Service::NFP::RegisterInfoPrivate register_info{}; std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); - + register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All); + register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'}); nfp_device->SetRegisterInfoPrivate(register_info); break; } @@ -130,7 +131,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) nfp_device->DeleteApplicationArea(); break; case Service::NFP::CabinetMode::StartRestorer: - nfp_device->RestoreAmiibo(); + nfp_device->Restore(); break; case Service::NFP::CabinetMode::StartFormatter: nfp_device->Format(); diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp index a019cc9f7..c27646fcf 100644 --- a/src/core/hle/service/mii/types/ver3_store_data.cpp +++ b/src/core/hle/service/mii/types/ver3_store_data.cpp @@ -98,7 +98,7 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { } void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { - version = 1; + version = 3; mii_information.gender.Assign(static_cast<u8>(store_data.GetGender())); mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor())); height = store_data.GetHeight(); diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index e7a00deb3..47516f883 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -401,6 +401,12 @@ Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& time } Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) { + bool is_corrupted = false; + + if (model_type != NFP::ModelType::Amiibo) { + return ResultInvalidArgument; + } + if (device_state != DeviceState::TagFound) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); return ResultWrongDeviceState; @@ -420,26 +426,32 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target if (is_plain_amiibo) { std::vector<u8> data(sizeof(NFP::NTAG215File)); memcpy(data.data(), &tag_data, sizeof(tag_data)); - WriteBackupData(tag_data.uid, data); + } - device_state = DeviceState::TagMounted; - mount_target = mount_target_; - return ResultSuccess; + if (!is_plain_amiibo && !NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { + LOG_ERROR(Service_NFP, "Can't decode amiibo"); + is_corrupted = true; } - if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { - bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); - LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); - return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; + if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) { + LOG_ERROR(Service_NFP, "Invalid mii data"); + is_corrupted = true; } - std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); - memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); - WriteBackupData(encrypted_tag_data.uuid, data); + if (!is_corrupted) { + std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File)); + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + WriteBackupData(encrypted_tag_data.uuid, data); + } device_state = DeviceState::TagMounted; mount_target = mount_target_; + if (is_corrupted) { + bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess(); + return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; + } + return ResultSuccess; } @@ -606,6 +618,17 @@ Result NfcDevice::Restore() { } } + // Restore mii data in case is corrupted by previous instances of yuzu + if (tag_data.settings.settings.amiibo_initialized && !tag_data.owner_mii.IsValid()) { + LOG_ERROR(Service_NFP, "Regenerating mii data"); + Mii::StoreData new_mii{}; + new_mii.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All); + new_mii.SetNickname({u'y', u'u', u'z', u'u', u'\0'}); + + tag_data.owner_mii.BuildFromStoreData(new_mii); + tag_data.mii_extension.SetFromStoreData(new_mii); + } + // Overwrite tag contents with backup and mount the tag tag_data = temporary_tag_data; encrypted_tag_data = temporary_encrypted_tag_data; @@ -851,25 +874,6 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe return Flush(); } -Result NfcDevice::RestoreAmiibo() { - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return ResultTagRemoved; - } - return ResultWrongDeviceState; - } - - if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) { - LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); - return ResultWrongDeviceState; - } - - // TODO: Load amiibo from backup on system - LOG_ERROR(Service_NFP, "Not Implemented"); - return ResultSuccess; -} - Result NfcDevice::Format() { Result result = ResultSuccess; @@ -877,7 +881,9 @@ Result NfcDevice::Format() { result = Mount(NFP::ModelType::Amiibo, NFP::MountTarget::All); } - if (result.IsError()) { + // We are formatting all data. Corruption is not an issue. + if (result.IsError() && + (result != ResultCorruptedData && result != ResultCorruptedDataWithBackup)) { return result; } diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 0ed1ff34c..d8efe25ec 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -68,7 +68,6 @@ public: Result DeleteRegisterInfo(); Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info); - Result RestoreAmiibo(); Result Format(); Result OpenApplicationArea(u32 access_id); diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 19c667b42..f5edfdc8b 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -19,19 +19,8 @@ namespace Service::Set { -namespace { -constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05; - -enum class GetFirmwareVersionType { - Version1, - Version2, -}; - -void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, - GetFirmwareVersionType type) { - ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100, - "FirmwareVersion output buffer must be 0x100 bytes in size!"); - +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type) { constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; auto& fsc = system.GetFileSystemController(); @@ -52,39 +41,34 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); } - const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { + const auto early_exit_failure = [](std::string_view desc, Result code) { LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", desc); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(code); + return code; }; const auto ver_file = romfs->GetFile("file"); if (ver_file == nullptr) { - early_exit_failure("The system version archive didn't contain the file 'file'.", - FileSys::ERROR_INVALID_ARGUMENT); - return; + return early_exit_failure("The system version archive didn't contain the file 'file'.", + FileSys::ERROR_INVALID_ARGUMENT); } auto data = ver_file->ReadAllBytes(); - if (data.size() != 0x100) { - early_exit_failure("The system version file 'file' was not the correct size.", - FileSys::ERROR_OUT_OF_BOUNDS); - return; + if (data.size() != sizeof(FirmwareVersionFormat)) { + return early_exit_failure("The system version file 'file' was not the correct size.", + FileSys::ERROR_OUT_OF_BOUNDS); } + std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); + // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will // zero out the REVISION_MINOR field. if (type == GetFirmwareVersionType::Version1) { - data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; + out_firmware.revision_minor = 0; } - ctx.WriteBuffer(data); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + return ResultSuccess; } -} // Anonymous namespace void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -98,12 +82,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called"); - GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); + + FirmwareVersionFormat firmware_data{}; + const auto result = + GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1); + + if (result.IsSuccess()) { + ctx.WriteBuffer(firmware_data); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called"); - GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); + + FirmwareVersionFormat firmware_data{}; + const auto result = + GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2); + + if (result.IsSuccess()) { + ctx.WriteBuffer(firmware_data); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index 93023c6dd..5f770fd32 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -4,6 +4,7 @@ #pragma once #include "common/uuid.h" +#include "core/hle/result.h" #include "core/hle/service/service.h" #include "core/hle/service/time/clock_types.h" @@ -12,6 +13,29 @@ class System; } namespace Service::Set { +enum class LanguageCode : u64; +enum class GetFirmwareVersionType { + Version1, + Version2, +}; + +struct FirmwareVersionFormat { + u8 major; + u8 minor; + u8 micro; + INSERT_PADDING_BYTES(1); + u8 revision_major; + u8 revision_minor; + INSERT_PADDING_BYTES(2); + std::array<char, 0x20> platform; + std::array<u8, 0x40> version_hash; + std::array<char, 0x18> display_version; + std::array<char, 0x80> display_title; +}; +static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size"); + +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type); class SET_SYS final : public ServiceFramework<SET_SYS> { public: diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index 9fc01ea90..7149fffeb 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h @@ -11,6 +11,11 @@ #include "core/hle/service/time/errors.h" #include "core/hle/service/time/time_zone_types.h" +// Defined by WinBase.h on Windows +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + namespace Service::Time::Clock { enum class TimeType : u8 { diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt index 1537271fc..22e9337c4 100644 --- a/src/frontend_common/CMakeLists.txt +++ b/src/frontend_common/CMakeLists.txt @@ -7,4 +7,4 @@ add_library(frontend_common STATIC ) create_target_directory_groups(frontend_common) -target_link_libraries(frontend_common PUBLIC core SimpleIni PRIVATE common Boost::headers) +target_link_libraries(frontend_common PUBLIC core SimpleIni::SimpleIni PRIVATE common Boost::headers) diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h index 20a1a8056..b3812af17 100644 --- a/src/frontend_common/config.h +++ b/src/frontend_common/config.h @@ -7,6 +7,7 @@ #include <string> #include "common/settings.h" +#define SI_NO_CONVERSION #include <SimpleIni.h> #include <boost/algorithm/string/replace.hpp> diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp index 2705ab140..9319ea007 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp @@ -5,6 +5,7 @@ #include "shader_recompiler/backend/glasm/glasm_emit_context.h" #include "shader_recompiler/frontend/ir/program.h" #include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/profile.h" #include "shader_recompiler/runtime_info.h" namespace Shader::Backend::GLASM { @@ -35,7 +36,9 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std continue; } const auto& ssbo{ctx.info.storage_buffers_descriptors[index]}; - ctx.Add("LDC.U64 DC.x,c{}[{}];" // ssbo_addr + const u64 ssbo_align_mask{~(ctx.profile.min_ssbo_alignment - 1U)}; + ctx.Add("LDC.U64 DC.x,c{}[{}];" // unaligned_ssbo_addr + "AND.U64 DC.x,DC.x,{};" // ssbo_addr = unaligned_ssbo_addr & ssbo_align_mask "LDC.U32 RC.x,c{}[{}];" // ssbo_size_u32 "CVT.U64.U32 DC.y,RC.x;" // ssbo_size = ssbo_size_u32 "ADD.U64 DC.y,DC.y,DC.x;" // ssbo_end = ssbo_addr + ssbo_size @@ -44,8 +47,8 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std "AND.U.CC RC.x,RC.x,RC.y;" // cond = a && b "IF NE.x;" // if cond "SUB.U64 DC.x,{}.x,DC.x;", // offset = input_addr - ssbo_addr - ssbo.cbuf_index, ssbo.cbuf_offset, ssbo.cbuf_index, ssbo.cbuf_offset + 8, address, - address, address); + ssbo.cbuf_index, ssbo.cbuf_offset, ssbo_align_mask, ssbo.cbuf_index, + ssbo.cbuf_offset + 8, address, address, address); if (pointer_based) { ctx.Add("PK64.U DC.y,c[{}];" // host_ssbo = cbuf "ADD.U64 DC.x,DC.x,DC.y;" // host_addr = host_ssbo + offset diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 9ff4028c2..fd9a99449 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -601,7 +601,10 @@ std::string EmitContext::DefineGlobalMemoryFunctions() { addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc)); size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc)); } - const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])}; + const u32 ssbo_align_mask{~(static_cast<u32>(profile.min_ssbo_alignment) - 1U)}; + const auto aligned_low_addr{fmt::format("{}&{}", addr_xy[0], ssbo_align_mask)}; + const auto aligned_addr{fmt::format("uvec2({},{})", aligned_low_addr, addr_xy[1])}; + const auto addr_pack{fmt::format("packUint2x32({})", aligned_addr)}; const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)}; func += addr_statment; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 57df6fc34..3350f1f85 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -891,7 +891,9 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, zero, ssbo_size_cbuf_offset)}; - const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; + const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)}; + const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; + const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))}; const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 8fac6bad3..321ea625b 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -298,7 +298,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo Optimization::PositionPass(env, program); - Optimization::GlobalMemoryToStorageBufferPass(program); + Optimization::GlobalMemoryToStorageBufferPass(program, host_info); Optimization::TexturePass(env, program, host_info); if (Settings::values.resolution_info.active) { diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h index 7d2ded907..1b53404fc 100644 --- a/src/shader_recompiler/host_translate_info.h +++ b/src/shader_recompiler/host_translate_info.h @@ -16,6 +16,7 @@ struct HostTranslateInfo { bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS + u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry ///< passthrough shaders bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp index d1e59f22e..0cea79945 100644 --- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp +++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp @@ -11,6 +11,7 @@ #include "shader_recompiler/frontend/ir/breadth_first_search.h" #include "shader_recompiler/frontend/ir/ir_emitter.h" #include "shader_recompiler/frontend/ir/value.h" +#include "shader_recompiler/host_translate_info.h" #include "shader_recompiler/ir_opt/passes.h" namespace Shader::Optimization { @@ -408,7 +409,7 @@ void CollectStorageBuffers(IR::Block& block, IR::Inst& inst, StorageInfo& info) } /// Returns the offset in indices (not bytes) for an equivalent storage instruction -IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer) { +IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer, u32 alignment) { IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::U32 offset; if (const std::optional<LowAddrInfo> low_addr{TrackLowAddress(&inst)}) { @@ -421,7 +422,10 @@ IR::U32 StorageOffset(IR::Block& block, IR::Inst& inst, StorageBufferAddr buffer } // Subtract the least significant 32 bits from the guest offset. The result is the storage // buffer offset in bytes. - const IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; + IR::U32 low_cbuf{ir.GetCbuf(ir.Imm32(buffer.index), ir.Imm32(buffer.offset))}; + + // Align the offset base to match the host alignment requirements + low_cbuf = ir.BitwiseAnd(low_cbuf, ir.Imm32(~(alignment - 1U))); return ir.ISub(offset, low_cbuf); } @@ -516,7 +520,7 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index, } } // Anonymous namespace -void GlobalMemoryToStorageBufferPass(IR::Program& program) { +void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info) { StorageInfo info; for (IR::Block* const block : program.post_order_blocks) { for (IR::Inst& inst : block->Instructions()) { @@ -540,7 +544,8 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) { const IR::U32 index{IR::Value{static_cast<u32>(info.set.index_of(it))}}; IR::Block* const block{storage_inst.block}; IR::Inst* const inst{storage_inst.inst}; - const IR::U32 offset{StorageOffset(*block, *inst, storage_buffer)}; + const IR::U32 offset{ + StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)}; Replace(*block, *inst, index, offset); } } diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index d4d5285e5..1e637cb23 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h @@ -16,7 +16,7 @@ void CollectShaderInfoPass(Environment& env, IR::Program& program); void ConditionalBarrierPass(IR::Program& program); void ConstantPropagationPass(Environment& env, IR::Program& program); void DeadCodeEliminationPass(IR::Program& program); -void GlobalMemoryToStorageBufferPass(IR::Program& program); +void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateInfo& host_info); void IdentityRemovalPass(IR::Program& program); void LowerFp64ToFp32(IR::Program& program); void LowerFp16ToFp32(IR::Program& program); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index a9de9f4a9..66901a965 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -85,6 +85,8 @@ struct Profile { /// Maxwell and earlier nVidia architectures have broken robust support bool has_broken_robust{}; + + u64 min_ssbo_alignment{}; }; } // namespace Shader diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index b65b9f2a2..c22c7631c 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(video_core STATIC buffer_cache/buffer_cache.cpp buffer_cache/buffer_cache.h buffer_cache/memory_tracker_base.h + buffer_cache/usage_tracker.h buffer_cache/word_manager.h cache_types.h cdma_pusher.cpp diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index f5b10411b..6d1fc3887 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -67,6 +67,7 @@ void BufferCache<P>::TickFrame() { if (!channel_state) { return; } + runtime.TickFrame(slot_buffers); // Calculate hits and shots and move hit bits to the right const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(), @@ -230,7 +231,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am for (const IntervalType& add_interval : tmp_intervals) { common_ranges.add(add_interval); } - runtime.CopyBuffer(dest_buffer, src_buffer, copies); + const auto& copy = copies[0]; + src_buffer.MarkUsage(copy.src_offset, copy.size); + dest_buffer.MarkUsage(copy.dst_offset, copy.size); + runtime.CopyBuffer(dest_buffer, src_buffer, copies, true); if (has_new_downloads) { memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); } @@ -258,9 +262,10 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { common_ranges.subtract(subtract_interval); const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); - auto& dest_buffer = slot_buffers[buffer]; + Buffer& dest_buffer = slot_buffers[buffer]; const u32 offset = dest_buffer.Offset(*cpu_dst_address); runtime.ClearBuffer(dest_buffer, offset, size, value); + dest_buffer.MarkUsage(offset, size); return true; } @@ -603,6 +608,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset); const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size}; async_downloads += std::make_pair(base_interval, 1); + buffer.MarkUsage(copy.src_offset, copy.size); runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); normalized_copies.push_back(second_copy); } @@ -621,8 +627,9 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { // Have in mind the staging buffer offset for the copy copy.dst_offset += download_staging.offset; const std::array copies{copy}; - runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, - false); + Buffer& buffer = slot_buffers[buffer_id]; + buffer.MarkUsage(copy.src_offset, copy.size); + runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); } runtime.PostCopyBarrier(); runtime.Finish(); @@ -742,7 +749,7 @@ void BufferCache<P>::BindHostIndexBuffer() { {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; std::memcpy(upload_staging.mapped_span.data(), draw_state.inline_index_draw_indexes.data(), size); - runtime.CopyBuffer(buffer, upload_staging.buffer, copies); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true); } else { buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); } @@ -754,6 +761,7 @@ void BufferCache<P>::BindHostIndexBuffer() { offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); runtime.BindIndexBuffer(buffer, new_offset, size); } else { + buffer.MarkUsage(offset, size); runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, draw_state.index_buffer.first, draw_state.index_buffer.count, buffer, offset, size); @@ -790,6 +798,7 @@ void BufferCache<P>::BindHostVertexBuffers() { const u32 stride = maxwell3d->regs.vertex_streams[index].stride; const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, binding.size); host_bindings.buffers.push_back(&buffer); host_bindings.offsets.push_back(offset); @@ -895,6 +904,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; } + buffer.MarkUsage(offset, size); if constexpr (NEEDS_BIND_UNIFORM_INDEX) { runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size); } else { @@ -913,6 +923,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) { SynchronizeBuffer(buffer, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, size); const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; if (is_written) { @@ -943,6 +954,7 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) { const u32 offset = buffer.Offset(binding.cpu_addr); const PixelFormat format = binding.format; + buffer.MarkUsage(offset, size); if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) { runtime.BindImageBuffer(buffer, offset, size, format); @@ -975,9 +987,10 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() { MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, size); host_bindings.buffers.push_back(&buffer); host_bindings.offsets.push_back(offset); - host_bindings.sizes.push_back(binding.size); + host_bindings.sizes.push_back(size); } if (host_bindings.buffers.size() > 0) { runtime.BindTransformFeedbackBuffers(host_bindings); @@ -1001,6 +1014,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() { SynchronizeBuffer(buffer, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, size); if constexpr (NEEDS_BIND_UNIFORM_INDEX) { runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size); ++binding_index; @@ -1021,6 +1035,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() { SynchronizeBuffer(buffer, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, size); const bool is_written = ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; @@ -1053,6 +1068,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() { const u32 offset = buffer.Offset(binding.cpu_addr); const PixelFormat format = binding.format; + buffer.MarkUsage(offset, size); if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) { runtime.BindImageBuffer(buffer, offset, size, format); @@ -1172,10 +1188,11 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) { if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); } + const BufferId buffer_id = FindBuffer(*cpu_addr, size); channel_state->vertex_buffers[index] = Binding{ .cpu_addr = *cpu_addr, .size = size, - .buffer_id = FindBuffer(*cpu_addr, size), + .buffer_id = buffer_id, }; } @@ -1401,7 +1418,8 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, .dst_offset = dst_base_offset, .size = overlap.SizeBytes(), }); - runtime.CopyBuffer(new_buffer, overlap, copies); + new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size); + runtime.CopyBuffer(new_buffer, overlap, copies, true); DeleteBuffer(overlap_id, true); } @@ -1414,7 +1432,9 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) { const u32 size = static_cast<u32>(overlap.end - overlap.begin); const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); auto& new_buffer = slot_buffers[new_buffer_id]; - runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0); + const size_t size_bytes = new_buffer.SizeBytes(); + runtime.ClearBuffer(new_buffer, 0, size_bytes, 0); + new_buffer.MarkUsage(0, size_bytes); for (const BufferId overlap_id : overlap.ids) { JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); } @@ -1467,11 +1487,6 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { template <class P> bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { - return SynchronizeBufferImpl(buffer, cpu_addr, size); -} - -template <class P> -bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { boost::container::small_vector<BufferCopy, 4> copies; u64 total_size_bytes = 0; u64 largest_copy = 0; @@ -1494,51 +1509,6 @@ bool BufferCache<P>::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s } template <class P> -bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) { - boost::container::small_vector<BufferCopy, 4> copies; - u64 total_size_bytes = 0; - u64 largest_copy = 0; - IntervalSet found_sets{}; - auto make_copies = [&] { - for (auto& interval : found_sets) { - const std::size_t sub_size = interval.upper() - interval.lower(); - const VAddr cpu_addr_ = interval.lower(); - copies.push_back(BufferCopy{ - .src_offset = total_size_bytes, - .dst_offset = cpu_addr_ - buffer.CpuAddr(), - .size = sub_size, - }); - total_size_bytes += sub_size; - largest_copy = std::max<u64>(largest_copy, sub_size); - } - const std::span<BufferCopy> copies_span(copies.data(), copies.size()); - UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); - }; - memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) { - const VAddr base_adr = cpu_addr_out; - const VAddr end_adr = base_adr + range_size; - const IntervalType add_interval{base_adr, end_adr}; - found_sets.add(add_interval); - }); - if (found_sets.empty()) { - return true; - } - const IntervalType search_interval{cpu_addr, cpu_addr + size}; - auto it = common_ranges.lower_bound(search_interval); - auto it_end = common_ranges.upper_bound(search_interval); - if (it == common_ranges.end()) { - make_copies(); - return false; - } - while (it != it_end) { - found_sets.subtract(*it); - it++; - } - make_copies(); - return false; -} - -template <class P> void BufferCache<P>::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, std::span<BufferCopy> copies) { if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) { @@ -1586,7 +1556,8 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer, // Apply the staging offset copy.src_offset += upload_staging.offset; } - runtime.CopyBuffer(buffer, upload_staging.buffer, copies); + const bool can_reorder = runtime.CanReorderUpload(buffer, copies); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder); } } @@ -1628,7 +1599,8 @@ void BufferCache<P>::InlineMemoryImplementation(VAddr dest_address, size_t copy_ }}; u8* const src_pointer = upload_staging.mapped_span.data(); std::memcpy(src_pointer, inlined_buffer.data(), copy_size); - runtime.CopyBuffer(buffer, upload_staging.buffer, copies); + const bool can_reorder = runtime.CanReorderUpload(buffer, copies); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder); } else { buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); } @@ -1681,8 +1653,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si for (BufferCopy& copy : copies) { // Modify copies to have the staging offset in mind copy.dst_offset += download_staging.offset; + buffer.MarkUsage(copy.src_offset, copy.size); } - runtime.CopyBuffer(download_staging.buffer, buffer, copies_span); + runtime.CopyBuffer(download_staging.buffer, buffer, copies_span, true); runtime.Finish(); for (const BufferCopy& copy : copies) { const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; @@ -1780,15 +1753,25 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index, const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr)); return std::min(memory_layout_size, static_cast<u32>(8_MiB)); }(); - const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); - if (!cpu_addr || size == 0) { + // Alignment only applies to the offset of the buffer + const u32 alignment = runtime.GetStorageBufferAlignment(); + const GPUVAddr aligned_gpu_addr = Common::AlignDown(gpu_addr, alignment); + const u32 aligned_size = static_cast<u32>(gpu_addr - aligned_gpu_addr) + size; + + const std::optional<VAddr> aligned_cpu_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr); + if (!aligned_cpu_addr || size == 0) { LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index); return NULL_BINDING; } - const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, YUZU_PAGESIZE); + const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr); + ASSERT_MSG(cpu_addr, "Unaligned storage buffer address not found for cbuf index {}", + cbuf_index); + // The end address used for size calculation does not need to be aligned + const VAddr cpu_end = Common::AlignUp(*cpu_addr + size, Core::Memory::YUZU_PAGESIZE); + const Binding binding{ - .cpu_addr = *cpu_addr, - .size = is_written ? size : static_cast<u32>(cpu_end - *cpu_addr), + .cpu_addr = *aligned_cpu_addr, + .size = is_written ? aligned_size : static_cast<u32>(cpu_end - *aligned_cpu_addr), .buffer_id = BufferId{}, }; return binding; diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index eed267361..d6d696d8c 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -529,10 +529,6 @@ private: bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); - bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); - - bool SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size); - void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, std::span<BufferCopy> copies); diff --git a/src/video_core/buffer_cache/usage_tracker.h b/src/video_core/buffer_cache/usage_tracker.h new file mode 100644 index 000000000..ab05fe415 --- /dev/null +++ b/src/video_core/buffer_cache/usage_tracker.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/alignment.h" +#include "common/common_types.h" + +namespace VideoCommon { + +class UsageTracker { + static constexpr size_t BYTES_PER_BIT_SHIFT = 6; + static constexpr size_t PAGE_SHIFT = 6 + BYTES_PER_BIT_SHIFT; + static constexpr size_t PAGE_BYTES = 1 << PAGE_SHIFT; + +public: + explicit UsageTracker(size_t size) { + const size_t num_pages = (size >> PAGE_SHIFT) + 1; + pages.resize(num_pages, 0ULL); + } + + void Reset() noexcept { + std::ranges::fill(pages, 0ULL); + } + + void Track(u64 offset, u64 size) noexcept { + const size_t page = offset >> PAGE_SHIFT; + const size_t page_end = (offset + size) >> PAGE_SHIFT; + TrackPage(page, offset, size); + if (page == page_end) { + return; + } + for (size_t i = page + 1; i < page_end; i++) { + pages[i] = ~u64{0}; + } + const size_t offset_end = offset + size; + const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES); + TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned); + } + + [[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept { + const size_t page = offset >> PAGE_SHIFT; + const size_t page_end = (offset + size) >> PAGE_SHIFT; + if (IsPageUsed(page, offset, size)) { + return true; + } + for (size_t i = page + 1; i < page_end; i++) { + if (pages[i] != 0) { + return true; + } + } + const size_t offset_end = offset + size; + const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES); + return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned); + } + +private: + void TrackPage(u64 page, u64 offset, u64 size) noexcept { + const size_t offset_in_page = offset % PAGE_BYTES; + const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; + const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; + const size_t mask = ~u64{0} >> (64 - num_bits); + pages[page] |= (~u64{0} & mask) << first_bit; + } + + bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept { + const size_t offset_in_page = offset % PAGE_BYTES; + const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; + const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; + const size_t mask = ~u64{0} >> (64 - num_bits); + const size_t mask2 = (~u64{0} & mask) << first_bit; + return (pages[page] & mask2) != 0; + } + +private: + std::vector<u64> pages; +}; + +} // namespace VideoCommon diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 38d553d3c..dfd696de6 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -178,13 +178,14 @@ void BufferCacheRuntime::CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, } void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, - std::span<const VideoCommon::BufferCopy> copies, bool barrier) { + std::span<const VideoCommon::BufferCopy> copies, bool barrier, + bool) { CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); } void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, - std::span<const VideoCommon::BufferCopy> copies) { - CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies); + std::span<const VideoCommon::BufferCopy> copies, bool) { + CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies, true); } void BufferCacheRuntime::PreCopyBarrier() { diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 41b746f3b..000f29a82 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -30,6 +30,8 @@ public: void MakeResident(GLenum access) noexcept; + void MarkUsage(u64 offset, u64 size) {} + [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept { @@ -66,22 +68,29 @@ public: [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size); + bool CanReorderUpload(const Buffer&, std::span<const VideoCommon::BufferCopy>) { + return false; + } + void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, - std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); + std::span<const VideoCommon::BufferCopy> copies, bool barrier); void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, - std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); + std::span<const VideoCommon::BufferCopy> copies, bool barrier); void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, - std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); + std::span<const VideoCommon::BufferCopy> copies, bool barrier, + bool can_reorder_upload = false); void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, - std::span<const VideoCommon::BufferCopy> copies); + std::span<const VideoCommon::BufferCopy> copies, bool); void PreCopyBarrier(); void PostCopyBarrier(); void Finish(); + void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {} + void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); @@ -182,6 +191,10 @@ public: return device.CanReportMemoryUsage(); } + u32 GetStorageBufferAlignment() const { + return static_cast<u32>(device.GetShaderStorageBufferAlignment()); + } + private: static constexpr std::array PABO_LUT{ GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 2888e0238..26f2d0ea7 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -232,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .has_gl_bool_ref_bug = device.HasBoolRefBug(), .ignore_nan_fp_comparisons = true, .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), + .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(), }, host_info{ .support_float64 = true, @@ -240,6 +241,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo .needs_demote_reorder = device.IsAmd(), .support_snorm_render_buffer = false, .support_viewport_index_layer = device.HasVertexViewportLayer(), + .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()), .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(), .support_conditional_barrier = device.SupportsConditionalBarriers(), } { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index d8148e89a..5958f52f7 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -79,13 +79,13 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo } // Anonymous namespace Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) - : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params) {} + : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {} Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, VAddr cpu_addr_, u64 size_bytes_) : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(rasterizer_, cpu_addr_, size_bytes_), - device{&runtime.device}, buffer{ - CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} { + device{&runtime.device}, buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, + tracker{SizeBytes()} { if (runtime.device.HasDebuggingToolAttached()) { buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); } @@ -355,12 +355,35 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } +u32 BufferCacheRuntime::GetStorageBufferAlignment() const { + return static_cast<u32>(device.GetStorageBufferAlignment()); +} + +void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept { + for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { + it->ResetUsageTracking(); + } +} + void BufferCacheRuntime::Finish() { scheduler.Finish(); } +bool BufferCacheRuntime::CanReorderUpload(const Buffer& buffer, + std::span<const VideoCommon::BufferCopy> copies) { + if (Settings::values.disable_buffer_reorder) { + return false; + } + const bool can_use_upload_cmdbuf = + std::ranges::all_of(copies, [&](const VideoCommon::BufferCopy& copy) { + return !buffer.IsRegionUsed(copy.dst_offset, copy.size); + }); + return can_use_upload_cmdbuf; +} + void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, - std::span<const VideoCommon::BufferCopy> copies, bool barrier) { + std::span<const VideoCommon::BufferCopy> copies, bool barrier, + bool can_reorder_upload) { if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { return; } @@ -376,9 +399,18 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; + // Measuring a popular game, this number never exceeds the specified size once data is warmed up boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size()); std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); + if (src_buffer == staging_pool.StreamBuf() && can_reorder_upload) { + scheduler.RecordWithUploadBuffer([src_buffer, dst_buffer, vk_copies]( + vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { + upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies); + }); + return; + } + scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { if (barrier) { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 95446c732..0b3fbd6d0 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -5,6 +5,7 @@ #include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/buffer_cache/memory_tracker_base.h" +#include "video_core/buffer_cache/usage_tracker.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" @@ -34,6 +35,18 @@ public: return *buffer; } + [[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept { + return tracker.IsUsed(offset, size); + } + + void MarkUsage(u64 offset, u64 size) noexcept { + tracker.Track(offset, size); + } + + void ResetUsageTracking() noexcept { + tracker.Reset(); + } + operator VkBuffer() const noexcept { return *buffer; } @@ -49,6 +62,7 @@ private: const Device* device{}; vk::Buffer buffer; std::vector<BufferView> views; + VideoCommon::UsageTracker tracker; }; class QuadArrayIndexBuffer; @@ -67,6 +81,8 @@ public: ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool); + void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept; + void Finish(); u64 GetDeviceLocalMemory() const; @@ -75,16 +91,21 @@ public: bool CanReportMemoryUsage() const; + u32 GetStorageBufferAlignment() const; + [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); + bool CanReorderUpload(const Buffer& buffer, std::span<const VideoCommon::BufferCopy> copies); + void FreeDeferredStagingBuffer(StagingBufferRef& ref); void PreCopyBarrier(); void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, - std::span<const VideoCommon::BufferCopy> copies, bool barrier = true); + std::span<const VideoCommon::BufferCopy> copies, bool barrier, + bool can_reorder_upload = false); void PostCopyBarrier(); diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 6b288b994..ac8b6e838 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -100,12 +100,14 @@ void MasterSemaphore::Wait(u64 tick) { Refresh(); } -VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick) { +VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick) { if (semaphore) { - return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick); + return SubmitQueueTimeline(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, + host_tick); } else { - return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick); + return SubmitQueueFence(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, host_tick); } } @@ -115,6 +117,7 @@ static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{ }; VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, + vk::CommandBuffer& upload_cmdbuf, VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick) { const VkSemaphore timeline_semaphore = *semaphore; @@ -123,6 +126,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, const std::array signal_values{host_tick, u64(0)}; const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; + const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; + const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; const VkTimelineSemaphoreSubmitInfo timeline_si{ .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, @@ -138,8 +143,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, .waitSemaphoreCount = num_wait_semaphores, .pWaitSemaphores = &wait_semaphore, .pWaitDstStageMask = wait_stage_masks.data(), - .commandBufferCount = 1, - .pCommandBuffers = cmdbuf.address(), + .commandBufferCount = static_cast<u32>(cmdbuffers.size()), + .pCommandBuffers = cmdbuffers.data(), .signalSemaphoreCount = num_signal_semaphores, .pSignalSemaphores = signal_semaphores.data(), }; @@ -147,19 +152,23 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, return device.GetGraphicsQueue().Submit(submit_info); } -VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick) { +VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, + vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick) { const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; + const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; + const VkSubmitInfo submit_info{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreCount = num_wait_semaphores, .pWaitSemaphores = &wait_semaphore, .pWaitDstStageMask = wait_stage_masks.data(), - .commandBufferCount = 1, - .pCommandBuffers = cmdbuf.address(), + .commandBufferCount = static_cast<u32>(cmdbuffers.size()), + .pCommandBuffers = cmdbuffers.data(), .signalSemaphoreCount = num_signal_semaphores, .pSignalSemaphores = &signal_semaphore, }; diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 3f599d7bd..7dfb93ffb 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -52,14 +52,16 @@ public: void Wait(u64 tick); /// Submits the device graphics queue, updating the tick as necessary - VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick); + VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick); private: - VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick); - VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick); + VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick); + VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick); void WaitThread(std::stop_token token); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 89b455bff..2a13b2a72 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -373,6 +373,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, .has_broken_robust = device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, + .min_ssbo_alignment = device.GetStorageBufferAlignment(), }; host_info = Shader::HostTranslateInfo{ @@ -383,6 +384,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, .support_snorm_render_buffer = true, .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), + .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), .support_conditional_barrier = device.SupportsConditionalBarriers(), }; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 3be7837f4..146923db4 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -22,11 +22,12 @@ namespace Vulkan { MICROPROFILE_DECLARE(Vulkan_WaitForWorker); -void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) { +void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf, + vk::CommandBuffer upload_cmdbuf) { auto command = first; while (command != nullptr) { auto next = command->GetNext(); - command->Execute(cmdbuf); + command->Execute(cmdbuf, upload_cmdbuf); command->~Command(); command = next; } @@ -180,7 +181,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { // Perform the work, tracking whether the chunk was a submission // before executing. const bool has_submit = work->HasSubmit(); - work->ExecuteAll(current_cmdbuf); + work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf); // If the chunk was a submission, reallocate the command buffer. if (has_submit) { @@ -205,6 +206,13 @@ void Scheduler::AllocateWorkerCommandBuffer() { .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, .pInheritanceInfo = nullptr, }); + current_upload_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader()); + current_upload_cmdbuf.Begin({ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = nullptr, + }); } u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { @@ -212,7 +220,17 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se InvalidateState(); const u64 signal_value = master_semaphore->NextTick(); - Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { + RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value, + this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { + static constexpr VkMemoryBarrier WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + }; + upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); + upload_cmdbuf.End(); cmdbuf.End(); if (on_submit) { @@ -221,7 +239,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se std::scoped_lock lock{submit_mutex}; switch (const VkResult result = master_semaphore->SubmitQueue( - cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { + cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { case VK_SUCCESS: break; case VK_ERROR_DEVICE_LOST: diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index da03803aa..f8d8ca80a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -80,7 +80,8 @@ public: /// Send work to a separate thread. template <typename T> - void Record(T&& command) { + requires std::is_invocable_v<T, vk::CommandBuffer, vk::CommandBuffer> + void RecordWithUploadBuffer(T&& command) { if (chunk->Record(command)) { return; } @@ -88,6 +89,15 @@ public: (void)chunk->Record(command); } + template <typename T> + requires std::is_invocable_v<T, vk::CommandBuffer> + void Record(T&& c) { + this->RecordWithUploadBuffer( + [command = std::move(c)](vk::CommandBuffer cmdbuf, vk::CommandBuffer) { + command(cmdbuf); + }); + } + /// Returns the current command buffer tick. [[nodiscard]] u64 CurrentTick() const noexcept { return master_semaphore->CurrentTick(); @@ -119,7 +129,7 @@ private: public: virtual ~Command() = default; - virtual void Execute(vk::CommandBuffer cmdbuf) const = 0; + virtual void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const = 0; Command* GetNext() const { return next; @@ -142,8 +152,8 @@ private: TypedCommand(TypedCommand&&) = delete; TypedCommand& operator=(TypedCommand&&) = delete; - void Execute(vk::CommandBuffer cmdbuf) const override { - command(cmdbuf); + void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const override { + command(cmdbuf, upload_cmdbuf); } private: @@ -152,7 +162,7 @@ private: class CommandChunk final { public: - void ExecuteAll(vk::CommandBuffer cmdbuf); + void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf); template <typename T> bool Record(T& command) { @@ -228,6 +238,7 @@ private: VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr; vk::CommandBuffer current_cmdbuf; + vk::CommandBuffer current_upload_cmdbuf; std::unique_ptr<CommandChunk> chunk; std::function<void()> on_submit; diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp index 5efd7d66e..70644ea82 100644 --- a/src/video_core/renderer_vulkan/vk_smaa.cpp +++ b/src/video_core/renderer_vulkan/vk_smaa.cpp @@ -672,7 +672,7 @@ void SMAA::UploadImages(Scheduler& scheduler) { UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); - scheduler.Record([&](vk::CommandBuffer& cmdbuf) { + scheduler.Record([&](vk::CommandBuffer cmdbuf) { for (auto& images : m_dynamic_images) { for (size_t i = 0; i < MaxDynamicImage; i++) { ClearColorImage(cmdbuf, *images.images[i]); @@ -707,7 +707,7 @@ VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_ UpdateDescriptorSets(source_image_view, image_index); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) { + scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index d3deb9072..f63a20327 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -36,6 +36,10 @@ public: StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); void FreeDeferred(StagingBufferRef& ref); + [[nodiscard]] VkBuffer StreamBuf() const noexcept { + return *stream_buffer; + } + void TickFrame(); private: diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index de34f6d49..5dbec2e62 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1785,8 +1785,22 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, : VideoCommon::ImageViewBase{info, view_info, gpu_addr_}, buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {} -ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params) - : VideoCommon::ImageViewBase{params} {} +ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params) + : VideoCommon::ImageViewBase{params}, device{&runtime.device} { + if (device->HasNullDescriptor()) { + return; + } + + // Handle fallback for devices without nullDescriptor + ImageInfo info{}; + info.format = PixelFormat::A8B8G8R8_UNORM; + + null_image = MakeImage(*device, runtime.memory_allocator, info, {}); + image_handle = *null_image; + for (u32 i = 0; i < Shader::NUM_TEXTURE_TYPES; i++) { + image_views[i] = MakeView(VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_ASPECT_COLOR_BIT); + } +} ImageView::~ImageView() = default; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 7a0807709..edf5d7635 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -267,6 +267,7 @@ private: vk::ImageView depth_view; vk::ImageView stencil_view; vk::ImageView color_view; + vk::Image null_image; VkImage image_handle = VK_NULL_HANDLE; VkImageView render_target = VK_NULL_HANDLE; VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 9df6a2903..3ffa2a661 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h @@ -138,6 +138,10 @@ public: return Iterator(this, SlotId{SlotId::INVALID_INDEX}); } + [[nodiscard]] size_t size() const noexcept { + return values_capacity - free_list.size(); + } + private: struct NonTrivialDummy { NonTrivialDummy() noexcept {} diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 6900b8ffa..fde36a49c 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -869,7 +869,8 @@ bool Device::ShouldBoostClocks() const { driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; - const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; + const bool is_steam_deck = (vendor_id == 0x1002 && device_id == 0x163F) || + (vendor_id == 0x1002 && device_id == 0x1435); const bool is_debugging = this->HasDebuggingToolAttached(); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 0487cd3b6..a0c70797f 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -1101,6 +1101,10 @@ public: return &handle; } + VkCommandBuffer operator*() const noexcept { + return handle; + } + void Begin(const VkCommandBufferBeginInfo& begin_info) const { Check(dld->vkBeginCommandBuffer(handle, &begin_info)); } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 2afa72140..ed5750155 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -30,7 +30,6 @@ #include <QSize> #include <QStringLiteral> #include <QSurfaceFormat> -#include <QTimer> #include <QWindow> #include <QtCore/qobjectdefs.h> @@ -66,6 +65,8 @@ class QObject; class QPaintEngine; class QSurface; +constexpr int default_mouse_constrain_timeout = 10; + EmuThread::EmuThread(Core::System& system) : m_system{system} {} EmuThread::~EmuThread() = default; @@ -304,6 +305,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, Qt::QueuedConnection); connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged); + + mouse_constrain_timer.setInterval(default_mouse_constrain_timeout); + connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse); } void GRenderWindow::ExecuteProgram(std::size_t program_index) { @@ -393,6 +397,22 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); } +void GRenderWindow::leaveEvent(QEvent* event) { + if (Settings::values.mouse_panning) { + const QRect& rect = QWidget::geometry(); + QPoint position = QCursor::pos(); + + qint32 x = qBound(rect.left(), position.x(), rect.right()); + qint32 y = qBound(rect.top(), position.y(), rect.bottom()); + // Only start the timer if the mouse has left the window bound. + // The leave event is also triggered when the window looses focus. + if (x != position.x() || y != position.y()) { + mouse_constrain_timer.start(); + } + event->accept(); + } +} + int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = { std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A}, @@ -658,10 +678,19 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { input_subsystem->GetMouse()->TouchMove(touch_x, touch_y); input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y); + // Center mouse for mouse panning if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); } + // Constrain mouse for mouse emulation with mouse panning + if (Settings::values.mouse_panning && Settings::values.mouse_enabled) { + const auto [clamped_mouse_x, clamped_mouse_y] = ClipToTouchScreen(x, y); + QCursor::setPos(mapToGlobal( + QPoint{static_cast<int>(clamped_mouse_x), static_cast<int>(clamped_mouse_y)})); + } + + mouse_constrain_timer.stop(); emit MouseActivity(); } @@ -675,6 +704,31 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { input_subsystem->GetMouse()->ReleaseButton(button); } +void GRenderWindow::ConstrainMouse() { + if (emu_thread == nullptr || !Settings::values.mouse_panning) { + mouse_constrain_timer.stop(); + return; + } + if (!this->isActiveWindow()) { + mouse_constrain_timer.stop(); + return; + } + + if (Settings::values.mouse_enabled) { + const auto pos = mapFromGlobal(QCursor::pos()); + const int new_pos_x = std::clamp(pos.x(), 0, width()); + const int new_pos_y = std::clamp(pos.y(), 0, height()); + + QCursor::setPos(mapToGlobal(QPoint{new_pos_x, new_pos_y})); + return; + } + + const int center_x = width() / 2; + const int center_y = height() / 2; + + QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); +} + void GRenderWindow::wheelEvent(QWheelEvent* event) { const int x = event->angleDelta().x(); const int y = event->angleDelta().y(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 87b23df12..60edd464c 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -17,6 +17,7 @@ #include <QString> #include <QStringList> #include <QThread> +#include <QTimer> #include <QWidget> #include <qglobal.h> #include <qnamespace.h> @@ -38,7 +39,6 @@ class QMouseEvent; class QObject; class QResizeEvent; class QShowEvent; -class QTimer; class QTouchEvent; class QWheelEvent; @@ -166,6 +166,7 @@ public: std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; void closeEvent(QCloseEvent* event) override; + void leaveEvent(QEvent* event) override; void resizeEvent(QResizeEvent* event) override; @@ -229,6 +230,7 @@ private: void TouchBeginEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); + void ConstrainMouse(); void RequestCameraCapture(); void OnCameraCapture(int requestId, const QImage& img); @@ -268,6 +270,8 @@ private: std::unique_ptr<QTimer> camera_timer; #endif + QTimer mouse_constrain_timer; + Core::System& system; protected: diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index ef421c754..1010038b7 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() { ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); ui->enable_renderdoc_hotkey->setEnabled(runtime_lock); ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue()); + ui->disable_buffer_reorder->setEnabled(runtime_lock); + ui->disable_buffer_reorder->setChecked(Settings::values.disable_buffer_reorder.GetValue()); ui->enable_graphics_debugging->setEnabled(runtime_lock); ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); ui->enable_shader_feedback->setEnabled(runtime_lock); @@ -96,6 +98,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked(); + Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked(); 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(); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 76fe98924..22b51f39c 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -271,19 +271,6 @@ </widget> </item> <item row="8" column="0"> - <widget class="QCheckBox" name="disable_macro_hle"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string> - </property> - <property name="text"> - <string>Disable Macro HLE</string> - </property> - </widget> - </item> - <item row="7" column="0"> <widget class="QCheckBox" name="dump_macros"> <property name="enabled"> <bool>true</bool> @@ -306,17 +293,27 @@ </property> </widget> </item> - <item row="2" column="0"> - <widget class="QCheckBox" name="enable_shader_feedback"> + <item row="6" column="0"> + <widget class="QCheckBox" name="dump_shaders"> + <property name="enabled"> + <bool>true</bool> + </property> <property name="toolTip"> - <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> + <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>Enable Shader Feedback</string> + <string>Dump Game Shaders</string> </property> </widget> </item> - <item row="6" column="0"> + <item row="1" column="0"> + <widget class="QCheckBox" name="enable_renderdoc_hotkey"> + <property name="text"> + <string>Enable Renderdoc Hotkey</string> + </property> + </widget> + </item> + <item row="7" column="0"> <widget class="QCheckBox" name="disable_macro_jit"> <property name="enabled"> <bool>true</bool> @@ -330,20 +327,17 @@ </widget> </item> <item row="9" column="0"> - <spacer name="verticalSpacer_5"> - <property name="orientation"> - <enum>Qt::Vertical</enum> + <widget class="QCheckBox" name="disable_macro_hle"> + <property name="enabled"> + <bool>true</bool> </property> - <property name="sizeType"> - <enum>QSizePolicy::Preferred</enum> + <property name="toolTip"> + <string>When checked, it disables the macro HLE functions. Enabling this makes games run slower</string> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>0</height> - </size> + <property name="text"> + <string>Disable Macro HLE</string> </property> - </spacer> + </widget> </item> <item row="0" column="0"> <widget class="QCheckBox" name="enable_graphics_debugging"> @@ -358,23 +352,39 @@ </property> </widget> </item> - <item row="5" column="0"> - <widget class="QCheckBox" name="dump_shaders"> - <property name="enabled"> - <bool>true</bool> + <item row="10" column="0"> + <spacer name="verticalSpacer_5"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="enable_shader_feedback"> <property name="toolTip"> - <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string> + <string>When checked, yuzu will log statistics about the compiled pipeline cache</string> </property> <property name="text"> - <string>Dump Game Shaders</string> + <string>Enable Shader Feedback</string> </property> </widget> </item> - <item row="1" column="0"> - <widget class="QCheckBox" name="enable_renderdoc_hotkey"> + <item row="5" column="0"> + <widget class="QCheckBox" name="disable_buffer_reorder"> + <property name="toolTip"> + <string><html><head/><body><p>When checked, disables reording of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.</p></body></html></string> + </property> <property name="text"> - <string>Enable Renderdoc Hotkey</string> + <string>Disable Buffer Reorder</string> </property> </widget> </item> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6ef518b6a..10c788290 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -48,6 +48,7 @@ #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/set/set_sys.h" #include "yuzu/multiplayer/state.h" #include "yuzu/util/controller_navigation.h" @@ -187,7 +188,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #endif constexpr int default_mouse_hide_timeout = 2500; -constexpr int default_mouse_center_timeout = 10; constexpr int default_input_update_timeout = 1; constexpr size_t CopyBufferSize = 1_MiB; @@ -438,9 +438,6 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); - mouse_center_timer.setInterval(default_mouse_center_timeout); - connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor); - update_input_timer.setInterval(default_input_update_timeout); connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers); update_input_timer.start(); @@ -1050,7 +1047,12 @@ void GMainWindow::InitializeWidgets() { statusBar()->addPermanentWidget(label); } - // TODO (flTobi): Add the widget when multiplayer is fully implemented + firmware_label = new QLabel(); + firmware_label->setObjectName(QStringLiteral("FirmwareLabel")); + firmware_label->setVisible(false); + firmware_label->setFocusPolicy(Qt::NoFocus); + statusBar()->addPermanentWidget(firmware_label); + statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); @@ -1372,14 +1374,6 @@ void GMainWindow::InitializeHotkeys() { } }); connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] { - if (Settings::values.mouse_enabled) { - Settings::values.mouse_panning = false; - QMessageBox::warning( - this, tr("Emulated mouse is enabled"), - tr("Real mouse input and mouse panning are incompatible. Please disable the " - "emulated mouse in input advanced settings to allow mouse panning.")); - return; - } Settings::values.mouse_panning = !Settings::values.mouse_panning; if (Settings::values.mouse_panning) { render_window->installEventFilter(render_window); @@ -2171,6 +2165,10 @@ void GMainWindow::OnEmulationStopped() { emu_frametime_label->setVisible(false); renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); + if (!firmware_label->text().isEmpty()) { + firmware_label->setVisible(true); + } + current_game_path.clear(); // When closing the game, destroy the GLWindow to clear the context after the game is closed @@ -4613,6 +4611,7 @@ void GMainWindow::UpdateStatusBar() { emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); game_fps_label->setVisible(true); emu_frametime_label->setVisible(true); + firmware_label->setVisible(false); } void GMainWindow::UpdateGPUAccuracyButton() { @@ -4722,26 +4721,10 @@ void GMainWindow::ShowMouseCursor() { } } -void GMainWindow::CenterMouseCursor() { - if (emu_thread == nullptr || !Settings::values.mouse_panning) { - mouse_center_timer.stop(); - return; - } - if (!this->isActiveWindow()) { - mouse_center_timer.stop(); - return; - } - const int center_x = render_window->width() / 2; - const int center_y = render_window->height() / 2; - - QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); -} - void GMainWindow::OnMouseActivity() { if (!Settings::values.mouse_panning) { ShowMouseCursor(); } - mouse_center_timer.stop(); } void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { @@ -4832,6 +4815,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { "games.")); } + SetFirmwareVersion(); + if (behavior == ReinitializeKeyBehavior::Warning) { game_list->PopulateAsync(UISettings::values.game_dirs); } @@ -4859,7 +4844,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() { } bool GMainWindow::CheckFirmwarePresence() { - constexpr u64 MiiEditId = 0x0100000000001009ull; + constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { @@ -4874,6 +4859,28 @@ bool GMainWindow::CheckFirmwarePresence() { return true; } +void GMainWindow::SetFirmwareVersion() { + Service::Set::FirmwareVersionFormat firmware_data{}; + const auto result = Service::Set::GetFirmwareVersionImpl( + firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); + + if (result.IsError() || !CheckFirmwarePresence()) { + LOG_INFO(Frontend, "Installed firmware: No firmware available"); + firmware_label->setVisible(false); + return; + } + + firmware_label->setVisible(true); + + const std::string display_version(firmware_data.display_version.data()); + const std::string display_title(firmware_data.display_title.data()); + + LOG_INFO(Frontend, "Installed firmware: {}", display_title); + + firmware_label->setText(QString::fromStdString(display_version)); + firmware_label->setToolTip(QString::fromStdString(display_title)); +} + bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, u64* selected_title_id, u8* selected_content_record_type) { using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; @@ -5018,22 +5025,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { AcceptDropEvent(event); } -void GMainWindow::leaveEvent(QEvent* event) { - if (Settings::values.mouse_panning) { - const QRect& rect = geometry(); - QPoint position = QCursor::pos(); - - qint32 x = qBound(rect.left(), position.x(), rect.right()); - qint32 y = qBound(rect.top(), position.y(), rect.bottom()); - // Only start the timer if the mouse has left the window bound. - // The leave event is also triggered when the window looses focus. - if (x != position.x() || y != position.y()) { - mouse_center_timer.start(); - } - event->accept(); - } -} - bool GMainWindow::ConfirmChangeGame() { if (emu_thread == nullptr) return true; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 2e13e1834..530e445f9 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -452,13 +452,13 @@ private: void UpdateInputDrivers(); void HideMouseCursor(); void ShowMouseCursor(); - void CenterMouseCursor(); void OpenURL(const QUrl& url); void LoadTranslation(); void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); bool CheckDarkMode(); bool CheckSystemArchiveDecryption(); bool CheckFirmwarePresence(); + void SetFirmwareVersion(); void ConfigureFilesystemProvider(const std::string& filepath); /** * Open (or not) the right confirm dialog based on current setting and game exit lock @@ -513,6 +513,7 @@ private: QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; QLabel* tas_label = nullptr; + QLabel* firmware_label = nullptr; QPushButton* gpu_accuracy_button = nullptr; QPushButton* renderer_status_button = nullptr; QPushButton* dock_status_button = nullptr; @@ -534,7 +535,6 @@ private: bool auto_paused = false; bool auto_muted = false; QTimer mouse_hide_timer; - QTimer mouse_center_timer; QTimer update_input_timer; QString startup_icon_theme; @@ -591,5 +591,4 @@ protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; - void leaveEvent(QEvent* event) override; }; |