summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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/settings.h3
-rw-r--r--src/common/x64/native_clock.cpp10
-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/service/am/applets/applet_web_browser.cpp8
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp2
-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/nvdrv/nvdrv_interface.cpp12
-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/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/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
33 files changed, 365 insertions, 361 deletions
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/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..7a3f21dcf 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -55,8 +55,9 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
55u64 NativeClock::GetRTSC() { 55u64 NativeClock::GetRTSC() {
56 TimePoint new_time_point{}; 56 TimePoint new_time_point{};
57 TimePoint current_time_point{}; 57 TimePoint current_time_point{};
58
59 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
58 do { 60 do {
59 current_time_point.pack = time_point.pack;
60 _mm_mfence(); 61 _mm_mfence();
61 const u64 current_measure = __rdtsc(); 62 const u64 current_measure = __rdtsc();
62 u64 diff = current_measure - current_time_point.inner.last_measure; 63 u64 diff = current_measure - current_time_point.inner.last_measure;
@@ -66,7 +67,7 @@ u64 NativeClock::GetRTSC() {
66 : current_time_point.inner.last_measure; 67 : current_time_point.inner.last_measure;
67 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; 68 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, 69 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
69 current_time_point.pack)); 70 current_time_point.pack, current_time_point.pack));
70 /// The clock cannot be more precise than the guest timer, remove the lower bits 71 /// The clock cannot be more precise than the guest timer, remove the lower bits
71 return new_time_point.inner.accumulated_ticks & inaccuracy_mask; 72 return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
72} 73}
@@ -75,13 +76,14 @@ void NativeClock::Pause(bool is_paused) {
75 if (!is_paused) { 76 if (!is_paused) {
76 TimePoint current_time_point{}; 77 TimePoint current_time_point{};
77 TimePoint new_time_point{}; 78 TimePoint new_time_point{};
79
80 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
78 do { 81 do {
79 current_time_point.pack = time_point.pack;
80 new_time_point.pack = current_time_point.pack; 82 new_time_point.pack = current_time_point.pack;
81 _mm_mfence(); 83 _mm_mfence();
82 new_time_point.inner.last_measure = __rdtsc(); 84 new_time_point.inner.last_measure = __rdtsc();
83 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 85 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
84 current_time_point.pack)); 86 current_time_point.pack, current_time_point.pack));
85 } 87 }
86} 88}
87 89
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 286976623..054572445 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -70,11 +70,13 @@ public:
70 } 70 }
71 71
72 void InterpreterFallback(u32 pc, std::size_t num_instructions) override { 72 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
73 parent.LogBacktrace();
73 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, 74 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
74 MemoryReadCode(pc)); 75 MemoryReadCode(pc));
75 } 76 }
76 77
77 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 78 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
79 parent.LogBacktrace();
78 LOG_CRITICAL(Core_ARM, 80 LOG_CRITICAL(Core_ARM,
79 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 81 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
80 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); 82 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
@@ -186,35 +188,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
186 if (!Settings::values.cpuopt_recompile_exclusives) { 188 if (!Settings::values.cpuopt_recompile_exclusives) {
187 config.recompile_on_exclusive_fastmem_failure = false; 189 config.recompile_on_exclusive_fastmem_failure = false;
188 } 190 }
189 } 191 } else {
192 // Unsafe optimizations
193 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
194 config.unsafe_optimizations = true;
195 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
196 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
197 }
198 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
199 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
200 }
201 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
202 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
203 }
204 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
205 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
206 }
207 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
208 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
209 }
210 }
190 211
191 // Unsafe optimizations 212 // Curated optimizations
192 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 213 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
193 config.unsafe_optimizations = true; 214 config.unsafe_optimizations = true;
194 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
195 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
196 }
197 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
198 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
199 }
200 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
201 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
202 }
203 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 217 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
205 }
206 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 218 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
208 } 219 }
209 }
210 220
211 // Curated optimizations 221 // Paranoia mode for debugging optimizations
212 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 222 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
213 config.unsafe_optimizations = true; 223 config.unsafe_optimizations = false;
214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 224 config.optimizations = Dynarmic::no_optimizations;
215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 225 }
216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
218 } 226 }
219 227
220 return std::make_unique<Dynarmic::A32::Jit>(config); 228 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 24107f9f6..7ff8f9495 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -81,6 +81,7 @@ public:
81 } 81 }
82 82
83 void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 83 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
84 parent.LogBacktrace();
84 LOG_ERROR(Core_ARM, 85 LOG_ERROR(Core_ARM,
85 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 86 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
86 num_instructions, MemoryReadCode(pc)); 87 num_instructions, MemoryReadCode(pc));
@@ -118,6 +119,7 @@ public:
118 return; 119 return;
119 case Dynarmic::A64::Exception::Breakpoint: 120 case Dynarmic::A64::Exception::Breakpoint:
120 default: 121 default:
122 parent.LogBacktrace();
121 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 123 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
122 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 124 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
123 } 125 }
@@ -248,35 +250,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
248 if (!Settings::values.cpuopt_recompile_exclusives) { 250 if (!Settings::values.cpuopt_recompile_exclusives) {
249 config.recompile_on_exclusive_fastmem_failure = false; 251 config.recompile_on_exclusive_fastmem_failure = false;
250 } 252 }
251 } 253 } else {
254 // Unsafe optimizations
255 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
256 config.unsafe_optimizations = true;
257 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
258 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
259 }
260 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
261 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
262 }
263 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
264 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
265 }
266 if (Settings::values.cpuopt_unsafe_fastmem_check) {
267 config.fastmem_address_space_bits = 64;
268 }
269 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
270 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
271 }
272 }
252 273
253 // Unsafe optimizations 274 // Curated optimizations
254 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 275 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
255 config.unsafe_optimizations = true; 276 config.unsafe_optimizations = true;
256 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
257 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
258 }
259 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
260 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
261 }
262 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
263 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 278 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
264 }
265 if (Settings::values.cpuopt_unsafe_fastmem_check) {
266 config.fastmem_address_space_bits = 64; 279 config.fastmem_address_space_bits = 64;
267 }
268 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
269 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 280 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
270 } 281 }
271 }
272 282
273 // Curated optimizations 283 // Paranoia mode for debugging optimizations
274 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 284 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
275 config.unsafe_optimizations = true; 285 config.unsafe_optimizations = false;
276 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 286 config.optimizations = Dynarmic::no_optimizations;
277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 287 }
278 config.fastmem_address_space_bits = 64;
279 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
280 } 288 }
281 289
282 return std::make_shared<Dynarmic::A64::Jit>(config); 290 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/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/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/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/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index b3b8576a9..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
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/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/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 =