summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--externals/CMakeLists.txt3
m---------externals/soundtouch0
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/cubeb_sink.cpp25
-rw-r--r--src/audio_core/sdl2_sink.cpp4
-rw-r--r--src/audio_core/time_stretch.cpp68
-rw-r--r--src/audio_core/time_stretch.h34
-rw-r--r--src/common/atomic_ops.h90
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-rw-r--r--src/common/settings.h3
-rw-r--r--src/common/x64/native_clock.cpp59
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp52
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp52
-rw-r--r--src/core/file_sys/registered_cache.cpp6
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp11
-rw-r--r--src/core/hle/kernel/hle_ipc.h8
-rw-r--r--src/core/hle/kernel/kernel.cpp14
-rw-r--r--src/core/hle/kernel/kernel.h16
-rw-r--r--src/core/hle/service/am/am.cpp9
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp8
-rw-r--r--src/core/hle/service/audio/audout_u.cpp7
-rw-r--r--src/core/hle/service/audio/audren_u.cpp3
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp32
-rw-r--r--src/core/hle/service/hid/controllers/npad.h99
-rw-r--r--src/core/hle/service/jit/jit.cpp53
-rw-r--r--src/core/hle/service/jit/jit.h20
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp14
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp44
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h4
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp1
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.h2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp101
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h2
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.cpp29
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.h3
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp4
-rw-r--r--src/core/hle/service/service.cpp7
-rw-r--r--src/core/hle/service/service.h14
-rw-r--r--src/core/hle/service/sm/sm.cpp2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp3
-rw-r--r--src/core/hle/service/vi/vi.cpp3
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp25
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp19
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.h1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp21
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp9
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp2
-rw-r--r--src/yuzu/configuration/configure_cpu.ui5
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp7
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp6
-rw-r--r--src/yuzu_cmd/default_ini.h6
61 files changed, 613 insertions, 434 deletions
diff --git a/.gitmodules b/.gitmodules
index a9cf9a24a..dc92d0a4b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,9 +7,6 @@
7[submodule "dynarmic"] 7[submodule "dynarmic"]
8 path = externals/dynarmic 8 path = externals/dynarmic
9 url = https://github.com/MerryMage/dynarmic.git 9 url = https://github.com/MerryMage/dynarmic.git
10[submodule "soundtouch"]
11 path = externals/soundtouch
12 url = https://github.com/citra-emu/ext-soundtouch.git
13[submodule "libressl"] 10[submodule "libressl"]
14 path = externals/libressl 11 path = externals/libressl
15 url = https://github.com/citra-emu/ext-libressl-portable.git 12 url = https://github.com/citra-emu/ext-libressl-portable.git
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 82e8ef18c..64361de5f 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -68,9 +68,6 @@ if (YUZU_USE_EXTERNAL_SDL2)
68 add_library(SDL2 ALIAS SDL2-static) 68 add_library(SDL2 ALIAS SDL2-static)
69endif() 69endif()
70 70
71# SoundTouch
72add_subdirectory(soundtouch)
73
74# Cubeb 71# Cubeb
75if(ENABLE_CUBEB) 72if(ENABLE_CUBEB)
76 set(BUILD_TESTS OFF CACHE BOOL "") 73 set(BUILD_TESTS OFF CACHE BOOL "")
diff --git a/externals/soundtouch b/externals/soundtouch
deleted file mode 160000
Subproject 060181eaf273180d3a7e87349895bd0cb6ccbf4
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1bdf70b76..3ad2c0950 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -103,12 +103,6 @@ else()
103 -Wno-unused-parameter 103 -Wno-unused-parameter
104 ) 104 )
105 105
106 # TODO: Remove when we update to a GCC compiler that enables this
107 # by default (i.e. GCC 10 or newer).
108 if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
109 add_compile_options(-fconcepts)
110 endif()
111
112 if (ARCHITECTURE_x86_64) 106 if (ARCHITECTURE_x86_64)
113 add_compile_options("-mcx16") 107 add_compile_options("-mcx16")
114 endif() 108 endif()
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 090dd19b1..e553b8203 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -36,8 +36,6 @@ add_library(audio_core STATIC
36 splitter_context.h 36 splitter_context.h
37 stream.cpp 37 stream.cpp
38 stream.h 38 stream.h
39 time_stretch.cpp
40 time_stretch.h
41 voice_context.cpp 39 voice_context.cpp
42 voice_context.h 40 voice_context.h
43 41
@@ -63,7 +61,6 @@ if (NOT MSVC)
63endif() 61endif()
64 62
65target_link_libraries(audio_core PUBLIC common core) 63target_link_libraries(audio_core PUBLIC common core)
66target_link_libraries(audio_core PRIVATE SoundTouch)
67 64
68if(ENABLE_CUBEB) 65if(ENABLE_CUBEB)
69 target_link_libraries(audio_core PRIVATE cubeb) 66 target_link_libraries(audio_core PRIVATE cubeb)
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 93c35e785..13de3087c 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -7,7 +7,6 @@
7#include <cstring> 7#include <cstring>
8#include "audio_core/cubeb_sink.h" 8#include "audio_core/cubeb_sink.h"
9#include "audio_core/stream.h" 9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h"
11#include "common/assert.h" 10#include "common/assert.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
13#include "common/ring_buffer.h" 12#include "common/ring_buffer.h"
@@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream {
23public: 22public:
24 CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, 23 CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
25 const std::string& name) 24 const std::string& name)
26 : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, 25 : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} {
27 num_channels} {
28 26
29 cubeb_stream_params params{}; 27 cubeb_stream_params params{};
30 params.rate = sample_rate; 28 params.rate = sample_rate;
@@ -131,7 +129,6 @@ private:
131 Common::RingBuffer<s16, 0x10000> queue; 129 Common::RingBuffer<s16, 0x10000> queue;
132 std::array<s16, 2> last_frame{}; 130 std::array<s16, 2> last_frame{};
133 std::atomic<bool> should_flush{}; 131 std::atomic<bool> should_flush{};
134 TimeStretcher time_stretch;
135 132
136 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, 133 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
137 void* output_buffer, long num_frames); 134 void* output_buffer, long num_frames);
@@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void*
205 202
206 const std::size_t num_channels = impl->GetNumChannels(); 203 const std::size_t num_channels = impl->GetNumChannels();
207 const std::size_t samples_to_write = num_channels * num_frames; 204 const std::size_t samples_to_write = num_channels * num_frames;
208 std::size_t samples_written; 205 const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write);
209
210 /*
211 if (Settings::values.enable_audio_stretching.GetValue()) {
212 const std::vector<s16> in{impl->queue.Pop()};
213 const std::size_t num_in{in.size() / num_channels};
214 s16* const out{reinterpret_cast<s16*>(buffer)};
215 const std::size_t out_frames =
216 impl->time_stretch.Process(in.data(), num_in, out, num_frames);
217 samples_written = out_frames * num_channels;
218
219 if (impl->should_flush) {
220 impl->time_stretch.Flush();
221 impl->should_flush = false;
222 }
223 } else {
224 samples_written = impl->queue.Pop(buffer, samples_to_write);
225 }*/
226 samples_written = impl->queue.Pop(buffer, samples_to_write);
227 206
228 if (samples_written >= num_channels) { 207 if (samples_written >= num_channels) {
229 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), 208 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
index 62d3716a6..2d14ce2cb 100644
--- a/src/audio_core/sdl2_sink.cpp
+++ b/src/audio_core/sdl2_sink.cpp
@@ -7,7 +7,6 @@
7#include <cstring> 7#include <cstring>
8#include "audio_core/sdl2_sink.h" 8#include "audio_core/sdl2_sink.h"
9#include "audio_core/stream.h" 9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h"
11#include "common/assert.h" 10#include "common/assert.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
13//#include "common/settings.h" 12//#include "common/settings.h"
@@ -27,7 +26,7 @@ namespace AudioCore {
27class SDLSinkStream final : public SinkStream { 26class SDLSinkStream final : public SinkStream {
28public: 27public:
29 SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device) 28 SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
30 : num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} { 29 : num_channels{std::min(num_channels_, 6u)} {
31 30
32 SDL_AudioSpec spec; 31 SDL_AudioSpec spec;
33 spec.freq = sample_rate; 32 spec.freq = sample_rate;
@@ -116,7 +115,6 @@ private:
116 SDL_AudioDeviceID dev = 0; 115 SDL_AudioDeviceID dev = 0;
117 u32 num_channels{}; 116 u32 num_channels{};
118 std::atomic<bool> should_flush{}; 117 std::atomic<bool> should_flush{};
119 TimeStretcher time_stretch;
120}; 118};
121 119
122SDLSink::SDLSink(std::string_view target_device_name) { 120SDLSink::SDLSink(std::string_view target_device_name) {
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
deleted file mode 100644
index 726591fce..000000000
--- a/src/audio_core/time_stretch.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cmath>
7#include <cstddef>
8#include "audio_core/time_stretch.h"
9#include "common/logging/log.h"
10
11namespace AudioCore {
12
13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
14 m_sound_touch.setChannels(channel_count);
15 m_sound_touch.setSampleRate(sample_rate);
16 m_sound_touch.setPitch(1.0);
17 m_sound_touch.setTempo(1.0);
18}
19
20void TimeStretcher::Clear() {
21 m_sound_touch.clear();
22}
23
24void TimeStretcher::Flush() {
25 m_sound_touch.flush();
26}
27
28std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
29 std::size_t num_out) {
30 const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
31
32 // We were given actual_samples number of samples, and num_samples were requested from us.
33 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
34
35 const double max_latency = 0.25; // seconds
36 const double max_backlog = m_sample_rate * max_latency;
37 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
38 if (backlog_fullness > 4.0) {
39 // Too many samples in backlog: Don't push anymore on
40 num_in = 0;
41 }
42
43 // We ideally want the backlog to be about 50% full.
44 // This gives some headroom both ways to prevent underflow and overflow.
45 // We tweak current_ratio to encourage this.
46 constexpr double tweak_time_scale = 0.05; // seconds
47 const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
48 current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
49
50 // This low-pass filter smoothes out variance in the calculated stretch ratio.
51 // The time-scale determines how responsive this filter is.
52 constexpr double lpf_time_scale = 0.712; // seconds
53 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
54 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
55
56 // Place a lower limit of 5% speed. When a game boots up, there will be
57 // many silence samples. These do not need to be timestretched.
58 m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
59 m_sound_touch.setTempo(m_stretch_ratio);
60
61 LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
62 backlog_fullness);
63
64 m_sound_touch.putSamples(in, static_cast<u32>(num_in));
65 return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
66}
67
68} // namespace AudioCore
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
deleted file mode 100644
index bb2270b96..000000000
--- a/src/audio_core/time_stretch.h
+++ /dev/null
@@ -1,34 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <SoundTouch.h>
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13class TimeStretcher {
14public:
15 TimeStretcher(u32 sample_rate, u32 channel_count);
16
17 /// @param in Input sample buffer
18 /// @param num_in Number of input frames in `in`
19 /// @param out Output sample buffer
20 /// @param num_out Desired number of output frames in `out`
21 /// @returns Actual number of frames written to `out`
22 std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
23
24 void Clear();
25
26 void Flush();
27
28private:
29 u32 m_sample_rate;
30 soundtouch::SoundTouch m_sound_touch;
31 double m_stretch_ratio = 1.0;
32};
33
34} // namespace AudioCore
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
index b94d73c7a..69fde8421 100644
--- a/src/common/atomic_ops.h
+++ b/src/common/atomic_ops.h
@@ -46,6 +46,50 @@ namespace Common {
46 reinterpret_cast<__int64*>(expected.data())) != 0; 46 reinterpret_cast<__int64*>(expected.data())) != 0;
47} 47}
48 48
49[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
50 u8& actual) {
51 actual =
52 _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
53 return actual == expected;
54}
55
56[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
57 u16& actual) {
58 actual =
59 _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
60 return actual == expected;
61}
62
63[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
64 u32& actual) {
65 actual =
66 _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
67 return actual == expected;
68}
69
70[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
71 u64& actual) {
72 actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
73 expected);
74 return actual == expected;
75}
76
77[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
78 u128& actual) {
79 const bool result =
80 _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
81 value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
82 actual = expected;
83 return result;
84}
85
86[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
87 u128 result{};
88 _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
89 result[0], reinterpret_cast<__int64*>(result.data()));
90 return result;
91}
92
49#else 93#else
50 94
51[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { 95[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
@@ -72,6 +116,52 @@ namespace Common {
72 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); 116 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
73} 117}
74 118
119[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
120 u8& actual) {
121 actual = __sync_val_compare_and_swap(pointer, expected, value);
122 return actual == expected;
123}
124
125[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
126 u16& actual) {
127 actual = __sync_val_compare_and_swap(pointer, expected, value);
128 return actual == expected;
129}
130
131[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
132 u32& actual) {
133 actual = __sync_val_compare_and_swap(pointer, expected, value);
134 return actual == expected;
135}
136
137[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
138 u64& actual) {
139 actual = __sync_val_compare_and_swap(pointer, expected, value);
140 return actual == expected;
141}
142
143[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
144 u128& actual) {
145 unsigned __int128 value_a;
146 unsigned __int128 expected_a;
147 unsigned __int128 actual_a;
148 std::memcpy(&value_a, value.data(), sizeof(u128));
149 std::memcpy(&expected_a, expected.data(), sizeof(u128));
150 actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
151 std::memcpy(actual.data(), &actual_a, sizeof(u128));
152 return actual_a == expected_a;
153}
154
155[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
156 unsigned __int128 zeros_a = 0;
157 unsigned __int128 result_a =
158 __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
159
160 u128 result;
161 std::memcpy(result.data(), &result_a, sizeof(u128));
162 return result;
163}
164
75#endif 165#endif
76 166
77} // namespace Common 167} // namespace Common
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 9120cc178..4acbff649 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -101,6 +101,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
101 SUB(Service, GRC) \ 101 SUB(Service, GRC) \
102 SUB(Service, HID) \ 102 SUB(Service, HID) \
103 SUB(Service, IRS) \ 103 SUB(Service, IRS) \
104 SUB(Service, JIT) \
104 SUB(Service, LBL) \ 105 SUB(Service, LBL) \
105 SUB(Service, LDN) \ 106 SUB(Service, LDN) \
106 SUB(Service, LDR) \ 107 SUB(Service, LDR) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index f803ab796..99c15fa96 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -69,6 +69,7 @@ enum class Class : u8 {
69 Service_GRC, ///< The game recording service 69 Service_GRC, ///< The game recording service
70 Service_HID, ///< The HID (Human interface device) service 70 Service_HID, ///< The HID (Human interface device) service
71 Service_IRS, ///< The IRS service 71 Service_IRS, ///< The IRS service
72 Service_JIT, ///< The JIT service
72 Service_LBL, ///< The LBL (LCD backlight) service 73 Service_LBL, ///< The LBL (LCD backlight) service
73 Service_LDN, ///< The LDN (Local domain network) service 74 Service_LDN, ///< The LDN (Local domain network) service
74 Service_LDR, ///< The loader service 75 Service_LDR, ///< The loader service
diff --git a/src/common/settings.h b/src/common/settings.h
index a37d83fb3..86e0fa140 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -38,6 +38,7 @@ enum class CPUAccuracy : u32 {
38 Auto = 0, 38 Auto = 0,
39 Accurate = 1, 39 Accurate = 1,
40 Unsafe = 2, 40 Unsafe = 2,
41 Paranoid = 3,
41}; 42};
42 43
43enum class FullscreenMode : u32 { 44enum class FullscreenMode : u32 {
@@ -470,7 +471,7 @@ struct Values {
470 471
471 // Cpu 472 // Cpu
472 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 473 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
473 CPUAccuracy::Unsafe, "cpu_accuracy"}; 474 CPUAccuracy::Paranoid, "cpu_accuracy"};
474 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 475 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
475 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 476 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
476 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 477 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 347e41efc..7fd9d22f8 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -10,25 +10,49 @@
10#include "common/uint128.h" 10#include "common/uint128.h"
11#include "common/x64/native_clock.h" 11#include "common/x64/native_clock.h"
12 12
13#ifdef _MSC_VER
14#include <intrin.h>
15#endif
16
13namespace Common { 17namespace Common {
14 18
19#ifdef _MSC_VER
20__forceinline static u64 FencedRDTSC() {
21 _mm_lfence();
22 _ReadWriteBarrier();
23 const u64 result = __rdtsc();
24 _mm_lfence();
25 _ReadWriteBarrier();
26 return result;
27}
28#else
29static u64 FencedRDTSC() {
30 u64 result;
31 asm volatile("lfence\n\t"
32 "rdtsc\n\t"
33 "shl $32, %%rdx\n\t"
34 "or %%rdx, %0\n\t"
35 "lfence"
36 : "=a"(result)
37 :
38 : "rdx", "memory", "cc");
39 return result;
40}
41#endif
42
15u64 EstimateRDTSCFrequency() { 43u64 EstimateRDTSCFrequency() {
16 // Discard the first result measuring the rdtsc. 44 // Discard the first result measuring the rdtsc.
17 _mm_mfence(); 45 FencedRDTSC();
18 __rdtsc();
19 std::this_thread::sleep_for(std::chrono::milliseconds{1}); 46 std::this_thread::sleep_for(std::chrono::milliseconds{1});
20 _mm_mfence(); 47 FencedRDTSC();
21 __rdtsc();
22 48
23 // Get the current time. 49 // Get the current time.
24 const auto start_time = std::chrono::steady_clock::now(); 50 const auto start_time = std::chrono::steady_clock::now();
25 _mm_mfence(); 51 const u64 tsc_start = FencedRDTSC();
26 const u64 tsc_start = __rdtsc();
27 // Wait for 200 milliseconds. 52 // Wait for 200 milliseconds.
28 std::this_thread::sleep_for(std::chrono::milliseconds{200}); 53 std::this_thread::sleep_for(std::chrono::milliseconds{200});
29 const auto end_time = std::chrono::steady_clock::now(); 54 const auto end_time = std::chrono::steady_clock::now();
30 _mm_mfence(); 55 const u64 tsc_end = FencedRDTSC();
31 const u64 tsc_end = __rdtsc();
32 // Calculate differences. 56 // Calculate differences.
33 const u64 timer_diff = static_cast<u64>( 57 const u64 timer_diff = static_cast<u64>(
34 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); 58 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
@@ -42,8 +66,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
42 u64 rtsc_frequency_) 66 u64 rtsc_frequency_)
43 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ 67 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
44 rtsc_frequency_} { 68 rtsc_frequency_} {
45 _mm_mfence(); 69 time_point.inner.last_measure = FencedRDTSC();
46 time_point.inner.last_measure = __rdtsc();
47 time_point.inner.accumulated_ticks = 0U; 70 time_point.inner.accumulated_ticks = 0U;
48 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); 71 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
49 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); 72 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
@@ -55,10 +78,10 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
55u64 NativeClock::GetRTSC() { 78u64 NativeClock::GetRTSC() {
56 TimePoint new_time_point{}; 79 TimePoint new_time_point{};
57 TimePoint current_time_point{}; 80 TimePoint current_time_point{};
81
82 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
58 do { 83 do {
59 current_time_point.pack = time_point.pack; 84 const u64 current_measure = FencedRDTSC();
60 _mm_mfence();
61 const u64 current_measure = __rdtsc();
62 u64 diff = current_measure - current_time_point.inner.last_measure; 85 u64 diff = current_measure - current_time_point.inner.last_measure;
63 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) 86 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
64 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure 87 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
@@ -66,7 +89,7 @@ u64 NativeClock::GetRTSC() {
66 : current_time_point.inner.last_measure; 89 : current_time_point.inner.last_measure;
67 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; 90 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
68 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 91 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
69 current_time_point.pack)); 92 current_time_point.pack, current_time_point.pack));
70 /// The clock cannot be more precise than the guest timer, remove the lower bits 93 /// The clock cannot be more precise than the guest timer, remove the lower bits
71 return new_time_point.inner.accumulated_ticks & inaccuracy_mask; 94 return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
72} 95}
@@ -75,13 +98,13 @@ void NativeClock::Pause(bool is_paused) {
75 if (!is_paused) { 98 if (!is_paused) {
76 TimePoint current_time_point{}; 99 TimePoint current_time_point{};
77 TimePoint new_time_point{}; 100 TimePoint new_time_point{};
101
102 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
78 do { 103 do {
79 current_time_point.pack = time_point.pack;
80 new_time_point.pack = current_time_point.pack; 104 new_time_point.pack = current_time_point.pack;
81 _mm_mfence(); 105 new_time_point.inner.last_measure = FencedRDTSC();
82 new_time_point.inner.last_measure = __rdtsc();
83 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 106 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
84 current_time_point.pack)); 107 current_time_point.pack, current_time_point.pack));
85 } 108 }
86} 109}
87 110
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 6536d0544..81eaf0942 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -458,6 +458,8 @@ add_library(core STATIC
458 hle/service/hid/controllers/touchscreen.h 458 hle/service/hid/controllers/touchscreen.h
459 hle/service/hid/controllers/xpad.cpp 459 hle/service/hid/controllers/xpad.cpp
460 hle/service/hid/controllers/xpad.h 460 hle/service/hid/controllers/xpad.h
461 hle/service/jit/jit.cpp
462 hle/service/jit/jit.h
461 hle/service/lbl/lbl.cpp 463 hle/service/lbl/lbl.cpp
462 hle/service/lbl/lbl.h 464 hle/service/lbl/lbl.h
463 hle/service/ldn/errors.h 465 hle/service/ldn/errors.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 224a30170..ab3210d84 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -73,11 +73,13 @@ public:
73 } 73 }
74 74
75 void InterpreterFallback(u32 pc, std::size_t num_instructions) override { 75 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
76 parent.LogBacktrace();
76 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, 77 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
77 MemoryReadCode(pc)); 78 MemoryReadCode(pc));
78 } 79 }
79 80
80 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 81 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
82 parent.LogBacktrace();
81 LOG_CRITICAL(Core_ARM, 83 LOG_CRITICAL(Core_ARM,
82 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 84 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
83 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); 85 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
@@ -184,35 +186,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
184 if (!Settings::values.cpuopt_recompile_exclusives) { 186 if (!Settings::values.cpuopt_recompile_exclusives) {
185 config.recompile_on_exclusive_fastmem_failure = false; 187 config.recompile_on_exclusive_fastmem_failure = false;
186 } 188 }
187 } 189 } else {
190 // Unsafe optimizations
191 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
192 config.unsafe_optimizations = true;
193 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
194 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
195 }
196 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
197 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
198 }
199 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
200 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
201 }
202 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
203 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
204 }
205 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
206 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
207 }
208 }
188 209
189 // Unsafe optimizations 210 // Curated optimizations
190 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 211 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
191 config.unsafe_optimizations = true; 212 config.unsafe_optimizations = true;
192 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
193 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
194 }
195 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
196 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
197 }
198 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
199 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
200 }
201 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
202 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
203 }
204 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
205 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
206 } 217 }
207 }
208 218
209 // Curated optimizations 219 // Paranoia mode for debugging optimizations
210 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 220 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
211 config.unsafe_optimizations = true; 221 config.unsafe_optimizations = false;
212 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 222 config.optimizations = Dynarmic::no_optimizations;
213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 223 }
214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
216 } 224 }
217 225
218 return std::make_unique<Dynarmic::A32::Jit>(config); 226 return std::make_unique<Dynarmic::A32::Jit>(config);
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 1966d6e56..68822a1fc 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -84,6 +84,7 @@ public:
84 } 84 }
85 85
86 void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 86 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
87 parent.LogBacktrace();
87 LOG_ERROR(Core_ARM, 88 LOG_ERROR(Core_ARM,
88 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 89 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
89 num_instructions, MemoryReadCode(pc)); 90 num_instructions, MemoryReadCode(pc));
@@ -121,6 +122,7 @@ public:
121 return; 122 return;
122 case Dynarmic::A64::Exception::Breakpoint: 123 case Dynarmic::A64::Exception::Breakpoint:
123 default: 124 default:
125 parent.LogBacktrace();
124 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 126 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
125 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 127 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
126 } 128 }
@@ -245,35 +247,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
245 if (!Settings::values.cpuopt_recompile_exclusives) { 247 if (!Settings::values.cpuopt_recompile_exclusives) {
246 config.recompile_on_exclusive_fastmem_failure = false; 248 config.recompile_on_exclusive_fastmem_failure = false;
247 } 249 }
248 } 250 } else {
251 // Unsafe optimizations
252 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
253 config.unsafe_optimizations = true;
254 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
255 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
256 }
257 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
258 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
259 }
260 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
261 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
262 }
263 if (Settings::values.cpuopt_unsafe_fastmem_check) {
264 config.fastmem_address_space_bits = 64;
265 }
266 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
267 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
268 }
269 }
249 270
250 // Unsafe optimizations 271 // Curated optimizations
251 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 272 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
252 config.unsafe_optimizations = true; 273 config.unsafe_optimizations = true;
253 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
254 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
255 }
256 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
257 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
258 }
259 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
260 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 275 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
261 }
262 if (Settings::values.cpuopt_unsafe_fastmem_check) {
263 config.fastmem_address_space_bits = 64; 276 config.fastmem_address_space_bits = 64;
264 }
265 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
266 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
267 } 278 }
268 }
269 279
270 // Curated optimizations 280 // Paranoia mode for debugging optimizations
271 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 281 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
272 config.unsafe_optimizations = true; 282 config.unsafe_optimizations = false;
273 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 283 config.optimizations = Dynarmic::no_optimizations;
274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 284 }
275 config.fastmem_address_space_bits = 64;
276 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
277 } 285 }
278 286
279 return std::make_shared<Dynarmic::A64::Jit>(config); 287 return std::make_shared<Dynarmic::A64::Jit>(config);
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 7a646b5f1..4ada4a69b 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -387,15 +387,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
387 continue; 387 continue;
388 388
389 for (const auto& nca_dir : d2_dir->GetSubdirectories()) { 389 for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
390 if (!FollowsNcaIdFormat(nca_dir->GetName())) 390 if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
391 continue; 391 continue;
392 }
392 393
393 ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20))); 394 ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
394 } 395 }
395 396
396 for (const auto& nca_file : d2_dir->GetFiles()) { 397 for (const auto& nca_file : d2_dir->GetFiles()) {
397 if (!FollowsNcaIdFormat(nca_file->GetName())) 398 if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
398 continue; 399 continue;
400 }
399 401
400 ids.push_back( 402 ids.push_back(
401 Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20))); 403 Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 42d1b0e31..b547a3463 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -24,8 +24,15 @@
24 24
25namespace Kernel { 25namespace Kernel {
26 26
27SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_) 27SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
28 : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {} 28 ServiceThreadType thread_type)
29 : kernel{kernel_} {
30 if (thread_type == ServiceThreadType::CreateNew) {
31 service_thread = kernel.CreateServiceThread(service_name_);
32 } else {
33 service_thread = kernel.GetDefaultServiceThread();
34 }
35}
29 36
30SessionRequestHandler::~SessionRequestHandler() { 37SessionRequestHandler::~SessionRequestHandler() {
31 kernel.ReleaseServiceThread(service_thread); 38 kernel.ReleaseServiceThread(service_thread);
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 670cc741c..640146137 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -33,6 +33,11 @@ namespace Service {
33class ServiceFrameworkBase; 33class ServiceFrameworkBase;
34} 34}
35 35
36enum class ServiceThreadType {
37 Default,
38 CreateNew,
39};
40
36namespace Kernel { 41namespace Kernel {
37 42
38class Domain; 43class Domain;
@@ -57,7 +62,8 @@ enum class ThreadWakeupReason;
57 */ 62 */
58class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { 63class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
59public: 64public:
60 SessionRequestHandler(KernelCore& kernel, const char* service_name_); 65 SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
66 ServiceThreadType thread_type);
61 virtual ~SessionRequestHandler(); 67 virtual ~SessionRequestHandler();
62 68
63 /** 69 /**
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 34da7c23b..6387d0c29 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -61,6 +61,7 @@ struct KernelCore::Impl {
61 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 61 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
62 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 62 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
63 global_handle_table->Initialize(KHandleTable::MaxTableSize); 63 global_handle_table->Initialize(KHandleTable::MaxTableSize);
64 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
64 65
65 is_phantom_mode_for_singlecore = false; 66 is_phantom_mode_for_singlecore = false;
66 67
@@ -677,6 +678,12 @@ struct KernelCore::Impl {
677 678
678 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 679 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
679 if (auto strong_ptr = service_thread.lock()) { 680 if (auto strong_ptr = service_thread.lock()) {
681 if (strong_ptr == default_service_thread.lock()) {
682 // Nothing to do here, the service is using default_service_thread, which will be
683 // released on shutdown.
684 return;
685 }
686
680 service_threads_manager.QueueWork( 687 service_threads_manager.QueueWork(
681 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); 688 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
682 } 689 }
@@ -739,7 +746,8 @@ struct KernelCore::Impl {
739 std::unique_ptr<KMemoryLayout> memory_layout; 746 std::unique_ptr<KMemoryLayout> memory_layout;
740 747
741 // Threads used for services 748 // Threads used for services
742 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; 749 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
750 std::weak_ptr<ServiceThread> default_service_thread;
743 Common::ThreadWorker service_threads_manager; 751 Common::ThreadWorker service_threads_manager;
744 752
745 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; 753 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
@@ -1065,6 +1073,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
1065 return impl->CreateServiceThread(*this, name); 1073 return impl->CreateServiceThread(*this, name);
1066} 1074}
1067 1075
1076std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
1077 return impl->default_service_thread;
1078}
1079
1068void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 1080void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
1069 impl->ReleaseServiceThread(service_thread); 1081 impl->ReleaseServiceThread(service_thread);
1070} 1082}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 4c68e96df..24e26fa44 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -271,9 +271,11 @@ public:
271 void ExitSVCProfile(); 271 void ExitSVCProfile();
272 272
273 /** 273 /**
274 * Creates an HLE service thread, which are used to execute service routines asynchronously. 274 * Creates a host thread to execute HLE service requests, which are used to execute service
275 * While these are allocated per ServerSession, these need to be owned and managed outside 275 * routines asynchronously. While these are allocated per ServerSession, these need to be owned
276 * of ServerSession to avoid a circular dependency. 276 * and managed outside of ServerSession to avoid a circular dependency. In general, most
277 * services can just use the default service thread, and not need their own host service thread.
278 * See GetDefaultServiceThread.
277 * @param name String name for the ServerSession creating this thread, used for debug 279 * @param name String name for the ServerSession creating this thread, used for debug
278 * purposes. 280 * purposes.
279 * @returns The a weak pointer newly created service thread. 281 * @returns The a weak pointer newly created service thread.
@@ -281,6 +283,14 @@ public:
281 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); 283 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
282 284
283 /** 285 /**
286 * Gets the default host service thread, which executes HLE service requests. Unless service
287 * requests need to block on the host, the default service thread should be used in favor of
288 * creating a new service thread.
289 * @returns The a weak pointer for the default service thread.
290 */
291 std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
292
293 /**
284 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when 294 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
285 * the ServerSession associated with the thread is destroyed. 295 * the ServerSession associated with the thread is destroyed.
286 * @param service_thread Service thread to release. 296 * @param service_thread Service thread to release.
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 420de3c54..4d7e5ecd3 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1337,7 +1337,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1337 {200, nullptr, "GetLastApplicationExitReason"}, 1337 {200, nullptr, "GetLastApplicationExitReason"},
1338 {500, nullptr, "StartContinuousRecordingFlushForDebug"}, 1338 {500, nullptr, "StartContinuousRecordingFlushForDebug"},
1339 {1000, nullptr, "CreateMovieMaker"}, 1339 {1000, nullptr, "CreateMovieMaker"},
1340 {1001, nullptr, "PrepareForJit"}, 1340 {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
1341 }; 1341 };
1342 // clang-format on 1342 // clang-format on
1343 1343
@@ -1787,6 +1787,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
1787 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); 1787 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
1788} 1788}
1789 1789
1790void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) {
1791 LOG_WARNING(Service_AM, "(STUBBED) called");
1792
1793 IPC::ResponseBuilder rb{ctx, 2};
1794 rb.Push(ResultSuccess);
1795}
1796
1790void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, 1797void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
1791 Core::System& system) { 1798 Core::System& system) {
1792 auto message_queue = std::make_shared<AppletMessageQueue>(system); 1799 auto message_queue = std::make_shared<AppletMessageQueue>(system);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index fdd937b82..11a3c0459 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -336,6 +336,7 @@ private:
336 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); 336 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
337 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); 337 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
338 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); 338 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
339 void PrepareForJit(Kernel::HLERequestContext& ctx);
339 340
340 KernelHelpers::ServiceContext service_context; 341 KernelHelpers::ServiceContext service_context;
341 342
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 bb5cb61be..a4b3fb187 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -446,6 +446,14 @@ void WebBrowser::ExecuteLogin() {
446} 446}
447 447
448void WebBrowser::ExecuteOffline() { 448void WebBrowser::ExecuteOffline() {
449 // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
450 // Super Mario 3D All-Stars.
451 // TODO (Morph): Implement WebSession.
452 if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
453 LOG_WARNING(Service_AM, "WebSession is not implemented");
454 return;
455 }
456
449 const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); 457 const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
450 458
451 if (!Common::FS::Exists(main_url)) { 459 if (!Common::FS::Exists(main_url)) {
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index affa7971c..a72956a28 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -41,9 +41,10 @@ public:
41 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, 41 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
42 AudioCore::AudioOut& audio_core_, std::string&& device_name_, 42 AudioCore::AudioOut& audio_core_, std::string&& device_name_,
43 std::string&& unique_name) 43 std::string&& unique_name)
44 : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, 44 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
45 device_name{std::move(device_name_)}, audio_params{audio_params_}, 45 audio_core{audio_core_}, device_name{std::move(device_name_)},
46 main_memory{system.Memory()}, service_context{system_, "IAudioOut"} { 46 audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
47 "IAudioOut"} {
47 // clang-format off 48 // clang-format off
48 static const FunctionInfo functions[] = { 49 static const FunctionInfo functions[] = {
49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 50 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index f45e5cecc..d4ffeb21d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -24,7 +24,8 @@ public:
24 explicit IAudioRenderer(Core::System& system_, 24 explicit IAudioRenderer(Core::System& system_,
25 const AudioCommon::AudioRendererParameter& audren_params, 25 const AudioCommon::AudioRendererParameter& audren_params,
26 const std::size_t instance_number) 26 const std::size_t instance_number)
27 : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} { 27 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
28 service_context{system_, "IAudioRenderer"} {
28 // clang-format off 29 // clang-format off
29 static const FunctionInfo functions[] = { 30 static const FunctionInfo functions[] = {
30 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, 31 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3703ca4c6..4208337db 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -174,7 +174,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
174 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); 174 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
175 175
176 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), 176 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
177 "Could not write all of the bytes but everything else has succeded."); 177 "Could not write all of the bytes but everything else has succeeded.");
178 178
179 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { 179 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
180 // TODO(DarkLordZach): Find a better error code for this 180 // TODO(DarkLordZach): Find a better error code for this
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index b087e7bba..c07929ab8 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -58,7 +58,8 @@ enum class FileSystemType : u8 {
58class IStorage final : public ServiceFramework<IStorage> { 58class IStorage final : public ServiceFramework<IStorage> {
59public: 59public:
60 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) 60 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
61 : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { 61 : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew},
62 backend(std::move(backend_)) {
62 static const FunctionInfo functions[] = { 63 static const FunctionInfo functions[] = {
63 {0, &IStorage::Read, "Read"}, 64 {0, &IStorage::Read, "Read"},
64 {1, nullptr, "Write"}, 65 {1, nullptr, "Write"},
@@ -116,7 +117,8 @@ private:
116class IFile final : public ServiceFramework<IFile> { 117class IFile final : public ServiceFramework<IFile> {
117public: 118public:
118 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) 119 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
119 : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { 120 : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew},
121 backend(std::move(backend_)) {
120 static const FunctionInfo functions[] = { 122 static const FunctionInfo functions[] = {
121 {0, &IFile::Read, "Read"}, 123 {0, &IFile::Read, "Read"},
122 {1, &IFile::Write, "Write"}, 124 {1, &IFile::Write, "Write"},
@@ -252,7 +254,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
252class IDirectory final : public ServiceFramework<IDirectory> { 254class IDirectory final : public ServiceFramework<IDirectory> {
253public: 255public:
254 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_) 256 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
255 : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { 257 : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew},
258 backend(std::move(backend_)) {
256 static const FunctionInfo functions[] = { 259 static const FunctionInfo functions[] = {
257 {0, &IDirectory::Read, "Read"}, 260 {0, &IDirectory::Read, "Read"},
258 {1, &IDirectory::GetEntryCount, "GetEntryCount"}, 261 {1, &IDirectory::GetEntryCount, "GetEntryCount"},
@@ -308,8 +311,8 @@ private:
308class IFileSystem final : public ServiceFramework<IFileSystem> { 311class IFileSystem final : public ServiceFramework<IFileSystem> {
309public: 312public:
310 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) 313 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
311 : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( 314 : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew},
312 size_)} { 315 backend{std::move(backend_)}, size{std::move(size_)} {
313 static const FunctionInfo functions[] = { 316 static const FunctionInfo functions[] = {
314 {0, &IFileSystem::CreateFile, "CreateFile"}, 317 {0, &IFileSystem::CreateFile, "CreateFile"},
315 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 318 {1, &IFileSystem::DeleteFile, "DeleteFile"},
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e5c951e06..aa6cb34b7 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -262,11 +262,6 @@ void Controller_NPad::OnInit() {
262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); 262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
263 } 263 }
264 264
265 if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
266 // We want to support all controllers
267 hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
268 }
269
270 supported_npad_id_types.resize(npad_id_list.size()); 265 supported_npad_id_types.resize(npad_id_list.size());
271 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 266 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
272 npad_id_list.size() * sizeof(Core::HID::NpadIdType)); 267 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
@@ -288,14 +283,6 @@ void Controller_NPad::OnInit() {
288 WriteEmptyEntry(npad); 283 WriteEmptyEntry(npad);
289 } 284 }
290 } 285 }
291
292 // Connect controllers
293 for (auto& controller : controller_data) {
294 const auto& device = controller.device;
295 if (device->IsConnected()) {
296 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
297 }
298 }
299} 286}
300 287
301void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { 288void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
@@ -320,6 +307,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
320} 307}
321 308
322void Controller_NPad::OnRelease() { 309void Controller_NPad::OnRelease() {
310 is_controller_initialized = false;
323 for (std::size_t i = 0; i < controller_data.size(); ++i) { 311 for (std::size_t i = 0; i < controller_data.size(); ++i) {
324 auto& controller = controller_data[i]; 312 auto& controller = controller_data[i];
325 service_context.CloseEvent(controller.styleset_changed_event); 313 service_context.CloseEvent(controller.styleset_changed_event);
@@ -651,9 +639,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
651 639
652void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { 640void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
653 hid_core.SetSupportedStyleTag(style_set); 641 hid_core.SetSupportedStyleTag(style_set);
642
643 if (is_controller_initialized) {
644 return;
645 }
646
647 // Once SetSupportedStyleSet is called controllers are fully initialized
648 is_controller_initialized = true;
649
650 // Connect all active controllers
651 for (auto& controller : controller_data) {
652 const auto& device = controller.device;
653 if (device->IsConnected()) {
654 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
655 }
656 }
654} 657}
655 658
656Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 659Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
660 if (!is_controller_initialized) {
661 return {Core::HID::NpadStyleSet::None};
662 }
657 return hid_core.GetSupportedStyleTag(); 663 return hid_core.GetSupportedStyleTag();
658} 664}
659 665
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 3287cf435..967379f05 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -191,16 +191,16 @@ private:
191 191
192 // This is nn::hid::detail::NpadFullKeyColorState 192 // This is nn::hid::detail::NpadFullKeyColorState
193 struct NpadFullKeyColorState { 193 struct NpadFullKeyColorState {
194 ColorAttribute attribute; 194 ColorAttribute attribute{ColorAttribute::NoController};
195 Core::HID::NpadControllerColor fullkey; 195 Core::HID::NpadControllerColor fullkey{};
196 }; 196 };
197 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); 197 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
198 198
199 // This is nn::hid::detail::NpadJoyColorState 199 // This is nn::hid::detail::NpadJoyColorState
200 struct NpadJoyColorState { 200 struct NpadJoyColorState {
201 ColorAttribute attribute; 201 ColorAttribute attribute{ColorAttribute::NoController};
202 Core::HID::NpadControllerColor left; 202 Core::HID::NpadControllerColor left{};
203 Core::HID::NpadControllerColor right; 203 Core::HID::NpadControllerColor right{};
204 }; 204 };
205 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); 205 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
206 206
@@ -226,11 +226,11 @@ private:
226 // This is nn::hid::NpadPalmaState 226 // This is nn::hid::NpadPalmaState
227 // This is nn::hid::NpadSystemExtState 227 // This is nn::hid::NpadSystemExtState
228 struct NPadGenericState { 228 struct NPadGenericState {
229 s64_le sampling_number; 229 s64_le sampling_number{};
230 Core::HID::NpadButtonState npad_buttons; 230 Core::HID::NpadButtonState npad_buttons{};
231 Core::HID::AnalogStickState l_stick; 231 Core::HID::AnalogStickState l_stick{};
232 Core::HID::AnalogStickState r_stick; 232 Core::HID::AnalogStickState r_stick{};
233 NpadAttribute connection_status; 233 NpadAttribute connection_status{};
234 INSERT_PADDING_BYTES(4); // Reserved 234 INSERT_PADDING_BYTES(4); // Reserved
235 }; 235 };
236 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 236 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -253,7 +253,7 @@ private:
253 Common::Vec3f gyro{}; 253 Common::Vec3f gyro{};
254 Common::Vec3f rotation{}; 254 Common::Vec3f rotation{};
255 std::array<Common::Vec3f, 3> orientation{}; 255 std::array<Common::Vec3f, 3> orientation{};
256 SixAxisSensorAttribute attribute; 256 SixAxisSensorAttribute attribute{};
257 INSERT_PADDING_BYTES(4); // Reserved 257 INSERT_PADDING_BYTES(4); // Reserved
258 }; 258 };
259 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); 259 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -325,11 +325,11 @@ private:
325 325
326 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl 326 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
327 struct NfcXcdDeviceHandleStateImpl { 327 struct NfcXcdDeviceHandleStateImpl {
328 u64 handle; 328 u64 handle{};
329 bool is_available; 329 bool is_available{};
330 bool is_activated; 330 bool is_activated{};
331 INSERT_PADDING_BYTES(0x6); // Reserved 331 INSERT_PADDING_BYTES(0x6); // Reserved
332 u64 sampling_number; 332 u64 sampling_number{};
333 }; 333 };
334 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 334 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
335 "NfcXcdDeviceHandleStateImpl is an invalid size"); 335 "NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -366,8 +366,8 @@ private:
366 }; 366 };
367 367
368 struct AppletFooterUi { 368 struct AppletFooterUi {
369 AppletFooterUiAttributes attributes; 369 AppletFooterUiAttributes attributes{};
370 AppletFooterUiType type; 370 AppletFooterUiType type{AppletFooterUiType::None};
371 INSERT_PADDING_BYTES(0x5B); // Reserved 371 INSERT_PADDING_BYTES(0x5B); // Reserved
372 }; 372 };
373 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); 373 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -404,41 +404,41 @@ private:
404 404
405 // This is nn::hid::detail::NpadInternalState 405 // This is nn::hid::detail::NpadInternalState
406 struct NpadInternalState { 406 struct NpadInternalState {
407 Core::HID::NpadStyleTag style_tag; 407 Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
408 NpadJoyAssignmentMode assignment_mode; 408 NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
409 NpadFullKeyColorState fullkey_color; 409 NpadFullKeyColorState fullkey_color{};
410 NpadJoyColorState joycon_color; 410 NpadJoyColorState joycon_color{};
411 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; 411 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
412 Lifo<NPadGenericState, hid_entry_count> handheld_lifo; 412 Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
413 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; 413 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
414 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; 414 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
415 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; 415 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
416 Lifo<NPadGenericState, hid_entry_count> palma_lifo; 416 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
417 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; 417 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
418 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; 418 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
419 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; 419 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
420 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; 420 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
421 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; 421 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
422 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; 422 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
423 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; 423 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
424 DeviceType device_type; 424 DeviceType device_type{};
425 INSERT_PADDING_BYTES(0x4); // Reserved 425 INSERT_PADDING_BYTES(0x4); // Reserved
426 NPadSystemProperties system_properties; 426 NPadSystemProperties system_properties{};
427 NpadSystemButtonProperties button_properties; 427 NpadSystemButtonProperties button_properties{};
428 Core::HID::NpadBatteryLevel battery_level_dual; 428 Core::HID::NpadBatteryLevel battery_level_dual{};
429 Core::HID::NpadBatteryLevel battery_level_left; 429 Core::HID::NpadBatteryLevel battery_level_left{};
430 Core::HID::NpadBatteryLevel battery_level_right; 430 Core::HID::NpadBatteryLevel battery_level_right{};
431 union { 431 union {
432 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; 432 AppletFooterUi applet_footer{};
433 AppletFooterUi applet_footer; 433 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
434 }; 434 };
435 INSERT_PADDING_BYTES(0x20); // Unknown 435 INSERT_PADDING_BYTES(0x20); // Unknown
436 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; 436 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
437 NpadLarkType lark_type_l_and_main; 437 NpadLarkType lark_type_l_and_main{};
438 NpadLarkType lark_type_r; 438 NpadLarkType lark_type_r{};
439 NpadLuciaType lucia_type; 439 NpadLuciaType lucia_type{};
440 NpadLagonType lagon_type; 440 NpadLagonType lagon_type{};
441 NpadLagerType lager_type; 441 NpadLagerType lager_type{};
442 // FW 13.x Investigate there is some sort of bitflag related to joycons 442 // FW 13.x Investigate there is some sort of bitflag related to joycons
443 INSERT_PADDING_BYTES(0x4); 443 INSERT_PADDING_BYTES(0x4);
444 INSERT_PADDING_BYTES(0xc08); // Unknown 444 INSERT_PADDING_BYTES(0xc08); // Unknown
@@ -511,7 +511,8 @@ private:
511 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 511 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
512 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 512 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
513 bool permit_vibration_session_enabled{false}; 513 bool permit_vibration_session_enabled{false};
514 bool analog_stick_use_center_clamp{}; 514 bool analog_stick_use_center_clamp{false};
515 bool is_in_lr_assignment_mode{false}; 515 bool is_in_lr_assignment_mode{false};
516 bool is_controller_initialized{false};
516}; 517};
517} // namespace Service::HID 518} // namespace Service::HID
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
new file mode 100644
index 000000000..c8ebd2e3f
--- /dev/null
+++ b/src/core/hle/service/jit/jit.cpp
@@ -0,0 +1,53 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/ipc_helpers.h"
6#include "core/hle/result.h"
7#include "core/hle/service/jit/jit.h"
8#include "core/hle/service/service.h"
9
10namespace Service::JIT {
11
12class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
13public:
14 explicit IJitEnvironment(Core::System& system_) : ServiceFramework{system_, "IJitEnvironment"} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, nullptr, "GenerateCode"},
18 {1, nullptr, "Control"},
19 {1000, nullptr, "LoadPlugin"},
20 {1001, nullptr, "GetCodeAddress"},
21 };
22 // clang-format on
23
24 RegisterHandlers(functions);
25 }
26};
27
28class JITU final : public ServiceFramework<JITU> {
29public:
30 explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
31 // clang-format off
32 static const FunctionInfo functions[] = {
33 {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
34 };
35 // clang-format on
36
37 RegisterHandlers(functions);
38 }
39
40 void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
41 LOG_DEBUG(Service_JIT, "called");
42
43 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
44 rb.Push(ResultSuccess);
45 rb.PushIpcInterface<IJitEnvironment>(system);
46 }
47};
48
49void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
50 std::make_shared<JITU>(system)->InstallAsService(sm);
51}
52
53} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit.h b/src/core/hle/service/jit/jit.h
new file mode 100644
index 000000000..8fbf504a1
--- /dev/null
+++ b/src/core/hle/service/jit/jit.h
@@ -0,0 +1,20 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Core {
8class System;
9}
10
11namespace Service::SM {
12class ServiceManager;
13}
14
15namespace Service::JIT {
16
17/// Registers all JIT services with the specified service manager.
18void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
19
20} // namespace Service::JIT
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index c16babe14..8467b50e4 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -26,7 +26,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
26 rb.Push<DeviceFD>(0); 26 rb.Push<DeviceFD>(0);
27 rb.PushEnum(NvResult::NotInitialized); 27 rb.PushEnum(NvResult::NotInitialized);
28 28
29 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 29 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
30 return; 30 return;
31 } 31 }
32 32
@@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
61 61
62 if (!is_initialized) { 62 if (!is_initialized) {
63 ServiceError(ctx, NvResult::NotInitialized); 63 ServiceError(ctx, NvResult::NotInitialized);
64 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 64 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
65 return; 65 return;
66 } 66 }
67 67
@@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
87 87
88 if (!is_initialized) { 88 if (!is_initialized) {
89 ServiceError(ctx, NvResult::NotInitialized); 89 ServiceError(ctx, NvResult::NotInitialized);
90 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 90 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
91 return; 91 return;
92 } 92 }
93 93
@@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
114 114
115 if (!is_initialized) { 115 if (!is_initialized) {
116 ServiceError(ctx, NvResult::NotInitialized); 116 ServiceError(ctx, NvResult::NotInitialized);
117 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 117 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
118 return; 118 return;
119 } 119 }
120 120
@@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
139 139
140 if (!is_initialized) { 140 if (!is_initialized) {
141 ServiceError(ctx, NvResult::NotInitialized); 141 ServiceError(ctx, NvResult::NotInitialized);
142 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 142 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
143 return; 143 return;
144 } 144 }
145 145
@@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
170 170
171 if (!is_initialized) { 171 if (!is_initialized) {
172 ServiceError(ctx, NvResult::NotInitialized); 172 ServiceError(ctx, NvResult::NotInitialized);
173 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 173 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
174 return; 174 return;
175 } 175 }
176 176
@@ -230,7 +230,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
230} 230}
231 231
232NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) 232NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name)
233 : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { 233 : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} {
234 static const FunctionInfo functions[] = { 234 static const FunctionInfo functions[] = {
235 {0, &NVDRV::Open, "Open"}, 235 {0, &NVDRV::Open, "Open"},
236 {1, &NVDRV::Ioctl1, "Ioctl"}, 236 {1, &NVDRV::Ioctl1, "Ioctl"},
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index 41fbba219..c527c577e 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -18,8 +18,7 @@ BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
18BufferQueueConsumer::~BufferQueueConsumer() = default; 18BufferQueueConsumer::~BufferQueueConsumer() = default;
19 19
20Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, 20Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
21 std::chrono::nanoseconds expected_present, 21 std::chrono::nanoseconds expected_present) {
22 u64 max_frame_number) {
23 std::scoped_lock lock(core->mutex); 22 std::scoped_lock lock(core->mutex);
24 23
25 // Check that the consumer doesn't currently have the maximum number of buffers acquired. 24 // Check that the consumer doesn't currently have the maximum number of buffers acquired.
@@ -50,12 +49,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
50 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { 49 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
51 const auto& buffer_item{core->queue[1]}; 50 const auto& buffer_item{core->queue[1]};
52 51
53 // If dropping entry[0] would leave us with a buffer that the consumer is not yet ready
54 // for, don't drop it.
55 if (max_frame_number && buffer_item.frame_number > max_frame_number) {
56 break;
57 }
58
59 // If entry[1] is timely, drop entry[0] (and repeat). 52 // If entry[1] is timely, drop entry[0] (and repeat).
60 const auto desired_present = buffer_item.timestamp; 53 const auto desired_present = buffer_item.timestamp;
61 if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || 54 if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
@@ -200,4 +193,39 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
200 return Status::NoError; 193 return Status::NoError;
201} 194}
202 195
196Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
197 if (out_slot_mask == nullptr) {
198 LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
199 return Status::BadValue;
200 }
201
202 std::scoped_lock lock(core->mutex);
203
204 if (core->is_abandoned) {
205 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
206 return Status::NoInit;
207 }
208
209 u64 mask = 0;
210 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
211 if (!slots[s].acquire_called) {
212 mask |= (1ULL << s);
213 }
214 }
215
216 // Remove from the mask queued buffers for which acquire has been called, since the consumer
217 // will not receive their buffer addresses and so must retain their cached information
218 auto current(core->queue.begin());
219 while (current != core->queue.end()) {
220 if (current->acquire_called) {
221 mask &= ~(1ULL << current->slot);
222 }
223 ++current;
224 }
225
226 LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
227 *out_slot_mask = mask;
228 return Status::NoError;
229}
230
203} // namespace Service::android 231} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
index f22854394..8a047fe06 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -24,10 +24,10 @@ public:
24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); 24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
25 ~BufferQueueConsumer(); 25 ~BufferQueueConsumer();
26 26
27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present, 27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
28 u64 max_frame_number = 0);
29 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); 28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
30 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); 29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30 Status GetReleasedBuffers(u64* out_slot_mask);
31 31
32private: 32private:
33 std::shared_ptr<BufferQueueCore> core; 33 std::shared_ptr<BufferQueueCore> core;
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
index 6082610e0..3a0481786 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -95,7 +95,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
95} 95}
96 96
97void BufferQueueCore::FreeAllBuffersLocked() { 97void BufferQueueCore::FreeAllBuffersLocked() {
98 queue.clear();
99 buffer_has_been_queued = false; 98 buffer_has_been_queued = false;
100 99
101 for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { 100 for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h
index 4dfd53387..e4e0937cb 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -73,8 +73,6 @@ private:
73 u32 transform_hint{}; 73 u32 transform_hint{};
74 bool is_allocating{}; 74 bool is_allocating{};
75 mutable std::condition_variable_any is_allocating_condition; 75 mutable std::condition_variable_any is_allocating_condition;
76 bool allow_allocation{true};
77 u64 buffer_age{};
78 bool is_shutting_down{}; 76 bool is_shutting_down{};
79}; 77};
80 78
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index 0833be57a..3d6e990c3 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -62,11 +62,12 @@ Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffe
62 62
63Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { 63Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
64 LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count); 64 LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
65 std::shared_ptr<IConsumerListener> listener;
66 65
66 std::shared_ptr<IConsumerListener> listener;
67 { 67 {
68 std::scoped_lock lock(core->mutex); 68 std::scoped_lock lock(core->mutex);
69 core->WaitWhileAllocatingLocked(); 69 core->WaitWhileAllocatingLocked();
70
70 if (core->is_abandoned) { 71 if (core->is_abandoned) {
71 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 72 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
72 return Status::NoInit; 73 return Status::NoInit;
@@ -120,7 +121,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
120} 121}
121 122
122Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, 123Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
123 Status* returnFlags) const { 124 Status* return_flags) const {
124 bool try_again = true; 125 bool try_again = true;
125 126
126 while (try_again) { 127 while (try_again) {
@@ -142,10 +143,12 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
142 ASSERT(slots[s].buffer_state == BufferState::Free); 143 ASSERT(slots[s].buffer_state == BufferState::Free);
143 if (slots[s].graphic_buffer != nullptr) { 144 if (slots[s].graphic_buffer != nullptr) {
144 core->FreeBufferLocked(s); 145 core->FreeBufferLocked(s);
145 *returnFlags |= Status::ReleaseAllBuffers; 146 *return_flags |= Status::ReleaseAllBuffers;
146 } 147 }
147 } 148 }
148 149
150 // Look for a free buffer to give to the client
151 *found = BufferQueueCore::INVALID_BUFFER_SLOT;
149 s32 dequeued_count{}; 152 s32 dequeued_count{};
150 s32 acquired_count{}; 153 s32 acquired_count{};
151 for (s32 s{}; s < max_buffer_count; ++s) { 154 for (s32 s{}; s < max_buffer_count; ++s) {
@@ -235,68 +238,50 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
235 { 238 {
236 std::scoped_lock lock(core->mutex); 239 std::scoped_lock lock(core->mutex);
237 core->WaitWhileAllocatingLocked(); 240 core->WaitWhileAllocatingLocked();
241
238 if (format == PixelFormat::NoFormat) { 242 if (format == PixelFormat::NoFormat) {
239 format = core->default_buffer_format; 243 format = core->default_buffer_format;
240 } 244 }
241 245
242 // Enable the usage bits the consumer requested 246 // Enable the usage bits the consumer requested
243 usage |= core->consumer_usage_bit; 247 usage |= core->consumer_usage_bit;
244 const bool use_default_size = !width && !height; 248
245 if (use_default_size) { 249 s32 found{};
246 width = core->default_width; 250 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
247 height = core->default_height; 251 if (status != Status::NoError) {
252 return status;
248 } 253 }
249 254
250 s32 found = BufferItem::INVALID_BUFFER_SLOT; 255 // This should not happen
251 while (found == BufferItem::INVALID_BUFFER_SLOT) { 256 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
252 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); 257 LOG_ERROR(Service_NVFlinger, "no available buffer slots");
253 if (status != Status::NoError) { 258 return Status::Busy;
254 return status; 259 }
255 }
256 260
257 // This should not happen 261 *out_slot = found;
258 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
259 LOG_DEBUG(Service_NVFlinger, "no available buffer slots");
260 return Status::Busy;
261 }
262 262
263 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); 263 attached_by_consumer = slots[found].attached_by_consumer;
264 264
265 // If we are not allowed to allocate new buffers, WaitForFreeSlotThenRelock must have 265 const bool use_default_size = !width && !height;
266 // returned a slot containing a buffer. If this buffer would require reallocation to 266 if (use_default_size) {
267 // meet the requested attributes, we free it and attempt to get another one. 267 width = core->default_width;
268 if (!core->allow_allocation) { 268 height = core->default_height;
269 if (buffer->NeedsReallocation(width, height, format, usage)) {
270 core->FreeBufferLocked(found);
271 found = BufferItem::INVALID_BUFFER_SLOT;
272 continue;
273 }
274 }
275 } 269 }
276 270
277 *out_slot = found;
278 attached_by_consumer = slots[found].attached_by_consumer;
279 slots[found].buffer_state = BufferState::Dequeued; 271 slots[found].buffer_state = BufferState::Dequeued;
280 272
281 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); 273 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
282 274 if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) ||
283 if ((buffer == nullptr) || buffer->NeedsReallocation(width, height, format, usage)) { 275 (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) {
284 slots[found].acquire_called = false; 276 slots[found].acquire_called = false;
285 slots[found].graphic_buffer = nullptr; 277 slots[found].graphic_buffer = nullptr;
286 slots[found].request_buffer_called = false; 278 slots[found].request_buffer_called = false;
287 slots[found].fence = Fence::NoFence(); 279 slots[found].fence = Fence::NoFence();
288 core->buffer_age = 0; 280
289 return_flags |= Status::BufferNeedsReallocation; 281 return_flags |= Status::BufferNeedsReallocation;
290 } else {
291 // We add 1 because that will be the frame number when this buffer
292 // is queued
293 core->buffer_age = core->frame_counter + 1 - slots[found].frame_number;
294 } 282 }
295 283
296 LOG_DEBUG(Service_NVFlinger, "setting buffer age to {}", core->buffer_age);
297
298 *out_fence = slots[found].fence; 284 *out_fence = slots[found].fence;
299
300 slots[found].fence = Fence::NoFence(); 285 slots[found].fence = Fence::NoFence();
301 } 286 }
302 287
@@ -311,6 +296,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
311 296
312 { 297 {
313 std::scoped_lock lock(core->mutex); 298 std::scoped_lock lock(core->mutex);
299
314 if (core->is_abandoned) { 300 if (core->is_abandoned) {
315 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 301 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
316 return Status::NoInit; 302 return Status::NoInit;
@@ -327,6 +313,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
327 313
328 LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot, 314 LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
329 slots[*out_slot].frame_number, return_flags); 315 slots[*out_slot].frame_number, return_flags);
316
330 return return_flags; 317 return return_flags;
331} 318}
332 319
@@ -334,6 +321,7 @@ Status BufferQueueProducer::DetachBuffer(s32 slot) {
334 LOG_DEBUG(Service_NVFlinger, "slot {}", slot); 321 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
335 322
336 std::scoped_lock lock(core->mutex); 323 std::scoped_lock lock(core->mutex);
324
337 if (core->is_abandoned) { 325 if (core->is_abandoned) {
338 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 326 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
339 return Status::NoInit; 327 return Status::NoInit;
@@ -369,7 +357,6 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out
369 } 357 }
370 358
371 std::scoped_lock lock(core->mutex); 359 std::scoped_lock lock(core->mutex);
372
373 core->WaitWhileAllocatingLocked(); 360 core->WaitWhileAllocatingLocked();
374 361
375 if (core->is_abandoned) { 362 if (core->is_abandoned) {
@@ -423,6 +410,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
423 return status; 410 return status;
424 } 411 }
425 412
413 // This should not happen
426 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { 414 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
427 LOG_ERROR(Service_NVFlinger, "No available buffer slots"); 415 LOG_ERROR(Service_NVFlinger, "No available buffer slots");
428 return Status::Busy; 416 return Status::Busy;
@@ -466,8 +454,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
466 return Status::BadValue; 454 return Status::BadValue;
467 } 455 }
468 456
469 std::shared_ptr<IConsumerListener> frameAvailableListener; 457 std::shared_ptr<IConsumerListener> frame_available_listener;
470 std::shared_ptr<IConsumerListener> frameReplacedListener; 458 std::shared_ptr<IConsumerListener> frame_replaced_listener;
471 s32 callback_ticket{}; 459 s32 callback_ticket{};
472 BufferItem item; 460 BufferItem item;
473 461
@@ -541,12 +529,13 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
541 item.fence = fence; 529 item.fence = fence;
542 item.is_droppable = core->dequeue_buffer_cannot_block || async; 530 item.is_droppable = core->dequeue_buffer_cannot_block || async;
543 item.swap_interval = swap_interval; 531 item.swap_interval = swap_interval;
532
544 sticky_transform = sticky_transform_; 533 sticky_transform = sticky_transform_;
545 534
546 if (core->queue.empty()) { 535 if (core->queue.empty()) {
547 // When the queue is empty, we can simply queue this buffer 536 // When the queue is empty, we can simply queue this buffer
548 core->queue.push_back(item); 537 core->queue.push_back(item);
549 frameAvailableListener = core->consumer_listener; 538 frame_available_listener = core->consumer_listener;
550 } else { 539 } else {
551 // When the queue is not empty, we need to look at the front buffer 540 // When the queue is not empty, we need to look at the front buffer
552 // state to see if we need to replace it 541 // state to see if we need to replace it
@@ -563,10 +552,10 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
563 } 552 }
564 // Overwrite the droppable buffer with the incoming one 553 // Overwrite the droppable buffer with the incoming one
565 *front = item; 554 *front = item;
566 frameReplacedListener = core->consumer_listener; 555 frame_replaced_listener = core->consumer_listener;
567 } else { 556 } else {
568 core->queue.push_back(item); 557 core->queue.push_back(item);
569 frameAvailableListener = core->consumer_listener; 558 frame_available_listener = core->consumer_listener;
570 } 559 }
571 } 560 }
572 561
@@ -592,10 +581,10 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
592 callback_condition.wait(callback_mutex); 581 callback_condition.wait(callback_mutex);
593 } 582 }
594 583
595 if (frameAvailableListener != nullptr) { 584 if (frame_available_listener != nullptr) {
596 frameAvailableListener->OnFrameAvailable(item); 585 frame_available_listener->OnFrameAvailable(item);
597 } else if (frameReplacedListener != nullptr) { 586 } else if (frame_replaced_listener != nullptr) {
598 frameReplacedListener->OnFrameReplaced(item); 587 frame_replaced_listener->OnFrameReplaced(item);
599 } 588 }
600 589
601 ++current_callback_ticket; 590 ++current_callback_ticket;
@@ -669,13 +658,6 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
669 case NativeWindow::ConsumerUsageBits: 658 case NativeWindow::ConsumerUsageBits:
670 value = core->consumer_usage_bit; 659 value = core->consumer_usage_bit;
671 break; 660 break;
672 case NativeWindow::BufferAge:
673 if (core->buffer_age > INT32_MAX) {
674 value = 0;
675 } else {
676 value = static_cast<u32>(core->buffer_age);
677 }
678 break;
679 default: 661 default:
680 UNREACHABLE(); 662 UNREACHABLE();
681 return Status::BadValue; 663 return Status::BadValue;
@@ -737,7 +719,6 @@ Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& li
737 core->buffer_has_been_queued = false; 719 core->buffer_has_been_queued = false;
738 core->dequeue_buffer_cannot_block = 720 core->dequeue_buffer_cannot_block =
739 core->consumer_controlled_by_app && producer_controlled_by_app; 721 core->consumer_controlled_by_app && producer_controlled_by_app;
740 core->allow_allocation = true;
741 722
742 return status; 723 return status;
743} 724}
@@ -770,7 +751,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
770 core->SignalDequeueCondition(); 751 core->SignalDequeueCondition();
771 buffer_wait_event->GetWritableEvent().Signal(); 752 buffer_wait_event->GetWritableEvent().Signal();
772 listener = core->consumer_listener; 753 listener = core->consumer_listener;
773 } else if (core->connected_api != NativeWindowApi::NoConnectedApi) { 754 } else {
774 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", 755 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
775 core->connected_api, api); 756 core->connected_api, api);
776 status = Status::BadValue; 757 status = Status::BadValue;
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
index 77fdcae8e..c4ca68fd3 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -66,7 +66,7 @@ public:
66private: 66private:
67 BufferQueueProducer(const BufferQueueProducer&) = delete; 67 BufferQueueProducer(const BufferQueueProducer&) = delete;
68 68
69 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* returnFlags) const; 69 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
70 70
71 Kernel::KEvent* buffer_wait_event{}; 71 Kernel::KEvent* buffer_wait_event{};
72 Service::KernelHelpers::ServiceContext& service_context; 72 Service::KernelHelpers::ServiceContext& service_context;
diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp
index be65a3f88..c2c80832c 100644
--- a/src/core/hle/service/nvflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -36,38 +36,41 @@ void ConsumerBase::FreeBufferLocked(s32 slot_index) {
36} 36}
37 37
38void ConsumerBase::OnFrameAvailable(const BufferItem& item) { 38void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
39 std::scoped_lock lock(mutex);
40 LOG_DEBUG(Service_NVFlinger, "called"); 39 LOG_DEBUG(Service_NVFlinger, "called");
41} 40}
42 41
43void ConsumerBase::OnFrameReplaced(const BufferItem& item) { 42void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
44 std::scoped_lock lock(mutex);
45 LOG_DEBUG(Service_NVFlinger, "called"); 43 LOG_DEBUG(Service_NVFlinger, "called");
46} 44}
47 45
48void ConsumerBase::OnBuffersReleased() { 46void ConsumerBase::OnBuffersReleased() {
49 std::scoped_lock lock(mutex); 47 std::scoped_lock lock(mutex);
50 LOG_DEBUG(Service_NVFlinger, "called");
51}
52 48
53void ConsumerBase::OnSidebandStreamChanged() {} 49 LOG_DEBUG(Service_NVFlinger, "called");
54 50
55Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when,
56 u64 max_frame_number) {
57 if (is_abandoned) { 51 if (is_abandoned) {
58 LOG_ERROR(Service_NVFlinger, "consumer is abandoned!"); 52 // Nothing to do if we're already abandoned.
59 return Status::NoInit; 53 return;
60 } 54 }
61 55
62 Status err = consumer->AcquireBuffer(item, present_when, max_frame_number); 56 u64 mask = 0;
57 consumer->GetReleasedBuffers(&mask);
58 for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
59 if (mask & (1ULL << i)) {
60 FreeBufferLocked(i);
61 }
62 }
63}
64
65void ConsumerBase::OnSidebandStreamChanged() {}
66
67Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
68 Status err = consumer->AcquireBuffer(item, present_when);
63 if (err != Status::NoError) { 69 if (err != Status::NoError) {
64 return err; 70 return err;
65 } 71 }
66 72
67 if (item->graphic_buffer != nullptr) { 73 if (item->graphic_buffer != nullptr) {
68 if (slots[item->slot].graphic_buffer != nullptr) {
69 FreeBufferLocked(item->slot);
70 }
71 slots[item->slot].graphic_buffer = item->graphic_buffer; 74 slots[item->slot].graphic_buffer = item->graphic_buffer;
72 } 75 }
73 76
diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h
index 9ab949420..736080e3a 100644
--- a/src/core/hle/service/nvflinger/consumer_base.h
+++ b/src/core/hle/service/nvflinger/consumer_base.h
@@ -35,8 +35,7 @@ protected:
35 virtual void OnSidebandStreamChanged() override; 35 virtual void OnSidebandStreamChanged() override;
36 36
37 void FreeBufferLocked(s32 slot_index); 37 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when, 38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 u64 max_frame_number = 0);
40 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer); 39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
41 bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const; 40 bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
42 Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer, 41 Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 76ce1fbfd..6fb2cdff1 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -104,7 +104,7 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
104std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { 104std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
105 const auto lock_guard = Lock(); 105 const auto lock_guard = Lock();
106 106
107 LOG_DEBUG(Service, "Opening \"{}\" display", name); 107 LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
108 108
109 const auto itr = 109 const auto itr =
110 std::find_if(displays.begin(), displays.end(), 110 std::find_if(displays.begin(), displays.end(),
@@ -219,7 +219,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
219 auto* layer = display->FindLayer(layer_id); 219 auto* layer = display->FindLayer(layer_id);
220 220
221 if (layer == nullptr) { 221 if (layer == nullptr) {
222 LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id); 222 LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
223 CreateLayerAtId(*display, layer_id); 223 CreateLayerAtId(*display, layer_id);
224 return display->FindLayer(layer_id); 224 return display->FindLayer(layer_id);
225 } 225 }
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index ab3286db9..0f59a03c5 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -32,6 +32,7 @@
32#include "core/hle/service/glue/glue.h" 32#include "core/hle/service/glue/glue.h"
33#include "core/hle/service/grc/grc.h" 33#include "core/hle/service/grc/grc.h"
34#include "core/hle/service/hid/hid.h" 34#include "core/hle/service/hid/hid.h"
35#include "core/hle/service/jit/jit.h"
35#include "core/hle/service/lbl/lbl.h" 36#include "core/hle/service/lbl/lbl.h"
36#include "core/hle/service/ldn/ldn.h" 37#include "core/hle/service/ldn/ldn.h"
37#include "core/hle/service/ldr/ldr.h" 38#include "core/hle/service/ldr/ldr.h"
@@ -91,8 +92,9 @@ namespace Service {
91} 92}
92 93
93ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, 94ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
94 u32 max_sessions_, InvokerFn* handler_invoker_) 95 ServiceThreadType thread_type, u32 max_sessions_,
95 : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, 96 InvokerFn* handler_invoker_)
97 : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_},
96 service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {} 98 service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
97 99
98ServiceFrameworkBase::~ServiceFrameworkBase() { 100ServiceFrameworkBase::~ServiceFrameworkBase() {
@@ -261,6 +263,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
261 Glue::InstallInterfaces(system); 263 Glue::InstallInterfaces(system);
262 GRC::InstallInterfaces(*sm, system); 264 GRC::InstallInterfaces(*sm, system);
263 HID::InstallInterfaces(*sm, system); 265 HID::InstallInterfaces(*sm, system);
266 JIT::InstallInterfaces(*sm, system);
264 LBL::InstallInterfaces(*sm, system); 267 LBL::InstallInterfaces(*sm, system);
265 LDN::InstallInterfaces(*sm, system); 268 LDN::InstallInterfaces(*sm, system);
266 LDR::InstallInterfaces(*sm, system); 269 LDR::InstallInterfaces(*sm, system);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index b9ab2c465..c78b2baeb 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -114,7 +114,8 @@ private:
114 Kernel::HLERequestContext& ctx); 114 Kernel::HLERequestContext& ctx);
115 115
116 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, 116 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
117 u32 max_sessions_, InvokerFn* handler_invoker_); 117 ServiceThreadType thread_type, u32 max_sessions_,
118 InvokerFn* handler_invoker_);
118 ~ServiceFrameworkBase() override; 119 ~ServiceFrameworkBase() override;
119 120
120 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); 121 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
@@ -176,14 +177,17 @@ protected:
176 /** 177 /**
177 * Initializes the handler with no functions installed. 178 * Initializes the handler with no functions installed.
178 * 179 *
179 * @param system_ The system context to construct this service under. 180 * @param system_ The system context to construct this service under.
180 * @param service_name_ Name of the service. 181 * @param service_name_ Name of the service.
181 * @param max_sessions_ Maximum number of sessions that can be 182 * @param thread_type Specifies the thread type for this service. If this is set to CreateNew,
182 * connected to this service at the same time. 183 * it creates a new thread for it, otherwise this uses the default thread.
184 * @param max_sessions_ Maximum number of sessions that can be connected to this service at the
185 * same time.
183 */ 186 */
184 explicit ServiceFramework(Core::System& system_, const char* service_name_, 187 explicit ServiceFramework(Core::System& system_, const char* service_name_,
188 ServiceThreadType thread_type = ServiceThreadType::Default,
185 u32 max_sessions_ = ServerSessionCountMax) 189 u32 max_sessions_ = ServerSessionCountMax)
186 : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} 190 : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {}
187 191
188 /// Registers handlers in the service. 192 /// Registers handlers in the service.
189 template <std::size_t N> 193 template <std::size_t N>
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 695a1faa6..97f895852 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -206,7 +206,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
206} 206}
207 207
208SM::SM(ServiceManager& service_manager_, Core::System& system_) 208SM::SM(ServiceManager& service_manager_, Core::System& system_)
209 : ServiceFramework{system_, "sm:", 4}, 209 : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4},
210 service_manager{service_manager_}, kernel{system_.Kernel()} { 210 service_manager{service_manager_}, kernel{system_.Kernel()} {
211 RegisterHandlers({ 211 RegisterHandlers({
212 {0, &SM::Initialize, "Initialize"}, 212 {0, &SM::Initialize, "Initialize"},
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index fc93fb743..d6702e4e1 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -837,7 +837,8 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
837 rb.PushEnum(bsd_errno); 837 rb.PushEnum(bsd_errno);
838} 838}
839 839
840BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { 840BSD::BSD(Core::System& system_, const char* name)
841 : ServiceFramework{system_, name, ServiceThreadType::CreateNew} {
841 // clang-format off 842 // clang-format off
842 static const FunctionInfo functions[] = { 843 static const FunctionInfo functions[] = {
843 {0, &BSD::RegisterClient, "RegisterClient"}, 844 {0, &BSD::RegisterClient, "RegisterClient"},
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 430cbc546..a3436c8ea 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -77,7 +77,8 @@ static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
77class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 77class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
78public: 78public:
79 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_) 79 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_)
80 : ServiceFramework{system_, "IHOSBinderDriver"}, server(server_) { 80 : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew},
81 server(server_) {
81 static const FunctionInfo functions[] = { 82 static const FunctionInfo functions[] = {
82 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 83 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
83 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, 84 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 0c1fbc7b1..282668b36 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -35,6 +35,15 @@ std::string_view OutputVertexIndex(EmitContext& ctx) {
35 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : ""; 35 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
36} 36}
37 37
38std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
39 if (binding.IsImmediate()) {
40 return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
41 } else {
42 const auto binding_var{ctx.var_alloc.Consume(binding)};
43 return fmt::format("GetCbufIndirect({},{})", binding_var, index);
44 }
45}
46
38void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding, 47void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
39 const IR::Value& offset, u32 num_bits, std::string_view cast = {}, 48 const IR::Value& offset, u32 num_bits, std::string_view cast = {},
40 std::string_view bit_offset = {}) { 49 std::string_view bit_offset = {}) {
@@ -55,8 +64,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
55 const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32())) 64 const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
56 : fmt::format("[({}>>2)%4]", offset_var)}; 65 : fmt::format("[({}>>2)%4]", offset_var)};
57 66
58 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())}; 67 const auto cbuf{ChooseCbuf(ctx, binding, index)};
59 const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)}; 68 const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
60 const auto extraction{num_bits == 32 ? cbuf_cast 69 const auto extraction{num_bits == 32 ? cbuf_cast
61 : fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast, 70 : fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
62 bit_offset, num_bits)}; 71 bit_offset, num_bits)};
@@ -140,9 +149,9 @@ void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
140 149
141void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 150void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
142 const IR::Value& offset) { 151 const IR::Value& offset) {
143 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
144 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"}; 152 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
145 if (offset.IsImmediate()) { 153 if (offset.IsImmediate()) {
154 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
146 static constexpr u32 cbuf_size{0x10000}; 155 static constexpr u32 cbuf_size{0x10000};
147 const u32 u32_offset{offset.U32()}; 156 const u32 u32_offset{offset.U32()};
148 const s32 signed_offset{static_cast<s32>(offset.U32())}; 157 const s32 signed_offset{static_cast<s32>(offset.U32())};
@@ -162,17 +171,17 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
162 return; 171 return;
163 } 172 }
164 const auto offset_var{ctx.var_alloc.Consume(offset)}; 173 const auto offset_var{ctx.var_alloc.Consume(offset)};
174 const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
165 if (!ctx.profile.has_gl_component_indexing_bug) { 175 if (!ctx.profile.has_gl_component_indexing_bug) {
166 ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst, 176 ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
167 cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var); 177 offset_var, cast, cbuf, offset_var);
168 return; 178 return;
169 } 179 }
170 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)}; 180 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
171 const auto cbuf_offset{fmt::format("{}>>2", offset_var)}; 181 const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
172 for (u32 swizzle = 0; swizzle < 4; ++swizzle) { 182 for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
173 ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset, 183 ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
174 swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var, 184 cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
175 "xyzw"[(swizzle + 1) % 4]);
176 } 185 }
177} 186}
178 187
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index e816a93ec..17266f40d 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -359,6 +359,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
359 header += "layout(location=0) uniform vec4 scaling;"; 359 header += "layout(location=0) uniform vec4 scaling;";
360 } 360 }
361 DefineConstantBuffers(bindings); 361 DefineConstantBuffers(bindings);
362 DefineConstantBufferIndirect();
362 DefineStorageBuffers(bindings); 363 DefineStorageBuffers(bindings);
363 SetupImages(bindings); 364 SetupImages(bindings);
364 SetupTextures(bindings); 365 SetupTextures(bindings);
@@ -436,6 +437,24 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
436 } 437 }
437} 438}
438 439
440void EmitContext::DefineConstantBufferIndirect() {
441 if (!info.uses_cbuf_indirect) {
442 return;
443 }
444
445 header += profile.has_gl_cbuf_ftou_bug ? "uvec4 " : "vec4 ";
446 header += "GetCbufIndirect(uint binding, uint offset){"
447 "switch(binding){"
448 "default:";
449
450 for (const auto& desc : info.constant_buffer_descriptors) {
451 header +=
452 fmt::format("case {}:return {}_cbuf{}[offset];", desc.index, stage_name, desc.index);
453 }
454
455 header += "}}";
456}
457
439void EmitContext::DefineStorageBuffers(Bindings& bindings) { 458void EmitContext::DefineStorageBuffers(Bindings& bindings) {
440 if (info.storage_buffers_descriptors.empty()) { 459 if (info.storage_buffers_descriptors.empty()) {
441 return; 460 return;
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.h b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
index d9b639d29..2b13db6e6 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.h
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
@@ -162,6 +162,7 @@ public:
162private: 162private:
163 void SetupExtensions(); 163 void SetupExtensions();
164 void DefineConstantBuffers(Bindings& bindings); 164 void DefineConstantBuffers(Bindings& bindings);
165 void DefineConstantBufferIndirect();
165 void DefineStorageBuffers(Bindings& bindings); 166 void DefineStorageBuffers(Bindings& bindings);
166 void DefineGenericOutput(size_t index, u32 invocations); 167 void DefineGenericOutput(size_t index, u32 invocations);
167 void DefineHelperFunctions(); 168 void DefineHelperFunctions();
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 28f6a6184..9c83cd2e4 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1043,15 +1043,15 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
1043 const Id merge_label{OpLabel()}; 1043 const Id merge_label{OpLabel()};
1044 const Id uniform_type{uniform_types.*member_ptr}; 1044 const Id uniform_type{uniform_types.*member_ptr};
1045 1045
1046 std::array<Id, Info::MAX_CBUFS> buf_labels; 1046 std::array<Id, Info::MAX_INDIRECT_CBUFS> buf_labels;
1047 std::array<Sirit::Literal, Info::MAX_CBUFS> buf_literals; 1047 std::array<Sirit::Literal, Info::MAX_INDIRECT_CBUFS> buf_literals;
1048 for (u32 i = 0; i < Info::MAX_CBUFS; i++) { 1048 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
1049 buf_labels[i] = OpLabel(); 1049 buf_labels[i] = OpLabel();
1050 buf_literals[i] = Sirit::Literal{i}; 1050 buf_literals[i] = Sirit::Literal{i};
1051 } 1051 }
1052 OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone); 1052 OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
1053 OpSwitch(binding, buf_labels[0], buf_literals, buf_labels); 1053 OpSwitch(binding, buf_labels[0], buf_literals, buf_labels);
1054 for (u32 i = 0; i < Info::MAX_CBUFS; i++) { 1054 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
1055 AddLabel(buf_labels[i]); 1055 AddLabel(buf_labels[i]);
1056 const Id cbuf{cbufs[i].*member_ptr}; 1056 const Id cbuf{cbufs[i].*member_ptr};
1057 const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)}; 1057 const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)};
@@ -1064,22 +1064,23 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
1064 return func; 1064 return func;
1065 }}; 1065 }};
1066 IR::Type types{info.used_indirect_cbuf_types}; 1066 IR::Type types{info.used_indirect_cbuf_types};
1067 if (True(types & IR::Type::U8)) { 1067 bool supports_aliasing = profile.support_descriptor_aliasing;
1068 if (supports_aliasing && True(types & IR::Type::U8)) {
1068 load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8); 1069 load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
1069 } 1070 }
1070 if (True(types & IR::Type::U16)) { 1071 if (supports_aliasing && True(types & IR::Type::U16)) {
1071 load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16); 1072 load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
1072 } 1073 }
1073 if (True(types & IR::Type::F32)) { 1074 if (supports_aliasing && True(types & IR::Type::F32)) {
1074 load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32); 1075 load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
1075 } 1076 }
1076 if (True(types & IR::Type::U32)) { 1077 if (supports_aliasing && True(types & IR::Type::U32)) {
1077 load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32); 1078 load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32);
1078 } 1079 }
1079 if (True(types & IR::Type::U32x2)) { 1080 if (supports_aliasing && True(types & IR::Type::U32x2)) {
1080 load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2); 1081 load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2);
1081 } 1082 }
1082 if (True(types & IR::Type::U32x4)) { 1083 if (!supports_aliasing || True(types & IR::Type::U32x4)) {
1083 load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4); 1084 load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4);
1084 } 1085 }
1085} 1086}
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
index 57b4f0eee..60732215b 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
@@ -132,7 +132,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
132 multisample = v.X(meta_reg++); 132 multisample = v.X(meta_reg++);
133 } 133 }
134 if (tld.clamp != 0) { 134 if (tld.clamp != 0) {
135 throw NotImplementedException("TLD.CL - CLAMP is not implmented"); 135 throw NotImplementedException("TLD.CL - CLAMP is not implemented");
136 } 136 }
137 IR::TextureInstInfo info{}; 137 IR::TextureInstInfo info{};
138 info.type.Assign(GetType(tld.type)); 138 info.type.Assign(GetType(tld.type));
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
index 311a9e763..f89ce1b68 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
@@ -81,7 +81,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
81 } const tmml{insn}; 81 } const tmml{insn};
82 82
83 if ((tmml.mask & 0b1100) != 0) { 83 if ((tmml.mask & 0b1100) != 0) {
84 throw NotImplementedException("TMML BA results are not implmented"); 84 throw NotImplementedException("TMML BA results are not implemented");
85 } 85 }
86 const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)}; 86 const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)};
87 87
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 0b2c60842..16278faab 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -32,13 +32,8 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
32void AddRegisterIndexedLdc(Info& info) { 32void AddRegisterIndexedLdc(Info& info) {
33 info.uses_cbuf_indirect = true; 33 info.uses_cbuf_indirect = true;
34 34
35 // The shader can use any possible constant buffer 35 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
36 info.constant_buffer_mask = (1 << Info::MAX_CBUFS) - 1; 36 AddConstantBufferDescriptor(info, i, 1);
37
38 auto& cbufs{info.constant_buffer_descriptors};
39 cbufs.clear();
40 for (u32 i = 0; i < Info::MAX_CBUFS; i++) {
41 cbufs.push_back(ConstantBufferDescriptor{.index = i, .count = 1});
42 37
43 // The shader can use any possible access size 38 // The shader can use any possible access size
44 info.constant_buffer_used_sizes[i] = 0x10'000; 39 info.constant_buffer_used_sizes[i] = 0x10'000;
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index 9d36bd9eb..a3a09c71c 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -105,6 +105,7 @@ struct ImageDescriptor {
105using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; 105using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;
106 106
107struct Info { 107struct Info {
108 static constexpr size_t MAX_INDIRECT_CBUFS{14};
108 static constexpr size_t MAX_CBUFS{18}; 109 static constexpr size_t MAX_CBUFS{18};
109 static constexpr size_t MAX_SSBOS{32}; 110 static constexpr size_t MAX_SSBOS{32};
110 111
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 4a5de9ddf..f3a05ada9 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -736,7 +736,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
736} 736}
737 737
738void Device::ReportLoss() const { 738void Device::ReportLoss() const {
739 LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); 739 LOG_CRITICAL(Render_Vulkan, "Device loss occurred!");
740 740
741 // Wait for the log to flush and for Nsight Aftermath to dump the results 741 // Wait for the log to flush and for Nsight Aftermath to dump the results
742 std::this_thread::sleep_for(std::chrono::seconds{15}); 742 std::this_thread::sleep_for(std::chrono::seconds{15});
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index 5d80a8c91..8ae569ee6 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -52,6 +52,11 @@
52 <string>Unsafe</string> 52 <string>Unsafe</string>
53 </property> 53 </property>
54 </item> 54 </item>
55 <item>
56 <property name="text">
57 <string>Paranoid (disables most optimizations)</string>
58 </property>
59 </item>
55 </widget> 60 </widget>
56 </item> 61 </item>
57 </layout> 62 </layout>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 53e629a5e..6679e9c53 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -35,8 +35,9 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
35 ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); 35 ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
36 ui->hotkey_list->setModel(model); 36 ui->hotkey_list->setModel(model);
37 37
38 ui->hotkey_list->setColumnWidth(name_column, 200); 38 ui->hotkey_list->header()->setStretchLastSection(false);
39 ui->hotkey_list->resizeColumnToContents(hotkey_column); 39 ui->hotkey_list->header()->setSectionResizeMode(name_column, QHeaderView::ResizeMode::Stretch);
40 ui->hotkey_list->header()->setMinimumSectionSize(150);
40 41
41 connect(ui->button_restore_defaults, &QPushButton::clicked, this, 42 connect(ui->button_restore_defaults, &QPushButton::clicked, this,
42 &ConfigureHotkeys::RestoreDefaults); 43 &ConfigureHotkeys::RestoreDefaults);
@@ -76,8 +77,8 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
76 } 77 }
77 78
78 ui->hotkey_list->expandAll(); 79 ui->hotkey_list->expandAll();
79 ui->hotkey_list->resizeColumnToContents(name_column);
80 ui->hotkey_list->resizeColumnToContents(hotkey_column); 80 ui->hotkey_list->resizeColumnToContents(hotkey_column);
81 ui->hotkey_list->resizeColumnToContents(controller_column);
81} 82}
82 83
83void ConfigureHotkeys::changeEvent(QEvent* event) { 84void ConfigureHotkeys::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 21e51d749..7893a85bb 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -47,6 +47,10 @@ ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* p
47 item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name")); 47 item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
48 item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); 48 item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
49 49
50 tree_view->header()->setStretchLastSection(false);
51 tree_view->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
52 tree_view->header()->setMinimumSectionSize(150);
53
50 // We must register all custom types with the Qt Automoc system so that we are able to use it 54 // We must register all custom types with the Qt Automoc system so that we are able to use it
51 // with signals/slots. In this case, QList falls under the umbrella of custom types. 55 // with signals/slots. In this case, QList falls under the umbrella of custom types.
52 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); 56 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
@@ -138,5 +142,5 @@ void ConfigurePerGameAddons::LoadConfiguration() {
138 item_model->appendRow(list_items.back()); 142 item_model->appendRow(list_items.back());
139 } 143 }
140 144
141 tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); 145 tree_view->resizeColumnToContents(1);
142} 146}
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 34782c378..f34d6b728 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -342,12 +342,6 @@ fps_cap =
342# null: No audio output 342# null: No audio output
343output_engine = 343output_engine =
344 344
345# Whether or not to enable the audio-stretching post-processing effect.
346# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
347# at the cost of increasing audio latency.
348# 0: No, 1 (default): Yes
349enable_audio_stretching =
350
351# Which audio device to use. 345# Which audio device to use.
352# auto (default): Auto-select 346# auto (default): Auto-select
353output_device = 347output_device =