diff options
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 | 
