diff options
| -rw-r--r-- | src/common/bounded_threadsafe_queue.h | 180 | ||||
| -rw-r--r-- | src/common/settings.h | 2 | ||||
| -rw-r--r-- | src/video_core/gpu_thread.cpp | 3 | ||||
| -rw-r--r-- | src/video_core/gpu_thread.h | 6 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_library.cpp | 4 | ||||
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/check_vulkan.cpp | 53 | ||||
| -rw-r--r-- | src/yuzu/check_vulkan.h | 6 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 8 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 36 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.h | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.ui | 91 | ||||
| -rw-r--r-- | src/yuzu/game_list.cpp | 14 | ||||
| -rw-r--r-- | src/yuzu/game_list.h | 3 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 42 | ||||
| -rw-r--r-- | src/yuzu/uisettings.h | 2 | ||||
| -rw-r--r-- | src/yuzu_cmd/default_ini.h | 2 |
17 files changed, 400 insertions, 56 deletions
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h new file mode 100644 index 000000000..e83064c7f --- /dev/null +++ b/src/common/bounded_threadsafe_queue.h | |||
| @@ -0,0 +1,180 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se> | ||
| 2 | // SPDX-License-Identifier: MIT | ||
| 3 | #pragma once | ||
| 4 | #ifdef _MSC_VER | ||
| 5 | #pragma warning(push) | ||
| 6 | #pragma warning(disable : 4324) | ||
| 7 | #endif | ||
| 8 | |||
| 9 | #include <atomic> | ||
| 10 | #include <bit> | ||
| 11 | #include <condition_variable> | ||
| 12 | #include <memory> | ||
| 13 | #include <mutex> | ||
| 14 | #include <new> | ||
| 15 | #include <stdexcept> | ||
| 16 | #include <stop_token> | ||
| 17 | #include <type_traits> | ||
| 18 | #include <utility> | ||
| 19 | |||
| 20 | namespace Common { | ||
| 21 | namespace mpsc { | ||
| 22 | #if defined(__cpp_lib_hardware_interference_size) | ||
| 23 | constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size; | ||
| 24 | #else | ||
| 25 | constexpr size_t hardware_interference_size = 64; | ||
| 26 | #endif | ||
| 27 | |||
| 28 | template <typename T> | ||
| 29 | using AlignedAllocator = std::allocator<T>; | ||
| 30 | |||
| 31 | template <typename T> | ||
| 32 | struct Slot { | ||
| 33 | ~Slot() noexcept { | ||
| 34 | if (turn.test()) { | ||
| 35 | destroy(); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | template <typename... Args> | ||
| 40 | void construct(Args&&... args) noexcept { | ||
| 41 | static_assert(std::is_nothrow_constructible_v<T, Args&&...>, | ||
| 42 | "T must be nothrow constructible with Args&&..."); | ||
| 43 | std::construct_at(reinterpret_cast<T*>(&storage), std::forward<Args>(args)...); | ||
| 44 | } | ||
| 45 | |||
| 46 | void destroy() noexcept { | ||
| 47 | static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); | ||
| 48 | std::destroy_at(reinterpret_cast<T*>(&storage)); | ||
| 49 | } | ||
| 50 | |||
| 51 | T&& move() noexcept { | ||
| 52 | return reinterpret_cast<T&&>(storage); | ||
| 53 | } | ||
| 54 | |||
| 55 | // Align to avoid false sharing between adjacent slots | ||
| 56 | alignas(hardware_interference_size) std::atomic_flag turn{}; | ||
| 57 | struct aligned_store { | ||
| 58 | struct type { | ||
| 59 | alignas(T) unsigned char data[sizeof(T)]; | ||
| 60 | }; | ||
| 61 | }; | ||
| 62 | typename aligned_store::type storage; | ||
| 63 | }; | ||
| 64 | |||
| 65 | template <typename T, typename Allocator = AlignedAllocator<Slot<T>>> | ||
| 66 | class Queue { | ||
| 67 | public: | ||
| 68 | explicit Queue(const size_t capacity, const Allocator& allocator = Allocator()) | ||
| 69 | : allocator_(allocator) { | ||
| 70 | if (capacity < 1) { | ||
| 71 | throw std::invalid_argument("capacity < 1"); | ||
| 72 | } | ||
| 73 | // Ensure that the queue length is an integer power of 2 | ||
| 74 | // This is so that idx(i) can be a simple i & mask_ insted of i % capacity | ||
| 75 | // https://github.com/rigtorp/MPMCQueue/pull/36 | ||
| 76 | if (!std::has_single_bit(capacity)) { | ||
| 77 | throw std::invalid_argument("capacity must be an integer power of 2"); | ||
| 78 | } | ||
| 79 | |||
| 80 | mask_ = capacity - 1; | ||
| 81 | |||
| 82 | // Allocate one extra slot to prevent false sharing on the last slot | ||
| 83 | slots_ = allocator_.allocate(mask_ + 2); | ||
| 84 | // Allocators are not required to honor alignment for over-aligned types | ||
| 85 | // (see http://eel.is/c++draft/allocator.requirements#10) so we verify | ||
| 86 | // alignment here | ||
| 87 | if (reinterpret_cast<uintptr_t>(slots_) % alignof(Slot<T>) != 0) { | ||
| 88 | allocator_.deallocate(slots_, mask_ + 2); | ||
| 89 | throw std::bad_alloc(); | ||
| 90 | } | ||
| 91 | for (size_t i = 0; i < mask_ + 1; ++i) { | ||
| 92 | std::construct_at(&slots_[i]); | ||
| 93 | } | ||
| 94 | static_assert(alignof(Slot<T>) == hardware_interference_size, | ||
| 95 | "Slot must be aligned to cache line boundary to prevent false sharing"); | ||
| 96 | static_assert(sizeof(Slot<T>) % hardware_interference_size == 0, | ||
| 97 | "Slot size must be a multiple of cache line size to prevent " | ||
| 98 | "false sharing between adjacent slots"); | ||
| 99 | static_assert(sizeof(Queue) % hardware_interference_size == 0, | ||
| 100 | "Queue size must be a multiple of cache line size to " | ||
| 101 | "prevent false sharing between adjacent queues"); | ||
| 102 | } | ||
| 103 | |||
| 104 | ~Queue() noexcept { | ||
| 105 | for (size_t i = 0; i < mask_ + 1; ++i) { | ||
| 106 | slots_[i].~Slot(); | ||
| 107 | } | ||
| 108 | allocator_.deallocate(slots_, mask_ + 2); | ||
| 109 | } | ||
| 110 | |||
| 111 | // non-copyable and non-movable | ||
| 112 | Queue(const Queue&) = delete; | ||
| 113 | Queue& operator=(const Queue&) = delete; | ||
| 114 | |||
| 115 | void Push(const T& v) noexcept { | ||
| 116 | static_assert(std::is_nothrow_copy_constructible_v<T>, | ||
| 117 | "T must be nothrow copy constructible"); | ||
| 118 | emplace(v); | ||
| 119 | } | ||
| 120 | |||
| 121 | template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>> | ||
| 122 | void Push(P&& v) noexcept { | ||
| 123 | emplace(std::forward<P>(v)); | ||
| 124 | } | ||
| 125 | |||
| 126 | void Pop(T& v, std::stop_token stop) noexcept { | ||
| 127 | auto const tail = tail_.fetch_add(1); | ||
| 128 | auto& slot = slots_[idx(tail)]; | ||
| 129 | if (false == slot.turn.test()) { | ||
| 130 | std::unique_lock lock{cv_mutex}; | ||
| 131 | cv.wait(lock, stop, [&slot] { return slot.turn.test(); }); | ||
| 132 | } | ||
| 133 | v = slot.move(); | ||
| 134 | slot.destroy(); | ||
| 135 | slot.turn.clear(); | ||
| 136 | slot.turn.notify_one(); | ||
| 137 | } | ||
| 138 | |||
| 139 | private: | ||
| 140 | template <typename... Args> | ||
| 141 | void emplace(Args&&... args) noexcept { | ||
| 142 | static_assert(std::is_nothrow_constructible_v<T, Args&&...>, | ||
| 143 | "T must be nothrow constructible with Args&&..."); | ||
| 144 | auto const head = head_.fetch_add(1); | ||
| 145 | auto& slot = slots_[idx(head)]; | ||
| 146 | slot.turn.wait(true); | ||
| 147 | slot.construct(std::forward<Args>(args)...); | ||
| 148 | slot.turn.test_and_set(); | ||
| 149 | cv.notify_one(); | ||
| 150 | } | ||
| 151 | |||
| 152 | constexpr size_t idx(size_t i) const noexcept { | ||
| 153 | return i & mask_; | ||
| 154 | } | ||
| 155 | |||
| 156 | std::conditional_t<true, std::condition_variable_any, std::condition_variable> cv; | ||
| 157 | std::mutex cv_mutex; | ||
| 158 | size_t mask_; | ||
| 159 | Slot<T>* slots_; | ||
| 160 | [[no_unique_address]] Allocator allocator_; | ||
| 161 | |||
| 162 | // Align to avoid false sharing between head_ and tail_ | ||
| 163 | alignas(hardware_interference_size) std::atomic<size_t> head_{0}; | ||
| 164 | alignas(hardware_interference_size) std::atomic<size_t> tail_{0}; | ||
| 165 | |||
| 166 | static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>, | ||
| 167 | "T must be nothrow copy or move assignable"); | ||
| 168 | |||
| 169 | static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); | ||
| 170 | }; | ||
| 171 | } // namespace mpsc | ||
| 172 | |||
| 173 | template <typename T, typename Allocator = mpsc::AlignedAllocator<mpsc::Slot<T>>> | ||
| 174 | using MPSCQueue = mpsc::Queue<T, Allocator>; | ||
| 175 | |||
| 176 | } // namespace Common | ||
| 177 | |||
| 178 | #ifdef _MSC_VER | ||
| 179 | #pragma warning(pop) | ||
| 180 | #endif | ||
diff --git a/src/common/settings.h b/src/common/settings.h index a7bbfb0da..a507744a2 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -496,7 +496,7 @@ struct Values { | |||
| 496 | 496 | ||
| 497 | // Renderer | 497 | // Renderer |
| 498 | RangedSetting<RendererBackend> renderer_backend{ | 498 | RangedSetting<RendererBackend> renderer_backend{ |
| 499 | RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; | 499 | RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; |
| 500 | BasicSetting<bool> renderer_debug{false, "debug"}; | 500 | BasicSetting<bool> renderer_debug{false, "debug"}; |
| 501 | BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; | 501 | BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; |
| 502 | BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; | 502 | BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; |
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index b79a73132..8479dc6d2 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -31,7 +31,8 @@ static void RunThread(std::stop_token stop_token, Core::System& system, | |||
| 31 | VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer(); | 31 | VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer(); |
| 32 | 32 | ||
| 33 | while (!stop_token.stop_requested()) { | 33 | while (!stop_token.stop_requested()) { |
| 34 | CommandDataContainer next = state.queue.PopWait(stop_token); | 34 | CommandDataContainer next; |
| 35 | state.queue.Pop(next, stop_token); | ||
| 35 | if (stop_token.stop_requested()) { | 36 | if (stop_token.stop_requested()) { |
| 36 | break; | 37 | break; |
| 37 | } | 38 | } |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 71cd35756..ad9fd5eff 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include <thread> | 10 | #include <thread> |
| 11 | #include <variant> | 11 | #include <variant> |
| 12 | 12 | ||
| 13 | #include "common/threadsafe_queue.h" | 13 | #include "common/bounded_threadsafe_queue.h" |
| 14 | #include "video_core/framebuffer_config.h" | 14 | #include "video_core/framebuffer_config.h" |
| 15 | 15 | ||
| 16 | namespace Tegra { | 16 | namespace Tegra { |
| @@ -96,9 +96,9 @@ struct CommandDataContainer { | |||
| 96 | 96 | ||
| 97 | /// Struct used to synchronize the GPU thread | 97 | /// Struct used to synchronize the GPU thread |
| 98 | struct SynchState final { | 98 | struct SynchState final { |
| 99 | using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>; | 99 | using CommandQueue = Common::MPSCQueue<CommandDataContainer>; |
| 100 | std::mutex write_lock; | 100 | std::mutex write_lock; |
| 101 | CommandQueue queue; | 101 | CommandQueue queue{512}; // size must be 2^n |
| 102 | u64 last_fence{}; | 102 | u64 last_fence{}; |
| 103 | std::atomic<u64> signaled_fence{}; | 103 | std::atomic<u64> signaled_fence{}; |
| 104 | std::condition_variable_any cv; | 104 | std::condition_variable_any cv; |
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp index a5dd33fb2..4eb3913ee 100644 --- a/src/video_core/vulkan_common/vulkan_library.cpp +++ b/src/video_core/vulkan_common/vulkan_library.cpp | |||
| @@ -5,11 +5,13 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/dynamic_library.h" | 6 | #include "common/dynamic_library.h" |
| 7 | #include "common/fs/path_util.h" | 7 | #include "common/fs/path_util.h" |
| 8 | #include "common/logging/log.h" | ||
| 8 | #include "video_core/vulkan_common/vulkan_library.h" | 9 | #include "video_core/vulkan_common/vulkan_library.h" |
| 9 | 10 | ||
| 10 | namespace Vulkan { | 11 | namespace Vulkan { |
| 11 | 12 | ||
| 12 | Common::DynamicLibrary OpenLibrary() { | 13 | Common::DynamicLibrary OpenLibrary() { |
| 14 | LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library"); | ||
| 13 | Common::DynamicLibrary library; | 15 | Common::DynamicLibrary library; |
| 14 | #ifdef __APPLE__ | 16 | #ifdef __APPLE__ |
| 15 | // Check if a path to a specific Vulkan library has been specified. | 17 | // Check if a path to a specific Vulkan library has been specified. |
| @@ -22,9 +24,11 @@ Common::DynamicLibrary OpenLibrary() { | |||
| 22 | } | 24 | } |
| 23 | #else | 25 | #else |
| 24 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); | 26 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); |
| 27 | LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename); | ||
| 25 | if (!library.Open(filename.c_str())) { | 28 | if (!library.Open(filename.c_str())) { |
| 26 | // Android devices may not have libvulkan.so.1, only libvulkan.so. | 29 | // Android devices may not have libvulkan.so.1, only libvulkan.so. |
| 27 | filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); | 30 | filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); |
| 31 | LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename); | ||
| 28 | void(library.Open(filename.c_str())); | 32 | void(library.Open(filename.c_str())); |
| 29 | } | 33 | } |
| 30 | #endif | 34 | #endif |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 69b46ddd9..242867a4f 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -30,6 +30,8 @@ add_executable(yuzu | |||
| 30 | applets/qt_web_browser_scripts.h | 30 | applets/qt_web_browser_scripts.h |
| 31 | bootmanager.cpp | 31 | bootmanager.cpp |
| 32 | bootmanager.h | 32 | bootmanager.h |
| 33 | check_vulkan.cpp | ||
| 34 | check_vulkan.h | ||
| 33 | compatdb.ui | 35 | compatdb.ui |
| 34 | compatibility_list.cpp | 36 | compatibility_list.cpp |
| 35 | compatibility_list.h | 37 | compatibility_list.h |
diff --git a/src/yuzu/check_vulkan.cpp b/src/yuzu/check_vulkan.cpp new file mode 100644 index 000000000..e6d66ab34 --- /dev/null +++ b/src/yuzu/check_vulkan.cpp | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 5 | |||
| 6 | #include <filesystem> | ||
| 7 | #include <fstream> | ||
| 8 | #include "common/fs/fs.h" | ||
| 9 | #include "common/fs/path_util.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "video_core/vulkan_common/vulkan_instance.h" | ||
| 12 | #include "video_core/vulkan_common/vulkan_library.h" | ||
| 13 | #include "yuzu/check_vulkan.h" | ||
| 14 | #include "yuzu/uisettings.h" | ||
| 15 | |||
| 16 | constexpr char TEMP_FILE_NAME[] = "vulkan_check"; | ||
| 17 | |||
| 18 | bool CheckVulkan() { | ||
| 19 | if (UISettings::values.has_broken_vulkan) { | ||
| 20 | return true; | ||
| 21 | } | ||
| 22 | |||
| 23 | LOG_DEBUG(Frontend, "Checking presence of Vulkan"); | ||
| 24 | |||
| 25 | const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir); | ||
| 26 | const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME; | ||
| 27 | |||
| 28 | if (std::filesystem::exists(temp_file_loc)) { | ||
| 29 | LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization"); | ||
| 30 | |||
| 31 | UISettings::values.has_broken_vulkan = true; | ||
| 32 | std::filesystem::remove(temp_file_loc); | ||
| 33 | return false; | ||
| 34 | } | ||
| 35 | |||
| 36 | std::ofstream temp_file_handle(temp_file_loc); | ||
| 37 | temp_file_handle.close(); | ||
| 38 | |||
| 39 | try { | ||
| 40 | Vulkan::vk::InstanceDispatch dld; | ||
| 41 | const Common::DynamicLibrary library = Vulkan::OpenLibrary(); | ||
| 42 | const Vulkan::vk::Instance instance = | ||
| 43 | Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); | ||
| 44 | |||
| 45 | } catch (const Vulkan::vk::Exception& exception) { | ||
| 46 | LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what()); | ||
| 47 | // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the | ||
| 48 | // application, not when we can handle it. | ||
| 49 | } | ||
| 50 | |||
| 51 | std::filesystem::remove(temp_file_loc); | ||
| 52 | return true; | ||
| 53 | } | ||
diff --git a/src/yuzu/check_vulkan.h b/src/yuzu/check_vulkan.h new file mode 100644 index 000000000..e4ea93582 --- /dev/null +++ b/src/yuzu/check_vulkan.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | bool CheckVulkan(); | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index fffbfab02..9df4752be 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -682,6 +682,12 @@ void Config::ReadRendererValues() { | |||
| 682 | ReadGlobalSetting(Settings::values.bg_green); | 682 | ReadGlobalSetting(Settings::values.bg_green); |
| 683 | ReadGlobalSetting(Settings::values.bg_blue); | 683 | ReadGlobalSetting(Settings::values.bg_blue); |
| 684 | 684 | ||
| 685 | if (!global && UISettings::values.has_broken_vulkan && | ||
| 686 | Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan && | ||
| 687 | !Settings::values.renderer_backend.UsingGlobal()) { | ||
| 688 | Settings::values.renderer_backend.SetGlobal(true); | ||
| 689 | } | ||
| 690 | |||
| 685 | if (global) { | 691 | if (global) { |
| 686 | ReadBasicSetting(Settings::values.renderer_debug); | 692 | ReadBasicSetting(Settings::values.renderer_debug); |
| 687 | ReadBasicSetting(Settings::values.renderer_shader_feedback); | 693 | ReadBasicSetting(Settings::values.renderer_shader_feedback); |
| @@ -801,6 +807,7 @@ void Config::ReadUIValues() { | |||
| 801 | ReadBasicSetting(UISettings::values.pause_when_in_background); | 807 | ReadBasicSetting(UISettings::values.pause_when_in_background); |
| 802 | ReadBasicSetting(UISettings::values.mute_when_in_background); | 808 | ReadBasicSetting(UISettings::values.mute_when_in_background); |
| 803 | ReadBasicSetting(UISettings::values.hide_mouse); | 809 | ReadBasicSetting(UISettings::values.hide_mouse); |
| 810 | ReadBasicSetting(UISettings::values.has_broken_vulkan); | ||
| 804 | ReadBasicSetting(UISettings::values.disable_web_applet); | 811 | ReadBasicSetting(UISettings::values.disable_web_applet); |
| 805 | 812 | ||
| 806 | qt_config->endGroup(); | 813 | qt_config->endGroup(); |
| @@ -1348,6 +1355,7 @@ void Config::SaveUIValues() { | |||
| 1348 | WriteBasicSetting(UISettings::values.pause_when_in_background); | 1355 | WriteBasicSetting(UISettings::values.pause_when_in_background); |
| 1349 | WriteBasicSetting(UISettings::values.mute_when_in_background); | 1356 | WriteBasicSetting(UISettings::values.mute_when_in_background); |
| 1350 | WriteBasicSetting(UISettings::values.hide_mouse); | 1357 | WriteBasicSetting(UISettings::values.hide_mouse); |
| 1358 | WriteBasicSetting(UISettings::values.has_broken_vulkan); | ||
| 1351 | WriteBasicSetting(UISettings::values.disable_web_applet); | 1359 | WriteBasicSetting(UISettings::values.disable_web_applet); |
| 1352 | 1360 | ||
| 1353 | qt_config->endGroup(); | 1361 | qt_config->endGroup(); |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 2f1435b10..85f34dc35 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "video_core/vulkan_common/vulkan_library.h" | 17 | #include "video_core/vulkan_common/vulkan_library.h" |
| 18 | #include "yuzu/configuration/configuration_shared.h" | 18 | #include "yuzu/configuration/configuration_shared.h" |
| 19 | #include "yuzu/configuration/configure_graphics.h" | 19 | #include "yuzu/configuration/configure_graphics.h" |
| 20 | #include "yuzu/uisettings.h" | ||
| 20 | 21 | ||
| 21 | ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) | 22 | ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) |
| 22 | : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { | 23 | : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { |
| @@ -57,6 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren | |||
| 57 | UpdateBackgroundColorButton(new_bg_color); | 58 | UpdateBackgroundColorButton(new_bg_color); |
| 58 | }); | 59 | }); |
| 59 | 60 | ||
| 61 | connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] { | ||
| 62 | UISettings::values.has_broken_vulkan = false; | ||
| 63 | |||
| 64 | if (RetrieveVulkanDevices()) { | ||
| 65 | ui->api->setEnabled(true); | ||
| 66 | ui->button_check_vulkan->hide(); | ||
| 67 | |||
| 68 | for (const auto& device : vulkan_devices) { | ||
| 69 | ui->device->addItem(device); | ||
| 70 | } | ||
| 71 | } else { | ||
| 72 | UISettings::values.has_broken_vulkan = true; | ||
| 73 | } | ||
| 74 | }); | ||
| 75 | |||
| 76 | ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue()); | ||
| 77 | ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue()); | ||
| 78 | |||
| 60 | ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); | 79 | ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); |
| 61 | ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); | 80 | ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); |
| 62 | } | 81 | } |
| @@ -296,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() { | |||
| 296 | vulkan_device = Settings::values.vulkan_device.GetValue(true); | 315 | vulkan_device = Settings::values.vulkan_device.GetValue(true); |
| 297 | shader_backend = Settings::values.shader_backend.GetValue(true); | 316 | shader_backend = Settings::values.shader_backend.GetValue(true); |
| 298 | ui->device_widget->setEnabled(false); | 317 | ui->device_widget->setEnabled(false); |
| 299 | ui->backend_widget->setEnabled(false); | 318 | ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue()); |
| 300 | } else { | 319 | } else { |
| 301 | vulkan_device = Settings::values.vulkan_device.GetValue(); | 320 | vulkan_device = Settings::values.vulkan_device.GetValue(); |
| 302 | shader_backend = Settings::values.shader_backend.GetValue(); | 321 | shader_backend = Settings::values.shader_backend.GetValue(); |
| @@ -318,7 +337,11 @@ void ConfigureGraphics::UpdateAPILayout() { | |||
| 318 | } | 337 | } |
| 319 | } | 338 | } |
| 320 | 339 | ||
| 321 | void ConfigureGraphics::RetrieveVulkanDevices() try { | 340 | bool ConfigureGraphics::RetrieveVulkanDevices() try { |
| 341 | if (UISettings::values.has_broken_vulkan) { | ||
| 342 | return false; | ||
| 343 | } | ||
| 344 | |||
| 322 | using namespace Vulkan; | 345 | using namespace Vulkan; |
| 323 | 346 | ||
| 324 | vk::InstanceDispatch dld; | 347 | vk::InstanceDispatch dld; |
| @@ -333,8 +356,10 @@ void ConfigureGraphics::RetrieveVulkanDevices() try { | |||
| 333 | vulkan_devices.push_back(QString::fromStdString(name)); | 356 | vulkan_devices.push_back(QString::fromStdString(name)); |
| 334 | } | 357 | } |
| 335 | 358 | ||
| 359 | return true; | ||
| 336 | } catch (const Vulkan::vk::Exception& exception) { | 360 | } catch (const Vulkan::vk::Exception& exception) { |
| 337 | LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); | 361 | LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); |
| 362 | return false; | ||
| 338 | } | 363 | } |
| 339 | 364 | ||
| 340 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | 365 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { |
| @@ -415,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
| 415 | ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); | 440 | ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); |
| 416 | ConfigurationShared::InsertGlobalItem( | 441 | ConfigurationShared::InsertGlobalItem( |
| 417 | ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); | 442 | ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); |
| 443 | |||
| 444 | if (UISettings::values.has_broken_vulkan) { | ||
| 445 | ui->backend_widget->setEnabled(true); | ||
| 446 | ConfigurationShared::SetColoredComboBox( | ||
| 447 | ui->backend, ui->backend_widget, | ||
| 448 | static_cast<int>(Settings::values.shader_backend.GetValue(true))); | ||
| 449 | } | ||
| 418 | } | 450 | } |
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 1b101c940..8438f0187 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h | |||
| @@ -41,7 +41,7 @@ private: | |||
| 41 | void UpdateDeviceSelection(int device); | 41 | void UpdateDeviceSelection(int device); |
| 42 | void UpdateShaderBackendSelection(int backend); | 42 | void UpdateShaderBackendSelection(int backend); |
| 43 | 43 | ||
| 44 | void RetrieveVulkanDevices(); | 44 | bool RetrieveVulkanDevices(); |
| 45 | 45 | ||
| 46 | void SetupPerGameUI(); | 46 | void SetupPerGameUI(); |
| 47 | 47 | ||
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 74f0e0b79..2f94c94bc 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | <rect> | 6 | <rect> |
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>437</width> | 9 | <width>471</width> |
| 10 | <height>482</height> | 10 | <height>759</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -171,11 +171,11 @@ | |||
| 171 | </widget> | 171 | </widget> |
| 172 | </item> | 172 | </item> |
| 173 | <item> | 173 | <item> |
| 174 | <widget class="QCheckBox" name="accelerate_astc"> | 174 | <widget class="QCheckBox" name="accelerate_astc"> |
| 175 | <property name="text"> | 175 | <property name="text"> |
| 176 | <string>Accelerate ASTC texture decoding</string> | 176 | <string>Accelerate ASTC texture decoding</string> |
| 177 | </property> | 177 | </property> |
| 178 | </widget> | 178 | </widget> |
| 179 | </item> | 179 | </item> |
| 180 | <item> | 180 | <item> |
| 181 | <widget class="QWidget" name="nvdec_emulation_widget" native="true"> | 181 | <widget class="QWidget" name="nvdec_emulation_widget" native="true"> |
| @@ -438,43 +438,43 @@ | |||
| 438 | </widget> | 438 | </widget> |
| 439 | </item> | 439 | </item> |
| 440 | <item> | 440 | <item> |
| 441 | <widget class="QWidget" name="anti_aliasing_layout" native="true"> | 441 | <widget class="QWidget" name="anti_aliasing_layout" native="true"> |
| 442 | <layout class="QHBoxLayout" name="horizontalLayout_7"> | 442 | <layout class="QHBoxLayout" name="horizontalLayout_7"> |
| 443 | <property name="leftMargin"> | 443 | <property name="leftMargin"> |
| 444 | <number>0</number> | 444 | <number>0</number> |
| 445 | </property> | 445 | </property> |
| 446 | <property name="topMargin"> | 446 | <property name="topMargin"> |
| 447 | <number>0</number> | 447 | <number>0</number> |
| 448 | </property> | 448 | </property> |
| 449 | <property name="rightMargin"> | 449 | <property name="rightMargin"> |
| 450 | <number>0</number> | 450 | <number>0</number> |
| 451 | </property> | ||
| 452 | <property name="bottomMargin"> | ||
| 453 | <number>0</number> | ||
| 454 | </property> | ||
| 455 | <item> | ||
| 456 | <widget class="QLabel" name="anti_aliasing_label"> | ||
| 457 | <property name="text"> | ||
| 458 | <string>Anti-Aliasing Method:</string> | ||
| 459 | </property> | ||
| 460 | </widget> | ||
| 461 | </item> | ||
| 462 | <item> | ||
| 463 | <widget class="QComboBox" name="anti_aliasing_combobox"> | ||
| 464 | <item> | ||
| 465 | <property name="text"> | ||
| 466 | <string>None</string> | ||
| 451 | </property> | 467 | </property> |
| 452 | <property name="bottomMargin"> | 468 | </item> |
| 453 | <number>0</number> | 469 | <item> |
| 470 | <property name="text"> | ||
| 471 | <string>FXAA</string> | ||
| 454 | </property> | 472 | </property> |
| 455 | <item> | 473 | </item> |
| 456 | <widget class="QLabel" name="anti_aliasing_label"> | 474 | </widget> |
| 457 | <property name="text"> | 475 | </item> |
| 458 | <string>Anti-Aliasing Method:</string> | 476 | </layout> |
| 459 | </property> | 477 | </widget> |
| 460 | </widget> | ||
| 461 | </item> | ||
| 462 | <item> | ||
| 463 | <widget class="QComboBox" name="anti_aliasing_combobox"> | ||
| 464 | <item> | ||
| 465 | <property name="text"> | ||
| 466 | <string>None</string> | ||
| 467 | </property> | ||
| 468 | </item> | ||
| 469 | <item> | ||
| 470 | <property name="text"> | ||
| 471 | <string>FXAA</string> | ||
| 472 | </property> | ||
| 473 | </item> | ||
| 474 | </widget> | ||
| 475 | </item> | ||
| 476 | </layout> | ||
| 477 | </widget> | ||
| 478 | </item> | 478 | </item> |
| 479 | <item> | 479 | <item> |
| 480 | <widget class="QWidget" name="bg_layout" native="true"> | 480 | <widget class="QWidget" name="bg_layout" native="true"> |
| @@ -574,6 +574,13 @@ | |||
| 574 | </property> | 574 | </property> |
| 575 | </spacer> | 575 | </spacer> |
| 576 | </item> | 576 | </item> |
| 577 | <item> | ||
| 578 | <widget class="QPushButton" name="button_check_vulkan"> | ||
| 579 | <property name="text"> | ||
| 580 | <string>Check for Working Vulkan</string> | ||
| 581 | </property> | ||
| 582 | </widget> | ||
| 583 | </item> | ||
| 577 | </layout> | 584 | </layout> |
| 578 | </widget> | 585 | </widget> |
| 579 | <resources/> | 586 | <resources/> |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index d13530a5b..6321afc83 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -870,7 +870,7 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} | |||
| 870 | layout->setAlignment(Qt::AlignCenter); | 870 | layout->setAlignment(Qt::AlignCenter); |
| 871 | image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); | 871 | image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); |
| 872 | 872 | ||
| 873 | text->setText(tr("Double-click to add a new folder to the game list")); | 873 | RetranslateUI(); |
| 874 | QFont font = text->font(); | 874 | QFont font = text->font(); |
| 875 | font.setPointSize(20); | 875 | font.setPointSize(20); |
| 876 | text->setFont(font); | 876 | text->setFont(font); |
| @@ -891,3 +891,15 @@ void GameListPlaceholder::onUpdateThemedIcons() { | |||
| 891 | void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { | 891 | void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { |
| 892 | emit GameListPlaceholder::AddDirectory(); | 892 | emit GameListPlaceholder::AddDirectory(); |
| 893 | } | 893 | } |
| 894 | |||
| 895 | void GameListPlaceholder::changeEvent(QEvent* event) { | ||
| 896 | if (event->type() == QEvent::LanguageChange) { | ||
| 897 | RetranslateUI(); | ||
| 898 | } | ||
| 899 | |||
| 900 | QWidget::changeEvent(event); | ||
| 901 | } | ||
| 902 | |||
| 903 | void GameListPlaceholder::RetranslateUI() { | ||
| 904 | text->setText(tr("Double-click to add a new folder to the game list")); | ||
| 905 | } | ||
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index d19dbe4b0..464da98ad 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -166,6 +166,9 @@ protected: | |||
| 166 | void mouseDoubleClickEvent(QMouseEvent* event) override; | 166 | void mouseDoubleClickEvent(QMouseEvent* event) override; |
| 167 | 167 | ||
| 168 | private: | 168 | private: |
| 169 | void changeEvent(QEvent* event) override; | ||
| 170 | void RetranslateUI(); | ||
| 171 | |||
| 169 | QVBoxLayout* layout = nullptr; | 172 | QVBoxLayout* layout = nullptr; |
| 170 | QLabel* image = nullptr; | 173 | QLabel* image = nullptr; |
| 171 | QLabel* text = nullptr; | 174 | QLabel* text = nullptr; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c57bac2a7..27f23bcb0 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -52,7 +52,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 52 | #define QT_NO_OPENGL | 52 | #define QT_NO_OPENGL |
| 53 | #include <QClipboard> | 53 | #include <QClipboard> |
| 54 | #include <QDesktopServices> | 54 | #include <QDesktopServices> |
| 55 | #include <QDesktopWidget> | ||
| 56 | #include <QFile> | 55 | #include <QFile> |
| 57 | #include <QFileDialog> | 56 | #include <QFileDialog> |
| 58 | #include <QInputDialog> | 57 | #include <QInputDialog> |
| @@ -60,6 +59,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 60 | #include <QProgressBar> | 59 | #include <QProgressBar> |
| 61 | #include <QProgressDialog> | 60 | #include <QProgressDialog> |
| 62 | #include <QPushButton> | 61 | #include <QPushButton> |
| 62 | #include <QScreen> | ||
| 63 | #include <QShortcut> | 63 | #include <QShortcut> |
| 64 | #include <QStatusBar> | 64 | #include <QStatusBar> |
| 65 | #include <QString> | 65 | #include <QString> |
| @@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 115 | #include "video_core/shader_notify.h" | 115 | #include "video_core/shader_notify.h" |
| 116 | #include "yuzu/about_dialog.h" | 116 | #include "yuzu/about_dialog.h" |
| 117 | #include "yuzu/bootmanager.h" | 117 | #include "yuzu/bootmanager.h" |
| 118 | #include "yuzu/check_vulkan.h" | ||
| 118 | #include "yuzu/compatdb.h" | 119 | #include "yuzu/compatdb.h" |
| 119 | #include "yuzu/compatibility_list.h" | 120 | #include "yuzu/compatibility_list.h" |
| 120 | #include "yuzu/configuration/config.h" | 121 | #include "yuzu/configuration/config.h" |
| @@ -351,6 +352,23 @@ GMainWindow::GMainWindow() | |||
| 351 | 352 | ||
| 352 | MigrateConfigFiles(); | 353 | MigrateConfigFiles(); |
| 353 | 354 | ||
| 355 | if (!CheckVulkan()) { | ||
| 356 | config->Save(); | ||
| 357 | |||
| 358 | QMessageBox::warning( | ||
| 359 | this, tr("Broken Vulkan Installation Detected"), | ||
| 360 | tr("Vulkan initialization failed on the previous boot.<br><br>Click <a " | ||
| 361 | "href='https://yuzu-emu.org/wiki/faq/" | ||
| 362 | "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for " | ||
| 363 | "instructions to fix the issue</a>.")); | ||
| 364 | } | ||
| 365 | if (UISettings::values.has_broken_vulkan) { | ||
| 366 | Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; | ||
| 367 | |||
| 368 | renderer_status_button->setDisabled(true); | ||
| 369 | renderer_status_button->setChecked(false); | ||
| 370 | } | ||
| 371 | |||
| 354 | #if defined(HAVE_SDL2) && !defined(_WIN32) | 372 | #if defined(HAVE_SDL2) && !defined(_WIN32) |
| 355 | SDL_InitSubSystem(SDL_INIT_VIDEO); | 373 | SDL_InitSubSystem(SDL_INIT_VIDEO); |
| 356 | // SDL disables the screen saver by default, and setting the hint | 374 | // SDL disables the screen saver by default, and setting the hint |
| @@ -1055,7 +1073,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1055 | 1073 | ||
| 1056 | void GMainWindow::SetDefaultUIGeometry() { | 1074 | void GMainWindow::SetDefaultUIGeometry() { |
| 1057 | // geometry: 53% of the window contents are in the upper screen half, 47% in the lower half | 1075 | // geometry: 53% of the window contents are in the upper screen half, 47% in the lower half |
| 1058 | const QRect screenRect = QApplication::desktop()->screenGeometry(this); | 1076 | const QRect screenRect = QGuiApplication::primaryScreen()->geometry(); |
| 1059 | 1077 | ||
| 1060 | const int w = screenRect.width() * 2 / 3; | 1078 | const int w = screenRect.width() * 2 / 3; |
| 1061 | const int h = screenRect.height() * 2 / 3; | 1079 | const int h = screenRect.height() * 2 / 3; |
| @@ -1620,7 +1638,7 @@ void GMainWindow::ShutdownGame() { | |||
| 1620 | emu_speed_label->setVisible(false); | 1638 | emu_speed_label->setVisible(false); |
| 1621 | game_fps_label->setVisible(false); | 1639 | game_fps_label->setVisible(false); |
| 1622 | emu_frametime_label->setVisible(false); | 1640 | emu_frametime_label->setVisible(false); |
| 1623 | renderer_status_button->setEnabled(true); | 1641 | renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); |
| 1624 | 1642 | ||
| 1625 | game_path.clear(); | 1643 | game_path.clear(); |
| 1626 | 1644 | ||
| @@ -2638,6 +2656,18 @@ void GMainWindow::ToggleFullscreen() { | |||
| 2638 | } | 2656 | } |
| 2639 | } | 2657 | } |
| 2640 | 2658 | ||
| 2659 | // We're going to return the screen that the given window has the most pixels on | ||
| 2660 | static QScreen* GuessCurrentScreen(QWidget* window) { | ||
| 2661 | const QList<QScreen*> screens = QGuiApplication::screens(); | ||
| 2662 | return *std::max_element( | ||
| 2663 | screens.cbegin(), screens.cend(), [window](const QScreen* left, const QScreen* right) { | ||
| 2664 | const QSize left_size = left->geometry().intersected(window->geometry()).size(); | ||
| 2665 | const QSize right_size = right->geometry().intersected(window->geometry()).size(); | ||
| 2666 | return (left_size.height() * left_size.width()) < | ||
| 2667 | (right_size.height() * right_size.width()); | ||
| 2668 | }); | ||
| 2669 | } | ||
| 2670 | |||
| 2641 | void GMainWindow::ShowFullscreen() { | 2671 | void GMainWindow::ShowFullscreen() { |
| 2642 | const auto show_fullscreen = [](QWidget* window) { | 2672 | const auto show_fullscreen = [](QWidget* window) { |
| 2643 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { | 2673 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { |
| @@ -2646,7 +2676,7 @@ void GMainWindow::ShowFullscreen() { | |||
| 2646 | } | 2676 | } |
| 2647 | window->hide(); | 2677 | window->hide(); |
| 2648 | window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint); | 2678 | window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint); |
| 2649 | const auto screen_geometry = QApplication::desktop()->screenGeometry(window); | 2679 | const auto screen_geometry = GuessCurrentScreen(window)->geometry(); |
| 2650 | window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), | 2680 | window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), |
| 2651 | screen_geometry.height() + 1); | 2681 | screen_geometry.height() + 1); |
| 2652 | window->raise(); | 2682 | window->raise(); |
| @@ -2830,6 +2860,10 @@ void GMainWindow::OnConfigure() { | |||
| 2830 | mouse_hide_timer.start(); | 2860 | mouse_hide_timer.start(); |
| 2831 | } | 2861 | } |
| 2832 | 2862 | ||
| 2863 | if (!UISettings::values.has_broken_vulkan) { | ||
| 2864 | renderer_status_button->setEnabled(!emulation_running); | ||
| 2865 | } | ||
| 2866 | |||
| 2833 | UpdateStatusButtons(); | 2867 | UpdateStatusButtons(); |
| 2834 | controller_dialog->refreshConfiguration(); | 2868 | controller_dialog->refreshConfiguration(); |
| 2835 | } | 2869 | } |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 15ba9ea17..c64d87ace 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -77,6 +77,8 @@ struct Values { | |||
| 77 | Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; | 77 | Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; |
| 78 | Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; | 78 | Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; |
| 79 | Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; | 79 | Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; |
| 80 | // Set when Vulkan is known to crash the application | ||
| 81 | Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"}; | ||
| 80 | 82 | ||
| 81 | Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; | 83 | Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; |
| 82 | 84 | ||
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index f34d6b728..39063e32b 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -218,7 +218,7 @@ cpuopt_unsafe_ignore_global_monitor = | |||
| 218 | 218 | ||
| 219 | [Renderer] | 219 | [Renderer] |
| 220 | # Which backend API to use. | 220 | # Which backend API to use. |
| 221 | # 0 (default): OpenGL, 1: Vulkan | 221 | # 0: OpenGL, 1 (default): Vulkan |
| 222 | backend = | 222 | backend = |
| 223 | 223 | ||
| 224 | # Enable graphics API debugging mode. | 224 | # Enable graphics API debugging mode. |