summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/command_generator.cpp4
-rw-r--r--src/audio_core/effect_context.cpp4
-rw-r--r--src/audio_core/splitter_context.cpp2
-rw-r--r--src/audio_core/voice_context.cpp4
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/assert.cpp7
-rw-r--r--src/common/assert.h55
-rw-r--r--src/common/bounded_threadsafe_queue.h180
-rw-r--r--src/common/elf.h333
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h2
-rw-r--r--src/core/arm/symbols.cpp85
-rw-r--r--src/core/core.cpp6
-rw-r--r--src/core/core.h3
-rw-r--r--src/core/cpu_manager.cpp116
-rw-r--r--src/core/cpu_manager.h11
-rw-r--r--src/core/crypto/key_manager.cpp2
-rw-r--r--src/core/debugger/debugger.cpp76
-rw-r--r--src/core/debugger/debugger.h5
-rw-r--r--src/core/debugger/debugger_interface.h5
-rw-r--r--src/core/debugger/gdbstub.cpp52
-rw-r--r--src/core/debugger/gdbstub.h1
-rw-r--r--src/core/debugger/gdbstub_arch.h1
-rw-r--r--src/core/file_sys/content_archive.cpp2
-rw-r--r--src/core/file_sys/nca_patch.cpp2
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/file_sys/vfs_real.cpp2
-rw-r--r--src/core/frontend/applets/controller.cpp2
-rw-r--r--src/core/hid/hid_core.cpp4
-rw-r--r--src/core/hle/kernel/hle_ipc.h2
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_address_arbiter.h4
-rw-r--r--src/core/hle/kernel/k_address_space_info.cpp4
-rw-r--r--src/core/hle/kernel/k_auto_object.h11
-rw-r--r--src/core/hle/kernel/k_class_token.h1
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp28
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp2
-rw-r--r--src/core/hle/kernel/k_page_table.cpp127
-rw-r--r--src/core/hle/kernel/k_page_table.h5
-rw-r--r--src/core/hle/kernel/k_port.cpp2
-rw-r--r--src/core/hle/kernel/k_process.cpp2
-rw-r--r--src/core/hle/kernel/k_server_session.cpp4
-rw-r--r--src/core/hle/kernel/k_thread.cpp2
-rw-r--r--src/core/hle/kernel/kernel.cpp8
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.cpp4
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp4
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp8
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/jit/jit_context.cpp32
-rw-r--r--src/core/hle/service/ldr/ldr.cpp2
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp2
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp29
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp4
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_slot.h1
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp4
-rw-r--r--src/core/hle/service/time/time_manager.cpp10
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp2
-rw-r--r--src/core/loader/elf.cpp183
-rw-r--r--src/core/loader/nso.cpp9
-rw-r--r--src/core/tools/freezer.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp6
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp2
-rw-r--r--src/video_core/command_classes/vic.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.h12
-rw-r--r--src/video_core/engines/maxwell_dma.cpp4
-rw-r--r--src/video_core/gpu_thread.cpp5
-rw-r--r--src/video_core/gpu_thread.h6
-rw-r--r--src/video_core/macro/macro.cpp2
-rw-r--r--src/video_core/macro/macro_interpreter.cpp1
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp2
-rw-r--r--src/video_core/memory_manager.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp38
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h12
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp4
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp2
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp24
-rw-r--r--src/video_core/shader_environment.cpp2
-rw-r--r--src/video_core/surface.cpp6
-rw-r--r--src/video_core/texture_cache/image_info.cpp2
-rw-r--r--src/video_core/texture_cache/image_view_info.cpp2
-rw-r--r--src/video_core/texture_cache/samples_helper.h4
-rw-r--r--src/video_core/texture_cache/texture_cache.h20
-rw-r--r--src/video_core/textures/decoders.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_library.cpp4
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp6
-rw-r--r--src/yuzu/CMakeLists.txt21
-rw-r--r--src/yuzu/applets/qt_controller.cpp2
-rw-r--r--src/yuzu/bootmanager.cpp2
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/check_vulkan.cpp53
-rw-r--r--src/yuzu/check_vulkan.h6
-rw-r--r--src/yuzu/configuration/config.cpp52
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp36
-rw-r--r--src/yuzu/configuration/configure_graphics.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui91
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp22
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp2
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui19
-rw-r--r--src/yuzu/configuration/configure_system.cpp6
-rw-r--r--src/yuzu/game_list.cpp16
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/loading_screen.cpp2
-rw-r--r--src/yuzu/loading_screen.h3
-rw-r--r--src/yuzu/main.cpp118
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/uisettings.h2
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp10
-rw-r--r--src/yuzu_cmd/yuzu.cpp9
130 files changed, 1416 insertions, 802 deletions
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index ff20ed00f..f97520820 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -429,7 +429,7 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo
429 in_params.node_id); 429 in_params.node_id);
430 break; 430 break;
431 default: 431 default:
432 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); 432 ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
433 } 433 }
434 } 434 }
435} 435}
@@ -1312,7 +1312,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::s
1312 samples_to_read - samples_read, channel, temp_mix_offset); 1312 samples_to_read - samples_read, channel, temp_mix_offset);
1313 break; 1313 break;
1314 default: 1314 default:
1315 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); 1315 ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
1316 } 1316 }
1317 1317
1318 temp_mix_offset += samples_decoded; 1318 temp_mix_offset += samples_decoded;
diff --git a/src/audio_core/effect_context.cpp b/src/audio_core/effect_context.cpp
index 51059580e..79bcd1192 100644
--- a/src/audio_core/effect_context.cpp
+++ b/src/audio_core/effect_context.cpp
@@ -50,7 +50,7 @@ EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
50 effects[i] = std::make_unique<EffectBiquadFilter>(); 50 effects[i] = std::make_unique<EffectBiquadFilter>();
51 break; 51 break;
52 default: 52 default:
53 UNREACHABLE_MSG("Unimplemented effect {}", effect); 53 ASSERT_MSG(false, "Unimplemented effect {}", effect);
54 effects[i] = std::make_unique<EffectStubbed>(); 54 effects[i] = std::make_unique<EffectStubbed>();
55 } 55 }
56 return GetInfo(i); 56 return GetInfo(i);
@@ -104,7 +104,7 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
104 auto& params = GetParams(); 104 auto& params = GetParams();
105 const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data()); 105 const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
106 if (!ValidChannelCountForEffect(reverb_params->max_channels)) { 106 if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
107 UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels); 107 ASSERT_MSG(false, "Invalid reverb max channel count {}", reverb_params->max_channels);
108 return; 108 return;
109 } 109 }
110 110
diff --git a/src/audio_core/splitter_context.cpp b/src/audio_core/splitter_context.cpp
index 1751d0212..10646dc05 100644
--- a/src/audio_core/splitter_context.cpp
+++ b/src/audio_core/splitter_context.cpp
@@ -483,7 +483,7 @@ bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
483 // Add more work 483 // Add more work
484 index_stack.push(j); 484 index_stack.push(j);
485 } else if (node_state == NodeStates::State::InFound) { 485 } else if (node_state == NodeStates::State::InFound) {
486 UNREACHABLE_MSG("Node start marked as found"); 486 ASSERT_MSG(false, "Node start marked as found");
487 ResetState(); 487 ResetState();
488 return false; 488 return false;
489 } 489 }
diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp
index c8e4a6caf..f58a5c754 100644
--- a/src/audio_core/voice_context.cpp
+++ b/src/audio_core/voice_context.cpp
@@ -114,7 +114,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
114 in_params.current_playstate = ServerPlayState::Play; 114 in_params.current_playstate = ServerPlayState::Play;
115 break; 115 break;
116 default: 116 default:
117 UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state); 117 ASSERT_MSG(false, "Unknown playstate {}", voice_in.play_state);
118 break; 118 break;
119 } 119 }
120 120
@@ -410,7 +410,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
410 return in_params.should_depop; 410 return in_params.should_depop;
411 } 411 }
412 default: 412 default:
413 UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate); 413 ASSERT_MSG(false, "Invalid playstate {}", in_params.current_playstate);
414 } 414 }
415 415
416 return false; 416 return false;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index adf70eb8b..73bf626d4 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -58,6 +58,7 @@ add_library(common STATIC
58 div_ceil.h 58 div_ceil.h
59 dynamic_library.cpp 59 dynamic_library.cpp
60 dynamic_library.h 60 dynamic_library.h
61 elf.h
61 error.cpp 62 error.cpp
62 error.h 63 error.h
63 expected.h 64 expected.h
diff --git a/src/common/assert.cpp b/src/common/assert.cpp
index b44570528..6026b7dc2 100644
--- a/src/common/assert.cpp
+++ b/src/common/assert.cpp
@@ -6,8 +6,13 @@
6 6
7#include "common/settings.h" 7#include "common/settings.h"
8 8
9void assert_handle_failure() { 9void assert_fail_impl() {
10 if (Settings::values.use_debug_asserts) { 10 if (Settings::values.use_debug_asserts) {
11 Crash(); 11 Crash();
12 } 12 }
13} 13}
14
15[[noreturn]] void unreachable_impl() {
16 Crash();
17 throw std::runtime_error("Unreachable code");
18}
diff --git a/src/common/assert.h b/src/common/assert.h
index dbfd8abaf..8c927fcc0 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -9,44 +9,43 @@
9// Sometimes we want to try to continue even after hitting an assert. 9// Sometimes we want to try to continue even after hitting an assert.
10// However touching this file yields a global recompilation as this header is included almost 10// However touching this file yields a global recompilation as this header is included almost
11// everywhere. So let's just move the handling of the failed assert to a single cpp file. 11// everywhere. So let's just move the handling of the failed assert to a single cpp file.
12void assert_handle_failure();
13 12
14// For asserts we'd like to keep all the junk executed when an assert happens away from the 13void assert_fail_impl();
15// important code in the function. One way of doing this is to put all the relevant code inside a 14[[noreturn]] void unreachable_impl();
16// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to 15
17// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper 16#ifdef _MSC_VER
18// template that calls the lambda. This seems to generate an extra instruction at the call-site 17#define YUZU_NO_INLINE __declspec(noinline)
19// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good 18#else
20// enough for our purposes. 19#define YUZU_NO_INLINE __attribute__((noinline))
21template <typename Fn>
22#if defined(_MSC_VER)
23[[msvc::noinline]]
24#elif defined(__GNUC__)
25[[gnu::cold, gnu::noinline]]
26#endif 20#endif
27static void
28assert_noinline_call(const Fn& fn) {
29 fn();
30 assert_handle_failure();
31}
32 21
33#define ASSERT(_a_) \ 22#define ASSERT(_a_) \
34 do \ 23 ([&]() YUZU_NO_INLINE { \
35 if (!(_a_)) { \ 24 if (!(_a_)) [[unlikely]] { \
36 assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \ 25 LOG_CRITICAL(Debug, "Assertion Failed!"); \
26 assert_fail_impl(); \
37 } \ 27 } \
38 while (0) 28 }())
39 29
40#define ASSERT_MSG(_a_, ...) \ 30#define ASSERT_MSG(_a_, ...) \
41 do \ 31 ([&]() YUZU_NO_INLINE { \
42 if (!(_a_)) { \ 32 if (!(_a_)) [[unlikely]] { \
43 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ 33 LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
34 assert_fail_impl(); \
44 } \ 35 } \
45 while (0) 36 }())
37
38#define UNREACHABLE() \
39 do { \
40 LOG_CRITICAL(Debug, "Unreachable code!"); \
41 unreachable_impl(); \
42 } while (0)
46 43
47#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
48#define UNREACHABLE_MSG(...) \ 44#define UNREACHABLE_MSG(...) \
49 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }) 45 do { \
46 LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
47 unreachable_impl(); \
48 } while (0)
50 49
51#ifdef _DEBUG 50#ifdef _DEBUG
52#define DEBUG_ASSERT(_a_) ASSERT(_a_) 51#define DEBUG_ASSERT(_a_) ASSERT(_a_)
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
20namespace Common {
21namespace mpsc {
22#if defined(__cpp_lib_hardware_interference_size)
23constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
24#else
25constexpr size_t hardware_interference_size = 64;
26#endif
27
28template <typename T>
29using AlignedAllocator = std::allocator<T>;
30
31template <typename T>
32struct 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
65template <typename T, typename Allocator = AlignedAllocator<Slot<T>>>
66class Queue {
67public:
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
139private:
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
173template <typename T, typename Allocator = mpsc::AlignedAllocator<mpsc::Slot<T>>>
174using 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/elf.h b/src/common/elf.h
new file mode 100644
index 000000000..14a5e9597
--- /dev/null
+++ b/src/common/elf.h
@@ -0,0 +1,333 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cstddef>
8
9#include "common_types.h"
10
11namespace Common {
12namespace ELF {
13
14/* Type for a 16-bit quantity. */
15using Elf32_Half = u16;
16using Elf64_Half = u16;
17
18/* Types for signed and unsigned 32-bit quantities. */
19using Elf32_Word = u32;
20using Elf32_Sword = s32;
21using Elf64_Word = u32;
22using Elf64_Sword = s32;
23
24/* Types for signed and unsigned 64-bit quantities. */
25using Elf32_Xword = u64;
26using Elf32_Sxword = s64;
27using Elf64_Xword = u64;
28using Elf64_Sxword = s64;
29
30/* Type of addresses. */
31using Elf32_Addr = u32;
32using Elf64_Addr = u64;
33
34/* Type of file offsets. */
35using Elf32_Off = u32;
36using Elf64_Off = u64;
37
38/* Type for section indices, which are 16-bit quantities. */
39using Elf32_Section = u16;
40using Elf64_Section = u16;
41
42/* Type for version symbol information. */
43using Elf32_Versym = Elf32_Half;
44using Elf64_Versym = Elf64_Half;
45
46constexpr size_t ElfIdentSize = 16;
47
48/* The ELF file header. This appears at the start of every ELF file. */
49
50struct Elf32_Ehdr {
51 std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
52 Elf32_Half e_type; /* Object file type */
53 Elf32_Half e_machine; /* Architecture */
54 Elf32_Word e_version; /* Object file version */
55 Elf32_Addr e_entry; /* Entry point virtual address */
56 Elf32_Off e_phoff; /* Program header table file offset */
57 Elf32_Off e_shoff; /* Section header table file offset */
58 Elf32_Word e_flags; /* Processor-specific flags */
59 Elf32_Half e_ehsize; /* ELF header size in bytes */
60 Elf32_Half e_phentsize; /* Program header table entry size */
61 Elf32_Half e_phnum; /* Program header table entry count */
62 Elf32_Half e_shentsize; /* Section header table entry size */
63 Elf32_Half e_shnum; /* Section header table entry count */
64 Elf32_Half e_shstrndx; /* Section header string table index */
65};
66
67struct Elf64_Ehdr {
68 std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
69 Elf64_Half e_type; /* Object file type */
70 Elf64_Half e_machine; /* Architecture */
71 Elf64_Word e_version; /* Object file version */
72 Elf64_Addr e_entry; /* Entry point virtual address */
73 Elf64_Off e_phoff; /* Program header table file offset */
74 Elf64_Off e_shoff; /* Section header table file offset */
75 Elf64_Word e_flags; /* Processor-specific flags */
76 Elf64_Half e_ehsize; /* ELF header size in bytes */
77 Elf64_Half e_phentsize; /* Program header table entry size */
78 Elf64_Half e_phnum; /* Program header table entry count */
79 Elf64_Half e_shentsize; /* Section header table entry size */
80 Elf64_Half e_shnum; /* Section header table entry count */
81 Elf64_Half e_shstrndx; /* Section header string table index */
82};
83
84constexpr u8 ElfClass32 = 1; /* 32-bit objects */
85constexpr u8 ElfClass64 = 2; /* 64-bit objects */
86constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */
87constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */
88constexpr u8 ElfOsAbiNone = 0; /* System V ABI */
89
90constexpr u16 ElfTypeNone = 0; /* No file type */
91constexpr u16 ElfTypeRel = 0; /* Relocatable file */
92constexpr u16 ElfTypeExec = 0; /* Executable file */
93constexpr u16 ElfTypeDyn = 0; /* Shared object file */
94
95constexpr u16 ElfMachineArm = 40; /* ARM */
96constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */
97
98constexpr std::array<u8, ElfIdentSize> Elf32Ident{
99 0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
100
101constexpr std::array<u8, ElfIdentSize> Elf64Ident{
102 0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
103
104/* Section header. */
105
106struct Elf32_Shdr {
107 Elf32_Word sh_name; /* Section name (string tbl index) */
108 Elf32_Word sh_type; /* Section type */
109 Elf32_Word sh_flags; /* Section flags */
110 Elf32_Addr sh_addr; /* Section virtual addr at execution */
111 Elf32_Off sh_offset; /* Section file offset */
112 Elf32_Word sh_size; /* Section size in bytes */
113 Elf32_Word sh_link; /* Link to another section */
114 Elf32_Word sh_info; /* Additional section information */
115 Elf32_Word sh_addralign; /* Section alignment */
116 Elf32_Word sh_entsize; /* Entry size if section holds table */
117};
118
119struct Elf64_Shdr {
120 Elf64_Word sh_name; /* Section name (string tbl index) */
121 Elf64_Word sh_type; /* Section type */
122 Elf64_Xword sh_flags; /* Section flags */
123 Elf64_Addr sh_addr; /* Section virtual addr at execution */
124 Elf64_Off sh_offset; /* Section file offset */
125 Elf64_Xword sh_size; /* Section size in bytes */
126 Elf64_Word sh_link; /* Link to another section */
127 Elf64_Word sh_info; /* Additional section information */
128 Elf64_Xword sh_addralign; /* Section alignment */
129 Elf64_Xword sh_entsize; /* Entry size if section holds table */
130};
131
132constexpr u32 ElfShnUndef = 0; /* Undefined section */
133
134constexpr u32 ElfShtNull = 0; /* Section header table entry unused */
135constexpr u32 ElfShtProgBits = 1; /* Program data */
136constexpr u32 ElfShtSymtab = 2; /* Symbol table */
137constexpr u32 ElfShtStrtab = 3; /* String table */
138constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */
139constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */
140constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */
141constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */
142constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */
143
144/* Symbol table entry. */
145
146struct Elf32_Sym {
147 Elf32_Word st_name; /* Symbol name (string tbl index) */
148 Elf32_Addr st_value; /* Symbol value */
149 Elf32_Word st_size; /* Symbol size */
150 u8 st_info; /* Symbol type and binding */
151 u8 st_other; /* Symbol visibility */
152 Elf32_Section st_shndx; /* Section index */
153};
154
155struct Elf64_Sym {
156 Elf64_Word st_name; /* Symbol name (string tbl index) */
157 u8 st_info; /* Symbol type and binding */
158 u8 st_other; /* Symbol visibility */
159 Elf64_Section st_shndx; /* Section index */
160 Elf64_Addr st_value; /* Symbol value */
161 Elf64_Xword st_size; /* Symbol size */
162};
163
164/* How to extract and insert information held in the st_info field. */
165
166static inline u8 ElfStBind(u8 st_info) {
167 return st_info >> 4;
168}
169static inline u8 ElfStType(u8 st_info) {
170 return st_info & 0xf;
171}
172static inline u8 ElfStInfo(u8 st_bind, u8 st_type) {
173 return static_cast<u8>((st_bind << 4) + (st_type & 0xf));
174}
175
176constexpr u8 ElfBindLocal = 0; /* Local symbol */
177constexpr u8 ElfBindGlobal = 1; /* Global symbol */
178constexpr u8 ElfBindWeak = 2; /* Weak symbol */
179
180constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */
181constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */
182constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */
183
184static inline u8 ElfStVisibility(u8 st_other) {
185 return static_cast<u8>(st_other & 0x3);
186}
187
188constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */
189constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */
190constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */
191constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */
192
193/* Relocation table entry without addend (in section of type ShtRel). */
194
195struct Elf32_Rel {
196 Elf32_Addr r_offset; /* Address */
197 Elf32_Word r_info; /* Relocation type and symbol index */
198};
199
200/* Relocation table entry with addend (in section of type ShtRela). */
201
202struct Elf32_Rela {
203 Elf32_Addr r_offset; /* Address */
204 Elf32_Word r_info; /* Relocation type and symbol index */
205 Elf32_Sword r_addend; /* Addend */
206};
207
208struct Elf64_Rela {
209 Elf64_Addr r_offset; /* Address */
210 Elf64_Xword r_info; /* Relocation type and symbol index */
211 Elf64_Sxword r_addend; /* Addend */
212};
213
214/* How to extract and insert information held in the r_info field. */
215
216static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
217 return r_info >> 8;
218}
219static inline u8 Elf32RelType(Elf32_Word r_info) {
220 return static_cast<u8>(r_info & 0xff);
221}
222static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) {
223 return (sym_index << 8) + type;
224}
225static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) {
226 return static_cast<u32>(r_info >> 32);
227}
228static inline u32 Elf64RelType(Elf64_Xword r_info) {
229 return r_info & 0xffffffff;
230}
231static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) {
232 return (static_cast<Elf64_Xword>(sym_index) << 32) + type;
233}
234
235constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */
236constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */
237constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */
238constexpr u32 ElfArmRelative = 23; /* Adjust by program base */
239
240constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */
241constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */
242constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */
243constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */
244
245/* Program segment header. */
246
247struct Elf32_Phdr {
248 Elf32_Word p_type; /* Segment type */
249 Elf32_Off p_offset; /* Segment file offset */
250 Elf32_Addr p_vaddr; /* Segment virtual address */
251 Elf32_Addr p_paddr; /* Segment physical address */
252 Elf32_Word p_filesz; /* Segment size in file */
253 Elf32_Word p_memsz; /* Segment size in memory */
254 Elf32_Word p_flags; /* Segment flags */
255 Elf32_Word p_align; /* Segment alignment */
256};
257
258struct Elf64_Phdr {
259 Elf64_Word p_type; /* Segment type */
260 Elf64_Word p_flags; /* Segment flags */
261 Elf64_Off p_offset; /* Segment file offset */
262 Elf64_Addr p_vaddr; /* Segment virtual address */
263 Elf64_Addr p_paddr; /* Segment physical address */
264 Elf64_Xword p_filesz; /* Segment size in file */
265 Elf64_Xword p_memsz; /* Segment size in memory */
266 Elf64_Xword p_align; /* Segment alignment */
267};
268
269/* Legal values for p_type (segment type). */
270
271constexpr u32 ElfPtNull = 0; /* Program header table entry unused */
272constexpr u32 ElfPtLoad = 1; /* Loadable program segment */
273constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */
274constexpr u32 ElfPtInterp = 3; /* Program interpreter */
275constexpr u32 ElfPtNote = 4; /* Auxiliary information */
276constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */
277constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */
278
279/* Legal values for p_flags (segment flags). */
280
281constexpr u32 ElfPfExec = 0; /* Segment is executable */
282constexpr u32 ElfPfWrite = 1; /* Segment is writable */
283constexpr u32 ElfPfRead = 2; /* Segment is readable */
284
285/* Dynamic section entry. */
286
287struct Elf32_Dyn {
288 Elf32_Sword d_tag; /* Dynamic entry type */
289 union {
290 Elf32_Word d_val; /* Integer value */
291 Elf32_Addr d_ptr; /* Address value */
292 } d_un;
293};
294
295struct Elf64_Dyn {
296 Elf64_Sxword d_tag; /* Dynamic entry type */
297 union {
298 Elf64_Xword d_val; /* Integer value */
299 Elf64_Addr d_ptr; /* Address value */
300 } d_un;
301};
302
303/* Legal values for d_tag (dynamic entry type). */
304
305constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */
306constexpr u32 ElfDtNeeded = 1; /* Name of needed library */
307constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */
308constexpr u32 ElfDtPltGot = 3; /* Processor defined value */
309constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */
310constexpr u32 ElfDtStrtab = 5; /* Address of string table */
311constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */
312constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */
313constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */
314constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */
315constexpr u32 ElfDtStrsz = 10; /* Size of string table */
316constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */
317constexpr u32 ElfDtInit = 12; /* Address of init function */
318constexpr u32 ElfDtFini = 13; /* Address of termination function */
319constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */
320constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */
321constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */
322constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */
323constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */
324constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */
325constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */
326constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */
327constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
328constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
329constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
330constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
331
332} // namespace ELF
333} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 6ffab63af..751549583 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -147,7 +147,7 @@ void UpdateRescalingInfo() {
147 info.down_shift = 0; 147 info.down_shift = 0;
148 break; 148 break;
149 default: 149 default:
150 UNREACHABLE(); 150 ASSERT(false);
151 info.up_scale = 1; 151 info.up_scale = 1;
152 info.down_shift = 0; 152 info.down_shift = 0;
153 } 153 }
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/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
index 4aa1a1ee1..0259c7ea2 100644
--- a/src/core/arm/symbols.cpp
+++ b/src/core/arm/symbols.cpp
@@ -3,73 +3,14 @@
3 3
4#include "common/bit_field.h" 4#include "common/bit_field.h"
5#include "common/common_funcs.h" 5#include "common/common_funcs.h"
6#include "common/elf.h"
6#include "core/arm/symbols.h" 7#include "core/arm/symbols.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/memory.h" 9#include "core/memory.h"
9 10
10namespace Core { 11using namespace Common::ELF;
11namespace {
12
13constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
14constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
15constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
16constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
17
18enum class ELFSymbolType : u8 {
19 None = 0,
20 Object = 1,
21 Function = 2,
22 Section = 3,
23 File = 4,
24 Common = 5,
25 TLS = 6,
26};
27
28enum class ELFSymbolBinding : u8 {
29 Local = 0,
30 Global = 1,
31 Weak = 2,
32};
33
34enum class ELFSymbolVisibility : u8 {
35 Default = 0,
36 Internal = 1,
37 Hidden = 2,
38 Protected = 3,
39};
40
41struct ELF64Symbol {
42 u32 name_index;
43 union {
44 u8 info;
45
46 BitField<0, 4, ELFSymbolType> type;
47 BitField<4, 4, ELFSymbolBinding> binding;
48 };
49 ELFSymbolVisibility visibility;
50 u16 sh_index;
51 u64 value;
52 u64 size;
53};
54static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
55
56struct ELF32Symbol {
57 u32 name_index;
58 u32 value;
59 u32 size;
60 union {
61 u8 info;
62
63 BitField<0, 4, ELFSymbolType> type;
64 BitField<4, 4, ELFSymbolBinding> binding;
65 };
66 ELFSymbolVisibility visibility;
67 u16 sh_index;
68};
69static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
70
71} // Anonymous namespace
72 12
13namespace Core {
73namespace Symbols { 14namespace Symbols {
74 15
75template <typename Word, typename ELFSymbol, typename ByteReader> 16template <typename Word, typename ELFSymbol, typename ByteReader>
@@ -110,15 +51,15 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
110 const Word value = ReadWord(dynamic_index + sizeof(Word)); 51 const Word value = ReadWord(dynamic_index + sizeof(Word));
111 dynamic_index += 2 * sizeof(Word); 52 dynamic_index += 2 * sizeof(Word);
112 53
113 if (tag == ELF_DYNAMIC_TAG_NULL) { 54 if (tag == ElfDtNull) {
114 break; 55 break;
115 } 56 }
116 57
117 if (tag == ELF_DYNAMIC_TAG_STRTAB) { 58 if (tag == ElfDtStrtab) {
118 string_table_offset = value; 59 string_table_offset = value;
119 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { 60 } else if (tag == ElfDtSymtab) {
120 symbol_table_offset = value; 61 symbol_table_offset = value;
121 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { 62 } else if (tag == ElfDtSyment) {
122 symbol_entry_size = value; 63 symbol_entry_size = value;
123 } 64 }
124 } 65 }
@@ -134,14 +75,14 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
134 ELFSymbol symbol{}; 75 ELFSymbol symbol{};
135 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol)); 76 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
136 77
137 VAddr string_offset = string_table_offset + symbol.name_index; 78 VAddr string_offset = string_table_offset + symbol.st_name;
138 std::string name; 79 std::string name;
139 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) { 80 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
140 name += static_cast<char>(c); 81 name += static_cast<char>(c);
141 } 82 }
142 83
143 symbol_index += symbol_entry_size; 84 symbol_index += symbol_entry_size;
144 out[name] = std::make_pair(symbol.value, symbol.size); 85 out[name] = std::make_pair(symbol.st_value, symbol.st_size);
145 } 86 }
146 87
147 return out; 88 return out;
@@ -152,9 +93,9 @@ Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
152 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }}; 93 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
153 94
154 if (is_64) { 95 if (is_64) {
155 return GetSymbols<u64, ELF64Symbol>(ReadBytes); 96 return GetSymbols<u64, Elf64_Sym>(ReadBytes);
156 } else { 97 } else {
157 return GetSymbols<u32, ELF32Symbol>(ReadBytes); 98 return GetSymbols<u32, Elf32_Sym>(ReadBytes);
158 } 99 }
159} 100}
160 101
@@ -164,9 +105,9 @@ Symbols GetSymbols(std::span<const u8> data, bool is_64) {
164 }}; 105 }};
165 106
166 if (is_64) { 107 if (is_64) {
167 return GetSymbols<u64, ELF64Symbol>(ReadBytes); 108 return GetSymbols<u64, Elf64_Sym>(ReadBytes);
168 } else { 109 } else {
169 return GetSymbols<u32, ELF32Symbol>(ReadBytes); 110 return GetSymbols<u32, Elf32_Sym>(ReadBytes);
170 } 111 }
171} 112}
172 113
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 7d974ba65..954136adb 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -493,6 +493,12 @@ void System::Shutdown() {
493 impl->Shutdown(); 493 impl->Shutdown();
494} 494}
495 495
496void System::DetachDebugger() {
497 if (impl->debugger) {
498 impl->debugger->NotifyShutdown();
499 }
500}
501
496std::unique_lock<std::mutex> System::StallCPU() { 502std::unique_lock<std::mutex> System::StallCPU() {
497 return impl->StallCPU(); 503 return impl->StallCPU();
498} 504}
diff --git a/src/core/core.h b/src/core/core.h
index 94477206e..5c367349e 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -160,6 +160,9 @@ public:
160 /// Shutdown the emulated system. 160 /// Shutdown the emulated system.
161 void Shutdown(); 161 void Shutdown();
162 162
163 /// Forcibly detach the debugger if it is running.
164 void DetachDebugger();
165
163 std::unique_lock<std::mutex> StallCPU(); 166 std::unique_lock<std::mutex> StallCPU();
164 void UnstallCPU(); 167 void UnstallCPU();
165 168
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 09d9c5163..b4718fbbe 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -16,7 +16,8 @@
16 16
17namespace Core { 17namespace Core {
18 18
19CpuManager::CpuManager(System& system_) : system{system_} {} 19CpuManager::CpuManager(System& system_)
20 : pause_barrier{std::make_unique<Common::Barrier>(1)}, system{system_} {}
20CpuManager::~CpuManager() = default; 21CpuManager::~CpuManager() = default;
21 22
22void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, 23void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
@@ -30,8 +31,10 @@ void CpuManager::Initialize() {
30 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 31 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
31 core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); 32 core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
32 } 33 }
34 pause_barrier = std::make_unique<Common::Barrier>(Core::Hardware::NUM_CPU_CORES + 1);
33 } else { 35 } else {
34 core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0); 36 core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
37 pause_barrier = std::make_unique<Common::Barrier>(2);
35 } 38 }
36} 39}
37 40
@@ -138,51 +141,14 @@ void CpuManager::MultiCoreRunSuspendThread() {
138 auto core = kernel.CurrentPhysicalCoreIndex(); 141 auto core = kernel.CurrentPhysicalCoreIndex();
139 auto& scheduler = *kernel.CurrentScheduler(); 142 auto& scheduler = *kernel.CurrentScheduler();
140 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 143 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
144 current_thread->DisableDispatch();
145
141 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); 146 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
142 ASSERT(scheduler.ContextSwitchPending());
143 ASSERT(core == kernel.CurrentPhysicalCoreIndex()); 147 ASSERT(core == kernel.CurrentPhysicalCoreIndex());
144 scheduler.RescheduleCurrentCore(); 148 scheduler.RescheduleCurrentCore();
145 } 149 }
146} 150}
147 151
148void CpuManager::MultiCorePause(bool paused) {
149 if (!paused) {
150 bool all_not_barrier = false;
151 while (!all_not_barrier) {
152 all_not_barrier = true;
153 for (const auto& data : core_data) {
154 all_not_barrier &= !data.is_running.load() && data.initialized.load();
155 }
156 }
157 for (auto& data : core_data) {
158 data.enter_barrier->Set();
159 }
160 if (paused_state.load()) {
161 bool all_barrier = false;
162 while (!all_barrier) {
163 all_barrier = true;
164 for (const auto& data : core_data) {
165 all_barrier &= data.is_paused.load() && data.initialized.load();
166 }
167 }
168 for (auto& data : core_data) {
169 data.exit_barrier->Set();
170 }
171 }
172 } else {
173 /// Wait until all cores are paused.
174 bool all_barrier = false;
175 while (!all_barrier) {
176 all_barrier = true;
177 for (const auto& data : core_data) {
178 all_barrier &= data.is_paused.load() && data.initialized.load();
179 }
180 }
181 /// Don't release the barrier
182 }
183 paused_state = paused;
184}
185
186/////////////////////////////////////////////////////////////////////////////// 152///////////////////////////////////////////////////////////////////////////////
187/// SingleCore /// 153/// SingleCore ///
188/////////////////////////////////////////////////////////////////////////////// 154///////////////////////////////////////////////////////////////////////////////
@@ -235,8 +201,9 @@ void CpuManager::SingleCoreRunSuspendThread() {
235 auto core = kernel.GetCurrentHostThreadID(); 201 auto core = kernel.GetCurrentHostThreadID();
236 auto& scheduler = *kernel.CurrentScheduler(); 202 auto& scheduler = *kernel.CurrentScheduler();
237 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 203 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
204 current_thread->DisableDispatch();
205
238 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context); 206 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
239 ASSERT(scheduler.ContextSwitchPending());
240 ASSERT(core == kernel.GetCurrentHostThreadID()); 207 ASSERT(core == kernel.GetCurrentHostThreadID());
241 scheduler.RescheduleCurrentCore(); 208 scheduler.RescheduleCurrentCore();
242 } 209 }
@@ -274,37 +241,21 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
274 } 241 }
275} 242}
276 243
277void CpuManager::SingleCorePause(bool paused) {
278 if (!paused) {
279 bool all_not_barrier = false;
280 while (!all_not_barrier) {
281 all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
282 }
283 core_data[0].enter_barrier->Set();
284 if (paused_state.load()) {
285 bool all_barrier = false;
286 while (!all_barrier) {
287 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
288 }
289 core_data[0].exit_barrier->Set();
290 }
291 } else {
292 /// Wait until all cores are paused.
293 bool all_barrier = false;
294 while (!all_barrier) {
295 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
296 }
297 /// Don't release the barrier
298 }
299 paused_state = paused;
300}
301
302void CpuManager::Pause(bool paused) { 244void CpuManager::Pause(bool paused) {
303 if (is_multicore) { 245 std::scoped_lock lk{pause_lock};
304 MultiCorePause(paused); 246
305 } else { 247 if (pause_state == paused) {
306 SingleCorePause(paused); 248 return;
307 } 249 }
250
251 // Set the new state
252 pause_state.store(paused);
253
254 // Wake up any waiting threads
255 pause_state.notify_all();
256
257 // Wait for all threads to successfully change state before returning
258 pause_barrier->Sync();
308} 259}
309 260
310void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { 261void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
@@ -320,27 +271,29 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
320 Common::SetCurrentThreadName(name.c_str()); 271 Common::SetCurrentThreadName(name.c_str());
321 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 272 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
322 auto& data = core_data[core]; 273 auto& data = core_data[core];
323 data.enter_barrier = std::make_unique<Common::Event>();
324 data.exit_barrier = std::make_unique<Common::Event>();
325 data.host_context = Common::Fiber::ThreadToFiber(); 274 data.host_context = Common::Fiber::ThreadToFiber();
326 data.is_running = false;
327 data.initialized = true;
328 const bool sc_sync = !is_async_gpu && !is_multicore; 275 const bool sc_sync = !is_async_gpu && !is_multicore;
329 bool sc_sync_first_use = sc_sync; 276 bool sc_sync_first_use = sc_sync;
330 277
331 // Cleanup 278 // Cleanup
332 SCOPE_EXIT({ 279 SCOPE_EXIT({
333 data.host_context->Exit(); 280 data.host_context->Exit();
334 data.enter_barrier.reset();
335 data.exit_barrier.reset();
336 data.initialized = false;
337 MicroProfileOnThreadExit(); 281 MicroProfileOnThreadExit();
338 }); 282 });
339 283
340 /// Running 284 /// Running
341 while (running_mode) { 285 while (running_mode) {
342 data.is_running = false; 286 if (pause_state.load(std::memory_order_relaxed)) {
343 data.enter_barrier->Wait(); 287 // Wait for caller to acknowledge pausing
288 pause_barrier->Sync();
289
290 // Wait until unpaused
291 pause_state.wait(true, std::memory_order_relaxed);
292
293 // Wait for caller to acknowledge unpausing
294 pause_barrier->Sync();
295 }
296
344 if (sc_sync_first_use) { 297 if (sc_sync_first_use) {
345 system.GPU().ObtainContext(); 298 system.GPU().ObtainContext();
346 sc_sync_first_use = false; 299 sc_sync_first_use = false;
@@ -352,12 +305,7 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
352 } 305 }
353 306
354 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); 307 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
355 data.is_running = true;
356 Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext()); 308 Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
357 data.is_running = false;
358 data.is_paused = true;
359 data.exit_barrier->Wait();
360 data.is_paused = false;
361 } 309 }
362} 310}
363 311
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index aee352245..ddd9691ca 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -69,13 +69,11 @@ private:
69 void MultiCoreRunGuestLoop(); 69 void MultiCoreRunGuestLoop();
70 void MultiCoreRunIdleThread(); 70 void MultiCoreRunIdleThread();
71 void MultiCoreRunSuspendThread(); 71 void MultiCoreRunSuspendThread();
72 void MultiCorePause(bool paused);
73 72
74 void SingleCoreRunGuestThread(); 73 void SingleCoreRunGuestThread();
75 void SingleCoreRunGuestLoop(); 74 void SingleCoreRunGuestLoop();
76 void SingleCoreRunIdleThread(); 75 void SingleCoreRunIdleThread();
77 void SingleCoreRunSuspendThread(); 76 void SingleCoreRunSuspendThread();
78 void SingleCorePause(bool paused);
79 77
80 static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); 78 static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
81 79
@@ -83,16 +81,13 @@ private:
83 81
84 struct CoreData { 82 struct CoreData {
85 std::shared_ptr<Common::Fiber> host_context; 83 std::shared_ptr<Common::Fiber> host_context;
86 std::unique_ptr<Common::Event> enter_barrier;
87 std::unique_ptr<Common::Event> exit_barrier;
88 std::atomic<bool> is_running;
89 std::atomic<bool> is_paused;
90 std::atomic<bool> initialized;
91 std::jthread host_thread; 84 std::jthread host_thread;
92 }; 85 };
93 86
94 std::atomic<bool> running_mode{}; 87 std::atomic<bool> running_mode{};
95 std::atomic<bool> paused_state{}; 88 std::atomic<bool> pause_state{};
89 std::unique_ptr<Common::Barrier> pause_barrier{};
90 std::mutex pause_lock{};
96 91
97 std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; 92 std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
98 93
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index e3c4f80eb..443323390 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -140,7 +140,6 @@ u64 GetSignatureTypeDataSize(SignatureType type) {
140 return 0x3C; 140 return 0x3C;
141 } 141 }
142 UNREACHABLE(); 142 UNREACHABLE();
143 return 0;
144} 143}
145 144
146u64 GetSignatureTypePaddingSize(SignatureType type) { 145u64 GetSignatureTypePaddingSize(SignatureType type) {
@@ -155,7 +154,6 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
155 return 0x40; 154 return 0x40;
156 } 155 }
157 UNREACHABLE(); 156 UNREACHABLE();
158 return 0;
159} 157}
160 158
161SignatureType Ticket::GetSignatureType() const { 159SignatureType Ticket::GetSignatureType() const {
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 68ab33e46..edf991d71 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -20,15 +20,16 @@ template <typename Readable, typename Buffer, typename Callback>
20static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { 20static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
21 static_assert(std::is_trivial_v<Buffer>); 21 static_assert(std::is_trivial_v<Buffer>);
22 auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))}; 22 auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
23 r.async_read_some(boost_buffer, [&](const boost::system::error_code& error, size_t bytes_read) { 23 r.async_read_some(
24 if (!error.failed()) { 24 boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
25 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); 25 if (!error.failed()) {
26 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; 26 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
27 c(received_data); 27 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
28 } 28 c(received_data);
29 }
29 30
30 AsyncReceiveInto(r, buffer, c); 31 AsyncReceiveInto(r, buffer, c);
31 }); 32 });
32} 33}
33 34
34template <typename Readable, typename Buffer> 35template <typename Readable, typename Buffer>
@@ -41,6 +42,16 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
41 return received_data; 42 return received_data;
42} 43}
43 44
45enum class SignalType {
46 Stopped,
47 ShuttingDown,
48};
49
50struct SignalInfo {
51 SignalType type;
52 Kernel::KThread* thread;
53};
54
44namespace Core { 55namespace Core {
45 56
46class DebuggerImpl : public DebuggerBackend { 57class DebuggerImpl : public DebuggerBackend {
@@ -55,7 +66,7 @@ public:
55 ShutdownServer(); 66 ShutdownServer();
56 } 67 }
57 68
58 bool NotifyThreadStopped(Kernel::KThread* thread) { 69 bool SignalDebugger(SignalInfo signal_info) {
59 std::scoped_lock lk{connection_lock}; 70 std::scoped_lock lk{connection_lock};
60 71
61 if (stopped) { 72 if (stopped) {
@@ -63,9 +74,13 @@ public:
63 // It should be ignored. 74 // It should be ignored.
64 return false; 75 return false;
65 } 76 }
77
78 // Set up the state.
66 stopped = true; 79 stopped = true;
80 info = signal_info;
67 81
68 signal_pipe.write_some(boost::asio::buffer(&thread, sizeof(thread))); 82 // Write a single byte into the pipe to wake up the debug interface.
83 boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
69 return true; 84 return true;
70 } 85 }
71 86
@@ -74,7 +89,7 @@ public:
74 } 89 }
75 90
76 void WriteToClient(std::span<const u8> data) override { 91 void WriteToClient(std::span<const u8> data) override {
77 client_socket.write_some(boost::asio::buffer(data.data(), data.size_bytes())); 92 boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
78 } 93 }
79 94
80 void SetActiveThread(Kernel::KThread* thread) override { 95 void SetActiveThread(Kernel::KThread* thread) override {
@@ -95,7 +110,7 @@ private:
95 connection_thread = std::jthread([&, port](std::stop_token stop_token) { 110 connection_thread = std::jthread([&, port](std::stop_token stop_token) {
96 try { 111 try {
97 // Initialize the listening socket and accept a new client. 112 // Initialize the listening socket and accept a new client.
98 tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port}; 113 tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
99 tcp::acceptor acceptor{io_context, endpoint}; 114 tcp::acceptor acceptor{io_context, endpoint};
100 115
101 acceptor.async_accept(client_socket, [](const auto&) {}); 116 acceptor.async_accept(client_socket, [](const auto&) {});
@@ -123,7 +138,7 @@ private:
123 Common::SetCurrentThreadName("yuzu:Debugger"); 138 Common::SetCurrentThreadName("yuzu:Debugger");
124 139
125 // Set up the client signals for new data. 140 // Set up the client signals for new data.
126 AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); }); 141 AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
127 AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); 142 AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
128 143
129 // Stop the emulated CPU. 144 // Stop the emulated CPU.
@@ -141,9 +156,28 @@ private:
141 } 156 }
142 157
143 void PipeData(std::span<const u8> data) { 158 void PipeData(std::span<const u8> data) {
144 AllCoreStop(); 159 switch (info.type) {
145 UpdateActiveThread(); 160 case SignalType::Stopped:
146 frontend->Stopped(active_thread); 161 // Stop emulation.
162 AllCoreStop();
163
164 // Notify the client.
165 active_thread = info.thread;
166 UpdateActiveThread();
167 frontend->Stopped(active_thread);
168
169 break;
170 case SignalType::ShuttingDown:
171 frontend->ShuttingDown();
172
173 // Wait for emulation to shut down gracefully now.
174 suspend.reset();
175 signal_pipe.close();
176 client_socket.shutdown(boost::asio::socket_base::shutdown_both);
177 LOG_INFO(Debug_GDBStub, "Shut down server");
178
179 break;
180 }
147 } 181 }
148 182
149 void ClientData(std::span<const u8> data) { 183 void ClientData(std::span<const u8> data) {
@@ -245,7 +279,9 @@ private:
245 boost::asio::ip::tcp::socket client_socket; 279 boost::asio::ip::tcp::socket client_socket;
246 std::optional<std::unique_lock<std::mutex>> suspend; 280 std::optional<std::unique_lock<std::mutex>> suspend;
247 281
282 SignalInfo info;
248 Kernel::KThread* active_thread; 283 Kernel::KThread* active_thread;
284 bool pipe_data;
249 bool stopped; 285 bool stopped;
250 286
251 std::array<u8, 4096> client_data; 287 std::array<u8, 4096> client_data;
@@ -262,7 +298,13 @@ Debugger::Debugger(Core::System& system, u16 port) {
262Debugger::~Debugger() = default; 298Debugger::~Debugger() = default;
263 299
264bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) { 300bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
265 return impl && impl->NotifyThreadStopped(thread); 301 return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread});
302}
303
304void Debugger::NotifyShutdown() {
305 if (impl) {
306 impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr});
307 }
266} 308}
267 309
268} // namespace Core 310} // namespace Core
diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h
index ea36c6ab2..f9738ca3d 100644
--- a/src/core/debugger/debugger.h
+++ b/src/core/debugger/debugger.h
@@ -35,6 +35,11 @@ public:
35 */ 35 */
36 bool NotifyThreadStopped(Kernel::KThread* thread); 36 bool NotifyThreadStopped(Kernel::KThread* thread);
37 37
38 /**
39 * Notify the debugger that a shutdown is being performed now and disconnect.
40 */
41 void NotifyShutdown();
42
38private: 43private:
39 std::unique_ptr<DebuggerImpl> impl; 44 std::unique_ptr<DebuggerImpl> impl;
40}; 45};
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
index 35ba0bc61..c0bb4ecaf 100644
--- a/src/core/debugger/debugger_interface.h
+++ b/src/core/debugger/debugger_interface.h
@@ -67,6 +67,11 @@ public:
67 virtual void Stopped(Kernel::KThread* thread) = 0; 67 virtual void Stopped(Kernel::KThread* thread) = 0;
68 68
69 /** 69 /**
70 * Called when emulation is shutting down.
71 */
72 virtual void ShuttingDown() = 0;
73
74 /**
70 * Called when new data is asynchronously received on the client socket. 75 * Called when new data is asynchronously received on the client socket.
71 * A list of actions to perform is returned. 76 * A list of actions to perform is returned.
72 */ 77 */
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 682651a86..52e76f659 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -106,6 +106,8 @@ GDBStub::~GDBStub() = default;
106 106
107void GDBStub::Connected() {} 107void GDBStub::Connected() {}
108 108
109void GDBStub::ShuttingDown() {}
110
109void GDBStub::Stopped(Kernel::KThread* thread) { 111void GDBStub::Stopped(Kernel::KThread* thread) {
110 SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); 112 SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
111} 113}
@@ -422,6 +424,18 @@ static std::string GetThreadState(const Kernel::KThread* thread) {
422 } 424 }
423} 425}
424 426
427static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
428 const auto amount{request.substr(request.find(',') + 1)};
429 const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
430 const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
431
432 if (offset_val + amount_val > buffer.size()) {
433 return fmt::format("l{}", buffer.substr(offset_val));
434 } else {
435 return fmt::format("m{}", buffer.substr(offset_val, amount_val));
436 }
437}
438
425void GDBStub::HandleQuery(std::string_view command) { 439void GDBStub::HandleQuery(std::string_view command) {
426 if (command.starts_with("TStatus")) { 440 if (command.starts_with("TStatus")) {
427 // no tracepoint support 441 // no tracepoint support
@@ -430,18 +444,8 @@ void GDBStub::HandleQuery(std::string_view command) {
430 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;" 444 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
431 "vContSupported+;QStartNoAckMode+"); 445 "vContSupported+;QStartNoAckMode+");
432 } else if (command.starts_with("Xfer:features:read:target.xml:")) { 446 } else if (command.starts_with("Xfer:features:read:target.xml:")) {
433 const auto offset{command.substr(30)};
434 const auto amount{command.substr(command.find(',') + 1)};
435
436 const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))};
437 const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
438 const auto target_xml{arch->GetTargetXML()}; 447 const auto target_xml{arch->GetTargetXML()};
439 448 SendReply(PaginateBuffer(target_xml, command.substr(30)));
440 if (offset_val + amount_val > target_xml.size()) {
441 SendReply("l" + target_xml.substr(offset_val));
442 } else {
443 SendReply("m" + target_xml.substr(offset_val, amount_val));
444 }
445 } else if (command.starts_with("Offsets")) { 449 } else if (command.starts_with("Offsets")) {
446 Loader::AppLoader::Modules modules; 450 Loader::AppLoader::Modules modules;
447 system.GetAppLoader().ReadNSOModules(modules); 451 system.GetAppLoader().ReadNSOModules(modules);
@@ -454,6 +458,20 @@ void GDBStub::HandleQuery(std::string_view command) {
454 SendReply(fmt::format("TextSeg={:x}", 458 SendReply(fmt::format("TextSeg={:x}",
455 system.CurrentProcess()->PageTable().GetCodeRegionStart())); 459 system.CurrentProcess()->PageTable().GetCodeRegionStart()));
456 } 460 }
461 } else if (command.starts_with("Xfer:libraries:read::")) {
462 Loader::AppLoader::Modules modules;
463 system.GetAppLoader().ReadNSOModules(modules);
464
465 std::string buffer;
466 buffer += R"(<?xml version="1.0"?>)";
467 buffer += "<library-list>";
468 for (const auto& [base, name] : modules) {
469 buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
470 EscapeXML(name), base);
471 }
472 buffer += "</library-list>";
473
474 SendReply(PaginateBuffer(buffer, command.substr(21)));
457 } else if (command.starts_with("fThreadInfo")) { 475 } else if (command.starts_with("fThreadInfo")) {
458 // beginning of list 476 // beginning of list
459 const auto& threads = system.GlobalSchedulerContext().GetThreadList(); 477 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
@@ -484,17 +502,7 @@ void GDBStub::HandleQuery(std::string_view command) {
484 502
485 buffer += "</threads>"; 503 buffer += "</threads>";
486 504
487 const auto offset{command.substr(19)}; 505 SendReply(PaginateBuffer(buffer, command.substr(19)));
488 const auto amount{command.substr(command.find(',') + 1)};
489
490 const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))};
491 const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
492
493 if (offset_val + amount_val > buffer.size()) {
494 SendReply("l" + buffer.substr(offset_val));
495 } else {
496 SendReply("m" + buffer.substr(offset_val, amount_val));
497 }
498 } else if (command.starts_with("Attached")) { 506 } else if (command.starts_with("Attached")) {
499 SendReply("0"); 507 SendReply("0");
500 } else if (command.starts_with("StartNoAckMode")) { 508 } else if (command.starts_with("StartNoAckMode")) {
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index 1bb638187..ec934c77e 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -23,6 +23,7 @@ public:
23 23
24 void Connected() override; 24 void Connected() override;
25 void Stopped(Kernel::KThread* thread) override; 25 void Stopped(Kernel::KThread* thread) override;
26 void ShuttingDown() override;
26 std::vector<DebuggerAction> ClientData(std::span<const u8> data) override; 27 std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
27 28
28private: 29private:
diff --git a/src/core/debugger/gdbstub_arch.h b/src/core/debugger/gdbstub_arch.h
index 4d039a9f7..2540d6456 100644
--- a/src/core/debugger/gdbstub_arch.h
+++ b/src/core/debugger/gdbstub_arch.h
@@ -15,6 +15,7 @@ namespace Core {
15 15
16class GDBStubArch { 16class GDBStubArch {
17public: 17public:
18 virtual ~GDBStubArch() = default;
18 virtual std::string GetTargetXML() const = 0; 19 virtual std::string GetTargetXML() const = 0;
19 virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0; 20 virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
20 virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0; 21 virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 93f784418..78e56bbbd 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -419,7 +419,7 @@ std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type
419 Core::Crypto::Mode::ECB); 419 Core::Crypto::Mode::ECB);
420 cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt); 420 cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
421 421
422 Core::Crypto::Key128 out; 422 Core::Crypto::Key128 out{};
423 if (type == NCASectionCryptoType::XTS) { 423 if (type == NCASectionCryptoType::XTS) {
424 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); 424 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
425 } else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) { 425 } else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) {
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index d4c0a974a..2735d053b 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.cpp
@@ -50,7 +50,7 @@ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockTyp
50 low = mid + 1; 50 low = mid + 1;
51 } 51 }
52 } 52 }
53 UNREACHABLE_MSG("Offset could not be found in BKTR block."); 53 ASSERT_MSG(false, "Offset could not be found in BKTR block.");
54 return {0, 0}; 54 return {0, 0};
55} 55}
56} // Anonymous namespace 56} // Anonymous namespace
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 2eaac73ef..878d832c2 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -108,7 +108,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
108 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. 108 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
109 return ContentRecordType::HtmlDocument; 109 return ContentRecordType::HtmlDocument;
110 default: 110 default:
111 UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type); 111 ASSERT_MSG(false, "Invalid NCAContentType={:02X}", type);
112 return ContentRecordType{}; 112 return ContentRecordType{};
113 } 113 }
114} 114}
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index e42d7c9f6..cc0076238 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -144,7 +144,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
144 LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); 144 LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
145 } 145 }
146 } else { 146 } else {
147 UNREACHABLE(); 147 ASSERT(false);
148 return nullptr; 148 return nullptr;
149 } 149 }
150 150
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 0ff2a338e..6c230f619 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -65,7 +65,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
65 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); 65 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
66 controller->Connect(true); 66 controller->Connect(true);
67 } else { 67 } else {
68 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); 68 ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!");
69 } 69 }
70 } 70 }
71 71
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index 7eed52593..7d6373414 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -48,7 +48,7 @@ EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
48 return handheld.get(); 48 return handheld.get();
49 case NpadIdType::Invalid: 49 case NpadIdType::Invalid:
50 default: 50 default:
51 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type); 51 ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
52 return nullptr; 52 return nullptr;
53 } 53 }
54} 54}
@@ -77,7 +77,7 @@ const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type
77 return handheld.get(); 77 return handheld.get();
78 case NpadIdType::Invalid: 78 case NpadIdType::Invalid:
79 default: 79 default:
80 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type); 80 ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
81 return nullptr; 81 return nullptr;
82 } 82 }
83} 83}
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index a427cbc93..0ddc8df9e 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -141,7 +141,7 @@ public:
141 if (index < DomainHandlerCount()) { 141 if (index < DomainHandlerCount()) {
142 domain_handlers[index] = nullptr; 142 domain_handlers[index] = nullptr;
143 } else { 143 } else {
144 UNREACHABLE_MSG("Unexpected handler index {}", index); 144 ASSERT_MSG(false, "Unexpected handler index {}", index);
145 } 145 }
146 } 146 }
147 147
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 34a8be052..9b6b284d0 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -244,7 +244,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
244 FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP) 244 FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
245 // If we somehow get an invalid type, abort. 245 // If we somehow get an invalid type, abort.
246 default: 246 default:
247 UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]); 247 ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
248 } 248 }
249 249
250 // If we've hit the end of a gap, free it. 250 // If we've hit the end of a gap, free it.
diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h
index e46e0d848..5fa19d386 100644
--- a/src/core/hle/kernel/k_address_arbiter.h
+++ b/src/core/hle/kernel/k_address_arbiter.h
@@ -35,7 +35,7 @@ public:
35 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: 35 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
36 return SignalAndModifyByWaitingCountIfEqual(addr, value, count); 36 return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
37 } 37 }
38 UNREACHABLE(); 38 ASSERT(false);
39 return ResultUnknown; 39 return ResultUnknown;
40 } 40 }
41 41
@@ -49,7 +49,7 @@ public:
49 case Svc::ArbitrationType::WaitIfEqual: 49 case Svc::ArbitrationType::WaitIfEqual:
50 return WaitIfEqual(addr, value, timeout); 50 return WaitIfEqual(addr, value, timeout);
51 } 51 }
52 UNREACHABLE(); 52 ASSERT(false);
53 return ResultUnknown; 53 return ResultUnknown;
54 } 54 }
55 55
diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp
index bc37cadda..3e612a207 100644
--- a/src/core/hle/kernel/k_address_space_info.cpp
+++ b/src/core/hle/kernel/k_address_space_info.cpp
@@ -84,7 +84,7 @@ u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
84 ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index])); 84 ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
85 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address; 85 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
86 } 86 }
87 UNREACHABLE(); 87 ASSERT(false);
88 return 0; 88 return 0;
89} 89}
90 90
@@ -101,7 +101,7 @@ std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
101 ASSERT(IsAllowed39BitType(type)); 101 ASSERT(IsAllowed39BitType(type));
102 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size; 102 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
103 } 103 }
104 UNREACHABLE(); 104 ASSERT(false);
105 return 0; 105 return 0;
106} 106}
107 107
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index ea47fc600..2827763d5 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -18,7 +18,7 @@ namespace Kernel {
18class KernelCore; 18class KernelCore;
19class KProcess; 19class KProcess;
20 20
21#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ 21#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \
22 \ 22 \
23private: \ 23private: \
24 friend class ::Kernel::KClassTokenGenerator; \ 24 friend class ::Kernel::KClassTokenGenerator; \
@@ -40,16 +40,19 @@ public:
40 static constexpr const char* GetStaticTypeName() { \ 40 static constexpr const char* GetStaticTypeName() { \
41 return TypeName; \ 41 return TypeName; \
42 } \ 42 } \
43 virtual TypeObj GetTypeObj() const { \ 43 virtual TypeObj GetTypeObj() ATTRIBUTE { \
44 return GetStaticTypeObj(); \ 44 return GetStaticTypeObj(); \
45 } \ 45 } \
46 virtual const char* GetTypeName() const { \ 46 virtual const char* GetTypeName() ATTRIBUTE { \
47 return GetStaticTypeName(); \ 47 return GetStaticTypeName(); \
48 } \ 48 } \
49 \ 49 \
50private: \ 50private: \
51 constexpr bool operator!=(const TypeObj& rhs) 51 constexpr bool operator!=(const TypeObj& rhs)
52 52
53#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
54 KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override)
55
53class KAutoObject { 56class KAutoObject {
54protected: 57protected:
55 class TypeObj { 58 class TypeObj {
@@ -82,7 +85,7 @@ protected:
82 }; 85 };
83 86
84private: 87private:
85 KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); 88 KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);
86 89
87public: 90public:
88 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { 91 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h
index be9e3c357..c9001ae3d 100644
--- a/src/core/hle/kernel/k_class_token.h
+++ b/src/core/hle/kernel/k_class_token.h
@@ -49,6 +49,7 @@ private:
49 } 49 }
50 } 50 }
51 } 51 }
52 UNREACHABLE();
52 }(); 53 }();
53 54
54 template <typename T> 55 template <typename T>
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index fd3cbfd94..4ae40ec8e 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -27,23 +27,18 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
27 auto& page_table = m_owner->PageTable(); 27 auto& page_table = m_owner->PageTable();
28 28
29 // Construct the page group. 29 // Construct the page group.
30 m_page_group = 30 m_page_group = {};
31 KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
32 31
33 // Lock the memory. 32 // Lock the memory.
34 R_TRY(page_table.LockForCodeMemory(addr, size)) 33 R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
35 34
36 // Clear the memory. 35 // Clear the memory.
37 // 36 for (const auto& block : m_page_group.Nodes()) {
38 // FIXME: this ends up clobbering address ranges outside the scope of the mapping within 37 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
39 // guest memory, and is not specifically required if the guest program is correctly 38 }
40 // written, so disable until this is further investigated.
41 //
42 // for (const auto& block : m_page_group.Nodes()) {
43 // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
44 // }
45 39
46 // Set remaining tracking members. 40 // Set remaining tracking members.
41 m_owner->Open();
47 m_address = addr; 42 m_address = addr;
48 m_is_initialized = true; 43 m_is_initialized = true;
49 m_is_owner_mapped = false; 44 m_is_owner_mapped = false;
@@ -57,8 +52,14 @@ void KCodeMemory::Finalize() {
57 // Unlock. 52 // Unlock.
58 if (!m_is_mapped && !m_is_owner_mapped) { 53 if (!m_is_mapped && !m_is_owner_mapped) {
59 const size_t size = m_page_group.GetNumPages() * PageSize; 54 const size_t size = m_page_group.GetNumPages() * PageSize;
60 m_owner->PageTable().UnlockForCodeMemory(m_address, size); 55 m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
61 } 56 }
57
58 // Close the page group.
59 m_page_group = {};
60
61 // Close our reference to our owner.
62 m_owner->Close();
62} 63}
63 64
64ResultCode KCodeMemory::Map(VAddr address, size_t size) { 65ResultCode KCodeMemory::Map(VAddr address, size_t size) {
@@ -118,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis
118 k_perm = KMemoryPermission::UserReadExecute; 119 k_perm = KMemoryPermission::UserReadExecute;
119 break; 120 break;
120 default: 121 default:
121 break; 122 // Already validated by ControlCodeMemory svc
123 UNREACHABLE();
122 } 124 }
123 125
124 // Map the memory. 126 // Map the memory.
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index a55db3088..58e540f31 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -29,7 +29,7 @@ constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
29 } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) { 29 } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
30 return KMemoryManager::Pool::SystemNonSecure; 30 return KMemoryManager::Pool::SystemNonSecure;
31 } else { 31 } else {
32 UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool"); 32 ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
33 return {}; 33 return {};
34 } 34 }
35} 35}
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index b38ef333b..504e22cb9 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -35,7 +35,7 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
35 case FileSys::ProgramAddressSpaceType::Is39Bit: 35 case FileSys::ProgramAddressSpaceType::Is39Bit:
36 return 39; 36 return 39;
37 default: 37 default:
38 UNREACHABLE(); 38 ASSERT(false);
39 return {}; 39 return {};
40 } 40 }
41} 41}
@@ -128,7 +128,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
128 const std::size_t needed_size{ 128 const std::size_t needed_size{
129 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; 129 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
130 if (alloc_size < needed_size) { 130 if (alloc_size < needed_size) {
131 UNREACHABLE(); 131 ASSERT(false);
132 return ResultOutOfMemory; 132 return ResultOutOfMemory;
133 } 133 }
134 134
@@ -542,6 +542,95 @@ ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num
542 return ResultSuccess; 542 return ResultSuccess;
543} 543}
544 544
545bool KPageTable::IsValidPageGroup(const KPageLinkedList& pg_ll, VAddr addr, size_t num_pages) {
546 ASSERT(this->IsLockedByCurrentThread());
547
548 const size_t size = num_pages * PageSize;
549 const auto& pg = pg_ll.Nodes();
550 const auto& memory_layout = system.Kernel().MemoryLayout();
551
552 // Empty groups are necessarily invalid.
553 if (pg.empty()) {
554 return false;
555 }
556
557 // We're going to validate that the group we'd expect is the group we see.
558 auto cur_it = pg.begin();
559 PAddr cur_block_address = cur_it->GetAddress();
560 size_t cur_block_pages = cur_it->GetNumPages();
561
562 auto UpdateCurrentIterator = [&]() {
563 if (cur_block_pages == 0) {
564 if ((++cur_it) == pg.end()) {
565 return false;
566 }
567
568 cur_block_address = cur_it->GetAddress();
569 cur_block_pages = cur_it->GetNumPages();
570 }
571 return true;
572 };
573
574 // Begin traversal.
575 Common::PageTable::TraversalContext context;
576 Common::PageTable::TraversalEntry next_entry;
577 if (!page_table_impl.BeginTraversal(next_entry, context, addr)) {
578 return false;
579 }
580
581 // Prepare tracking variables.
582 PAddr cur_addr = next_entry.phys_addr;
583 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
584 size_t tot_size = cur_size;
585
586 // Iterate, comparing expected to actual.
587 while (tot_size < size) {
588 if (!page_table_impl.ContinueTraversal(next_entry, context)) {
589 return false;
590 }
591
592 if (next_entry.phys_addr != (cur_addr + cur_size)) {
593 const size_t cur_pages = cur_size / PageSize;
594
595 if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
596 return false;
597 }
598
599 if (!UpdateCurrentIterator()) {
600 return false;
601 }
602
603 if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
604 return false;
605 }
606
607 cur_block_address += cur_size;
608 cur_block_pages -= cur_pages;
609 cur_addr = next_entry.phys_addr;
610 cur_size = next_entry.block_size;
611 } else {
612 cur_size += next_entry.block_size;
613 }
614
615 tot_size += next_entry.block_size;
616 }
617
618 // Ensure we compare the right amount for the last block.
619 if (tot_size > size) {
620 cur_size -= (tot_size - size);
621 }
622
623 if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
624 return false;
625 }
626
627 if (!UpdateCurrentIterator()) {
628 return false;
629 }
630
631 return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
632}
633
545ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 634ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
546 KPageTable& src_page_table, VAddr src_addr) { 635 KPageTable& src_page_table, VAddr src_addr) {
547 KScopedLightLock lk(general_lock); 636 KScopedLightLock lk(general_lock);
@@ -1341,7 +1430,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1341 new_state = KMemoryState::AliasCodeData; 1430 new_state = KMemoryState::AliasCodeData;
1342 break; 1431 break;
1343 default: 1432 default:
1344 UNREACHABLE(); 1433 ASSERT(false);
1345 } 1434 }
1346 } 1435 }
1347 1436
@@ -1687,22 +1776,22 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1687 return ResultSuccess; 1776 return ResultSuccess;
1688} 1777}
1689 1778
1690ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1779ResultCode KPageTable::LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size) {
1691 return this->LockMemoryAndOpen( 1780 return this->LockMemoryAndOpen(
1692 nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, 1781 out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
1693 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, 1782 KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
1694 KMemoryAttribute::All, KMemoryAttribute::None, 1783 KMemoryAttribute::None,
1695 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | 1784 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1696 KMemoryPermission::KernelReadWrite), 1785 KMemoryPermission::KernelReadWrite),
1697 KMemoryAttribute::Locked); 1786 KMemoryAttribute::Locked);
1698} 1787}
1699 1788
1700ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1789ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size,
1701 return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory, 1790 const KPageLinkedList& pg) {
1702 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, 1791 return this->UnlockMemory(
1703 KMemoryPermission::None, KMemoryAttribute::All, 1792 addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
1704 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, 1793 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
1705 KMemoryAttribute::Locked, nullptr); 1794 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg);
1706} 1795}
1707 1796
1708ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1797ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1734,9 +1823,7 @@ void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
1734 VAddr addr{start}; 1823 VAddr addr{start};
1735 while (addr < start + (num_pages * PageSize)) { 1824 while (addr < start + (num_pages * PageSize)) {
1736 const PAddr paddr{GetPhysicalAddr(addr)}; 1825 const PAddr paddr{GetPhysicalAddr(addr)};
1737 if (!paddr) { 1826 ASSERT(paddr != 0);
1738 UNREACHABLE();
1739 }
1740 page_linked_list.AddBlock(paddr, 1); 1827 page_linked_list.AddBlock(paddr, 1);
1741 addr += PageSize; 1828 addr += PageSize;
1742 } 1829 }
@@ -1767,7 +1854,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
1767 system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress()); 1854 system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress());
1768 break; 1855 break;
1769 default: 1856 default:
1770 UNREACHABLE(); 1857 ASSERT(false);
1771 } 1858 }
1772 1859
1773 addr += size; 1860 addr += size;
@@ -1798,7 +1885,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
1798 case OperationType::ChangePermissionsAndRefresh: 1885 case OperationType::ChangePermissionsAndRefresh:
1799 break; 1886 break;
1800 default: 1887 default:
1801 UNREACHABLE(); 1888 ASSERT(false);
1802 } 1889 }
1803 return ResultSuccess; 1890 return ResultSuccess;
1804} 1891}
@@ -1835,7 +1922,6 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
1835 return code_region_start; 1922 return code_region_start;
1836 default: 1923 default:
1837 UNREACHABLE(); 1924 UNREACHABLE();
1838 return {};
1839 } 1925 }
1840} 1926}
1841 1927
@@ -1871,7 +1957,6 @@ std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
1871 return code_region_end - code_region_start; 1957 return code_region_end - code_region_start;
1872 default: 1958 default:
1873 UNREACHABLE(); 1959 UNREACHABLE();
1874 return {};
1875 } 1960 }
1876} 1961}
1877 1962
@@ -2125,7 +2210,7 @@ ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_
2125 2210
2126 // Check the page group. 2211 // Check the page group.
2127 if (pg != nullptr) { 2212 if (pg != nullptr) {
2128 UNIMPLEMENTED_MSG("PageGroup support is unimplemented!"); 2213 R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
2129 } 2214 }
2130 2215
2131 // Decide on new perm and attr. 2216 // Decide on new perm and attr.
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 52a93ce86..6312eb682 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -72,8 +72,8 @@ public:
72 KMemoryPermission perm, PAddr map_addr = 0); 72 KMemoryPermission perm, PAddr map_addr = 0);
73 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); 73 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
74 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 74 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
75 ResultCode LockForCodeMemory(VAddr addr, std::size_t size); 75 ResultCode LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size);
76 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); 76 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageLinkedList& pg);
77 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages, 77 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
78 KMemoryState state_mask, KMemoryState state, 78 KMemoryState state_mask, KMemoryState state,
79 KMemoryPermission perm_mask, KMemoryPermission perm, 79 KMemoryPermission perm_mask, KMemoryPermission perm,
@@ -178,6 +178,7 @@ private:
178 const KPageLinkedList* pg); 178 const KPageLinkedList* pg);
179 179
180 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages); 180 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
181 bool IsValidPageGroup(const KPageLinkedList& pg, VAddr addr, size_t num_pages);
181 182
182 bool IsLockedByCurrentThread() const { 183 bool IsLockedByCurrentThread() const {
183 return general_lock.IsLockedByCurrentThread(); 184 return general_lock.IsLockedByCurrentThread();
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index a31861cdb..51c2cd1ef 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -60,7 +60,7 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
60 if (auto session_ptr = server.GetSessionRequestHandler().lock()) { 60 if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
61 session_ptr->ClientConnected(server.AcceptSession()); 61 session_ptr->ClientConnected(server.AcceptSession());
62 } else { 62 } else {
63 UNREACHABLE(); 63 ASSERT(false);
64 } 64 }
65 65
66 return ResultSuccess; 66 return ResultSuccess;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index dcfeacccd..8c79b4f0f 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -350,7 +350,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
350 break; 350 break;
351 351
352 default: 352 default:
353 UNREACHABLE(); 353 ASSERT(false);
354 } 354 }
355 355
356 // Create TLS region 356 // Create TLS region
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 7e39f6d50..60f8ed470 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -97,13 +97,13 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
97 "object_id {} is too big! This probably means a recent service call " 97 "object_id {} is too big! This probably means a recent service call "
98 "to {} needed to return a new interface!", 98 "to {} needed to return a new interface!",
99 object_id, name); 99 object_id, name);
100 UNREACHABLE(); 100 ASSERT(false);
101 return ResultSuccess; // Ignore error if asserts are off 101 return ResultSuccess; // Ignore error if asserts are off
102 } 102 }
103 if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) { 103 if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
104 return strong_ptr->HandleSyncRequest(*this, context); 104 return strong_ptr->HandleSyncRequest(*this, context);
105 } else { 105 } else {
106 UNREACHABLE(); 106 ASSERT(false);
107 return ResultSuccess; 107 return ResultSuccess;
108 } 108 }
109 109
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 940334f59..ea2160099 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -133,7 +133,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
133 UNIMPLEMENTED(); 133 UNIMPLEMENTED();
134 break; 134 break;
135 default: 135 default:
136 UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); 136 ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
137 break; 137 break;
138 } 138 }
139 thread_type = type; 139 thread_type = type;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 92f6d8c49..b2c4f12b4 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -212,7 +212,9 @@ struct KernelCore::Impl {
212 system_resource_limit = KResourceLimit::Create(system.Kernel()); 212 system_resource_limit = KResourceLimit::Create(system.Kernel());
213 system_resource_limit->Initialize(&core_timing); 213 system_resource_limit->Initialize(&core_timing);
214 214
215 const auto [total_size, kernel_size] = memory_layout->GetTotalAndKernelMemorySizes(); 215 const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
216 const auto total_size{sizes.first};
217 const auto kernel_size{sizes.second};
216 218
217 // If setting the default system values fails, then something seriously wrong has occurred. 219 // If setting the default system values fails, then something seriously wrong has occurred.
218 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) 220 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
@@ -252,6 +254,7 @@ struct KernelCore::Impl {
252 core_id) 254 core_id)
253 .IsSuccess()); 255 .IsSuccess());
254 suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id)); 256 suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
257 suspend_threads[core_id]->DisableDispatch();
255 } 258 }
256 } 259 }
257 260
@@ -1073,9 +1076,6 @@ void KernelCore::Suspend(bool in_suspention) {
1073 impl->suspend_threads[core_id]->SetState(state); 1076 impl->suspend_threads[core_id]->SetState(state);
1074 impl->suspend_threads[core_id]->SetWaitReasonForDebugging( 1077 impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
1075 ThreadWaitReasonForDebugging::Suspended); 1078 ThreadWaitReasonForDebugging::Suspended);
1076 if (!should_suspend) {
1077 impl->suspend_threads[core_id]->DisableDispatch();
1078 }
1079 } 1079 }
1080 } 1080 }
1081} 1081}
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 66e0ce2d0..584fa5b1c 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1876,7 +1876,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
1876 KScheduler::YieldToAnyThread(kernel); 1876 KScheduler::YieldToAnyThread(kernel);
1877 } else { 1877 } else {
1878 // Nintendo does nothing at all if an otherwise invalid value is passed. 1878 // Nintendo does nothing at all if an otherwise invalid value is passed.
1879 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); 1879 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1880 } 1880 }
1881} 1881}
1882 1882
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index 655f2e936..0a5603d18 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -178,7 +178,7 @@ ResultCode Controller::GetStatus() const {
178} 178}
179 179
180void Controller::ExecuteInteractive() { 180void Controller::ExecuteInteractive() {
181 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); 181 ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
182} 182}
183 183
184void Controller::Execute() { 184void Controller::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index 911b2c229..0b87c60b9 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -156,7 +156,7 @@ ResultCode Error::GetStatus() const {
156} 156}
157 157
158void Error::ExecuteInteractive() { 158void Error::ExecuteInteractive() {
159 UNREACHABLE_MSG("Unexpected interactive applet data!"); 159 ASSERT_MSG(false, "Unexpected interactive applet data!");
160} 160}
161 161
162void Error::Execute() { 162void Error::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp
index 3fe1a390a..41c002ef2 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/applets/applet_general_backend.cpp
@@ -76,7 +76,7 @@ ResultCode Auth::GetStatus() const {
76} 76}
77 77
78void Auth::ExecuteInteractive() { 78void Auth::ExecuteInteractive() {
79 UNREACHABLE_MSG("Unexpected interactive applet data."); 79 ASSERT_MSG(false, "Unexpected interactive applet data.");
80} 80}
81 81
82void Auth::Execute() { 82void Auth::Execute() {
@@ -175,7 +175,7 @@ ResultCode PhotoViewer::GetStatus() const {
175} 175}
176 176
177void PhotoViewer::ExecuteInteractive() { 177void PhotoViewer::ExecuteInteractive() {
178 UNREACHABLE_MSG("Unexpected interactive applet data."); 178 ASSERT_MSG(false, "Unexpected interactive applet data.");
179} 179}
180 180
181void PhotoViewer::Execute() { 181void PhotoViewer::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp
index 3acde1630..8d847c3f6 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit.cpp
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -67,7 +67,7 @@ ResultCode MiiEdit::GetStatus() const {
67} 67}
68 68
69void MiiEdit::ExecuteInteractive() { 69void MiiEdit::ExecuteInteractive() {
70 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); 70 ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
71} 71}
72 72
73void MiiEdit::Execute() { 73void MiiEdit::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp
index fd16f2e49..02049fd9f 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.cpp
+++ b/src/core/hle/service/am/applets/applet_profile_select.cpp
@@ -44,7 +44,7 @@ ResultCode ProfileSelect::GetStatus() const {
44} 44}
45 45
46void ProfileSelect::ExecuteInteractive() { 46void ProfileSelect::ExecuteInteractive() {
47 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); 47 ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
48} 48}
49 49
50void ProfileSelect::Execute() { 50void ProfileSelect::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index 7c21365e4..4116fbaa7 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -71,7 +71,7 @@ void SoftwareKeyboard::Initialize() {
71 InitializeBackground(applet_mode); 71 InitializeBackground(applet_mode);
72 break; 72 break;
73 default: 73 default:
74 UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); 74 ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
75 break; 75 break;
76 } 76 }
77} 77}
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index 2aa4a00ad..7b3f77a51 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -279,7 +279,7 @@ void WebBrowser::Initialize() {
279 InitializeLobby(); 279 InitializeLobby();
280 break; 280 break;
281 default: 281 default:
282 UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); 282 ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
283 break; 283 break;
284 } 284 }
285} 285}
@@ -320,7 +320,7 @@ void WebBrowser::Execute() {
320 ExecuteLobby(); 320 ExecuteLobby();
321 break; 321 break;
322 default: 322 default:
323 UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); 323 ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
324 WebBrowserExit(WebExitReason::EndButtonPressed); 324 WebBrowserExit(WebExitReason::EndButtonPressed);
325 break; 325 break;
326 } 326 }
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index ddfcba0f1..fae6e5aff 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -899,7 +899,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
899 case FileSys::SaveDataSpaceId::TemporaryStorage: 899 case FileSys::SaveDataSpaceId::TemporaryStorage:
900 case FileSys::SaveDataSpaceId::ProperSystem: 900 case FileSys::SaveDataSpaceId::ProperSystem:
901 case FileSys::SaveDataSpaceId::SafeMode: 901 case FileSys::SaveDataSpaceId::SafeMode:
902 UNREACHABLE(); 902 ASSERT(false);
903 } 903 }
904 904
905 auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()), 905 auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()),
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 3eae1ae35..32e0708ba 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -61,6 +61,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
61 } 61 }
62 62
63 last_update_timestamp = shared_memory->gesture_lifo.timestamp; 63 last_update_timestamp = shared_memory->gesture_lifo.timestamp;
64 UpdateGestureSharedMemory(gesture, time_difference);
64} 65}
65 66
66void Controller_Gesture::ReadTouchInput() { 67void Controller_Gesture::ReadTouchInput() {
@@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
94 return false; 95 return false;
95} 96}
96 97
97void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, 98void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
98 GestureProperties& gesture,
99 f32 time_difference) { 99 f32 time_difference) {
100 GestureType type = GestureType::Idle; 100 GestureType type = GestureType::Idle;
101 GestureAttribute attributes{}; 101 GestureAttribute attributes{};
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index c62a341bf..0d6099ea0 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -107,8 +107,7 @@ private:
107 bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); 107 bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
108 108
109 // Updates the shared memory to the next state 109 // Updates the shared memory to the next state
110 void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture, 110 void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
111 f32 time_difference);
112 111
113 // Initializes new gesture 112 // Initializes new gesture
114 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); 113 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 1e04ee3f2..ac5c38cc6 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -160,7 +160,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
160 shared_memory->system_properties.raw = 0; 160 shared_memory->system_properties.raw = 0;
161 switch (controller_type) { 161 switch (controller_type) {
162 case Core::HID::NpadStyleIndex::None: 162 case Core::HID::NpadStyleIndex::None:
163 UNREACHABLE(); 163 ASSERT(false);
164 break; 164 break;
165 case Core::HID::NpadStyleIndex::ProController: 165 case Core::HID::NpadStyleIndex::ProController:
166 shared_memory->style_tag.fullkey.Assign(1); 166 shared_memory->style_tag.fullkey.Assign(1);
@@ -422,7 +422,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
422 libnx_state.connection_status.is_connected.Assign(1); 422 libnx_state.connection_status.is_connected.Assign(1);
423 switch (controller_type) { 423 switch (controller_type) {
424 case Core::HID::NpadStyleIndex::None: 424 case Core::HID::NpadStyleIndex::None:
425 UNREACHABLE(); 425 ASSERT(false);
426 break; 426 break;
427 case Core::HID::NpadStyleIndex::ProController: 427 case Core::HID::NpadStyleIndex::ProController:
428 case Core::HID::NpadStyleIndex::NES: 428 case Core::HID::NpadStyleIndex::NES:
@@ -597,7 +597,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
597 597
598 switch (controller_type) { 598 switch (controller_type) {
599 case Core::HID::NpadStyleIndex::None: 599 case Core::HID::NpadStyleIndex::None:
600 UNREACHABLE(); 600 ASSERT(false);
601 break; 601 break;
602 case Core::HID::NpadStyleIndex::ProController: 602 case Core::HID::NpadStyleIndex::ProController:
603 case Core::HID::NpadStyleIndex::Pokeball: 603 case Core::HID::NpadStyleIndex::Pokeball:
@@ -856,7 +856,7 @@ void Controller_NPad::VibrateController(
856 } 856 }
857 857
858 if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) { 858 if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
859 UNREACHABLE_MSG("DeviceIndex should never be None!"); 859 ASSERT_MSG(false, "DeviceIndex should never be None!");
860 return; 860 return;
861 } 861 }
862 862
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8a496c38c..dc5d0366d 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1441,7 +1441,7 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
1441 break; 1441 break;
1442 case Core::HID::DeviceIndex::None: 1442 case Core::HID::DeviceIndex::None:
1443 default: 1443 default:
1444 UNREACHABLE_MSG("DeviceIndex should never be None!"); 1444 ASSERT_MSG(false, "DeviceIndex should never be None!");
1445 vibration_device_info.position = Core::HID::VibrationDevicePosition::None; 1445 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1446 break; 1446 break;
1447 } 1447 }
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp
index 19bd85b6c..4ed3f02e2 100644
--- a/src/core/hle/service/jit/jit_context.cpp
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -11,10 +11,13 @@
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/div_ceil.h" 13#include "common/div_ceil.h"
14#include "common/elf.h"
14#include "common/logging/log.h" 15#include "common/logging/log.h"
15#include "core/hle/service/jit/jit_context.h" 16#include "core/hle/service/jit/jit_context.h"
16#include "core/memory.h" 17#include "core/memory.h"
17 18
19using namespace Common::ELF;
20
18namespace Service::JIT { 21namespace Service::JIT {
19 22
20constexpr std::array<u8, 8> SVC0_ARM64 = { 23constexpr std::array<u8, 8> SVC0_ARM64 = {
@@ -26,25 +29,6 @@ constexpr std::array HELPER_FUNCTIONS{
26 "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset", 29 "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",
27}; 30};
28 31
29struct Elf64_Dyn {
30 u64 d_tag;
31 u64 d_un;
32};
33
34struct Elf64_Rela {
35 u64 r_offset;
36 u64 r_info;
37 s64 r_addend;
38};
39
40static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
41 return static_cast<u32>(rela->r_info);
42}
43
44constexpr int DT_RELA = 7; /* Address of Rela relocs */
45constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
46constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
47
48constexpr size_t STACK_ALIGN = 16; 32constexpr size_t STACK_ALIGN = 16;
49 33
50class JITContextImpl; 34class JITContextImpl;
@@ -206,17 +190,17 @@ public:
206 if (!dyn.d_tag) { 190 if (!dyn.d_tag) {
207 break; 191 break;
208 } 192 }
209 if (dyn.d_tag == DT_RELA) { 193 if (dyn.d_tag == ElfDtRela) {
210 rela_dyn = dyn.d_un; 194 rela_dyn = dyn.d_un.d_ptr;
211 } 195 }
212 if (dyn.d_tag == DT_RELASZ) { 196 if (dyn.d_tag == ElfDtRelasz) {
213 num_rela = dyn.d_un / sizeof(Elf64_Rela); 197 num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
214 } 198 }
215 } 199 }
216 200
217 for (size_t i = 0; i < num_rela; i++) { 201 for (size_t i = 0; i < num_rela; i++) {
218 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; 202 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
219 if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) { 203 if (Elf64RelType(rela.r_info) != ElfAArch64Relative) {
220 continue; 204 continue;
221 } 205 }
222 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; 206 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index fa72fcba9..72e4902cb 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -347,7 +347,7 @@ public:
347 } 347 }
348 348
349 if (!succeeded) { 349 if (!succeeded) {
350 UNREACHABLE_MSG("Out of address space!"); 350 ASSERT_MSG(false, "Out of address space!");
351 return Kernel::ResultOutOfMemory; 351 return Kernel::ResultOutOfMemory;
352 } 352 }
353 353
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 4964539f9..08300a1a6 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -290,7 +290,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo
290 u8 glasses_type{}; 290 u8 glasses_type{};
291 while (glasses_type_start < glasses_type_info.values[glasses_type]) { 291 while (glasses_type_start < glasses_type_info.values[glasses_type]) {
292 if (++glasses_type >= glasses_type_info.values_count) { 292 if (++glasses_type >= glasses_type_info.values_count) {
293 UNREACHABLE(); 293 ASSERT(false);
294 break; 294 break;
295 } 295 }
296 } 296 }
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
index f77f0df27..a6fa943e8 100644
--- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
@@ -23,7 +23,7 @@ u32 SyncpointManager::AllocateSyncpoint() {
23 return syncpoint_id; 23 return syncpoint_id;
24 } 24 }
25 } 25 }
26 UNREACHABLE_MSG("No more available syncpoints!"); 26 ASSERT_MSG(false, "No more available syncpoints!");
27 return {}; 27 return {};
28} 28}
29 29
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index d7db77aff..4b3d5efd6 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -89,14 +89,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
89 89
90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); 90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
91 91
92 // If the front buffer is still being tracked, update its slot state
93 if (core->StillTracking(*front)) {
94 slots[slot].acquire_called = true;
95 slots[slot].needs_cleanup_on_release = false;
96 slots[slot].buffer_state = BufferState::Acquired;
97 slots[slot].fence = Fence::NoFence();
98 }
99
100 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to 92 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
101 // avoid unnecessarily remapping this buffer on the consumer side. 93 // avoid unnecessarily remapping this buffer on the consumer side.
102 if (out_buffer->acquire_called) { 94 if (out_buffer->acquire_called) {
@@ -139,26 +131,11 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
139 ++current; 131 ++current;
140 } 132 }
141 133
142 if (slots[slot].buffer_state == BufferState::Acquired) { 134 slots[slot].buffer_state = BufferState::Free;
143 slots[slot].fence = release_fence;
144 slots[slot].buffer_state = BufferState::Free;
145
146 listener = core->connected_producer_listener;
147
148 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
149 } else if (slots[slot].needs_cleanup_on_release) {
150 LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
151 slots[slot].buffer_state);
152 135
153 slots[slot].needs_cleanup_on_release = false; 136 listener = core->connected_producer_listener;
154 137
155 return Status::StaleBufferSlot; 138 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
156 } else {
157 LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
158 slot, slots[slot].buffer_state);
159
160 return Status::BadValue;
161 }
162 139
163 core->SignalDequeueCondition(); 140 core->SignalDequeueCondition();
164 } 141 }
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
index d4e8b44d0..ea4a14ea4 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -84,10 +84,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
84 84
85 slots[slot].graphic_buffer.reset(); 85 slots[slot].graphic_buffer.reset();
86 86
87 if (slots[slot].buffer_state == BufferState::Acquired) {
88 slots[slot].needs_cleanup_on_release = true;
89 }
90
91 slots[slot].buffer_state = BufferState::Free; 87 slots[slot].buffer_state = BufferState::Free;
92 slots[slot].frame_number = UINT32_MAX; 88 slots[slot].frame_number = UINT32_MAX;
93 slots[slot].acquire_called = false; 89 slots[slot].acquire_called = false;
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index fe95d1b73..337431488 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -659,7 +659,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
659 value = core->consumer_usage_bit; 659 value = core->consumer_usage_bit;
660 break; 660 break;
661 default: 661 default:
662 UNREACHABLE(); 662 ASSERT(false);
663 return Status::BadValue; 663 return Status::BadValue;
664 } 664 }
665 665
diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h
index 6b3e87446..0cd0e9964 100644
--- a/src/core/hle/service/nvflinger/buffer_slot.h
+++ b/src/core/hle/service/nvflinger/buffer_slot.h
@@ -31,7 +31,6 @@ struct BufferSlot final {
31 u64 frame_number{}; 31 u64 frame_number{};
32 Fence fence; 32 Fence fence;
33 bool acquire_called{}; 33 bool acquire_called{};
34 bool needs_cleanup_on_release{};
35 bool attached_by_consumer{}; 34 bool attached_by_consumer{};
36 bool is_preallocated{}; 35 bool is_preallocated{};
37}; 36};
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
index f0cc9a155..508091dc2 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.cpp
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -48,12 +48,12 @@ ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
48} 48}
49 49
50ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) { 50ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) {
51 UNREACHABLE(); 51 UNIMPLEMENTED();
52 return ERROR_NOT_IMPLEMENTED; 52 return ERROR_NOT_IMPLEMENTED;
53} 53}
54 54
55ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { 55ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
56 UNREACHABLE(); 56 UNIMPLEMENTED();
57 return ERROR_NOT_IMPLEMENTED; 57 return ERROR_NOT_IMPLEMENTED;
58} 58}
59 59
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index acc038dbf..28667710e 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -111,7 +111,7 @@ struct TimeManager::Impl final {
111 FileSys::VirtualFile& vfs_file) { 111 FileSys::VirtualFile& vfs_file) {
112 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( 112 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
113 location_name, vfs_file) != ResultSuccess) { 113 location_name, vfs_file) != ResultSuccess) {
114 UNREACHABLE(); 114 ASSERT(false);
115 return; 115 return;
116 } 116 }
117 117
@@ -155,7 +155,7 @@ struct TimeManager::Impl final {
155 } else { 155 } else {
156 if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) != 156 if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) !=
157 ResultSuccess) { 157 ResultSuccess) {
158 UNREACHABLE(); 158 ASSERT(false);
159 return; 159 return;
160 } 160 }
161 } 161 }
@@ -170,7 +170,7 @@ struct TimeManager::Impl final {
170 170
171 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != 171 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
172 ResultSuccess) { 172 ResultSuccess) {
173 UNREACHABLE(); 173 ASSERT(false);
174 return; 174 return;
175 } 175 }
176 176
@@ -183,7 +183,7 @@ struct TimeManager::Impl final {
183 Clock::SteadyClockTimePoint steady_clock_time_point) { 183 Clock::SteadyClockTimePoint steady_clock_time_point) {
184 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled( 184 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
185 system_, is_automatic_correction_enabled) != ResultSuccess) { 185 system_, is_automatic_correction_enabled) != ResultSuccess) {
186 UNREACHABLE(); 186 ASSERT(false);
187 return; 187 return;
188 } 188 }
189 189
@@ -203,7 +203,7 @@ struct TimeManager::Impl final {
203 if (GetStandardLocalSystemClockCore() 203 if (GetStandardLocalSystemClockCore()
204 .SetCurrentTime(system_, timespan.ToSeconds()) 204 .SetCurrentTime(system_, timespan.ToSeconds())
205 .IsError()) { 205 .IsError()) {
206 UNREACHABLE(); 206 ASSERT(false);
207 return; 207 return;
208 } 208 }
209 } 209 }
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index eeec34436..fee05ec7a 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -279,7 +279,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
279 break; 279 break;
280 } 280 }
281 default: 281 default:
282 UNREACHABLE(); 282 ASSERT(false);
283 } 283 }
284 return value + rule.transition_time + offset; 284 return value + rule.transition_time + offset;
285} 285}
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index cf5933699..dfb10c34f 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/elf.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "core/hle/kernel/code_set.h" 11#include "core/hle/kernel/code_set.h"
11#include "core/hle/kernel/k_page_table.h" 12#include "core/hle/kernel/k_page_table.h"
@@ -13,159 +14,7 @@
13#include "core/loader/elf.h" 14#include "core/loader/elf.h"
14#include "core/memory.h" 15#include "core/memory.h"
15 16
16//////////////////////////////////////////////////////////////////////////////////////////////////// 17using namespace Common::ELF;
17// ELF Header Constants
18
19// File type
20enum ElfType {
21 ET_NONE = 0,
22 ET_REL = 1,
23 ET_EXEC = 2,
24 ET_DYN = 3,
25 ET_CORE = 4,
26 ET_LOPROC = 0xFF00,
27 ET_HIPROC = 0xFFFF,
28};
29
30// Machine/Architecture
31enum ElfMachine {
32 EM_NONE = 0,
33 EM_M32 = 1,
34 EM_SPARC = 2,
35 EM_386 = 3,
36 EM_68K = 4,
37 EM_88K = 5,
38 EM_860 = 7,
39 EM_MIPS = 8
40};
41
42// File version
43#define EV_NONE 0
44#define EV_CURRENT 1
45
46// Identification index
47#define EI_MAG0 0
48#define EI_MAG1 1
49#define EI_MAG2 2
50#define EI_MAG3 3
51#define EI_CLASS 4
52#define EI_DATA 5
53#define EI_VERSION 6
54#define EI_PAD 7
55#define EI_NIDENT 16
56
57// Sections constants
58
59// Section types
60#define SHT_NULL 0
61#define SHT_PROGBITS 1
62#define SHT_SYMTAB 2
63#define SHT_STRTAB 3
64#define SHT_RELA 4
65#define SHT_HASH 5
66#define SHT_DYNAMIC 6
67#define SHT_NOTE 7
68#define SHT_NOBITS 8
69#define SHT_REL 9
70#define SHT_SHLIB 10
71#define SHT_DYNSYM 11
72#define SHT_LOPROC 0x70000000
73#define SHT_HIPROC 0x7FFFFFFF
74#define SHT_LOUSER 0x80000000
75#define SHT_HIUSER 0xFFFFFFFF
76
77// Section flags
78enum ElfSectionFlags {
79 SHF_WRITE = 0x1,
80 SHF_ALLOC = 0x2,
81 SHF_EXECINSTR = 0x4,
82 SHF_MASKPROC = 0xF0000000,
83};
84
85// Segment types
86#define PT_NULL 0
87#define PT_LOAD 1
88#define PT_DYNAMIC 2
89#define PT_INTERP 3
90#define PT_NOTE 4
91#define PT_SHLIB 5
92#define PT_PHDR 6
93#define PT_LOPROC 0x70000000
94#define PT_HIPROC 0x7FFFFFFF
95
96// Segment flags
97#define PF_X 0x1
98#define PF_W 0x2
99#define PF_R 0x4
100#define PF_MASKPROC 0xF0000000
101
102typedef unsigned int Elf32_Addr;
103typedef unsigned short Elf32_Half;
104typedef unsigned int Elf32_Off;
105typedef signed int Elf32_Sword;
106typedef unsigned int Elf32_Word;
107
108////////////////////////////////////////////////////////////////////////////////////////////////////
109// ELF file header
110
111struct Elf32_Ehdr {
112 unsigned char e_ident[EI_NIDENT];
113 Elf32_Half e_type;
114 Elf32_Half e_machine;
115 Elf32_Word e_version;
116 Elf32_Addr e_entry;
117 Elf32_Off e_phoff;
118 Elf32_Off e_shoff;
119 Elf32_Word e_flags;
120 Elf32_Half e_ehsize;
121 Elf32_Half e_phentsize;
122 Elf32_Half e_phnum;
123 Elf32_Half e_shentsize;
124 Elf32_Half e_shnum;
125 Elf32_Half e_shstrndx;
126};
127
128// Section header
129struct Elf32_Shdr {
130 Elf32_Word sh_name;
131 Elf32_Word sh_type;
132 Elf32_Word sh_flags;
133 Elf32_Addr sh_addr;
134 Elf32_Off sh_offset;
135 Elf32_Word sh_size;
136 Elf32_Word sh_link;
137 Elf32_Word sh_info;
138 Elf32_Word sh_addralign;
139 Elf32_Word sh_entsize;
140};
141
142// Segment header
143struct Elf32_Phdr {
144 Elf32_Word p_type;
145 Elf32_Off p_offset;
146 Elf32_Addr p_vaddr;
147 Elf32_Addr p_paddr;
148 Elf32_Word p_filesz;
149 Elf32_Word p_memsz;
150 Elf32_Word p_flags;
151 Elf32_Word p_align;
152};
153
154// Symbol table entry
155struct Elf32_Sym {
156 Elf32_Word st_name;
157 Elf32_Addr st_value;
158 Elf32_Word st_size;
159 unsigned char st_info;
160 unsigned char st_other;
161 Elf32_Half st_shndx;
162};
163
164// Relocation entries
165struct Elf32_Rel {
166 Elf32_Addr r_offset;
167 Elf32_Word r_info;
168};
169 18
170//////////////////////////////////////////////////////////////////////////////////////////////////// 19////////////////////////////////////////////////////////////////////////////////////////////////////
171// ElfReader class 20// ElfReader class
@@ -193,11 +42,11 @@ public:
193 } 42 }
194 43
195 // Quick accessors 44 // Quick accessors
196 ElfType GetType() const { 45 u16 GetType() const {
197 return (ElfType)(header->e_type); 46 return header->e_type;
198 } 47 }
199 ElfMachine GetMachine() const { 48 u16 GetMachine() const {
200 return (ElfMachine)(header->e_machine); 49 return header->e_machine;
201 } 50 }
202 VAddr GetEntryPoint() const { 51 VAddr GetEntryPoint() const {
203 return entryPoint; 52 return entryPoint;
@@ -220,13 +69,13 @@ public:
220 const u8* GetSectionDataPtr(int section) const { 69 const u8* GetSectionDataPtr(int section) const {
221 if (section < 0 || section >= header->e_shnum) 70 if (section < 0 || section >= header->e_shnum)
222 return nullptr; 71 return nullptr;
223 if (sections[section].sh_type != SHT_NOBITS) 72 if (sections[section].sh_type != ElfShtNobits)
224 return GetPtr(sections[section].sh_offset); 73 return GetPtr(sections[section].sh_offset);
225 else 74 else
226 return nullptr; 75 return nullptr;
227 } 76 }
228 bool IsCodeSection(int section) const { 77 bool IsCodeSection(int section) const {
229 return sections[section].sh_type == SHT_PROGBITS; 78 return sections[section].sh_type == ElfShtProgBits;
230 } 79 }
231 const u8* GetSegmentPtr(int segment) { 80 const u8* GetSegmentPtr(int segment) {
232 return GetPtr(segments[segment].p_offset); 81 return GetPtr(segments[segment].p_offset);
@@ -256,7 +105,7 @@ ElfReader::ElfReader(void* ptr) {
256} 105}
257 106
258const char* ElfReader::GetSectionName(int section) const { 107const char* ElfReader::GetSectionName(int section) const {
259 if (sections[section].sh_type == SHT_NULL) 108 if (sections[section].sh_type == ElfShtNull)
260 return nullptr; 109 return nullptr;
261 110
262 int name_offset = sections[section].sh_name; 111 int name_offset = sections[section].sh_name;
@@ -272,7 +121,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
272 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); 121 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
273 122
274 // Should we relocate? 123 // Should we relocate?
275 relocate = (header->e_type != ET_EXEC); 124 relocate = (header->e_type != ElfTypeExec);
276 125
277 if (relocate) { 126 if (relocate) {
278 LOG_DEBUG(Loader, "Relocatable module"); 127 LOG_DEBUG(Loader, "Relocatable module");
@@ -288,7 +137,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
288 u64 total_image_size = 0; 137 u64 total_image_size = 0;
289 for (unsigned int i = 0; i < header->e_phnum; ++i) { 138 for (unsigned int i = 0; i < header->e_phnum; ++i) {
290 const Elf32_Phdr* p = &segments[i]; 139 const Elf32_Phdr* p = &segments[i];
291 if (p->p_type == PT_LOAD) { 140 if (p->p_type == ElfPtLoad) {
292 total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; 141 total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
293 } 142 }
294 } 143 }
@@ -303,14 +152,14 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
303 LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, 152 LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
304 p->p_vaddr, p->p_filesz, p->p_memsz); 153 p->p_vaddr, p->p_filesz, p->p_memsz);
305 154
306 if (p->p_type == PT_LOAD) { 155 if (p->p_type == ElfPtLoad) {
307 Kernel::CodeSet::Segment* codeset_segment; 156 Kernel::CodeSet::Segment* codeset_segment;
308 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); 157 u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
309 if (permission_flags == (PF_R | PF_X)) { 158 if (permission_flags == (ElfPfRead | ElfPfExec)) {
310 codeset_segment = &codeset.CodeSegment(); 159 codeset_segment = &codeset.CodeSegment();
311 } else if (permission_flags == (PF_R)) { 160 } else if (permission_flags == (ElfPfRead)) {
312 codeset_segment = &codeset.RODataSegment(); 161 codeset_segment = &codeset.RODataSegment();
313 } else if (permission_flags == (PF_R | PF_W)) { 162 } else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
314 codeset_segment = &codeset.DataSegment(); 163 codeset_segment = &codeset.DataSegment();
315 } else { 164 } else {
316 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 165 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 8a938aa83..8dd956fc6 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -128,11 +128,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
128 128
129 // Apply patches if necessary 129 // Apply patches if necessary
130 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { 130 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
131 std::vector<u8> pi_header; 131 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
132 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header), 132 std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
133 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader)); 133 std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(),
134 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.data(), 134 program_image.size());
135 program_image.data() + program_image.size());
136 135
137 pi_header = pm->PatchNSO(pi_header, nso_file.GetName()); 136 pi_header = pm->PatchNSO(pi_header, nso_file.GetName());
138 137
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 7a0b73eca..5cc99fbe4 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -25,7 +25,6 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
25 return memory.Read64(addr); 25 return memory.Read64(addr);
26 default: 26 default:
27 UNREACHABLE(); 27 UNREACHABLE();
28 return 0;
29 } 28 }
30} 29}
31 30
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h
index 2487b9b0b..1ce45b3a5 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.h
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.h
@@ -58,7 +58,7 @@ public:
58 [[nodiscard]] Stack Remove(Token token) const; 58 [[nodiscard]] Stack Remove(Token token) const;
59 59
60private: 60private:
61 boost::container::small_vector<StackEntry, 3> entries; 61 std::vector<StackEntry> entries;
62}; 62};
63 63
64struct IndirectBranch { 64struct IndirectBranch {
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 3dc7c9a11..578bc8c1b 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -975,13 +975,7 @@ private:
975 Environment& env; 975 Environment& env;
976 IR::AbstractSyntaxList& syntax_list; 976 IR::AbstractSyntaxList& syntax_list;
977 bool uses_demote_to_helper{}; 977 bool uses_demote_to_helper{};
978
979// TODO: C++20 Remove this when all compilers support constexpr std::vector
980#if __cpp_lib_constexpr_vector >= 201907
981 static constexpr Flow::Block dummy_flow_block;
982#else
983 const Flow::Block dummy_flow_block; 978 const Flow::Block dummy_flow_block;
984#endif
985}; 979};
986} // Anonymous namespace 980} // Anonymous namespace
987 981
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 83b2e0fc4..a5eb97b7f 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -224,7 +224,7 @@ void Codec::Decode() {
224 vp9_hidden_frame = vp9_decoder->WasFrameHidden(); 224 vp9_hidden_frame = vp9_decoder->WasFrameHidden();
225 return vp9_decoder->GetFrameBytes(); 225 return vp9_decoder->GetFrameBytes();
226 default: 226 default:
227 UNREACHABLE(); 227 ASSERT(false);
228 return std::vector<u8>{}; 228 return std::vector<u8>{};
229 } 229 }
230 }(); 230 }();
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index bef321b6e..7c17df353 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -228,7 +228,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
228 break; 228 break;
229 } 229 }
230 default: 230 default:
231 UNREACHABLE(); 231 ASSERT(false);
232 break; 232 break;
233 } 233 }
234 gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), 234 gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(),
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 434ba0877..5f9eb208c 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -202,7 +202,7 @@ public:
202 case Size::Size_11_11_10: 202 case Size::Size_11_11_10:
203 return 3; 203 return 3;
204 default: 204 default:
205 UNREACHABLE(); 205 ASSERT(false);
206 return 1; 206 return 1;
207 } 207 }
208 } 208 }
@@ -238,7 +238,7 @@ public:
238 case Size::Size_11_11_10: 238 case Size::Size_11_11_10:
239 return 4; 239 return 4;
240 default: 240 default:
241 UNREACHABLE(); 241 ASSERT(false);
242 return 1; 242 return 1;
243 } 243 }
244 } 244 }
@@ -274,7 +274,7 @@ public:
274 case Size::Size_11_11_10: 274 case Size::Size_11_11_10:
275 return "11_11_10"; 275 return "11_11_10";
276 default: 276 default:
277 UNREACHABLE(); 277 ASSERT(false);
278 return {}; 278 return {};
279 } 279 }
280 } 280 }
@@ -296,7 +296,7 @@ public:
296 case Type::Float: 296 case Type::Float:
297 return "FLOAT"; 297 return "FLOAT";
298 } 298 }
299 UNREACHABLE(); 299 ASSERT(false);
300 return {}; 300 return {};
301 } 301 }
302 302
@@ -336,7 +336,7 @@ public:
336 case 3: 336 case 3:
337 return {x3, y3}; 337 return {x3, y3};
338 default: 338 default:
339 UNREACHABLE(); 339 ASSERT(false);
340 return {0, 0}; 340 return {0, 0};
341 } 341 }
342 } 342 }
@@ -1193,7 +1193,7 @@ public:
1193 case IndexFormat::UnsignedInt: 1193 case IndexFormat::UnsignedInt:
1194 return 4; 1194 return 4;
1195 } 1195 }
1196 UNREACHABLE(); 1196 ASSERT(false);
1197 return 1; 1197 return 1;
1198 } 1198 }
1199 1199
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index a7302f7c1..0efe58282 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -62,7 +62,7 @@ void MaxwellDMA::Launch() {
62 62
63 if (!is_src_pitch && !is_dst_pitch) { 63 if (!is_src_pitch && !is_dst_pitch) {
64 // If both the source and the destination are in block layout, assert. 64 // If both the source and the destination are in block layout, assert.
65 UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented"); 65 UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented");
66 return; 66 return;
67 } 67 }
68 68
@@ -260,7 +260,7 @@ void MaxwellDMA::ReleaseSemaphore() {
260 memory_manager.Write<u64>(address + 8, system.GPU().GetTicks()); 260 memory_manager.Write<u64>(address + 8, system.GPU().GetTicks());
261 break; 261 break;
262 default: 262 default:
263 UNREACHABLE_MSG("Unknown semaphore type: {}", static_cast<u32>(type.Value())); 263 ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
264 } 264 }
265} 265}
266 266
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index b79a73132..b0ce9f000 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 }
@@ -49,7 +50,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
49 } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { 50 } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
50 rasterizer->OnCPUWrite(invalidate->addr, invalidate->size); 51 rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
51 } else { 52 } else {
52 UNREACHABLE(); 53 ASSERT(false);
53 } 54 }
54 state.signaled_fence.store(next.fence); 55 state.signaled_fence.store(next.fence);
55 if (next.block) { 56 if (next.block) {
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
16namespace Tegra { 16namespace 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
98struct SynchState final { 98struct 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/macro/macro.cpp b/src/video_core/macro/macro.cpp
index e7279efcd..43f8b5904 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -71,7 +71,7 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
71 } 71 }
72 } 72 }
73 if (!mid_method.has_value()) { 73 if (!mid_method.has_value()) {
74 UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); 74 ASSERT_MSG(false, "Macro 0x{0:x} was not uploaded", method);
75 return; 75 return;
76 } 76 }
77 } 77 }
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index 87d2e8721..f670b1bca 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -308,7 +308,6 @@ bool MacroInterpreterImpl::EvaluateBranchCondition(Macro::BranchCondition cond,
308 return value != 0; 308 return value != 0;
309 } 309 }
310 UNREACHABLE(); 310 UNREACHABLE();
311 return true;
312} 311}
313 312
314Macro::Opcode MacroInterpreterImpl::GetOpcode() const { 313Macro::Opcode MacroInterpreterImpl::GetOpcode() const {
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index dc5376501..aca25d902 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -411,7 +411,7 @@ void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
411 411
412 Xbyak::Label end; 412 Xbyak::Label end;
413 auto value = Compile_GetRegister(opcode.src_a, eax); 413 auto value = Compile_GetRegister(opcode.src_a, eax);
414 test(value, value); 414 cmp(value, 0); // test(value, value);
415 if (optimizer.has_delayed_pc) { 415 if (optimizer.has_delayed_pc) {
416 switch (opcode.branch_condition) { 416 switch (opcode.branch_condition) {
417 case Macro::BranchCondition::Zero: 417 case Macro::BranchCondition::Zero:
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index c8d99fdb5..d373be0ba 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -67,7 +67,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
67 ASSERT(it->first == gpu_addr); 67 ASSERT(it->first == gpu_addr);
68 map_ranges.erase(it); 68 map_ranges.erase(it);
69 } else { 69 } else {
70 UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr); 70 ASSERT_MSG(false, "Unmapping non-existent GPU address=0x{:x}", gpu_addr);
71 } 71 }
72 const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); 72 const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
73 73
@@ -206,7 +206,7 @@ T MemoryManager::Read(GPUVAddr addr) const {
206 return value; 206 return value;
207 } 207 }
208 208
209 UNREACHABLE(); 209 ASSERT(false);
210 210
211 return {}; 211 return {};
212} 212}
@@ -219,7 +219,7 @@ void MemoryManager::Write(GPUVAddr addr, T data) {
219 return; 219 return;
220 } 220 }
221 221
222 UNREACHABLE(); 222 ASSERT(false);
223} 223}
224 224
225template u8 MemoryManager::Read<u8>(GPUVAddr addr) const; 225template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 35f42f2f8..67eae369d 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -48,7 +48,7 @@ GLenum Stage(size_t stage_index) {
48 case 4: 48 case 4:
49 return GL_FRAGMENT_SHADER; 49 return GL_FRAGMENT_SHADER;
50 } 50 }
51 UNREACHABLE_MSG("{}", stage_index); 51 ASSERT_MSG(false, "{}", stage_index);
52 return GL_NONE; 52 return GL_NONE;
53} 53}
54 54
@@ -65,7 +65,7 @@ GLenum AssemblyStage(size_t stage_index) {
65 case 4: 65 case 4:
66 return GL_FRAGMENT_PROGRAM_NV; 66 return GL_FRAGMENT_PROGRAM_NV;
67 } 67 }
68 UNREACHABLE_MSG("{}", stage_index); 68 ASSERT_MSG(false, "{}", stage_index);
69 return GL_NONE; 69 return GL_NONE;
70} 70}
71 71
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index cd48fef26..07d4b7cf0 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -85,7 +85,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
85 case Maxwell::TessellationPrimitive::Quads: 85 case Maxwell::TessellationPrimitive::Quads:
86 return Shader::TessPrimitive::Quads; 86 return Shader::TessPrimitive::Quads;
87 } 87 }
88 UNREACHABLE(); 88 ASSERT(false);
89 return Shader::TessPrimitive::Triangles; 89 return Shader::TessPrimitive::Triangles;
90 }(); 90 }();
91 info.tess_spacing = [&] { 91 info.tess_spacing = [&] {
@@ -97,7 +97,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
97 case Maxwell::TessellationSpacing::FractionalEven: 97 case Maxwell::TessellationSpacing::FractionalEven:
98 return Shader::TessSpacing::FractionalEven; 98 return Shader::TessSpacing::FractionalEven;
99 } 99 }
100 UNREACHABLE(); 100 ASSERT(false);
101 return Shader::TessSpacing::Equal; 101 return Shader::TessSpacing::Equal;
102 }(); 102 }();
103 break; 103 break;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 29ff736fb..8c0fffc67 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -83,7 +83,7 @@ GLenum ImageTarget(const VideoCommon::ImageInfo& info) {
83 case ImageType::Buffer: 83 case ImageType::Buffer:
84 return GL_TEXTURE_BUFFER; 84 return GL_TEXTURE_BUFFER;
85 } 85 }
86 UNREACHABLE_MSG("Invalid image type={}", info.type); 86 ASSERT_MSG(false, "Invalid image type={}", info.type);
87 return GL_NONE; 87 return GL_NONE;
88} 88}
89 89
@@ -107,7 +107,7 @@ GLenum ImageTarget(Shader::TextureType type, int num_samples = 1) {
107 case Shader::TextureType::Buffer: 107 case Shader::TextureType::Buffer:
108 return GL_TEXTURE_BUFFER; 108 return GL_TEXTURE_BUFFER;
109 } 109 }
110 UNREACHABLE_MSG("Invalid image view type={}", type); 110 ASSERT_MSG(false, "Invalid image view type={}", type);
111 return GL_NONE; 111 return GL_NONE;
112} 112}
113 113
@@ -119,7 +119,7 @@ GLenum TextureMode(PixelFormat format, bool is_first) {
119 case PixelFormat::S8_UINT_D24_UNORM: 119 case PixelFormat::S8_UINT_D24_UNORM:
120 return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT; 120 return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT;
121 default: 121 default:
122 UNREACHABLE(); 122 ASSERT(false);
123 return GL_DEPTH_COMPONENT; 123 return GL_DEPTH_COMPONENT;
124 } 124 }
125} 125}
@@ -140,7 +140,7 @@ GLint Swizzle(SwizzleSource source) {
140 case SwizzleSource::OneFloat: 140 case SwizzleSource::OneFloat:
141 return GL_ONE; 141 return GL_ONE;
142 } 142 }
143 UNREACHABLE_MSG("Invalid swizzle source={}", source); 143 ASSERT_MSG(false, "Invalid swizzle source={}", source);
144 return GL_NONE; 144 return GL_NONE;
145} 145}
146 146
@@ -197,7 +197,7 @@ GLint ConvertA5B5G5R1_UNORM(SwizzleSource source) {
197 case SwizzleSource::OneFloat: 197 case SwizzleSource::OneFloat:
198 return GL_ONE; 198 return GL_ONE;
199 } 199 }
200 UNREACHABLE_MSG("Invalid swizzle source={}", source); 200 ASSERT_MSG(false, "Invalid swizzle source={}", source);
201 return GL_NONE; 201 return GL_NONE;
202} 202}
203 203
@@ -381,10 +381,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
381 glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, depth); 381 glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, depth);
382 break; 382 break;
383 case GL_TEXTURE_BUFFER: 383 case GL_TEXTURE_BUFFER:
384 UNREACHABLE(); 384 ASSERT(false);
385 break; 385 break;
386 default: 386 default:
387 UNREACHABLE_MSG("Invalid target=0x{:x}", target); 387 ASSERT_MSG(false, "Invalid target=0x{:x}", target);
388 break; 388 break;
389 } 389 }
390 return texture; 390 return texture;
@@ -420,7 +420,7 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
420 case Shader::ImageFormat::R32G32B32A32_UINT: 420 case Shader::ImageFormat::R32G32B32A32_UINT:
421 return GL_RGBA32UI; 421 return GL_RGBA32UI;
422 } 422 }
423 UNREACHABLE_MSG("Invalid image format={}", format); 423 ASSERT_MSG(false, "Invalid image format={}", format);
424 return GL_R32UI; 424 return GL_R32UI;
425} 425}
426 426
@@ -579,7 +579,7 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
579 } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) { 579 } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
580 format_conversion_pass.ConvertImage(dst, src, copies); 580 format_conversion_pass.ConvertImage(dst, src, copies);
581 } else { 581 } else {
582 UNREACHABLE(); 582 ASSERT(false);
583 } 583 }
584} 584}
585 585
@@ -620,7 +620,7 @@ void TextureCacheRuntime::AccelerateImageUpload(Image& image, const ImageBufferM
620 case ImageType::Linear: 620 case ImageType::Linear:
621 return util_shaders.PitchUpload(image, map, swizzles); 621 return util_shaders.PitchUpload(image, map, swizzles);
622 default: 622 default:
623 UNREACHABLE(); 623 ASSERT(false);
624 break; 624 break;
625 } 625 }
626} 626}
@@ -639,7 +639,7 @@ FormatProperties TextureCacheRuntime::FormatInfo(ImageType type, GLenum internal
639 case ImageType::e3D: 639 case ImageType::e3D:
640 return format_properties[2].at(internal_format); 640 return format_properties[2].at(internal_format);
641 default: 641 default:
642 UNREACHABLE(); 642 ASSERT(false);
643 return FormatProperties{}; 643 return FormatProperties{};
644 } 644 }
645} 645}
@@ -888,7 +888,7 @@ void Image::CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t b
888 } 888 }
889 break; 889 break;
890 default: 890 default:
891 UNREACHABLE(); 891 ASSERT(false);
892 } 892 }
893} 893}
894 894
@@ -924,7 +924,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b
924 depth = copy.image_extent.depth; 924 depth = copy.image_extent.depth;
925 break; 925 break;
926 default: 926 default:
927 UNREACHABLE(); 927 ASSERT(false);
928 } 928 }
929 // Compressed formats don't have a pixel format or type 929 // Compressed formats don't have a pixel format or type
930 const bool is_compressed = gl_format == GL_NONE; 930 const bool is_compressed = gl_format == GL_NONE;
@@ -950,7 +950,7 @@ void Image::Scale(bool up_scale) {
950 case SurfaceType::DepthStencil: 950 case SurfaceType::DepthStencil:
951 return GL_DEPTH_STENCIL_ATTACHMENT; 951 return GL_DEPTH_STENCIL_ATTACHMENT;
952 default: 952 default:
953 UNREACHABLE(); 953 ASSERT(false);
954 return GL_COLOR_ATTACHMENT0; 954 return GL_COLOR_ATTACHMENT0;
955 } 955 }
956 }(); 956 }();
@@ -965,7 +965,7 @@ void Image::Scale(bool up_scale) {
965 case SurfaceType::DepthStencil: 965 case SurfaceType::DepthStencil:
966 return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; 966 return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
967 default: 967 default:
968 UNREACHABLE(); 968 ASSERT(false);
969 return GL_COLOR_BUFFER_BIT; 969 return GL_COLOR_BUFFER_BIT;
970 } 970 }
971 }(); 971 }();
@@ -980,7 +980,7 @@ void Image::Scale(bool up_scale) {
980 case SurfaceType::DepthStencil: 980 case SurfaceType::DepthStencil:
981 return 3; 981 return 3;
982 default: 982 default:
983 UNREACHABLE(); 983 ASSERT(false);
984 return 0; 984 return 0;
985 } 985 }
986 }(); 986 }();
@@ -1045,7 +1045,7 @@ bool Image::ScaleUp(bool ignore) {
1045 return false; 1045 return false;
1046 } 1046 }
1047 if (info.type == ImageType::Linear) { 1047 if (info.type == ImageType::Linear) {
1048 UNREACHABLE(); 1048 ASSERT(false);
1049 return false; 1049 return false;
1050 } 1050 }
1051 flags |= ImageFlagBits::Rescaled; 1051 flags |= ImageFlagBits::Rescaled;
@@ -1139,7 +1139,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1139 UNIMPLEMENTED(); 1139 UNIMPLEMENTED();
1140 break; 1140 break;
1141 case ImageViewType::Buffer: 1141 case ImageViewType::Buffer:
1142 UNREACHABLE(); 1142 ASSERT(false);
1143 break; 1143 break;
1144 } 1144 }
1145 switch (info.type) { 1145 switch (info.type) {
@@ -1319,7 +1319,7 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
1319 buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; 1319 buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
1320 break; 1320 break;
1321 default: 1321 default:
1322 UNREACHABLE(); 1322 ASSERT(false);
1323 buffer_bits |= GL_DEPTH_BUFFER_BIT; 1323 buffer_bits |= GL_DEPTH_BUFFER_BIT;
1324 break; 1324 break;
1325 } 1325 }
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index c2a6da5a7..644b60d73 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -206,7 +206,7 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) {
206 case Maxwell::IndexFormat::UnsignedInt: 206 case Maxwell::IndexFormat::UnsignedInt:
207 return GL_UNSIGNED_INT; 207 return GL_UNSIGNED_INT;
208 } 208 }
209 UNREACHABLE_MSG("Invalid index_format={}", index_format); 209 ASSERT_MSG(false, "Invalid index_format={}", index_format);
210 return {}; 210 return {};
211} 211}
212 212
@@ -243,7 +243,7 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
243 case Maxwell::PrimitiveTopology::Patches: 243 case Maxwell::PrimitiveTopology::Patches:
244 return GL_PATCHES; 244 return GL_PATCHES;
245 } 245 }
246 UNREACHABLE_MSG("Invalid topology={}", topology); 246 ASSERT_MSG(false, "Invalid topology={}", topology);
247 return GL_POINTS; 247 return GL_POINTS;
248} 248}
249 249
@@ -271,8 +271,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
271 } 271 }
272 break; 272 break;
273 } 273 }
274 UNREACHABLE_MSG("Invalid texture filter mode={} and mipmap filter mode={}", filter_mode, 274 ASSERT_MSG(false, "Invalid texture filter mode={} and mipmap filter mode={}", filter_mode,
275 mipmap_filter_mode); 275 mipmap_filter_mode);
276 return GL_NEAREST; 276 return GL_NEAREST;
277} 277}
278 278
@@ -550,7 +550,7 @@ inline GLenum PolygonMode(Maxwell::PolygonMode polygon_mode) {
550 case Maxwell::PolygonMode::Fill: 550 case Maxwell::PolygonMode::Fill:
551 return GL_FILL; 551 return GL_FILL;
552 } 552 }
553 UNREACHABLE_MSG("Invalid polygon mode={}", polygon_mode); 553 ASSERT_MSG(false, "Invalid polygon mode={}", polygon_mode);
554 return GL_FILL; 554 return GL_FILL;
555} 555}
556 556
@@ -563,7 +563,7 @@ inline GLenum ReductionFilter(Tegra::Texture::SamplerReduction filter) {
563 case Tegra::Texture::SamplerReduction::Max: 563 case Tegra::Texture::SamplerReduction::Max:
564 return GL_MAX; 564 return GL_MAX;
565 } 565 }
566 UNREACHABLE_MSG("Invalid reduction filter={}", static_cast<int>(filter)); 566 ASSERT_MSG(false, "Invalid reduction filter={}", static_cast<int>(filter));
567 return GL_WEIGHTED_AVERAGE_ARB; 567 return GL_WEIGHTED_AVERAGE_ARB;
568} 568}
569 569
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 3a3c213bb..9a9243544 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -79,7 +79,7 @@ const char* GetSource(GLenum source) {
79 case GL_DEBUG_SOURCE_OTHER: 79 case GL_DEBUG_SOURCE_OTHER:
80 return "OTHER"; 80 return "OTHER";
81 default: 81 default:
82 UNREACHABLE(); 82 ASSERT(false);
83 return "Unknown source"; 83 return "Unknown source";
84 } 84 }
85} 85}
@@ -101,7 +101,7 @@ const char* GetType(GLenum type) {
101 case GL_DEBUG_TYPE_MARKER: 101 case GL_DEBUG_TYPE_MARKER:
102 return "MARKER"; 102 return "MARKER";
103 default: 103 default:
104 UNREACHABLE(); 104 ASSERT(false);
105 return "Unknown type"; 105 return "Unknown type";
106 } 106 }
107} 107}
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 837825737..404def62e 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -282,7 +282,7 @@ GLenum StoreFormat(u32 bytes_per_block) {
282 case 16: 282 case 16:
283 return GL_RGBA32UI; 283 return GL_RGBA32UI;
284 } 284 }
285 UNREACHABLE(); 285 ASSERT(false);
286 return GL_R8UI; 286 return GL_R8UI;
287} 287}
288 288
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index ea360f339..193cbe15e 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -25,7 +25,7 @@ VkFilter Filter(Tegra::Texture::TextureFilter filter) {
25 case Tegra::Texture::TextureFilter::Linear: 25 case Tegra::Texture::TextureFilter::Linear:
26 return VK_FILTER_LINEAR; 26 return VK_FILTER_LINEAR;
27 } 27 }
28 UNREACHABLE_MSG("Invalid sampler filter={}", filter); 28 ASSERT_MSG(false, "Invalid sampler filter={}", filter);
29 return {}; 29 return {};
30} 30}
31 31
@@ -42,7 +42,7 @@ VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter
42 case Tegra::Texture::TextureMipmapFilter::Linear: 42 case Tegra::Texture::TextureMipmapFilter::Linear:
43 return VK_SAMPLER_MIPMAP_MODE_LINEAR; 43 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
44 } 44 }
45 UNREACHABLE_MSG("Invalid sampler mipmap mode={}", mipmap_filter); 45 ASSERT_MSG(false, "Invalid sampler mipmap mode={}", mipmap_filter);
46 return {}; 46 return {};
47} 47}
48 48
@@ -70,7 +70,7 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra
70 case Tegra::Texture::TextureFilter::Linear: 70 case Tegra::Texture::TextureFilter::Linear:
71 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; 71 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
72 } 72 }
73 UNREACHABLE(); 73 ASSERT(false);
74 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 74 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
75 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 75 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
76 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; 76 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
@@ -744,7 +744,7 @@ VkViewportCoordinateSwizzleNV ViewportSwizzle(Maxwell::ViewportSwizzle swizzle)
744 case Maxwell::ViewportSwizzle::NegativeW: 744 case Maxwell::ViewportSwizzle::NegativeW:
745 return VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV; 745 return VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV;
746 } 746 }
747 UNREACHABLE_MSG("Invalid swizzle={}", swizzle); 747 ASSERT_MSG(false, "Invalid swizzle={}", swizzle);
748 return {}; 748 return {};
749} 749}
750 750
@@ -757,7 +757,7 @@ VkSamplerReductionMode SamplerReduction(Tegra::Texture::SamplerReduction reducti
757 case Tegra::Texture::SamplerReduction::Max: 757 case Tegra::Texture::SamplerReduction::Max:
758 return VK_SAMPLER_REDUCTION_MODE_MAX_EXT; 758 return VK_SAMPLER_REDUCTION_MODE_MAX_EXT;
759 } 759 }
760 UNREACHABLE_MSG("Invalid sampler mode={}", static_cast<int>(reduction)); 760 ASSERT_MSG(false, "Invalid sampler mode={}", static_cast<int>(reduction));
761 return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; 761 return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
762} 762}
763 763
@@ -780,7 +780,7 @@ VkSampleCountFlagBits MsaaMode(Tegra::Texture::MsaaMode msaa_mode) {
780 case Tegra::Texture::MsaaMode::Msaa4x4: 780 case Tegra::Texture::MsaaMode::Msaa4x4:
781 return VK_SAMPLE_COUNT_16_BIT; 781 return VK_SAMPLE_COUNT_16_BIT;
782 default: 782 default:
783 UNREACHABLE_MSG("Invalid msaa_mode={}", static_cast<int>(msaa_mode)); 783 ASSERT_MSG(false, "Invalid msaa_mode={}", static_cast<int>(msaa_mode));
784 return VK_SAMPLE_COUNT_1_BIT; 784 return VK_SAMPLE_COUNT_1_BIT;
785 } 785 }
786} 786}
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 0aeb37538..450905197 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -46,7 +46,7 @@ size_t BytesPerIndex(VkIndexType index_type) {
46 case VK_INDEX_TYPE_UINT32: 46 case VK_INDEX_TYPE_UINT32:
47 return 4; 47 return 4;
48 default: 48 default:
49 UNREACHABLE_MSG("Invalid index type={}", index_type); 49 ASSERT_MSG(false, "Invalid index type={}", index_type);
50 return 1; 50 return 1;
51 } 51 }
52} 52}
@@ -366,7 +366,7 @@ void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle
366 std::memcpy(staging_data, MakeQuadIndices<u32>(quad, first).data(), quad_size); 366 std::memcpy(staging_data, MakeQuadIndices<u32>(quad, first).data(), quad_size);
367 break; 367 break;
368 default: 368 default:
369 UNREACHABLE(); 369 ASSERT(false);
370 break; 370 break;
371 } 371 }
372 staging_data += quad_size; 372 staging_data += quad_size;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 29481a102..b75c99d44 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -265,7 +265,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
265 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedInt: 265 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedInt:
266 return 2; 266 return 2;
267 } 267 }
268 UNREACHABLE(); 268 ASSERT(false);
269 return 2; 269 return 2;
270 }(); 270 }();
271 const u32 input_size = num_vertices << index_shift; 271 const u32 input_size = num_vertices << index_shift;
@@ -333,7 +333,7 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
333 const VkImageMemoryBarrier image_barrier{ 333 const VkImageMemoryBarrier image_barrier{
334 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 334 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
335 .pNext = nullptr, 335 .pNext = nullptr,
336 .srcAccessMask = is_initialized ? VK_ACCESS_SHADER_WRITE_BIT : VkAccessFlags{}, 336 .srcAccessMask = is_initialized ? VK_ACCESS_SHADER_WRITE_BIT : VkAccessFlagBits{},
337 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, 337 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
338 .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, 338 .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
339 .newLayout = VK_IMAGE_LAYOUT_GENERAL, 339 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 5196bdcf2..978e827f5 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -174,7 +174,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
174 case Maxwell::TessellationPrimitive::Quads: 174 case Maxwell::TessellationPrimitive::Quads:
175 return Shader::TessPrimitive::Quads; 175 return Shader::TessPrimitive::Quads;
176 } 176 }
177 UNREACHABLE(); 177 ASSERT(false);
178 return Shader::TessPrimitive::Triangles; 178 return Shader::TessPrimitive::Triangles;
179 }(); 179 }();
180 info.tess_spacing = [&] { 180 info.tess_spacing = [&] {
@@ -187,7 +187,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
187 case Maxwell::TessellationSpacing::FractionalEven: 187 case Maxwell::TessellationSpacing::FractionalEven:
188 return Shader::TessSpacing::FractionalEven; 188 return Shader::TessSpacing::FractionalEven;
189 } 189 }
190 UNREACHABLE(); 190 ASSERT(false);
191 return Shader::TessSpacing::Equal; 191 return Shader::TessSpacing::Equal;
192 }(); 192 }();
193 break; 193 break;
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 31ce2f815..9a6afaca6 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -263,7 +263,7 @@ StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage
263 case MemoryUsage::Download: 263 case MemoryUsage::Download:
264 return download_cache; 264 return download_cache;
265 default: 265 default:
266 UNREACHABLE_MSG("Invalid memory usage={}", usage); 266 ASSERT_MSG(false, "Invalid memory usage={}", usage);
267 return upload_cache; 267 return upload_cache;
268 } 268 }
269} 269}
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 353594293..43ecb9647 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -70,7 +70,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
70 case ImageType::Buffer: 70 case ImageType::Buffer:
71 break; 71 break;
72 } 72 }
73 UNREACHABLE_MSG("Invalid image type={}", type); 73 ASSERT_MSG(false, "Invalid image type={}", type);
74 return {}; 74 return {};
75} 75}
76 76
@@ -87,7 +87,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
87 case 16: 87 case 16:
88 return VK_SAMPLE_COUNT_16_BIT; 88 return VK_SAMPLE_COUNT_16_BIT;
89 default: 89 default:
90 UNREACHABLE_MSG("Invalid number of samples={}", num_samples); 90 ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
91 return VK_SAMPLE_COUNT_1_BIT; 91 return VK_SAMPLE_COUNT_1_BIT;
92 } 92 }
93} 93}
@@ -107,7 +107,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
107 usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; 107 usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
108 break; 108 break;
109 default: 109 default:
110 UNREACHABLE_MSG("Invalid surface type"); 110 ASSERT_MSG(false, "Invalid surface type");
111 } 111 }
112 } 112 }
113 if (info.storage) { 113 if (info.storage) {
@@ -179,7 +179,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
179 case VideoCore::Surface::SurfaceType::DepthStencil: 179 case VideoCore::Surface::SurfaceType::DepthStencil:
180 return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; 180 return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
181 default: 181 default:
182 UNREACHABLE_MSG("Invalid surface type"); 182 ASSERT_MSG(false, "Invalid surface type");
183 return VkImageAspectFlags{}; 183 return VkImageAspectFlags{};
184 } 184 }
185} 185}
@@ -221,7 +221,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
221 case SwizzleSource::OneInt: 221 case SwizzleSource::OneInt:
222 return VK_COMPONENT_SWIZZLE_ONE; 222 return VK_COMPONENT_SWIZZLE_ONE;
223 } 223 }
224 UNREACHABLE_MSG("Invalid swizzle={}", swizzle); 224 ASSERT_MSG(false, "Invalid swizzle={}", swizzle);
225 return VK_COMPONENT_SWIZZLE_ZERO; 225 return VK_COMPONENT_SWIZZLE_ZERO;
226} 226}
227 227
@@ -242,10 +242,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
242 case Shader::TextureType::ColorArrayCube: 242 case Shader::TextureType::ColorArrayCube:
243 return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; 243 return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
244 case Shader::TextureType::Buffer: 244 case Shader::TextureType::Buffer:
245 UNREACHABLE_MSG("Texture buffers can't be image views"); 245 ASSERT_MSG(false, "Texture buffers can't be image views");
246 return VK_IMAGE_VIEW_TYPE_1D; 246 return VK_IMAGE_VIEW_TYPE_1D;
247 } 247 }
248 UNREACHABLE_MSG("Invalid image view type={}", type); 248 ASSERT_MSG(false, "Invalid image view type={}", type);
249 return VK_IMAGE_VIEW_TYPE_2D; 249 return VK_IMAGE_VIEW_TYPE_2D;
250} 250}
251 251
@@ -269,10 +269,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
269 UNIMPLEMENTED_MSG("Rect image view"); 269 UNIMPLEMENTED_MSG("Rect image view");
270 return VK_IMAGE_VIEW_TYPE_2D; 270 return VK_IMAGE_VIEW_TYPE_2D;
271 case VideoCommon::ImageViewType::Buffer: 271 case VideoCommon::ImageViewType::Buffer:
272 UNREACHABLE_MSG("Texture buffers can't be image views"); 272 ASSERT_MSG(false, "Texture buffers can't be image views");
273 return VK_IMAGE_VIEW_TYPE_1D; 273 return VK_IMAGE_VIEW_TYPE_1D;
274 } 274 }
275 UNREACHABLE_MSG("Invalid image view type={}", type); 275 ASSERT_MSG(false, "Invalid image view type={}", type);
276 return VK_IMAGE_VIEW_TYPE_2D; 276 return VK_IMAGE_VIEW_TYPE_2D;
277} 277}
278 278
@@ -644,7 +644,7 @@ struct RangedBarrierRange {
644 case Shader::ImageFormat::R32G32B32A32_UINT: 644 case Shader::ImageFormat::R32G32B32A32_UINT:
645 return VK_FORMAT_R32G32B32A32_UINT; 645 return VK_FORMAT_R32G32B32A32_UINT;
646 } 646 }
647 UNREACHABLE_MSG("Invalid image format={}", format); 647 ASSERT_MSG(false, "Invalid image format={}", format);
648 return VK_FORMAT_R32_UINT; 648 return VK_FORMAT_R32_UINT;
649} 649}
650 650
@@ -1596,7 +1596,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1596 UNIMPLEMENTED(); 1596 UNIMPLEMENTED();
1597 break; 1597 break;
1598 case VideoCommon::ImageViewType::Buffer: 1598 case VideoCommon::ImageViewType::Buffer:
1599 UNREACHABLE(); 1599 ASSERT(false);
1600 break; 1600 break;
1601 } 1601 }
1602} 1602}
@@ -1822,7 +1822,7 @@ void TextureCacheRuntime::AccelerateImageUpload(
1822 if (IsPixelFormatASTC(image.info.format)) { 1822 if (IsPixelFormatASTC(image.info.format)) {
1823 return astc_decoder_pass.Assemble(image, map, swizzles); 1823 return astc_decoder_pass.Assemble(image, map, swizzles);
1824 } 1824 }
1825 UNREACHABLE(); 1825 ASSERT(false);
1826} 1826}
1827 1827
1828} // namespace Vulkan 1828} // namespace Vulkan
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index d469964f6..c4e923bbf 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -280,7 +280,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
280 stage_index = 4; 280 stage_index = 4;
281 break; 281 break;
282 default: 282 default:
283 UNREACHABLE_MSG("Invalid program={}", program); 283 ASSERT_MSG(false, "Invalid program={}", program);
284 break; 284 break;
285 } 285 }
286 const u64 local_size{sph.LocalMemorySize()}; 286 const u64 local_size{sph.LocalMemorySize()};
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 5f428d35d..69c1b1e6d 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -29,7 +29,7 @@ SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_t
29 return SurfaceTarget::Texture2DArray; 29 return SurfaceTarget::Texture2DArray;
30 default: 30 default:
31 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", texture_type); 31 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", texture_type);
32 UNREACHABLE(); 32 ASSERT(false);
33 return SurfaceTarget::Texture2D; 33 return SurfaceTarget::Texture2D;
34 } 34 }
35} 35}
@@ -48,7 +48,7 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
48 return true; 48 return true;
49 default: 49 default:
50 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target); 50 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
51 UNREACHABLE(); 51 ASSERT(false);
52 return false; 52 return false;
53 } 53 }
54} 54}
@@ -67,7 +67,7 @@ bool SurfaceTargetIsArray(SurfaceTarget target) {
67 return true; 67 return true;
68 default: 68 default:
69 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target); 69 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
70 UNREACHABLE(); 70 ASSERT(false);
71 return false; 71 return false;
72 } 72 }
73} 73}
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 802939f6c..6c073ee57 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -94,7 +94,7 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
94 resources.layers = 1; 94 resources.layers = 1;
95 break; 95 break;
96 default: 96 default:
97 UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 97 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
98 break; 98 break;
99 } 99 }
100 if (type != ImageType::Linear) { 100 if (type != ImageType::Linear) {
diff --git a/src/video_core/texture_cache/image_view_info.cpp b/src/video_core/texture_cache/image_view_info.cpp
index 0cee5e45f..f47885147 100644
--- a/src/video_core/texture_cache/image_view_info.cpp
+++ b/src/video_core/texture_cache/image_view_info.cpp
@@ -71,7 +71,7 @@ ImageViewInfo::ImageViewInfo(const TICEntry& config, s32 base_layer) noexcept
71 range.extent.layers = config.Depth() * 6; 71 range.extent.layers = config.Depth() * 6;
72 break; 72 break;
73 default: 73 default:
74 UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 74 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
75 break; 75 break;
76 } 76 }
77} 77}
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h
index 91fec60bd..d552bccf0 100644
--- a/src/video_core/texture_cache/samples_helper.h
+++ b/src/video_core/texture_cache/samples_helper.h
@@ -23,7 +23,7 @@ namespace VideoCommon {
23 case 16: 23 case 16:
24 return {2, 2}; 24 return {2, 2};
25 } 25 }
26 UNREACHABLE_MSG("Invalid number of samples={}", num_samples); 26 ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
27 return {1, 1}; 27 return {1, 1};
28} 28}
29 29
@@ -47,7 +47,7 @@ namespace VideoCommon {
47 case MsaaMode::Msaa4x4: 47 case MsaaMode::Msaa4x4:
48 return 16; 48 return 16;
49 } 49 }
50 UNREACHABLE_MSG("Invalid MSAA mode={}", static_cast<int>(msaa_mode)); 50 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
51 return 1; 51 return 1;
52} 52}
53 53
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 6622d7818..cf3ca06a6 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -1485,14 +1485,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1485 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) { 1485 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) {
1486 const auto page_it = selected_page_table.find(page); 1486 const auto page_it = selected_page_table.find(page);
1487 if (page_it == selected_page_table.end()) { 1487 if (page_it == selected_page_table.end()) {
1488 UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1488 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
1489 return; 1489 return;
1490 } 1490 }
1491 std::vector<ImageId>& image_ids = page_it->second; 1491 std::vector<ImageId>& image_ids = page_it->second;
1492 const auto vector_it = std::ranges::find(image_ids, image_id); 1492 const auto vector_it = std::ranges::find(image_ids, image_id);
1493 if (vector_it == image_ids.end()) { 1493 if (vector_it == image_ids.end()) {
1494 UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", 1494 ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}",
1495 page << PAGE_BITS); 1495 page << PAGE_BITS);
1496 return; 1496 return;
1497 } 1497 }
1498 image_ids.erase(vector_it); 1498 image_ids.erase(vector_it);
@@ -1504,14 +1504,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1504 ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { 1504 ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) {
1505 const auto page_it = page_table.find(page); 1505 const auto page_it = page_table.find(page);
1506 if (page_it == page_table.end()) { 1506 if (page_it == page_table.end()) {
1507 UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1507 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
1508 return; 1508 return;
1509 } 1509 }
1510 std::vector<ImageMapId>& image_map_ids = page_it->second; 1510 std::vector<ImageMapId>& image_map_ids = page_it->second;
1511 const auto vector_it = std::ranges::find(image_map_ids, map_id); 1511 const auto vector_it = std::ranges::find(image_map_ids, map_id);
1512 if (vector_it == image_map_ids.end()) { 1512 if (vector_it == image_map_ids.end()) {
1513 UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", 1513 ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}",
1514 page << PAGE_BITS); 1514 page << PAGE_BITS);
1515 return; 1515 return;
1516 } 1516 }
1517 image_map_ids.erase(vector_it); 1517 image_map_ids.erase(vector_it);
@@ -1532,7 +1532,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1532 ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) { 1532 ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) {
1533 const auto page_it = page_table.find(page); 1533 const auto page_it = page_table.find(page);
1534 if (page_it == page_table.end()) { 1534 if (page_it == page_table.end()) {
1535 UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1535 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
1536 return; 1536 return;
1537 } 1537 }
1538 std::vector<ImageMapId>& image_map_ids = page_it->second; 1538 std::vector<ImageMapId>& image_map_ids = page_it->second;
@@ -1616,15 +1616,15 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
1616 const GPUVAddr gpu_addr = image.gpu_addr; 1616 const GPUVAddr gpu_addr = image.gpu_addr;
1617 const auto alloc_it = image_allocs_table.find(gpu_addr); 1617 const auto alloc_it = image_allocs_table.find(gpu_addr);
1618 if (alloc_it == image_allocs_table.end()) { 1618 if (alloc_it == image_allocs_table.end()) {
1619 UNREACHABLE_MSG("Trying to delete an image alloc that does not exist in address 0x{:x}", 1619 ASSERT_MSG(false, "Trying to delete an image alloc that does not exist in address 0x{:x}",
1620 gpu_addr); 1620 gpu_addr);
1621 return; 1621 return;
1622 } 1622 }
1623 const ImageAllocId alloc_id = alloc_it->second; 1623 const ImageAllocId alloc_id = alloc_it->second;
1624 std::vector<ImageId>& alloc_images = slot_image_allocs[alloc_id].images; 1624 std::vector<ImageId>& alloc_images = slot_image_allocs[alloc_id].images;
1625 const auto alloc_image_it = std::ranges::find(alloc_images, image_id); 1625 const auto alloc_image_it = std::ranges::find(alloc_images, image_id);
1626 if (alloc_image_it == alloc_images.end()) { 1626 if (alloc_image_it == alloc_images.end()) {
1627 UNREACHABLE_MSG("Trying to delete an image that does not exist"); 1627 ASSERT_MSG(false, "Trying to delete an image that does not exist");
1628 return; 1628 return;
1629 } 1629 }
1630 ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked"); 1630 ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked");
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index c81343850..9b6b8527b 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -87,7 +87,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
87 BPP_CASE(16) 87 BPP_CASE(16)
88#undef BPP_CASE 88#undef BPP_CASE
89 default: 89 default:
90 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 90 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
91 } 91 }
92} 92}
93 93
@@ -209,7 +209,7 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
209 BPP_CASE(16) 209 BPP_CASE(16)
210#undef BPP_CASE 210#undef BPP_CASE
211 default: 211 default:
212 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 212 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
213 } 213 }
214} 214}
215 215
@@ -230,7 +230,7 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width,
230 BPP_CASE(16) 230 BPP_CASE(16)
231#undef BPP_CASE 231#undef BPP_CASE
232 default: 232 default:
233 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 233 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
234 } 234 }
235} 235}
236 236
@@ -253,7 +253,7 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
253 BPP_CASE(16) 253 BPP_CASE(16)
254#undef BPP_CASE 254#undef BPP_CASE
255 default: 255 default:
256 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 256 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
257 } 257 }
258} 258}
259 259
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index b3a77e07f..11ce865a7 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -738,9 +738,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
738 // The wanted format is not supported by hardware, search for alternatives 738 // The wanted format is not supported by hardware, search for alternatives
739 const VkFormat* alternatives = GetFormatAlternatives(wanted_format); 739 const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
740 if (alternatives == nullptr) { 740 if (alternatives == nullptr) {
741 UNREACHABLE_MSG("Format={} with usage={} and type={} has no defined alternatives and host " 741 ASSERT_MSG(false,
742 "hardware does not support it", 742 "Format={} with usage={} and type={} has no defined alternatives and host "
743 wanted_format, wanted_usage, format_type); 743 "hardware does not support it",
744 wanted_format, wanted_usage, format_type);
744 return wanted_format; 745 return wanted_format;
745 } 746 }
746 747
@@ -756,9 +757,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
756 } 757 }
757 758
758 // No alternatives found, panic 759 // No alternatives found, panic
759 UNREACHABLE_MSG("Format={} with usage={} and type={} is not supported by the host hardware and " 760 ASSERT_MSG(false,
760 "doesn't support any of the alternatives", 761 "Format={} with usage={} and type={} is not supported by the host hardware and "
761 wanted_format, wanted_usage, format_type); 762 "doesn't support any of the alternatives",
763 wanted_format, wanted_usage, format_type);
762 return wanted_format; 764 return wanted_format;
763} 765}
764 766
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
10namespace Vulkan { 11namespace Vulkan {
11 12
12Common::DynamicLibrary OpenLibrary() { 13Common::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/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index caae6dfdc..6442898bd 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -49,7 +49,7 @@ struct Range {
49 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | 49 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
50 VK_MEMORY_PROPERTY_HOST_CACHED_BIT; 50 VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
51 } 51 }
52 UNREACHABLE_MSG("Invalid memory usage={}", usage); 52 ASSERT_MSG(false, "Invalid memory usage={}", usage);
53 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; 53 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
54} 54}
55 55
@@ -325,7 +325,7 @@ VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
325 // Remove device local, if it's not supported by the requested resource 325 // Remove device local, if it's not supported by the requested resource
326 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); 326 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
327 } 327 }
328 UNREACHABLE_MSG("No compatible memory types found"); 328 ASSERT_MSG(false, "No compatible memory types found");
329 return 0; 329 return 0;
330} 330}
331 331
@@ -349,7 +349,7 @@ bool IsHostVisible(MemoryUsage usage) noexcept {
349 case MemoryUsage::Download: 349 case MemoryUsage::Download:
350 return true; 350 return true;
351 } 351 }
352 UNREACHABLE_MSG("Invalid memory usage={}", usage); 352 ASSERT_MSG(false, "Invalid memory usage={}", usage);
353 return false; 353 return false;
354} 354}
355 355
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 07df9675d..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
@@ -187,7 +189,7 @@ if (ENABLE_QT_TRANSLATION)
187 # Update source TS file if enabled 189 # Update source TS file if enabled
188 if (GENERATE_QT_TRANSLATION) 190 if (GENERATE_QT_TRANSLATION)
189 get_target_property(SRCS yuzu SOURCES) 191 get_target_property(SRCS yuzu SOURCES)
190 qt5_create_translation(QM_FILES 192 qt_create_translation(QM_FILES
191 ${SRCS} 193 ${SRCS}
192 ${UIS} 194 ${UIS}
193 ${YUZU_QT_LANGUAGES}/en.ts 195 ${YUZU_QT_LANGUAGES}/en.ts
@@ -203,7 +205,7 @@ if (ENABLE_QT_TRANSLATION)
203 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) 205 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)
204 206
205 # Compile TS files to QM files 207 # Compile TS files to QM files
206 qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) 208 qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
207 209
208 # Build a QRC file from the QM file list 210 # Build a QRC file from the QM file list
209 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) 211 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
@@ -215,7 +217,7 @@ if (ENABLE_QT_TRANSLATION)
215 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") 217 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
216 218
217 # Add the QRC file to package in all QM files 219 # Add the QRC file to package in all QM files
218 qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) 220 qt_add_resources(LANGUAGES ${LANGUAGES_QRC})
219else() 221else()
220 set(LANGUAGES) 222 set(LANGUAGES)
221endif() 223endif()
@@ -236,8 +238,13 @@ if (APPLE)
236 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) 238 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
237elseif(WIN32) 239elseif(WIN32)
238 # compile as a win32 gui application instead of a console application 240 # compile as a win32 gui application instead of a console application
239 target_link_libraries(yuzu PRIVATE Qt5::WinMain) 241 if (QT_VERSION VERSION_GREATER 6)
242 target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
243 else()
244 target_link_libraries(yuzu PRIVATE Qt5::WinMain)
245 endif()
240 if(MSVC) 246 if(MSVC)
247 target_link_libraries(yuzu PRIVATE version.lib)
241 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") 248 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
242 elseif(MINGW) 249 elseif(MINGW)
243 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows") 250 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
@@ -247,7 +254,7 @@ endif()
247create_target_directory_groups(yuzu) 254create_target_directory_groups(yuzu)
248 255
249target_link_libraries(yuzu PRIVATE common core input_common video_core) 256target_link_libraries(yuzu PRIVATE common core input_common video_core)
250target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets) 257target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
251target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 258target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
252 259
253target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 260target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
@@ -255,7 +262,7 @@ if (NOT WIN32)
255 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 262 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
256endif() 263endif()
257if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 264if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
258 target_link_libraries(yuzu PRIVATE Qt5::DBus) 265 target_link_libraries(yuzu PRIVATE Qt::DBus)
259endif() 266endif()
260 267
261target_compile_definitions(yuzu PRIVATE 268target_compile_definitions(yuzu PRIVATE
@@ -291,7 +298,7 @@ if (USE_DISCORD_PRESENCE)
291endif() 298endif()
292 299
293if (YUZU_USE_QT_WEB_ENGINE) 300if (YUZU_USE_QT_WEB_ENGINE)
294 target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets) 301 target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets)
295 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) 302 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
296endif () 303endif ()
297 304
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index c924cb0cb..8be311fcb 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -631,7 +631,7 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
631 switch (max_supported_players) { 631 switch (max_supported_players) {
632 case 0: 632 case 0:
633 default: 633 default:
634 UNREACHABLE(); 634 ASSERT(false);
635 return; 635 return;
636 case 1: 636 case 1:
637 ui->widgetSpacer->hide(); 637 ui->widgetSpacer->hide();
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index aae2de2f8..bde465485 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -752,7 +752,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
752 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y); 752 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
753 753
754 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { 754 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
755 QCursor::setPos(mapToGlobal({center_x, center_y})); 755 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
756 } 756 }
757 757
758 emit MouseActivity(); 758 emit MouseActivity();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 87c559e7a..d01538039 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -10,6 +10,7 @@
10#include <mutex> 10#include <mutex>
11 11
12#include <QImage> 12#include <QImage>
13#include <QStringList>
13#include <QThread> 14#include <QThread>
14#include <QTouchEvent> 15#include <QTouchEvent>
15#include <QWidget> 16#include <QWidget>
@@ -20,7 +21,6 @@
20class GRenderWindow; 21class GRenderWindow;
21class GMainWindow; 22class GMainWindow;
22class QKeyEvent; 23class QKeyEvent;
23class QStringList;
24 24
25namespace Core { 25namespace Core {
26enum class SystemResultStatus : u32; 26enum class SystemResultStatus : u32;
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
16constexpr char TEMP_FILE_NAME[] = "vulkan_check";
17
18bool 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
6bool CheckVulkan();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 583e9df24..9df4752be 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -71,28 +71,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{
71// UISetting::values.shortcuts, which is alphabetically ordered. 71// UISetting::values.shortcuts, which is alphabetically ordered.
72// clang-format off 72// clang-format off
73const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ 73const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
74 {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, 74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
75 {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, 75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
76 {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, 76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
77 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, 77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
78 {QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, 78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
79 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, 79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
80 {QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, 80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
81 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, 81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
82 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, 82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
83 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, 83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
84 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, 84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
85 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, 85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
86 {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, 86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
87 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, 87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
88 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, 88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
89 {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, 89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
90 {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, 90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
91 {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, 91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
92 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, 92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
93 {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, 93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
94 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, 94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
95 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, 95 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
96}}; 96}};
97// clang-format on 97// clang-format on
98 98
@@ -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
21ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) 22ConfigureGraphics::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
321void ConfigureGraphics::RetrieveVulkanDevices() try { 340bool 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
340Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 365Settings::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/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 6679e9c53..edf0893c4 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -61,14 +61,18 @@ ConfigureHotkeys::~ConfigureHotkeys() = default;
61 61
62void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { 62void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
63 for (const auto& group : registry.hotkey_groups) { 63 for (const auto& group : registry.hotkey_groups) {
64 auto* parent_item = new QStandardItem(group.first); 64 auto* parent_item =
65 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first)));
65 parent_item->setEditable(false); 66 parent_item->setEditable(false);
67 parent_item->setData(group.first);
66 for (const auto& hotkey : group.second) { 68 for (const auto& hotkey : group.second) {
67 auto* action = new QStandardItem(hotkey.first); 69 auto* action =
70 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first)));
68 auto* keyseq = 71 auto* keyseq =
69 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); 72 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
70 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); 73 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
71 action->setEditable(false); 74 action->setEditable(false);
75 action->setData(hotkey.first);
72 keyseq->setEditable(false); 76 keyseq->setEditable(false);
73 controller_keyseq->setEditable(false); 77 controller_keyseq->setEditable(false);
74 parent_item->appendRow({action, keyseq, controller_keyseq}); 78 parent_item->appendRow({action, keyseq, controller_keyseq});
@@ -93,6 +97,16 @@ void ConfigureHotkeys::RetranslateUI() {
93 ui->retranslateUi(this); 97 ui->retranslateUi(this);
94 98
95 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); 99 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")});
100 for (int key_id = 0; key_id < model->rowCount(); key_id++) {
101 QStandardItem* parent = model->item(key_id, 0);
102 parent->setText(
103 QCoreApplication::translate("Hotkeys", qPrintable(parent->data().toString())));
104 for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
105 QStandardItem* action = parent->child(key_column_id, name_column);
106 action->setText(
107 QCoreApplication::translate("Hotkeys", qPrintable(action->data().toString())));
108 }
109 }
96} 110}
97 111
98void ConfigureHotkeys::Configure(QModelIndex index) { 112void ConfigureHotkeys::Configure(QModelIndex index) {
@@ -273,10 +287,10 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
273 const QStandardItem* controller_keyseq = 287 const QStandardItem* controller_keyseq =
274 parent->child(key_column_id, controller_column); 288 parent->child(key_column_id, controller_column);
275 for (auto& [group, sub_actions] : registry.hotkey_groups) { 289 for (auto& [group, sub_actions] : registry.hotkey_groups) {
276 if (group != parent->text()) 290 if (group != parent->data())
277 continue; 291 continue;
278 for (auto& [action_name, hotkey] : sub_actions) { 292 for (auto& [action_name, hotkey] : sub_actions) {
279 if (action_name != action->text()) 293 if (action_name != action->data())
280 continue; 294 continue;
281 hotkey.keyseq = QKeySequence(keyseq->text()); 295 hotkey.keyseq = QKeySequence(keyseq->text());
282 hotkey.controller_keyseq = controller_keyseq->text(); 296 hotkey.controller_keyseq = controller_keyseq->text();
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index 27559c37b..c313b0919 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -151,6 +151,8 @@ void ConfigureMotionTouch::ConnectEvents() {
151 &ConfigureMotionTouch::OnConfigureTouchCalibration); 151 &ConfigureMotionTouch::OnConfigureTouchCalibration);
152 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, 152 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
153 &ConfigureMotionTouch::OnConfigureTouchFromButton); 153 &ConfigureMotionTouch::OnConfigureTouchFromButton);
154 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
155 &ConfigureMotionTouch::ApplyConfiguration);
154 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { 156 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
155 if (CanCloseDialog()) { 157 if (CanCloseDialog()) {
156 reject(); 158 reject();
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index c75a84ae4..0237fae54 100644
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -293,22 +293,5 @@
293 </layout> 293 </layout>
294 </widget> 294 </widget>
295 <resources/> 295 <resources/>
296 <connections> 296 <connections/>
297 <connection>
298 <sender>buttonBox</sender>
299 <signal>accepted()</signal>
300 <receiver>ConfigureMotionTouch</receiver>
301 <slot>ApplyConfiguration()</slot>
302 <hints>
303 <hint type="sourcelabel">
304 <x>20</x>
305 <y>20</y>
306 </hint>
307 <hint type="destinationlabel">
308 <x>20</x>
309 <y>20</y>
310 </hint>
311 </hints>
312 </connection>
313 </connections>
314</ui> 297</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 19aa589f9..ecebb0fb7 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -130,8 +130,7 @@ void ConfigureSystem::ApplyConfiguration() {
130 // Guard if during game and set to game-specific value 130 // Guard if during game and set to game-specific value
131 if (Settings::values.rng_seed.UsingGlobal()) { 131 if (Settings::values.rng_seed.UsingGlobal()) {
132 if (ui->rng_seed_checkbox->isChecked()) { 132 if (ui->rng_seed_checkbox->isChecked()) {
133 Settings::values.rng_seed.SetValue( 133 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
134 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
135 } else { 134 } else {
136 Settings::values.rng_seed.SetValue(std::nullopt); 135 Settings::values.rng_seed.SetValue(std::nullopt);
137 } 136 }
@@ -142,8 +141,7 @@ void ConfigureSystem::ApplyConfiguration() {
142 case ConfigurationShared::CheckState::Off: 141 case ConfigurationShared::CheckState::Off:
143 Settings::values.rng_seed.SetGlobal(false); 142 Settings::values.rng_seed.SetGlobal(false);
144 if (ui->rng_seed_checkbox->isChecked()) { 143 if (ui->rng_seed_checkbox->isChecked()) {
145 Settings::values.rng_seed.SetValue( 144 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
146 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
147 } else { 145 } else {
148 Settings::values.rng_seed.SetValue(std::nullopt); 146 Settings::values.rng_seed.SetValue(std::nullopt);
149 } 147 }
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 4a6d74a7e..6321afc83 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -483,7 +483,7 @@ void GameList::DonePopulating(const QStringList& watch_list) {
483 // Also artificially caps the watcher to a certain number of directories 483 // Also artificially caps the watcher to a certain number of directories
484 constexpr int LIMIT_WATCH_DIRECTORIES = 5000; 484 constexpr int LIMIT_WATCH_DIRECTORIES = 5000;
485 constexpr int SLICE_SIZE = 25; 485 constexpr int SLICE_SIZE = 25;
486 int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES); 486 int len = std::min(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES);
487 for (int i = 0; i < len; i += SLICE_SIZE) { 487 for (int i = 0; i < len; i += SLICE_SIZE) {
488 watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); 488 watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE));
489 QCoreApplication::processEvents(); 489 QCoreApplication::processEvents();
@@ -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() {
891void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { 891void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) {
892 emit GameListPlaceholder::AddDirectory(); 892 emit GameListPlaceholder::AddDirectory();
893} 893}
894
895void GameListPlaceholder::changeEvent(QEvent* event) {
896 if (event->type() == QEvent::LanguageChange) {
897 RetranslateUI();
898 }
899
900 QWidget::changeEvent(event);
901}
902
903void 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
168private: 168private:
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/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index edfb946a8..e273744fd 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -183,7 +183,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
183 183
184void LoadingScreen::paintEvent(QPaintEvent* event) { 184void LoadingScreen::paintEvent(QPaintEvent* event) {
185 QStyleOption opt; 185 QStyleOption opt;
186 opt.init(this); 186 opt.initFrom(this);
187 QPainter p(this); 187 QPainter p(this);
188 style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); 188 style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
189 QWidget::paintEvent(event); 189 QWidget::paintEvent(event);
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h
index 7c960ee72..17045595d 100644
--- a/src/yuzu/loading_screen.h
+++ b/src/yuzu/loading_screen.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <QString> 8#include <QString>
9#include <QWidget> 9#include <QWidget>
10#include <QtGlobal>
10 11
11#if !QT_CONFIG(movie) 12#if !QT_CONFIG(movie)
12#define YUZU_QT_MOVIE_MISSING 1 13#define YUZU_QT_MOVIE_MISSING 1
@@ -88,4 +89,6 @@ private:
88 std::size_t slow_shader_first_value = 0; 89 std::size_t slow_shader_first_value = 0;
89}; 90};
90 91
92#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
91Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); 93Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage);
94#endif
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 8e7f91a0f..33886e50e 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"
@@ -198,6 +199,59 @@ static void RemoveCachedContents() {
198 Common::FS::RemoveDirRecursively(offline_system_data); 199 Common::FS::RemoveDirRecursively(offline_system_data);
199} 200}
200 201
202static void LogRuntimes() {
203#ifdef _MSC_VER
204 // It is possible that the name of the dll will change.
205 // vcruntime140.dll is for 2015 and onwards
206 constexpr char runtime_dll_name[] = "vcruntime140.dll";
207 UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
208 bool runtime_version_inspection_worked = false;
209 if (sz > 0) {
210 std::vector<u8> buf(sz);
211 if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
212 VS_FIXEDFILEINFO* pvi;
213 sz = sizeof(VS_FIXEDFILEINFO);
214 if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
215 if (pvi->dwSignature == VS_FFI_SIGNATURE) {
216 runtime_version_inspection_worked = true;
217 LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
218 pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
219 pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
220 }
221 }
222 }
223 }
224 if (!runtime_version_inspection_worked) {
225 LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
226 }
227#endif
228}
229
230static QString PrettyProductName() {
231#ifdef _WIN32
232 // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
233 // With that notation change they changed the registry key used to denote the current version
234 QSettings windows_registry(
235 QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
236 QSettings::NativeFormat);
237 const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
238 if (release_id == QStringLiteral("2009")) {
239 const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
240 const QString display_version =
241 windows_registry.value(QStringLiteral("DisplayVersion")).toString();
242 const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
243 u32 version = 10;
244 if (current_build >= 22000) {
245 version = 11;
246 }
247 return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
248 .arg(QString::number(version), display_version, QString::number(current_build),
249 QString::number(ubr));
250 }
251#endif
252 return QSysInfo::prettyProductName();
253}
254
201GMainWindow::GMainWindow() 255GMainWindow::GMainWindow()
202 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, 256 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
203 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 257 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@@ -243,6 +297,7 @@ GMainWindow::GMainWindow()
243 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; 297 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
244 298
245 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); 299 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
300 LogRuntimes();
246#ifdef ARCHITECTURE_x86_64 301#ifdef ARCHITECTURE_x86_64
247 const auto& caps = Common::GetCPUCaps(); 302 const auto& caps = Common::GetCPUCaps();
248 std::string cpu_string = caps.cpu_string; 303 std::string cpu_string = caps.cpu_string;
@@ -259,7 +314,7 @@ GMainWindow::GMainWindow()
259 } 314 }
260 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 315 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
261#endif 316#endif
262 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); 317 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
263 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 318 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
264 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); 319 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
265 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); 320 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
@@ -297,6 +352,23 @@ GMainWindow::GMainWindow()
297 352
298 MigrateConfigFiles(); 353 MigrateConfigFiles();
299 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
300#if defined(HAVE_SDL2) && !defined(_WIN32) 372#if defined(HAVE_SDL2) && !defined(_WIN32)
301 SDL_InitSubSystem(SDL_INIT_VIDEO); 373 SDL_InitSubSystem(SDL_INIT_VIDEO);
302 // SDL disables the screen saver by default, and setting the hint 374 // SDL disables the screen saver by default, and setting the hint
@@ -827,12 +899,11 @@ void GMainWindow::InitializeWidgets() {
827 899
828 // Setup Dock button 900 // Setup Dock button
829 dock_status_button = new QPushButton(); 901 dock_status_button = new QPushButton();
830 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 902 dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
831 dock_status_button->setFocusPolicy(Qt::NoFocus); 903 dock_status_button->setFocusPolicy(Qt::NoFocus);
832 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); 904 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
833 dock_status_button->setText(tr("DOCK"));
834 dock_status_button->setCheckable(true); 905 dock_status_button->setCheckable(true);
835 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); 906 UpdateDockedButton();
836 statusBar()->insertPermanentWidget(0, dock_status_button); 907 statusBar()->insertPermanentWidget(0, dock_status_button);
837 908
838 gpu_accuracy_button = new QPushButton(); 909 gpu_accuracy_button = new QPushButton();
@@ -1002,7 +1073,7 @@ void GMainWindow::InitializeHotkeys() {
1002 1073
1003void GMainWindow::SetDefaultUIGeometry() { 1074void GMainWindow::SetDefaultUIGeometry() {
1004 // 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
1005 const QRect screenRect = QApplication::desktop()->screenGeometry(this); 1076 const QRect screenRect = QGuiApplication::primaryScreen()->geometry();
1006 1077
1007 const int w = screenRect.width() * 2 / 3; 1078 const int w = screenRect.width() * 2 / 3;
1008 const int h = screenRect.height() * 2 / 3; 1079 const int h = screenRect.height() * 2 / 3;
@@ -1520,6 +1591,7 @@ void GMainWindow::ShutdownGame() {
1520 1591
1521 AllowOSSleep(); 1592 AllowOSSleep();
1522 1593
1594 system->DetachDebugger();
1523 discord_rpc->Pause(); 1595 discord_rpc->Pause();
1524 emu_thread->RequestStop(); 1596 emu_thread->RequestStop();
1525 1597
@@ -1567,7 +1639,7 @@ void GMainWindow::ShutdownGame() {
1567 emu_speed_label->setVisible(false); 1639 emu_speed_label->setVisible(false);
1568 game_fps_label->setVisible(false); 1640 game_fps_label->setVisible(false);
1569 emu_frametime_label->setVisible(false); 1641 emu_frametime_label->setVisible(false);
1570 renderer_status_button->setEnabled(true); 1642 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
1571 1643
1572 game_path.clear(); 1644 game_path.clear();
1573 1645
@@ -1587,7 +1659,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
1587 1659
1588void GMainWindow::UpdateRecentFiles() { 1660void GMainWindow::UpdateRecentFiles() {
1589 const int num_recent_files = 1661 const int num_recent_files =
1590 std::min(UISettings::values.recent_files.size(), max_recent_files_item); 1662 std::min(static_cast<int>(UISettings::values.recent_files.size()), max_recent_files_item);
1591 1663
1592 for (int i = 0; i < num_recent_files; i++) { 1664 for (int i = 0; i < num_recent_files; i++) {
1593 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( 1665 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg(
@@ -2585,6 +2657,18 @@ void GMainWindow::ToggleFullscreen() {
2585 } 2657 }
2586} 2658}
2587 2659
2660// We're going to return the screen that the given window has the most pixels on
2661static QScreen* GuessCurrentScreen(QWidget* window) {
2662 const QList<QScreen*> screens = QGuiApplication::screens();
2663 return *std::max_element(
2664 screens.cbegin(), screens.cend(), [window](const QScreen* left, const QScreen* right) {
2665 const QSize left_size = left->geometry().intersected(window->geometry()).size();
2666 const QSize right_size = right->geometry().intersected(window->geometry()).size();
2667 return (left_size.height() * left_size.width()) <
2668 (right_size.height() * right_size.width());
2669 });
2670}
2671
2588void GMainWindow::ShowFullscreen() { 2672void GMainWindow::ShowFullscreen() {
2589 const auto show_fullscreen = [](QWidget* window) { 2673 const auto show_fullscreen = [](QWidget* window) {
2590 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 2674 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
@@ -2593,7 +2677,7 @@ void GMainWindow::ShowFullscreen() {
2593 } 2677 }
2594 window->hide(); 2678 window->hide();
2595 window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint); 2679 window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint);
2596 const auto screen_geometry = QApplication::desktop()->screenGeometry(window); 2680 const auto screen_geometry = GuessCurrentScreen(window)->geometry();
2597 window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), 2681 window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(),
2598 screen_geometry.height() + 1); 2682 screen_geometry.height() + 1);
2599 window->raise(); 2683 window->raise();
@@ -2777,6 +2861,10 @@ void GMainWindow::OnConfigure() {
2777 mouse_hide_timer.start(); 2861 mouse_hide_timer.start();
2778 } 2862 }
2779 2863
2864 if (!UISettings::values.has_broken_vulkan) {
2865 renderer_status_button->setEnabled(!emulation_running);
2866 }
2867
2780 UpdateStatusButtons(); 2868 UpdateStatusButtons();
2781 controller_dialog->refreshConfiguration(); 2869 controller_dialog->refreshConfiguration();
2782} 2870}
@@ -2862,7 +2950,7 @@ void GMainWindow::OnToggleDockedMode() {
2862 } 2950 }
2863 2951
2864 Settings::values.use_docked_mode.SetValue(!is_docked); 2952 Settings::values.use_docked_mode.SetValue(!is_docked);
2865 dock_status_button->setChecked(!is_docked); 2953 UpdateDockedButton();
2866 OnDockedModeChanged(is_docked, !is_docked, *system); 2954 OnDockedModeChanged(is_docked, !is_docked, *system);
2867} 2955}
2868 2956
@@ -3228,6 +3316,12 @@ void GMainWindow::UpdateGPUAccuracyButton() {
3228 } 3316 }
3229} 3317}
3230 3318
3319void GMainWindow::UpdateDockedButton() {
3320 const bool is_docked = Settings::values.use_docked_mode.GetValue();
3321 dock_status_button->setChecked(is_docked);
3322 dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD"));
3323}
3324
3231void GMainWindow::UpdateFilterText() { 3325void GMainWindow::UpdateFilterText() {
3232 const auto filter = Settings::values.scaling_filter.GetValue(); 3326 const auto filter = Settings::values.scaling_filter.GetValue();
3233 switch (filter) { 3327 switch (filter) {
@@ -3271,10 +3365,10 @@ void GMainWindow::UpdateAAText() {
3271} 3365}
3272 3366
3273void GMainWindow::UpdateStatusButtons() { 3367void GMainWindow::UpdateStatusButtons() {
3274 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
3275 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 3368 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
3276 Settings::RendererBackend::Vulkan); 3369 Settings::RendererBackend::Vulkan);
3277 UpdateGPUAccuracyButton(); 3370 UpdateGPUAccuracyButton();
3371 UpdateDockedButton();
3278 UpdateFilterText(); 3372 UpdateFilterText();
3279 UpdateAAText(); 3373 UpdateAAText();
3280} 3374}
@@ -3325,7 +3419,7 @@ void GMainWindow::CenterMouseCursor() {
3325 const int center_x = render_window->width() / 2; 3419 const int center_x = render_window->width() / 2;
3326 const int center_y = render_window->height() / 2; 3420 const int center_y = render_window->height() / 2;
3327 3421
3328 QCursor::setPos(mapToGlobal({center_x, center_y})); 3422 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
3329} 3423}
3330 3424
3331void GMainWindow::OnMouseActivity() { 3425void GMainWindow::OnMouseActivity() {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b399e9b01..600647015 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -320,6 +320,7 @@ private:
320 void MigrateConfigFiles(); 320 void MigrateConfigFiles();
321 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, 321 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
322 std::string_view gpu_vendor = {}); 322 std::string_view gpu_vendor = {});
323 void UpdateDockedButton();
323 void UpdateFilterText(); 324 void UpdateFilterText();
324 void UpdateAAText(); 325 void UpdateAAText();
325 void UpdateStatusBar(); 326 void UpdateStatusBar();
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/config.cpp b/src/yuzu_cmd/config.cpp
index fc16f0f0c..fc4744fb0 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -344,6 +344,8 @@ void Config::ReadValues() {
344 ReadSetting("Debugging", Settings::values.use_debug_asserts); 344 ReadSetting("Debugging", Settings::values.use_debug_asserts);
345 ReadSetting("Debugging", Settings::values.use_auto_stub); 345 ReadSetting("Debugging", Settings::values.use_auto_stub);
346 ReadSetting("Debugging", Settings::values.disable_macro_jit); 346 ReadSetting("Debugging", Settings::values.disable_macro_jit);
347 ReadSetting("Debugging", Settings::values.use_gdbstub);
348 ReadSetting("Debugging", Settings::values.gdbstub_port);
347 349
348 const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); 350 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
349 std::stringstream ss(title_list); 351 std::stringstream ss(title_list);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index f34d6b728..a3b8432f5 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
222backend = 222backend =
223 223
224# Enable graphics API debugging mode. 224# Enable graphics API debugging mode.
@@ -437,6 +437,11 @@ disable_macro_jit=false
437# Presents guest frames as they become available. Experimental. 437# Presents guest frames as they become available. Experimental.
438# false: Disabled (default), true: Enabled 438# false: Disabled (default), true: Enabled
439disable_fps_limit=false 439disable_fps_limit=false
440# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
441# false: Disabled (default), true: Enabled
442use_gdbstub=false
443# The port to use for the GDB server, if it is enabled.
444gdbstub_port=6543
440 445
441[WebService] 446[WebService]
442# Whether or not to enable telemetry 447# Whether or not to enable telemetry
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 71c413e64..8e38724db 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -162,7 +162,15 @@ void EmuWindow_SDL2::WaitEvent() {
162 SDL_Event event; 162 SDL_Event event;
163 163
164 if (!SDL_WaitEvent(&event)) { 164 if (!SDL_WaitEvent(&event)) {
165 LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError()); 165 const char* error = SDL_GetError();
166 if (!error || strcmp(error, "") == 0) {
167 // https://github.com/libsdl-org/SDL/issues/5780
168 // Sometimes SDL will return without actually having hit an error condition;
169 // just ignore it in this case.
170 return;
171 }
172
173 LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
166 exit(1); 174 exit(1);
167 } 175 }
168 176
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index ab12dd15d..0dce5e274 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -217,10 +217,19 @@ int main(int argc, char** argv) {
217 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); 217 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
218 } 218 }
219 219
220 system.RegisterExitCallback([&] {
221 // Just exit right away.
222 exit(0);
223 });
224
220 void(system.Run()); 225 void(system.Run());
226 if (system.DebuggerEnabled()) {
227 system.InitializeDebugger();
228 }
221 while (emu_window->IsOpen()) { 229 while (emu_window->IsOpen()) {
222 emu_window->WaitEvent(); 230 emu_window->WaitEvent();
223 } 231 }
232 system.DetachDebugger();
224 void(system.Pause()); 233 void(system.Pause());
225 system.Shutdown(); 234 system.Shutdown();
226 235