diff options
Diffstat (limited to '')
27 files changed, 310 insertions, 95 deletions
diff --git a/externals/dynarmic b/externals/dynarmic -Subproject befe547d5631024a70d81d2ccee808bbfcb3854 +Subproject 165621a872ffb802c7a26ef5900e1e62681f1a8 diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 32c1b1cb3..9133f5388 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp @@ -302,11 +302,21 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) { std::vector<std::string> device_list; cubeb* ctx; +#ifdef _WIN32 + auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); +#endif + if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); return {}; } +#ifdef _WIN32 + if (SUCCEEDED(com_init_result)) { + CoUninitialize(); + } +#endif + auto type{capture ? CUBEB_DEVICE_TYPE_INPUT : CUBEB_DEVICE_TYPE_OUTPUT}; cubeb_device_collection collection; if (cubeb_enumerate_devices(ctx, type, &collection) != CUBEB_OK) { @@ -329,12 +339,22 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) { u32 GetCubebLatency() { cubeb* ctx; +#ifdef _WIN32 + auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); +#endif + if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); // Return a large latency so we choose SDL instead. return 10000u; } +#ifdef _WIN32 + if (SUCCEEDED(com_init_result)) { + CoUninitialize(); + } +#endif + cubeb_stream_params params{}; params.rate = TargetSampleRate; params.channels = 2; diff --git a/src/common/settings.h b/src/common/settings.h index 64db66f37..6d27dd5ee 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -488,6 +488,7 @@ struct Values { Setting<bool> enable_raw_input{false, "enable_raw_input"}; Setting<bool> controller_navigation{true, "controller_navigation"}; Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"}; + Setting<bool> enable_procon_driver{false, "enable_procon_driver"}; SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp index 64f1d7371..2907cc6e3 100644 --- a/src/core/hle/kernel/k_capabilities.cpp +++ b/src/core/hle/kernel/k_capabilities.cpp @@ -203,23 +203,23 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) { Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) { // Map each region into the process's page table. - R_RETURN(ProcessMapRegionCapability( + return ProcessMapRegionCapability( cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result { // R_RETURN(page_table->MapRegion(region_type, perm)); UNIMPLEMENTED(); R_SUCCEED(); - })); + }); } Result KCapabilities::CheckMapRegion(KernelCore& kernel, const u32 cap) { // Check that each region has a physical backing store. - R_RETURN(ProcessMapRegionCapability( + return ProcessMapRegionCapability( cap, [&](KMemoryRegionType region_type, KMemoryPermission perm) -> Result { R_UNLESS(kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived( region_type) != nullptr, ResultOutOfRange); R_SUCCEED(); - })); + }); } Result KCapabilities::SetInterruptPairCapability(const u32 cap) { diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h index fb2ba4c6b..fb8e7933e 100644 --- a/src/core/hle/kernel/physical_core.h +++ b/src/core/hle/kernel/physical_core.h @@ -3,6 +3,7 @@ #pragma once +#include <condition_variable> #include <cstddef> #include <memory> #include <mutex> diff --git a/src/core/hle/kernel/svc_version.h b/src/core/hle/kernel/svc_version.h index e4f47b34b..3eb95aa7b 100644 --- a/src/core/hle/kernel/svc_version.h +++ b/src/core/hle/kernel/svc_version.h @@ -35,11 +35,11 @@ constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) { } constexpr inline u32 GetKernelMajorVersion(u32 encoded) { - return std::bit_cast<decltype(KernelVersion::major_version)>(encoded).Value(); + return decltype(KernelVersion::major_version)::ExtractValue(encoded); } constexpr inline u32 GetKernelMinorVersion(u32 encoded) { - return std::bit_cast<decltype(KernelVersion::minor_version)>(encoded).Value(); + return decltype(KernelVersion::minor_version)::ExtractValue(encoded); } // Nintendo doesn't support programs targeting SVC versions < 3.0. diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 513ea485a..80eba22e8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -758,12 +758,20 @@ Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { return hid_core.GetSupportedStyleTag(); } -void Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { +Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { + constexpr std::size_t max_number_npad_ids = 0xa; const auto length = data.size(); ASSERT(length > 0 && (length % sizeof(u32)) == 0); + const std::size_t elements = length / sizeof(u32); + + if (elements > max_number_npad_ids) { + return InvalidArraySize; + } + supported_npad_id_types.clear(); - supported_npad_id_types.resize(length / sizeof(u32)); + supported_npad_id_types.resize(elements); std::memcpy(supported_npad_id_types.data(), data.data(), length); + return ResultSuccess; } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 1f7d33459..02cc00920 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -96,7 +96,7 @@ public: void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); Core::HID::NpadStyleTag GetSupportedStyleSet() const; - void SetSupportedNpadIdTypes(std::span<const u8> data); + Result SetSupportedNpadIdTypes(std::span<const u8> data); void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); std::size_t GetSupportedNpadIdTypesSize() const; diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h index 76208e9a4..9585bdaf0 100644 --- a/src/core/hle/service/hid/errors.h +++ b/src/core/hle/service/hid/errors.h @@ -18,6 +18,7 @@ constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; constexpr Result NpadIsSameType{ErrorModule::HID, 602}; constexpr Result InvalidNpadId{ErrorModule::HID, 709}; constexpr Result NpadNotConnected{ErrorModule::HID, 710}; +constexpr Result InvalidArraySize{ErrorModule::HID, 715}; constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index f15f1a6bb..ac2c0c76d 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1025,13 +1025,13 @@ void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSupportedNpadIdTypes(ctx.ReadBuffer()); + const auto result = applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetSupportedNpadIdTypes(ctx.ReadBuffer()); LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) { diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 4fcfb4510..afc33db57 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -16,7 +16,7 @@ namespace InputCommon { Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) { // Avoid conflicting with SDL driver - if (!Settings::values.enable_joycon_driver) { + if (!Settings::values.enable_joycon_driver && !Settings::values.enable_procon_driver) { return; } LOG_INFO(Input, "Joycon driver Initialization started"); @@ -46,6 +46,12 @@ void Joycons::Reset() { } device->Stop(); } + for (const auto& device : pro_controller) { + if (!device) { + continue; + } + device->Stop(); + } SDL_hid_exit(); } @@ -61,6 +67,11 @@ void Joycons::Setup() { PreSetController(GetIdentifier(port, Joycon::ControllerType::Right)); device = std::make_shared<Joycon::JoyconDriver>(port++); } + port = 0; + for (auto& device : pro_controller) { + PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro)); + device = std::make_shared<Joycon::JoyconDriver>(port++); + } scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); }); } @@ -116,6 +127,9 @@ bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { // Check if device already exist switch (type) { case Joycon::ControllerType::Left: + if (!Settings::values.enable_joycon_driver) { + return false; + } for (const auto& device : left_joycons) { if (is_handle_identical(device)) { return false; @@ -123,12 +137,25 @@ bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { } break; case Joycon::ControllerType::Right: + if (!Settings::values.enable_joycon_driver) { + return false; + } for (const auto& device : right_joycons) { if (is_handle_identical(device)) { return false; } } break; + case Joycon::ControllerType::Pro: + if (!Settings::values.enable_procon_driver) { + return false; + } + for (const auto& device : pro_controller) { + if (is_handle_identical(device)) { + return false; + } + } + break; default: return false; } @@ -199,6 +226,14 @@ std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle( return *unconnected_device; } } + if (type == Joycon::ControllerType::Pro) { + const auto unconnected_device = std::ranges::find_if( + pro_controller, [](auto& device) { return !device->IsConnected(); }); + + if (unconnected_device != pro_controller.end()) { + return *unconnected_device; + } + } return nullptr; } @@ -409,6 +444,15 @@ std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifie } } + if (type == Joycon::ControllerType::Pro) { + const auto matching_device = std::ranges::find_if( + pro_controller, [is_handle_active](auto& device) { return is_handle_active(device); }); + + if (matching_device != pro_controller.end()) { + return *matching_device; + } + } + return nullptr; } @@ -455,6 +499,9 @@ std::vector<Common::ParamPackage> Joycons::GetInputDevices() const { for (const auto& controller : right_joycons) { add_entry(controller); } + for (const auto& controller : pro_controller) { + add_entry(controller); + } // List dual joycon pairs for (std::size_t i = 0; i < MaxSupportedControllers; i++) { diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index 2149ab7fd..473ba1b9e 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -106,6 +106,7 @@ private: // Joycon types are split by type to ease supporting dualjoycon configurations std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{}; std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{}; + std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> pro_controller{}; }; } // namespace InputCommon diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index d975eb815..88cacd615 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -343,6 +343,14 @@ void SDLDriver::InitJoystick(int joystick_index) { } } + if (Settings::values.enable_procon_driver) { + if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && guid.uuid[8] == 0x09) { + LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index); + SDL_JoystickClose(sdl_joystick); + return; + } + } + std::scoped_lock lock{joystick_map_mutex}; if (joystick_map.find(guid) == joystick_map.end()) { auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); @@ -465,13 +473,19 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); - // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled + // Disable hidapi drivers for joycon controllers when the custom joycon driver is enabled if (Settings::values.enable_joycon_driver) { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); } else { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); } - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); + + // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled + if (Settings::values.enable_procon_driver) { + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0"); + } else { + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); + } // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native // driver on Linux. diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 8f94c9f45..e65b6b845 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -543,9 +543,10 @@ void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) { DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, ControllerType& controller_type) { - static constexpr std::array<std::pair<u32, ControllerType>, 2> supported_devices{ + static constexpr std::array<std::pair<u32, ControllerType>, 6> supported_devices{ std::pair<u32, ControllerType>{0x2006, ControllerType::Left}, {0x2007, ControllerType::Right}, + {0x2009, ControllerType::Pro}, }; constexpr u16 nintendo_vendor_id = 0x057e; diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index 1b006e811..c3c2281bb 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -310,12 +310,6 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile if (runtime_info.force_early_z) { header += "layout(early_fragment_tests)in;"; } - if (info.uses_sample_id) { - header += "in int gl_SampleID;"; - } - if (info.stores_sample_mask) { - header += "out int gl_SampleMask[];"; - } break; case Stage::Compute: stage_name = "cs"; diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index 1275cca24..734dbf4b6 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp @@ -538,7 +538,7 @@ TEST_CASE("BufferBase: Cached write downloads") { int num = 0; buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; }); buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); - REQUIRE(num == 1); + REQUIRE(num == 0); REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE)); buffer.FlushCachedWrites(); diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index c47b7d866..92d77eef2 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -430,7 +430,7 @@ private: if (query_begin >= SizeBytes() || size < 0) { return; } - [[maybe_unused]] u64* const untracked_words = Array<Type::Untracked>(); + u64* const untracked_words = Array<Type::Untracked>(); u64* const state_words = Array<type>(); const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes()); u64* const words_begin = state_words + query_begin / BYTES_PER_WORD; @@ -483,7 +483,7 @@ private: NotifyRasterizer<true>(word_index, current_bits, ~u64{0}); } // Exclude CPU modified pages when visiting GPU pages - const u64 word = current_word; + const u64 word = current_word & ~(type == Type::GPU ? untracked_words[word_index] : 0); u64 page = page_begin; page_begin = 0; @@ -531,7 +531,7 @@ private: [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { static_assert(type != Type::Untracked); - [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>(); + const u64* const untracked_words = Array<Type::Untracked>(); const u64* const state_words = Array<type>(); const u64 num_query_words = size / BYTES_PER_WORD + 1; const u64 word_begin = offset / BYTES_PER_WORD; @@ -539,7 +539,8 @@ private: const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { - const u64 word = state_words[word_index]; + const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0; + const u64 word = state_words[word_index] & ~off_word; if (word == 0) { continue; } @@ -563,7 +564,7 @@ private: [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept { static_assert(type != Type::Untracked); - [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>(); + const u64* const untracked_words = Array<Type::Untracked>(); const u64* const state_words = Array<type>(); const u64 num_query_words = size / BYTES_PER_WORD + 1; const u64 word_begin = offset / BYTES_PER_WORD; @@ -573,7 +574,8 @@ private: u64 begin = std::numeric_limits<u64>::max(); u64 end = 0; for (u64 word_index = word_begin; word_index < word_end; ++word_index) { - const u64 word = state_words[word_index]; + const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0; + const u64 word = state_words[word_index] & ~off_word; if (word == 0) { continue; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index f91bb5a1d..baedc4424 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -548,31 +548,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { static_vector<VkVertexInputBindingDescription, 32> vertex_bindings; static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; - if (key.state.dynamic_vertex_input) { - const size_t num_vertex_arrays = std::min( - key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings())); - for (size_t index = 0; index < num_vertex_arrays; ++index) { - const u32 type = key.state.DynamicAttributeType(index); - if (!stage_infos[0].loads.Generic(index) || type == 0) { - continue; - } - vertex_attributes.push_back({ - .location = static_cast<u32>(index), - .binding = 0, - .format = type == 1 ? VK_FORMAT_R32_SFLOAT - : type == 2 ? VK_FORMAT_R32_SINT - : VK_FORMAT_R32_UINT, - .offset = 0, - }); - } - if (!vertex_attributes.empty()) { - vertex_bindings.push_back({ - .binding = 0, - .stride = 4, - .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, - }); - } - } else { + if (!key.state.dynamic_vertex_input) { const size_t num_vertex_arrays = std::min( Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings())); for (size_t index = 0; index < num_vertex_arrays; ++index) { diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index 734b0ea40..4ae49506d 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -575,7 +575,7 @@ void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) { QDialog::resize(size); // High DPI - const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; + const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f; RescaleKeyboardElements(size.width(), size.height(), dpi_scale); } diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 35fef506a..31209fb2e 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -441,6 +441,7 @@ void Config::ReadControlValues() { Settings::values.mouse_panning = false; ReadBasicSetting(Settings::values.mouse_panning_sensitivity); ReadBasicSetting(Settings::values.enable_joycon_driver); + ReadBasicSetting(Settings::values.enable_procon_driver); ReadBasicSetting(Settings::values.tas_enable); ReadBasicSetting(Settings::values.tas_loop); @@ -1141,6 +1142,7 @@ void Config::SaveControlValues() { WriteGlobalSetting(Settings::values.motion_enabled); WriteBasicSetting(Settings::values.enable_raw_input); WriteBasicSetting(Settings::values.enable_joycon_driver); + WriteBasicSetting(Settings::values.enable_procon_driver); WriteBasicSetting(Settings::values.keyboard_enabled); WriteBasicSetting(Settings::values.emulate_analog_keyboard); WriteBasicSetting(Settings::values.mouse_panning_sensitivity); diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 77b976e74..8d81322f3 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -139,6 +139,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() { Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked(); + Settings::values.enable_procon_driver = ui->enable_procon_driver->isChecked(); } void ConfigureInputAdvanced::LoadConfiguration() { @@ -174,6 +175,7 @@ void ConfigureInputAdvanced::LoadConfiguration() { ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue()); + ui->enable_procon_driver->setChecked(Settings::values.enable_procon_driver.GetValue()); UpdateUIEnabled(); } diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index 75d96d3ab..0eb2b34bc 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -2712,6 +2712,22 @@ </widget> </item> <item row="6" column="0"> + <widget class="QCheckBox" name="enable_procon_driver"> + <property name="toolTip"> + <string>Requires restarting yuzu</string> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>23</height> + </size> + </property> + <property name="text"> + <string>Enable direct Pro Controller driver [EXPERIMENTAL]</string> + </property> + </widget> + </item> + <item row="7" column="0"> <widget class="QCheckBox" name="mouse_panning"> <property name="minimumSize"> <size> @@ -2724,7 +2740,7 @@ </property> </widget> </item> - <item row="6" column="2"> + <item row="7" column="2"> <widget class="QSpinBox" name="mouse_panning_sensitivity"> <property name="toolTip"> <string>Mouse sensitivity</string> @@ -2746,14 +2762,14 @@ </property> </widget> </item> - <item row="7" column="0"> + <item row="8" column="0"> <widget class="QLabel" name="motion_touch"> <property name="text"> <string>Motion / Touch</string> </property> </widget> </item> - <item row="7" column="2"> + <item row="8" column="2"> <widget class="QPushButton" name="buttonMotionTouch"> <property name="text"> <string>Configure</string> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 42b7b64c8..c278d8dab 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -680,8 +680,10 @@ void GMainWindow::SoftwareKeyboardShowNormal() { const auto y = layout.screen.top; const auto w = layout.screen.GetWidth(); const auto h = layout.screen.GetHeight(); + const auto scale_ratio = devicePixelRatioF(); - software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); + software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y) / scale_ratio), + QSize(w, h) / scale_ratio); } void GMainWindow::SoftwareKeyboardShowTextCheck( @@ -714,9 +716,11 @@ void GMainWindow::SoftwareKeyboardShowInline( (1.0f - appear_parameters.key_top_scale_y)))); const auto w = static_cast<int>(layout.screen.GetWidth() * appear_parameters.key_top_scale_x); const auto h = static_cast<int>(layout.screen.GetHeight() * appear_parameters.key_top_scale_y); + const auto scale_ratio = devicePixelRatioF(); software_keyboard->ShowInlineKeyboard(std::move(appear_parameters), - render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); + render_window->mapToGlobal(QPoint(x, y) / scale_ratio), + QSize(w, h) / scale_ratio); } void GMainWindow::SoftwareKeyboardHideInline() { @@ -796,9 +800,12 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url, } const auto& layout = render_window->GetFramebufferLayout(); - web_browser_view.resize(layout.screen.GetWidth(), layout.screen.GetHeight()); - web_browser_view.move(layout.screen.left, layout.screen.top + menuBar()->height()); - web_browser_view.setZoomFactor(static_cast<qreal>(layout.screen.GetWidth()) / + const auto scale_ratio = devicePixelRatioF(); + web_browser_view.resize(layout.screen.GetWidth() / scale_ratio, + layout.screen.GetHeight() / scale_ratio); + web_browser_view.move(layout.screen.left / scale_ratio, + (layout.screen.top / scale_ratio) + menuBar()->height()); + web_browser_view.setZoomFactor(static_cast<qreal>(layout.screen.GetWidth() / scale_ratio) / static_cast<qreal>(Layout::ScreenUndocked::Width)); web_browser_view.setFocus(); @@ -957,6 +964,38 @@ void GMainWindow::InitializeWidgets() { tas_label->setFocusPolicy(Qt::NoFocus); statusBar()->insertPermanentWidget(0, tas_label); + volume_popup = new QWidget(this); + volume_popup->setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup); + volume_popup->setLayout(new QVBoxLayout()); + volume_popup->setMinimumWidth(200); + + volume_slider = new QSlider(Qt::Horizontal); + volume_slider->setObjectName(QStringLiteral("volume_slider")); + volume_slider->setMaximum(200); + volume_slider->setPageStep(5); + connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) { + Settings::values.audio_muted = false; + const auto volume = static_cast<u8>(percentage); + Settings::values.volume.SetValue(volume); + UpdateVolumeUI(); + }); + volume_popup->layout()->addWidget(volume_slider); + + volume_button = new QPushButton(); + volume_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + volume_button->setFocusPolicy(Qt::NoFocus); + volume_button->setCheckable(true); + UpdateVolumeUI(); + connect(volume_button, &QPushButton::clicked, this, [&] { + UpdateVolumeUI(); + volume_popup->setVisible(!volume_popup->isVisible()); + QRect rect = volume_button->geometry(); + QPoint bottomLeft = statusBar()->mapToGlobal(rect.topLeft()); + bottomLeft.setY(bottomLeft.y() - volume_popup->geometry().height()); + volume_popup->setGeometry(QRect(bottomLeft, QSize(rect.width(), rect.height()))); + }); + statusBar()->insertPermanentWidget(0, volume_button); + // setup AA button aa_status_button = new QPushButton(); aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); @@ -1119,30 +1158,9 @@ void GMainWindow::InitializeHotkeys() { &GMainWindow::OnToggleAdaptingFilter); connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode); connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy); - connect_shortcut(QStringLiteral("Audio Mute/Unmute"), - [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); - connect_shortcut(QStringLiteral("Audio Volume Down"), [] { - const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue()); - int step = 5; - if (current_volume <= 30) { - step = 2; - } - if (current_volume <= 6) { - step = 1; - } - Settings::values.volume.SetValue(std::max(current_volume - step, 0)); - }); - connect_shortcut(QStringLiteral("Audio Volume Up"), [] { - const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue()); - int step = 5; - if (current_volume < 30) { - step = 2; - } - if (current_volume < 6) { - step = 1; - } - Settings::values.volume.SetValue(current_volume + step); - }); + connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &GMainWindow::OnMute); + connect_shortcut(QStringLiteral("Audio Volume Down"), &GMainWindow::OnDecreaseVolume); + connect_shortcut(QStringLiteral("Audio Volume Up"), &GMainWindow::OnIncreaseVolume); connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] { Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue()); }); @@ -3456,6 +3474,39 @@ void GMainWindow::OnToggleGpuAccuracy() { UpdateGPUAccuracyButton(); } +void GMainWindow::OnMute() { + Settings::values.audio_muted = !Settings::values.audio_muted; + UpdateVolumeUI(); +} + +void GMainWindow::OnDecreaseVolume() { + Settings::values.audio_muted = false; + const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue()); + int step = 5; + if (current_volume <= 30) { + step = 2; + } + if (current_volume <= 6) { + step = 1; + } + Settings::values.volume.SetValue(std::max(current_volume - step, 0)); + UpdateVolumeUI(); +} + +void GMainWindow::OnIncreaseVolume() { + Settings::values.audio_muted = false; + const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue()); + int step = 5; + if (current_volume < 30) { + step = 2; + } + if (current_volume < 6) { + step = 1; + } + Settings::values.volume.SetValue(current_volume + step); + UpdateVolumeUI(); +} + void GMainWindow::OnToggleAdaptingFilter() { auto filter = Settings::values.scaling_filter.GetValue(); if (filter == Settings::ScalingFilter::LastFilter) { @@ -3914,6 +3965,18 @@ void GMainWindow::UpdateAAText() { } } +void GMainWindow::UpdateVolumeUI() { + const auto volume_value = static_cast<int>(Settings::values.volume.GetValue()); + volume_slider->setValue(volume_value); + if (Settings::values.audio_muted) { + volume_button->setChecked(false); + volume_button->setText(tr("VOLUME: MUTE")); + } else { + volume_button->setChecked(true); + volume_button->setText(tr("VOLUME: %1%", "Volume percentage (e.g. 50%)").arg(volume_value)); + } +} + void GMainWindow::UpdateStatusButtons() { renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan); @@ -3922,6 +3985,7 @@ void GMainWindow::UpdateStatusButtons() { UpdateDockedButton(); UpdateFilterText(); UpdateAAText(); + UpdateVolumeUI(); } void GMainWindow::UpdateUISettings() { @@ -4391,6 +4455,55 @@ void GMainWindow::changeEvent(QEvent* event) { #undef main #endif +static void SetHighDPIAttributes() { +#ifdef _WIN32 + // For Windows, we want to avoid scaling artifacts on fractional scaling ratios. + // This is done by setting the optimal scaling policy for the primary screen. + + // Create a temporary QApplication. + int temp_argc = 0; + char** temp_argv = nullptr; + QApplication temp{temp_argc, temp_argv}; + + // Get the current screen geometry. + const QScreen* primary_screen = QGuiApplication::primaryScreen(); + if (primary_screen == nullptr) { + return; + } + + const QRect screen_rect = primary_screen->geometry(); + const int real_width = screen_rect.width(); + const int real_height = screen_rect.height(); + const float real_ratio = primary_screen->logicalDotsPerInch() / 96.0f; + + // Recommended minimum width and height for proper window fit. + // Any screen with a lower resolution than this will still have a scale of 1. + constexpr float minimum_width = 1350.0f; + constexpr float minimum_height = 900.0f; + + const float width_ratio = std::max(1.0f, real_width / minimum_width); + const float height_ratio = std::max(1.0f, real_height / minimum_height); + + // Get the lower of the 2 ratios and truncate, this is the maximum integer scale. + const float max_ratio = std::trunc(std::min(width_ratio, height_ratio)); + + if (max_ratio > real_ratio) { + QApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::Round); + } else { + QApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::Floor); + } +#else + // Other OSes should be better than Windows at fractional scaling. + QApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif + + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +} + int main(int argc, char* argv[]) { std::unique_ptr<Config> config = std::make_unique<Config>(); bool has_broken_vulkan = false; @@ -4446,6 +4559,8 @@ int main(int argc, char* argv[]) { } #endif + SetHighDPIAttributes(); + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Disables the "?" button on all dialogs. Disabled by default on Qt6. QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); @@ -4453,6 +4568,7 @@ int main(int argc, char* argv[]) { // Enables the core to make the qt created contexts current on std::threads QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); + QApplication app(argc, argv); #ifdef _WIN32 diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 0f61abc7a..a23b373a5 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -37,6 +37,8 @@ class QLabel; class MultiplayerState; class QPushButton; class QProgressDialog; +class QSlider; +class QHBoxLayout; class WaitTreeWidget; enum class GameListOpenTarget; enum class GameListRemoveTarget; @@ -312,6 +314,9 @@ private slots: void OnMenuRecentFile(); void OnConfigure(); void OnConfigureTas(); + void OnDecreaseVolume(); + void OnIncreaseVolume(); + void OnMute(); void OnTasStartStop(); void OnTasRecord(); void OnTasReset(); @@ -364,6 +369,7 @@ private: void UpdateAPIText(); void UpdateFilterText(); void UpdateAAText(); + void UpdateVolumeUI(); void UpdateStatusBar(); void UpdateGPUAccuracyButton(); void UpdateStatusButtons(); @@ -412,6 +418,9 @@ private: QPushButton* dock_status_button = nullptr; QPushButton* filter_status_button = nullptr; QPushButton* aa_status_button = nullptr; + QPushButton* volume_button = nullptr; + QWidget* volume_popup = nullptr; + QSlider* volume_slider = nullptr; QTimer status_bar_update_timer; std::unique_ptr<Config> config; diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp index 796f5bf41..ee35a3e15 100644 --- a/src/yuzu/util/overlay_dialog.cpp +++ b/src/yuzu/util/overlay_dialog.cpp @@ -163,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() { const auto height = static_cast<float>(parentWidget()->height()); // High DPI - const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f; + const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f; const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; const auto body_text_font_size = diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 9c34cdc6e..3b6dce296 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -178,6 +178,7 @@ void Config::ReadValues() { ReadSetting("ControlsGeneral", Settings::values.enable_raw_input); ReadSetting("ControlsGeneral", Settings::values.enable_joycon_driver); + ReadSetting("ControlsGeneral", Settings::values.enable_procon_driver); ReadSetting("ControlsGeneral", Settings::values.emulate_analog_keyboard); ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 3f3651dbe..cf3cc4c4e 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -286,11 +286,14 @@ vulkan_device = # 0: 0.5x (360p/540p) [EXPERIMENTAL] # 1: 0.75x (540p/810p) [EXPERIMENTAL] # 2 (default): 1x (720p/1080p) -# 3: 2x (1440p/2160p) -# 4: 3x (2160p/3240p) -# 5: 4x (2880p/4320p) -# 6: 5x (3600p/5400p) -# 7: 6x (4320p/6480p) +# 3: 1.5x (1080p/1620p) [EXPERIMENTAL] +# 4: 2x (1440p/2160p) +# 5: 3x (2160p/3240p) +# 6: 4x (2880p/4320p) +# 7: 5x (3600p/5400p) +# 8: 6x (4320p/6480p) +# 9: 7x (5040p/7560p) +# 10: 8x (5760/8640p) resolution_setup = # Pixel filter to use when up- or down-sampling rendered frames. @@ -299,11 +302,11 @@ resolution_setup = # 2: Bicubic # 3: Gaussian # 4: ScaleForce -# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only] +# 5: AMD FidelityFX™️ Super Resolution scaling_filter = # Anti-Aliasing (AA) -# 0 (default): None, 1: FXAA +# 0 (default): None, 1: FXAA, 2: SMAA anti_aliasing = # Whether to use fullscreen or borderless window mode |