summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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/x64/native_clock.cpp10
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp6
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp3
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp242
-rw-r--r--src/core/hle/kernel/k_page_table.h33
-rw-r--r--src/core/hle/kernel/svc.cpp13
-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/hid/hid.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp12
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.cpp4
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp206
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h154
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp182
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp26
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.h22
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp79
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h2
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.cpp8
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp1
-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/command_classes/codecs/codec.cpp35
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp12
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp3
-rw-r--r--src/video_core/texture_cache/image_base.h3
-rw-r--r--src/video_core/texture_cache/texture_cache.h43
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h11
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp7
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/configure_debug.ui4
-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/main.cpp13
-rw-r--r--src/yuzu_cmd/CMakeLists.txt2
-rw-r--r--src/yuzu_cmd/default_ini.h6
-rw-r--r--src/yuzu_cmd/yuzu.cpp1
53 files changed, 637 insertions, 924 deletions
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/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_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index a2d893450..6d5a1ecfd 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -93,17 +93,19 @@ public:
93 static constexpr u64 ICACHE_LINE_SIZE = 64; 93 static constexpr u64 ICACHE_LINE_SIZE = 64;
94 94
95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); 95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
96 parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); 96 parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
97 break; 97 break;
98 } 98 }
99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: 99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
100 parent.ClearInstructionCache(); 100 parent.system.InvalidateCpuInstructionCaches();
101 break; 101 break;
102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: 102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
103 default: 103 default:
104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); 104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
105 break; 105 break;
106 } 106 }
107
108 parent.jit->HaltExecution();
107 } 109 }
108 110
109 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 111 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index b365ce7b7..63bbe02e9 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -28,7 +28,8 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
28 auto& page_table = m_owner->PageTable(); 28 auto& page_table = m_owner->PageTable();
29 29
30 // Construct the page group. 30 // Construct the page group.
31 m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); 31 m_page_group =
32 KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
32 33
33 // Lock the memory. 34 // Lock the memory.
34 R_TRY(page_table.LockForCodeMemory(addr, size)) 35 R_TRY(page_table.LockForCodeMemory(addr, size))
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 0e2ae582a..869228322 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -89,6 +89,10 @@ public:
89 return ResultSuccess; 89 return ResultSuccess;
90 } 90 }
91 91
92 bool Empty() const {
93 return nodes.empty();
94 }
95
92private: 96private:
93 std::list<Node> nodes; 97 std::list<Node> nodes;
94}; 98};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 02d93b12e..599013cf6 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -486,6 +486,58 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
486 return address; 486 return address;
487} 487}
488 488
489ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
490 ASSERT(this->IsLockedByCurrentThread());
491
492 const size_t size = num_pages * PageSize;
493
494 // We're making a new group, not adding to an existing one.
495 R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
496
497 // Begin traversal.
498 Common::PageTable::TraversalContext context;
499 Common::PageTable::TraversalEntry next_entry;
500 R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
501
502 // Prepare tracking variables.
503 PAddr cur_addr = next_entry.phys_addr;
504 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
505 size_t tot_size = cur_size;
506
507 // Iterate, adding to group as we go.
508 const auto& memory_layout = system.Kernel().MemoryLayout();
509 while (tot_size < size) {
510 R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
511 ResultInvalidCurrentMemory);
512
513 if (next_entry.phys_addr != (cur_addr + cur_size)) {
514 const size_t cur_pages = cur_size / PageSize;
515
516 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
517 R_TRY(pg.AddBlock(cur_addr, cur_pages));
518
519 cur_addr = next_entry.phys_addr;
520 cur_size = next_entry.block_size;
521 } else {
522 cur_size += next_entry.block_size;
523 }
524
525 tot_size += next_entry.block_size;
526 }
527
528 // Ensure we add the right amount for the last block.
529 if (tot_size > size) {
530 cur_size -= (tot_size - size);
531 }
532
533 // Add the last block.
534 const size_t cur_pages = cur_size / PageSize;
535 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
536 R_TRY(pg.AddBlock(cur_addr, cur_pages));
537
538 return ResultSuccess;
539}
540
489ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 541ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
490 KPageTable& src_page_table, VAddr src_addr) { 542 KPageTable& src_page_table, VAddr src_addr) {
491 KScopedLightLock lk(general_lock); 543 KScopedLightLock lk(general_lock);
@@ -1223,6 +1275,31 @@ ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryS
1223 return ResultSuccess; 1275 return ResultSuccess;
1224} 1276}
1225 1277
1278ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
1279 KMemoryState state_mask, KMemoryState state,
1280 KMemoryPermission perm_mask, KMemoryPermission perm,
1281 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
1282 // Ensure that the page group isn't null.
1283 ASSERT(out != nullptr);
1284
1285 // Make sure that the region we're mapping is valid for the table.
1286 const size_t size = num_pages * PageSize;
1287 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1288
1289 // Lock the table.
1290 KScopedLightLock lk(general_lock);
1291
1292 // Check if state allows us to create the group.
1293 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
1294 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
1295 attr_mask, attr));
1296
1297 // Create a new page group for the region.
1298 R_TRY(this->MakePageGroup(*out, address, num_pages));
1299
1300 return ResultSuccess;
1301}
1302
1226ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1303ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1227 Svc::MemoryPermission svc_perm) { 1304 Svc::MemoryPermission svc_perm) {
1228 const size_t num_pages = size / PageSize; 1305 const size_t num_pages = size / PageSize;
@@ -1605,57 +1682,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1605} 1682}
1606 1683
1607ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1684ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1608 KScopedLightLock lk(general_lock); 1685 return this->LockMemoryAndOpen(
1609 1686 nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1610 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; 1687 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1611 1688 KMemoryAttribute::All, KMemoryAttribute::None,
1612 KMemoryPermission old_perm{}; 1689 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1613 1690 KMemoryPermission::KernelReadWrite),
1614 if (const ResultCode result{CheckMemoryState( 1691 KMemoryAttribute::Locked);
1615 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1616 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
1617 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
1618 result.IsError()) {
1619 return result;
1620 }
1621
1622 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1623
1624 block_manager->UpdateLock(
1625 addr, size / PageSize,
1626 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1627 block->ShareToDevice(permission);
1628 },
1629 new_perm);
1630
1631 return ResultSuccess;
1632} 1692}
1633 1693
1634ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1694ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1635 KScopedLightLock lk(general_lock); 1695 return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
1636 1696 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
1637 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; 1697 KMemoryPermission::None, KMemoryAttribute::All,
1638 1698 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
1639 KMemoryPermission old_perm{}; 1699 KMemoryAttribute::Locked, nullptr);
1640
1641 if (const ResultCode result{CheckMemoryState(
1642 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1643 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
1644 KMemoryAttribute::All, KMemoryAttribute::Locked)};
1645 result.IsError()) {
1646 return result;
1647 }
1648
1649 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1650
1651 block_manager->UpdateLock(
1652 addr, size / PageSize,
1653 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1654 block->UnshareToDevice(permission);
1655 },
1656 new_perm);
1657
1658 return ResultSuccess;
1659} 1700}
1660 1701
1661ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1702ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1991,4 +2032,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
1991 return ResultSuccess; 2032 return ResultSuccess;
1992} 2033}
1993 2034
2035ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
2036 size_t size, KMemoryState state_mask, KMemoryState state,
2037 KMemoryPermission perm_mask, KMemoryPermission perm,
2038 KMemoryAttribute attr_mask, KMemoryAttribute attr,
2039 KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
2040 // Validate basic preconditions.
2041 ASSERT((lock_attr & attr) == KMemoryAttribute::None);
2042 ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
2043 KMemoryAttribute::None);
2044
2045 // Validate the lock request.
2046 const size_t num_pages = size / PageSize;
2047 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2048
2049 // Lock the table.
2050 KScopedLightLock lk(general_lock);
2051
2052 // Check that the output page group is empty, if it exists.
2053 if (out_pg) {
2054 ASSERT(out_pg->GetNumPages() == 0);
2055 }
2056
2057 // Check the state.
2058 KMemoryState old_state{};
2059 KMemoryPermission old_perm{};
2060 KMemoryAttribute old_attr{};
2061 size_t num_allocator_blocks{};
2062 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2063 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2064 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2065 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2066 attr_mask, attr));
2067
2068 // Get the physical address, if we're supposed to.
2069 if (out_paddr != nullptr) {
2070 ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
2071 }
2072
2073 // Make the page group, if we're supposed to.
2074 if (out_pg != nullptr) {
2075 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
2076 }
2077
2078 // Decide on new perm and attr.
2079 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2080 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
2081
2082 // Update permission, if we need to.
2083 if (new_perm != old_perm) {
2084 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2085 }
2086
2087 // Apply the memory block updates.
2088 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2089
2090 return ResultSuccess;
2091}
2092
2093ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
2094 KMemoryState state, KMemoryPermission perm_mask,
2095 KMemoryPermission perm, KMemoryAttribute attr_mask,
2096 KMemoryAttribute attr, KMemoryPermission new_perm,
2097 KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
2098 // Validate basic preconditions.
2099 ASSERT((attr_mask & lock_attr) == lock_attr);
2100 ASSERT((attr & lock_attr) == lock_attr);
2101
2102 // Validate the unlock request.
2103 const size_t num_pages = size / PageSize;
2104 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2105
2106 // Lock the table.
2107 KScopedLightLock lk(general_lock);
2108
2109 // Check the state.
2110 KMemoryState old_state{};
2111 KMemoryPermission old_perm{};
2112 KMemoryAttribute old_attr{};
2113 size_t num_allocator_blocks{};
2114 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2115 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2116 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2117 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2118 attr_mask, attr));
2119
2120 // Check the page group.
2121 if (pg != nullptr) {
2122 UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
2123 }
2124
2125 // Decide on new perm and attr.
2126 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2127 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
2128
2129 // Update permission, if we need to.
2130 if (new_perm != old_perm) {
2131 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2132 }
2133
2134 // Apply the memory block updates.
2135 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2136
2137 return ResultSuccess;
2138}
2139
1994} // namespace Kernel 2140} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 54c6adf8d..bfabdf38c 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -12,6 +12,7 @@
12#include "core/file_sys/program_metadata.h" 12#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/k_light_lock.h" 13#include "core/hle/kernel/k_light_lock.h"
14#include "core/hle/kernel/k_memory_block.h" 14#include "core/hle/kernel/k_memory_block.h"
15#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 16#include "core/hle/kernel/k_memory_manager.h"
16#include "core/hle/result.h" 17#include "core/hle/result.h"
17 18
@@ -71,6 +72,10 @@ public:
71 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 72 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
72 ResultCode LockForCodeMemory(VAddr addr, std::size_t size); 73 ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
73 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); 74 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
75 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
76 KMemoryState state_mask, KMemoryState state,
77 KMemoryPermission perm_mask, KMemoryPermission perm,
78 KMemoryAttribute attr_mask, KMemoryAttribute attr);
74 79
75 Common::PageTable& PageTableImpl() { 80 Common::PageTable& PageTableImpl() {
76 return page_table_impl; 81 return page_table_impl;
@@ -159,10 +164,37 @@ private:
159 attr_mask, attr, ignore_attr); 164 attr_mask, attr, ignore_attr);
160 } 165 }
161 166
167 ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
168 KMemoryState state_mask, KMemoryState state,
169 KMemoryPermission perm_mask, KMemoryPermission perm,
170 KMemoryAttribute attr_mask, KMemoryAttribute attr,
171 KMemoryPermission new_perm, KMemoryAttribute lock_attr);
172 ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
173 KMemoryPermission perm_mask, KMemoryPermission perm,
174 KMemoryAttribute attr_mask, KMemoryAttribute attr,
175 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
176 const KPageLinkedList* pg);
177
178 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
179
162 bool IsLockedByCurrentThread() const { 180 bool IsLockedByCurrentThread() const {
163 return general_lock.IsLockedByCurrentThread(); 181 return general_lock.IsLockedByCurrentThread();
164 } 182 }
165 183
184 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
185 ASSERT(this->IsLockedByCurrentThread());
186
187 return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
188 }
189
190 bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
191 ASSERT(this->IsLockedByCurrentThread());
192
193 *out = GetPhysicalAddr(virt_addr);
194
195 return *out != 0;
196 }
197
166 mutable KLightLock general_lock; 198 mutable KLightLock general_lock;
167 mutable KLightLock map_physical_memory_lock; 199 mutable KLightLock map_physical_memory_lock;
168 200
@@ -322,6 +354,7 @@ private:
322 bool is_aslr_enabled{}; 354 bool is_aslr_enabled{};
323 355
324 u32 heap_fill_value{}; 356 u32 heap_fill_value{};
357 const KMemoryRegion* cached_physical_heap_region{};
325 358
326 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; 359 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
327 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; 360 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 839171e85..976d63234 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1362,8 +1362,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
1362 ResultInvalidMemoryRegion); 1362 ResultInvalidMemoryRegion);
1363 1363
1364 // Create a new page group. 1364 // Create a new page group.
1365 KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); 1365 KPageLinkedList pg;
1366 KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); 1366 R_TRY(src_pt.MakeAndOpenPageGroup(
1367 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1368 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1369 KMemoryAttribute::All, KMemoryAttribute::None));
1367 1370
1368 // Map the group. 1371 // Map the group.
1369 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, 1372 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
@@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
1408} 1411}
1409 1412
1410static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { 1413static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1411 LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}", 1414 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
1412 static_cast<void*>(out), address, size); 1415
1413 // Get kernel instance. 1416 // Get kernel instance.
1414 auto& kernel = system.Kernel(); 1417 auto& kernel = system.Kernel();
1415 1418
@@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1664 return ResultInvalidAddress; 1667 return ResultInvalidAddress;
1665 } 1668 }
1666 1669
1667 if (size == 0 || Common::Is4KBAligned(size)) { 1670 if (size == 0 || !Common::Is4KBAligned(size)) {
1668 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); 1671 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1669 return ResultInvalidSize; 1672 return ResultInvalidSize;
1670 } 1673 }
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/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d9202ea6c..92e6bf889 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -63,10 +63,6 @@ IAppletResource::IAppletResource(Core::System& system_,
63 MakeController<Controller_Gesture>(HidController::Gesture); 63 MakeController<Controller_Gesture>(HidController::Gesture);
64 MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); 64 MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor);
65 65
66 // Homebrew doesn't try to activate some controllers, so we activate them by default
67 GetController<Controller_NPad>(HidController::NPad).ActivateController();
68 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
69
70 GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); 66 GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
71 GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); 67 GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
72 GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); 68 GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000);
@@ -878,6 +874,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
878 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", 874 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
879 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); 875 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
880 876
877 // Games expect this event to be signaled after calling this function
878 applet_resource->GetController<Controller_NPad>(HidController::NPad)
879 .SignalStyleSetChangedEvent(parameters.npad_id);
880
881 IPC::ResponseBuilder rb{ctx, 2, 1}; 881 IPC::ResponseBuilder rb{ctx, 2, 1};
882 rb.Push(ResultSuccess); 882 rb.Push(ResultSuccess);
883 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 883 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index f9b82b504..44c54c665 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -134,7 +134,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
134 } 134 }
135 135
136 EventState status = events_interface.status[event_id]; 136 EventState status = events_interface.status[event_id];
137 const bool bad_parameter = status != EventState::Free && status != EventState::Registered; 137 const bool bad_parameter = status == EventState::Busy;
138 if (bad_parameter) { 138 if (bad_parameter) {
139 std::memcpy(output.data(), &params, sizeof(params)); 139 std::memcpy(output.data(), &params, sizeof(params));
140 return NvResult::BadParameter; 140 return NvResult::BadParameter;
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index c16babe14..1ce2a856b 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_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
index 7f32c0775..93fa1ec10 100644
--- a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
@@ -21,7 +21,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
21 return Status::BadValue; 21 return Status::BadValue;
22 } 22 }
23 23
24 std::unique_lock lock(mutex); 24 std::scoped_lock lock(mutex);
25 25
26 if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { 26 if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
27 if (status != Status::NoBufferAvailable) { 27 if (status != Status::NoBufferAvailable) {
@@ -40,7 +40,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
40} 40}
41 41
42Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { 42Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
43 std::unique_lock lock(mutex); 43 std::scoped_lock lock(mutex);
44 44
45 if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); 45 if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
46 status != Status::NoError) { 46 status != Status::NoError) {
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
deleted file mode 100644
index 5fead6d1b..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/hle/kernel/k_writable_event.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvflinger/buffer_queue.h"
14
15namespace Service::NVFlinger {
16
17BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
18 KernelHelpers::ServiceContext& service_context_)
19 : id(id_), layer_id(layer_id_), service_context{service_context_} {
20 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
21}
22
23BufferQueue::~BufferQueue() {
24 service_context.CloseEvent(buffer_wait_event);
25}
26
27void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
28 ASSERT(slot < buffer_slots);
29 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
30
31 {
32 std::unique_lock lock{free_buffers_mutex};
33 free_buffers.push_back(slot);
34 }
35 free_buffers_condition.notify_one();
36
37 buffers[slot] = {
38 .slot = slot,
39 .status = Buffer::Status::Free,
40 .igbp_buffer = igbp_buffer,
41 .transform = {},
42 .crop_rect = {},
43 .swap_interval = 0,
44 .multi_fence = {},
45 };
46
47 buffer_wait_event->GetWritableEvent().Signal();
48}
49
50std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
51 u32 height) {
52 // Wait for first request before trying to dequeue
53 {
54 std::unique_lock lock{free_buffers_mutex};
55 free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
56 }
57
58 if (!is_connect) {
59 // Buffer was disconnected while the thread was blocked, this is most likely due to
60 // emulation being stopped
61 return std::nullopt;
62 }
63
64 std::unique_lock lock{free_buffers_mutex};
65
66 auto f_itr = free_buffers.begin();
67 auto slot = buffers.size();
68
69 while (f_itr != free_buffers.end()) {
70 const Buffer& buffer = buffers[*f_itr];
71 if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
72 buffer.igbp_buffer.height == height) {
73 slot = *f_itr;
74 free_buffers.erase(f_itr);
75 break;
76 }
77 ++f_itr;
78 }
79 if (slot == buffers.size()) {
80 return std::nullopt;
81 }
82 buffers[slot].status = Buffer::Status::Dequeued;
83 return {{buffers[slot].slot, &buffers[slot].multi_fence}};
84}
85
86const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
87 ASSERT(slot < buffers.size());
88 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
89 ASSERT(buffers[slot].slot == slot);
90
91 return buffers[slot].igbp_buffer;
92}
93
94void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
95 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
96 Service::Nvidia::MultiFence& multi_fence) {
97 ASSERT(slot < buffers.size());
98 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
99 ASSERT(buffers[slot].slot == slot);
100
101 buffers[slot].status = Buffer::Status::Queued;
102 buffers[slot].transform = transform;
103 buffers[slot].crop_rect = crop_rect;
104 buffers[slot].swap_interval = swap_interval;
105 buffers[slot].multi_fence = multi_fence;
106 std::unique_lock lock{queue_sequence_mutex};
107 queue_sequence.push_back(slot);
108}
109
110void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
111 ASSERT(slot < buffers.size());
112 ASSERT(buffers[slot].status != Buffer::Status::Free);
113 ASSERT(buffers[slot].slot == slot);
114
115 buffers[slot].status = Buffer::Status::Free;
116 buffers[slot].multi_fence = multi_fence;
117 buffers[slot].swap_interval = 0;
118
119 {
120 std::unique_lock lock{free_buffers_mutex};
121 free_buffers.push_back(slot);
122 }
123 free_buffers_condition.notify_one();
124
125 buffer_wait_event->GetWritableEvent().Signal();
126}
127
128std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
129 std::unique_lock lock{queue_sequence_mutex};
130 std::size_t buffer_slot = buffers.size();
131 // Iterate to find a queued buffer matching the requested slot.
132 while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
133 const auto slot = static_cast<std::size_t>(queue_sequence.front());
134 ASSERT(slot < buffers.size());
135 if (buffers[slot].status == Buffer::Status::Queued) {
136 ASSERT(buffers[slot].slot == slot);
137 buffer_slot = slot;
138 }
139 queue_sequence.pop_front();
140 }
141 if (buffer_slot == buffers.size()) {
142 return std::nullopt;
143 }
144 buffers[buffer_slot].status = Buffer::Status::Acquired;
145 return {{buffers[buffer_slot]}};
146}
147
148void BufferQueue::ReleaseBuffer(u32 slot) {
149 ASSERT(slot < buffers.size());
150 ASSERT(buffers[slot].status == Buffer::Status::Acquired);
151 ASSERT(buffers[slot].slot == slot);
152
153 buffers[slot].status = Buffer::Status::Free;
154 {
155 std::unique_lock lock{free_buffers_mutex};
156 free_buffers.push_back(slot);
157 }
158 free_buffers_condition.notify_one();
159
160 buffer_wait_event->GetWritableEvent().Signal();
161}
162
163void BufferQueue::Connect() {
164 std::unique_lock lock{queue_sequence_mutex};
165 queue_sequence.clear();
166 is_connect = true;
167}
168
169void BufferQueue::Disconnect() {
170 buffers.fill({});
171 {
172 std::unique_lock lock{queue_sequence_mutex};
173 queue_sequence.clear();
174 }
175 buffer_wait_event->GetWritableEvent().Signal();
176 is_connect = false;
177 free_buffers_condition.notify_one();
178}
179
180u32 BufferQueue::Query(QueryType type) {
181 LOG_WARNING(Service, "(STUBBED) called type={}", type);
182
183 switch (type) {
184 case QueryType::NativeWindowFormat:
185 return static_cast<u32>(PixelFormat::RGBA8888);
186 case QueryType::NativeWindowWidth:
187 case QueryType::NativeWindowHeight:
188 break;
189 case QueryType::NativeWindowMinUndequeuedBuffers:
190 return 0;
191 case QueryType::NativeWindowConsumerUsageBits:
192 return 0;
193 }
194 UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
195 return 0;
196}
197
198Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
199 return buffer_wait_event->GetWritableEvent();
200}
201
202Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
203 return buffer_wait_event->GetReadableEvent();
204}
205
206} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
deleted file mode 100644
index f2a579133..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ /dev/null
@@ -1,154 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <condition_variable>
8#include <list>
9#include <mutex>
10#include <optional>
11
12#include "common/common_funcs.h"
13#include "common/math_util.h"
14#include "common/swap.h"
15#include "core/hle/kernel/k_event.h"
16#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/nvdata.h"
18
19namespace Kernel {
20class KernelCore;
21class KEvent;
22class KReadableEvent;
23class KWritableEvent;
24} // namespace Kernel
25
26namespace Service::KernelHelpers {
27class ServiceContext;
28} // namespace Service::KernelHelpers
29
30namespace Service::NVFlinger {
31
32constexpr u32 buffer_slots = 0x40;
33struct IGBPBuffer {
34 u32_le magic;
35 u32_le width;
36 u32_le height;
37 u32_le stride;
38 u32_le format;
39 u32_le usage;
40 INSERT_PADDING_WORDS(1);
41 u32_le index;
42 INSERT_PADDING_WORDS(3);
43 u32_le gpu_buffer_id;
44 INSERT_PADDING_WORDS(6);
45 u32_le external_format;
46 INSERT_PADDING_WORDS(10);
47 u32_le nvmap_handle;
48 u32_le offset;
49 INSERT_PADDING_WORDS(60);
50};
51
52static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
53
54class BufferQueue final {
55public:
56 enum class QueryType {
57 NativeWindowWidth = 0,
58 NativeWindowHeight = 1,
59 NativeWindowFormat = 2,
60 /// The minimum number of buffers that must remain un-dequeued after a buffer has been
61 /// queued
62 NativeWindowMinUndequeuedBuffers = 3,
63 /// The consumer gralloc usage bits currently set by the consumer
64 NativeWindowConsumerUsageBits = 10,
65 };
66
67 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
68 KernelHelpers::ServiceContext& service_context_);
69 ~BufferQueue();
70
71 enum class BufferTransformFlags : u32 {
72 /// No transform flags are set
73 Unset = 0x00,
74 /// Flip source image horizontally (around the vertical axis)
75 FlipH = 0x01,
76 /// Flip source image vertically (around the horizontal axis)
77 FlipV = 0x02,
78 /// Rotate source image 90 degrees clockwise
79 Rotate90 = 0x04,
80 /// Rotate source image 180 degrees
81 Rotate180 = 0x03,
82 /// Rotate source image 270 degrees clockwise
83 Rotate270 = 0x07,
84 };
85
86 enum class PixelFormat : u32 {
87 RGBA8888 = 1,
88 RGBX8888 = 2,
89 RGB888 = 3,
90 RGB565 = 4,
91 BGRA8888 = 5,
92 RGBA5551 = 6,
93 RRGBA4444 = 7,
94 };
95
96 struct Buffer {
97 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
98
99 u32 slot;
100 Status status = Status::Free;
101 IGBPBuffer igbp_buffer;
102 BufferTransformFlags transform;
103 Common::Rectangle<int> crop_rect;
104 u32 swap_interval;
105 Service::Nvidia::MultiFence multi_fence;
106 };
107
108 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
109 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
110 u32 height);
111 const IGBPBuffer& RequestBuffer(u32 slot) const;
112 void QueueBuffer(u32 slot, BufferTransformFlags transform,
113 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
114 Service::Nvidia::MultiFence& multi_fence);
115 void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
116 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
117 void ReleaseBuffer(u32 slot);
118 void Connect();
119 void Disconnect();
120 u32 Query(QueryType type);
121
122 u32 GetId() const {
123 return id;
124 }
125
126 bool IsConnected() const {
127 return is_connect;
128 }
129
130 Kernel::KWritableEvent& GetWritableBufferWaitEvent();
131
132 Kernel::KReadableEvent& GetBufferWaitEvent();
133
134private:
135 BufferQueue(const BufferQueue&) = delete;
136
137 u32 id{};
138 u64 layer_id{};
139 std::atomic_bool is_connect{};
140
141 std::list<u32> free_buffers;
142 std::array<Buffer, buffer_slots> buffers;
143 std::list<u32> queue_sequence;
144 Kernel::KEvent* buffer_wait_event{};
145
146 std::mutex free_buffers_mutex;
147 std::condition_variable free_buffers_condition;
148
149 std::mutex queue_sequence_mutex;
150
151 KernelHelpers::ServiceContext& service_context;
152};
153
154} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index 677bec932..41fbba219 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -20,122 +20,102 @@ BufferQueueConsumer::~BufferQueueConsumer() = default;
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) { 22 u64 max_frame_number) {
23 s32 num_dropped_buffers{}; 23 std::scoped_lock lock(core->mutex);
24
25 // Check that the consumer doesn't currently have the maximum number of buffers acquired.
26 const s32 num_acquired_buffers{
27 static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
28 return slot.buffer_state == BufferState::Acquired;
29 }))};
30
31 if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
32 LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
33 num_acquired_buffers, core->max_acquired_buffer_count);
34 return Status::InvalidOperation;
35 }
24 36
25 std::shared_ptr<IProducerListener> listener; 37 // Check if the queue is empty.
26 { 38 if (core->queue.empty()) {
27 std::unique_lock lock(core->mutex); 39 return Status::NoBufferAvailable;
28 40 }
29 // Check that the consumer doesn't currently have the maximum number of buffers acquired.
30 const s32 num_acquired_buffers{
31 static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
32 return slot.buffer_state == BufferState::Acquired;
33 }))};
34
35 if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
36 LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
37 num_acquired_buffers, core->max_acquired_buffer_count);
38 return Status::InvalidOperation;
39 }
40 41
41 // Check if the queue is empty. 42 auto front(core->queue.begin());
42 if (core->queue.empty()) {
43 return Status::NoBufferAvailable;
44 }
45 43
46 auto front(core->queue.begin()); 44 // If expected_present is specified, we may not want to return a buffer yet.
47 45 if (expected_present.count() != 0) {
48 // If expected_present is specified, we may not want to return a buffer yet. 46 constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
49 if (expected_present.count() != 0) {
50 constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
51
52 // The expected_present argument indicates when the buffer is expected to be
53 // presented on-screen.
54 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
55 const auto& buffer_item{core->queue[1]};
56
57 // If dropping entry[0] would leave us with a buffer that the consumer is not yet
58 // ready for, don't drop it.
59 if (max_frame_number && buffer_item.frame_number > max_frame_number) {
60 break;
61 }
62
63 // If entry[1] is timely, drop entry[0] (and repeat).
64 const auto desired_present = buffer_item.timestamp;
65 if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
66 desired_present > expected_present.count()) {
67 // This buffer is set to display in the near future, or desired_present is
68 // garbage.
69 LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
70 expected_present.count());
71 break;
72 }
73
74 LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
75 expected_present.count(), core->queue.size());
76
77 if (core->StillTracking(*front)) {
78 // Front buffer is still in mSlots, so mark the slot as free
79 slots[front->slot].buffer_state = BufferState::Free;
80 core->free_buffers.push_back(front->slot);
81 listener = core->connected_producer_listener;
82 ++num_dropped_buffers;
83 }
84
85 core->queue.erase(front);
86 front = core->queue.begin();
87 }
88 47
89 // See if the front buffer is ready to be acquired. 48 // The expected_present argument indicates when the buffer is expected to be presented
90 const auto desired_present = front->timestamp; 49 // on-screen.
91 const auto buffer_is_due = 50 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
92 desired_present <= expected_present.count() || 51 const auto& buffer_item{core->queue[1]};
93 desired_present > expected_present.count() + MAX_REASONABLE_NSEC;
94 const auto consumer_is_ready =
95 max_frame_number > 0 ? front->frame_number <= max_frame_number : true;
96 52
97 if (!buffer_is_due || !consumer_is_ready) { 53 // If dropping entry[0] would leave us with a buffer that the consumer is not yet ready
98 LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present, 54 // for, don't drop it.
99 expected_present.count()); 55 if (max_frame_number && buffer_item.frame_number > max_frame_number) {
100 return Status::PresentLater; 56 break;
101 } 57 }
102 58
103 LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present, 59 // If entry[1] is timely, drop entry[0] (and repeat).
104 expected_present.count()); 60 const auto desired_present = buffer_item.timestamp;
105 } 61 if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
62 desired_present > expected_present.count()) {
63 // This buffer is set to display in the near future, or desired_present is garbage.
64 LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
65 expected_present.count());
66 break;
67 }
106 68
107 const auto slot = front->slot; 69 LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
108 *out_buffer = *front; 70 expected_present.count(), core->queue.size());
109 71
110 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); 72 if (core->StillTracking(*front)) {
73 // Front buffer is still in mSlots, so mark the slot as free
74 slots[front->slot].buffer_state = BufferState::Free;
75 }
111 76
112 // If the front buffer is still being tracked, update its slot state 77 core->queue.erase(front);
113 if (core->StillTracking(*front)) { 78 front = core->queue.begin();
114 slots[slot].acquire_called = true;
115 slots[slot].needs_cleanup_on_release = false;
116 slots[slot].buffer_state = BufferState::Acquired;
117 slots[slot].fence = Fence::NoFence();
118 } 79 }
119 80
120 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr 81 // See if the front buffer is ready to be acquired.
121 // to avoid unnecessarily remapping this buffer on the consumer side. 82 const auto desired_present = front->timestamp;
122 if (out_buffer->acquire_called) { 83 if (desired_present > expected_present.count() &&
123 out_buffer->graphic_buffer = nullptr; 84 desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
85 LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
86 expected_present.count());
87 return Status::PresentLater;
124 } 88 }
125 89
126 core->queue.erase(front); 90 LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
91 expected_present.count());
92 }
93
94 const auto slot = front->slot;
95 *out_buffer = *front;
127 96
128 // We might have freed a slot while dropping old buffers, or the producer may be blocked 97 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
129 // waiting for the number of buffers in the queue to decrease. 98
130 core->SignalDequeueCondition(); 99 // If the front buffer is still being tracked, update its slot state
100 if (core->StillTracking(*front)) {
101 slots[slot].acquire_called = true;
102 slots[slot].needs_cleanup_on_release = false;
103 slots[slot].buffer_state = BufferState::Acquired;
104 slots[slot].fence = Fence::NoFence();
131 } 105 }
132 106
133 if (listener != nullptr) { 107 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
134 for (s32 i = 0; i < num_dropped_buffers; ++i) { 108 // avoid unnecessarily remapping this buffer on the consumer side.
135 listener->OnBufferReleased(); 109 if (out_buffer->acquire_called) {
136 } 110 out_buffer->graphic_buffer = nullptr;
137 } 111 }
138 112
113 core->queue.erase(front);
114
115 // We might have freed a slot while dropping old buffers, or the producer may be blocked
116 // waiting for the number of buffers in the queue to decrease.
117 core->SignalDequeueCondition();
118
139 return Status::NoError; 119 return Status::NoError;
140} 120}
141 121
@@ -147,7 +127,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
147 127
148 std::shared_ptr<IProducerListener> listener; 128 std::shared_ptr<IProducerListener> listener;
149 { 129 {
150 std::unique_lock lock(core->mutex); 130 std::scoped_lock lock(core->mutex);
151 131
152 // If the frame number has changed because the buffer has been reallocated, we can ignore 132 // If the frame number has changed because the buffer has been reallocated, we can ignore
153 // this ReleaseBuffer for the old buffer. 133 // this ReleaseBuffer for the old buffer.
@@ -170,8 +150,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
170 slots[slot].fence = release_fence; 150 slots[slot].fence = release_fence;
171 slots[slot].buffer_state = BufferState::Free; 151 slots[slot].buffer_state = BufferState::Free;
172 152
173 core->free_buffers.push_back(slot);
174
175 listener = core->connected_producer_listener; 153 listener = core->connected_producer_listener;
176 154
177 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); 155 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
@@ -189,7 +167,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
189 return Status::BadValue; 167 return Status::BadValue;
190 } 168 }
191 169
192 core->dequeue_condition.notify_all(); 170 core->SignalDequeueCondition();
193 } 171 }
194 172
195 // Call back without lock held 173 // Call back without lock held
@@ -209,7 +187,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
209 187
210 LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); 188 LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
211 189
212 BufferQueueCore::AutoLock lock(core); 190 std::scoped_lock lock(core->mutex);
213 191
214 if (core->is_abandoned) { 192 if (core->is_abandoned) {
215 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 193 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
index eb93b43ee..6082610e0 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -10,16 +10,12 @@
10 10
11namespace Service::android { 11namespace Service::android {
12 12
13BufferQueueCore::BufferQueueCore() : lock{mutex, std::defer_lock} { 13BufferQueueCore::BufferQueueCore() = default;
14 for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
15 free_slots.insert(slot);
16 }
17}
18 14
19BufferQueueCore::~BufferQueueCore() = default; 15BufferQueueCore::~BufferQueueCore() = default;
20 16
21void BufferQueueCore::NotifyShutdown() { 17void BufferQueueCore::NotifyShutdown() {
22 std::unique_lock lk(mutex); 18 std::scoped_lock lock(mutex);
23 19
24 is_shutting_down = true; 20 is_shutting_down = true;
25 21
@@ -35,7 +31,7 @@ bool BufferQueueCore::WaitForDequeueCondition() {
35 return false; 31 return false;
36 } 32 }
37 33
38 dequeue_condition.wait(lock); 34 dequeue_condition.wait(mutex);
39 35
40 return true; 36 return true;
41} 37}
@@ -86,26 +82,15 @@ s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
86void BufferQueueCore::FreeBufferLocked(s32 slot) { 82void BufferQueueCore::FreeBufferLocked(s32 slot) {
87 LOG_DEBUG(Service_NVFlinger, "slot {}", slot); 83 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
88 84
89 const auto had_buffer = slots[slot].graphic_buffer != nullptr;
90
91 slots[slot].graphic_buffer.reset(); 85 slots[slot].graphic_buffer.reset();
92 86
93 if (slots[slot].buffer_state == BufferState::Acquired) { 87 if (slots[slot].buffer_state == BufferState::Acquired) {
94 slots[slot].needs_cleanup_on_release = true; 88 slots[slot].needs_cleanup_on_release = true;
95 } 89 }
96 90
97 if (slots[slot].buffer_state != BufferState::Free) {
98 free_slots.insert(slot);
99 } else if (had_buffer) {
100 // If the slot was FREE, but we had a buffer, we need to move this slot from the free
101 // buffers list to the the free slots list.
102 free_buffers.remove(slot);
103 free_slots.insert(slot);
104 }
105
106 slots[slot].buffer_state = BufferState::Free; 91 slots[slot].buffer_state = BufferState::Free;
92 slots[slot].frame_number = UINT32_MAX;
107 slots[slot].acquire_called = false; 93 slots[slot].acquire_called = false;
108 slots[slot].frame_number = 0;
109 slots[slot].fence = Fence::NoFence(); 94 slots[slot].fence = Fence::NoFence();
110} 95}
111 96
@@ -126,8 +111,7 @@ bool BufferQueueCore::StillTracking(const BufferItem& item) const {
126 111
127void BufferQueueCore::WaitWhileAllocatingLocked() const { 112void BufferQueueCore::WaitWhileAllocatingLocked() const {
128 while (is_allocating) { 113 while (is_allocating) {
129 std::unique_lock lk(mutex); 114 is_allocating_condition.wait(mutex);
130 is_allocating_condition.wait(lk);
131 } 115 }
132} 116}
133 117
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h
index a3cd89f1c..4dfd53387 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -50,23 +50,7 @@ private:
50 void WaitWhileAllocatingLocked() const; 50 void WaitWhileAllocatingLocked() const;
51 51
52private: 52private:
53 class AutoLock final {
54 public:
55 AutoLock(std::shared_ptr<BufferQueueCore>& core_) : core{core_} {
56 core->lock.lock();
57 }
58
59 ~AutoLock() {
60 core->lock.unlock();
61 }
62
63 private:
64 std::shared_ptr<BufferQueueCore>& core;
65 };
66
67private:
68 mutable std::mutex mutex; 53 mutable std::mutex mutex;
69 mutable std::unique_lock<std::mutex> lock;
70 bool is_abandoned{}; 54 bool is_abandoned{};
71 bool consumer_controlled_by_app{}; 55 bool consumer_controlled_by_app{};
72 std::shared_ptr<IConsumerListener> consumer_listener; 56 std::shared_ptr<IConsumerListener> consumer_listener;
@@ -75,10 +59,8 @@ private:
75 std::shared_ptr<IProducerListener> connected_producer_listener; 59 std::shared_ptr<IProducerListener> connected_producer_listener;
76 BufferQueueDefs::SlotsType slots{}; 60 BufferQueueDefs::SlotsType slots{};
77 std::vector<BufferItem> queue; 61 std::vector<BufferItem> queue;
78 std::set<s32> free_slots;
79 std::list<s32> free_buffers;
80 s32 override_max_buffer_count{}; 62 s32 override_max_buffer_count{};
81 mutable std::condition_variable dequeue_condition; 63 mutable std::condition_variable_any dequeue_condition;
82 const bool use_async_buffer{}; // This is always disabled on HOS 64 const bool use_async_buffer{}; // This is always disabled on HOS
83 bool dequeue_buffer_cannot_block{}; 65 bool dequeue_buffer_cannot_block{};
84 PixelFormat default_buffer_format{PixelFormat::Rgba8888}; 66 PixelFormat default_buffer_format{PixelFormat::Rgba8888};
@@ -90,7 +72,7 @@ private:
90 u64 frame_counter{}; 72 u64 frame_counter{};
91 u32 transform_hint{}; 73 u32 transform_hint{};
92 bool is_allocating{}; 74 bool is_allocating{};
93 mutable std::condition_variable is_allocating_condition; 75 mutable std::condition_variable_any is_allocating_condition;
94 bool allow_allocation{true}; 76 bool allow_allocation{true};
95 u64 buffer_age{}; 77 u64 buffer_age{};
96 bool is_shutting_down{}; 78 bool is_shutting_down{};
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index 078091904..0833be57a 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -38,7 +38,7 @@ BufferQueueProducer::~BufferQueueProducer() {
38Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) { 38Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
39 LOG_DEBUG(Service_NVFlinger, "slot {}", slot); 39 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
40 40
41 BufferQueueCore::AutoLock lock(core); 41 std::scoped_lock lock(core->mutex);
42 42
43 if (core->is_abandoned) { 43 if (core->is_abandoned) {
44 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 44 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -65,7 +65,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
65 std::shared_ptr<IConsumerListener> listener; 65 std::shared_ptr<IConsumerListener> listener;
66 66
67 { 67 {
68 BufferQueueCore::AutoLock lock(core); 68 std::scoped_lock lock(core->mutex);
69 core->WaitWhileAllocatingLocked(); 69 core->WaitWhileAllocatingLocked();
70 if (core->is_abandoned) { 70 if (core->is_abandoned) {
71 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 71 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -156,6 +156,14 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
156 case BufferState::Acquired: 156 case BufferState::Acquired:
157 ++acquired_count; 157 ++acquired_count;
158 break; 158 break;
159 case BufferState::Free:
160 // We return the oldest of the free buffers to avoid stalling the producer if
161 // possible, since the consumer may still have pending reads of in-flight buffers
162 if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
163 slots[s].frame_number < slots[*found].frame_number) {
164 *found = s;
165 }
166 break;
159 default: 167 default:
160 break; 168 break;
161 } 169 }
@@ -183,27 +191,12 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
183 } 191 }
184 } 192 }
185 193
186 *found = BufferQueueCore::INVALID_BUFFER_SLOT;
187
188 // If we disconnect and reconnect quickly, we can be in a state where our slots are empty 194 // If we disconnect and reconnect quickly, we can be in a state where our slots are empty
189 // but we have many buffers in the queue. This can cause us to run out of memory if we 195 // but we have many buffers in the queue. This can cause us to run out of memory if we
190 // outrun the consumer. Wait here if it looks like we have too many buffers queued up. 196 // outrun the consumer. Wait here if it looks like we have too many buffers queued up.
191 const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count); 197 const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
192 if (too_many_buffers) { 198 if (too_many_buffers) {
193 LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size()); 199 LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
194 } else {
195 if (!core->free_buffers.empty()) {
196 auto slot = core->free_buffers.begin();
197 *found = *slot;
198 core->free_buffers.erase(slot);
199 } else if (core->allow_allocation && !core->free_slots.empty()) {
200 auto slot = core->free_slots.begin();
201 // Only return free slots up to the max buffer count
202 if (*slot < max_buffer_count) {
203 *found = *slot;
204 core->free_slots.erase(slot);
205 }
206 }
207 } 200 }
208 201
209 // If no buffer is found, or if the queue has too many buffers outstanding, wait for a 202 // If no buffer is found, or if the queue has too many buffers outstanding, wait for a
@@ -240,7 +233,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
240 Status return_flags = Status::NoError; 233 Status return_flags = Status::NoError;
241 bool attached_by_consumer = false; 234 bool attached_by_consumer = false;
242 { 235 {
243 BufferQueueCore::AutoLock lock(core); 236 std::scoped_lock lock(core->mutex);
244 core->WaitWhileAllocatingLocked(); 237 core->WaitWhileAllocatingLocked();
245 if (format == PixelFormat::NoFormat) { 238 if (format == PixelFormat::NoFormat) {
246 format = core->default_buffer_format; 239 format = core->default_buffer_format;
@@ -317,12 +310,13 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
317 } 310 }
318 311
319 { 312 {
320 BufferQueueCore::AutoLock lock(core); 313 std::scoped_lock lock(core->mutex);
321 if (core->is_abandoned) { 314 if (core->is_abandoned) {
322 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 315 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
323 return Status::NoInit; 316 return Status::NoInit;
324 } 317 }
325 318
319 slots[*out_slot].frame_number = UINT32_MAX;
326 slots[*out_slot].graphic_buffer = graphic_buffer; 320 slots[*out_slot].graphic_buffer = graphic_buffer;
327 } 321 }
328 } 322 }
@@ -339,7 +333,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
339Status BufferQueueProducer::DetachBuffer(s32 slot) { 333Status BufferQueueProducer::DetachBuffer(s32 slot) {
340 LOG_DEBUG(Service_NVFlinger, "slot {}", slot); 334 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
341 335
342 BufferQueueCore::AutoLock lock(core); 336 std::scoped_lock lock(core->mutex);
343 if (core->is_abandoned) { 337 if (core->is_abandoned) {
344 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 338 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
345 return Status::NoInit; 339 return Status::NoInit;
@@ -374,7 +368,7 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out
374 return Status::BadValue; 368 return Status::BadValue;
375 } 369 }
376 370
377 BufferQueueCore::AutoLock lock(core); 371 std::scoped_lock lock(core->mutex);
378 372
379 core->WaitWhileAllocatingLocked(); 373 core->WaitWhileAllocatingLocked();
380 374
@@ -382,12 +376,21 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out
382 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 376 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
383 return Status::NoInit; 377 return Status::NoInit;
384 } 378 }
385 if (core->free_buffers.empty()) { 379
386 return Status::NoMemory; 380 // Find the oldest valid slot
381 int found = BufferQueueCore::INVALID_BUFFER_SLOT;
382 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
383 if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) {
384 if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
385 slots[s].frame_number < slots[found].frame_number) {
386 found = s;
387 }
388 }
387 } 389 }
388 390
389 const s32 found = core->free_buffers.front(); 391 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
390 core->free_buffers.remove(found); 392 return Status::NoMemory;
393 }
391 394
392 LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found); 395 LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
393 396
@@ -409,7 +412,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
409 return Status::BadValue; 412 return Status::BadValue;
410 } 413 }
411 414
412 BufferQueueCore::AutoLock lock(core); 415 std::scoped_lock lock(core->mutex);
413 core->WaitWhileAllocatingLocked(); 416 core->WaitWhileAllocatingLocked();
414 417
415 Status return_flags = Status::NoError; 418 Status return_flags = Status::NoError;
@@ -469,7 +472,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
469 BufferItem item; 472 BufferItem item;
470 473
471 { 474 {
472 BufferQueueCore::AutoLock lock(core); 475 std::scoped_lock lock(core->mutex);
473 476
474 if (core->is_abandoned) { 477 if (core->is_abandoned) {
475 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 478 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -554,7 +557,9 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
554 // mark it as freed 557 // mark it as freed
555 if (core->StillTracking(*front)) { 558 if (core->StillTracking(*front)) {
556 slots[front->slot].buffer_state = BufferState::Free; 559 slots[front->slot].buffer_state = BufferState::Free;
557 core->free_buffers.push_front(front->slot); 560 // Reset the frame number of the freed buffer so that it is the first in line to
561 // be dequeued again
562 slots[front->slot].frame_number = 0;
558 } 563 }
559 // Overwrite the droppable buffer with the incoming one 564 // Overwrite the droppable buffer with the incoming one
560 *front = item; 565 *front = item;
@@ -582,10 +587,9 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
582 // Call back without the main BufferQueue lock held, but with the callback lock held so we can 587 // Call back without the main BufferQueue lock held, but with the callback lock held so we can
583 // ensure that callbacks occur in order 588 // ensure that callbacks occur in order
584 { 589 {
585 std::unique_lock lock(callback_mutex); 590 std::scoped_lock lock(callback_mutex);
586 while (callback_ticket != current_callback_ticket) { 591 while (callback_ticket != current_callback_ticket) {
587 std::unique_lock<std::mutex> lk(callback_mutex); 592 callback_condition.wait(callback_mutex);
588 callback_condition.wait(lk);
589 } 593 }
590 594
591 if (frameAvailableListener != nullptr) { 595 if (frameAvailableListener != nullptr) {
@@ -604,7 +608,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
604void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { 608void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
605 LOG_DEBUG(Service_NVFlinger, "slot {}", slot); 609 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
606 610
607 BufferQueueCore::AutoLock lock(core); 611 std::scoped_lock lock(core->mutex);
608 612
609 if (core->is_abandoned) { 613 if (core->is_abandoned) {
610 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); 614 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -621,8 +625,8 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
621 return; 625 return;
622 } 626 }
623 627
624 core->free_buffers.push_front(slot);
625 slots[slot].buffer_state = BufferState::Free; 628 slots[slot].buffer_state = BufferState::Free;
629 slots[slot].frame_number = 0;
626 slots[slot].fence = fence; 630 slots[slot].fence = fence;
627 631
628 core->SignalDequeueCondition(); 632 core->SignalDequeueCondition();
@@ -630,7 +634,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
630} 634}
631 635
632Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { 636Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
633 BufferQueueCore::AutoLock lock(core); 637 std::scoped_lock lock(core->mutex);
634 638
635 if (out_value == nullptr) { 639 if (out_value == nullptr) {
636 LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); 640 LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
@@ -687,7 +691,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
687Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener, 691Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
688 NativeWindowApi api, bool producer_controlled_by_app, 692 NativeWindowApi api, bool producer_controlled_by_app,
689 QueueBufferOutput* output) { 693 QueueBufferOutput* output) {
690 BufferQueueCore::AutoLock lock(core); 694 std::scoped_lock lock(core->mutex);
691 695
692 LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, 696 LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
693 producer_controlled_by_app); 697 producer_controlled_by_app);
@@ -745,7 +749,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
745 std::shared_ptr<IConsumerListener> listener; 749 std::shared_ptr<IConsumerListener> listener;
746 750
747 { 751 {
748 BufferQueueCore::AutoLock lock(core); 752 std::scoped_lock lock(core->mutex);
749 753
750 core->WaitWhileAllocatingLocked(); 754 core->WaitWhileAllocatingLocked();
751 755
@@ -795,10 +799,11 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
795 return Status::BadValue; 799 return Status::BadValue;
796 } 800 }
797 801
798 BufferQueueCore::AutoLock lock(core); 802 std::scoped_lock lock(core->mutex);
799 803
800 slots[slot] = {}; 804 slots[slot] = {};
801 slots[slot].graphic_buffer = buffer; 805 slots[slot].graphic_buffer = buffer;
806 slots[slot].frame_number = 0;
802 807
803 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for 808 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
804 // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. 809 // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this.
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
index 5ddeebe0c..77fdcae8e 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -77,7 +77,7 @@ private:
77 std::mutex callback_mutex; 77 std::mutex callback_mutex;
78 s32 next_callback_ticket{}; 78 s32 next_callback_ticket{};
79 s32 current_callback_ticket{}; 79 s32 current_callback_ticket{};
80 std::condition_variable callback_condition; 80 std::condition_variable_any callback_condition;
81}; 81};
82 82
83} // namespace Service::android 83} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp
index 3ccbb7fb8..be65a3f88 100644
--- a/src/core/hle/service/nvflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -18,7 +18,7 @@ ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
18 : consumer{std::move(consumer_)} {} 18 : consumer{std::move(consumer_)} {}
19 19
20ConsumerBase::~ConsumerBase() { 20ConsumerBase::~ConsumerBase() {
21 std::unique_lock lock(mutex); 21 std::scoped_lock lock(mutex);
22 22
23 ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); 23 ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
24} 24}
@@ -36,17 +36,17 @@ void ConsumerBase::FreeBufferLocked(s32 slot_index) {
36} 36}
37 37
38void ConsumerBase::OnFrameAvailable(const BufferItem& item) { 38void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
39 std::unique_lock lock(mutex); 39 std::scoped_lock lock(mutex);
40 LOG_DEBUG(Service_NVFlinger, "called"); 40 LOG_DEBUG(Service_NVFlinger, "called");
41} 41}
42 42
43void ConsumerBase::OnFrameReplaced(const BufferItem& item) { 43void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
44 std::unique_lock lock(mutex); 44 std::scoped_lock lock(mutex);
45 LOG_DEBUG(Service_NVFlinger, "called"); 45 LOG_DEBUG(Service_NVFlinger, "called");
46} 46}
47 47
48void ConsumerBase::OnBuffersReleased() { 48void ConsumerBase::OnBuffersReleased() {
49 std::unique_lock lock(mutex); 49 std::scoped_lock lock(mutex);
50 LOG_DEBUG(Service_NVFlinger, "called"); 50 LOG_DEBUG(Service_NVFlinger, "called");
51} 51}
52 52
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 3fed51400..28d30eee2 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -322,7 +322,7 @@ struct Memory::Impl {
322 } 322 }
323 323
324 if (Settings::IsFastmemEnabled()) { 324 if (Settings::IsFastmemEnabled()) {
325 const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached; 325 const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
326 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 326 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
327 } 327 }
328 328
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
index d3cbb14a9..cb47d253c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
6
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 7#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 9#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 3c2a5e16f..aa7082978 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
5#include <tuple> 6#include <tuple>
6#include <utility> 7#include <utility>
7 8
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 53be98ced..28f6a6184 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <bit>
7#include <climits> 8#include <climits>
8 9
9#include <boost/container/static_vector.hpp> 10#include <boost/container/static_vector.hpp>
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/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 81fac94bf..40f7755e8 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -56,6 +56,18 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi
56 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; 56 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
57 return PREFERRED_CPU_FMT; 57 return PREFERRED_CPU_FMT;
58} 58}
59
60// List all the currently available hwcontext in ffmpeg
61std::vector<AVHWDeviceType> ListSupportedContexts() {
62 std::vector<AVHWDeviceType> contexts{};
63 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
64 do {
65 current_device_type = av_hwdevice_iterate_types(current_device_type);
66 contexts.push_back(current_device_type);
67 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
68 return contexts;
69}
70
59} // namespace 71} // namespace
60 72
61void AVFrameDeleter(AVFrame* ptr) { 73void AVFrameDeleter(AVFrame* ptr) {
@@ -76,17 +88,6 @@ Codec::~Codec() {
76 av_buffer_unref(&av_gpu_decoder); 88 av_buffer_unref(&av_gpu_decoder);
77} 89}
78 90
79// List all the currently available hwcontext in ffmpeg
80static std::vector<AVHWDeviceType> ListSupportedContexts() {
81 std::vector<AVHWDeviceType> contexts{};
82 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
83 do {
84 current_device_type = av_hwdevice_iterate_types(current_device_type);
85 contexts.push_back(current_device_type);
86 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
87 return contexts;
88}
89
90bool Codec::CreateGpuAvDevice() { 91bool Codec::CreateGpuAvDevice() {
91 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; 92 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
92 static const auto supported_contexts = ListSupportedContexts(); 93 static const auto supported_contexts = ListSupportedContexts();
@@ -96,6 +97,8 @@ bool Codec::CreateGpuAvDevice() {
96 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); 97 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
97 continue; 98 continue;
98 } 99 }
100 // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create
101 av_buffer_unref(&av_gpu_decoder);
99 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); 102 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
100 if (hwdevice_res < 0) { 103 if (hwdevice_res < 0) {
101 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", 104 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
@@ -127,15 +130,19 @@ bool Codec::CreateGpuAvDevice() {
127 av_codec->name, av_hwdevice_get_type_name(type)); 130 av_codec->name, av_hwdevice_get_type_name(type));
128 break; 131 break;
129 } 132 }
130 if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { 133 if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
131 av_codec_ctx->pix_fmt = config->pix_fmt; 134#if defined(__unix__)
132 if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) { 135 // Some linux decoding backends are reported to crash with this config method
136 // TODO(ameerj): Properly support this method
137 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) {
133 // skip zero-copy decoders, we don't currently support them 138 // skip zero-copy decoders, we don't currently support them
134 LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", 139 LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
135 av_hwdevice_get_type_name(type), config->methods); 140 av_hwdevice_get_type_name(type), config->methods);
136 continue; 141 continue;
137 } 142 }
143#endif
138 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); 144 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
145 av_codec_ctx->pix_fmt = config->pix_fmt;
139 return true; 146 return true;
140 } 147 }
141 } 148 }
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 656dd7eb0..597301eeb 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -282,7 +282,7 @@ void main() {
282 282
283u64 Device::GetCurrentDedicatedVideoMemory() const { 283u64 Device::GetCurrentDedicatedVideoMemory() const {
284 GLint cur_avail_mem_kb = 0; 284 GLint cur_avail_mem_kb = 0;
285 glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &cur_avail_mem_kb); 285 glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &cur_avail_mem_kb);
286 return static_cast<u64>(cur_avail_mem_kb) * 1_KiB; 286 return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
287} 287}
288 288
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 7e06d0069..e6f9ece8b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -15,8 +15,9 @@
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/math_util.h" 16#include "common/math_util.h"
17#include "common/microprofile.h" 17#include "common/microprofile.h"
18#include "common/scope_exit.h"
18#include "common/settings.h" 19#include "common/settings.h"
19#include "core/memory.h" 20
20#include "video_core/engines/kepler_compute.h" 21#include "video_core/engines/kepler_compute.h"
21#include "video_core/engines/maxwell_3d.h" 22#include "video_core/engines/maxwell_3d.h"
22#include "video_core/memory_manager.h" 23#include "video_core/memory_manager.h"
@@ -210,6 +211,7 @@ void RasterizerOpenGL::Clear() {
210void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { 211void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
211 MICROPROFILE_SCOPE(OpenGL_Drawing); 212 MICROPROFILE_SCOPE(OpenGL_Drawing);
212 213
214 SCOPE_EXIT({ gpu.TickWork(); });
213 query_cache.UpdateCounters(); 215 query_cache.UpdateCounters();
214 216
215 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; 217 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -265,8 +267,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
265 267
266 ++num_queued_commands; 268 ++num_queued_commands;
267 has_written_global_memory |= pipeline->WritesGlobalMemory(); 269 has_written_global_memory |= pipeline->WritesGlobalMemory();
268
269 gpu.TickWork();
270} 270}
271 271
272void RasterizerOpenGL::DispatchCompute() { 272void RasterizerOpenGL::DispatchCompute() {
@@ -352,7 +352,7 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
352 shader_cache.OnCPUWrite(addr, size); 352 shader_cache.OnCPUWrite(addr, size);
353 { 353 {
354 std::scoped_lock lock{texture_cache.mutex}; 354 std::scoped_lock lock{texture_cache.mutex};
355 texture_cache.CachedWriteMemory(addr, size); 355 texture_cache.WriteMemory(addr, size);
356 } 356 }
357 { 357 {
358 std::scoped_lock lock{buffer_cache.mutex}; 358 std::scoped_lock lock{buffer_cache.mutex};
@@ -364,10 +364,6 @@ void RasterizerOpenGL::SyncGuestHost() {
364 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 364 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
365 shader_cache.SyncGuestHost(); 365 shader_cache.SyncGuestHost();
366 { 366 {
367 std::scoped_lock lock{texture_cache.mutex};
368 texture_cache.FlushCachedWrites();
369 }
370 {
371 std::scoped_lock lock{buffer_cache.mutex}; 367 std::scoped_lock lock{buffer_cache.mutex};
372 buffer_cache.FlushCachedWrites(); 368 buffer_cache.FlushCachedWrites();
373 } 369 }
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index ec03cca38..abda1c490 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -367,17 +367,14 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
367 PipelineLayoutCreateInfo(two_textures_set_layout.address()))), 367 PipelineLayoutCreateInfo(two_textures_set_layout.address()))),
368 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), 368 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
369 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), 369 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)),
370 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
370 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), 371 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
371 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), 372 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
372 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), 373 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
373 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), 374 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
374 convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), 375 convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
375 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), 376 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
376 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { 377 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
377 if (device.IsExtShaderStencilExportSupported()) {
378 blit_depth_stencil_frag = BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV);
379 }
380}
381 378
382BlitImageHelper::~BlitImageHelper() = default; 379BlitImageHelper::~BlitImageHelper() = default;
383 380
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index dd6e0027e..fa87d37f8 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -408,7 +408,7 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
408 pipeline_cache.OnCPUWrite(addr, size); 408 pipeline_cache.OnCPUWrite(addr, size);
409 { 409 {
410 std::scoped_lock lock{texture_cache.mutex}; 410 std::scoped_lock lock{texture_cache.mutex};
411 texture_cache.CachedWriteMemory(addr, size); 411 texture_cache.WriteMemory(addr, size);
412 } 412 }
413 { 413 {
414 std::scoped_lock lock{buffer_cache.mutex}; 414 std::scoped_lock lock{buffer_cache.mutex};
@@ -419,10 +419,6 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
419void RasterizerVulkan::SyncGuestHost() { 419void RasterizerVulkan::SyncGuestHost() {
420 pipeline_cache.SyncGuestHost(); 420 pipeline_cache.SyncGuestHost();
421 { 421 {
422 std::scoped_lock lock{texture_cache.mutex};
423 texture_cache.FlushCachedWrites();
424 }
425 {
426 std::scoped_lock lock{buffer_cache.mutex}; 422 std::scoped_lock lock{buffer_cache.mutex};
427 buffer_cache.FlushCachedWrites(); 423 buffer_cache.FlushCachedWrites();
428 } 424 }
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index f2890d263..2c2ccc7c6 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1451,8 +1451,7 @@ bool Image::BlitScaleHelper(bool scale_up) {
1451 1451
1452 runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region, 1452 runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region,
1453 src_region, operation, BLIT_OPERATION); 1453 src_region, operation, BLIT_OPERATION);
1454 } else if (!runtime->device.IsBlitDepthStencilSupported() && 1454 } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
1455 aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
1456 if (!blit_framebuffer) { 1455 if (!blit_framebuffer) {
1457 blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent); 1456 blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent);
1458 } 1457 }
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index cc7999027..dd0106432 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -39,9 +39,6 @@ enum class ImageFlagBits : u32 {
39 Rescaled = 1 << 13, 39 Rescaled = 1 << 13,
40 CheckingRescalable = 1 << 14, 40 CheckingRescalable = 1 << 14,
41 IsRescalable = 1 << 15, 41 IsRescalable = 1 << 15,
42
43 // Cached CPU
44 CachedCpuModified = 1 << 16, ///< Contents have been modified from the CPU
45}; 42};
46DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) 43DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
47 44
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 099b2ae1b..8fef74117 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -438,23 +438,6 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
438} 438}
439 439
440template <class P> 440template <class P>
441void TextureCache<P>::CachedWriteMemory(VAddr cpu_addr, size_t size) {
442 const VAddr new_cpu_addr = Common::AlignDown(cpu_addr, CPU_PAGE_SIZE);
443 const size_t new_size = Common::AlignUp(size + cpu_addr - new_cpu_addr, CPU_PAGE_SIZE);
444 ForEachImageInRegion(new_cpu_addr, new_size, [this](ImageId image_id, Image& image) {
445 if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
446 return;
447 }
448 image.flags |= ImageFlagBits::CachedCpuModified;
449 cached_cpu_invalidate.insert(image_id);
450
451 if (True(image.flags & ImageFlagBits::Tracked)) {
452 UntrackImage(image, image_id);
453 }
454 });
455}
456
457template <class P>
458void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) { 441void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
459 std::vector<ImageId> images; 442 std::vector<ImageId> images;
460 ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) { 443 ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) {
@@ -512,18 +495,6 @@ void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) {
512} 495}
513 496
514template <class P> 497template <class P>
515void TextureCache<P>::FlushCachedWrites() {
516 for (ImageId image_id : cached_cpu_invalidate) {
517 Image& image = slot_images[image_id];
518 if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
519 image.flags &= ~ImageFlagBits::CachedCpuModified;
520 image.flags |= ImageFlagBits::CpuModified;
521 }
522 }
523 cached_cpu_invalidate.clear();
524}
525
526template <class P>
527void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, 498void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
528 const Tegra::Engines::Fermi2D::Surface& src, 499 const Tegra::Engines::Fermi2D::Surface& src,
529 const Tegra::Engines::Fermi2D::Config& copy) { 500 const Tegra::Engines::Fermi2D::Config& copy) {
@@ -1109,8 +1080,6 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1109 Image& overlap = slot_images[overlap_id]; 1080 Image& overlap = slot_images[overlap_id];
1110 if (True(overlap.flags & ImageFlagBits::GpuModified)) { 1081 if (True(overlap.flags & ImageFlagBits::GpuModified)) {
1111 new_image.flags |= ImageFlagBits::GpuModified; 1082 new_image.flags |= ImageFlagBits::GpuModified;
1112 new_image.modification_tick =
1113 std::max(overlap.modification_tick, new_image.modification_tick);
1114 } 1083 }
1115 if (overlap.info.num_samples != new_image.info.num_samples) { 1084 if (overlap.info.num_samples != new_image.info.num_samples) {
1116 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented"); 1085 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
@@ -1589,9 +1558,6 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1589template <class P> 1558template <class P>
1590void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) { 1559void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
1591 ASSERT(False(image.flags & ImageFlagBits::Tracked)); 1560 ASSERT(False(image.flags & ImageFlagBits::Tracked));
1592 if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
1593 return;
1594 }
1595 image.flags |= ImageFlagBits::Tracked; 1561 image.flags |= ImageFlagBits::Tracked;
1596 if (False(image.flags & ImageFlagBits::Sparse)) { 1562 if (False(image.flags & ImageFlagBits::Sparse)) {
1597 rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1); 1563 rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1);
@@ -1648,9 +1614,6 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
1648 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); 1614 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1649 } 1615 }
1650 total_used_memory -= Common::AlignUp(tentative_size, 1024); 1616 total_used_memory -= Common::AlignUp(tentative_size, 1024);
1651 if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
1652 cached_cpu_invalidate.erase(image_id);
1653 }
1654 const GPUVAddr gpu_addr = image.gpu_addr; 1617 const GPUVAddr gpu_addr = image.gpu_addr;
1655 const auto alloc_it = image_allocs_table.find(gpu_addr); 1618 const auto alloc_it = image_allocs_table.find(gpu_addr);
1656 if (alloc_it == image_allocs_table.end()) { 1619 if (alloc_it == image_allocs_table.end()) {
@@ -1817,11 +1780,7 @@ template <class P>
1817void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool invalidate) { 1780void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool invalidate) {
1818 Image& image = slot_images[image_id]; 1781 Image& image = slot_images[image_id];
1819 if (invalidate) { 1782 if (invalidate) {
1820 if (True(image.flags & ImageFlagBits::CachedCpuModified)) { 1783 image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified);
1821 cached_cpu_invalidate.erase(image_id);
1822 }
1823 image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified |
1824 ImageFlagBits::CachedCpuModified);
1825 if (False(image.flags & ImageFlagBits::Tracked)) { 1784 if (False(image.flags & ImageFlagBits::Tracked)) {
1826 TrackImage(image, image_id); 1785 TrackImage(image, image_id);
1827 } 1786 }
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index ad5978a33..b1324edf3 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -8,7 +8,6 @@
8#include <span> 8#include <span>
9#include <type_traits> 9#include <type_traits>
10#include <unordered_map> 10#include <unordered_map>
11#include <unordered_set>
12#include <vector> 11#include <vector>
13#include <queue> 12#include <queue>
14 13
@@ -51,9 +50,6 @@ class TextureCache {
51 /// Address shift for caching images into a hash table 50 /// Address shift for caching images into a hash table
52 static constexpr u64 PAGE_BITS = 20; 51 static constexpr u64 PAGE_BITS = 20;
53 52
54 static constexpr u64 CPU_PAGE_BITS = 12;
55 static constexpr u64 CPU_PAGE_SIZE = 1ULL << CPU_PAGE_BITS;
56
57 /// Enables debugging features to the texture cache 53 /// Enables debugging features to the texture cache
58 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; 54 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
59 /// Implement blits as copies between framebuffers 55 /// Implement blits as copies between framebuffers
@@ -140,9 +136,6 @@ public:
140 /// Mark images in a range as modified from the CPU 136 /// Mark images in a range as modified from the CPU
141 void WriteMemory(VAddr cpu_addr, size_t size); 137 void WriteMemory(VAddr cpu_addr, size_t size);
142 138
143 /// Mark images in a range as modified from the CPU
144 void CachedWriteMemory(VAddr cpu_addr, size_t size);
145
146 /// Download contents of host images to guest memory in a region 139 /// Download contents of host images to guest memory in a region
147 void DownloadMemory(VAddr cpu_addr, size_t size); 140 void DownloadMemory(VAddr cpu_addr, size_t size);
148 141
@@ -152,8 +145,6 @@ public:
152 /// Remove images in a region 145 /// Remove images in a region
153 void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size); 146 void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size);
154 147
155 void FlushCachedWrites();
156
157 /// Blit an image with the given parameters 148 /// Blit an image with the given parameters
158 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, 149 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
159 const Tegra::Engines::Fermi2D::Surface& src, 150 const Tegra::Engines::Fermi2D::Surface& src,
@@ -375,8 +366,6 @@ private:
375 366
376 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views; 367 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
377 368
378 std::unordered_set<ImageId> cached_cpu_invalidate;
379
380 VAddr virtual_invalid_space{}; 369 VAddr virtual_invalid_space{};
381 370
382 bool has_deleted_images = false; 371 bool has_deleted_images = false;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index e142bee35..f3a05ada9 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -621,6 +621,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
621 khr_push_descriptor = false; 621 khr_push_descriptor = false;
622 break; 622 break;
623 } 623 }
624 const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff;
625 if (nv_major_version >= 510) {
626 LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
627 cant_blit_msaa = true;
628 }
624 } 629 }
625 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; 630 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
626 if (ext_extended_dynamic_state && is_radv) { 631 if (ext_extended_dynamic_state && is_radv) {
@@ -731,7 +736,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
731} 736}
732 737
733void Device::ReportLoss() const { 738void Device::ReportLoss() const {
734 LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); 739 LOG_CRITICAL(Render_Vulkan, "Device loss occurred!");
735 740
736 // 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
737 std::this_thread::sleep_for(std::chrono::seconds{15}); 742 std::this_thread::sleep_for(std::chrono::seconds{15});
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 30902101d..b1467d016 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -293,7 +293,7 @@ if (YUZU_USE_QT_WEB_ENGINE)
293endif () 293endif ()
294 294
295if(UNIX AND NOT APPLE) 295if(UNIX AND NOT APPLE)
296 install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 296 install(TARGETS yuzu)
297endif() 297endif()
298 298
299if (YUZU_USE_BUNDLED_QT) 299if (YUZU_USE_BUNDLED_QT)
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index edb525e82..c1d90d588 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -214,14 +214,14 @@
214 <item row="1" column="1"> 214 <item row="1" column="1">
215 <widget class="QCheckBox" name="enable_all_controllers"> 215 <widget class="QCheckBox" name="enable_all_controllers">
216 <property name="text"> 216 <property name="text">
217 <string>Enable all Controller Types</string> 217 <string>Enable All Controller Types</string>
218 </property> 218 </property>
219 </widget> 219 </widget>
220 </item> 220 </item>
221 <item row="2" column="1"> 221 <item row="2" column="1">
222 <widget class="QCheckBox" name="disable_web_applet"> 222 <widget class="QCheckBox" name="disable_web_applet">
223 <property name="text"> 223 <property name="text">
224 <string>Disable Web Applet**</string> 224 <string>Disable Web Applet</string>
225 </property> 225 </property>
226 </widget> 226 </widget>
227 </item> 227 </item>
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/main.cpp b/src/yuzu/main.cpp
index 3b7058a2b..62d15f8cd 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -293,8 +293,6 @@ GMainWindow::GMainWindow()
293 293
294 MigrateConfigFiles(); 294 MigrateConfigFiles();
295 295
296 ui->action_Fullscreen->setChecked(false);
297
298#if defined(HAVE_SDL2) && !defined(_WIN32) 296#if defined(HAVE_SDL2) && !defined(_WIN32)
299 SDL_InitSubSystem(SDL_INIT_VIDEO); 297 SDL_InitSubSystem(SDL_INIT_VIDEO);
300 // SDL disables the screen saver by default, and setting the hint 298 // SDL disables the screen saver by default, and setting the hint
@@ -312,17 +310,20 @@ GMainWindow::GMainWindow()
312 } 310 }
313 311
314 QString game_path; 312 QString game_path;
313 bool has_gamepath = false;
314 bool is_fullscreen = false;
315 315
316 for (int i = 1; i < args.size(); ++i) { 316 for (int i = 1; i < args.size(); ++i) {
317 // Preserves drag/drop functionality 317 // Preserves drag/drop functionality
318 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) { 318 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
319 game_path = args[1]; 319 game_path = args[1];
320 has_gamepath = true;
320 break; 321 break;
321 } 322 }
322 323
323 // Launch game in fullscreen mode 324 // Launch game in fullscreen mode
324 if (args[i] == QStringLiteral("-f")) { 325 if (args[i] == QStringLiteral("-f")) {
325 ui->action_Fullscreen->setChecked(true); 326 is_fullscreen = true;
326 continue; 327 continue;
327 } 328 }
328 329
@@ -365,9 +366,15 @@ GMainWindow::GMainWindow()
365 } 366 }
366 367
367 game_path = args[++i]; 368 game_path = args[++i];
369 has_gamepath = true;
368 } 370 }
369 } 371 }
370 372
373 // Override fullscreen setting if gamepath or argument is provided
374 if (has_gamepath || is_fullscreen) {
375 ui->action_Fullscreen->setChecked(is_fullscreen);
376 }
377
371 if (!game_path.isEmpty()) { 378 if (!game_path.isEmpty()) {
372 BootGame(game_path); 379 BootGame(game_path);
373 } 380 }
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 74fc24972..c8901f2df 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -45,7 +45,7 @@ if (YUZU_USE_EXTERNAL_SDL2)
45endif() 45endif()
46 46
47if(UNIX AND NOT APPLE) 47if(UNIX AND NOT APPLE)
48 install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 48 install(TARGETS yuzu-cmd)
49endif() 49endif()
50 50
51if (MSVC) 51if (MSVC)
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 =
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 14bf82f39..ab12dd15d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -74,6 +74,7 @@ static void PrintVersion() {
74int main(int argc, char** argv) { 74int main(int argc, char** argv) {
75 Common::Log::Initialize(); 75 Common::Log::Initialize();
76 Common::Log::SetColorConsoleBackendEnabled(true); 76 Common::Log::SetColorConsoleBackendEnabled(true);
77 Common::Log::Start();
77 Common::DetachedTasks detached_tasks; 78 Common::DetachedTasks detached_tasks;
78 79
79 int option_index = 0; 80 int option_index = 0;