summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_out.cpp4
-rw-r--r--src/audio_core/audio_out.h3
-rw-r--r--src/audio_core/audio_renderer.cpp110
-rw-r--r--src/audio_core/audio_renderer.h20
-rw-r--r--src/audio_core/behavior_info.h30
-rw-r--r--src/audio_core/command_generator.h14
-rw-r--r--src/audio_core/common.h2
-rw-r--r--src/audio_core/effect_context.h26
-rw-r--r--src/audio_core/mix_context.h26
-rw-r--r--src/audio_core/sink_context.cpp18
-rw-r--r--src/audio_core/sink_context.h17
-rw-r--r--src/audio_core/stream.cpp10
-rw-r--r--src/audio_core/stream.h21
-rw-r--r--src/core/core.cpp56
-rw-r--r--src/core/core.h177
-rw-r--r--src/core/cpu_manager.cpp24
-rw-r--r--src/core/file_sys/card_image.cpp5
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/submission_package.cpp6
-rw-r--r--src/core/file_sys/submission_package.h4
-rw-r--r--src/core/frontend/emu_window.h4
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp51
-rw-r--r--src/core/hle/service/am/am.h12
-rw-r--r--src/core/hle/service/am/applet_ae.cpp21
-rw-r--r--src/core/hle/service/am/applet_ae.h4
-rw-r--r--src/core/hle/service/am/applet_oe.cpp14
-rw-r--r--src/core/hle/service/am/applet_oe.h4
-rw-r--r--src/core/hle/service/service.cpp20
-rw-r--r--src/core/hle/service/service.h21
-rw-r--r--src/core/hle/service/vi/vi.cpp57
-rw-r--r--src/core/hle/service/vi/vi.h7
-rw-r--r--src/core/hle/service/vi/vi_m.cpp3
-rw-r--r--src/core/hle/service/vi/vi_m.h4
-rw-r--r--src/core/hle/service/vi/vi_s.cpp3
-rw-r--r--src/core/hle/service/vi/vi_s.h4
-rw-r--r--src/core/hle/service/vi/vi_u.cpp3
-rw-r--r--src/core/hle/service/vi/vi_u.h4
-rw-r--r--src/core/loader/loader.cpp12
-rw-r--r--src/core/loader/loader.h4
-rw-r--r--src/core/loader/nsp.cpp5
-rw-r--r--src/core/loader/nsp.h3
-rw-r--r--src/core/loader/xci.cpp5
-rw-r--r--src/core/loader/xci.h3
-rw-r--r--src/input_common/CMakeLists.txt4
-rwxr-xr-xsrc/input_common/analog_from_button.cpp122
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp3
-rw-r--r--src/input_common/sdl/sdl_impl.cpp3
-rw-r--r--src/input_common/udp/client.cpp4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp2
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp9
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h7
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp44
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.h2
-rw-r--r--src/video_core/shader/decode/image.cpp4
-rw-r--r--src/yuzu/applets/controller.cpp123
-rw-r--r--src/yuzu/applets/controller.h17
-rw-r--r--src/yuzu/bootmanager.cpp8
-rw-r--r--src/yuzu/bootmanager.h9
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp138
-rw-r--r--src/yuzu/configuration/configure_input_player.h13
-rw-r--r--src/yuzu/main.cpp24
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp100
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h20
-rw-r--r--src/yuzu_cmd/yuzu.cpp6
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp2
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h3
-rw-r--r--src/yuzu_tester/yuzu.cpp4
71 files changed, 987 insertions, 547 deletions
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 8619a3f03..fe3a898ad 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -43,6 +43,10 @@ std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
43 return stream->GetTagsAndReleaseBuffers(max_count); 43 return stream->GetTagsAndReleaseBuffers(max_count);
44} 44}
45 45
46std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream) {
47 return stream->GetTagsAndReleaseBuffers();
48}
49
46void AudioOut::StartStream(StreamPtr stream) { 50void AudioOut::StartStream(StreamPtr stream) {
47 stream->Play(); 51 stream->Play();
48} 52}
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index b07588287..6ce08cd0d 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -31,6 +31,9 @@ public:
31 /// Returns a vector of recently released buffers specified by tag for the specified stream 31 /// Returns a vector of recently released buffers specified by tag for the specified stream
32 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count); 32 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
33 33
34 /// Returns a vector of all recently released buffers specified by tag for the specified stream
35 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream);
36
34 /// Starts an audio stream for playback 37 /// Starts an audio stream for playback
35 void StartStream(StreamPtr stream); 38 void StartStream(StreamPtr stream);
36 39
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index a7e851bb8..e1ded84e0 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.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 <limits>
5#include <vector> 6#include <vector>
6 7
7#include "audio_core/audio_out.h" 8#include "audio_core/audio_out.h"
@@ -14,6 +15,59 @@
14#include "core/memory.h" 15#include "core/memory.h"
15#include "core/settings.h" 16#include "core/settings.h"
16 17
18namespace {
19[[nodiscard]] static constexpr s16 ClampToS16(s32 value) {
20 return static_cast<s16>(std::clamp(value, s32{std::numeric_limits<s16>::min()},
21 s32{std::numeric_limits<s16>::max()}));
22}
23
24[[nodiscard]] static constexpr s16 Mix2To1(s16 l_channel, s16 r_channel) {
25 // Mix 50% from left and 50% from right channel
26 constexpr float l_mix_amount = 50.0f / 100.0f;
27 constexpr float r_mix_amount = 50.0f / 100.0f;
28 return ClampToS16(static_cast<s32>((static_cast<float>(l_channel) * l_mix_amount) +
29 (static_cast<float>(r_channel) * r_mix_amount)));
30}
31
32[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel,
33 s16 fc_channel,
34 [[maybe_unused]] s16 lf_channel,
35 s16 bl_channel, s16 br_channel) {
36 // Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
37 // are mixed to be 36.94%
38
39 constexpr float front_mix_amount = 36.94f / 100.0f;
40 constexpr float center_mix_amount = 26.12f / 100.0f;
41 constexpr float back_mix_amount = 36.94f / 100.0f;
42
43 // Mix 50% from left and 50% from right channel
44 const auto left = front_mix_amount * static_cast<float>(fl_channel) +
45 center_mix_amount * static_cast<float>(fc_channel) +
46 back_mix_amount * static_cast<float>(bl_channel);
47
48 const auto right = front_mix_amount * static_cast<float>(fr_channel) +
49 center_mix_amount * static_cast<float>(fc_channel) +
50 back_mix_amount * static_cast<float>(br_channel);
51
52 return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
53}
54
55[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2WithCoefficients(
56 s16 fl_channel, s16 fr_channel, s16 fc_channel, s16 lf_channel, s16 bl_channel, s16 br_channel,
57 const std::array<float_le, 4>& coeff) {
58 const auto left =
59 static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
60 static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0];
61
62 const auto right =
63 static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
64 static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0];
65
66 return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
67}
68
69} // namespace
70
17namespace AudioCore { 71namespace AudioCore {
18AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 72AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
19 AudioCommon::AudioRendererParameter params, 73 AudioCommon::AudioRendererParameter params,
@@ -62,10 +116,6 @@ Stream::State AudioRenderer::GetStreamState() const {
62 return stream->GetState(); 116 return stream->GetState();
63} 117}
64 118
65static constexpr s16 ClampToS16(s32 value) {
66 return static_cast<s16>(std::clamp(value, -32768, 32767));
67}
68
69ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, 119ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
70 std::vector<u8>& output_params) { 120 std::vector<u8>& output_params) {
71 121
@@ -104,8 +154,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
104 } 154 }
105 } 155 }
106 156
107 auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, 157 const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
108 splitter_context, effect_context); 158 splitter_context, effect_context);
109 159
110 if (mix_result.IsError()) { 160 if (mix_result.IsError()) {
111 LOG_ERROR(Audio, "Failed to update mix parameters"); 161 LOG_ERROR(Audio, "Failed to update mix parameters");
@@ -194,20 +244,22 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
194 for (std::size_t i = 0; i < BUFFER_SIZE; i++) { 244 for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
195 if (channel_count == 1) { 245 if (channel_count == 1) {
196 const auto sample = ClampToS16(mix_buffers[0][i]); 246 const auto sample = ClampToS16(mix_buffers[0][i]);
197 buffer[i * stream_channel_count + 0] = sample; 247
198 if (stream_channel_count > 1) { 248 // Place sample in all channels
199 buffer[i * stream_channel_count + 1] = sample; 249 for (u32 channel = 0; channel < stream_channel_count; channel++) {
250 buffer[i * stream_channel_count + channel] = sample;
200 } 251 }
252
201 if (stream_channel_count == 6) { 253 if (stream_channel_count == 6) {
202 buffer[i * stream_channel_count + 2] = sample; 254 // Output stream has a LF channel, mute it!
203 buffer[i * stream_channel_count + 4] = sample; 255 buffer[i * stream_channel_count + 3] = 0;
204 buffer[i * stream_channel_count + 5] = sample;
205 } 256 }
257
206 } else if (channel_count == 2) { 258 } else if (channel_count == 2) {
207 const auto l_sample = ClampToS16(mix_buffers[0][i]); 259 const auto l_sample = ClampToS16(mix_buffers[0][i]);
208 const auto r_sample = ClampToS16(mix_buffers[1][i]); 260 const auto r_sample = ClampToS16(mix_buffers[1][i]);
209 if (stream_channel_count == 1) { 261 if (stream_channel_count == 1) {
210 buffer[i * stream_channel_count + 0] = l_sample; 262 buffer[i * stream_channel_count + 0] = Mix2To1(l_sample, r_sample);
211 } else if (stream_channel_count == 2) { 263 } else if (stream_channel_count == 2) {
212 buffer[i * stream_channel_count + 0] = l_sample; 264 buffer[i * stream_channel_count + 0] = l_sample;
213 buffer[i * stream_channel_count + 1] = r_sample; 265 buffer[i * stream_channel_count + 1] = r_sample;
@@ -215,8 +267,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
215 buffer[i * stream_channel_count + 0] = l_sample; 267 buffer[i * stream_channel_count + 0] = l_sample;
216 buffer[i * stream_channel_count + 1] = r_sample; 268 buffer[i * stream_channel_count + 1] = r_sample;
217 269
218 buffer[i * stream_channel_count + 2] = 270 // Combine both left and right channels to the center channel
219 ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2); 271 buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample);
220 272
221 buffer[i * stream_channel_count + 4] = l_sample; 273 buffer[i * stream_channel_count + 4] = l_sample;
222 buffer[i * stream_channel_count + 5] = r_sample; 274 buffer[i * stream_channel_count + 5] = r_sample;
@@ -231,17 +283,25 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
231 const auto br_sample = ClampToS16(mix_buffers[5][i]); 283 const auto br_sample = ClampToS16(mix_buffers[5][i]);
232 284
233 if (stream_channel_count == 1) { 285 if (stream_channel_count == 1) {
234 buffer[i * stream_channel_count + 0] = fc_sample; 286 // Games seem to ignore the center channel half the time, we use the front left
287 // and right channel for mixing as that's where majority of the audio goes
288 buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
235 } else if (stream_channel_count == 2) { 289 } else if (stream_channel_count == 2) {
236 buffer[i * stream_channel_count + 0] = 290 // Mix all channels into 2 channels
237 static_cast<s16>(0.3694f * static_cast<float>(fl_sample) + 291 if (sink_context.HasDownMixingCoefficients()) {
238 0.2612f * static_cast<float>(fc_sample) + 292 const auto [left, right] = Mix6To2WithCoefficients(
239 0.3694f * static_cast<float>(bl_sample)); 293 fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
240 buffer[i * stream_channel_count + 1] = 294 sink_context.GetDownmixCoefficients());
241 static_cast<s16>(0.3694f * static_cast<float>(fr_sample) + 295 buffer[i * stream_channel_count + 0] = left;
242 0.2612f * static_cast<float>(fc_sample) + 296 buffer[i * stream_channel_count + 1] = right;
243 0.3694f * static_cast<float>(br_sample)); 297 } else {
298 const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample,
299 lf_sample, bl_sample, br_sample);
300 buffer[i * stream_channel_count + 0] = left;
301 buffer[i * stream_channel_count + 1] = right;
302 }
244 } else if (stream_channel_count == 6) { 303 } else if (stream_channel_count == 6) {
304 // Pass through
245 buffer[i * stream_channel_count + 0] = fl_sample; 305 buffer[i * stream_channel_count + 0] = fl_sample;
246 buffer[i * stream_channel_count + 1] = fr_sample; 306 buffer[i * stream_channel_count + 1] = fr_sample;
247 buffer[i * stream_channel_count + 2] = fc_sample; 307 buffer[i * stream_channel_count + 2] = fc_sample;
@@ -259,7 +319,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
259} 319}
260 320
261void AudioRenderer::ReleaseAndQueueBuffers() { 321void AudioRenderer::ReleaseAndQueueBuffers() {
262 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)}; 322 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
263 for (const auto& tag : released_buffers) { 323 for (const auto& tag : released_buffers) {
264 QueueMixedBuffer(tag); 324 QueueMixedBuffer(tag);
265 } 325 }
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 2fd93e058..a85219045 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -36,16 +36,10 @@ class Memory;
36} 36}
37 37
38namespace AudioCore { 38namespace AudioCore {
39using DSPStateHolder = std::array<VoiceState*, 6>; 39using DSPStateHolder = std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>;
40 40
41class AudioOut; 41class AudioOut;
42 42
43struct RendererInfo {
44 u64_le elasped_frame_count{};
45 INSERT_PADDING_WORDS(2);
46};
47static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
48
49class AudioRenderer { 43class AudioRenderer {
50public: 44public:
51 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 45 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
@@ -53,14 +47,14 @@ public:
53 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number); 47 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
54 ~AudioRenderer(); 48 ~AudioRenderer();
55 49
56 ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, 50 [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
57 std::vector<u8>& output_params); 51 std::vector<u8>& output_params);
58 void QueueMixedBuffer(Buffer::Tag tag); 52 void QueueMixedBuffer(Buffer::Tag tag);
59 void ReleaseAndQueueBuffers(); 53 void ReleaseAndQueueBuffers();
60 u32 GetSampleRate() const; 54 [[nodiscard]] u32 GetSampleRate() const;
61 u32 GetSampleCount() const; 55 [[nodiscard]] u32 GetSampleCount() const;
62 u32 GetMixBufferCount() const; 56 [[nodiscard]] u32 GetMixBufferCount() const;
63 Stream::State GetStreamState() const; 57 [[nodiscard]] Stream::State GetStreamState() const;
64 58
65private: 59private:
66 BehaviorInfo behavior_info{}; 60 BehaviorInfo behavior_info{};
diff --git a/src/audio_core/behavior_info.h b/src/audio_core/behavior_info.h
index 512a4ebe3..5a96bf75e 100644
--- a/src/audio_core/behavior_info.h
+++ b/src/audio_core/behavior_info.h
@@ -43,22 +43,22 @@ public:
43 void ClearError(); 43 void ClearError();
44 void UpdateFlags(u64_le dest_flags); 44 void UpdateFlags(u64_le dest_flags);
45 void SetUserRevision(u32_le revision); 45 void SetUserRevision(u32_le revision);
46 u32_le GetUserRevision() const; 46 [[nodiscard]] u32_le GetUserRevision() const;
47 u32_le GetProcessRevision() const; 47 [[nodiscard]] u32_le GetProcessRevision() const;
48 48
49 bool IsAdpcmLoopContextBugFixed() const; 49 [[nodiscard]] bool IsAdpcmLoopContextBugFixed() const;
50 bool IsSplitterSupported() const; 50 [[nodiscard]] bool IsSplitterSupported() const;
51 bool IsLongSizePreDelaySupported() const; 51 [[nodiscard]] bool IsLongSizePreDelaySupported() const;
52 bool IsAudioRendererProcessingTimeLimit80PercentSupported() const; 52 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
53 bool IsAudioRendererProcessingTimeLimit75PercentSupported() const; 53 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
54 bool IsAudioRendererProcessingTimeLimit70PercentSupported() const; 54 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
55 bool IsElapsedFrameCountSupported() const; 55 [[nodiscard]] bool IsElapsedFrameCountSupported() const;
56 bool IsMemoryPoolForceMappingEnabled() const; 56 [[nodiscard]] bool IsMemoryPoolForceMappingEnabled() const;
57 bool IsFlushVoiceWaveBuffersSupported() const; 57 [[nodiscard]] bool IsFlushVoiceWaveBuffersSupported() const;
58 bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const; 58 [[nodiscard]] bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
59 bool IsVoicePitchAndSrcSkippedSupported() const; 59 [[nodiscard]] bool IsVoicePitchAndSrcSkippedSupported() const;
60 bool IsMixInParameterDirtyOnlyUpdateSupported() const; 60 [[nodiscard]] bool IsMixInParameterDirtyOnlyUpdateSupported() const;
61 bool IsSplitterBugFixed() const; 61 [[nodiscard]] bool IsSplitterBugFixed() const;
62 void CopyErrorInfo(OutParams& dst); 62 void CopyErrorInfo(OutParams& dst);
63 63
64private: 64private:
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h
index 53e57748b..87ece00c4 100644
--- a/src/audio_core/command_generator.h
+++ b/src/audio_core/command_generator.h
@@ -39,13 +39,13 @@ public:
39 void PreCommand(); 39 void PreCommand();
40 void PostCommand(); 40 void PostCommand();
41 41
42 s32* GetChannelMixBuffer(s32 channel); 42 [[nodiscard]] s32* GetChannelMixBuffer(s32 channel);
43 const s32* GetChannelMixBuffer(s32 channel) const; 43 [[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const;
44 s32* GetMixBuffer(std::size_t index); 44 [[nodiscard]] s32* GetMixBuffer(std::size_t index);
45 const s32* GetMixBuffer(std::size_t index) const; 45 [[nodiscard]] const s32* GetMixBuffer(std::size_t index) const;
46 std::size_t GetMixChannelBufferOffset(s32 channel) const; 46 [[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
47 47
48 std::size_t GetTotalMixBufferCount() const; 48 [[nodiscard]] std::size_t GetTotalMixBufferCount() const;
49 49
50private: 50private:
51 void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel); 51 void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
@@ -73,7 +73,7 @@ private:
73 void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 73 void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
74 void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 74 void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
75 void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 75 void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
76 ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index); 76 [[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
77 77
78 s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data, 78 s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
79 u32 sample_count, u32 write_offset, u32 write_count); 79 u32 sample_count, u32 write_offset, u32 write_count);
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index 7b4a1e9e8..ec59a3ba9 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -22,7 +22,7 @@ constexpr std::size_t MAX_CHANNEL_COUNT = 6;
22constexpr std::size_t MAX_WAVE_BUFFERS = 4; 22constexpr std::size_t MAX_WAVE_BUFFERS = 4;
23constexpr std::size_t MAX_SAMPLE_HISTORY = 4; 23constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
24constexpr u32 STREAM_SAMPLE_RATE = 48000; 24constexpr u32 STREAM_SAMPLE_RATE = 48000;
25constexpr u32 STREAM_NUM_CHANNELS = 6; 25constexpr u32 STREAM_NUM_CHANNELS = 2;
26constexpr s32 NO_SPLITTER = -1; 26constexpr s32 NO_SPLITTER = -1;
27constexpr s32 NO_MIX = 0x7fffffff; 27constexpr s32 NO_MIX = 0x7fffffff;
28constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min(); 28constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h
index 2c4ce53ef..03c5a0f04 100644
--- a/src/audio_core/effect_context.h
+++ b/src/audio_core/effect_context.h
@@ -189,11 +189,11 @@ public:
189 189
190 virtual void Update(EffectInfo::InParams& in_params) = 0; 190 virtual void Update(EffectInfo::InParams& in_params) = 0;
191 virtual void UpdateForCommandGeneration() = 0; 191 virtual void UpdateForCommandGeneration() = 0;
192 UsageState GetUsage() const; 192 [[nodiscard]] UsageState GetUsage() const;
193 EffectType GetType() const; 193 [[nodiscard]] EffectType GetType() const;
194 bool IsEnabled() const; 194 [[nodiscard]] bool IsEnabled() const;
195 s32 GetMixID() const; 195 [[nodiscard]] s32 GetMixID() const;
196 s32 GetProcessingOrder() const; 196 [[nodiscard]] s32 GetProcessingOrder() const;
197 197
198protected: 198protected:
199 UsageState usage{UsageState::Invalid}; 199 UsageState usage{UsageState::Invalid};
@@ -257,10 +257,10 @@ public:
257 257
258 void Update(EffectInfo::InParams& in_params) override; 258 void Update(EffectInfo::InParams& in_params) override;
259 void UpdateForCommandGeneration() override; 259 void UpdateForCommandGeneration() override;
260 VAddr GetSendInfo() const; 260 [[nodiscard]] VAddr GetSendInfo() const;
261 VAddr GetSendBuffer() const; 261 [[nodiscard]] VAddr GetSendBuffer() const;
262 VAddr GetRecvInfo() const; 262 [[nodiscard]] VAddr GetRecvInfo() const;
263 VAddr GetRecvBuffer() const; 263 [[nodiscard]] VAddr GetRecvBuffer() const;
264 264
265private: 265private:
266 VAddr send_info{}; 266 VAddr send_info{};
@@ -309,10 +309,10 @@ public:
309 explicit EffectContext(std::size_t effect_count); 309 explicit EffectContext(std::size_t effect_count);
310 ~EffectContext(); 310 ~EffectContext();
311 311
312 std::size_t GetCount() const; 312 [[nodiscard]] std::size_t GetCount() const;
313 EffectBase* GetInfo(std::size_t i); 313 [[nodiscard]] EffectBase* GetInfo(std::size_t i);
314 EffectBase* RetargetEffect(std::size_t i, EffectType effect); 314 [[nodiscard]] EffectBase* RetargetEffect(std::size_t i, EffectType effect);
315 const EffectBase* GetInfo(std::size_t i) const; 315 [[nodiscard]] const EffectBase* GetInfo(std::size_t i) const;
316 316
317private: 317private:
318 std::size_t effect_count{}; 318 std::size_t effect_count{};
diff --git a/src/audio_core/mix_context.h b/src/audio_core/mix_context.h
index 6a588eeb4..68bc673c6 100644
--- a/src/audio_core/mix_context.h
+++ b/src/audio_core/mix_context.h
@@ -62,17 +62,17 @@ public:
62 ServerMixInfo(); 62 ServerMixInfo();
63 ~ServerMixInfo(); 63 ~ServerMixInfo();
64 64
65 const ServerMixInfo::InParams& GetInParams() const; 65 [[nodiscard]] const ServerMixInfo::InParams& GetInParams() const;
66 ServerMixInfo::InParams& GetInParams(); 66 [[nodiscard]] ServerMixInfo::InParams& GetInParams();
67 67
68 bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, 68 bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
69 BehaviorInfo& behavior_info, SplitterContext& splitter_context, 69 BehaviorInfo& behavior_info, SplitterContext& splitter_context,
70 EffectContext& effect_context); 70 EffectContext& effect_context);
71 bool HasAnyConnection() const; 71 [[nodiscard]] bool HasAnyConnection() const;
72 void Cleanup(); 72 void Cleanup();
73 void SetEffectCount(std::size_t count); 73 void SetEffectCount(std::size_t count);
74 void ResetEffectProcessingOrder(); 74 void ResetEffectProcessingOrder();
75 s32 GetEffectOrder(std::size_t i) const; 75 [[nodiscard]] s32 GetEffectOrder(std::size_t i) const;
76 76
77private: 77private:
78 std::vector<s32> effect_processing_order; 78 std::vector<s32> effect_processing_order;
@@ -91,15 +91,15 @@ public:
91 void SortInfo(); 91 void SortInfo();
92 bool TsortInfo(SplitterContext& splitter_context); 92 bool TsortInfo(SplitterContext& splitter_context);
93 93
94 std::size_t GetCount() const; 94 [[nodiscard]] std::size_t GetCount() const;
95 ServerMixInfo& GetInfo(std::size_t i); 95 [[nodiscard]] ServerMixInfo& GetInfo(std::size_t i);
96 const ServerMixInfo& GetInfo(std::size_t i) const; 96 [[nodiscard]] const ServerMixInfo& GetInfo(std::size_t i) const;
97 ServerMixInfo& GetSortedInfo(std::size_t i); 97 [[nodiscard]] ServerMixInfo& GetSortedInfo(std::size_t i);
98 const ServerMixInfo& GetSortedInfo(std::size_t i) const; 98 [[nodiscard]] const ServerMixInfo& GetSortedInfo(std::size_t i) const;
99 ServerMixInfo& GetFinalMixInfo(); 99 [[nodiscard]] ServerMixInfo& GetFinalMixInfo();
100 const ServerMixInfo& GetFinalMixInfo() const; 100 [[nodiscard]] const ServerMixInfo& GetFinalMixInfo() const;
101 EdgeMatrix& GetEdgeMatrix(); 101 [[nodiscard]] EdgeMatrix& GetEdgeMatrix();
102 const EdgeMatrix& GetEdgeMatrix() const; 102 [[nodiscard]] const EdgeMatrix& GetEdgeMatrix() const;
103 103
104private: 104private:
105 void CalcMixBufferOffset(); 105 void CalcMixBufferOffset();
diff --git a/src/audio_core/sink_context.cpp b/src/audio_core/sink_context.cpp
index 0882b411a..b29b47890 100644
--- a/src/audio_core/sink_context.cpp
+++ b/src/audio_core/sink_context.cpp
@@ -12,10 +12,16 @@ std::size_t SinkContext::GetCount() const {
12 return sink_count; 12 return sink_count;
13} 13}
14 14
15void SinkContext::UpdateMainSink(SinkInfo::InParams& in) { 15void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
16 ASSERT(in.type == SinkTypes::Device);
17
18 has_downmix_coefs = in.device.down_matrix_enabled;
19 if (has_downmix_coefs) {
20 downmix_coefficients = in.device.down_matrix_coef;
21 }
16 in_use = in.in_use; 22 in_use = in.in_use;
17 use_count = in.device.input_count; 23 use_count = in.device.input_count;
18 std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT); 24 buffers = in.device.input;
19} 25}
20 26
21bool SinkContext::InUse() const { 27bool SinkContext::InUse() const {
@@ -28,4 +34,12 @@ std::vector<u8> SinkContext::OutputBuffers() const {
28 return buffer_ret; 34 return buffer_ret;
29} 35}
30 36
37bool SinkContext::HasDownMixingCoefficients() const {
38 return has_downmix_coefs;
39}
40
41const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
42 return downmix_coefficients;
43}
44
31} // namespace AudioCore 45} // namespace AudioCore
diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h
index d7aa72ba7..e2e7880b7 100644
--- a/src/audio_core/sink_context.h
+++ b/src/audio_core/sink_context.h
@@ -11,6 +11,8 @@
11 11
12namespace AudioCore { 12namespace AudioCore {
13 13
14using DownmixCoefficients = std::array<float_le, 4>;
15
14enum class SinkTypes : u8 { 16enum class SinkTypes : u8 {
15 Invalid = 0, 17 Invalid = 0,
16 Device = 1, 18 Device = 1,
@@ -50,7 +52,7 @@ public:
50 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; 52 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
51 INSERT_UNION_PADDING_BYTES(1); 53 INSERT_UNION_PADDING_BYTES(1);
52 bool down_matrix_enabled; 54 bool down_matrix_enabled;
53 std::array<float_le, 4> down_matrix_coef; 55 DownmixCoefficients down_matrix_coef;
54 }; 56 };
55 static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size"); 57 static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
56 58
@@ -74,16 +76,21 @@ public:
74 explicit SinkContext(std::size_t sink_count); 76 explicit SinkContext(std::size_t sink_count);
75 ~SinkContext(); 77 ~SinkContext();
76 78
77 std::size_t GetCount() const; 79 [[nodiscard]] std::size_t GetCount() const;
80
81 void UpdateMainSink(const SinkInfo::InParams& in);
82 [[nodiscard]] bool InUse() const;
83 [[nodiscard]] std::vector<u8> OutputBuffers() const;
78 84
79 void UpdateMainSink(SinkInfo::InParams& in); 85 [[nodiscard]] bool HasDownMixingCoefficients() const;
80 bool InUse() const; 86 [[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
81 std::vector<u8> OutputBuffers() const;
82 87
83private: 88private:
84 bool in_use{false}; 89 bool in_use{false};
85 s32 use_count{}; 90 s32 use_count{};
86 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{}; 91 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
87 std::size_t sink_count{}; 92 std::size_t sink_count{};
93 bool has_downmix_coefs{false};
94 DownmixCoefficients downmix_coefficients{};
88}; 95};
89} // namespace AudioCore 96} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 4bbb1e0c4..41bc2f4d6 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -136,4 +136,14 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count)
136 return tags; 136 return tags;
137} 137}
138 138
139std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
140 std::vector<Buffer::Tag> tags;
141 tags.reserve(released_buffers.size());
142 while (!released_buffers.empty()) {
143 tags.push_back(released_buffers.front()->GetTag());
144 released_buffers.pop();
145 }
146 return tags;
147}
148
139} // namespace AudioCore 149} // namespace AudioCore
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 6437b8591..71c2d0b4f 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -57,37 +57,40 @@ public:
57 bool QueueBuffer(BufferPtr&& buffer); 57 bool QueueBuffer(BufferPtr&& buffer);
58 58
59 /// Returns true if the audio stream contains a buffer with the specified tag 59 /// Returns true if the audio stream contains a buffer with the specified tag
60 bool ContainsBuffer(Buffer::Tag tag) const; 60 [[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;
61 61
62 /// Returns a vector of recently released buffers specified by tag 62 /// Returns a vector of recently released buffers specified by tag
63 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count); 63 [[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
64
65 /// Returns a vector of all recently released buffers specified by tag
66 [[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers();
64 67
65 void SetVolume(float volume); 68 void SetVolume(float volume);
66 69
67 float GetVolume() const { 70 [[nodiscard]] float GetVolume() const {
68 return game_volume; 71 return game_volume;
69 } 72 }
70 73
71 /// Returns true if the stream is currently playing 74 /// Returns true if the stream is currently playing
72 bool IsPlaying() const { 75 [[nodiscard]] bool IsPlaying() const {
73 return state == State::Playing; 76 return state == State::Playing;
74 } 77 }
75 78
76 /// Returns the number of queued buffers 79 /// Returns the number of queued buffers
77 std::size_t GetQueueSize() const { 80 [[nodiscard]] std::size_t GetQueueSize() const {
78 return queued_buffers.size(); 81 return queued_buffers.size();
79 } 82 }
80 83
81 /// Gets the sample rate 84 /// Gets the sample rate
82 u32 GetSampleRate() const { 85 [[nodiscard]] u32 GetSampleRate() const {
83 return sample_rate; 86 return sample_rate;
84 } 87 }
85 88
86 /// Gets the number of channels 89 /// Gets the number of channels
87 u32 GetNumChannels() const; 90 [[nodiscard]] u32 GetNumChannels() const;
88 91
89 /// Get the state 92 /// Get the state
90 State GetState() const; 93 [[nodiscard]] State GetState() const;
91 94
92private: 95private:
93 /// Plays the next queued buffer in the audio stream, starting playback if necessary 96 /// Plays the next queued buffer in the audio stream, starting playback if necessary
@@ -97,7 +100,7 @@ private:
97 void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {}); 100 void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
98 101
99 /// Gets the number of core cycles when the specified buffer will be released 102 /// Gets the number of core cycles when the specified buffer will be released
100 std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const; 103 [[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
101 104
102 u32 sample_rate; ///< Sample rate of the stream 105 u32 sample_rate; ///< Sample rate of the stream
103 Format format; ///< Format of the stream 106 Format format; ///< Format of the stream
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9253e05b7..7ca3652af 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -145,7 +145,7 @@ struct System::Impl {
145 } 145 }
146 146
147 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 147 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
148 LOG_DEBUG(HW_Memory, "initialized OK"); 148 LOG_DEBUG(Core, "initialized OK");
149 149
150 device_memory = std::make_unique<Core::DeviceMemory>(); 150 device_memory = std::make_unique<Core::DeviceMemory>();
151 151
@@ -187,7 +187,7 @@ struct System::Impl {
187 187
188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
189 189
190 Service::Init(service_manager, system); 190 services = std::make_unique<Service::Services>(service_manager, system);
191 GDBStub::DeferStart(); 191 GDBStub::DeferStart();
192 192
193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); 193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
@@ -208,9 +208,11 @@ struct System::Impl {
208 return ResultStatus::Success; 208 return ResultStatus::Success;
209 } 209 }
210 210
211 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, 211 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
212 const std::string& filepath) { 212 std::size_t program_index) {
213 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath)); 213 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
214 program_index);
215
214 if (!app_loader) { 216 if (!app_loader) {
215 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 217 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
216 return ResultStatus::ErrorGetLoader; 218 return ResultStatus::ErrorGetLoader;
@@ -296,7 +298,7 @@ struct System::Impl {
296 298
297 // Shutdown emulation session 299 // Shutdown emulation session
298 GDBStub::Shutdown(); 300 GDBStub::Shutdown();
299 Service::Shutdown(); 301 services.reset();
300 service_manager.reset(); 302 service_manager.reset();
301 cheat_engine.reset(); 303 cheat_engine.reset();
302 telemetry_session.reset(); 304 telemetry_session.reset();
@@ -306,8 +308,8 @@ struct System::Impl {
306 cpu_manager.Shutdown(); 308 cpu_manager.Shutdown();
307 309
308 // Shutdown kernel and core timing 310 // Shutdown kernel and core timing
309 kernel.Shutdown();
310 core_timing.Shutdown(); 311 core_timing.Shutdown();
312 kernel.Shutdown();
311 313
312 // Close app loader 314 // Close app loader
313 app_loader.reset(); 315 app_loader.reset();
@@ -398,6 +400,9 @@ struct System::Impl {
398 /// Service manager 400 /// Service manager
399 std::shared_ptr<Service::SM::ServiceManager> service_manager; 401 std::shared_ptr<Service::SM::ServiceManager> service_manager;
400 402
403 /// Services
404 std::unique_ptr<Service::Services> services;
405
401 /// Telemetry session for this emulation session 406 /// Telemetry session for this emulation session
402 std::unique_ptr<Core::TelemetrySession> telemetry_session; 407 std::unique_ptr<Core::TelemetrySession> telemetry_session;
403 408
@@ -413,6 +418,8 @@ struct System::Impl {
413 bool is_multicore{}; 418 bool is_multicore{};
414 bool is_async_gpu{}; 419 bool is_async_gpu{};
415 420
421 ExecuteProgramCallback execute_program_callback;
422
416 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; 423 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
417 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; 424 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
418}; 425};
@@ -444,8 +451,13 @@ void System::InvalidateCpuInstructionCaches() {
444 impl->kernel.InvalidateAllInstructionCaches(); 451 impl->kernel.InvalidateAllInstructionCaches();
445} 452}
446 453
447System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 454void System::Shutdown() {
448 return impl->Load(*this, emu_window, filepath); 455 impl->Shutdown();
456}
457
458System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
459 std::size_t program_index) {
460 return impl->Load(*this, emu_window, filepath, program_index);
449} 461}
450 462
451bool System::IsPoweredOn() const { 463bool System::IsPoweredOn() const {
@@ -632,7 +644,11 @@ const std::string& System::GetStatusDetails() const {
632 return impl->status_details; 644 return impl->status_details;
633} 645}
634 646
635Loader::AppLoader& System::GetAppLoader() const { 647Loader::AppLoader& System::GetAppLoader() {
648 return *impl->app_loader;
649}
650
651const Loader::AppLoader& System::GetAppLoader() const {
636 return *impl->app_loader; 652 return *impl->app_loader;
637} 653}
638 654
@@ -748,14 +764,6 @@ const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const {
748 return impl->build_id; 764 return impl->build_id;
749} 765}
750 766
751System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
752 return impl->Init(*this, emu_window);
753}
754
755void System::Shutdown() {
756 impl->Shutdown();
757}
758
759Service::SM::ServiceManager& System::ServiceManager() { 767Service::SM::ServiceManager& System::ServiceManager() {
760 return *impl->service_manager; 768 return *impl->service_manager;
761} 769}
@@ -786,4 +794,16 @@ bool System::IsMulticore() const {
786 return impl->is_multicore; 794 return impl->is_multicore;
787} 795}
788 796
797void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
798 impl->execute_program_callback = std::move(callback);
799}
800
801void System::ExecuteProgram(std::size_t program_index) {
802 if (impl->execute_program_callback) {
803 impl->execute_program_callback(program_index);
804 } else {
805 LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend");
806 }
807}
808
789} // namespace Core 809} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index 6db896bae..f642befc0 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <functional>
8#include <memory> 9#include <memory>
9#include <string> 10#include <string>
10#include <vector> 11#include <vector>
@@ -144,19 +145,19 @@ public:
144 * Run the OS and Application 145 * Run the OS and Application
145 * This function will start emulation and run the relevant devices 146 * This function will start emulation and run the relevant devices
146 */ 147 */
147 ResultStatus Run(); 148 [[nodiscard]] ResultStatus Run();
148 149
149 /** 150 /**
150 * Pause the OS and Application 151 * Pause the OS and Application
151 * This function will pause emulation and stop the relevant devices 152 * This function will pause emulation and stop the relevant devices
152 */ 153 */
153 ResultStatus Pause(); 154 [[nodiscard]] ResultStatus Pause();
154 155
155 /** 156 /**
156 * Step the CPU one instruction 157 * Step the CPU one instruction
157 * @return Result status, indicating whether or not the operation succeeded. 158 * @return Result status, indicating whether or not the operation succeeded.
158 */ 159 */
159 ResultStatus SingleStep(); 160 [[nodiscard]] ResultStatus SingleStep();
160 161
161 /** 162 /**
162 * Invalidate the CPU instruction caches 163 * Invalidate the CPU instruction caches
@@ -173,22 +174,24 @@ public:
173 * @param emu_window Reference to the host-system window used for video output and keyboard 174 * @param emu_window Reference to the host-system window used for video output and keyboard
174 * input. 175 * input.
175 * @param filepath String path to the executable application to load on the host file system. 176 * @param filepath String path to the executable application to load on the host file system.
177 * @param program_index Specifies the index within the container of the program to launch.
176 * @returns ResultStatus code, indicating if the operation succeeded. 178 * @returns ResultStatus code, indicating if the operation succeeded.
177 */ 179 */
178 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath); 180 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
181 std::size_t program_index = 0);
179 182
180 /** 183 /**
181 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an 184 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an
182 * application). 185 * application).
183 * @returns True if the emulated system is powered on, otherwise false. 186 * @returns True if the emulated system is powered on, otherwise false.
184 */ 187 */
185 bool IsPoweredOn() const; 188 [[nodiscard]] bool IsPoweredOn() const;
186 189
187 /// Gets a reference to the telemetry session for this emulation session. 190 /// Gets a reference to the telemetry session for this emulation session.
188 Core::TelemetrySession& TelemetrySession(); 191 [[nodiscard]] Core::TelemetrySession& TelemetrySession();
189 192
190 /// Gets a reference to the telemetry session for this emulation session. 193 /// Gets a reference to the telemetry session for this emulation session.
191 const Core::TelemetrySession& TelemetrySession() const; 194 [[nodiscard]] const Core::TelemetrySession& TelemetrySession() const;
192 195
193 /// Prepare the core emulation for a reschedule 196 /// Prepare the core emulation for a reschedule
194 void PrepareReschedule(); 197 void PrepareReschedule();
@@ -197,185 +200,178 @@ public:
197 void PrepareReschedule(u32 core_index); 200 void PrepareReschedule(u32 core_index);
198 201
199 /// Gets and resets core performance statistics 202 /// Gets and resets core performance statistics
200 PerfStatsResults GetAndResetPerfStats(); 203 [[nodiscard]] PerfStatsResults GetAndResetPerfStats();
201 204
202 /// Gets an ARM interface to the CPU core that is currently running 205 /// Gets an ARM interface to the CPU core that is currently running
203 ARM_Interface& CurrentArmInterface(); 206 [[nodiscard]] ARM_Interface& CurrentArmInterface();
204 207
205 /// Gets an ARM interface to the CPU core that is currently running 208 /// Gets an ARM interface to the CPU core that is currently running
206 const ARM_Interface& CurrentArmInterface() const; 209 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
207 210
208 /// Gets the index of the currently running CPU core 211 /// Gets the index of the currently running CPU core
209 std::size_t CurrentCoreIndex() const; 212 [[nodiscard]] std::size_t CurrentCoreIndex() const;
210 213
211 /// Gets the scheduler for the CPU core that is currently running 214 /// Gets the scheduler for the CPU core that is currently running
212 Kernel::Scheduler& CurrentScheduler(); 215 [[nodiscard]] Kernel::Scheduler& CurrentScheduler();
213 216
214 /// Gets the scheduler for the CPU core that is currently running 217 /// Gets the scheduler for the CPU core that is currently running
215 const Kernel::Scheduler& CurrentScheduler() const; 218 [[nodiscard]] const Kernel::Scheduler& CurrentScheduler() const;
216 219
217 /// Gets the physical core for the CPU core that is currently running 220 /// Gets the physical core for the CPU core that is currently running
218 Kernel::PhysicalCore& CurrentPhysicalCore(); 221 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
219 222
220 /// Gets the physical core for the CPU core that is currently running 223 /// Gets the physical core for the CPU core that is currently running
221 const Kernel::PhysicalCore& CurrentPhysicalCore() const; 224 [[nodiscard]] const Kernel::PhysicalCore& CurrentPhysicalCore() const;
222 225
223 /// Gets a reference to an ARM interface for the CPU core with the specified index 226 /// Gets a reference to an ARM interface for the CPU core with the specified index
224 ARM_Interface& ArmInterface(std::size_t core_index); 227 [[nodiscard]] ARM_Interface& ArmInterface(std::size_t core_index);
225 228
226 /// Gets a const reference to an ARM interface from the CPU core with the specified index 229 /// Gets a const reference to an ARM interface from the CPU core with the specified index
227 const ARM_Interface& ArmInterface(std::size_t core_index) const; 230 [[nodiscard]] const ARM_Interface& ArmInterface(std::size_t core_index) const;
228 231
229 CpuManager& GetCpuManager(); 232 /// Gets a reference to the underlying CPU manager.
233 [[nodiscard]] CpuManager& GetCpuManager();
230 234
231 const CpuManager& GetCpuManager() const; 235 /// Gets a const reference to the underlying CPU manager
236 [[nodiscard]] const CpuManager& GetCpuManager() const;
232 237
233 /// Gets a reference to the exclusive monitor 238 /// Gets a reference to the exclusive monitor
234 ExclusiveMonitor& Monitor(); 239 [[nodiscard]] ExclusiveMonitor& Monitor();
235 240
236 /// Gets a constant reference to the exclusive monitor 241 /// Gets a constant reference to the exclusive monitor
237 const ExclusiveMonitor& Monitor() const; 242 [[nodiscard]] const ExclusiveMonitor& Monitor() const;
238 243
239 /// Gets a mutable reference to the system memory instance. 244 /// Gets a mutable reference to the system memory instance.
240 Core::Memory::Memory& Memory(); 245 [[nodiscard]] Core::Memory::Memory& Memory();
241 246
242 /// Gets a constant reference to the system memory instance. 247 /// Gets a constant reference to the system memory instance.
243 const Core::Memory::Memory& Memory() const; 248 [[nodiscard]] const Core::Memory::Memory& Memory() const;
244 249
245 /// Gets a mutable reference to the GPU interface 250 /// Gets a mutable reference to the GPU interface
246 Tegra::GPU& GPU(); 251 [[nodiscard]] Tegra::GPU& GPU();
247 252
248 /// Gets an immutable reference to the GPU interface. 253 /// Gets an immutable reference to the GPU interface.
249 const Tegra::GPU& GPU() const; 254 [[nodiscard]] const Tegra::GPU& GPU() const;
250 255
251 /// Gets a mutable reference to the renderer. 256 /// Gets a mutable reference to the renderer.
252 VideoCore::RendererBase& Renderer(); 257 [[nodiscard]] VideoCore::RendererBase& Renderer();
253 258
254 /// Gets an immutable reference to the renderer. 259 /// Gets an immutable reference to the renderer.
255 const VideoCore::RendererBase& Renderer() const; 260 [[nodiscard]] const VideoCore::RendererBase& Renderer() const;
256 261
257 /// Gets the scheduler for the CPU core with the specified index 262 /// Gets the scheduler for the CPU core with the specified index
258 Kernel::Scheduler& Scheduler(std::size_t core_index); 263 [[nodiscard]] Kernel::Scheduler& Scheduler(std::size_t core_index);
259 264
260 /// Gets the scheduler for the CPU core with the specified index 265 /// Gets the scheduler for the CPU core with the specified index
261 const Kernel::Scheduler& Scheduler(std::size_t core_index) const; 266 [[nodiscard]] const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
262 267
263 /// Gets the global scheduler 268 /// Gets the global scheduler
264 Kernel::GlobalScheduler& GlobalScheduler(); 269 [[nodiscard]] Kernel::GlobalScheduler& GlobalScheduler();
265 270
266 /// Gets the global scheduler 271 /// Gets the global scheduler
267 const Kernel::GlobalScheduler& GlobalScheduler() const; 272 [[nodiscard]] const Kernel::GlobalScheduler& GlobalScheduler() const;
268 273
269 /// Gets the manager for the guest device memory 274 /// Gets the manager for the guest device memory
270 Core::DeviceMemory& DeviceMemory(); 275 [[nodiscard]] Core::DeviceMemory& DeviceMemory();
271 276
272 /// Gets the manager for the guest device memory 277 /// Gets the manager for the guest device memory
273 const Core::DeviceMemory& DeviceMemory() const; 278 [[nodiscard]] const Core::DeviceMemory& DeviceMemory() const;
274 279
275 /// Provides a pointer to the current process 280 /// Provides a pointer to the current process
276 Kernel::Process* CurrentProcess(); 281 [[nodiscard]] Kernel::Process* CurrentProcess();
277 282
278 /// Provides a constant pointer to the current process. 283 /// Provides a constant pointer to the current process.
279 const Kernel::Process* CurrentProcess() const; 284 [[nodiscard]] const Kernel::Process* CurrentProcess() const;
280 285
281 /// Provides a reference to the core timing instance. 286 /// Provides a reference to the core timing instance.
282 Timing::CoreTiming& CoreTiming(); 287 [[nodiscard]] Timing::CoreTiming& CoreTiming();
283 288
284 /// Provides a constant reference to the core timing instance. 289 /// Provides a constant reference to the core timing instance.
285 const Timing::CoreTiming& CoreTiming() const; 290 [[nodiscard]] const Timing::CoreTiming& CoreTiming() const;
286 291
287 /// Provides a reference to the interrupt manager instance. 292 /// Provides a reference to the interrupt manager instance.
288 Core::Hardware::InterruptManager& InterruptManager(); 293 [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager();
289 294
290 /// Provides a constant reference to the interrupt manager instance. 295 /// Provides a constant reference to the interrupt manager instance.
291 const Core::Hardware::InterruptManager& InterruptManager() const; 296 [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const;
292 297
293 /// Provides a reference to the kernel instance. 298 /// Provides a reference to the kernel instance.
294 Kernel::KernelCore& Kernel(); 299 [[nodiscard]] Kernel::KernelCore& Kernel();
295 300
296 /// Provides a constant reference to the kernel instance. 301 /// Provides a constant reference to the kernel instance.
297 const Kernel::KernelCore& Kernel() const; 302 [[nodiscard]] const Kernel::KernelCore& Kernel() const;
298 303
299 /// Provides a reference to the internal PerfStats instance. 304 /// Provides a reference to the internal PerfStats instance.
300 Core::PerfStats& GetPerfStats(); 305 [[nodiscard]] Core::PerfStats& GetPerfStats();
301 306
302 /// Provides a constant reference to the internal PerfStats instance. 307 /// Provides a constant reference to the internal PerfStats instance.
303 const Core::PerfStats& GetPerfStats() const; 308 [[nodiscard]] const Core::PerfStats& GetPerfStats() const;
304 309
305 /// Provides a reference to the frame limiter; 310 /// Provides a reference to the frame limiter;
306 Core::FrameLimiter& FrameLimiter(); 311 [[nodiscard]] Core::FrameLimiter& FrameLimiter();
307 312
308 /// Provides a constant referent to the frame limiter 313 /// Provides a constant referent to the frame limiter
309 const Core::FrameLimiter& FrameLimiter() const; 314 [[nodiscard]] const Core::FrameLimiter& FrameLimiter() const;
310 315
311 /// Gets the name of the current game 316 /// Gets the name of the current game
312 Loader::ResultStatus GetGameName(std::string& out) const; 317 [[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
313 318
314 void SetStatus(ResultStatus new_status, const char* details); 319 void SetStatus(ResultStatus new_status, const char* details);
315 320
316 const std::string& GetStatusDetails() const; 321 [[nodiscard]] const std::string& GetStatusDetails() const;
317 322
318 Loader::AppLoader& GetAppLoader() const; 323 [[nodiscard]] Loader::AppLoader& GetAppLoader();
324 [[nodiscard]] const Loader::AppLoader& GetAppLoader() const;
319 325
320 Service::SM::ServiceManager& ServiceManager(); 326 [[nodiscard]] Service::SM::ServiceManager& ServiceManager();
321 const Service::SM::ServiceManager& ServiceManager() const; 327 [[nodiscard]] const Service::SM::ServiceManager& ServiceManager() const;
322 328
323 void SetFilesystem(FileSys::VirtualFilesystem vfs); 329 void SetFilesystem(FileSys::VirtualFilesystem vfs);
324 330
325 FileSys::VirtualFilesystem GetFilesystem() const; 331 [[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const;
326 332
327 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, 333 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
328 const std::array<u8, 0x20>& build_id, VAddr main_region_begin, 334 const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
329 u64 main_region_size); 335 u64 main_region_size);
330 336
331 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); 337 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
332
333 void SetDefaultAppletFrontendSet(); 338 void SetDefaultAppletFrontendSet();
334 339
335 Service::AM::Applets::AppletManager& GetAppletManager(); 340 [[nodiscard]] Service::AM::Applets::AppletManager& GetAppletManager();
336 341 [[nodiscard]] const Service::AM::Applets::AppletManager& GetAppletManager() const;
337 const Service::AM::Applets::AppletManager& GetAppletManager() const;
338 342
339 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); 343 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
340 344
341 FileSys::ContentProvider& GetContentProvider(); 345 [[nodiscard]] FileSys::ContentProvider& GetContentProvider();
342 346 [[nodiscard]] const FileSys::ContentProvider& GetContentProvider() const;
343 const FileSys::ContentProvider& GetContentProvider() const;
344 347
345 Service::FileSystem::FileSystemController& GetFileSystemController(); 348 [[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController();
346 349 [[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const;
347 const Service::FileSystem::FileSystemController& GetFileSystemController() const;
348 350
349 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, 351 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
350 FileSys::ContentProvider* provider); 352 FileSys::ContentProvider* provider);
351 353
352 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); 354 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
353 355
354 const Reporter& GetReporter() const; 356 [[nodiscard]] const Reporter& GetReporter() const;
355
356 Service::Glue::ARPManager& GetARPManager();
357 357
358 const Service::Glue::ARPManager& GetARPManager() const; 358 [[nodiscard]] Service::Glue::ARPManager& GetARPManager();
359 [[nodiscard]] const Service::Glue::ARPManager& GetARPManager() const;
359 360
360 Service::APM::Controller& GetAPMController(); 361 [[nodiscard]] Service::APM::Controller& GetAPMController();
362 [[nodiscard]] const Service::APM::Controller& GetAPMController() const;
361 363
362 const Service::APM::Controller& GetAPMController() const; 364 [[nodiscard]] Service::LM::Manager& GetLogManager();
365 [[nodiscard]] const Service::LM::Manager& GetLogManager() const;
363 366
364 Service::LM::Manager& GetLogManager(); 367 [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
365 368 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
366 const Service::LM::Manager& GetLogManager() const;
367
368 Service::Time::TimeManager& GetTimeManager();
369
370 const Service::Time::TimeManager& GetTimeManager() const;
371 369
372 void SetExitLock(bool locked); 370 void SetExitLock(bool locked);
373 371 [[nodiscard]] bool GetExitLock() const;
374 bool GetExitLock() const;
375 372
376 void SetCurrentProcessBuildID(const CurrentBuildProcessID& id); 373 void SetCurrentProcessBuildID(const CurrentBuildProcessID& id);
377 374 [[nodiscard]] const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
378 const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
379 375
380 /// Register a host thread as an emulated CPU Core. 376 /// Register a host thread as an emulated CPU Core.
381 void RegisterCoreThread(std::size_t id); 377 void RegisterCoreThread(std::size_t id);
@@ -390,18 +386,27 @@ public:
390 void ExitDynarmicProfile(); 386 void ExitDynarmicProfile();
391 387
392 /// Tells if system is running on multicore. 388 /// Tells if system is running on multicore.
393 bool IsMulticore() const; 389 [[nodiscard]] bool IsMulticore() const;
394 390
395private: 391 /// Type used for the frontend to designate a callback for System to re-launch the application
396 System(); 392 /// using a specified program index.
393 using ExecuteProgramCallback = std::function<void(std::size_t)>;
397 394
398 /** 395 /**
399 * Initialize the emulated system. 396 * Registers a callback from the frontend for System to re-launch the application using a
400 * @param emu_window Reference to the host-system window used for video output and keyboard 397 * specified program index.
401 * input. 398 * @param callback Callback from the frontend to relaunch the application.
402 * @return ResultStatus code, indicating if the operation succeeded. 399 */
400 void RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback);
401
402 /**
403 * Instructs the frontend to re-launch the application using the specified program_index.
404 * @param program_index Specifies the index within the application of the program to launch.
403 */ 405 */
404 ResultStatus Init(Frontend::EmuWindow& emu_window); 406 void ExecuteProgram(std::size_t program_index);
407
408private:
409 System();
405 410
406 struct Impl; 411 struct Impl;
407 std::unique_ptr<Impl> impl; 412 std::unique_ptr<Impl> impl;
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 983210197..100e90d82 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/fiber.h" 5#include "common/fiber.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "common/scope_exit.h"
7#include "common/thread.h" 8#include "common/thread.h"
8#include "core/arm/exclusive_monitor.h" 9#include "core/arm/exclusive_monitor.h"
9#include "core/core.h" 10#include "core/core.h"
@@ -343,6 +344,16 @@ void CpuManager::RunThread(std::size_t core) {
343 data.initialized = true; 344 data.initialized = true;
344 const bool sc_sync = !is_async_gpu && !is_multicore; 345 const bool sc_sync = !is_async_gpu && !is_multicore;
345 bool sc_sync_first_use = sc_sync; 346 bool sc_sync_first_use = sc_sync;
347
348 // Cleanup
349 SCOPE_EXIT({
350 data.host_context->Exit();
351 data.enter_barrier.reset();
352 data.exit_barrier.reset();
353 data.initialized = false;
354 MicroProfileOnThreadExit();
355 });
356
346 /// Running 357 /// Running
347 while (running_mode) { 358 while (running_mode) {
348 data.is_running = false; 359 data.is_running = false;
@@ -351,6 +362,12 @@ void CpuManager::RunThread(std::size_t core) {
351 system.GPU().ObtainContext(); 362 system.GPU().ObtainContext();
352 sc_sync_first_use = false; 363 sc_sync_first_use = false;
353 } 364 }
365
366 // Abort if emulation was killed before the session really starts
367 if (!system.IsPoweredOn()) {
368 return;
369 }
370
354 auto& scheduler = system.Kernel().CurrentScheduler(); 371 auto& scheduler = system.Kernel().CurrentScheduler();
355 Kernel::Thread* current_thread = scheduler.GetCurrentThread(); 372 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
356 data.is_running = true; 373 data.is_running = true;
@@ -360,13 +377,6 @@ void CpuManager::RunThread(std::size_t core) {
360 data.exit_barrier->Wait(); 377 data.exit_barrier->Wait();
361 data.is_paused = false; 378 data.is_paused = false;
362 } 379 }
363 /// Time to cleanup
364 data.host_context->Exit();
365 data.enter_barrier.reset();
366 data.exit_barrier.reset();
367 data.initialized = false;
368
369 MicroProfileOnThreadExit();
370} 380}
371 381
372} // namespace Core 382} // namespace Core
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 956da68f7..8dee5590b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -29,7 +29,7 @@ constexpr std::array partition_names{
29 "logo", 29 "logo",
30}; 30};
31 31
32XCI::XCI(VirtualFile file_) 32XCI::XCI(VirtualFile file_, std::size_t program_index)
33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, 33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
34 partitions(partition_names.size()), 34 partitions(partition_names.size()),
35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { 35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
@@ -62,7 +62,8 @@ XCI::XCI(VirtualFile file_)
62 } 62 }
63 63
64 secure_partition = std::make_shared<NSP>( 64 secure_partition = std::make_shared<NSP>(
65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)])); 65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
66 program_index);
66 67
67 ncas = secure_partition->GetNCAsCollapsed(); 68 ncas = secure_partition->GetNCAsCollapsed();
68 program = 69 program =
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 2d0a0f285..4960e90fe 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
78 78
79class XCI : public ReadOnlyVfsDirectory { 79class XCI : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit XCI(VirtualFile file); 81 explicit XCI(VirtualFile file, std::size_t program_index = 0);
82 ~XCI() override; 82 ~XCI() override;
83 83
84 Loader::ResultStatus GetStatus() const; 84 Loader::ResultStatus GetStatus() const;
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 90641d23b..c05735ddd 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -20,8 +20,8 @@
20 20
21namespace FileSys { 21namespace FileSys {
22 22
23NSP::NSP(VirtualFile file_) 23NSP::NSP(VirtualFile file_, std::size_t program_index)
24 : file(std::move(file_)), status{Loader::ResultStatus::Success}, 24 : file(std::move(file_)), program_index(program_index), status{Loader::ResultStatus::Success},
25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { 25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
26 if (pfs->GetStatus() != Loader::ResultStatus::Success) { 26 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
27 status = pfs->GetStatus(); 27 status = pfs->GetStatus();
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
146 if (extracted) 146 if (extracted)
147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
148 148
149 const auto title_id_iter = ncas.find(title_id); 149 const auto title_id_iter = ncas.find(title_id + program_index);
150 if (title_id_iter == ncas.end()) 150 if (title_id_iter == ncas.end())
151 return nullptr; 151 return nullptr;
152 152
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index c70a11b5b..54581a6f3 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -27,7 +27,7 @@ enum class ContentRecordType : u8;
27 27
28class NSP : public ReadOnlyVfsDirectory { 28class NSP : public ReadOnlyVfsDirectory {
29public: 29public:
30 explicit NSP(VirtualFile file); 30 explicit NSP(VirtualFile file, std::size_t program_index = 0);
31 ~NSP() override; 31 ~NSP() override;
32 32
33 Loader::ResultStatus GetStatus() const; 33 Loader::ResultStatus GetStatus() const;
@@ -69,6 +69,8 @@ private:
69 69
70 VirtualFile file; 70 VirtualFile file;
71 71
72 const std::size_t program_index;
73
72 bool extracted = false; 74 bool extracted = false;
73 Loader::ResultStatus status; 75 Loader::ResultStatus status;
74 std::map<u64, Loader::ResultStatus> program_status; 76 std::map<u64, Loader::ResultStatus> program_status;
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 3e8780243..276d2b906 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -102,8 +102,8 @@ public:
102 float render_surface_scale = 1.0f; 102 float render_surface_scale = 1.0f;
103 }; 103 };
104 104
105 /// Polls window events 105 /// Called from GPU thread when a frame is displayed.
106 virtual void PollEvents() = 0; 106 virtual void OnFrameDisplayed() {}
107 107
108 /** 108 /**
109 * Returns a GraphicsContext that the frontend provides to be used for rendering. 109 * Returns a GraphicsContext that the frontend provides to be used for rendering.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index bafd1ced7..e3b770d66 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -681,7 +681,7 @@ static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
681} 681}
682 682
683/// Used to output a message on a debug hardware unit - does nothing on a retail unit 683/// Used to output a message on a debug hardware unit - does nothing on a retail unit
684static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { 684static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
685 if (len == 0) { 685 if (len == 0) {
686 return; 686 return;
687 } 687 }
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 63421b963..703a9b234 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -246,9 +246,8 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
246 246
247IDebugFunctions::~IDebugFunctions() = default; 247IDebugFunctions::~IDebugFunctions() = default;
248 248
249ISelfController::ISelfController(Core::System& system, 249ISelfController::ISelfController(Core::System& system, NVFlinger::NVFlinger& nvflinger)
250 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 250 : ServiceFramework("ISelfController"), system(system), nvflinger(nvflinger) {
251 : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
252 // clang-format off 251 // clang-format off
253 static const FunctionInfo functions[] = { 252 static const FunctionInfo functions[] = {
254 {0, &ISelfController::Exit, "Exit"}, 253 {0, &ISelfController::Exit, "Exit"},
@@ -458,8 +457,8 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
458 457
459 // TODO(Subv): Find out how AM determines the display to use, for now just 458 // TODO(Subv): Find out how AM determines the display to use, for now just
460 // create the layer in the Default display. 459 // create the layer in the Default display.
461 const auto display_id = nvflinger->OpenDisplay("Default"); 460 const auto display_id = nvflinger.OpenDisplay("Default");
462 const auto layer_id = nvflinger->CreateLayer(*display_id); 461 const auto layer_id = nvflinger.CreateLayer(*display_id);
463 462
464 IPC::ResponseBuilder rb{ctx, 4}; 463 IPC::ResponseBuilder rb{ctx, 4};
465 rb.Push(RESULT_SUCCESS); 464 rb.Push(RESULT_SUCCESS);
@@ -476,8 +475,8 @@ void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestConte
476 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse 475 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
477 // side effects. 476 // side effects.
478 // TODO: Support multiple layers 477 // TODO: Support multiple layers
479 const auto display_id = nvflinger->OpenDisplay("Default"); 478 const auto display_id = nvflinger.OpenDisplay("Default");
480 const auto layer_id = nvflinger->CreateLayer(*display_id); 479 const auto layer_id = nvflinger.CreateLayer(*display_id);
481 480
482 IPC::ResponseBuilder rb{ctx, 4}; 481 IPC::ResponseBuilder rb{ctx, 4};
483 rb.Push(RESULT_SUCCESS); 482 rb.Push(RESULT_SUCCESS);
@@ -1189,9 +1188,9 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1189 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"}, 1188 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
1190 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"}, 1189 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
1191 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"}, 1190 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
1192 {120, nullptr, "ExecuteProgram"}, 1191 {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
1193 {121, nullptr, "ClearUserChannel"}, 1192 {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
1194 {122, nullptr, "UnpopToUserChannel"}, 1193 {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
1195 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, 1194 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
1196 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, 1195 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
1197 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, 1196 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
@@ -1562,6 +1561,34 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
1562 rb.Push<u32>(0); 1561 rb.Push<u32>(0);
1563} 1562}
1564 1563
1564void IApplicationFunctions::ExecuteProgram(Kernel::HLERequestContext& ctx) {
1565 LOG_WARNING(Service_AM, "(STUBBED) called");
1566
1567 IPC::RequestParser rp{ctx};
1568 [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
1569 [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
1570 const auto program_index = rp.Pop<u64>();
1571
1572 IPC::ResponseBuilder rb{ctx, 2};
1573 rb.Push(RESULT_SUCCESS);
1574
1575 system.ExecuteProgram(program_index);
1576}
1577
1578void IApplicationFunctions::ClearUserChannel(Kernel::HLERequestContext& ctx) {
1579 LOG_WARNING(Service_AM, "(STUBBED) called");
1580
1581 IPC::ResponseBuilder rb{ctx, 2};
1582 rb.Push(RESULT_SUCCESS);
1583}
1584
1585void IApplicationFunctions::UnpopToUserChannel(Kernel::HLERequestContext& ctx) {
1586 LOG_WARNING(Service_AM, "(STUBBED) called");
1587
1588 IPC::ResponseBuilder rb{ctx, 2};
1589 rb.Push(RESULT_SUCCESS);
1590}
1591
1565void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) { 1592void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
1566 LOG_WARNING(Service_AM, "(STUBBED) called"); 1593 LOG_WARNING(Service_AM, "(STUBBED) called");
1567 1594
@@ -1586,8 +1613,8 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe
1586 rb.PushCopyObjects(friend_invitation_storage_channel_event.readable); 1613 rb.PushCopyObjects(friend_invitation_storage_channel_event.readable);
1587} 1614}
1588 1615
1589void InstallInterfaces(SM::ServiceManager& service_manager, 1616void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
1590 std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) { 1617 Core::System& system) {
1591 auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); 1618 auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel());
1592 // Needed on game boot 1619 // Needed on game boot
1593 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); 1620 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index bcc06affe..af97c303a 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -121,8 +121,7 @@ public:
121 121
122class ISelfController final : public ServiceFramework<ISelfController> { 122class ISelfController final : public ServiceFramework<ISelfController> {
123public: 123public:
124 explicit ISelfController(Core::System& system_, 124 explicit ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_);
125 std::shared_ptr<NVFlinger::NVFlinger> nvflinger_);
126 ~ISelfController() override; 125 ~ISelfController() override;
127 126
128private: 127private:
@@ -156,7 +155,7 @@ private:
156 }; 155 };
157 156
158 Core::System& system; 157 Core::System& system;
159 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 158 NVFlinger::NVFlinger& nvflinger;
160 Kernel::EventPair launchable_event; 159 Kernel::EventPair launchable_event;
161 Kernel::EventPair accumulated_suspended_tick_changed_event; 160 Kernel::EventPair accumulated_suspended_tick_changed_event;
162 161
@@ -288,6 +287,9 @@ private:
288 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); 287 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
289 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); 288 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
290 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); 289 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
290 void ExecuteProgram(Kernel::HLERequestContext& ctx);
291 void ClearUserChannel(Kernel::HLERequestContext& ctx);
292 void UnpopToUserChannel(Kernel::HLERequestContext& ctx);
291 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx); 293 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
292 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 294 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
293 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); 295 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
@@ -332,7 +334,7 @@ public:
332}; 334};
333 335
334/// Registers all AM services with the specified service manager. 336/// Registers all AM services with the specified service manager.
335void InstallInterfaces(SM::ServiceManager& service_manager, 337void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
336 std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system); 338 Core::System& system);
337 339
338} // namespace Service::AM 340} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index be23ca747..7de506b70 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -13,10 +13,10 @@ namespace Service::AM {
13 13
14class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { 14class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
15public: 15public:
16 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 16 explicit ILibraryAppletProxy(NVFlinger::NVFlinger& nvflinger,
17 std::shared_ptr<AppletMessageQueue> msg_queue, 17 std::shared_ptr<AppletMessageQueue> msg_queue,
18 Core::System& system) 18 Core::System& system)
19 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), 19 : ServiceFramework("ILibraryAppletProxy"), nvflinger(nvflinger),
20 msg_queue(std::move(msg_queue)), system(system) { 20 msg_queue(std::move(msg_queue)), system(system) {
21 // clang-format off 21 // clang-format off
22 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
@@ -109,16 +109,16 @@ private:
109 rb.PushIpcInterface<IApplicationFunctions>(system); 109 rb.PushIpcInterface<IApplicationFunctions>(system);
110 } 110 }
111 111
112 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 112 NVFlinger::NVFlinger& nvflinger;
113 std::shared_ptr<AppletMessageQueue> msg_queue; 113 std::shared_ptr<AppletMessageQueue> msg_queue;
114 Core::System& system; 114 Core::System& system;
115}; 115};
116 116
117class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { 117class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
118public: 118public:
119 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 119 explicit ISystemAppletProxy(NVFlinger::NVFlinger& nvflinger,
120 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 120 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
121 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), 121 : ServiceFramework("ISystemAppletProxy"), nvflinger(nvflinger),
122 msg_queue(std::move(msg_queue)), system(system) { 122 msg_queue(std::move(msg_queue)), system(system) {
123 // clang-format off 123 // clang-format off
124 static const FunctionInfo functions[] = { 124 static const FunctionInfo functions[] = {
@@ -220,7 +220,8 @@ private:
220 rb.Push(RESULT_SUCCESS); 220 rb.Push(RESULT_SUCCESS);
221 rb.PushIpcInterface<IApplicationCreator>(); 221 rb.PushIpcInterface<IApplicationCreator>();
222 } 222 }
223 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 223
224 NVFlinger::NVFlinger& nvflinger;
224 std::shared_ptr<AppletMessageQueue> msg_queue; 225 std::shared_ptr<AppletMessageQueue> msg_queue;
225 Core::System& system; 226 Core::System& system;
226}; 227};
@@ -249,10 +250,10 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
249 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system); 250 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
250} 251}
251 252
252AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 253AppletAE::AppletAE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue,
253 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 254 Core::System& system)
254 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)), 255 : ServiceFramework("appletAE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)),
255 msg_queue(std::move(msg_queue)), system(system) { 256 system(system) {
256 // clang-format off 257 // clang-format off
257 static const FunctionInfo functions[] = { 258 static const FunctionInfo functions[] = {
258 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, 259 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 2e3e45915..761844a1f 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -23,7 +23,7 @@ class AppletMessageQueue;
23 23
24class AppletAE final : public ServiceFramework<AppletAE> { 24class AppletAE final : public ServiceFramework<AppletAE> {
25public: 25public:
26 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletAE(NVFlinger::NVFlinger& nvflinger,
27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); 27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
28 ~AppletAE() override; 28 ~AppletAE() override;
29 29
@@ -34,7 +34,7 @@ private:
34 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx); 34 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
35 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); 35 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
36 36
37 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 37 NVFlinger::NVFlinger& nvflinger;
38 std::shared_ptr<AppletMessageQueue> msg_queue; 38 std::shared_ptr<AppletMessageQueue> msg_queue;
39 Core::System& system; 39 Core::System& system;
40}; 40};
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index a2ffaa440..7bed86ec4 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -12,9 +12,9 @@ namespace Service::AM {
12 12
13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { 13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
14public: 14public:
15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 15 explicit IApplicationProxy(NVFlinger::NVFlinger& nvflinger,
16 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 16 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
17 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)), 17 : ServiceFramework("IApplicationProxy"), nvflinger(nvflinger),
18 msg_queue(std::move(msg_queue)), system(system) { 18 msg_queue(std::move(msg_queue)), system(system) {
19 // clang-format off 19 // clang-format off
20 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
@@ -98,7 +98,7 @@ private:
98 rb.PushIpcInterface<IApplicationFunctions>(system); 98 rb.PushIpcInterface<IApplicationFunctions>(system);
99 } 99 }
100 100
101 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 101 NVFlinger::NVFlinger& nvflinger;
102 std::shared_ptr<AppletMessageQueue> msg_queue; 102 std::shared_ptr<AppletMessageQueue> msg_queue;
103 Core::System& system; 103 Core::System& system;
104}; 104};
@@ -111,10 +111,10 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
111 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system); 111 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system);
112} 112}
113 113
114AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 114AppletOE::AppletOE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue,
115 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 115 Core::System& system)
116 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)), 116 : ServiceFramework("appletOE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)),
117 msg_queue(std::move(msg_queue)), system(system) { 117 system(system) {
118 static const FunctionInfo functions[] = { 118 static const FunctionInfo functions[] = {
119 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, 119 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
120 }; 120 };
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 758da792d..88906d354 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -23,7 +23,7 @@ class AppletMessageQueue;
23 23
24class AppletOE final : public ServiceFramework<AppletOE> { 24class AppletOE final : public ServiceFramework<AppletOE> {
25public: 25public:
26 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletOE(NVFlinger::NVFlinger& nvflinger,
27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); 27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
28 ~AppletOE() override; 28 ~AppletOE() override;
29 29
@@ -32,7 +32,7 @@ public:
32private: 32private:
33 void OpenApplicationProxy(Kernel::HLERequestContext& ctx); 33 void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
34 34
35 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 35 NVFlinger::NVFlinger& nvflinger;
36 std::shared_ptr<AppletMessageQueue> msg_queue; 36 std::shared_ptr<AppletMessageQueue> msg_queue;
37 Core::System& system; 37 Core::System& system;
38}; 38};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index fbfda2d5b..fb4979af2 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -188,17 +188,19 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
188 return RESULT_SUCCESS; 188 return RESULT_SUCCESS;
189} 189}
190 190
191/// Initialize ServiceManager 191/// Initialize Services
192void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { 192Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
193 : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} {
194
193 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 195 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
194 // here and pass it into the respective InstallInterfaces functions. 196 // here and pass it into the respective InstallInterfaces functions.
195 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system); 197
196 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); 198 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
197 199
198 SM::ServiceManager::InstallInterfaces(sm, system.Kernel()); 200 SM::ServiceManager::InstallInterfaces(sm, system.Kernel());
199 201
200 Account::InstallInterfaces(system); 202 Account::InstallInterfaces(system);
201 AM::InstallInterfaces(*sm, nv_flinger, system); 203 AM::InstallInterfaces(*sm, *nv_flinger, system);
202 AOC::InstallInterfaces(*sm, system); 204 AOC::InstallInterfaces(*sm, system);
203 APM::InstallInterfaces(system); 205 APM::InstallInterfaces(system);
204 Audio::InstallInterfaces(*sm, system); 206 Audio::InstallInterfaces(*sm, system);
@@ -246,14 +248,10 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
246 SSL::InstallInterfaces(*sm); 248 SSL::InstallInterfaces(*sm);
247 Time::InstallInterfaces(system); 249 Time::InstallInterfaces(system);
248 USB::InstallInterfaces(*sm); 250 USB::InstallInterfaces(*sm);
249 VI::InstallInterfaces(*sm, nv_flinger); 251 VI::InstallInterfaces(*sm, *nv_flinger);
250 WLAN::InstallInterfaces(*sm); 252 WLAN::InstallInterfaces(*sm);
251
252 LOG_DEBUG(Service, "initialized OK");
253} 253}
254 254
255/// Shutdown ServiceManager 255Services::~Services() = default;
256void Shutdown() { 256
257 LOG_DEBUG(Service, "shutdown OK");
258}
259} // namespace Service 257} // namespace Service
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index a01ef3353..ed4792289 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -29,7 +29,11 @@ namespace Service {
29 29
30namespace FileSystem { 30namespace FileSystem {
31class FileSystemController; 31class FileSystemController;
32} // namespace FileSystem 32}
33
34namespace NVFlinger {
35class NVFlinger;
36}
33 37
34namespace SM { 38namespace SM {
35class ServiceManager; 39class ServiceManager;
@@ -181,10 +185,17 @@ private:
181 } 185 }
182}; 186};
183 187
184/// Initialize ServiceManager 188/**
185void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); 189 * The purpose of this class is to own any objects that need to be shared across the other service
190 * implementations. Will be torn down when the global system instance is shutdown.
191 */
192class Services final {
193public:
194 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
195 ~Services();
186 196
187/// Shutdown ServiceManager 197private:
188void Shutdown(); 198 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
199};
189 200
190} // namespace Service 201} // namespace Service
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 55e00dd93..86bd604f4 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -492,8 +492,8 @@ private:
492 492
493class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 493class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
494public: 494public:
495 explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 495 explicit IHOSBinderDriver(NVFlinger::NVFlinger& nv_flinger)
496 : ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) { 496 : ServiceFramework("IHOSBinderDriver"), nv_flinger(nv_flinger) {
497 static const FunctionInfo functions[] = { 497 static const FunctionInfo functions[] = {
498 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 498 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
499 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, 499 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -530,8 +530,8 @@ private:
530 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 530 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
531 static_cast<u32>(transaction), flags); 531 static_cast<u32>(transaction), flags);
532 532
533 const auto guard = nv_flinger->Lock(); 533 const auto guard = nv_flinger.Lock();
534 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 534 auto& buffer_queue = nv_flinger.FindBufferQueue(id);
535 535
536 switch (transaction) { 536 switch (transaction) {
537 case TransactionId::Connect: { 537 case TransactionId::Connect: {
@@ -570,8 +570,8 @@ private:
570 [=, this](std::shared_ptr<Kernel::Thread> thread, 570 [=, this](std::shared_ptr<Kernel::Thread> thread,
571 Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { 571 Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
572 // Repeat TransactParcel DequeueBuffer when a buffer is available 572 // Repeat TransactParcel DequeueBuffer when a buffer is available
573 const auto guard = nv_flinger->Lock(); 573 const auto guard = nv_flinger.Lock();
574 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 574 auto& buffer_queue = nv_flinger.FindBufferQueue(id);
575 auto result = buffer_queue.DequeueBuffer(width, height); 575 auto result = buffer_queue.DequeueBuffer(width, height);
576 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); 576 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
577 577
@@ -676,7 +676,7 @@ private:
676 676
677 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 677 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
678 678
679 const auto& buffer_queue = nv_flinger->FindBufferQueue(id); 679 const auto& buffer_queue = nv_flinger.FindBufferQueue(id);
680 680
681 // TODO(Subv): Find out what this actually is. 681 // TODO(Subv): Find out what this actually is.
682 IPC::ResponseBuilder rb{ctx, 2, 1}; 682 IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -684,8 +684,8 @@ private:
684 rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent()); 684 rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent());
685 } 685 }
686 686
687 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 687 NVFlinger::NVFlinger& nv_flinger;
688}; // namespace VI 688};
689 689
690class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 690class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
691public: 691public:
@@ -790,8 +790,8 @@ private:
790 790
791class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { 791class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
792public: 792public:
793 explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 793 explicit IManagerDisplayService(NVFlinger::NVFlinger& nv_flinger)
794 : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) { 794 : ServiceFramework("IManagerDisplayService"), nv_flinger(nv_flinger) {
795 // clang-format off 795 // clang-format off
796 static const FunctionInfo functions[] = { 796 static const FunctionInfo functions[] = {
797 {200, nullptr, "AllocateProcessHeapBlock"}, 797 {200, nullptr, "AllocateProcessHeapBlock"},
@@ -893,7 +893,7 @@ private:
893 "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", 893 "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
894 unknown, display, aruid); 894 unknown, display, aruid);
895 895
896 const auto layer_id = nv_flinger->CreateLayer(display); 896 const auto layer_id = nv_flinger.CreateLayer(display);
897 if (!layer_id) { 897 if (!layer_id) {
898 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); 898 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
899 IPC::ResponseBuilder rb{ctx, 2}; 899 IPC::ResponseBuilder rb{ctx, 2};
@@ -930,12 +930,12 @@ private:
930 rb.Push(RESULT_SUCCESS); 930 rb.Push(RESULT_SUCCESS);
931 } 931 }
932 932
933 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 933 NVFlinger::NVFlinger& nv_flinger;
934}; 934};
935 935
936class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 936class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
937public: 937public:
938 explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 938 explicit IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger);
939 939
940private: 940private:
941 enum class ConvertedScaleMode : u64 { 941 enum class ConvertedScaleMode : u64 {
@@ -1010,7 +1010,7 @@ private:
1010 1010
1011 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); 1011 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
1012 1012
1013 const auto display_id = nv_flinger->OpenDisplay(name); 1013 const auto display_id = nv_flinger.OpenDisplay(name);
1014 if (!display_id) { 1014 if (!display_id) {
1015 LOG_ERROR(Service_VI, "Display not found! display_name={}", name); 1015 LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
1016 IPC::ResponseBuilder rb{ctx, 2}; 1016 IPC::ResponseBuilder rb{ctx, 2};
@@ -1110,7 +1110,7 @@ private:
1110 1110
1111 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); 1111 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
1112 1112
1113 const auto display_id = nv_flinger->OpenDisplay(display_name); 1113 const auto display_id = nv_flinger.OpenDisplay(display_name);
1114 if (!display_id) { 1114 if (!display_id) {
1115 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); 1115 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
1116 IPC::ResponseBuilder rb{ctx, 2}; 1116 IPC::ResponseBuilder rb{ctx, 2};
@@ -1118,7 +1118,7 @@ private:
1118 return; 1118 return;
1119 } 1119 }
1120 1120
1121 const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id); 1121 const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, layer_id);
1122 if (!buffer_queue_id) { 1122 if (!buffer_queue_id) {
1123 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); 1123 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
1124 IPC::ResponseBuilder rb{ctx, 2}; 1124 IPC::ResponseBuilder rb{ctx, 2};
@@ -1138,7 +1138,7 @@ private:
1138 1138
1139 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); 1139 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
1140 1140
1141 nv_flinger->CloseLayer(layer_id); 1141 nv_flinger.CloseLayer(layer_id);
1142 1142
1143 IPC::ResponseBuilder rb{ctx, 2}; 1143 IPC::ResponseBuilder rb{ctx, 2};
1144 rb.Push(RESULT_SUCCESS); 1144 rb.Push(RESULT_SUCCESS);
@@ -1154,7 +1154,7 @@ private:
1154 1154
1155 // TODO(Subv): What's the difference between a Stray and a Managed layer? 1155 // TODO(Subv): What's the difference between a Stray and a Managed layer?
1156 1156
1157 const auto layer_id = nv_flinger->CreateLayer(display_id); 1157 const auto layer_id = nv_flinger.CreateLayer(display_id);
1158 if (!layer_id) { 1158 if (!layer_id) {
1159 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); 1159 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id);
1160 IPC::ResponseBuilder rb{ctx, 2}; 1160 IPC::ResponseBuilder rb{ctx, 2};
@@ -1162,7 +1162,7 @@ private:
1162 return; 1162 return;
1163 } 1163 }
1164 1164
1165 const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id); 1165 const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *layer_id);
1166 if (!buffer_queue_id) { 1166 if (!buffer_queue_id) {
1167 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); 1167 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
1168 IPC::ResponseBuilder rb{ctx, 2}; 1168 IPC::ResponseBuilder rb{ctx, 2};
@@ -1193,7 +1193,7 @@ private:
1193 1193
1194 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); 1194 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
1195 1195
1196 const auto vsync_event = nv_flinger->FindVsyncEvent(display_id); 1196 const auto vsync_event = nv_flinger.FindVsyncEvent(display_id);
1197 if (!vsync_event) { 1197 if (!vsync_event) {
1198 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); 1198 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
1199 IPC::ResponseBuilder rb{ctx, 2}; 1199 IPC::ResponseBuilder rb{ctx, 2};
@@ -1258,12 +1258,11 @@ private:
1258 } 1258 }
1259 } 1259 }
1260 1260
1261 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 1261 NVFlinger::NVFlinger& nv_flinger;
1262}; 1262};
1263 1263
1264IApplicationDisplayService::IApplicationDisplayService( 1264IApplicationDisplayService::IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger)
1265 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 1265 : ServiceFramework("IApplicationDisplayService"), nv_flinger(nv_flinger) {
1266 : ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) {
1267 static const FunctionInfo functions[] = { 1266 static const FunctionInfo functions[] = {
1268 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, 1267 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
1269 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, 1268 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
@@ -1304,8 +1303,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
1304 return false; 1303 return false;
1305} 1304}
1306 1305
1307void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, 1306void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger,
1308 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
1309 Permission permission) { 1307 Permission permission) {
1310 IPC::RequestParser rp{ctx}; 1308 IPC::RequestParser rp{ctx};
1311 const auto policy = rp.PopEnum<Policy>(); 1309 const auto policy = rp.PopEnum<Policy>();
@@ -1319,11 +1317,10 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
1319 1317
1320 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1318 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1321 rb.Push(RESULT_SUCCESS); 1319 rb.Push(RESULT_SUCCESS);
1322 rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger)); 1320 rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
1323} 1321}
1324 1322
1325void InstallInterfaces(SM::ServiceManager& service_manager, 1323void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger) {
1326 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
1327 std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager); 1324 std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
1328 std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager); 1325 std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
1329 std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager); 1326 std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 6b66f8b81..5229fa753 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -43,12 +43,11 @@ enum class Policy {
43}; 43};
44 44
45namespace detail { 45namespace detail {
46void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, 46void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger,
47 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission); 47 Permission permission);
48} // namespace detail 48} // namespace detail
49 49
50/// Registers all VI services with the specified service manager. 50/// Registers all VI services with the specified service manager.
51void InstallInterfaces(SM::ServiceManager& service_manager, 51void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger);
52 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
53 52
54} // namespace Service::VI 53} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 06070087f..41da3ee93 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_M::VI_M(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:m"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 13 {2, &VI_M::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 290e06689..ee2489874 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_M final : public ServiceFramework<VI_M> { 19class VI_M final : public ServiceFramework<VI_M> {
20public: 20public:
21 explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_M(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_M() override; 22 ~VI_M() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 57c596cc4..6acb51e2a 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_S::VI_S(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:s"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 13 {1, &VI_S::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index 47804dc0b..6790673ab 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_S final : public ServiceFramework<VI_S> { 19class VI_S final : public ServiceFramework<VI_S> {
20public: 20public:
21 explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_S(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_S() override; 22 ~VI_S() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index 6b7329345..44e00a4f6 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_U::VI_U(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:u"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 13 {0, &VI_U::GetDisplayService, "GetDisplayService"},
15 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index 19bdb73b0..b59f986f0 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_U final : public ServiceFramework<VI_U> { 19class VI_U final : public ServiceFramework<VI_U> {
20public: 20public:
21 explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_U(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_U() override; 22 ~VI_U() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index deffe7379..d91c15561 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -198,10 +198,11 @@ AppLoader::~AppLoader() = default;
198 * @param system The system context to use. 198 * @param system The system context to use.
199 * @param file The file to retrieve the loader for 199 * @param file The file to retrieve the loader for
200 * @param type The file type 200 * @param type The file type
201 * @param program_index Specifies the index within the container of the program to launch.
201 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type 202 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
202 */ 203 */
203static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file, 204static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
204 FileType type) { 205 FileType type, std::size_t program_index) {
205 switch (type) { 206 switch (type) {
206 // Standard ELF file format. 207 // Standard ELF file format.
207 case FileType::ELF: 208 case FileType::ELF:
@@ -222,7 +223,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
222 // NX XCI (nX Card Image) file format. 223 // NX XCI (nX Card Image) file format.
223 case FileType::XCI: 224 case FileType::XCI:
224 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(), 225 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
225 system.GetContentProvider()); 226 system.GetContentProvider(), program_index);
226 227
227 // NX NAX (NintendoAesXts) file format. 228 // NX NAX (NintendoAesXts) file format.
228 case FileType::NAX: 229 case FileType::NAX:
@@ -231,7 +232,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
231 // NX NSP (Nintendo Submission Package) file format 232 // NX NSP (Nintendo Submission Package) file format
232 case FileType::NSP: 233 case FileType::NSP:
233 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(), 234 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
234 system.GetContentProvider()); 235 system.GetContentProvider(), program_index);
235 236
236 // NX KIP (Kernel Internal Process) file format 237 // NX KIP (Kernel Internal Process) file format
237 case FileType::KIP: 238 case FileType::KIP:
@@ -246,7 +247,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
246 } 247 }
247} 248}
248 249
249std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file) { 250std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
251 std::size_t program_index) {
250 FileType type = IdentifyFile(file); 252 FileType type = IdentifyFile(file);
251 const FileType filename_type = GuessFromFilename(file->GetName()); 253 const FileType filename_type = GuessFromFilename(file->GetName());
252 254
@@ -260,7 +262,7 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile
260 262
261 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); 263 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
262 264
263 return GetFileLoader(system, std::move(file), type); 265 return GetFileLoader(system, std::move(file), type, program_index);
264} 266}
265 267
266} // namespace Loader 268} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 8dc2d7615..36e79e71d 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -293,9 +293,11 @@ protected:
293 * 293 *
294 * @param system The system context. 294 * @param system The system context.
295 * @param file The bootable file. 295 * @param file The bootable file.
296 * @param program_index Specifies the index within the container of the program to launch.
296 * 297 *
297 * @return the best loader for this file. 298 * @return the best loader for this file.
298 */ 299 */
299std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file); 300std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
301 std::size_t program_index = 0);
300 302
301} // namespace Loader 303} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index e821937fd..928f64c8c 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -23,8 +23,9 @@ namespace Loader {
23 23
24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file, 24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file,
25 const Service::FileSystem::FileSystemController& fsc, 25 const Service::FileSystem::FileSystemController& fsc,
26 const FileSys::ContentProvider& content_provider) 26 const FileSys::ContentProvider& content_provider,
27 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)), 27 std::size_t program_index)
28 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file, program_index)),
28 title_id(nsp->GetProgramTitleID()) { 29 title_id(nsp->GetProgramTitleID()) {
29 30
30 if (nsp->GetStatus() != ResultStatus::Success) { 31 if (nsp->GetStatus() != ResultStatus::Success) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 36e8e3533..f0518ac47 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -28,7 +28,8 @@ class AppLoader_NSP final : public AppLoader {
28public: 28public:
29 explicit AppLoader_NSP(FileSys::VirtualFile file, 29 explicit AppLoader_NSP(FileSys::VirtualFile file,
30 const Service::FileSystem::FileSystemController& fsc, 30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider); 31 const FileSys::ContentProvider& content_provider,
32 std::size_t program_index);
32 ~AppLoader_NSP() override; 33 ~AppLoader_NSP() override;
33 34
34 /** 35 /**
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 536e721fc..aaa250cea 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -22,8 +22,9 @@ namespace Loader {
22 22
23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file, 23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file,
24 const Service::FileSystem::FileSystemController& fsc, 24 const Service::FileSystem::FileSystemController& fsc,
25 const FileSys::ContentProvider& content_provider) 25 const FileSys::ContentProvider& content_provider,
26 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), 26 std::size_t program_index)
27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file, program_index)),
27 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { 28 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
28 if (xci->GetStatus() != ResultStatus::Success) { 29 if (xci->GetStatus() != ResultStatus::Success) {
29 return; 30 return;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 6dc1f9243..764dc8328 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -28,7 +28,8 @@ class AppLoader_XCI final : public AppLoader {
28public: 28public:
29 explicit AppLoader_XCI(FileSys::VirtualFile file, 29 explicit AppLoader_XCI(FileSys::VirtualFile file,
30 const Service::FileSystem::FileSystemController& fsc, 30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider); 31 const FileSys::ContentProvider& content_provider,
32 std::size_t program_index);
32 ~AppLoader_XCI() override; 33 ~AppLoader_XCI() override;
33 34
34 /** 35 /**
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 1d1b2e08a..5682e5ca5 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -56,8 +56,8 @@ else()
56 -Werror=reorder 56 -Werror=reorder
57 -Werror=shadow 57 -Werror=shadow
58 -Werror=sign-compare 58 -Werror=sign-compare
59 -Werror=unused-but-set-parameter 59 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
60 -Werror=unused-but-set-variable 60 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
61 -Werror=unused-variable 61 -Werror=unused-variable
62 ) 62 )
63endif() 63endif()
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
index 74744d7f3..d748c1c04 100755
--- a/src/input_common/analog_from_button.cpp
+++ b/src/input_common/analog_from_button.cpp
@@ -2,6 +2,10 @@
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 <chrono>
6#include <cmath>
7#include <thread>
8#include "common/math_util.h"
5#include "input_common/analog_from_button.h" 9#include "input_common/analog_from_button.h"
6 10
7namespace InputCommon { 11namespace InputCommon {
@@ -11,31 +15,104 @@ public:
11 using Button = std::unique_ptr<Input::ButtonDevice>; 15 using Button = std::unique_ptr<Input::ButtonDevice>;
12 16
13 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_, 17 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
14 float modifier_scale_) 18 float modifier_scale_, float modifier_angle_)
15 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), 19 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
16 right(std::move(right_)), modifier(std::move(modifier_)), 20 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
17 modifier_scale(modifier_scale_) {} 21 modifier_angle(modifier_angle_) {
18 22 update_thread = std::thread(&Analog::UpdateStatus, this);
19 std::tuple<float, float> GetStatus() const override { 23 }
20 constexpr float SQRT_HALF = 0.707106781f;
21 int x = 0, y = 0;
22 24
23 if (right->GetStatus()) { 25 ~Analog() override {
24 ++x; 26 update_thread_running = false;
27 if (update_thread.joinable()) {
28 update_thread.join();
25 } 29 }
26 if (left->GetStatus()) { 30 }
27 --x; 31
32 void MoveToDirection(bool enable, float to_angle) {
33 if (!enable) {
34 return;
28 } 35 }
29 if (up->GetStatus()) { 36 constexpr float TAU = Common::PI * 2.0f;
30 ++y; 37 // Use wider angle to ease the transition.
38 constexpr float aperture = TAU * 0.15f;
39 const float top_limit = to_angle + aperture;
40 const float bottom_limit = to_angle - aperture;
41
42 if ((angle > to_angle && angle <= top_limit) ||
43 (angle + TAU > to_angle && angle + TAU <= top_limit)) {
44 angle -= modifier_angle;
45 if (angle < 0) {
46 angle += TAU;
47 }
48 } else if ((angle >= bottom_limit && angle < to_angle) ||
49 (angle - TAU >= bottom_limit && angle - TAU < to_angle)) {
50 angle += modifier_angle;
51 if (angle >= TAU) {
52 angle -= TAU;
53 }
54 } else {
55 angle = to_angle;
31 } 56 }
32 if (down->GetStatus()) { 57 }
33 --y; 58
59 void UpdateStatus() {
60 while (update_thread_running) {
61 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
62
63 bool r = right->GetStatus();
64 bool l = left->GetStatus();
65 bool u = up->GetStatus();
66 bool d = down->GetStatus();
67
68 // Eliminate contradictory movements
69 if (r && l) {
70 r = false;
71 l = false;
72 }
73 if (u && d) {
74 u = false;
75 d = false;
76 }
77
78 // Move to the right
79 MoveToDirection(r && !u && !d, 0.0f);
80
81 // Move to the upper right
82 MoveToDirection(r && u && !d, Common::PI * 0.25f);
83
84 // Move up
85 MoveToDirection(u && !l && !r, Common::PI * 0.5f);
86
87 // Move to the upper left
88 MoveToDirection(l && u && !d, Common::PI * 0.75f);
89
90 // Move to the left
91 MoveToDirection(l && !u && !d, Common::PI);
92
93 // Move to the bottom left
94 MoveToDirection(l && !u && d, Common::PI * 1.25f);
95
96 // Move down
97 MoveToDirection(d && !l && !r, Common::PI * 1.5f);
98
99 // Move to the bottom right
100 MoveToDirection(r && !u && d, Common::PI * 1.75f);
101
102 // Move if a key is pressed
103 if (r || l || u || d) {
104 amplitude = coef;
105 } else {
106 amplitude = 0;
107 }
108
109 // Delay the update rate to 100hz
110 std::this_thread::sleep_for(std::chrono::milliseconds(10));
34 } 111 }
112 }
35 113
36 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; 114 std::tuple<float, float> GetStatus() const override {
37 return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF), 115 return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude);
38 static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
39 } 116 }
40 117
41 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 118 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
@@ -59,6 +136,11 @@ private:
59 Button right; 136 Button right;
60 Button modifier; 137 Button modifier;
61 float modifier_scale; 138 float modifier_scale;
139 float modifier_angle;
140 float angle{};
141 float amplitude{};
142 std::thread update_thread;
143 bool update_thread_running{true};
62}; 144};
63 145
64std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { 146std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
@@ -69,8 +151,10 @@ std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::Para
69 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); 151 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
70 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); 152 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
71 auto modifier_scale = params.Get("modifier_scale", 0.5f); 153 auto modifier_scale = params.Get("modifier_scale", 0.5f);
154 auto modifier_angle = params.Get("modifier_angle", 0.035f);
72 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), 155 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
73 std::move(right), std::move(modifier), modifier_scale); 156 std::move(right), std::move(modifier), modifier_scale,
157 modifier_angle);
74} 158}
75 159
76} // namespace InputCommon 160} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 4e8c7e8b9..4d1052414 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -299,7 +299,8 @@ public:
299 return gcadapter->RumblePlay(port, 0); 299 return gcadapter->RumblePlay(port, 0);
300 } 300 }
301 301
302 bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { 302 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
303 [[maybe_unused]] f32 freq_high) const override {
303 const auto mean_amplitude = (amp_low + amp_high) * 0.5f; 304 const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
304 const auto processed_amplitude = 305 const auto processed_amplitude =
305 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8); 306 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index c16928e98..7827e324c 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -400,7 +400,8 @@ public:
400 return joystick->RumblePlay(0, 0); 400 return joystick->RumblePlay(0, 0);
401 } 401 }
402 402
403 bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { 403 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
404 [[maybe_unused]] f32 freq_high) const override {
404 const auto process_amplitude = [](f32 amplitude) { 405 const auto process_amplitude = [](f32 amplitude) {
405 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF); 406 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
406 }; 407 };
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 10b07d338..c0bb90048 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -189,11 +189,11 @@ void Client::ReloadSocket(const std::string& host, u16 port, std::size_t pad_ind
189 StartCommunication(client, host, port, pad_index, client_id); 189 StartCommunication(client, host, port, pad_index, client_id);
190} 190}
191 191
192void Client::OnVersion(Response::Version data) { 192void Client::OnVersion([[maybe_unused]] Response::Version data) {
193 LOG_TRACE(Input, "Version packet received: {}", data.version); 193 LOG_TRACE(Input, "Version packet received: {}", data.version);
194} 194}
195 195
196void Client::OnPortInfo(Response::PortInfo data) { 196void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
197 LOG_TRACE(Input, "PortInfo packet received: {}", data.model); 197 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
198} 198}
199 199
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 2ccca1993..c869bb0e2 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -151,8 +151,8 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
151 151
152 rasterizer->TickFrame(); 152 rasterizer->TickFrame();
153 153
154 render_window.PollEvents();
155 context->SwapBuffers(); 154 context->SwapBuffers();
155 render_window.OnFrameDisplayed();
156} 156}
157 157
158void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { 158void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index da5c550ea..fffae528e 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -8,6 +8,7 @@
8 8
9#include <boost/functional/hash.hpp> 9#include <boost/functional/hash.hpp>
10 10
11#include "common/bit_cast.h"
11#include "common/cityhash.h" 12#include "common/cityhash.h"
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 14#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
@@ -60,7 +61,13 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta
60 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); 61 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
61 topology.Assign(regs.draw.topology); 62 topology.Assign(regs.draw.topology);
62 63
63 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast 64 alpha_raw = 0;
65 const auto test_func =
66 regs.alpha_test_enabled == 1 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always;
67 alpha_test_func.Assign(PackComparisonOp(test_func));
68 alpha_test_ref = Common::BitCast<u32>(regs.alpha_test_ref);
69
70 point_size = Common::BitCast<u32>(regs.point_size);
64 71
65 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 72 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
66 binding_divisors[index] = 73 binding_divisors[index] =
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 2c18eeaae..42480e8d0 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -187,6 +187,13 @@ struct FixedPipelineState {
187 BitField<23, 1, u32> rasterize_enable; 187 BitField<23, 1, u32> rasterize_enable;
188 BitField<24, 4, Maxwell::PrimitiveTopology> topology; 188 BitField<24, 4, Maxwell::PrimitiveTopology> topology;
189 }; 189 };
190
191 u32 alpha_test_ref; ///< Alpha test reference value
192 union {
193 u32 alpha_raw;
194 BitField<0, 3, u32> alpha_test_func;
195 };
196
190 u32 point_size; 197 u32 point_size;
191 std::array<u32, Maxwell::NumVertexArrays> binding_divisors; 198 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
192 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes; 199 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index f2610868e..a2173edd2 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -252,8 +252,6 @@ RendererVulkan::~RendererVulkan() {
252} 252}
253 253
254void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 254void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
255 render_window.PollEvents();
256
257 if (!framebuffer) { 255 if (!framebuffer) {
258 return; 256 return;
259 } 257 }
@@ -283,7 +281,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
283 rasterizer->TickFrame(); 281 rasterizer->TickFrame();
284 } 282 }
285 283
286 render_window.PollEvents(); 284 render_window.OnFrameDisplayed();
287} 285}
288 286
289bool RendererVulkan::Init() { 287bool RendererVulkan::Init() {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index dedc9c466..f9efe526d 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9 9
10#include "common/bit_cast.h"
10#include "common/microprofile.h" 11#include "common/microprofile.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "core/memory.h" 13#include "core/memory.h"
@@ -344,6 +345,11 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
344 } 345 }
345 specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one; 346 specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
346 347
348 // Alpha test
349 specialization.alpha_test_func =
350 FixedPipelineState::UnpackComparisonOp(fixed_state.alpha_test_func.Value());
351 specialization.alpha_test_ref = Common::BitCast<float>(fixed_state.alpha_test_ref);
352
347 SPIRVProgram program; 353 SPIRVProgram program;
348 std::vector<VkDescriptorSetLayoutBinding> bindings; 354 std::vector<VkDescriptorSetLayoutBinding> bindings;
349 355
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index a20452b87..1c52f40bb 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -2075,6 +2075,45 @@ private:
2075 return {}; 2075 return {};
2076 } 2076 }
2077 2077
2078 Id MaxwellToSpirvComparison(Maxwell::ComparisonOp compare_op, Id operand_1, Id operand_2) {
2079 using Compare = Maxwell::ComparisonOp;
2080 switch (compare_op) {
2081 case Compare::NeverOld:
2082 return v_false; // Never let the test pass
2083 case Compare::LessOld:
2084 return OpFOrdLessThan(t_bool, operand_1, operand_2);
2085 case Compare::EqualOld:
2086 return OpFOrdEqual(t_bool, operand_1, operand_2);
2087 case Compare::LessEqualOld:
2088 return OpFOrdLessThanEqual(t_bool, operand_1, operand_2);
2089 case Compare::GreaterOld:
2090 return OpFOrdGreaterThan(t_bool, operand_1, operand_2);
2091 case Compare::NotEqualOld:
2092 return OpFOrdNotEqual(t_bool, operand_1, operand_2);
2093 case Compare::GreaterEqualOld:
2094 return OpFOrdGreaterThanEqual(t_bool, operand_1, operand_2);
2095 default:
2096 UNREACHABLE();
2097 }
2098 }
2099
2100 void AlphaTest(Id pointer) {
2101 if (specialization.alpha_test_func == Maxwell::ComparisonOp::AlwaysOld) {
2102 return;
2103 }
2104 const Id true_label = OpLabel();
2105 const Id discard_label = OpLabel();
2106 const Id alpha_reference = Constant(t_float, specialization.alpha_test_ref);
2107 const Id alpha_value = OpLoad(t_float, pointer);
2108 const Id condition =
2109 MaxwellToSpirvComparison(specialization.alpha_test_func, alpha_value, alpha_reference);
2110
2111 OpBranchConditional(condition, true_label, discard_label);
2112 AddLabel(discard_label);
2113 OpKill();
2114 AddLabel(true_label);
2115 }
2116
2078 void PreExit() { 2117 void PreExit() {
2079 if (stage == ShaderType::Vertex && specialization.ndc_minus_one_to_one) { 2118 if (stage == ShaderType::Vertex && specialization.ndc_minus_one_to_one) {
2080 const u32 position_index = out_indices.position.value(); 2119 const u32 position_index = out_indices.position.value();
@@ -2097,8 +2136,6 @@ private:
2097 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, 2136 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0,
2098 "Sample mask write is unimplemented"); 2137 "Sample mask write is unimplemented");
2099 2138
2100 // TODO(Rodrigo): Alpha testing
2101
2102 // Write the color outputs using the data in the shader registers, disabled 2139 // Write the color outputs using the data in the shader registers, disabled
2103 // rendertargets/components are skipped in the register assignment. 2140 // rendertargets/components are skipped in the register assignment.
2104 u32 current_reg = 0; 2141 u32 current_reg = 0;
@@ -2110,6 +2147,9 @@ private:
2110 } 2147 }
2111 const Id pointer = AccessElement(t_out_float, frag_colors[rt], component); 2148 const Id pointer = AccessElement(t_out_float, frag_colors[rt], component);
2112 OpStore(pointer, SafeGetRegister(current_reg)); 2149 OpStore(pointer, SafeGetRegister(current_reg));
2150 if (rt == 0 && component == 3) {
2151 AlphaTest(pointer);
2152 }
2113 ++current_reg; 2153 ++current_reg;
2114 } 2154 }
2115 } 2155 }
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
index 2b0e90396..cd3d0a415 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
@@ -95,6 +95,8 @@ struct Specialization final {
95 std::bitset<Maxwell::NumVertexAttributes> enabled_attributes; 95 std::bitset<Maxwell::NumVertexAttributes> enabled_attributes;
96 std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{}; 96 std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{};
97 bool ndc_minus_one_to_one{}; 97 bool ndc_minus_one_to_one{};
98 float alpha_test_ref{};
99 Maxwell::ComparisonOp alpha_test_func{};
98}; 100};
99// Old gcc versions don't consider this trivially copyable. 101// Old gcc versions don't consider this trivially copyable.
100// static_assert(std::is_trivially_copyable_v<Specialization>); 102// static_assert(std::is_trivially_copyable_v<Specialization>);
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index 618d309d2..1ed4212ee 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -212,10 +212,10 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) {
212 return 0; 212 return 0;
213 case TextureFormat::R8G24: 213 case TextureFormat::R8G24:
214 if (component == 0) { 214 if (component == 0) {
215 return 8; 215 return 24;
216 } 216 }
217 if (component == 1) { 217 if (component == 1) {
218 return 24; 218 return 8;
219 } 219 }
220 return 0; 220 return 0;
221 case TextureFormat::R8G8: 221 case TextureFormat::R8G8:
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
index 8ecfec770..6944478f3 100644
--- a/src/yuzu/applets/controller.cpp
+++ b/src/yuzu/applets/controller.cpp
@@ -72,40 +72,6 @@ bool IsControllerCompatible(Settings::ControllerType controller_type,
72 } 72 }
73} 73}
74 74
75/// Maps the controller type combobox index to Controller Type enum
76constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
77 switch (index) {
78 case 0:
79 default:
80 return Settings::ControllerType::ProController;
81 case 1:
82 return Settings::ControllerType::DualJoyconDetached;
83 case 2:
84 return Settings::ControllerType::LeftJoycon;
85 case 3:
86 return Settings::ControllerType::RightJoycon;
87 case 4:
88 return Settings::ControllerType::Handheld;
89 }
90}
91
92/// Maps the Controller Type enum to controller type combobox index
93constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
94 switch (type) {
95 case Settings::ControllerType::ProController:
96 default:
97 return 0;
98 case Settings::ControllerType::DualJoyconDetached:
99 return 1;
100 case Settings::ControllerType::LeftJoycon:
101 return 2;
102 case Settings::ControllerType::RightJoycon:
103 return 3;
104 case Settings::ControllerType::Handheld:
105 return 4;
106 }
107}
108
109} // namespace 75} // namespace
110 76
111QtControllerSelectorDialog::QtControllerSelectorDialog( 77QtControllerSelectorDialog::QtControllerSelectorDialog(
@@ -184,6 +150,11 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
184 // This avoids unintentionally changing the states of elements while loading them in. 150 // This avoids unintentionally changing the states of elements while loading them in.
185 SetSupportedControllers(); 151 SetSupportedControllers();
186 DisableUnsupportedPlayers(); 152 DisableUnsupportedPlayers();
153
154 for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
155 SetEmulatedControllers(player_index);
156 }
157
187 LoadConfiguration(); 158 LoadConfiguration();
188 159
189 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { 160 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
@@ -223,8 +194,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
223 194
224 if (i == 0) { 195 if (i == 0) {
225 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), 196 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
226 [this](int index) { 197 [this, i](int index) {
227 UpdateDockedState(GetControllerTypeFromIndex(index) == 198 UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
228 Settings::ControllerType::Handheld); 199 Settings::ControllerType::Handheld);
229 }); 200 });
230 } 201 }
@@ -281,8 +252,8 @@ void QtControllerSelectorDialog::LoadConfiguration() {
281 (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected); 252 (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
282 player_groupboxes[index]->setChecked(connected); 253 player_groupboxes[index]->setChecked(connected);
283 connected_controller_checkboxes[index]->setChecked(connected); 254 connected_controller_checkboxes[index]->setChecked(connected);
284 emulated_controllers[index]->setCurrentIndex( 255 emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType(
285 GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type)); 256 Settings::values.players.GetValue()[index].controller_type, index));
286 } 257 }
287 258
288 UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); 259 UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
@@ -338,7 +309,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() {
338 } 309 }
339 310
340 const auto compatible = IsControllerCompatible( 311 const auto compatible = IsControllerCompatible(
341 GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()), 312 GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index),
342 parameters); 313 parameters);
343 314
344 // If any controller is found to be incompatible, return false early. 315 // If any controller is found to be incompatible, return false early.
@@ -422,6 +393,63 @@ void QtControllerSelectorDialog::SetSupportedControllers() {
422 } 393 }
423} 394}
424 395
396void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
397 auto& pairs = index_controller_type_pairs[player_index];
398
399 pairs.clear();
400 emulated_controllers[player_index]->clear();
401
402 pairs.emplace_back(emulated_controllers[player_index]->count(),
403 Settings::ControllerType::ProController);
404 emulated_controllers[player_index]->addItem(tr("Pro Controller"));
405
406 pairs.emplace_back(emulated_controllers[player_index]->count(),
407 Settings::ControllerType::DualJoyconDetached);
408 emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
409
410 pairs.emplace_back(emulated_controllers[player_index]->count(),
411 Settings::ControllerType::LeftJoycon);
412 emulated_controllers[player_index]->addItem(tr("Left Joycon"));
413
414 pairs.emplace_back(emulated_controllers[player_index]->count(),
415 Settings::ControllerType::RightJoycon);
416 emulated_controllers[player_index]->addItem(tr("Right Joycon"));
417
418 if (player_index == 0) {
419 pairs.emplace_back(emulated_controllers[player_index]->count(),
420 Settings::ControllerType::Handheld);
421 emulated_controllers[player_index]->addItem(tr("Handheld"));
422 }
423}
424
425Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
426 int index, std::size_t player_index) const {
427 const auto& pairs = index_controller_type_pairs[player_index];
428
429 const auto it = std::find_if(pairs.begin(), pairs.end(),
430 [index](const auto& pair) { return pair.first == index; });
431
432 if (it == pairs.end()) {
433 return Settings::ControllerType::ProController;
434 }
435
436 return it->second;
437}
438
439int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type,
440 std::size_t player_index) const {
441 const auto& pairs = index_controller_type_pairs[player_index];
442
443 const auto it = std::find_if(pairs.begin(), pairs.end(),
444 [type](const auto& pair) { return pair.second == type; });
445
446 if (it == pairs.end()) {
447 return 0;
448 }
449
450 return it->first;
451}
452
425void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) { 453void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
426 if (!player_groupboxes[player_index]->isChecked()) { 454 if (!player_groupboxes[player_index]->isChecked()) {
427 connected_controller_icons[player_index]->setStyleSheet(QString{}); 455 connected_controller_icons[player_index]->setStyleSheet(QString{});
@@ -430,7 +458,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
430 } 458 }
431 459
432 const QString stylesheet = [this, player_index] { 460 const QString stylesheet = [this, player_index] {
433 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) { 461 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
462 player_index)) {
434 case Settings::ControllerType::ProController: 463 case Settings::ControllerType::ProController:
435 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); 464 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
436 case Settings::ControllerType::DualJoyconDetached: 465 case Settings::ControllerType::DualJoyconDetached:
@@ -446,6 +475,12 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
446 } 475 }
447 }(); 476 }();
448 477
478 if (stylesheet.isEmpty()) {
479 connected_controller_icons[player_index]->setStyleSheet(QString{});
480 player_labels[player_index]->show();
481 return;
482 }
483
449 const QString theme = [] { 484 const QString theme = [] {
450 if (QIcon::themeName().contains(QStringLiteral("dark"))) { 485 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
451 return QStringLiteral("_dark"); 486 return QStringLiteral("_dark");
@@ -463,8 +498,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
463void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { 498void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
464 auto& player = Settings::values.players.GetValue()[player_index]; 499 auto& player = Settings::values.players.GetValue()[player_index];
465 500
466 const auto controller_type = 501 const auto controller_type = GetControllerTypeFromIndex(
467 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()); 502 emulated_controllers[player_index]->currentIndex(), player_index);
468 const auto player_connected = player_groupboxes[player_index]->isChecked() && 503 const auto player_connected = player_groupboxes[player_index]->isChecked() &&
469 controller_type != Settings::ControllerType::Handheld; 504 controller_type != Settings::ControllerType::Handheld;
470 505
@@ -507,8 +542,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)
507 542
508void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { 543void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
509 if (!player_groupboxes[player_index]->isChecked() || 544 if (!player_groupboxes[player_index]->isChecked() ||
510 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) == 545 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
511 Settings::ControllerType::Handheld) { 546 player_index) == Settings::ControllerType::Handheld) {
512 led_patterns_boxes[player_index][0]->setChecked(false); 547 led_patterns_boxes[player_index][0]->setChecked(false);
513 led_patterns_boxes[player_index][1]->setChecked(false); 548 led_patterns_boxes[player_index][1]->setChecked(false);
514 led_patterns_boxes[player_index][2]->setChecked(false); 549 led_patterns_boxes[player_index][2]->setChecked(false);
diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h
index 4344e1dd0..7a421d856 100644
--- a/src/yuzu/applets/controller.h
+++ b/src/yuzu/applets/controller.h
@@ -22,6 +22,10 @@ namespace InputCommon {
22class InputSubsystem; 22class InputSubsystem;
23} 23}
24 24
25namespace Settings {
26enum class ControllerType;
27}
28
25namespace Ui { 29namespace Ui {
26class QtControllerSelectorDialog; 30class QtControllerSelectorDialog;
27} 31}
@@ -57,6 +61,15 @@ private:
57 // Sets the controller icons for "Supported Controller Types". 61 // Sets the controller icons for "Supported Controller Types".
58 void SetSupportedControllers(); 62 void SetSupportedControllers();
59 63
64 // Sets the emulated controllers per player.
65 void SetEmulatedControllers(std::size_t player_index);
66
67 // Gets the Controller Type for a given controller combobox index per player.
68 Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const;
69
70 // Gets the controller combobox index for a given Controller Type per player.
71 int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const;
72
60 // Updates the controller icons per player. 73 // Updates the controller icons per player.
61 void UpdateControllerIcon(std::size_t player_index); 74 void UpdateControllerIcon(std::size_t player_index);
62 75
@@ -114,6 +127,10 @@ private:
114 // Comboboxes with a list of emulated controllers per player. 127 // Comboboxes with a list of emulated controllers per player.
115 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers; 128 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
116 129
130 /// Pairs of emulated controller index and Controller Type enum per player.
131 std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS>
132 index_controller_type_pairs;
133
117 // Labels representing the number of connected controllers 134 // Labels representing the number of connected controllers
118 // above the "Connected Controllers" checkboxes. 135 // above the "Connected Controllers" checkboxes.
119 std::array<QLabel*, NUM_PLAYERS> connected_controller_labels; 136 std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d62b0efc2..ea8f0d7b1 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -302,13 +302,19 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
302 this->setMouseTracking(true); 302 this->setMouseTracking(true);
303 303
304 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 304 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
305 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
306 Qt::QueuedConnection);
307}
308
309void GRenderWindow::ExecuteProgram(std::size_t program_index) {
310 emit ExecuteProgramSignal(program_index);
305} 311}
306 312
307GRenderWindow::~GRenderWindow() { 313GRenderWindow::~GRenderWindow() {
308 input_subsystem->Shutdown(); 314 input_subsystem->Shutdown();
309} 315}
310 316
311void GRenderWindow::PollEvents() { 317void GRenderWindow::OnFrameDisplayed() {
312 if (!first_frame) { 318 if (!first_frame) {
313 first_frame = true; 319 first_frame = true;
314 emit FirstFrameDisplayed(); 320 emit FirstFrameDisplayed();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index ca35cf831..a6d788d40 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -131,7 +131,7 @@ public:
131 ~GRenderWindow() override; 131 ~GRenderWindow() override;
132 132
133 // EmuWindow implementation. 133 // EmuWindow implementation.
134 void PollEvents() override; 134 void OnFrameDisplayed() override;
135 bool IsShown() const override; 135 bool IsShown() const override;
136 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 136 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
137 137
@@ -166,6 +166,12 @@ public:
166 166
167 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; 167 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
168 168
169 /**
170 * Instructs the window to re-launch the application using the specified program_index.
171 * @param program_index Specifies the index within the application of the program to launch.
172 */
173 void ExecuteProgram(std::size_t program_index);
174
169public slots: 175public slots:
170 void OnEmulationStarting(EmuThread* emu_thread); 176 void OnEmulationStarting(EmuThread* emu_thread);
171 void OnEmulationStopping(); 177 void OnEmulationStopping();
@@ -175,6 +181,7 @@ signals:
175 /// Emitted when the window is closed 181 /// Emitted when the window is closed
176 void Closed(); 182 void Closed();
177 void FirstFrameDisplayed(); 183 void FirstFrameDisplayed();
184 void ExecuteProgramSignal(std::size_t program_index);
178 185
179private: 186private:
180 void TouchBeginEvent(const QTouchEvent* event); 187 void TouchBeginEvent(const QTouchEvent* event);
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 56ab32a35..918bfb56b 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -27,6 +27,8 @@
27#include "yuzu/configuration/input_profiles.h" 27#include "yuzu/configuration/input_profiles.h"
28#include "yuzu/util/limitable_input_dialog.h" 28#include "yuzu/util/limitable_input_dialog.h"
29 29
30using namespace Service::HID;
31
30const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> 32const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
31 ConfigureInputPlayer::analog_sub_buttons{{ 33 ConfigureInputPlayer::analog_sub_buttons{{
32 "up", 34 "up",
@@ -47,48 +49,12 @@ void UpdateController(Settings::ControllerType controller_type, std::size_t npad
47 } 49 }
48 Service::SM::ServiceManager& sm = system.ServiceManager(); 50 Service::SM::ServiceManager& sm = system.ServiceManager();
49 51
50 auto& npad = 52 auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
51 sm.GetService<Service::HID::Hid>("hid") 53 HidController::NPad);
52 ->GetAppletResource()
53 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
54 54
55 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected); 55 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
56} 56}
57 57
58/// Maps the controller type combobox index to Controller Type enum
59constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
60 switch (index) {
61 case 0:
62 default:
63 return Settings::ControllerType::ProController;
64 case 1:
65 return Settings::ControllerType::DualJoyconDetached;
66 case 2:
67 return Settings::ControllerType::LeftJoycon;
68 case 3:
69 return Settings::ControllerType::RightJoycon;
70 case 4:
71 return Settings::ControllerType::Handheld;
72 }
73}
74
75/// Maps the Controller Type enum to controller type combobox index
76constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
77 switch (type) {
78 case Settings::ControllerType::ProController:
79 default:
80 return 0;
81 case Settings::ControllerType::DualJoyconDetached:
82 return 1;
83 case Settings::ControllerType::LeftJoycon:
84 return 2;
85 case Settings::ControllerType::RightJoycon:
86 return 3;
87 case Settings::ControllerType::Handheld:
88 return 4;
89 }
90}
91
92QString GetKeyName(int key_code) { 58QString GetKeyName(int key_code) {
93 switch (key_code) { 59 switch (key_code) {
94 case Qt::LeftButton: 60 case Qt::LeftButton:
@@ -453,18 +419,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
453 connect(ui->groupConnectedController, &QGroupBox::toggled, 419 connect(ui->groupConnectedController, &QGroupBox::toggled,
454 [this](bool checked) { emit Connected(checked); }); 420 [this](bool checked) { emit Connected(checked); });
455 421
456 // Set up controller type. Only Player 1 can choose Handheld.
457 ui->comboControllerType->clear();
458
459 QStringList controller_types = {
460 tr("Pro Controller"),
461 tr("Dual Joycons"),
462 tr("Left Joycon"),
463 tr("Right Joycon"),
464 };
465
466 if (player_index == 0) { 422 if (player_index == 0) {
467 controller_types.append(tr("Handheld"));
468 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), 423 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
469 [this](int index) { 424 [this](int index) {
470 emit HandheldStateChanged(GetControllerTypeFromIndex(index) == 425 emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
@@ -480,12 +435,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
480 if (debug) { 435 if (debug) {
481 ui->buttonScreenshot->setEnabled(false); 436 ui->buttonScreenshot->setEnabled(false);
482 ui->buttonHome->setEnabled(false); 437 ui->buttonHome->setEnabled(false);
483 QStringList debug_controller_types = { 438 ui->comboControllerType->addItem(tr("Pro Controller"));
484 tr("Pro Controller"),
485 };
486 ui->comboControllerType->addItems(debug_controller_types);
487 } else { 439 } else {
488 ui->comboControllerType->addItems(controller_types); 440 SetConnectableControllers();
489 } 441 }
490 442
491 UpdateControllerIcon(); 443 UpdateControllerIcon();
@@ -667,7 +619,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
667 return; 619 return;
668 } 620 }
669 621
670 ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type)); 622 ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type));
671 ui->groupConnectedController->setChecked( 623 ui->groupConnectedController->setChecked(
672 player.connected || 624 player.connected ||
673 (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); 625 (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
@@ -841,6 +793,82 @@ void ConfigureInputPlayer::UpdateUI() {
841 } 793 }
842} 794}
843 795
796void ConfigureInputPlayer::SetConnectableControllers() {
797 const auto add_controllers = [this](bool enable_all,
798 Controller_NPad::NpadStyleSet npad_style_set = {}) {
799 index_controller_type_pairs.clear();
800 ui->comboControllerType->clear();
801
802 if (enable_all || npad_style_set.pro_controller == 1) {
803 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
804 Settings::ControllerType::ProController);
805 ui->comboControllerType->addItem(tr("Pro Controller"));
806 }
807
808 if (enable_all || npad_style_set.joycon_dual == 1) {
809 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
810 Settings::ControllerType::DualJoyconDetached);
811 ui->comboControllerType->addItem(tr("Dual Joycons"));
812 }
813
814 if (enable_all || npad_style_set.joycon_left == 1) {
815 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
816 Settings::ControllerType::LeftJoycon);
817 ui->comboControllerType->addItem(tr("Left Joycon"));
818 }
819
820 if (enable_all || npad_style_set.joycon_right == 1) {
821 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
822 Settings::ControllerType::RightJoycon);
823 ui->comboControllerType->addItem(tr("Right Joycon"));
824 }
825
826 if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) {
827 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
828 Settings::ControllerType::Handheld);
829 ui->comboControllerType->addItem(tr("Handheld"));
830 }
831 };
832
833 Core::System& system{Core::System::GetInstance()};
834
835 if (!system.IsPoweredOn()) {
836 add_controllers(true);
837 return;
838 }
839
840 Service::SM::ServiceManager& sm = system.ServiceManager();
841
842 auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
843 HidController::NPad);
844
845 add_controllers(false, npad.GetSupportedStyleSet());
846}
847
848Settings::ControllerType ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const {
849 const auto it =
850 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
851 [index](const auto& pair) { return pair.first == index; });
852
853 if (it == index_controller_type_pairs.end()) {
854 return Settings::ControllerType::ProController;
855 }
856
857 return it->second;
858}
859
860int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType type) const {
861 const auto it =
862 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
863 [type](const auto& pair) { return pair.second == type; });
864
865 if (it == index_controller_type_pairs.end()) {
866 return -1;
867 }
868
869 return it->first;
870}
871
844void ConfigureInputPlayer::UpdateInputDevices() { 872void ConfigureInputPlayer::UpdateInputDevices() {
845 input_devices = input_subsystem->GetInputDevices(); 873 input_devices = input_subsystem->GetInputDevices();
846 ui->comboDevices->clear(); 874 ui->comboDevices->clear();
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 23cf6f958..9c30879a2 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -9,6 +9,7 @@
9#include <memory> 9#include <memory>
10#include <optional> 10#include <optional>
11#include <string> 11#include <string>
12#include <vector>
12 13
13#include <QWidget> 14#include <QWidget>
14 15
@@ -112,6 +113,15 @@ private:
112 /// Update UI to reflect current configuration. 113 /// Update UI to reflect current configuration.
113 void UpdateUI(); 114 void UpdateUI();
114 115
116 /// Sets the available controllers.
117 void SetConnectableControllers();
118
119 /// Gets the Controller Type for a given controller combobox index.
120 Settings::ControllerType GetControllerTypeFromIndex(int index) const;
121
122 /// Gets the controller combobox index for a given Controller Type.
123 int GetIndexFromControllerType(Settings::ControllerType type) const;
124
115 /// Update the available input devices. 125 /// Update the available input devices.
116 void UpdateInputDevices(); 126 void UpdateInputDevices();
117 127
@@ -151,6 +161,9 @@ private:
151 std::unique_ptr<QTimer> timeout_timer; 161 std::unique_ptr<QTimer> timeout_timer;
152 std::unique_ptr<QTimer> poll_timer; 162 std::unique_ptr<QTimer> poll_timer;
153 163
164 /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum.
165 std::vector<std::pair<int, Settings::ControllerType>> index_controller_type_pairs;
166
154 static constexpr int PLAYER_COUNT = 8; 167 static constexpr int PLAYER_COUNT = 8;
155 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; 168 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
156 169
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e704cc656..805619ccf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -978,7 +978,7 @@ void GMainWindow::AllowOSSleep() {
978#endif 978#endif
979} 979}
980 980
981bool GMainWindow::LoadROM(const QString& filename) { 981bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
982 // Shutdown previous session if the emu thread is still active... 982 // Shutdown previous session if the emu thread is still active...
983 if (emu_thread != nullptr) 983 if (emu_thread != nullptr)
984 ShutdownGame(); 984 ShutdownGame();
@@ -1003,7 +1003,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
1003 1003
1004 system.RegisterHostThread(); 1004 system.RegisterHostThread();
1005 1005
1006 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 1006 const Core::System::ResultStatus result{
1007 system.Load(*render_window, filename.toStdString(), program_index)};
1007 1008
1008 const auto drd_callout = 1009 const auto drd_callout =
1009 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; 1010 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1085,14 +1086,18 @@ void GMainWindow::SelectAndSetCurrentUser() {
1085 Settings::values.current_user = dialog.GetIndex(); 1086 Settings::values.current_user = dialog.GetIndex();
1086} 1087}
1087 1088
1088void GMainWindow::BootGame(const QString& filename) { 1089void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1089 LOG_INFO(Frontend, "yuzu starting..."); 1090 LOG_INFO(Frontend, "yuzu starting...");
1090 StoreRecentFile(filename); // Put the filename on top of the list 1091 StoreRecentFile(filename); // Put the filename on top of the list
1091 1092
1092 u64 title_id{0}; 1093 u64 title_id{0};
1094
1095 last_filename_booted = filename;
1096
1093 auto& system = Core::System::GetInstance(); 1097 auto& system = Core::System::GetInstance();
1094 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); 1098 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1095 const auto loader = Loader::GetLoader(system, v_file); 1099 const auto loader = Loader::GetLoader(system, v_file, program_index);
1100
1096 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { 1101 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
1097 // Load per game settings 1102 // Load per game settings
1098 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); 1103 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
@@ -1106,7 +1111,7 @@ void GMainWindow::BootGame(const QString& filename) {
1106 SelectAndSetCurrentUser(); 1111 SelectAndSetCurrentUser();
1107 } 1112 }
1108 1113
1109 if (!LoadROM(filename)) 1114 if (!LoadROM(filename, program_index))
1110 return; 1115 return;
1111 1116
1112 // Create and start the emulation thread 1117 // Create and start the emulation thread
@@ -1114,6 +1119,10 @@ void GMainWindow::BootGame(const QString& filename) {
1114 emit EmulationStarting(emu_thread.get()); 1119 emit EmulationStarting(emu_thread.get());
1115 emu_thread->start(); 1120 emu_thread->start();
1116 1121
1122 // Register an ExecuteProgram callback such that Core can execute a sub-program
1123 system.RegisterExecuteProgramCallback(
1124 [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
1125
1117 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 1126 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
1118 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views 1127 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
1119 // before the CPU continues 1128 // before the CPU continues
@@ -2136,6 +2145,11 @@ void GMainWindow::OnLoadComplete() {
2136 loading_screen->OnLoadComplete(); 2145 loading_screen->OnLoadComplete();
2137} 2146}
2138 2147
2148void GMainWindow::OnExecuteProgram(std::size_t program_index) {
2149 ShutdownGame();
2150 BootGame(last_filename_booted, program_index);
2151}
2152
2139void GMainWindow::ErrorDisplayDisplayError(QString body) { 2153void GMainWindow::ErrorDisplayDisplayError(QString body) {
2140 QMessageBox::critical(this, tr("Error Display"), body); 2154 QMessageBox::critical(this, tr("Error Display"), body);
2141 emit ErrorDisplayFinished(); 2155 emit ErrorDisplayFinished();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b380a66f3..6242341d1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -131,6 +131,7 @@ signals:
131 131
132public slots: 132public slots:
133 void OnLoadComplete(); 133 void OnLoadComplete();
134 void OnExecuteProgram(std::size_t program_index);
134 void ControllerSelectorReconfigureControllers( 135 void ControllerSelectorReconfigureControllers(
135 const Core::Frontend::ControllerParameters& parameters); 136 const Core::Frontend::ControllerParameters& parameters);
136 void ErrorDisplayDisplayError(QString body); 137 void ErrorDisplayDisplayError(QString body);
@@ -154,8 +155,8 @@ private:
154 void PreventOSSleep(); 155 void PreventOSSleep();
155 void AllowOSSleep(); 156 void AllowOSSleep();
156 157
157 bool LoadROM(const QString& filename); 158 bool LoadROM(const QString& filename, std::size_t program_index);
158 void BootGame(const QString& filename); 159 void BootGame(const QString& filename, std::size_t program_index = 0);
159 void ShutdownGame(); 160 void ShutdownGame();
160 161
161 void ShowTelemetryCallout(); 162 void ShowTelemetryCallout();
@@ -317,6 +318,9 @@ private:
317 // Install progress dialog 318 // Install progress dialog
318 QProgressDialog* install_progress; 319 QProgressDialog* install_progress;
319 320
321 // Last game booted, used for multi-process apps
322 QString last_filename_booted;
323
320protected: 324protected:
321 void dropEvent(QDropEvent* event) override; 325 void dropEvent(QDropEvent* event) override;
322 void dragEnterEvent(QDragEnterEvent* event) override; 326 void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 521209622..c4a4a36be 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -121,62 +121,64 @@ void EmuWindow_SDL2::Fullscreen() {
121 SDL_MaximizeWindow(render_window); 121 SDL_MaximizeWindow(render_window);
122} 122}
123 123
124void EmuWindow_SDL2::PollEvents() { 124void EmuWindow_SDL2::WaitEvent() {
125 // Called on main thread
125 SDL_Event event; 126 SDL_Event event;
126 127
127 // SDL_PollEvent returns 0 when there are no more events in the event queue 128 if (!SDL_WaitEvent(&event)) {
128 while (SDL_PollEvent(&event)) { 129 LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError());
129 switch (event.type) { 130 exit(1);
130 case SDL_WINDOWEVENT: 131 }
131 switch (event.window.event) { 132
132 case SDL_WINDOWEVENT_SIZE_CHANGED: 133 switch (event.type) {
133 case SDL_WINDOWEVENT_RESIZED: 134 case SDL_WINDOWEVENT:
134 case SDL_WINDOWEVENT_MAXIMIZED: 135 switch (event.window.event) {
135 case SDL_WINDOWEVENT_RESTORED: 136 case SDL_WINDOWEVENT_SIZE_CHANGED:
136 OnResize(); 137 case SDL_WINDOWEVENT_RESIZED:
137 break; 138 case SDL_WINDOWEVENT_MAXIMIZED:
138 case SDL_WINDOWEVENT_MINIMIZED: 139 case SDL_WINDOWEVENT_RESTORED:
139 case SDL_WINDOWEVENT_EXPOSED: 140 OnResize();
140 is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED;
141 OnResize();
142 break;
143 case SDL_WINDOWEVENT_CLOSE:
144 is_open = false;
145 break;
146 }
147 break;
148 case SDL_KEYDOWN:
149 case SDL_KEYUP:
150 OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
151 break;
152 case SDL_MOUSEMOTION:
153 // ignore if it came from touch
154 if (event.button.which != SDL_TOUCH_MOUSEID)
155 OnMouseMotion(event.motion.x, event.motion.y);
156 break;
157 case SDL_MOUSEBUTTONDOWN:
158 case SDL_MOUSEBUTTONUP:
159 // ignore if it came from touch
160 if (event.button.which != SDL_TOUCH_MOUSEID) {
161 OnMouseButton(event.button.button, event.button.state, event.button.x,
162 event.button.y);
163 }
164 break;
165 case SDL_FINGERDOWN:
166 OnFingerDown(event.tfinger.x, event.tfinger.y);
167 break;
168 case SDL_FINGERMOTION:
169 OnFingerMotion(event.tfinger.x, event.tfinger.y);
170 break; 141 break;
171 case SDL_FINGERUP: 142 case SDL_WINDOWEVENT_MINIMIZED:
172 OnFingerUp(); 143 case SDL_WINDOWEVENT_EXPOSED:
144 is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED;
145 OnResize();
173 break; 146 break;
174 case SDL_QUIT: 147 case SDL_WINDOWEVENT_CLOSE:
175 is_open = false; 148 is_open = false;
176 break; 149 break;
177 default:
178 break;
179 } 150 }
151 break;
152 case SDL_KEYDOWN:
153 case SDL_KEYUP:
154 OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
155 break;
156 case SDL_MOUSEMOTION:
157 // ignore if it came from touch
158 if (event.button.which != SDL_TOUCH_MOUSEID)
159 OnMouseMotion(event.motion.x, event.motion.y);
160 break;
161 case SDL_MOUSEBUTTONDOWN:
162 case SDL_MOUSEBUTTONUP:
163 // ignore if it came from touch
164 if (event.button.which != SDL_TOUCH_MOUSEID) {
165 OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
166 }
167 break;
168 case SDL_FINGERDOWN:
169 OnFingerDown(event.tfinger.x, event.tfinger.y);
170 break;
171 case SDL_FINGERMOTION:
172 OnFingerMotion(event.tfinger.x, event.tfinger.y);
173 break;
174 case SDL_FINGERUP:
175 OnFingerUp();
176 break;
177 case SDL_QUIT:
178 is_open = false;
179 break;
180 default:
181 break;
180 } 182 }
181 183
182 const u32 current_time = SDL_GetTicks(); 184 const u32 current_time = SDL_GetTicks();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 53d756c3c..a93141240 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -23,38 +23,38 @@ public:
23 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem); 23 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
24 ~EmuWindow_SDL2(); 24 ~EmuWindow_SDL2();
25 25
26 /// Polls window events
27 void PollEvents() override;
28
29 /// Whether the window is still open, and a close request hasn't yet been sent 26 /// Whether the window is still open, and a close request hasn't yet been sent
30 bool IsOpen() const; 27 bool IsOpen() const;
31 28
32 /// Returns if window is shown (not minimized) 29 /// Returns if window is shown (not minimized)
33 bool IsShown() const override; 30 bool IsShown() const override;
34 31
32 /// Wait for the next event on the main thread.
33 void WaitEvent();
34
35protected: 35protected:
36 /// Called by PollEvents when a key is pressed or released. 36 /// Called by WaitEvent when a key is pressed or released.
37 void OnKeyEvent(int key, u8 state); 37 void OnKeyEvent(int key, u8 state);
38 38
39 /// Called by PollEvents when the mouse moves. 39 /// Called by WaitEvent when the mouse moves.
40 void OnMouseMotion(s32 x, s32 y); 40 void OnMouseMotion(s32 x, s32 y);
41 41
42 /// Called by PollEvents when a mouse button is pressed or released 42 /// Called by WaitEvent when a mouse button is pressed or released
43 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 43 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
44 44
45 /// Translates pixel position (0..1) to pixel positions 45 /// Translates pixel position (0..1) to pixel positions
46 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; 46 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
47 47
48 /// Called by PollEvents when a finger starts touching the touchscreen 48 /// Called by WaitEvent when a finger starts touching the touchscreen
49 void OnFingerDown(float x, float y); 49 void OnFingerDown(float x, float y);
50 50
51 /// Called by PollEvents when a finger moves while touching the touchscreen 51 /// Called by WaitEvent when a finger moves while touching the touchscreen
52 void OnFingerMotion(float x, float y); 52 void OnFingerMotion(float x, float y);
53 53
54 /// Called by PollEvents when a finger stops touching the touchscreen 54 /// Called by WaitEvent when a finger stops touching the touchscreen
55 void OnFingerUp(); 55 void OnFingerUp();
56 56
57 /// Called by PollEvents when any event that may cause the window to be resized occurs 57 /// Called by WaitEvent when any event that may cause the window to be resized occurs
58 void OnResize(); 58 void OnResize();
59 59
60 /// Called when user passes the fullscreen parameter flag 60 /// Called when user passes the fullscreen parameter flag
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 3a76c785f..ba6e89249 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -240,11 +240,11 @@ int main(int argc, char** argv) {
240 system.CurrentProcess()->GetTitleID(), false, 240 system.CurrentProcess()->GetTitleID(), false,
241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); 241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
242 242
243 system.Run(); 243 void(system.Run());
244 while (emu_window->IsOpen()) { 244 while (emu_window->IsOpen()) {
245 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 245 emu_window->WaitEvent();
246 } 246 }
247 system.Pause(); 247 void(system.Pause());
248 system.Shutdown(); 248 system.Shutdown();
249 249
250 detached_tasks.WaitForAllTasks(); 250 detached_tasks.WaitForAllTasks();
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
index 78f75fb38..358e03870 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -109,8 +109,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
109 SDL_Quit(); 109 SDL_Quit();
110} 110}
111 111
112void EmuWindow_SDL2_Hide::PollEvents() {}
113
114bool EmuWindow_SDL2_Hide::IsShown() const { 112bool EmuWindow_SDL2_Hide::IsShown() const {
115 return false; 113 return false;
116} 114}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
index a553b4b95..adccdf35e 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -17,9 +17,6 @@ public:
17 explicit EmuWindow_SDL2_Hide(); 17 explicit EmuWindow_SDL2_Hide();
18 ~EmuWindow_SDL2_Hide(); 18 ~EmuWindow_SDL2_Hide();
19 19
20 /// Polls window events
21 void PollEvents() override;
22
23 /// Whether the screen is being shown or not. 20 /// Whether the screen is being shown or not.
24 bool IsShown() const override; 21 bool IsShown() const override;
25 22
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 5798ce43a..88e4bd1f7 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -256,11 +256,11 @@ int main(int argc, char** argv) {
256 256
257 system.GPU().Start(); 257 system.GPU().Start();
258 258
259 system.Run(); 259 void(system.Run());
260 while (!finished) { 260 while (!finished) {
261 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 261 std::this_thread::sleep_for(std::chrono::milliseconds(1));
262 } 262 }
263 system.Pause(); 263 void(system.Pause());
264 264
265 detached_tasks.WaitForAllTasks(); 265 detached_tasks.WaitForAllTasks();
266 return return_value; 266 return return_value;