summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt16
-rw-r--r--src/audio_core/algorithm/interpolate.cpp32
-rw-r--r--src/audio_core/algorithm/interpolate.h3
-rw-r--r--src/audio_core/audio_renderer.cpp543
-rw-r--r--src/audio_core/audio_renderer.h224
-rw-r--r--src/audio_core/behavior_info.cpp79
-rw-r--r--src/audio_core/behavior_info.h52
-rw-r--r--src/audio_core/command_generator.cpp976
-rw-r--r--src/audio_core/command_generator.h103
-rw-r--r--src/audio_core/common.h65
-rw-r--r--src/audio_core/cubeb_sink.cpp18
-rw-r--r--src/audio_core/effect_context.cpp299
-rw-r--r--src/audio_core/effect_context.h322
-rw-r--r--src/audio_core/info_updater.cpp517
-rw-r--r--src/audio_core/info_updater.h58
-rw-r--r--src/audio_core/memory_pool.cpp62
-rw-r--r--src/audio_core/memory_pool.h53
-rw-r--r--src/audio_core/mix_context.cpp296
-rw-r--r--src/audio_core/mix_context.h114
-rw-r--r--src/audio_core/sink_context.cpp31
-rw-r--r--src/audio_core/sink_context.h89
-rw-r--r--src/audio_core/splitter_context.cpp617
-rw-r--r--src/audio_core/splitter_context.h221
-rw-r--r--src/audio_core/stream.cpp6
-rw-r--r--src/audio_core/voice_context.cpp526
-rw-r--r--src/audio_core/voice_context.h296
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/frontend/applets/controller.cpp81
-rw-r--r--src/core/frontend/applets/controller.h48
-rw-r--r--src/core/hle/service/am/applets/applets.cpp71
-rw-r--r--src/core/hle/service/am/applets/applets.h19
-rw-r--r--src/core/hle/service/am/applets/controller.cpp210
-rw-r--r--src/core/hle/service/am/applets/controller.h123
-rw-r--r--src/core/hle/service/audio/audren_u.cpp149
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp116
-rw-r--r--src/core/hle/service/hid/controllers/npad.h7
-rw-r--r--src/core/hle/service/sockets/blocking_worker.h7
-rw-r--r--src/core/hle/service/sockets/bsd.cpp7
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp24
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp2
-rw-r--r--src/input_common/gcadapter/gc_adapter.h2
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp14
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h51
-rw-r--r--src/video_core/fence_manager.h27
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/gpu.h3
-rw-r--r--src/video_core/query_cache.h31
-rw-r--r--src/video_core/rasterizer_interface.h7
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h6
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp261
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h26
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp63
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h23
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h16
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.h28
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h6
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp19
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h21
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp23
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h16
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp20
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h9
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp17
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp92
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h28
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp128
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h13
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h12
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp34
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h16
-rw-r--r--src/video_core/shader/memory_util.cpp7
-rw-r--r--src/video_core/shader/memory_util.h6
-rw-r--r--src/video_core/texture_cache/surface_params.cpp11
-rw-r--r--src/video_core/texture_cache/surface_params.h5
-rw-r--r--src/video_core/texture_cache/texture_cache.h51
-rw-r--r--src/video_core/video_core.cpp11
-rw-r--r--src/yuzu/CMakeLists.txt12
-rw-r--r--src/yuzu/applets/controller.cpp601
-rw-r--r--src/yuzu/applets/controller.h133
-rw-r--r--src/yuzu/applets/controller.ui2672
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/configure_input.cpp22
-rw-r--r--src/yuzu/configuration/configure_input.h2
-rw-r--r--src/yuzu/configuration/configure_input_dialog.cpp37
-rw-r--r--src/yuzu/configuration/configure_input_dialog.h38
-rw-r--r--src/yuzu/configuration/configure_input_dialog.ui57
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp4
-rw-r--r--src/yuzu/main.cpp39
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu_cmd/yuzu.cpp5
-rw-r--r--src/yuzu_tester/yuzu.cpp1
107 files changed, 9801 insertions, 1530 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 5ef38a337..cb00ef60e 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -12,16 +12,32 @@ add_library(audio_core STATIC
12 buffer.h 12 buffer.h
13 codec.cpp 13 codec.cpp
14 codec.h 14 codec.h
15 command_generator.cpp
16 command_generator.h
15 common.h 17 common.h
18 effect_context.cpp
19 effect_context.h
20 info_updater.cpp
21 info_updater.h
22 memory_pool.cpp
23 memory_pool.h
24 mix_context.cpp
25 mix_context.h
16 null_sink.h 26 null_sink.h
17 sink.h 27 sink.h
28 sink_context.cpp
29 sink_context.h
18 sink_details.cpp 30 sink_details.cpp
19 sink_details.h 31 sink_details.h
20 sink_stream.h 32 sink_stream.h
33 splitter_context.cpp
34 splitter_context.h
21 stream.cpp 35 stream.cpp
22 stream.h 36 stream.h
23 time_stretch.cpp 37 time_stretch.cpp
24 time_stretch.h 38 time_stretch.h
39 voice_context.cpp
40 voice_context.h
25 41
26 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> 42 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
27) 43)
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp
index 49ab9d3e1..689a54508 100644
--- a/src/audio_core/algorithm/interpolate.cpp
+++ b/src/audio_core/algorithm/interpolate.cpp
@@ -197,4 +197,36 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
197 return output; 197 return output;
198} 198}
199 199
200void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size_t sample_count) {
201 const std::array<s16, 512>& lut = [pitch] {
202 if (pitch > 0xaaaa) {
203 return curve_lut0;
204 }
205 if (pitch <= 0x8000) {
206 return curve_lut1;
207 }
208 return curve_lut2;
209 }();
210
211 std::size_t index{};
212
213 for (std::size_t i = 0; i < sample_count; i++) {
214 const std::size_t lut_index{(static_cast<std::size_t>(fraction) >> 8) * 4};
215 const auto l0 = lut[lut_index + 0];
216 const auto l1 = lut[lut_index + 1];
217 const auto l2 = lut[lut_index + 2];
218 const auto l3 = lut[lut_index + 3];
219
220 const auto s0 = static_cast<s32>(input[index]);
221 const auto s1 = static_cast<s32>(input[index + 1]);
222 const auto s2 = static_cast<s32>(input[index + 2]);
223 const auto s3 = static_cast<s32>(input[index + 3]);
224
225 output[i] = (l0 * s0 + l1 * s1 + l2 * s2 + l3 * s3) >> 15;
226 fraction += pitch;
227 index += (fraction >> 15);
228 fraction &= 0x7fff;
229 }
230}
231
200} // namespace AudioCore 232} // namespace AudioCore
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h
index ab1a31754..d534077af 100644
--- a/src/audio_core/algorithm/interpolate.h
+++ b/src/audio_core/algorithm/interpolate.h
@@ -38,4 +38,7 @@ inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16>
38 return Interpolate(state, std::move(input), ratio); 38 return Interpolate(state, std::move(input), ratio);
39} 39}
40 40
41/// Nintendo Switchs DSP resampling algorithm. Based on a single channel
42void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size_t sample_count);
43
41} // namespace AudioCore 44} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index d64452617..56dc892b1 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -2,95 +2,49 @@
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 <vector>
5#include "audio_core/algorithm/interpolate.h" 6#include "audio_core/algorithm/interpolate.h"
6#include "audio_core/audio_out.h" 7#include "audio_core/audio_out.h"
7#include "audio_core/audio_renderer.h" 8#include "audio_core/audio_renderer.h"
8#include "audio_core/codec.h" 9#include "audio_core/codec.h"
9#include "audio_core/common.h" 10#include "audio_core/common.h"
11#include "audio_core/info_updater.h"
12#include "audio_core/voice_context.h"
10#include "common/assert.h" 13#include "common/assert.h"
11#include "common/logging/log.h" 14#include "common/logging/log.h"
12#include "core/core.h" 15#include "core/core.h"
13#include "core/hle/kernel/writable_event.h" 16#include "core/hle/kernel/writable_event.h"
14#include "core/memory.h" 17#include "core/memory.h"
18#include "core/settings.h"
15 19
16namespace AudioCore { 20namespace AudioCore {
17
18constexpr u32 STREAM_SAMPLE_RATE{48000};
19constexpr u32 STREAM_NUM_CHANNELS{2};
20using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>;
21class AudioRenderer::VoiceState {
22public:
23 bool IsPlaying() const {
24 return is_in_use && info.play_state == PlayState::Started;
25 }
26
27 const VoiceOutStatus& GetOutStatus() const {
28 return out_status;
29 }
30
31 const VoiceInfo& GetInfo() const {
32 return info;
33 }
34
35 VoiceInfo& GetInfo() {
36 return info;
37 }
38
39 void SetWaveIndex(std::size_t index);
40 std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory,
41 const VoiceChannelHolder& voice_resources);
42 void UpdateState();
43 void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources);
44
45private:
46 bool is_in_use{};
47 bool is_refresh_pending{};
48 std::size_t wave_index{};
49 std::size_t offset{};
50 Codec::ADPCMState adpcm_state{};
51 InterpolationState interp_state{};
52 std::vector<s16> samples;
53 VoiceOutStatus out_status{};
54 VoiceInfo info{};
55};
56
57class AudioRenderer::EffectState {
58public:
59 const EffectOutStatus& GetOutStatus() const {
60 return out_status;
61 }
62
63 const EffectInStatus& GetInfo() const {
64 return info;
65 }
66
67 EffectInStatus& GetInfo() {
68 return info;
69 }
70
71 void UpdateState(Core::Memory::Memory& memory);
72
73private:
74 EffectOutStatus out_status{};
75 EffectInStatus info{};
76};
77
78AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 21AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
79 AudioRendererParameter params, 22 AudioCommon::AudioRendererParameter params,
80 std::shared_ptr<Kernel::WritableEvent> buffer_event, 23 std::shared_ptr<Kernel::WritableEvent> buffer_event,
81 std::size_t instance_number) 24 std::size_t instance_number)
82 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), 25 : worker_params{params}, buffer_event{buffer_event},
83 voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} { 26 memory_pool_info(params.effect_count + params.voice_count * 4),
27 voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
28 sink_context(params.sink_count), splitter_context(),
29 voices(params.voice_count), memory{memory_},
30 command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
31 memory),
32 temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) {
84 behavior_info.SetUserRevision(params.revision); 33 behavior_info.SetUserRevision(params.revision);
34 splitter_context.Initialize(behavior_info, params.splitter_count,
35 params.num_splitter_send_channels);
36 mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
85 audio_out = std::make_unique<AudioCore::AudioOut>(); 37 audio_out = std::make_unique<AudioCore::AudioOut>();
86 stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, 38 stream =
87 fmt::format("AudioRenderer-Instance{}", instance_number), 39 audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
88 [=]() { buffer_event->Signal(); }); 40 fmt::format("AudioRenderer-Instance{}", instance_number),
41 [=]() { buffer_event->Signal(); });
89 audio_out->StartStream(stream); 42 audio_out->StartStream(stream);
90 43
91 QueueMixedBuffer(0); 44 QueueMixedBuffer(0);
92 QueueMixedBuffer(1); 45 QueueMixedBuffer(1);
93 QueueMixedBuffer(2); 46 QueueMixedBuffer(2);
47 QueueMixedBuffer(3);
94} 48}
95 49
96AudioRenderer::~AudioRenderer() = default; 50AudioRenderer::~AudioRenderer() = default;
@@ -111,355 +65,200 @@ Stream::State AudioRenderer::GetStreamState() const {
111 return stream->GetState(); 65 return stream->GetState();
112} 66}
113 67
114ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { 68static constexpr s16 ClampToS16(s32 value) {
115 // Copy UpdateDataHeader struct 69 return static_cast<s16>(std::clamp(value, -32768, 32767));
116 UpdateDataHeader config{}; 70}
117 std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
118 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
119
120 if (!behavior_info.UpdateInput(input_params, sizeof(UpdateDataHeader))) {
121 LOG_ERROR(Audio, "Failed to update behavior info input parameters");
122 return Audren::ERR_INVALID_PARAMETERS;
123 }
124
125 // Copy MemoryPoolInfo structs
126 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
127 std::memcpy(mem_pool_info.data(),
128 input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
129 memory_pool_count * sizeof(MemoryPoolInfo));
130
131 // Copy voice resources
132 const std::size_t voice_resource_offset{sizeof(UpdateDataHeader) + config.behavior_size +
133 config.memory_pools_size};
134 std::memcpy(voice_resources.data(), input_params.data() + voice_resource_offset,
135 sizeof(VoiceResourceInformation) * voice_resources.size());
136
137 // Copy VoiceInfo structs
138 std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
139 config.memory_pools_size + config.voice_resource_size};
140 for (auto& voice : voices) {
141 std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo));
142 voice_offset += sizeof(VoiceInfo);
143 }
144
145 std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size +
146 config.memory_pools_size + config.voice_resource_size +
147 config.voices_size};
148 for (auto& effect : effects) {
149 std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus));
150 effect_offset += sizeof(EffectInStatus);
151 }
152
153 // Update memory pool state
154 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
155 for (std::size_t index = 0; index < memory_pool.size(); ++index) {
156 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
157 memory_pool[index].state = MemoryPoolStates::Attached;
158 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
159 memory_pool[index].state = MemoryPoolStates::Detached;
160 }
161 }
162
163 // Update voices
164 for (auto& voice : voices) {
165 voice.UpdateState();
166 if (!voice.GetInfo().is_in_use) {
167 continue;
168 }
169 if (voice.GetInfo().is_new) {
170 voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
171 }
172 }
173
174 for (auto& effect : effects) {
175 effect.UpdateState(memory);
176 }
177
178 // Release previous buffers and queue next ones for playback
179 ReleaseAndQueueBuffers();
180
181 // Copy output header
182 UpdateDataHeader response_data{worker_params};
183 if (behavior_info.IsElapsedFrameCountSupported()) {
184 response_data.render_info = sizeof(RendererInfo);
185 response_data.total_size += sizeof(RendererInfo);
186 }
187
188 std::vector<u8> output_params(response_data.total_size);
189 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
190
191 // Copy output memory pool entries
192 std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
193 response_data.memory_pools_size);
194
195 // Copy output voice status
196 std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
197 for (const auto& voice : voices) {
198 std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
199 sizeof(VoiceOutStatus));
200 voice_out_status_offset += sizeof(VoiceOutStatus);
201 }
202 71
203 std::size_t effect_out_status_offset{ 72ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
204 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + 73 std::vector<u8>& output_params) {
205 response_data.voice_resource_size};
206 for (const auto& effect : effects) {
207 std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(),
208 sizeof(EffectOutStatus));
209 effect_out_status_offset += sizeof(EffectOutStatus);
210 }
211 74
212 // Update behavior info output 75 InfoUpdater info_updater{input_params, output_params, behavior_info};
213 const std::size_t behavior_out_status_offset{
214 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
215 response_data.effects_size + response_data.sinks_size +
216 response_data.performance_manager_size};
217 76
218 if (!behavior_info.UpdateOutput(output_params, behavior_out_status_offset)) { 77 if (!info_updater.UpdateBehaviorInfo(behavior_info)) {
219 LOG_ERROR(Audio, "Failed to update behavior info output parameters"); 78 LOG_ERROR(Audio, "Failed to update behavior info input parameters");
220 return Audren::ERR_INVALID_PARAMETERS; 79 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
221 } 80 }
222 81
223 if (behavior_info.IsElapsedFrameCountSupported()) { 82 if (!info_updater.UpdateMemoryPools(memory_pool_info)) {
224 const std::size_t renderer_info_offset{ 83 LOG_ERROR(Audio, "Failed to update memory pool parameters");
225 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + 84 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
226 response_data.effects_size + response_data.sinks_size +
227 response_data.performance_manager_size + response_data.behavior_size};
228 RendererInfo renderer_info{};
229 renderer_info.elasped_frame_count = elapsed_frame_count;
230 std::memcpy(output_params.data() + renderer_info_offset, &renderer_info,
231 sizeof(RendererInfo));
232 } 85 }
233 86
234 return MakeResult(output_params); 87 if (!info_updater.UpdateVoiceChannelResources(voice_context)) {
235} 88 LOG_ERROR(Audio, "Failed to update voice channel resource parameters");
236 89 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
237void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
238 wave_index = index & 3;
239 is_refresh_pending = true;
240}
241
242std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(
243 std::size_t sample_count, Core::Memory::Memory& memory,
244 const VoiceChannelHolder& voice_resources) {
245 if (!IsPlaying()) {
246 return {};
247 } 90 }
248 91
249 if (is_refresh_pending) { 92 if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) {
250 RefreshBuffer(memory, voice_resources); 93 LOG_ERROR(Audio, "Failed to update voice parameters");
94 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
251 } 95 }
252 96
253 const std::size_t max_size{samples.size() - offset}; 97 // TODO(ogniK): Deal with stopped audio renderer but updates still taking place
254 const std::size_t dequeue_offset{offset}; 98 if (!info_updater.UpdateEffects(effect_context, true)) {
255 std::size_t size{sample_count * STREAM_NUM_CHANNELS}; 99 LOG_ERROR(Audio, "Failed to update effect parameters");
256 if (size > max_size) { 100 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
257 size = max_size;
258 } 101 }
259 102
260 out_status.played_sample_count += size / STREAM_NUM_CHANNELS; 103 if (behavior_info.IsSplitterSupported()) {
261 offset += size; 104 if (!info_updater.UpdateSplitterInfo(splitter_context)) {
262 105 LOG_ERROR(Audio, "Failed to update splitter parameters");
263 const auto& wave_buffer{info.wave_buffer[wave_index]}; 106 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
264 if (offset == samples.size()) {
265 offset = 0;
266
267 if (!wave_buffer.is_looping && wave_buffer.buffer_sz) {
268 SetWaveIndex(wave_index + 1);
269 }
270
271 if (wave_buffer.buffer_sz) {
272 out_status.wave_buffer_consumed++;
273 }
274
275 if (wave_buffer.end_of_stream || wave_buffer.buffer_sz == 0) {
276 info.play_state = PlayState::Paused;
277 } 107 }
278 } 108 }
279 109
280 return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size}; 110 auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
281} 111 splitter_context, effect_context);
282 112
283void AudioRenderer::VoiceState::UpdateState() { 113 if (mix_result.IsError()) {
284 if (is_in_use && !info.is_in_use) { 114 LOG_ERROR(Audio, "Failed to update mix parameters");
285 // No longer in use, reset state 115 return mix_result;
286 is_refresh_pending = true;
287 wave_index = 0;
288 offset = 0;
289 out_status = {};
290 } 116 }
291 is_in_use = info.is_in_use;
292}
293 117
294void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory, 118 // TODO(ogniK): Sinks
295 const VoiceChannelHolder& voice_resources) { 119 if (!info_updater.UpdateSinks(sink_context)) {
296 const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; 120 LOG_ERROR(Audio, "Failed to update sink parameters");
297 const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; 121 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
298 std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
299 memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size);
300
301 switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
302 case Codec::PcmFormat::Int16: {
303 // PCM16 is played as-is
304 break;
305 }
306 case Codec::PcmFormat::Adpcm: {
307 // Decode ADPCM to PCM16
308 Codec::ADPCM_Coeff coeffs;
309 memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
310 new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
311 new_samples.size() * sizeof(s16), coeffs, adpcm_state);
312 break;
313 } 122 }
314 default:
315 UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
316 break;
317 }
318
319 switch (info.channel_count) {
320 case 1: {
321 // 1 channel is upsampled to 2 channel
322 samples.resize(new_samples.size() * 2);
323 123
324 for (std::size_t index = 0; index < new_samples.size(); ++index) { 124 // TODO(ogniK): Performance buffer
325 auto sample = static_cast<float>(new_samples[index]); 125 if (!info_updater.UpdatePerformanceBuffer()) {
326 if (voice_resources[0]->in_use) { 126 LOG_ERROR(Audio, "Failed to update performance buffer parameters");
327 sample *= voice_resources[0]->mix_volumes[0]; 127 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
328 }
329
330 samples[index * 2] = static_cast<s16>(sample * info.volume);
331 samples[index * 2 + 1] = static_cast<s16>(sample * info.volume);
332 }
333 break;
334 } 128 }
335 case 2: {
336 // 2 channel is played as is
337 samples = std::move(new_samples);
338 const std::size_t sample_count = (samples.size() / 2);
339 for (std::size_t index = 0; index < sample_count; ++index) {
340 const std::size_t index_l = index * 2;
341 const std::size_t index_r = index * 2 + 1;
342
343 auto sample_l = static_cast<float>(samples[index_l]);
344 auto sample_r = static_cast<float>(samples[index_r]);
345
346 if (voice_resources[0]->in_use) {
347 sample_l *= voice_resources[0]->mix_volumes[0];
348 }
349
350 if (voice_resources[1]->in_use) {
351 sample_r *= voice_resources[1]->mix_volumes[1];
352 }
353 129
354 samples[index_l] = static_cast<s16>(sample_l * info.volume); 130 if (!info_updater.UpdateErrorInfo(behavior_info)) {
355 samples[index_r] = static_cast<s16>(sample_r * info.volume); 131 LOG_ERROR(Audio, "Failed to update error info");
356 } 132 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
357 break;
358 } 133 }
359 case 6: {
360 samples.resize((new_samples.size() / 6) * 2);
361 const std::size_t sample_count = samples.size() / 2;
362
363 for (std::size_t index = 0; index < sample_count; ++index) {
364 auto FL = static_cast<float>(new_samples[index * 6]);
365 auto FR = static_cast<float>(new_samples[index * 6 + 1]);
366 auto FC = static_cast<float>(new_samples[index * 6 + 2]);
367 auto BL = static_cast<float>(new_samples[index * 6 + 4]);
368 auto BR = static_cast<float>(new_samples[index * 6 + 5]);
369
370 if (voice_resources[0]->in_use) {
371 FL *= voice_resources[0]->mix_volumes[0];
372 }
373 if (voice_resources[1]->in_use) {
374 FR *= voice_resources[1]->mix_volumes[1];
375 }
376 if (voice_resources[2]->in_use) {
377 FC *= voice_resources[2]->mix_volumes[2];
378 }
379 if (voice_resources[4]->in_use) {
380 BL *= voice_resources[4]->mix_volumes[4];
381 }
382 if (voice_resources[5]->in_use) {
383 BR *= voice_resources[5]->mix_volumes[5];
384 }
385 134
386 samples[index * 2] = 135 if (behavior_info.IsElapsedFrameCountSupported()) {
387 static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume); 136 if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) {
388 samples[index * 2 + 1] = 137 LOG_ERROR(Audio, "Failed to update renderer info");
389 static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume); 138 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
390 } 139 }
391 break;
392 }
393 default:
394 UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
395 break;
396 } 140 }
141 // TODO(ogniK): Statistics
397 142
398 // Only interpolate when necessary, expensive. 143 if (!info_updater.WriteOutputHeader()) {
399 if (GetInfo().sample_rate != STREAM_SAMPLE_RATE) { 144 LOG_ERROR(Audio, "Failed to write output header");
400 samples = Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, 145 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
401 STREAM_SAMPLE_RATE);
402 } 146 }
403 147
404 is_refresh_pending = false; 148 // TODO(ogniK): Check when all sections are implemented
405}
406 149
407void AudioRenderer::EffectState::UpdateState(Core::Memory::Memory& memory) { 150 if (!info_updater.CheckConsumedSize()) {
408 if (info.is_new) { 151 LOG_ERROR(Audio, "Audio buffers were not consumed!");
409 out_status.state = EffectStatus::New; 152 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
410 } else {
411 if (info.type == Effect::Aux) {
412 ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0,
413 "Aux buffers tried to update");
414 ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0,
415 "Aux buffers tried to update");
416 ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0,
417 "Aux buffers tried to update");
418 ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0,
419 "Aux buffers tried to update");
420 }
421 } 153 }
422}
423 154
424static constexpr s16 ClampToS16(s32 value) { 155 ReleaseAndQueueBuffers();
425 return static_cast<s16>(std::clamp(value, -32768, 32767)); 156
157 return RESULT_SUCCESS;
426} 158}
427 159
428void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { 160void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
429 constexpr std::size_t BUFFER_SIZE{512}; 161 command_generator.PreCommand();
162 // Clear mix buffers before our next operation
163 command_generator.ClearMixBuffers();
164
165 // If the splitter is not in use, sort our mixes
166 if (!splitter_context.UsingSplitter()) {
167 mix_context.SortInfo();
168 }
169 // Sort our voices
170 voice_context.SortInfo();
171
172 // Handle samples
173 command_generator.GenerateVoiceCommands();
174 command_generator.GenerateSubMixCommands();
175 command_generator.GenerateFinalMixCommands();
176
177 command_generator.PostCommand();
178 // Base sample size
179 std::size_t BUFFER_SIZE{worker_params.sample_count};
180 // Samples
430 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); 181 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
431 182 // Make sure to clear our samples
432 for (auto& voice : voices) { 183 std::memset(buffer.data(), 0, buffer.size() * sizeof(s16));
433 if (!voice.IsPlaying()) { 184
434 continue; 185 if (sink_context.InUse()) {
435 } 186 const auto stream_channel_count = stream->GetNumChannels();
436 VoiceChannelHolder resources{}; 187 const auto buffer_offsets = sink_context.OutputBuffers();
437 for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) { 188 const auto channel_count = buffer_offsets.size();
438 const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel]; 189 const auto& final_mix = mix_context.GetFinalMixInfo();
439 resources[channel] = &voice_resources[channel_resource_id]; 190 const auto& in_params = final_mix.GetInParams();
191 std::vector<s32*> mix_buffers(channel_count);
192 for (std::size_t i = 0; i < channel_count; i++) {
193 mix_buffers[i] =
194 command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]);
440 } 195 }
441 196
442 std::size_t offset{}; 197 for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
443 s64 samples_remaining{BUFFER_SIZE}; 198 if (channel_count == 1) {
444 while (samples_remaining > 0) { 199 const auto sample = ClampToS16(mix_buffers[0][i]);
445 const std::vector<s16> samples{ 200 buffer[i * stream_channel_count + 0] = sample;
446 voice.DequeueSamples(samples_remaining, memory, resources)}; 201 if (stream_channel_count > 1) {
447 202 buffer[i * stream_channel_count + 1] = sample;
448 if (samples.empty()) { 203 }
449 break; 204 if (stream_channel_count == 6) {
450 } 205 buffer[i * stream_channel_count + 2] = sample;
451 206 buffer[i * stream_channel_count + 4] = sample;
452 samples_remaining -= samples.size() / stream->GetNumChannels(); 207 buffer[i * stream_channel_count + 5] = sample;
453 208 }
454 for (const auto& sample : samples) { 209 } else if (channel_count == 2) {
455 const s32 buffer_sample{buffer[offset]}; 210 const auto l_sample = ClampToS16(mix_buffers[0][i]);
456 buffer[offset++] = 211 const auto r_sample = ClampToS16(mix_buffers[1][i]);
457 ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume)); 212 if (stream_channel_count == 1) {
213 buffer[i * stream_channel_count + 0] = l_sample;
214 } else if (stream_channel_count == 2) {
215 buffer[i * stream_channel_count + 0] = l_sample;
216 buffer[i * stream_channel_count + 1] = r_sample;
217 } else if (stream_channel_count == 6) {
218 buffer[i * stream_channel_count + 0] = l_sample;
219 buffer[i * stream_channel_count + 1] = r_sample;
220
221 buffer[i * stream_channel_count + 2] =
222 ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2);
223
224 buffer[i * stream_channel_count + 4] = l_sample;
225 buffer[i * stream_channel_count + 5] = r_sample;
226 }
227
228 } else if (channel_count == 6) {
229 const auto fl_sample = ClampToS16(mix_buffers[0][i]);
230 const auto fr_sample = ClampToS16(mix_buffers[1][i]);
231 const auto fc_sample = ClampToS16(mix_buffers[2][i]);
232 const auto lf_sample = ClampToS16(mix_buffers[3][i]);
233 const auto bl_sample = ClampToS16(mix_buffers[4][i]);
234 const auto br_sample = ClampToS16(mix_buffers[5][i]);
235
236 if (stream_channel_count == 1) {
237 buffer[i * stream_channel_count + 0] = fc_sample;
238 } else if (stream_channel_count == 2) {
239 buffer[i * stream_channel_count + 0] =
240 static_cast<s16>(0.3694f * static_cast<float>(fl_sample) +
241 0.2612f * static_cast<float>(fc_sample) +
242 0.3694f * static_cast<float>(bl_sample));
243 buffer[i * stream_channel_count + 1] =
244 static_cast<s16>(0.3694f * static_cast<float>(fr_sample) +
245 0.2612f * static_cast<float>(fc_sample) +
246 0.3694f * static_cast<float>(br_sample));
247 } else if (stream_channel_count == 6) {
248 buffer[i * stream_channel_count + 0] = fl_sample;
249 buffer[i * stream_channel_count + 1] = fr_sample;
250 buffer[i * stream_channel_count + 2] = fc_sample;
251 buffer[i * stream_channel_count + 3] = lf_sample;
252 buffer[i * stream_channel_count + 4] = bl_sample;
253 buffer[i * stream_channel_count + 5] = br_sample;
254 }
458 } 255 }
459 } 256 }
460 } 257 }
258
461 audio_out->QueueBuffer(stream, tag, std::move(buffer)); 259 audio_out->QueueBuffer(stream, tag, std::move(buffer));
462 elapsed_frame_count++; 260 elapsed_frame_count++;
261 voice_context.UpdateStateByDspShared();
463} 262}
464 263
465void AudioRenderer::ReleaseAndQueueBuffers() { 264void AudioRenderer::ReleaseAndQueueBuffers() {
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index f0b691a86..2bca795ba 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -9,8 +9,15 @@
9#include <vector> 9#include <vector>
10 10
11#include "audio_core/behavior_info.h" 11#include "audio_core/behavior_info.h"
12#include "audio_core/command_generator.h"
12#include "audio_core/common.h" 13#include "audio_core/common.h"
14#include "audio_core/effect_context.h"
15#include "audio_core/memory_pool.h"
16#include "audio_core/mix_context.h"
17#include "audio_core/sink_context.h"
18#include "audio_core/splitter_context.h"
13#include "audio_core/stream.h" 19#include "audio_core/stream.h"
20#include "audio_core/voice_context.h"
14#include "common/common_funcs.h" 21#include "common/common_funcs.h"
15#include "common/common_types.h" 22#include "common/common_types.h"
16#include "common/swap.h" 23#include "common/swap.h"
@@ -30,220 +37,25 @@ class Memory;
30} 37}
31 38
32namespace AudioCore { 39namespace AudioCore {
40using DSPStateHolder = std::array<VoiceState*, 6>;
33 41
34class AudioOut; 42class AudioOut;
35 43
36enum class PlayState : u8 {
37 Started = 0,
38 Stopped = 1,
39 Paused = 2,
40};
41
42enum class Effect : u8 {
43 None = 0,
44 Aux = 2,
45};
46
47enum class EffectStatus : u8 {
48 None = 0,
49 New = 1,
50};
51
52struct AudioRendererParameter {
53 u32_le sample_rate;
54 u32_le sample_count;
55 u32_le mix_buffer_count;
56 u32_le submix_count;
57 u32_le voice_count;
58 u32_le sink_count;
59 u32_le effect_count;
60 u32_le performance_frame_count;
61 u8 is_voice_drop_enabled;
62 u8 unknown_21;
63 u8 unknown_22;
64 u8 execution_mode;
65 u32_le splitter_count;
66 u32_le num_splitter_send_channels;
67 u32_le unknown_30;
68 u32_le revision;
69};
70static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
71
72enum class MemoryPoolStates : u32 { // Should be LE
73 Invalid = 0x0,
74 Unknown = 0x1,
75 RequestDetach = 0x2,
76 Detached = 0x3,
77 RequestAttach = 0x4,
78 Attached = 0x5,
79 Released = 0x6,
80};
81
82struct MemoryPoolEntry {
83 MemoryPoolStates state;
84 u32_le unknown_4;
85 u32_le unknown_8;
86 u32_le unknown_c;
87};
88static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
89
90struct MemoryPoolInfo {
91 u64_le pool_address;
92 u64_le pool_size;
93 MemoryPoolStates pool_state;
94 INSERT_PADDING_WORDS(3); // Unknown
95};
96static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
97struct BiquadFilter {
98 u8 enable;
99 INSERT_PADDING_BYTES(1);
100 std::array<s16_le, 3> numerator;
101 std::array<s16_le, 2> denominator;
102};
103static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
104
105struct WaveBuffer {
106 u64_le buffer_addr;
107 u64_le buffer_sz;
108 s32_le start_sample_offset;
109 s32_le end_sample_offset;
110 u8 is_looping;
111 u8 end_of_stream;
112 u8 sent_to_server;
113 INSERT_PADDING_BYTES(5);
114 u64 context_addr;
115 u64 context_sz;
116 INSERT_PADDING_BYTES(8);
117};
118static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
119
120struct VoiceResourceInformation {
121 s32_le id{};
122 std::array<float_le, MAX_MIX_BUFFERS> mix_volumes{};
123 bool in_use{};
124 INSERT_PADDING_BYTES(11);
125};
126static_assert(sizeof(VoiceResourceInformation) == 0x70, "VoiceResourceInformation has wrong size");
127
128struct VoiceInfo {
129 u32_le id;
130 u32_le node_id;
131 u8 is_new;
132 u8 is_in_use;
133 PlayState play_state;
134 u8 sample_format;
135 u32_le sample_rate;
136 u32_le priority;
137 u32_le sorting_order;
138 u32_le channel_count;
139 float_le pitch;
140 float_le volume;
141 std::array<BiquadFilter, 2> biquad_filter;
142 u32_le wave_buffer_count;
143 u32_le wave_buffer_head;
144 INSERT_PADDING_WORDS(1);
145 u64_le additional_params_addr;
146 u64_le additional_params_sz;
147 u32_le mix_id;
148 u32_le splitter_info_id;
149 std::array<WaveBuffer, 4> wave_buffer;
150 std::array<u32_le, 6> voice_channel_resource_ids;
151 INSERT_PADDING_BYTES(24);
152};
153static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
154
155struct VoiceOutStatus {
156 u64_le played_sample_count;
157 u32_le wave_buffer_consumed;
158 u32_le voice_drops_count;
159};
160static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
161
162struct AuxInfo {
163 std::array<u8, 24> input_mix_buffers;
164 std::array<u8, 24> output_mix_buffers;
165 u32_le mix_buffer_count;
166 u32_le sample_rate; // Stored in the aux buffer currently
167 u32_le sample_count;
168 u64_le send_buffer_info;
169 u64_le send_buffer_base;
170
171 u64_le return_buffer_info;
172 u64_le return_buffer_base;
173};
174static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
175
176struct EffectInStatus {
177 Effect type;
178 u8 is_new;
179 u8 is_enabled;
180 INSERT_PADDING_BYTES(1);
181 u32_le mix_id;
182 u64_le buffer_base;
183 u64_le buffer_sz;
184 s32_le priority;
185 INSERT_PADDING_BYTES(4);
186 union {
187 std::array<u8, 0xa0> raw;
188 AuxInfo aux_info;
189 };
190};
191static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size");
192
193struct EffectOutStatus {
194 EffectStatus state;
195 INSERT_PADDING_BYTES(0xf);
196};
197static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
198
199struct RendererInfo { 44struct RendererInfo {
200 u64_le elasped_frame_count{}; 45 u64_le elasped_frame_count{};
201 INSERT_PADDING_WORDS(2); 46 INSERT_PADDING_WORDS(2);
202}; 47};
203static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size"); 48static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
204 49
205struct UpdateDataHeader {
206 UpdateDataHeader() {}
207
208 explicit UpdateDataHeader(const AudioRendererParameter& config) {
209 revision = Common::MakeMagic('R', 'E', 'V', '8'); // 9.2.0 Revision
210 behavior_size = 0xb0;
211 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
212 voices_size = config.voice_count * 0x10;
213 voice_resource_size = 0x0;
214 effects_size = config.effect_count * 0x10;
215 mixes_size = 0x0;
216 sinks_size = config.sink_count * 0x20;
217 performance_manager_size = 0x10;
218 render_info = 0;
219 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
220 effects_size + sinks_size + performance_manager_size;
221 }
222
223 u32_le revision{};
224 u32_le behavior_size{};
225 u32_le memory_pools_size{};
226 u32_le voices_size{};
227 u32_le voice_resource_size{};
228 u32_le effects_size{};
229 u32_le mixes_size{};
230 u32_le sinks_size{};
231 u32_le performance_manager_size{};
232 u32_le splitter_size{};
233 u32_le render_info{};
234 INSERT_PADDING_WORDS(4);
235 u32_le total_size{};
236};
237static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
238
239class AudioRenderer { 50class AudioRenderer {
240public: 51public:
241 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 52 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
242 AudioRendererParameter params, 53 AudioCommon::AudioRendererParameter params,
243 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number); 54 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
244 ~AudioRenderer(); 55 ~AudioRenderer();
245 56
246 ResultVal<std::vector<u8>> UpdateAudioRenderer(const std::vector<u8>& input_params); 57 ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
58 std::vector<u8>& output_params);
247 void QueueMixedBuffer(Buffer::Tag tag); 59 void QueueMixedBuffer(Buffer::Tag tag);
248 void ReleaseAndQueueBuffers(); 60 void ReleaseAndQueueBuffers();
249 u32 GetSampleRate() const; 61 u32 GetSampleRate() const;
@@ -252,19 +64,23 @@ public:
252 Stream::State GetStreamState() const; 64 Stream::State GetStreamState() const;
253 65
254private: 66private:
255 class EffectState;
256 class VoiceState;
257 BehaviorInfo behavior_info{}; 67 BehaviorInfo behavior_info{};
258 68
259 AudioRendererParameter worker_params; 69 AudioCommon::AudioRendererParameter worker_params;
260 std::shared_ptr<Kernel::WritableEvent> buffer_event; 70 std::shared_ptr<Kernel::WritableEvent> buffer_event;
71 std::vector<ServerMemoryPoolInfo> memory_pool_info;
72 VoiceContext voice_context;
73 EffectContext effect_context;
74 MixContext mix_context;
75 SinkContext sink_context;
76 SplitterContext splitter_context;
261 std::vector<VoiceState> voices; 77 std::vector<VoiceState> voices;
262 std::vector<VoiceResourceInformation> voice_resources;
263 std::vector<EffectState> effects;
264 std::unique_ptr<AudioOut> audio_out; 78 std::unique_ptr<AudioOut> audio_out;
265 StreamPtr stream; 79 StreamPtr stream;
266 Core::Memory::Memory& memory; 80 Core::Memory::Memory& memory;
81 CommandGenerator command_generator;
267 std::size_t elapsed_frame_count{}; 82 std::size_t elapsed_frame_count{};
83 std::vector<s32> temp_mix_buffer{};
268}; 84};
269 85
270} // namespace AudioCore 86} // namespace AudioCore
diff --git a/src/audio_core/behavior_info.cpp b/src/audio_core/behavior_info.cpp
index 94b7a3bf1..5d62adb0b 100644
--- a/src/audio_core/behavior_info.cpp
+++ b/src/audio_core/behavior_info.cpp
@@ -9,39 +9,11 @@
9 9
10namespace AudioCore { 10namespace AudioCore {
11 11
12BehaviorInfo::BehaviorInfo() : process_revision(CURRENT_PROCESS_REVISION) {} 12BehaviorInfo::BehaviorInfo() : process_revision(AudioCommon::CURRENT_PROCESS_REVISION) {}
13BehaviorInfo::~BehaviorInfo() = default; 13BehaviorInfo::~BehaviorInfo() = default;
14 14
15bool BehaviorInfo::UpdateInput(const std::vector<u8>& buffer, std::size_t offset) {
16 if (!CanConsumeBuffer(buffer.size(), offset, sizeof(InParams))) {
17 LOG_ERROR(Audio, "Buffer is an invalid size!");
18 return false;
19 }
20 InParams params{};
21 std::memcpy(&params, buffer.data() + offset, sizeof(InParams));
22
23 if (!IsValidRevision(params.revision)) {
24 LOG_ERROR(Audio, "Invalid input revision, revision=0x{:08X}", params.revision);
25 return false;
26 }
27
28 if (user_revision != params.revision) {
29 LOG_ERROR(Audio,
30 "User revision differs from input revision, expecting 0x{:08X} but got 0x{:08X}",
31 user_revision, params.revision);
32 return false;
33 }
34
35 ClearError();
36 UpdateFlags(params.flags);
37
38 // TODO(ogniK): Check input params size when InfoUpdater is used
39
40 return true;
41}
42
43bool BehaviorInfo::UpdateOutput(std::vector<u8>& buffer, std::size_t offset) { 15bool BehaviorInfo::UpdateOutput(std::vector<u8>& buffer, std::size_t offset) {
44 if (!CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) { 16 if (!AudioCommon::CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) {
45 LOG_ERROR(Audio, "Buffer is an invalid size!"); 17 LOG_ERROR(Audio, "Buffer is an invalid size!");
46 return false; 18 return false;
47 } 19 }
@@ -65,36 +37,69 @@ void BehaviorInfo::SetUserRevision(u32_le revision) {
65 user_revision = revision; 37 user_revision = revision;
66} 38}
67 39
40u32_le BehaviorInfo::GetUserRevision() const {
41 return user_revision;
42}
43
44u32_le BehaviorInfo::GetProcessRevision() const {
45 return process_revision;
46}
47
68bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const { 48bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const {
69 return IsRevisionSupported(2, user_revision); 49 return AudioCommon::IsRevisionSupported(2, user_revision);
70} 50}
71 51
72bool BehaviorInfo::IsSplitterSupported() const { 52bool BehaviorInfo::IsSplitterSupported() const {
73 return IsRevisionSupported(2, user_revision); 53 return AudioCommon::IsRevisionSupported(2, user_revision);
74} 54}
75 55
76bool BehaviorInfo::IsLongSizePreDelaySupported() const { 56bool BehaviorInfo::IsLongSizePreDelaySupported() const {
77 return IsRevisionSupported(3, user_revision); 57 return AudioCommon::IsRevisionSupported(3, user_revision);
78} 58}
79 59
80bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const { 60bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const {
81 return IsRevisionSupported(5, user_revision); 61 return AudioCommon::IsRevisionSupported(5, user_revision);
82} 62}
83 63
84bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const { 64bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const {
85 return IsRevisionSupported(4, user_revision); 65 return AudioCommon::IsRevisionSupported(4, user_revision);
86} 66}
87 67
88bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const { 68bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const {
89 return IsRevisionSupported(1, user_revision); 69 return AudioCommon::IsRevisionSupported(1, user_revision);
90} 70}
91 71
92bool BehaviorInfo::IsElapsedFrameCountSupported() const { 72bool BehaviorInfo::IsElapsedFrameCountSupported() const {
93 return IsRevisionSupported(5, user_revision); 73 return AudioCommon::IsRevisionSupported(5, user_revision);
94} 74}
95 75
96bool BehaviorInfo::IsMemoryPoolForceMappingEnabled() const { 76bool BehaviorInfo::IsMemoryPoolForceMappingEnabled() const {
97 return (flags & 1) != 0; 77 return (flags & 1) != 0;
98} 78}
99 79
80bool BehaviorInfo::IsFlushVoiceWaveBuffersSupported() const {
81 return AudioCommon::IsRevisionSupported(5, user_revision);
82}
83
84bool BehaviorInfo::IsVoicePlayedSampleCountResetAtLoopPointSupported() const {
85 return AudioCommon::IsRevisionSupported(5, user_revision);
86}
87
88bool BehaviorInfo::IsVoicePitchAndSrcSkippedSupported() const {
89 return AudioCommon::IsRevisionSupported(5, user_revision);
90}
91
92bool BehaviorInfo::IsMixInParameterDirtyOnlyUpdateSupported() const {
93 return AudioCommon::IsRevisionSupported(7, user_revision);
94}
95
96bool BehaviorInfo::IsSplitterBugFixed() const {
97 return AudioCommon::IsRevisionSupported(5, user_revision);
98}
99
100void BehaviorInfo::CopyErrorInfo(BehaviorInfo::OutParams& dst) {
101 dst.error_count = static_cast<u32>(error_count);
102 std::copy(errors.begin(), errors.begin() + error_count, dst.errors.begin());
103}
104
100} // namespace AudioCore 105} // namespace AudioCore
diff --git a/src/audio_core/behavior_info.h b/src/audio_core/behavior_info.h
index c5e91ab39..50948e8df 100644
--- a/src/audio_core/behavior_info.h
+++ b/src/audio_core/behavior_info.h
@@ -14,15 +14,37 @@
14namespace AudioCore { 14namespace AudioCore {
15class BehaviorInfo { 15class BehaviorInfo {
16public: 16public:
17 struct ErrorInfo {
18 u32_le result{};
19 INSERT_PADDING_WORDS(1);
20 u64_le result_info{};
21 };
22 static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size");
23
24 struct InParams {
25 u32_le revision{};
26 u32_le padding{};
27 u64_le flags{};
28 };
29 static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size");
30
31 struct OutParams {
32 std::array<ErrorInfo, 10> errors{};
33 u32_le error_count{};
34 INSERT_PADDING_BYTES(12);
35 };
36 static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size");
37
17 explicit BehaviorInfo(); 38 explicit BehaviorInfo();
18 ~BehaviorInfo(); 39 ~BehaviorInfo();
19 40
20 bool UpdateInput(const std::vector<u8>& buffer, std::size_t offset);
21 bool UpdateOutput(std::vector<u8>& buffer, std::size_t offset); 41 bool UpdateOutput(std::vector<u8>& buffer, std::size_t offset);
22 42
23 void ClearError(); 43 void ClearError();
24 void UpdateFlags(u64_le dest_flags); 44 void UpdateFlags(u64_le dest_flags);
25 void SetUserRevision(u32_le revision); 45 void SetUserRevision(u32_le revision);
46 u32_le GetUserRevision() const;
47 u32_le GetProcessRevision() const;
26 48
27 bool IsAdpcmLoopContextBugFixed() const; 49 bool IsAdpcmLoopContextBugFixed() const;
28 bool IsSplitterSupported() const; 50 bool IsSplitterSupported() const;
@@ -32,35 +54,19 @@ public:
32 bool IsAudioRenererProcessingTimeLimit70PercentSupported() const; 54 bool IsAudioRenererProcessingTimeLimit70PercentSupported() const;
33 bool IsElapsedFrameCountSupported() const; 55 bool IsElapsedFrameCountSupported() const;
34 bool IsMemoryPoolForceMappingEnabled() const; 56 bool IsMemoryPoolForceMappingEnabled() const;
57 bool IsFlushVoiceWaveBuffersSupported() const;
58 bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
59 bool IsVoicePitchAndSrcSkippedSupported() const;
60 bool IsMixInParameterDirtyOnlyUpdateSupported() const;
61 bool IsSplitterBugFixed() const;
62 void CopyErrorInfo(OutParams& dst);
35 63
36private: 64private:
37 u32_le process_revision{}; 65 u32_le process_revision{};
38 u32_le user_revision{}; 66 u32_le user_revision{};
39 u64_le flags{}; 67 u64_le flags{};
40
41 struct ErrorInfo {
42 u32_le result{};
43 INSERT_PADDING_WORDS(1);
44 u64_le result_info{};
45 };
46 static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size");
47
48 std::array<ErrorInfo, 10> errors{}; 68 std::array<ErrorInfo, 10> errors{};
49 std::size_t error_count{}; 69 std::size_t error_count{};
50
51 struct InParams {
52 u32_le revision{};
53 u32_le padding{};
54 u64_le flags{};
55 };
56 static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size");
57
58 struct OutParams {
59 std::array<ErrorInfo, 10> errors{};
60 u32_le error_count{};
61 INSERT_PADDING_BYTES(12);
62 };
63 static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size");
64}; 70};
65 71
66} // namespace AudioCore 72} // namespace AudioCore
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
new file mode 100644
index 000000000..84782cde6
--- /dev/null
+++ b/src/audio_core/command_generator.cpp
@@ -0,0 +1,976 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/algorithm/interpolate.h"
6#include "audio_core/command_generator.h"
7#include "audio_core/effect_context.h"
8#include "audio_core/mix_context.h"
9#include "audio_core/voice_context.h"
10#include "core/memory.h"
11
12namespace AudioCore {
13namespace {
14constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
15constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
16
17template <std::size_t N>
18void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
19 for (std::size_t i = 0; i < static_cast<std::size_t>(sample_count); i += N) {
20 for (std::size_t j = 0; j < N; j++) {
21 output[i + j] +=
22 static_cast<s32>((static_cast<s64>(input[i + j]) * gain + 0x4000) >> 15);
23 }
24 }
25}
26
27s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sample_count) {
28 s32 x = 0;
29 for (s32 i = 0; i < sample_count; i++) {
30 x = static_cast<s32>(static_cast<float>(input[i]) * gain);
31 output[i] += x;
32 gain += delta;
33 }
34 return x;
35}
36
37void ApplyGain(s32* output, const s32* input, s32 gain, s32 delta, s32 sample_count) {
38 for (s32 i = 0; i < sample_count; i++) {
39 output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
40 gain += delta;
41 }
42}
43
44void ApplyGainWithoutDelta(s32* output, const s32* input, s32 gain, s32 sample_count) {
45 for (s32 i = 0; i < sample_count; i++) {
46 output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
47 }
48}
49
50s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
51 const bool positive = first_sample > 0;
52 auto final_sample = std::abs(first_sample);
53 for (s32 i = 0; i < sample_count; i++) {
54 final_sample = static_cast<s32>((static_cast<s64>(final_sample) * delta) >> 15);
55 if (positive) {
56 output[i] += final_sample;
57 } else {
58 output[i] -= final_sample;
59 }
60 }
61 if (positive) {
62 return final_sample;
63 } else {
64 return -final_sample;
65 }
66}
67
68} // namespace
69
70CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
71 VoiceContext& voice_context, MixContext& mix_context,
72 SplitterContext& splitter_context, EffectContext& effect_context,
73 Core::Memory::Memory& memory)
74 : worker_params(worker_params), voice_context(voice_context), mix_context(mix_context),
75 splitter_context(splitter_context), effect_context(effect_context), memory(memory),
76 mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
77 worker_params.sample_count),
78 sample_buffer(MIX_BUFFER_SIZE),
79 depop_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
80 worker_params.sample_count) {}
81CommandGenerator::~CommandGenerator() = default;
82
83void CommandGenerator::ClearMixBuffers() {
84 std::fill(mix_buffer.begin(), mix_buffer.end(), 0);
85 std::fill(sample_buffer.begin(), sample_buffer.end(), 0);
86 // std::fill(depop_buffer.begin(), depop_buffer.end(), 0);
87}
88
89void CommandGenerator::GenerateVoiceCommands() {
90 if (dumping_frame) {
91 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateVoiceCommands");
92 }
93 // Grab all our voices
94 const auto voice_count = voice_context.GetVoiceCount();
95 for (std::size_t i = 0; i < voice_count; i++) {
96 auto& voice_info = voice_context.GetSortedInfo(i);
97 // Update voices and check if we should queue them
98 if (voice_info.ShouldSkip() || !voice_info.UpdateForCommandGeneration(voice_context)) {
99 continue;
100 }
101
102 // Queue our voice
103 GenerateVoiceCommand(voice_info);
104 }
105 // Update our splitters
106 splitter_context.UpdateInternalState();
107}
108
109void CommandGenerator::GenerateVoiceCommand(ServerVoiceInfo& voice_info) {
110 auto& in_params = voice_info.GetInParams();
111 const auto channel_count = in_params.channel_count;
112
113 for (s32 channel = 0; channel < channel_count; channel++) {
114 const auto resource_id = in_params.voice_channel_resource_id[channel];
115 auto& dsp_state = voice_context.GetDspSharedState(resource_id);
116 auto& channel_resource = voice_context.GetChannelResource(resource_id);
117
118 // Decode our samples for our channel
119 GenerateDataSourceCommand(voice_info, dsp_state, channel);
120
121 if (in_params.should_depop) {
122 in_params.last_volume = 0.0f;
123 } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER ||
124 in_params.mix_id != AudioCommon::NO_MIX) {
125 // Apply a biquad filter if needed
126 GenerateBiquadFilterCommandForVoice(voice_info, dsp_state,
127 worker_params.mix_buffer_count, channel);
128 // Base voice volume ramping
129 GenerateVolumeRampCommand(in_params.last_volume, in_params.volume, channel,
130 in_params.node_id);
131 in_params.last_volume = in_params.volume;
132
133 if (in_params.mix_id != AudioCommon::NO_MIX) {
134 // If we're using a mix id
135 auto& mix_info = mix_context.GetInfo(in_params.mix_id);
136 const auto& dest_mix_params = mix_info.GetInParams();
137
138 // Voice Mixing
139 GenerateVoiceMixCommand(
140 channel_resource.GetCurrentMixVolume(), channel_resource.GetLastMixVolume(),
141 dsp_state, dest_mix_params.buffer_offset, dest_mix_params.buffer_count,
142 worker_params.mix_buffer_count + channel, in_params.node_id);
143
144 // Update last mix volumes
145 channel_resource.UpdateLastMixVolumes();
146 } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) {
147 s32 base = channel;
148 while (auto* destination_data =
149 GetDestinationData(in_params.splitter_info_id, base)) {
150 base += channel_count;
151
152 if (!destination_data->IsConfigured()) {
153 continue;
154 }
155 if (destination_data->GetMixId() >= mix_context.GetCount()) {
156 continue;
157 }
158
159 const auto& mix_info = mix_context.GetInfo(destination_data->GetMixId());
160 const auto& dest_mix_params = mix_info.GetInParams();
161 GenerateVoiceMixCommand(
162 destination_data->CurrentMixVolumes(), destination_data->LastMixVolumes(),
163 dsp_state, dest_mix_params.buffer_offset, dest_mix_params.buffer_count,
164 worker_params.mix_buffer_count + channel, in_params.node_id);
165 destination_data->MarkDirty();
166 }
167 }
168 // Update biquad filter enabled states
169 for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
170 in_params.was_biquad_filter_enabled[i] = in_params.biquad_filter[i].enabled;
171 }
172 }
173 }
174}
175
176void CommandGenerator::GenerateSubMixCommands() {
177 const auto mix_count = mix_context.GetCount();
178 for (std::size_t i = 0; i < mix_count; i++) {
179 auto& mix_info = mix_context.GetSortedInfo(i);
180 const auto& in_params = mix_info.GetInParams();
181 if (!in_params.in_use || in_params.mix_id == AudioCommon::FINAL_MIX) {
182 continue;
183 }
184 GenerateSubMixCommand(mix_info);
185 }
186}
187
188void CommandGenerator::GenerateFinalMixCommands() {
189 GenerateFinalMixCommand();
190}
191
192void CommandGenerator::PreCommand() {
193 if (!dumping_frame) {
194 return;
195 }
196 for (std::size_t i = 0; i < splitter_context.GetInfoCount(); i++) {
197 const auto& base = splitter_context.GetInfo(i);
198 std::string graph = fmt::format("b[{}]", i);
199 auto* head = base.GetHead();
200 while (head != nullptr) {
201 graph += fmt::format("->{}", head->GetMixId());
202 head = head->GetNextDestination();
203 }
204 LOG_DEBUG(Audio, "(DSP_TRACE) SplitterGraph splitter_info={}, {}", i, graph);
205 }
206}
207
208void CommandGenerator::PostCommand() {
209 if (!dumping_frame) {
210 return;
211 }
212 dumping_frame = false;
213}
214
215void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
216 s32 channel) {
217 auto& in_params = voice_info.GetInParams();
218 const auto depop = in_params.should_depop;
219
220 if (depop) {
221 if (in_params.mix_id != AudioCommon::NO_MIX) {
222 auto& mix_info = mix_context.GetInfo(in_params.mix_id);
223 const auto& mix_in = mix_info.GetInParams();
224 GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset);
225 } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) {
226 s32 index{};
227 while (const auto* destination =
228 GetDestinationData(in_params.splitter_info_id, index++)) {
229 if (!destination->IsConfigured()) {
230 continue;
231 }
232 auto& mix_info = mix_context.GetInfo(destination->GetMixId());
233 const auto& mix_in = mix_info.GetInParams();
234 GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset);
235 }
236 }
237 } else {
238 switch (in_params.sample_format) {
239 case SampleFormat::Pcm16:
240 DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(channel), dsp_state, channel,
241 worker_params.sample_rate, worker_params.sample_count,
242 in_params.node_id);
243 break;
244 case SampleFormat::Adpcm:
245 ASSERT(channel == 0 && in_params.channel_count == 1);
246 DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(0), dsp_state, 0,
247 worker_params.sample_rate, worker_params.sample_count,
248 in_params.node_id);
249 break;
250 default:
251 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
252 }
253 }
254}
255
256void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info,
257 VoiceState& dsp_state,
258 s32 mix_buffer_count, s32 channel) {
259 for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
260 const auto& in_params = voice_info.GetInParams();
261 auto& biquad_filter = in_params.biquad_filter[i];
262 // Check if biquad filter is actually used
263 if (!biquad_filter.enabled) {
264 continue;
265 }
266
267 // Reinitialize our biquad filter state if it was enabled previously
268 if (!in_params.was_biquad_filter_enabled[i]) {
269 dsp_state.biquad_filter_state.fill(0);
270 }
271
272 // Generate biquad filter
273 // GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
274 // dsp_state.biquad_filter_state,
275 // mix_buffer_count + channel, mix_buffer_count +
276 // channel, worker_params.sample_count,
277 // voice_info.GetInParams().node_id);
278 }
279}
280
281void AudioCore::CommandGenerator::GenerateBiquadFilterCommand(
282 s32 mix_buffer, const BiquadFilterParameter& params, std::array<s64, 2>& state,
283 std::size_t input_offset, std::size_t output_offset, s32 sample_count, s32 node_id) {
284 if (dumping_frame) {
285 LOG_DEBUG(Audio,
286 "(DSP_TRACE) GenerateBiquadFilterCommand node_id={}, "
287 "input_mix_buffer={}, output_mix_buffer={}",
288 node_id, input_offset, output_offset);
289 }
290 const auto* input = GetMixBuffer(input_offset);
291 auto* output = GetMixBuffer(output_offset);
292
293 // Biquad filter parameters
294 const auto [n0, n1, n2] = params.numerator;
295 const auto [d0, d1] = params.denominator;
296
297 // Biquad filter states
298 auto [s0, s1] = state;
299
300 constexpr s64 int32_min = std::numeric_limits<s32>::min();
301 constexpr s64 int32_max = std::numeric_limits<s32>::max();
302
303 for (int i = 0; i < sample_count; ++i) {
304 const auto sample = static_cast<s64>(input[i]);
305 const auto f = (sample * n0 + s0 + 0x4000) >> 15;
306 const auto y = std::clamp(f, int32_min, int32_max);
307 s0 = sample * n1 + y * d0 + s1;
308 s1 = sample * n2 + y * d1;
309 output[i] = static_cast<s32>(y);
310 }
311
312 state = {s0, s1};
313}
314
315void CommandGenerator::GenerateDepopPrepareCommand(VoiceState& dsp_state,
316 std::size_t mix_buffer_count,
317 std::size_t mix_buffer_offset) {
318 for (std::size_t i = 0; i < mix_buffer_count; i++) {
319 auto& sample = dsp_state.previous_samples[i];
320 if (sample != 0) {
321 depop_buffer[mix_buffer_offset + i] += sample;
322 sample = 0;
323 }
324 }
325}
326
327void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
328 std::size_t mix_buffer_offset,
329 s32 sample_rate) {
330 const std::size_t end_offset =
331 std::min(mix_buffer_offset + mix_buffer_count, GetTotalMixBufferCount());
332 const s32 delta = sample_rate == 48000 ? 0x7B29 : 0x78CB;
333 for (std::size_t i = mix_buffer_offset; i < end_offset; i++) {
334 if (depop_buffer[i] == 0) {
335 continue;
336 }
337
338 depop_buffer[i] =
339 ApplyMixDepop(GetMixBuffer(i), depop_buffer[i], delta, worker_params.sample_count);
340 }
341}
342
343void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
344 const std::size_t effect_count = effect_context.GetCount();
345 const auto buffer_offset = mix_info.GetInParams().buffer_offset;
346 for (std::size_t i = 0; i < effect_count; i++) {
347 const auto index = mix_info.GetEffectOrder(i);
348 if (index == AudioCommon::NO_EFFECT_ORDER) {
349 break;
350 }
351 auto* info = effect_context.GetInfo(index);
352 const auto type = info->GetType();
353
354 // TODO(ogniK): Finish remaining effects
355 switch (type) {
356 case EffectType::Aux:
357 GenerateAuxCommand(buffer_offset, info, info->IsEnabled());
358 break;
359 case EffectType::I3dl2Reverb:
360 GenerateI3dl2ReverbEffectCommand(buffer_offset, info, info->IsEnabled());
361 break;
362 case EffectType::BiquadFilter:
363 GenerateBiquadFilterEffectCommand(buffer_offset, info, info->IsEnabled());
364 break;
365 default:
366 break;
367 }
368
369 info->UpdateForCommandGeneration();
370 }
371}
372
373void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
374 bool enabled) {
375 if (!enabled) {
376 return;
377 }
378 const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
379 const auto channel_count = params.channel_count;
380 for (s32 i = 0; i < channel_count; i++) {
381 // TODO(ogniK): Actually implement reverb
382 if (params.input[i] != params.output[i]) {
383 const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
384 auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
385 ApplyMix<1>(output, input, 32768, worker_params.sample_count);
386 }
387 }
388}
389
390void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info,
391 bool enabled) {
392 if (!enabled) {
393 return;
394 }
395 const auto& params = dynamic_cast<EffectBiquadFilter*>(info)->GetParams();
396 const auto channel_count = params.channel_count;
397 for (s32 i = 0; i < channel_count; i++) {
398 // TODO(ogniK): Actually implement biquad filter
399 if (params.input[i] != params.output[i]) {
400 const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
401 auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
402 ApplyMix<1>(output, input, 32768, worker_params.sample_count);
403 }
404 }
405}
406
407void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) {
408 auto aux = dynamic_cast<EffectAuxInfo*>(info);
409 const auto& params = aux->GetParams();
410 if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) {
411 const auto max_channels = params.count;
412 u32 offset{};
413 for (u32 channel = 0; channel < max_channels; channel++) {
414 u32 write_count = 0;
415 if (channel == (max_channels - 1)) {
416 write_count = offset + worker_params.sample_count;
417 }
418
419 const auto input_index = params.input_mix_buffers[channel] + mix_buffer_offset;
420 const auto output_index = params.output_mix_buffers[channel] + mix_buffer_offset;
421
422 if (enabled) {
423 AuxInfoDSP send_info{};
424 AuxInfoDSP recv_info{};
425 memory.ReadBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
426 memory.ReadBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
427
428 WriteAuxBuffer(send_info, aux->GetSendBuffer(), params.sample_count,
429 GetMixBuffer(input_index), worker_params.sample_count, offset,
430 write_count);
431 memory.WriteBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
432
433 const auto samples_read = ReadAuxBuffer(
434 recv_info, aux->GetRecvBuffer(), params.sample_count,
435 GetMixBuffer(output_index), worker_params.sample_count, offset, write_count);
436 memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
437
438 if (samples_read != worker_params.sample_count &&
439 samples_read <= params.sample_count) {
440 std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read);
441 }
442 } else {
443 AuxInfoDSP empty{};
444 memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
445 memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
446 if (output_index != input_index) {
447 std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index),
448 worker_params.sample_count * sizeof(s32));
449 }
450 }
451
452 offset += worker_params.sample_count;
453 }
454 }
455}
456
457ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) {
458 if (splitter_id == AudioCommon::NO_SPLITTER) {
459 return nullptr;
460 }
461 return splitter_context.GetDestinationData(splitter_id, index);
462}
463
464s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
465 const s32* data, u32 sample_count, u32 write_offset,
466 u32 write_count) {
467 if (max_samples == 0) {
468 return 0;
469 }
470 u32 offset = dsp_info.write_offset + write_offset;
471 if (send_buffer == 0 || offset > max_samples) {
472 return 0;
473 }
474
475 std::size_t data_offset{};
476 u32 remaining = sample_count;
477 while (remaining > 0) {
478 // Get position in buffer
479 const auto base = send_buffer + (offset * sizeof(u32));
480 const auto samples_to_grab = std::min(max_samples - offset, remaining);
481 // Write to output
482 memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32));
483 offset = (offset + samples_to_grab) % max_samples;
484 remaining -= samples_to_grab;
485 data_offset += samples_to_grab;
486 }
487
488 if (write_count != 0) {
489 dsp_info.write_offset = (dsp_info.write_offset + write_count) % max_samples;
490 }
491 return sample_count;
492}
493
494s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples,
495 s32* out_data, u32 sample_count, u32 read_offset,
496 u32 read_count) {
497 if (max_samples == 0) {
498 return 0;
499 }
500
501 u32 offset = recv_info.read_offset + read_offset;
502 if (recv_buffer == 0 || offset > max_samples) {
503 return 0;
504 }
505
506 u32 remaining = sample_count;
507 while (remaining > 0) {
508 const auto base = recv_buffer + (offset * sizeof(u32));
509 const auto samples_to_grab = std::min(max_samples - offset, remaining);
510 std::vector<s32> buffer(samples_to_grab);
511 memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
512 std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32));
513 out_data += samples_to_grab;
514 offset = (offset + samples_to_grab) % max_samples;
515 remaining -= samples_to_grab;
516 }
517
518 if (read_count != 0) {
519 recv_info.read_offset = (recv_info.read_offset + read_count) % max_samples;
520 }
521 return sample_count;
522}
523
524void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
525 s32 channel, s32 node_id) {
526 const auto last = static_cast<s32>(last_volume * 32768.0f);
527 const auto current = static_cast<s32>(current_volume * 32768.0f);
528 const auto delta = static_cast<s32>((static_cast<float>(current) - static_cast<float>(last)) /
529 static_cast<float>(worker_params.sample_count));
530
531 if (dumping_frame) {
532 LOG_DEBUG(Audio,
533 "(DSP_TRACE) GenerateVolumeRampCommand node_id={}, input={}, output={}, "
534 "last_volume={}, current_volume={}",
535 node_id, GetMixChannelBufferOffset(channel), GetMixChannelBufferOffset(channel),
536 last_volume, current_volume);
537 }
538 // Apply generic gain on samples
539 ApplyGain(GetChannelMixBuffer(channel), GetChannelMixBuffer(channel), last, delta,
540 worker_params.sample_count);
541}
542
543void CommandGenerator::GenerateVoiceMixCommand(const MixVolumeBuffer& mix_volumes,
544 const MixVolumeBuffer& last_mix_volumes,
545 VoiceState& dsp_state, s32 mix_buffer_offset,
546 s32 mix_buffer_count, s32 voice_index, s32 node_id) {
547 // Loop all our mix buffers
548 for (s32 i = 0; i < mix_buffer_count; i++) {
549 if (last_mix_volumes[i] != 0.0f || mix_volumes[i] != 0.0f) {
550 const auto delta = static_cast<float>((mix_volumes[i] - last_mix_volumes[i])) /
551 static_cast<float>(worker_params.sample_count);
552
553 if (dumping_frame) {
554 LOG_DEBUG(Audio,
555 "(DSP_TRACE) GenerateVoiceMixCommand node_id={}, input={}, "
556 "output={}, last_volume={}, current_volume={}",
557 node_id, voice_index, mix_buffer_offset + i, last_mix_volumes[i],
558 mix_volumes[i]);
559 }
560
561 dsp_state.previous_samples[i] =
562 ApplyMixRamp(GetMixBuffer(mix_buffer_offset + i), GetMixBuffer(voice_index),
563 last_mix_volumes[i], delta, worker_params.sample_count);
564 } else {
565 dsp_state.previous_samples[i] = 0;
566 }
567 }
568}
569
570void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) {
571 if (dumping_frame) {
572 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand");
573 }
574 auto& in_params = mix_info.GetInParams();
575 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
576 in_params.sample_rate);
577
578 GenerateEffectCommand(mix_info);
579
580 GenerateMixCommands(mix_info);
581}
582
583void CommandGenerator::GenerateMixCommands(ServerMixInfo& mix_info) {
584 if (!mix_info.HasAnyConnection()) {
585 return;
586 }
587 const auto& in_params = mix_info.GetInParams();
588 if (in_params.dest_mix_id != AudioCommon::NO_MIX) {
589 const auto& dest_mix = mix_context.GetInfo(in_params.dest_mix_id);
590 const auto& dest_in_params = dest_mix.GetInParams();
591
592 const auto buffer_count = in_params.buffer_count;
593
594 for (s32 i = 0; i < buffer_count; i++) {
595 for (s32 j = 0; j < dest_in_params.buffer_count; j++) {
596 const auto mixed_volume = in_params.volume * in_params.mix_volume[i][j];
597 if (mixed_volume != 0.0f) {
598 GenerateMixCommand(dest_in_params.buffer_offset + j,
599 in_params.buffer_offset + i, mixed_volume,
600 in_params.node_id);
601 }
602 }
603 }
604 } else if (in_params.splitter_id != AudioCommon::NO_SPLITTER) {
605 s32 base{};
606 while (const auto* destination_data = GetDestinationData(in_params.splitter_id, base++)) {
607 if (!destination_data->IsConfigured()) {
608 continue;
609 }
610
611 const auto& dest_mix = mix_context.GetInfo(destination_data->GetMixId());
612 const auto& dest_in_params = dest_mix.GetInParams();
613 const auto mix_index = (base - 1) % in_params.buffer_count + in_params.buffer_offset;
614 for (std::size_t i = 0; i < dest_in_params.buffer_count; i++) {
615 const auto mixed_volume = in_params.volume * destination_data->GetMixVolume(i);
616 if (mixed_volume != 0.0f) {
617 GenerateMixCommand(dest_in_params.buffer_offset + i, mix_index, mixed_volume,
618 in_params.node_id);
619 }
620 }
621 }
622 }
623}
624
625void CommandGenerator::GenerateMixCommand(std::size_t output_offset, std::size_t input_offset,
626 float volume, s32 node_id) {
627
628 if (dumping_frame) {
629 LOG_DEBUG(Audio,
630 "(DSP_TRACE) GenerateMixCommand node_id={}, input={}, output={}, volume={}",
631 node_id, input_offset, output_offset, volume);
632 }
633
634 auto* output = GetMixBuffer(output_offset);
635 const auto* input = GetMixBuffer(input_offset);
636
637 const s32 gain = static_cast<s32>(volume * 32768.0f);
638 // Mix with loop unrolling
639 if (worker_params.sample_count % 4 == 0) {
640 ApplyMix<4>(output, input, gain, worker_params.sample_count);
641 } else if (worker_params.sample_count % 2 == 0) {
642 ApplyMix<2>(output, input, gain, worker_params.sample_count);
643 } else {
644 ApplyMix<1>(output, input, gain, worker_params.sample_count);
645 }
646}
647
648void CommandGenerator::GenerateFinalMixCommand() {
649 if (dumping_frame) {
650 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand");
651 }
652 auto& mix_info = mix_context.GetFinalMixInfo();
653 const auto in_params = mix_info.GetInParams();
654
655 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
656 in_params.sample_rate);
657
658 GenerateEffectCommand(mix_info);
659
660 for (s32 i = 0; i < in_params.buffer_count; i++) {
661 const s32 gain = static_cast<s32>(in_params.volume * 32768.0f);
662 if (dumping_frame) {
663 LOG_DEBUG(
664 Audio,
665 "(DSP_TRACE) ApplyGainWithoutDelta node_id={}, input={}, output={}, volume={}",
666 in_params.node_id, in_params.buffer_offset + i, in_params.buffer_offset + i,
667 in_params.volume);
668 }
669 ApplyGainWithoutDelta(GetMixBuffer(in_params.buffer_offset + i),
670 GetMixBuffer(in_params.buffer_offset + i), gain,
671 worker_params.sample_count);
672 }
673}
674
675s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
676 s32 sample_count, s32 channel, std::size_t mix_offset) {
677 auto& in_params = voice_info.GetInParams();
678 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
679 if (wave_buffer.buffer_address == 0) {
680 return 0;
681 }
682 if (wave_buffer.buffer_size == 0) {
683 return 0;
684 }
685 if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
686 return 0;
687 }
688 const auto samples_remaining =
689 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
690 const auto start_offset =
691 ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) *
692 sizeof(s16);
693 const auto buffer_pos = wave_buffer.buffer_address + start_offset;
694 const auto samples_processed = std::min(sample_count, samples_remaining);
695
696 if (in_params.channel_count == 1) {
697 std::vector<s16> buffer(samples_processed);
698 memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
699 for (std::size_t i = 0; i < buffer.size(); i++) {
700 sample_buffer[mix_offset + i] = buffer[i];
701 }
702 } else {
703 const auto channel_count = in_params.channel_count;
704 std::vector<s16> buffer(samples_processed * channel_count);
705 memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
706
707 for (std::size_t i = 0; i < samples_processed; i++) {
708 sample_buffer[mix_offset + i] = buffer[i * channel_count + channel];
709 }
710 }
711
712 return samples_processed;
713}
714
715s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
716 s32 sample_count, s32 channel, std::size_t mix_offset) {
717 auto& in_params = voice_info.GetInParams();
718 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
719 if (wave_buffer.buffer_address == 0) {
720 return 0;
721 }
722 if (wave_buffer.buffer_size == 0) {
723 return 0;
724 }
725 if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
726 return 0;
727 }
728
729 constexpr std::array<int, 16> SIGNED_NIBBLES = {
730 {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
731
732 constexpr std::size_t FRAME_LEN = 8;
733 constexpr std::size_t NIBBLES_PER_SAMPLE = 16;
734 constexpr std::size_t SAMPLES_PER_FRAME = 14;
735
736 auto frame_header = dsp_state.context.header;
737 s32 idx = (frame_header >> 4) & 0xf;
738 s32 scale = frame_header & 0xf;
739 s16 yn1 = dsp_state.context.yn1;
740 s16 yn2 = dsp_state.context.yn2;
741
742 Codec::ADPCM_Coeff coeffs;
743 memory.ReadBlock(in_params.additional_params_address, coeffs.data(),
744 sizeof(Codec::ADPCM_Coeff));
745
746 s32 coef1 = coeffs[idx * 2];
747 s32 coef2 = coeffs[idx * 2 + 1];
748
749 const auto samples_remaining =
750 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
751 const auto samples_processed = std::min(sample_count, samples_remaining);
752 const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset;
753
754 const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME;
755 auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) +
756 samples_remaining_in_frame + (samples_remaining_in_frame != 0 ? 2 : 0);
757
758 const auto decode_sample = [&](const int nibble) -> s16 {
759 const int xn = nibble * (1 << scale);
760 // We first transform everything into 11 bit fixed point, perform the second order
761 // digital filter, then transform back.
762 // 0x400 == 0.5 in 11 bit fixed point.
763 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
764 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
765 // Clamp to output range.
766 val = std::clamp<s32>(val, -32768, 32767);
767 // Advance output feedback.
768 yn2 = yn1;
769 yn1 = val;
770 return static_cast<s16>(val);
771 };
772
773 std::size_t buffer_offset{};
774 std::vector<u8> buffer(
775 std::max((samples_processed / FRAME_LEN) * SAMPLES_PER_FRAME, FRAME_LEN));
776 memory.ReadBlock(wave_buffer.buffer_address + (position_in_frame / 2), buffer.data(),
777 buffer.size());
778 std::size_t cur_mix_offset = mix_offset;
779
780 auto remaining_samples = samples_processed;
781 while (remaining_samples > 0) {
782 if (position_in_frame % NIBBLES_PER_SAMPLE == 0) {
783 // Read header
784 frame_header = buffer[buffer_offset++];
785 idx = (frame_header >> 4) & 0xf;
786 scale = frame_header & 0xf;
787 coef1 = coeffs[idx * 2];
788 coef2 = coeffs[idx * 2 + 1];
789 position_in_frame += 2;
790
791 // Decode entire frame
792 if (remaining_samples >= SAMPLES_PER_FRAME) {
793 for (std::size_t i = 0; i < SAMPLES_PER_FRAME / 2; i++) {
794
795 // Sample 1
796 const s32 s0 = SIGNED_NIBBLES[buffer[buffer_offset] >> 4];
797 const s32 s1 = SIGNED_NIBBLES[buffer[buffer_offset++] & 0xf];
798 const s16 sample_1 = decode_sample(s0);
799 const s16 sample_2 = decode_sample(s1);
800 sample_buffer[cur_mix_offset++] = sample_1;
801 sample_buffer[cur_mix_offset++] = sample_2;
802 }
803 remaining_samples -= SAMPLES_PER_FRAME;
804 position_in_frame += SAMPLES_PER_FRAME;
805 continue;
806 }
807 }
808 // Decode mid frame
809 s32 current_nibble = buffer[buffer_offset];
810 if (position_in_frame++ & 0x1) {
811 current_nibble &= 0xf;
812 buffer_offset++;
813 } else {
814 current_nibble >>= 4;
815 }
816 const s16 sample = decode_sample(SIGNED_NIBBLES[current_nibble]);
817 sample_buffer[cur_mix_offset++] = sample;
818 remaining_samples--;
819 }
820
821 dsp_state.context.header = frame_header;
822 dsp_state.context.yn1 = yn1;
823 dsp_state.context.yn2 = yn2;
824
825 return samples_processed;
826}
827
828s32* CommandGenerator::GetMixBuffer(std::size_t index) {
829 return mix_buffer.data() + (index * worker_params.sample_count);
830}
831
832const s32* CommandGenerator::GetMixBuffer(std::size_t index) const {
833 return mix_buffer.data() + (index * worker_params.sample_count);
834}
835
836std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const {
837 return worker_params.mix_buffer_count + channel;
838}
839
840std::size_t CommandGenerator::GetTotalMixBufferCount() const {
841 return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT;
842}
843
844s32* CommandGenerator::GetChannelMixBuffer(s32 channel) {
845 return GetMixBuffer(worker_params.mix_buffer_count + channel);
846}
847
848const s32* CommandGenerator::GetChannelMixBuffer(s32 channel) const {
849 return GetMixBuffer(worker_params.mix_buffer_count + channel);
850}
851
852void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output,
853 VoiceState& dsp_state, s32 channel,
854 s32 target_sample_rate, s32 sample_count,
855 s32 node_id) {
856 auto& in_params = voice_info.GetInParams();
857 if (dumping_frame) {
858 LOG_DEBUG(Audio,
859 "(DSP_TRACE) DecodeFromWaveBuffers, node_id={}, channel={}, "
860 "format={}, sample_count={}, sample_rate={}, mix_id={}, splitter_id={}",
861 node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate,
862 in_params.mix_id, in_params.splitter_info_id);
863 }
864 ASSERT_OR_EXECUTE(output != nullptr, { return; });
865
866 const auto resample_rate = static_cast<s32>(
867 static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
868 static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
869 auto* output_base = output;
870 if ((dsp_state.fraction + sample_count * resample_rate) > (SCALED_MIX_BUFFER_SIZE - 4ULL)) {
871 return;
872 }
873
874 auto min_required_samples =
875 std::min(static_cast<s32>(SCALED_MIX_BUFFER_SIZE) - dsp_state.fraction, resample_rate);
876 if (min_required_samples >= sample_count) {
877 min_required_samples = sample_count;
878 }
879
880 std::size_t temp_mix_offset{};
881 bool is_buffer_completed{false};
882 auto samples_remaining = sample_count;
883 while (samples_remaining > 0 && !is_buffer_completed) {
884 const auto samples_to_output = std::min(samples_remaining, min_required_samples);
885 const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15;
886
887 if (!in_params.behavior_flags.is_pitch_and_src_skipped) {
888 // Append sample histtory for resampler
889 for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
890 sample_buffer[temp_mix_offset + i] = dsp_state.sample_history[i];
891 }
892 temp_mix_offset += 4;
893 }
894
895 s32 samples_read{};
896 while (samples_read < samples_to_read) {
897 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
898 // No more data can be read
899 if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) {
900 is_buffer_completed = true;
901 break;
902 }
903
904 if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 &&
905 wave_buffer.context_address != 0 && wave_buffer.context_size != 0) {
906 // TODO(ogniK): ADPCM loop context
907 }
908
909 s32 samples_decoded{0};
910 switch (in_params.sample_format) {
911 case SampleFormat::Pcm16:
912 samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read,
913 channel, temp_mix_offset);
914 break;
915 case SampleFormat::Adpcm:
916 samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read,
917 channel, temp_mix_offset);
918 break;
919 default:
920 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
921 }
922
923 temp_mix_offset += samples_decoded;
924 samples_read += samples_decoded;
925 dsp_state.offset += samples_decoded;
926 dsp_state.played_sample_count += samples_decoded;
927
928 if (dsp_state.offset >=
929 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) ||
930 samples_decoded == 0) {
931 // Reset our sample offset
932 dsp_state.offset = 0;
933 if (wave_buffer.is_looping) {
934 if (samples_decoded == 0) {
935 // End of our buffer
936 is_buffer_completed = true;
937 break;
938 }
939
940 if (in_params.behavior_flags.is_played_samples_reset_at_loop_point.Value()) {
941 dsp_state.played_sample_count = 0;
942 }
943 } else {
944
945 // Update our wave buffer states
946 dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false;
947 dsp_state.wave_buffer_consumed++;
948 dsp_state.wave_buffer_index =
949 (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
950 if (wave_buffer.end_of_stream) {
951 dsp_state.played_sample_count = 0;
952 }
953 }
954 }
955 }
956
957 if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) {
958 // No need to resample
959 std::memcpy(output, sample_buffer.data(), samples_read * sizeof(s32));
960 } else {
961 std::fill(sample_buffer.begin() + temp_mix_offset,
962 sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read),
963 0);
964 AudioCore::Resample(output, sample_buffer.data(), resample_rate, dsp_state.fraction,
965 samples_to_output);
966 // Resample
967 for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
968 dsp_state.sample_history[i] = sample_buffer[samples_to_read + i];
969 }
970 }
971 output += samples_to_output;
972 samples_remaining -= samples_to_output;
973 }
974}
975
976} // namespace AudioCore
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h
new file mode 100644
index 000000000..967d24078
--- /dev/null
+++ b/src/audio_core/command_generator.h
@@ -0,0 +1,103 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "audio_core/common.h"
9#include "audio_core/voice_context.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12
13namespace Core::Memory {
14class Memory;
15}
16
17namespace AudioCore {
18class MixContext;
19class SplitterContext;
20class ServerSplitterDestinationData;
21class ServerMixInfo;
22class EffectContext;
23class EffectBase;
24struct AuxInfoDSP;
25using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
26
27class CommandGenerator {
28public:
29 explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
30 VoiceContext& voice_context, MixContext& mix_context,
31 SplitterContext& splitter_context, EffectContext& effect_context,
32 Core::Memory::Memory& memory);
33 ~CommandGenerator();
34
35 void ClearMixBuffers();
36 void GenerateVoiceCommands();
37 void GenerateVoiceCommand(ServerVoiceInfo& voice_info);
38 void GenerateSubMixCommands();
39 void GenerateFinalMixCommands();
40 void PreCommand();
41 void PostCommand();
42
43 s32* GetChannelMixBuffer(s32 channel);
44 const s32* GetChannelMixBuffer(s32 channel) const;
45 s32* GetMixBuffer(std::size_t index);
46 const s32* GetMixBuffer(std::size_t index) const;
47 std::size_t GetMixChannelBufferOffset(s32 channel) const;
48
49 std::size_t GetTotalMixBufferCount() const;
50
51private:
52 void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
53 void GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
54 s32 mix_buffer_count, s32 channel);
55 void GenerateVolumeRampCommand(float last_volume, float current_volume, s32 channel,
56 s32 node_id);
57 void GenerateVoiceMixCommand(const MixVolumeBuffer& mix_volumes,
58 const MixVolumeBuffer& last_mix_volumes, VoiceState& dsp_state,
59 s32 mix_buffer_offset, s32 mix_buffer_count, s32 voice_index,
60 s32 node_id);
61 void GenerateSubMixCommand(ServerMixInfo& mix_info);
62 void GenerateMixCommands(ServerMixInfo& mix_info);
63 void GenerateMixCommand(std::size_t output_offset, std::size_t input_offset, float volume,
64 s32 node_id);
65 void GenerateFinalMixCommand();
66 void GenerateBiquadFilterCommand(s32 mix_buffer, const BiquadFilterParameter& params,
67 std::array<s64, 2>& state, std::size_t input_offset,
68 std::size_t output_offset, s32 sample_count, s32 node_id);
69 void GenerateDepopPrepareCommand(VoiceState& dsp_state, std::size_t mix_buffer_count,
70 std::size_t mix_buffer_offset);
71 void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
72 std::size_t mix_buffer_offset, s32 sample_rate);
73 void GenerateEffectCommand(ServerMixInfo& mix_info);
74 void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
75 void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
76 void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
77 ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
78
79 s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
80 u32 sample_count, u32 write_offset, u32 write_count);
81 s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
82 u32 sample_count, u32 read_offset, u32 read_count);
83
84 // DSP Code
85 s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
86 s32 channel, std::size_t mix_offset);
87 s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
88 s32 channel, std::size_t mix_offset);
89 void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state,
90 s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id);
91
92 AudioCommon::AudioRendererParameter& worker_params;
93 VoiceContext& voice_context;
94 MixContext& mix_context;
95 SplitterContext& splitter_context;
96 EffectContext& effect_context;
97 Core::Memory::Memory& memory;
98 std::vector<s32> mix_buffer{};
99 std::vector<s32> sample_buffer{};
100 std::vector<s32> depop_buffer{};
101 bool dumping_frame{false};
102};
103} // namespace AudioCore
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index 7bb145c53..72ebce221 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -8,13 +8,30 @@
8#include "common/swap.h" 8#include "common/swap.h"
9#include "core/hle/result.h" 9#include "core/hle/result.h"
10 10
11namespace AudioCore { 11namespace AudioCommon {
12namespace Audren { 12namespace Audren {
13constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; 13constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
14} 14constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
15} // namespace Audren
15 16
16constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); 17constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8');
17constexpr std::size_t MAX_MIX_BUFFERS = 24; 18constexpr std::size_t MAX_MIX_BUFFERS = 24;
19constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
20constexpr std::size_t MAX_CHANNEL_COUNT = 6;
21constexpr std::size_t MAX_WAVE_BUFFERS = 4;
22constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
23constexpr u32 STREAM_SAMPLE_RATE = 48000;
24constexpr u32 STREAM_NUM_CHANNELS = 6;
25constexpr s32 NO_SPLITTER = -1;
26constexpr s32 NO_MIX = 0x7fffffff;
27constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
28constexpr s32 FINAL_MIX = 0;
29constexpr s32 NO_EFFECT_ORDER = -1;
30constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant
31// Any size checks seem to take the sample history into account
32// and our const ends up being 0x3f04, the 4 bytes are most
33// likely the sample history
34constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
18 35
19static constexpr u32 VersionFromRevision(u32_le rev) { 36static constexpr u32 VersionFromRevision(u32_le rev) {
20 // "REV7" -> 7 37 // "REV7" -> 7
@@ -45,4 +62,46 @@ static constexpr bool CanConsumeBuffer(std::size_t size, std::size_t offset, std
45 return true; 62 return true;
46} 63}
47 64
48} // namespace AudioCore 65struct UpdateDataSizes {
66 u32_le behavior{};
67 u32_le memory_pool{};
68 u32_le voice{};
69 u32_le voice_channel_resource{};
70 u32_le effect{};
71 u32_le mixer{};
72 u32_le sink{};
73 u32_le performance{};
74 u32_le splitter{};
75 u32_le render_info{};
76 INSERT_PADDING_WORDS(4);
77};
78static_assert(sizeof(UpdateDataSizes) == 0x38, "UpdateDataSizes is an invalid size");
79
80struct UpdateDataHeader {
81 u32_le revision{};
82 UpdateDataSizes size{};
83 u32_le total_size{};
84};
85static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader is an invalid size");
86
87struct AudioRendererParameter {
88 u32_le sample_rate;
89 u32_le sample_count;
90 u32_le mix_buffer_count;
91 u32_le submix_count;
92 u32_le voice_count;
93 u32_le sink_count;
94 u32_le effect_count;
95 u32_le performance_frame_count;
96 u8 is_voice_drop_enabled;
97 u8 unknown_21;
98 u8 unknown_22;
99 u8 execution_mode;
100 u32_le splitter_count;
101 u32_le num_splitter_send_channels;
102 u32_le unknown_30;
103 u32_le revision;
104};
105static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
106
107} // namespace AudioCommon
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index c27df946c..83c06c0ed 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -23,14 +23,24 @@ class CubebSinkStream final : public SinkStream {
23public: 23public:
24 CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, 24 CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
25 const std::string& name) 25 const std::string& name)
26 : ctx{ctx}, num_channels{std::min(num_channels_, 2u)}, time_stretch{sample_rate, 26 : ctx{ctx}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
27 num_channels} { 27 num_channels} {
28 28
29 cubeb_stream_params params{}; 29 cubeb_stream_params params{};
30 params.rate = sample_rate; 30 params.rate = sample_rate;
31 params.channels = num_channels; 31 params.channels = num_channels;
32 params.format = CUBEB_SAMPLE_S16NE; 32 params.format = CUBEB_SAMPLE_S16NE;
33 params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO; 33 switch (num_channels) {
34 case 1:
35 params.layout = CUBEB_LAYOUT_MONO;
36 break;
37 case 2:
38 params.layout = CUBEB_LAYOUT_STEREO;
39 break;
40 case 6:
41 params.layout = CUBEB_LAYOUT_3F2_LFE;
42 break;
43 }
34 44
35 u32 minimum_latency{}; 45 u32 minimum_latency{};
36 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) { 46 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
@@ -193,6 +203,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
193 const std::size_t samples_to_write = num_channels * num_frames; 203 const std::size_t samples_to_write = num_channels * num_frames;
194 std::size_t samples_written; 204 std::size_t samples_written;
195 205
206 /*
196 if (Settings::values.enable_audio_stretching.GetValue()) { 207 if (Settings::values.enable_audio_stretching.GetValue()) {
197 const std::vector<s16> in{impl->queue.Pop()}; 208 const std::vector<s16> in{impl->queue.Pop()};
198 const std::size_t num_in{in.size() / num_channels}; 209 const std::size_t num_in{in.size() / num_channels};
@@ -207,7 +218,8 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
207 } 218 }
208 } else { 219 } else {
209 samples_written = impl->queue.Pop(buffer, samples_to_write); 220 samples_written = impl->queue.Pop(buffer, samples_to_write);
210 } 221 }*/
222 samples_written = impl->queue.Pop(buffer, samples_to_write);
211 223
212 if (samples_written >= num_channels) { 224 if (samples_written >= num_channels) {
213 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), 225 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
diff --git a/src/audio_core/effect_context.cpp b/src/audio_core/effect_context.cpp
new file mode 100644
index 000000000..adfec3df5
--- /dev/null
+++ b/src/audio_core/effect_context.cpp
@@ -0,0 +1,299 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "audio_core/effect_context.h"
7
8namespace AudioCore {
9namespace {
10bool ValidChannelCountForEffect(s32 channel_count) {
11 return channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6;
12}
13} // namespace
14
15EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) {
16 effects.reserve(effect_count);
17 std::generate_n(std::back_inserter(effects), effect_count,
18 [] { return std::make_unique<EffectStubbed>(); });
19}
20EffectContext::~EffectContext() = default;
21
22std::size_t EffectContext::GetCount() const {
23 return effect_count;
24}
25
26EffectBase* EffectContext::GetInfo(std::size_t i) {
27 return effects.at(i).get();
28}
29
30EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
31 switch (effect) {
32 case EffectType::Invalid:
33 effects[i] = std::make_unique<EffectStubbed>();
34 break;
35 case EffectType::BufferMixer:
36 effects[i] = std::make_unique<EffectBufferMixer>();
37 break;
38 case EffectType::Aux:
39 effects[i] = std::make_unique<EffectAuxInfo>();
40 break;
41 case EffectType::Delay:
42 effects[i] = std::make_unique<EffectDelay>();
43 break;
44 case EffectType::Reverb:
45 effects[i] = std::make_unique<EffectReverb>();
46 break;
47 case EffectType::I3dl2Reverb:
48 effects[i] = std::make_unique<EffectI3dl2Reverb>();
49 break;
50 case EffectType::BiquadFilter:
51 effects[i] = std::make_unique<EffectBiquadFilter>();
52 break;
53 default:
54 UNREACHABLE_MSG("Unimplemented effect {}", effect);
55 effects[i] = std::make_unique<EffectStubbed>();
56 }
57 return GetInfo(i);
58}
59
60const EffectBase* EffectContext::GetInfo(std::size_t i) const {
61 return effects.at(i).get();
62}
63
64EffectStubbed::EffectStubbed() : EffectBase::EffectBase(EffectType::Invalid) {}
65EffectStubbed::~EffectStubbed() = default;
66
67void EffectStubbed::Update(EffectInfo::InParams& in_params) {}
68void EffectStubbed::UpdateForCommandGeneration() {}
69
70EffectBase::EffectBase(EffectType effect_type) : effect_type(effect_type) {}
71EffectBase::~EffectBase() = default;
72
73UsageState EffectBase::GetUsage() const {
74 return usage;
75}
76
77EffectType EffectBase::GetType() const {
78 return effect_type;
79}
80
81bool EffectBase::IsEnabled() const {
82 return enabled;
83}
84
85s32 EffectBase::GetMixID() const {
86 return mix_id;
87}
88
89s32 EffectBase::GetProcessingOrder() const {
90 return processing_order;
91}
92
93EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric::EffectGeneric(EffectType::I3dl2Reverb) {}
94EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
95
96void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
97 auto& internal_params = GetParams();
98 const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
99 if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
100 UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
101 return;
102 }
103
104 const auto last_status = internal_params.status;
105 mix_id = in_params.mix_id;
106 processing_order = in_params.processing_order;
107 internal_params = *reverb_params;
108 if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
109 internal_params.channel_count = internal_params.max_channels;
110 }
111 enabled = in_params.is_enabled;
112 if (last_status != ParameterStatus::Updated) {
113 internal_params.status = last_status;
114 }
115
116 if (in_params.is_new || skipped) {
117 usage = UsageState::Initialized;
118 internal_params.status = ParameterStatus::Initialized;
119 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
120 }
121}
122
123void EffectI3dl2Reverb::UpdateForCommandGeneration() {
124 if (enabled) {
125 usage = UsageState::Running;
126 } else {
127 usage = UsageState::Stopped;
128 }
129 GetParams().status = ParameterStatus::Updated;
130}
131
132EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric::EffectGeneric(EffectType::BiquadFilter) {}
133EffectBiquadFilter::~EffectBiquadFilter() = default;
134
135void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
136 auto& internal_params = GetParams();
137 const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data());
138 mix_id = in_params.mix_id;
139 processing_order = in_params.processing_order;
140 internal_params = *biquad_params;
141 enabled = in_params.is_enabled;
142}
143
144void EffectBiquadFilter::UpdateForCommandGeneration() {
145 if (enabled) {
146 usage = UsageState::Running;
147 } else {
148 usage = UsageState::Stopped;
149 }
150 GetParams().status = ParameterStatus::Updated;
151}
152
153EffectAuxInfo::EffectAuxInfo() : EffectGeneric::EffectGeneric(EffectType::Aux) {}
154EffectAuxInfo::~EffectAuxInfo() = default;
155
156void EffectAuxInfo::Update(EffectInfo::InParams& in_params) {
157 const auto* aux_params = reinterpret_cast<AuxInfo*>(in_params.raw.data());
158 mix_id = in_params.mix_id;
159 processing_order = in_params.processing_order;
160 GetParams() = *aux_params;
161 enabled = in_params.is_enabled;
162
163 if (in_params.is_new || skipped) {
164 skipped = aux_params->send_buffer_info == 0 || aux_params->return_buffer_info == 0;
165 if (skipped) {
166 return;
167 }
168
169 // There's two AuxInfos which are an identical size, the first one is managed by the cpu,
170 // the second is managed by the dsp. All we care about is managing the DSP one
171 send_info = aux_params->send_buffer_info + sizeof(AuxInfoDSP);
172 send_buffer = aux_params->send_buffer_info + (sizeof(AuxInfoDSP) * 2);
173
174 recv_info = aux_params->return_buffer_info + sizeof(AuxInfoDSP);
175 recv_buffer = aux_params->return_buffer_info + (sizeof(AuxInfoDSP) * 2);
176 }
177}
178
179void EffectAuxInfo::UpdateForCommandGeneration() {
180 if (enabled) {
181 usage = UsageState::Running;
182 } else {
183 usage = UsageState::Stopped;
184 }
185}
186
187const VAddr EffectAuxInfo::GetSendInfo() const {
188 return send_info;
189}
190
191const VAddr EffectAuxInfo::GetSendBuffer() const {
192 return send_buffer;
193}
194
195const VAddr EffectAuxInfo::GetRecvInfo() const {
196 return recv_info;
197}
198
199const VAddr EffectAuxInfo::GetRecvBuffer() const {
200 return recv_buffer;
201}
202
203EffectDelay::EffectDelay() : EffectGeneric::EffectGeneric(EffectType::Delay) {}
204EffectDelay::~EffectDelay() = default;
205
206void EffectDelay::Update(EffectInfo::InParams& in_params) {
207 const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data());
208 auto& internal_params = GetParams();
209 if (!ValidChannelCountForEffect(delay_params->max_channels)) {
210 return;
211 }
212
213 const auto last_status = internal_params.status;
214 mix_id = in_params.mix_id;
215 processing_order = in_params.processing_order;
216 internal_params = *delay_params;
217 if (!ValidChannelCountForEffect(delay_params->channels)) {
218 internal_params.channels = internal_params.max_channels;
219 }
220 enabled = in_params.is_enabled;
221
222 if (last_status != ParameterStatus::Updated) {
223 internal_params.status = last_status;
224 }
225
226 if (in_params.is_new || skipped) {
227 usage = UsageState::Initialized;
228 internal_params.status = ParameterStatus::Initialized;
229 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
230 }
231}
232
233void EffectDelay::UpdateForCommandGeneration() {
234 if (enabled) {
235 usage = UsageState::Running;
236 } else {
237 usage = UsageState::Stopped;
238 }
239 GetParams().status = ParameterStatus::Updated;
240}
241
242EffectBufferMixer::EffectBufferMixer() : EffectGeneric::EffectGeneric(EffectType::BufferMixer) {}
243EffectBufferMixer::~EffectBufferMixer() = default;
244
245void EffectBufferMixer::Update(EffectInfo::InParams& in_params) {
246 mix_id = in_params.mix_id;
247 processing_order = in_params.processing_order;
248 GetParams() = *reinterpret_cast<BufferMixerParams*>(in_params.raw.data());
249 enabled = in_params.is_enabled;
250}
251
252void EffectBufferMixer::UpdateForCommandGeneration() {
253 if (enabled) {
254 usage = UsageState::Running;
255 } else {
256 usage = UsageState::Stopped;
257 }
258}
259
260EffectReverb::EffectReverb() : EffectGeneric::EffectGeneric(EffectType::Reverb) {}
261EffectReverb::~EffectReverb() = default;
262
263void EffectReverb::Update(EffectInfo::InParams& in_params) {
264 const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data());
265 auto& internal_params = GetParams();
266 if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
267 return;
268 }
269
270 const auto last_status = internal_params.status;
271 mix_id = in_params.mix_id;
272 processing_order = in_params.processing_order;
273 internal_params = *reverb_params;
274 if (!ValidChannelCountForEffect(reverb_params->channels)) {
275 internal_params.channels = internal_params.max_channels;
276 }
277 enabled = in_params.is_enabled;
278
279 if (last_status != ParameterStatus::Updated) {
280 internal_params.status = last_status;
281 }
282
283 if (in_params.is_new || skipped) {
284 usage = UsageState::Initialized;
285 internal_params.status = ParameterStatus::Initialized;
286 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
287 }
288}
289
290void EffectReverb::UpdateForCommandGeneration() {
291 if (enabled) {
292 usage = UsageState::Running;
293 } else {
294 usage = UsageState::Stopped;
295 }
296 GetParams().status = ParameterStatus::Updated;
297}
298
299} // namespace AudioCore
diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h
new file mode 100644
index 000000000..2f2da72dd
--- /dev/null
+++ b/src/audio_core/effect_context.h
@@ -0,0 +1,322 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9#include <vector>
10#include "audio_core/common.h"
11#include "common/common_funcs.h"
12#include "common/common_types.h"
13#include "common/swap.h"
14
15namespace AudioCore {
16enum class EffectType : u8 {
17 Invalid = 0,
18 BufferMixer = 1,
19 Aux = 2,
20 Delay = 3,
21 Reverb = 4,
22 I3dl2Reverb = 5,
23 BiquadFilter = 6,
24};
25
26enum class UsageStatus : u8 {
27 Invalid = 0,
28 New = 1,
29 Initialized = 2,
30 Used = 3,
31 Removed = 4,
32};
33
34enum class UsageState {
35 Invalid = 0,
36 Initialized = 1,
37 Running = 2,
38 Stopped = 3,
39};
40
41enum class ParameterStatus : u8 {
42 Initialized = 0,
43 Updating = 1,
44 Updated = 2,
45};
46
47struct BufferMixerParams {
48 std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input{};
49 std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output{};
50 std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> volume{};
51 s32_le count{};
52};
53static_assert(sizeof(BufferMixerParams) == 0x94, "BufferMixerParams is an invalid size");
54
55struct AuxInfoDSP {
56 u32_le read_offset{};
57 u32_le write_offset{};
58 u32_le remaining{};
59 INSERT_PADDING_WORDS(13);
60};
61static_assert(sizeof(AuxInfoDSP) == 0x40, "AuxInfoDSP is an invalid size");
62
63struct AuxInfo {
64 std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input_mix_buffers{};
65 std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output_mix_buffers{};
66 u32_le count{};
67 s32_le sample_rate{};
68 s32_le sample_count{};
69 s32_le mix_buffer_count{};
70 u64_le send_buffer_info{};
71 u64_le send_buffer_base{};
72
73 u64_le return_buffer_info{};
74 u64_le return_buffer_base{};
75};
76static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
77
78struct I3dl2ReverbParams {
79 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
80 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
81 u16_le max_channels{};
82 u16_le channel_count{};
83 INSERT_PADDING_BYTES(1);
84 u32_le sample_rate{};
85 f32 room_hf{};
86 f32 hf_reference{};
87 f32 decay_time{};
88 f32 hf_decay_ratio{};
89 f32 room{};
90 f32 reflection{};
91 f32 reverb{};
92 f32 diffusion{};
93 f32 reflection_delay{};
94 f32 reverb_delay{};
95 f32 density{};
96 f32 dry_gain{};
97 ParameterStatus status{};
98 INSERT_PADDING_BYTES(3);
99};
100static_assert(sizeof(I3dl2ReverbParams) == 0x4c, "I3dl2ReverbParams is an invalid size");
101
102struct BiquadFilterParams {
103 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
104 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
105 std::array<s16_le, 3> numerator;
106 std::array<s16_le, 2> denominator;
107 s8 channel_count{};
108 ParameterStatus status{};
109};
110static_assert(sizeof(BiquadFilterParams) == 0x18, "BiquadFilterParams is an invalid size");
111
112struct DelayParams {
113 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
114 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
115 u16_le max_channels{};
116 u16_le channels{};
117 s32_le max_delay{};
118 s32_le delay{};
119 s32_le sample_rate{};
120 s32_le gain{};
121 s32_le feedback_gain{};
122 s32_le out_gain{};
123 s32_le dry_gain{};
124 s32_le channel_spread{};
125 s32_le low_pass{};
126 ParameterStatus status{};
127 INSERT_PADDING_BYTES(3);
128};
129static_assert(sizeof(DelayParams) == 0x38, "DelayParams is an invalid size");
130
131struct ReverbParams {
132 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
133 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
134 u16_le max_channels{};
135 u16_le channels{};
136 s32_le sample_rate{};
137 s32_le mode0{};
138 s32_le mode0_gain{};
139 s32_le pre_delay{};
140 s32_le mode1{};
141 s32_le mode1_gain{};
142 s32_le decay{};
143 s32_le hf_decay_ratio{};
144 s32_le coloration{};
145 s32_le reverb_gain{};
146 s32_le out_gain{};
147 s32_le dry_gain{};
148 ParameterStatus status{};
149 INSERT_PADDING_BYTES(3);
150};
151static_assert(sizeof(ReverbParams) == 0x44, "ReverbParams is an invalid size");
152
153class EffectInfo {
154public:
155 struct InParams {
156 EffectType type{};
157 u8 is_new{};
158 u8 is_enabled{};
159 INSERT_PADDING_BYTES(1);
160 s32_le mix_id{};
161 u64_le buffer_address{};
162 u64_le buffer_size{};
163 s32_le processing_order{};
164 INSERT_PADDING_BYTES(4);
165 union {
166 std::array<u8, 0xa0> raw;
167 };
168 };
169 static_assert(sizeof(EffectInfo::InParams) == 0xc0, "InParams is an invalid size");
170
171 struct OutParams {
172 UsageStatus status{};
173 INSERT_PADDING_BYTES(15);
174 };
175 static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size");
176};
177
178struct AuxAddress {
179 VAddr send_dsp_info{};
180 VAddr send_buffer_base{};
181 VAddr return_dsp_info{};
182 VAddr return_buffer_base{};
183};
184
185class EffectBase {
186public:
187 EffectBase(EffectType effect_type);
188 ~EffectBase();
189
190 virtual void Update(EffectInfo::InParams& in_params) = 0;
191 virtual void UpdateForCommandGeneration() = 0;
192 UsageState GetUsage() const;
193 EffectType GetType() const;
194 bool IsEnabled() const;
195 s32 GetMixID() const;
196 s32 GetProcessingOrder() const;
197
198protected:
199 UsageState usage{UsageState::Invalid};
200 EffectType effect_type{};
201 s32 mix_id{};
202 s32 processing_order{};
203 bool enabled = false;
204};
205
206template <typename T>
207class EffectGeneric : public EffectBase {
208public:
209 EffectGeneric(EffectType effect_type) : EffectBase::EffectBase(effect_type) {}
210 ~EffectGeneric() = default;
211
212 T& GetParams() {
213 return internal_params;
214 }
215
216 const I3dl2ReverbParams& GetParams() const {
217 return internal_params;
218 }
219
220private:
221 T internal_params{};
222};
223
224class EffectStubbed : public EffectBase {
225public:
226 explicit EffectStubbed();
227 ~EffectStubbed();
228
229 void Update(EffectInfo::InParams& in_params) override;
230 void UpdateForCommandGeneration() override;
231};
232
233class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
234public:
235 explicit EffectI3dl2Reverb();
236 ~EffectI3dl2Reverb();
237
238 void Update(EffectInfo::InParams& in_params) override;
239 void UpdateForCommandGeneration() override;
240
241private:
242 bool skipped = false;
243};
244
245class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
246public:
247 explicit EffectBiquadFilter();
248 ~EffectBiquadFilter();
249
250 void Update(EffectInfo::InParams& in_params) override;
251 void UpdateForCommandGeneration() override;
252};
253
254class EffectAuxInfo : public EffectGeneric<AuxInfo> {
255public:
256 explicit EffectAuxInfo();
257 ~EffectAuxInfo();
258
259 void Update(EffectInfo::InParams& in_params) override;
260 void UpdateForCommandGeneration() override;
261 const VAddr GetSendInfo() const;
262 const VAddr GetSendBuffer() const;
263 const VAddr GetRecvInfo() const;
264 const VAddr GetRecvBuffer() const;
265
266private:
267 VAddr send_info{};
268 VAddr send_buffer{};
269 VAddr recv_info{};
270 VAddr recv_buffer{};
271 bool skipped = false;
272 AuxAddress addresses{};
273};
274
275class EffectDelay : public EffectGeneric<DelayParams> {
276public:
277 explicit EffectDelay();
278 ~EffectDelay();
279
280 void Update(EffectInfo::InParams& in_params) override;
281 void UpdateForCommandGeneration() override;
282
283private:
284 bool skipped = false;
285};
286
287class EffectBufferMixer : public EffectGeneric<BufferMixerParams> {
288public:
289 explicit EffectBufferMixer();
290 ~EffectBufferMixer();
291
292 void Update(EffectInfo::InParams& in_params) override;
293 void UpdateForCommandGeneration() override;
294};
295
296class EffectReverb : public EffectGeneric<ReverbParams> {
297public:
298 explicit EffectReverb();
299 ~EffectReverb();
300
301 void Update(EffectInfo::InParams& in_params) override;
302 void UpdateForCommandGeneration() override;
303
304private:
305 bool skipped = false;
306};
307
308class EffectContext {
309public:
310 explicit EffectContext(std::size_t effect_count);
311 ~EffectContext();
312
313 std::size_t GetCount() const;
314 EffectBase* GetInfo(std::size_t i);
315 EffectBase* RetargetEffect(std::size_t i, EffectType effect);
316 const EffectBase* GetInfo(std::size_t i) const;
317
318private:
319 std::size_t effect_count{};
320 std::vector<std::unique_ptr<EffectBase>> effects;
321};
322} // namespace AudioCore
diff --git a/src/audio_core/info_updater.cpp b/src/audio_core/info_updater.cpp
new file mode 100644
index 000000000..f53ce21a5
--- /dev/null
+++ b/src/audio_core/info_updater.cpp
@@ -0,0 +1,517 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/behavior_info.h"
6#include "audio_core/effect_context.h"
7#include "audio_core/info_updater.h"
8#include "audio_core/memory_pool.h"
9#include "audio_core/mix_context.h"
10#include "audio_core/sink_context.h"
11#include "audio_core/splitter_context.h"
12#include "audio_core/voice_context.h"
13#include "common/logging/log.h"
14
15namespace AudioCore {
16
17InfoUpdater::InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
18 BehaviorInfo& behavior_info)
19 : in_params(in_params), out_params(out_params), behavior_info(behavior_info) {
20 ASSERT(
21 AudioCommon::CanConsumeBuffer(in_params.size(), 0, sizeof(AudioCommon::UpdateDataHeader)));
22 std::memcpy(&input_header, in_params.data(), sizeof(AudioCommon::UpdateDataHeader));
23 output_header.total_size = sizeof(AudioCommon::UpdateDataHeader);
24}
25
26InfoUpdater::~InfoUpdater() = default;
27
28bool InfoUpdater::UpdateBehaviorInfo(BehaviorInfo& in_behavior_info) {
29 if (input_header.size.behavior != sizeof(BehaviorInfo::InParams)) {
30 LOG_ERROR(Audio, "Behavior info is an invalid size, expecting 0x{:X} but got 0x{:X}",
31 sizeof(BehaviorInfo::InParams), input_header.size.behavior);
32 return false;
33 }
34
35 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset,
36 sizeof(BehaviorInfo::InParams))) {
37 LOG_ERROR(Audio, "Buffer is an invalid size!");
38 return false;
39 }
40
41 BehaviorInfo::InParams behavior_in{};
42 std::memcpy(&behavior_in, in_params.data() + input_offset, sizeof(BehaviorInfo::InParams));
43 input_offset += sizeof(BehaviorInfo::InParams);
44
45 // Make sure it's an audio revision we can actually support
46 if (!AudioCommon::IsValidRevision(behavior_in.revision)) {
47 LOG_ERROR(Audio, "Invalid input revision, revision=0x{:08X}", behavior_in.revision);
48 return false;
49 }
50
51 // Make sure that our behavior info revision matches the input
52 if (in_behavior_info.GetUserRevision() != behavior_in.revision) {
53 LOG_ERROR(Audio,
54 "User revision differs from input revision, expecting 0x{:08X} but got 0x{:08X}",
55 in_behavior_info.GetUserRevision(), behavior_in.revision);
56 return false;
57 }
58
59 // Update behavior info flags
60 in_behavior_info.ClearError();
61 in_behavior_info.UpdateFlags(behavior_in.flags);
62
63 return true;
64}
65
66bool InfoUpdater::UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info) {
67 const auto force_mapping = behavior_info.IsMemoryPoolForceMappingEnabled();
68 const auto memory_pool_count = memory_pool_info.size();
69 const auto total_memory_pool_in = sizeof(ServerMemoryPoolInfo::InParams) * memory_pool_count;
70 const auto total_memory_pool_out = sizeof(ServerMemoryPoolInfo::OutParams) * memory_pool_count;
71
72 if (input_header.size.memory_pool != total_memory_pool_in) {
73 LOG_ERROR(Audio, "Memory pools are an invalid size, expecting 0x{:X} but got 0x{:X}",
74 total_memory_pool_in, input_header.size.memory_pool);
75 return false;
76 }
77
78 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_memory_pool_in)) {
79 LOG_ERROR(Audio, "Buffer is an invalid size!");
80 return false;
81 }
82
83 std::vector<ServerMemoryPoolInfo::InParams> mempool_in(memory_pool_count);
84 std::vector<ServerMemoryPoolInfo::OutParams> mempool_out(memory_pool_count);
85
86 std::memcpy(mempool_in.data(), in_params.data() + input_offset, total_memory_pool_in);
87 input_offset += total_memory_pool_in;
88
89 // Update our memory pools
90 for (std::size_t i = 0; i < memory_pool_count; i++) {
91 if (!memory_pool_info[i].Update(mempool_in[i], mempool_out[i])) {
92 LOG_ERROR(Audio, "Failed to update memory pool {}!", i);
93 return false;
94 }
95 }
96
97 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset,
98 sizeof(BehaviorInfo::InParams))) {
99 LOG_ERROR(Audio, "Buffer is an invalid size!");
100 return false;
101 }
102
103 std::memcpy(out_params.data() + output_offset, mempool_out.data(), total_memory_pool_out);
104 output_offset += total_memory_pool_out;
105 output_header.size.memory_pool = static_cast<u32>(total_memory_pool_out);
106 return true;
107}
108
109bool InfoUpdater::UpdateVoiceChannelResources(VoiceContext& voice_context) {
110 const auto voice_count = voice_context.GetVoiceCount();
111 const auto voice_size = voice_count * sizeof(VoiceChannelResource::InParams);
112 std::vector<VoiceChannelResource::InParams> resources_in(voice_count);
113
114 if (input_header.size.voice_channel_resource != voice_size) {
115 LOG_ERROR(Audio, "VoiceChannelResource is an invalid size, expecting 0x{:X} but got 0x{:X}",
116 voice_size, input_header.size.voice_channel_resource);
117 return false;
118 }
119
120 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, voice_size)) {
121 LOG_ERROR(Audio, "Buffer is an invalid size!");
122 return false;
123 }
124
125 std::memcpy(resources_in.data(), in_params.data() + input_offset, voice_size);
126 input_offset += voice_size;
127
128 // Update our channel resources
129 for (std::size_t i = 0; i < voice_count; i++) {
130 // Grab our channel resource
131 auto& resource = voice_context.GetChannelResource(i);
132 resource.Update(resources_in[i]);
133 }
134
135 return true;
136}
137
138bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
139 std::vector<ServerMemoryPoolInfo>& memory_pool_info,
140 VAddr audio_codec_dsp_addr) {
141 const auto voice_count = voice_context.GetVoiceCount();
142 std::vector<VoiceInfo::InParams> voice_in(voice_count);
143 std::vector<VoiceInfo::OutParams> voice_out(voice_count);
144
145 const auto voice_in_size = voice_count * sizeof(VoiceInfo::InParams);
146 const auto voice_out_size = voice_count * sizeof(VoiceInfo::OutParams);
147
148 if (input_header.size.voice != voice_in_size) {
149 LOG_ERROR(Audio, "Voices are an invalid size, expecting 0x{:X} but got 0x{:X}",
150 voice_in_size, input_header.size.voice);
151 return false;
152 }
153
154 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, voice_in_size)) {
155 LOG_ERROR(Audio, "Buffer is an invalid size!");
156 return false;
157 }
158
159 std::memcpy(voice_in.data(), in_params.data() + input_offset, voice_in_size);
160 input_offset += voice_in_size;
161
162 // Set all voices to not be in use
163 for (std::size_t i = 0; i < voice_count; i++) {
164 voice_context.GetInfo(i).GetInParams().in_use = false;
165 }
166
167 // Update our voices
168 for (std::size_t i = 0; i < voice_count; i++) {
169 auto& in_params = voice_in[i];
170 const auto channel_count = static_cast<std::size_t>(in_params.channel_count);
171 // Skip if it's not currently in use
172 if (!in_params.is_in_use) {
173 continue;
174 }
175 // Voice states for each channel
176 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> voice_states{};
177 ASSERT(in_params.id < voice_count);
178
179 // Grab our current voice info
180 auto& voice_info = voice_context.GetInfo(static_cast<std::size_t>(in_params.id));
181
182 ASSERT(channel_count <= AudioCommon::MAX_CHANNEL_COUNT);
183
184 // Get all our channel voice states
185 for (std::size_t channel = 0; channel < channel_count; channel++) {
186 voice_states[channel] =
187 &voice_context.GetState(in_params.voice_channel_resource_ids[channel]);
188 }
189
190 if (in_params.is_new) {
191 // Default our values for our voice
192 voice_info.Initialize();
193 if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) {
194 continue;
195 }
196
197 // Zero out our voice states
198 for (std::size_t channel = 0; channel < channel_count; channel++) {
199 std::memset(voice_states[channel], 0, sizeof(VoiceState));
200 }
201 }
202
203 // Update our voice
204 voice_info.UpdateParameters(in_params, behavior_info);
205 // TODO(ogniK): Handle mapping errors with behavior info based on in params response
206
207 // Update our wave buffers
208 voice_info.UpdateWaveBuffers(in_params, voice_states, behavior_info);
209 voice_info.WriteOutStatus(voice_out[i], in_params, voice_states);
210 }
211
212 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, voice_out_size)) {
213 LOG_ERROR(Audio, "Buffer is an invalid size!");
214 return false;
215 }
216 std::memcpy(out_params.data() + output_offset, voice_out.data(), voice_out_size);
217 output_offset += voice_out_size;
218 output_header.size.voice = static_cast<u32>(voice_out_size);
219 return true;
220}
221
222bool InfoUpdater::UpdateEffects(EffectContext& effect_context, bool is_active) {
223 const auto effect_count = effect_context.GetCount();
224 std::vector<EffectInfo::InParams> effect_in(effect_count);
225 std::vector<EffectInfo::OutParams> effect_out(effect_count);
226
227 const auto total_effect_in = effect_count * sizeof(EffectInfo::InParams);
228 const auto total_effect_out = effect_count * sizeof(EffectInfo::OutParams);
229
230 if (input_header.size.effect != total_effect_in) {
231 LOG_ERROR(Audio, "Effects are an invalid size, expecting 0x{:X} but got 0x{:X}",
232 total_effect_in, input_header.size.effect);
233 return false;
234 }
235
236 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_effect_in)) {
237 LOG_ERROR(Audio, "Buffer is an invalid size!");
238 return false;
239 }
240
241 std::memcpy(effect_in.data(), in_params.data() + input_offset, total_effect_in);
242 input_offset += total_effect_in;
243
244 // Update effects
245 for (std::size_t i = 0; i < effect_count; i++) {
246 auto* info = effect_context.GetInfo(i);
247 if (effect_in[i].type != info->GetType()) {
248 info = effect_context.RetargetEffect(i, effect_in[i].type);
249 }
250
251 info->Update(effect_in[i]);
252
253 if ((!is_active && info->GetUsage() != UsageState::Initialized) ||
254 info->GetUsage() == UsageState::Stopped) {
255 effect_out[i].status = UsageStatus::Removed;
256 } else {
257 effect_out[i].status = UsageStatus::Used;
258 }
259 }
260
261 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_effect_out)) {
262 LOG_ERROR(Audio, "Buffer is an invalid size!");
263 return false;
264 }
265
266 std::memcpy(out_params.data() + output_offset, effect_out.data(), total_effect_out);
267 output_offset += total_effect_out;
268 output_header.size.effect = static_cast<u32>(total_effect_out);
269
270 return true;
271}
272
273bool InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) {
274 std::size_t start_offset = input_offset;
275 std::size_t bytes_read{};
276 // Update splitter context
277 if (!splitter_context.Update(in_params, input_offset, bytes_read)) {
278 LOG_ERROR(Audio, "Failed to update splitter context!");
279 return false;
280 }
281
282 const auto consumed = input_offset - start_offset;
283
284 if (input_header.size.splitter != consumed) {
285 LOG_ERROR(Audio, "Splitters is an invalid size, expecting 0x{:X} but got 0x{:X}",
286 bytes_read, input_header.size.splitter);
287 return false;
288 }
289
290 return true;
291}
292
293ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
294 SplitterContext& splitter_context,
295 EffectContext& effect_context) {
296 std::vector<MixInfo::InParams> mix_in_params;
297
298 if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
299 // If we're not dirty, get ALL mix in parameters
300 const auto context_mix_count = mix_context.GetCount();
301 const auto total_mix_in = context_mix_count * sizeof(MixInfo::InParams);
302 if (input_header.size.mixer != total_mix_in) {
303 LOG_ERROR(Audio, "Mixer is an invalid size, expecting 0x{:X} but got 0x{:X}",
304 total_mix_in, input_header.size.mixer);
305 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
306 }
307
308 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_mix_in)) {
309 LOG_ERROR(Audio, "Buffer is an invalid size!");
310 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
311 }
312
313 mix_in_params.resize(context_mix_count);
314 std::memcpy(mix_in_params.data(), in_params.data() + input_offset, total_mix_in);
315
316 input_offset += total_mix_in;
317 } else {
318 // Only update the "dirty" mixes
319 MixInfo::DirtyHeader dirty_header{};
320 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset,
321 sizeof(MixInfo::DirtyHeader))) {
322 LOG_ERROR(Audio, "Buffer is an invalid size!");
323 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
324 }
325
326 std::memcpy(&dirty_header, in_params.data() + input_offset, sizeof(MixInfo::DirtyHeader));
327 input_offset += sizeof(MixInfo::DirtyHeader);
328
329 const auto total_mix_in =
330 dirty_header.mixer_count * sizeof(MixInfo::InParams) + sizeof(MixInfo::DirtyHeader);
331
332 if (input_header.size.mixer != total_mix_in) {
333 LOG_ERROR(Audio, "Mixer is an invalid size, expecting 0x{:X} but got 0x{:X}",
334 total_mix_in, input_header.size.mixer);
335 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
336 }
337
338 if (dirty_header.mixer_count != 0) {
339 mix_in_params.resize(dirty_header.mixer_count);
340 std::memcpy(mix_in_params.data(), in_params.data() + input_offset,
341 mix_in_params.size() * sizeof(MixInfo::InParams));
342 input_offset += mix_in_params.size() * sizeof(MixInfo::InParams);
343 }
344 }
345
346 // Get our total input count
347 const auto mix_count = mix_in_params.size();
348
349 if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
350 // Only verify our buffer count if we're not dirty
351 std::size_t total_buffer_count{};
352 for (std::size_t i = 0; i < mix_count; i++) {
353 const auto& in = mix_in_params[i];
354 total_buffer_count += in.buffer_count;
355 if (in.dest_mix_id > mix_count && in.dest_mix_id != AudioCommon::NO_MIX &&
356 in.mix_id != AudioCommon::FINAL_MIX) {
357 LOG_ERROR(
358 Audio,
359 "Invalid mix destination, mix_id={:X}, dest_mix_id={:X}, mix_buffer_count={:X}",
360 in.mix_id, in.dest_mix_id, mix_buffer_count);
361 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
362 }
363 }
364
365 if (total_buffer_count > mix_buffer_count) {
366 LOG_ERROR(Audio,
367 "Too many mix buffers used! mix_buffer_count={:X}, requesting_buffers={:X}",
368 mix_buffer_count, total_buffer_count);
369 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
370 }
371 }
372
373 if (mix_buffer_count == 0) {
374 LOG_ERROR(Audio, "No mix buffers!");
375 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
376 }
377
378 bool should_sort = false;
379 for (std::size_t i = 0; i < mix_count; i++) {
380 const auto& mix_in = mix_in_params[i];
381 std::size_t target_mix{};
382 if (behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
383 target_mix = mix_in.mix_id;
384 } else {
385 // Non dirty supported games just use i instead of the actual mix_id
386 target_mix = i;
387 }
388 auto& mix_info = mix_context.GetInfo(target_mix);
389 auto& mix_info_params = mix_info.GetInParams();
390 if (mix_info_params.in_use != mix_in.in_use) {
391 mix_info_params.in_use = mix_in.in_use;
392 mix_info.ResetEffectProcessingOrder();
393 should_sort = true;
394 }
395
396 if (mix_in.in_use) {
397 should_sort |= mix_info.Update(mix_context.GetEdgeMatrix(), mix_in, behavior_info,
398 splitter_context, effect_context);
399 }
400 }
401
402 if (should_sort && behavior_info.IsSplitterSupported()) {
403 // Sort our splitter data
404 if (!mix_context.TsortInfo(splitter_context)) {
405 return AudioCommon::Audren::ERR_SPLITTER_SORT_FAILED;
406 }
407 }
408
409 // TODO(ogniK): Sort when splitter is suppoorted
410
411 return RESULT_SUCCESS;
412}
413
414bool InfoUpdater::UpdateSinks(SinkContext& sink_context) {
415 const auto sink_count = sink_context.GetCount();
416 std::vector<SinkInfo::InParams> sink_in_params(sink_count);
417 const auto total_sink_in = sink_count * sizeof(SinkInfo::InParams);
418
419 if (input_header.size.sink != total_sink_in) {
420 LOG_ERROR(Audio, "Sinks are an invalid size, expecting 0x{:X} but got 0x{:X}",
421 total_sink_in, input_header.size.effect);
422 return false;
423 }
424
425 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_sink_in)) {
426 LOG_ERROR(Audio, "Buffer is an invalid size!");
427 return false;
428 }
429
430 std::memcpy(sink_in_params.data(), in_params.data() + input_offset, total_sink_in);
431 input_offset += total_sink_in;
432
433 // TODO(ogniK): Properly update sinks
434 if (!sink_in_params.empty()) {
435 sink_context.UpdateMainSink(sink_in_params[0]);
436 }
437
438 output_header.size.sink = static_cast<u32>(0x20 * sink_count);
439 output_offset += 0x20 * sink_count;
440 return true;
441}
442
443bool InfoUpdater::UpdatePerformanceBuffer() {
444 output_header.size.performance = 0x10;
445 output_offset += 0x10;
446 return true;
447}
448
449bool InfoUpdater::UpdateErrorInfo(BehaviorInfo& in_behavior_info) {
450 const auto total_beahvior_info_out = sizeof(BehaviorInfo::OutParams);
451
452 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_beahvior_info_out)) {
453 LOG_ERROR(Audio, "Buffer is an invalid size!");
454 return false;
455 }
456
457 BehaviorInfo::OutParams behavior_info_out{};
458 behavior_info.CopyErrorInfo(behavior_info_out);
459
460 std::memcpy(out_params.data() + output_offset, &behavior_info_out, total_beahvior_info_out);
461 output_offset += total_beahvior_info_out;
462 output_header.size.behavior = total_beahvior_info_out;
463
464 return true;
465}
466
467struct RendererInfo {
468 u64_le elasped_frame_count{};
469 INSERT_PADDING_WORDS(2);
470};
471static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
472
473bool InfoUpdater::UpdateRendererInfo(std::size_t elapsed_frame_count) {
474 const auto total_renderer_info_out = sizeof(RendererInfo);
475 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_renderer_info_out)) {
476 LOG_ERROR(Audio, "Buffer is an invalid size!");
477 return false;
478 }
479 RendererInfo out{};
480 out.elasped_frame_count = elapsed_frame_count;
481 std::memcpy(out_params.data() + output_offset, &out, total_renderer_info_out);
482 output_offset += total_renderer_info_out;
483 output_header.size.render_info = total_renderer_info_out;
484
485 return true;
486}
487
488bool InfoUpdater::CheckConsumedSize() const {
489 if (output_offset != out_params.size()) {
490 LOG_ERROR(Audio, "Output is not consumed! Consumed {}, but requires {}. {} bytes remaining",
491 output_offset, out_params.size(), out_params.size() - output_offset);
492 return false;
493 }
494 /*if (input_offset != in_params.size()) {
495 LOG_ERROR(Audio, "Input is not consumed!");
496 return false;
497 }*/
498 return true;
499}
500
501bool InfoUpdater::WriteOutputHeader() {
502 if (!AudioCommon::CanConsumeBuffer(out_params.size(), 0,
503 sizeof(AudioCommon::UpdateDataHeader))) {
504 LOG_ERROR(Audio, "Buffer is an invalid size!");
505 return false;
506 }
507 output_header.revision = AudioCommon::CURRENT_PROCESS_REVISION;
508 const auto& sz = output_header.size;
509 output_header.total_size += sz.behavior + sz.memory_pool + sz.voice +
510 sz.voice_channel_resource + sz.effect + sz.mixer + sz.sink +
511 sz.performance + sz.splitter + sz.render_info;
512
513 std::memcpy(out_params.data(), &output_header, sizeof(AudioCommon::UpdateDataHeader));
514 return true;
515}
516
517} // namespace AudioCore
diff --git a/src/audio_core/info_updater.h b/src/audio_core/info_updater.h
new file mode 100644
index 000000000..06f9d770f
--- /dev/null
+++ b/src/audio_core/info_updater.h
@@ -0,0 +1,58 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "audio_core/common.h"
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13class BehaviorInfo;
14class ServerMemoryPoolInfo;
15class VoiceContext;
16class EffectContext;
17class MixContext;
18class SinkContext;
19class SplitterContext;
20
21class InfoUpdater {
22public:
23 // TODO(ogniK): Pass process handle when we support it
24 InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
25 BehaviorInfo& behavior_info);
26 ~InfoUpdater();
27
28 bool UpdateBehaviorInfo(BehaviorInfo& in_behavior_info);
29 bool UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info);
30 bool UpdateVoiceChannelResources(VoiceContext& voice_context);
31 bool UpdateVoices(VoiceContext& voice_context,
32 std::vector<ServerMemoryPoolInfo>& memory_pool_info,
33 VAddr audio_codec_dsp_addr);
34 bool UpdateEffects(EffectContext& effect_context, bool is_active);
35 bool UpdateSplitterInfo(SplitterContext& splitter_context);
36 ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
37 SplitterContext& splitter_context, EffectContext& effect_context);
38 bool UpdateSinks(SinkContext& sink_context);
39 bool UpdatePerformanceBuffer();
40 bool UpdateErrorInfo(BehaviorInfo& in_behavior_info);
41 bool UpdateRendererInfo(std::size_t elapsed_frame_count);
42 bool CheckConsumedSize() const;
43
44 bool WriteOutputHeader();
45
46private:
47 const std::vector<u8>& in_params;
48 std::vector<u8>& out_params;
49 BehaviorInfo& behavior_info;
50
51 AudioCommon::UpdateDataHeader input_header{};
52 AudioCommon::UpdateDataHeader output_header{};
53
54 std::size_t input_offset{sizeof(AudioCommon::UpdateDataHeader)};
55 std::size_t output_offset{sizeof(AudioCommon::UpdateDataHeader)};
56};
57
58} // namespace AudioCore
diff --git a/src/audio_core/memory_pool.cpp b/src/audio_core/memory_pool.cpp
new file mode 100644
index 000000000..5a3453063
--- /dev/null
+++ b/src/audio_core/memory_pool.cpp
@@ -0,0 +1,62 @@
1
2// Copyright 2020 yuzu Emulator Project
3// Licensed under GPLv2 or any later version
4// Refer to the license.txt file included.
5
6#include "audio_core/memory_pool.h"
7#include "common/logging/log.h"
8
9namespace AudioCore {
10
11ServerMemoryPoolInfo::ServerMemoryPoolInfo() = default;
12ServerMemoryPoolInfo::~ServerMemoryPoolInfo() = default;
13bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_params,
14 ServerMemoryPoolInfo::OutParams& out_params) {
15 // Our state does not need to be changed
16 if (in_params.state != ServerMemoryPoolInfo::State::RequestAttach &&
17 in_params.state != ServerMemoryPoolInfo::State::RequestDetach) {
18 return true;
19 }
20
21 // Address or size is null
22 if (in_params.address == 0 || in_params.size == 0) {
23 LOG_ERROR(Audio, "Memory pool address or size is zero! address={:X}, size={:X}",
24 in_params.address, in_params.size);
25 return false;
26 }
27
28 // Address or size is not aligned
29 if ((in_params.address % 0x1000) != 0 || (in_params.size % 0x1000) != 0) {
30 LOG_ERROR(Audio, "Memory pool address or size is not aligned! address={:X}, size={:X}",
31 in_params.address, in_params.size);
32 return false;
33 }
34
35 if (in_params.state == ServerMemoryPoolInfo::State::RequestAttach) {
36 cpu_address = in_params.address;
37 size = in_params.size;
38 used = true;
39 out_params.state = ServerMemoryPoolInfo::State::Attached;
40 } else {
41 // Unexpected address
42 if (cpu_address != in_params.address) {
43 LOG_ERROR(Audio, "Memory pool address differs! Expecting {:X} but address is {:X}",
44 cpu_address, in_params.address);
45 return false;
46 }
47
48 if (size != in_params.size) {
49 LOG_ERROR(Audio, "Memory pool size differs! Expecting {:X} but size is {:X}", size,
50 in_params.size);
51 return false;
52 }
53
54 cpu_address = 0;
55 size = 0;
56 used = false;
57 out_params.state = ServerMemoryPoolInfo::State::Detached;
58 }
59 return true;
60}
61
62} // namespace AudioCore
diff --git a/src/audio_core/memory_pool.h b/src/audio_core/memory_pool.h
new file mode 100644
index 000000000..8ac503f1c
--- /dev/null
+++ b/src/audio_core/memory_pool.h
@@ -0,0 +1,53 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10
11namespace AudioCore {
12
13class ServerMemoryPoolInfo {
14public:
15 ServerMemoryPoolInfo();
16 ~ServerMemoryPoolInfo();
17
18 enum class State : u32_le {
19 Invalid = 0x0,
20 Aquired = 0x1,
21 RequestDetach = 0x2,
22 Detached = 0x3,
23 RequestAttach = 0x4,
24 Attached = 0x5,
25 Released = 0x6,
26 };
27
28 struct InParams {
29 u64_le address{};
30 u64_le size{};
31 ServerMemoryPoolInfo::State state{};
32 INSERT_PADDING_WORDS(3);
33 };
34 static_assert(sizeof(ServerMemoryPoolInfo::InParams) == 0x20, "InParams are an invalid size");
35
36 struct OutParams {
37 ServerMemoryPoolInfo::State state{};
38 INSERT_PADDING_WORDS(3);
39 };
40 static_assert(sizeof(ServerMemoryPoolInfo::OutParams) == 0x10, "OutParams are an invalid size");
41
42 bool Update(const ServerMemoryPoolInfo::InParams& in_params,
43 ServerMemoryPoolInfo::OutParams& out_params);
44
45private:
46 // There's another entry here which is the DSP address, however since we're not talking to the
47 // DSP we can just use the same address provided by the guest without needing to remap
48 u64_le cpu_address{};
49 u64_le size{};
50 bool used{};
51};
52
53} // namespace AudioCore
diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp
new file mode 100644
index 000000000..042891490
--- /dev/null
+++ b/src/audio_core/mix_context.cpp
@@ -0,0 +1,296 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/behavior_info.h"
6#include "audio_core/common.h"
7#include "audio_core/effect_context.h"
8#include "audio_core/mix_context.h"
9#include "audio_core/splitter_context.h"
10
11namespace AudioCore {
12MixContext::MixContext() = default;
13MixContext::~MixContext() = default;
14
15void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
16 std::size_t effect_count) {
17 info_count = mix_count;
18 infos.resize(info_count);
19 auto& final_mix = GetInfo(AudioCommon::FINAL_MIX);
20 final_mix.GetInParams().mix_id = AudioCommon::FINAL_MIX;
21 sorted_info.reserve(infos.size());
22 for (auto& info : infos) {
23 sorted_info.push_back(&info);
24 }
25
26 for (auto& info : infos) {
27 info.SetEffectCount(effect_count);
28 }
29
30 // Only initialize our edge matrix and node states if splitters are supported
31 if (behavior_info.IsSplitterSupported()) {
32 node_states.Initialize(mix_count);
33 edge_matrix.Initialize(mix_count);
34 }
35}
36
37void MixContext::UpdateDistancesFromFinalMix() {
38 // Set all distances to be invalid
39 for (std::size_t i = 0; i < info_count; i++) {
40 GetInfo(i).GetInParams().final_mix_distance = AudioCommon::NO_FINAL_MIX;
41 }
42
43 for (std::size_t i = 0; i < info_count; i++) {
44 auto& info = GetInfo(i);
45 auto& in_params = info.GetInParams();
46 // Populate our sorted info
47 sorted_info[i] = &info;
48
49 if (!in_params.in_use) {
50 continue;
51 }
52
53 auto mix_id = in_params.mix_id;
54 // Needs to be referenced out of scope
55 s32 distance_to_final_mix{AudioCommon::FINAL_MIX};
56 for (; distance_to_final_mix < info_count; distance_to_final_mix++) {
57 if (mix_id == AudioCommon::FINAL_MIX) {
58 // If we're at the final mix, we're done
59 break;
60 } else if (mix_id == AudioCommon::NO_MIX) {
61 // If we have no more mix ids, we're done
62 distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
63 break;
64 } else {
65 const auto& dest_mix = GetInfo(mix_id);
66 const auto dest_mix_distance = dest_mix.GetInParams().final_mix_distance;
67
68 if (dest_mix_distance == AudioCommon::NO_FINAL_MIX) {
69 // If our current mix isn't pointing to a final mix, follow through
70 mix_id = dest_mix.GetInParams().dest_mix_id;
71 } else {
72 // Our current mix + 1 = final distance
73 distance_to_final_mix = dest_mix_distance + 1;
74 break;
75 }
76 }
77 }
78
79 // If we're out of range for our distance, mark it as no final mix
80 if (distance_to_final_mix >= info_count) {
81 distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
82 }
83
84 in_params.final_mix_distance = distance_to_final_mix;
85 }
86}
87
88void MixContext::CalcMixBufferOffset() {
89 s32 offset{};
90 for (std::size_t i = 0; i < info_count; i++) {
91 auto& info = GetSortedInfo(i);
92 auto& in_params = info.GetInParams();
93 if (in_params.in_use) {
94 // Only update if in use
95 in_params.buffer_offset = offset;
96 offset += in_params.buffer_count;
97 }
98 }
99}
100
101void MixContext::SortInfo() {
102 // Get the distance to the final mix
103 UpdateDistancesFromFinalMix();
104
105 // Sort based on the distance to the final mix
106 std::sort(sorted_info.begin(), sorted_info.end(),
107 [](const ServerMixInfo* lhs, const ServerMixInfo* rhs) {
108 return lhs->GetInParams().final_mix_distance >
109 rhs->GetInParams().final_mix_distance;
110 });
111
112 // Calculate the mix buffer offset
113 CalcMixBufferOffset();
114}
115
116bool MixContext::TsortInfo(SplitterContext& splitter_context) {
117 // If we're not using mixes, just calculate the mix buffer offset
118 if (!splitter_context.UsingSplitter()) {
119 CalcMixBufferOffset();
120 return true;
121 }
122 // Sort our node states
123 if (!node_states.Tsort(edge_matrix)) {
124 return false;
125 }
126
127 // Get our sorted list
128 const auto sorted_list = node_states.GetIndexList();
129 std::size_t info_id{};
130 for (auto itr = sorted_list.rbegin(); itr != sorted_list.rend(); ++itr) {
131 // Set our sorted info
132 sorted_info[info_id++] = &GetInfo(*itr);
133 }
134
135 // Calculate the mix buffer offset
136 CalcMixBufferOffset();
137 return true;
138}
139
140std::size_t MixContext::GetCount() const {
141 return info_count;
142}
143
144ServerMixInfo& MixContext::GetInfo(std::size_t i) {
145 ASSERT(i < info_count);
146 return infos.at(i);
147}
148
149const ServerMixInfo& MixContext::GetInfo(std::size_t i) const {
150 ASSERT(i < info_count);
151 return infos.at(i);
152}
153
154ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) {
155 ASSERT(i < info_count);
156 return *sorted_info.at(i);
157}
158
159const ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) const {
160 ASSERT(i < info_count);
161 return *sorted_info.at(i);
162}
163
164ServerMixInfo& MixContext::GetFinalMixInfo() {
165 return infos.at(AudioCommon::FINAL_MIX);
166}
167
168const ServerMixInfo& MixContext::GetFinalMixInfo() const {
169 return infos.at(AudioCommon::FINAL_MIX);
170}
171
172EdgeMatrix& MixContext::GetEdgeMatrix() {
173 return edge_matrix;
174}
175
176const EdgeMatrix& MixContext::GetEdgeMatrix() const {
177 return edge_matrix;
178}
179
180ServerMixInfo::ServerMixInfo() {
181 Cleanup();
182}
183ServerMixInfo::~ServerMixInfo() = default;
184
185const ServerMixInfo::InParams& ServerMixInfo::GetInParams() const {
186 return in_params;
187}
188
189ServerMixInfo::InParams& ServerMixInfo::GetInParams() {
190 return in_params;
191}
192
193bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
194 BehaviorInfo& behavior_info, SplitterContext& splitter_context,
195 EffectContext& effect_context) {
196 in_params.volume = mix_in.volume;
197 in_params.sample_rate = mix_in.sample_rate;
198 in_params.buffer_count = mix_in.buffer_count;
199 in_params.in_use = mix_in.in_use;
200 in_params.mix_id = mix_in.mix_id;
201 in_params.node_id = mix_in.node_id;
202 for (std::size_t i = 0; i < mix_in.mix_volume.size(); i++) {
203 std::copy(mix_in.mix_volume[i].begin(), mix_in.mix_volume[i].end(),
204 in_params.mix_volume[i].begin());
205 }
206
207 bool require_sort = false;
208
209 if (behavior_info.IsSplitterSupported()) {
210 require_sort = UpdateConnection(edge_matrix, mix_in, splitter_context);
211 } else {
212 in_params.dest_mix_id = mix_in.dest_mix_id;
213 in_params.splitter_id = AudioCommon::NO_SPLITTER;
214 }
215
216 ResetEffectProcessingOrder();
217 const auto effect_count = effect_context.GetCount();
218 for (std::size_t i = 0; i < effect_count; i++) {
219 auto* effect_info = effect_context.GetInfo(i);
220 if (effect_info->GetMixID() == in_params.mix_id) {
221 effect_processing_order[effect_info->GetProcessingOrder()] = static_cast<s32>(i);
222 }
223 }
224
225 // TODO(ogniK): Update effect processing order
226 return require_sort;
227}
228
229bool ServerMixInfo::HasAnyConnection() const {
230 return in_params.splitter_id != AudioCommon::NO_SPLITTER ||
231 in_params.mix_id != AudioCommon::NO_MIX;
232}
233
234void ServerMixInfo::Cleanup() {
235 in_params.volume = 0.0f;
236 in_params.sample_rate = 0;
237 in_params.buffer_count = 0;
238 in_params.in_use = false;
239 in_params.mix_id = AudioCommon::NO_MIX;
240 in_params.node_id = 0;
241 in_params.buffer_offset = 0;
242 in_params.dest_mix_id = AudioCommon::NO_MIX;
243 in_params.splitter_id = AudioCommon::NO_SPLITTER;
244 std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size());
245}
246
247void ServerMixInfo::SetEffectCount(std::size_t count) {
248 effect_processing_order.resize(count);
249 ResetEffectProcessingOrder();
250}
251
252void ServerMixInfo::ResetEffectProcessingOrder() {
253 for (auto& order : effect_processing_order) {
254 order = AudioCommon::NO_EFFECT_ORDER;
255 }
256}
257
258s32 ServerMixInfo::GetEffectOrder(std::size_t i) const {
259 return effect_processing_order.at(i);
260}
261
262bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
263 SplitterContext& splitter_context) {
264 // Mixes are identical
265 if (in_params.dest_mix_id == mix_in.dest_mix_id &&
266 in_params.splitter_id == mix_in.splitter_id &&
267 ((in_params.splitter_id == AudioCommon::NO_SPLITTER) ||
268 !splitter_context.GetInfo(in_params.splitter_id).HasNewConnection())) {
269 return false;
270 }
271 // Remove current edges for mix id
272 edge_matrix.RemoveEdges(in_params.mix_id);
273 if (mix_in.dest_mix_id != AudioCommon::NO_MIX) {
274 // If we have a valid destination mix id, set our edge matrix
275 edge_matrix.Connect(in_params.mix_id, mix_in.dest_mix_id);
276 } else if (mix_in.splitter_id != AudioCommon::NO_SPLITTER) {
277 // Recurse our splitter linked and set our edges
278 auto& splitter_info = splitter_context.GetInfo(mix_in.splitter_id);
279 const auto length = splitter_info.GetLength();
280 for (s32 i = 0; i < length; i++) {
281 const auto* splitter_destination =
282 splitter_context.GetDestinationData(mix_in.splitter_id, i);
283 if (splitter_destination == nullptr) {
284 continue;
285 }
286 if (splitter_destination->ValidMixId()) {
287 edge_matrix.Connect(in_params.mix_id, splitter_destination->GetMixId());
288 }
289 }
290 }
291 in_params.dest_mix_id = mix_in.dest_mix_id;
292 in_params.splitter_id = mix_in.splitter_id;
293 return true;
294}
295
296} // namespace AudioCore
diff --git a/src/audio_core/mix_context.h b/src/audio_core/mix_context.h
new file mode 100644
index 000000000..6a588eeb4
--- /dev/null
+++ b/src/audio_core/mix_context.h
@@ -0,0 +1,114 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9#include "audio_core/common.h"
10#include "audio_core/splitter_context.h"
11#include "common/common_funcs.h"
12#include "common/common_types.h"
13
14namespace AudioCore {
15class BehaviorInfo;
16class EffectContext;
17
18class MixInfo {
19public:
20 struct DirtyHeader {
21 u32_le magic{};
22 u32_le mixer_count{};
23 INSERT_PADDING_BYTES(0x18);
24 };
25 static_assert(sizeof(DirtyHeader) == 0x20, "MixInfo::DirtyHeader is an invalid size");
26
27 struct InParams {
28 float_le volume{};
29 s32_le sample_rate{};
30 s32_le buffer_count{};
31 bool in_use{};
32 INSERT_PADDING_BYTES(3);
33 s32_le mix_id{};
34 s32_le effect_count{};
35 u32_le node_id{};
36 INSERT_PADDING_WORDS(2);
37 std::array<std::array<float_le, AudioCommon::MAX_MIX_BUFFERS>, AudioCommon::MAX_MIX_BUFFERS>
38 mix_volume{};
39 s32_le dest_mix_id{};
40 s32_le splitter_id{};
41 INSERT_PADDING_WORDS(1);
42 };
43 static_assert(sizeof(MixInfo::InParams) == 0x930, "MixInfo::InParams is an invalid size");
44};
45
46class ServerMixInfo {
47public:
48 struct InParams {
49 float volume{};
50 s32 sample_rate{};
51 s32 buffer_count{};
52 bool in_use{};
53 s32 mix_id{};
54 u32 node_id{};
55 std::array<std::array<float_le, AudioCommon::MAX_MIX_BUFFERS>, AudioCommon::MAX_MIX_BUFFERS>
56 mix_volume{};
57 s32 dest_mix_id{};
58 s32 splitter_id{};
59 s32 buffer_offset{};
60 s32 final_mix_distance{};
61 };
62 ServerMixInfo();
63 ~ServerMixInfo();
64
65 const ServerMixInfo::InParams& GetInParams() const;
66 ServerMixInfo::InParams& GetInParams();
67
68 bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
69 BehaviorInfo& behavior_info, SplitterContext& splitter_context,
70 EffectContext& effect_context);
71 bool HasAnyConnection() const;
72 void Cleanup();
73 void SetEffectCount(std::size_t count);
74 void ResetEffectProcessingOrder();
75 s32 GetEffectOrder(std::size_t i) const;
76
77private:
78 std::vector<s32> effect_processing_order;
79 InParams in_params{};
80 bool UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
81 SplitterContext& splitter_context);
82};
83
84class MixContext {
85public:
86 MixContext();
87 ~MixContext();
88
89 void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
90 std::size_t effect_count);
91 void SortInfo();
92 bool TsortInfo(SplitterContext& splitter_context);
93
94 std::size_t GetCount() const;
95 ServerMixInfo& GetInfo(std::size_t i);
96 const ServerMixInfo& GetInfo(std::size_t i) const;
97 ServerMixInfo& GetSortedInfo(std::size_t i);
98 const ServerMixInfo& GetSortedInfo(std::size_t i) const;
99 ServerMixInfo& GetFinalMixInfo();
100 const ServerMixInfo& GetFinalMixInfo() const;
101 EdgeMatrix& GetEdgeMatrix();
102 const EdgeMatrix& GetEdgeMatrix() const;
103
104private:
105 void CalcMixBufferOffset();
106 void UpdateDistancesFromFinalMix();
107
108 NodeStates node_states{};
109 EdgeMatrix edge_matrix{};
110 std::size_t info_count{};
111 std::vector<ServerMixInfo> infos{};
112 std::vector<ServerMixInfo*> sorted_info{};
113};
114} // namespace AudioCore
diff --git a/src/audio_core/sink_context.cpp b/src/audio_core/sink_context.cpp
new file mode 100644
index 000000000..0882b411a
--- /dev/null
+++ b/src/audio_core/sink_context.cpp
@@ -0,0 +1,31 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/sink_context.h"
6
7namespace AudioCore {
8SinkContext::SinkContext(std::size_t sink_count) : sink_count(sink_count) {}
9SinkContext::~SinkContext() = default;
10
11std::size_t SinkContext::GetCount() const {
12 return sink_count;
13}
14
15void SinkContext::UpdateMainSink(SinkInfo::InParams& in) {
16 in_use = in.in_use;
17 use_count = in.device.input_count;
18 std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT);
19}
20
21bool SinkContext::InUse() const {
22 return in_use;
23}
24
25std::vector<u8> SinkContext::OutputBuffers() const {
26 std::vector<u8> buffer_ret(use_count);
27 std::memcpy(buffer_ret.data(), buffers.data(), use_count);
28 return buffer_ret;
29}
30
31} // namespace AudioCore
diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h
new file mode 100644
index 000000000..d7aa72ba7
--- /dev/null
+++ b/src/audio_core/sink_context.h
@@ -0,0 +1,89 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "audio_core/common.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11
12namespace AudioCore {
13
14enum class SinkTypes : u8 {
15 Invalid = 0,
16 Device = 1,
17 Circular = 2,
18};
19
20enum class SinkSampleFormat : u32_le {
21 None = 0,
22 Pcm8 = 1,
23 Pcm16 = 2,
24 Pcm24 = 3,
25 Pcm32 = 4,
26 PcmFloat = 5,
27 Adpcm = 6,
28};
29
30class SinkInfo {
31public:
32 struct CircularBufferIn {
33 u64_le address;
34 u32_le size;
35 u32_le input_count;
36 u32_le sample_count;
37 u32_le previous_position;
38 SinkSampleFormat sample_format;
39 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
40 bool in_use;
41 INSERT_UNION_PADDING_BYTES(5);
42 };
43 static_assert(sizeof(SinkInfo::CircularBufferIn) == 0x28,
44 "SinkInfo::CircularBufferIn is in invalid size");
45
46 struct DeviceIn {
47 std::array<u8, 255> device_name;
48 INSERT_UNION_PADDING_BYTES(1);
49 s32_le input_count;
50 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
51 INSERT_UNION_PADDING_BYTES(1);
52 bool down_matrix_enabled;
53 std::array<float_le, 4> down_matrix_coef;
54 };
55 static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
56
57 struct InParams {
58 SinkTypes type{};
59 bool in_use{};
60 INSERT_PADDING_BYTES(2);
61 u32_le node_id{};
62 INSERT_PADDING_WORDS(6);
63 union {
64 // std::array<u8, 0x120> raw{};
65 SinkInfo::DeviceIn device;
66 SinkInfo::CircularBufferIn circular_buffer;
67 };
68 };
69 static_assert(sizeof(SinkInfo::InParams) == 0x140, "SinkInfo::InParams are an invalid size!");
70};
71
72class SinkContext {
73public:
74 explicit SinkContext(std::size_t sink_count);
75 ~SinkContext();
76
77 std::size_t GetCount() const;
78
79 void UpdateMainSink(SinkInfo::InParams& in);
80 bool InUse() const;
81 std::vector<u8> OutputBuffers() const;
82
83private:
84 bool in_use{false};
85 s32 use_count{};
86 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
87 std::size_t sink_count{};
88};
89} // namespace AudioCore
diff --git a/src/audio_core/splitter_context.cpp b/src/audio_core/splitter_context.cpp
new file mode 100644
index 000000000..79bb2f516
--- /dev/null
+++ b/src/audio_core/splitter_context.cpp
@@ -0,0 +1,617 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/behavior_info.h"
6#include "audio_core/splitter_context.h"
7#include "common/alignment.h"
8#include "common/assert.h"
9#include "common/logging/log.h"
10
11namespace AudioCore {
12
13ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id) : id(id) {}
14ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
15
16void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
17 // Log error as these are not actually failure states
18 if (header.magic != SplitterMagic::DataHeader) {
19 LOG_ERROR(Audio, "Splitter destination header is invalid!");
20 return;
21 }
22
23 // Incorrect splitter id
24 if (header.splitter_id != id) {
25 LOG_ERROR(Audio, "Splitter destination ids do not match!");
26 return;
27 }
28
29 mix_id = header.mix_id;
30 // Copy our mix volumes
31 std::copy(header.mix_volumes.begin(), header.mix_volumes.end(), current_mix_volumes.begin());
32 if (!in_use && header.in_use) {
33 // Update mix volumes
34 std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
35 needs_update = false;
36 }
37 in_use = header.in_use;
38}
39
40ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() {
41 return next;
42}
43
44const ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() const {
45 return next;
46}
47
48void ServerSplitterDestinationData::SetNextDestination(ServerSplitterDestinationData* dest) {
49 next = dest;
50}
51
52bool ServerSplitterDestinationData::ValidMixId() const {
53 return GetMixId() != AudioCommon::NO_MIX;
54}
55
56s32 ServerSplitterDestinationData::GetMixId() const {
57 return mix_id;
58}
59
60bool ServerSplitterDestinationData::IsConfigured() const {
61 return in_use && ValidMixId();
62}
63
64float ServerSplitterDestinationData::GetMixVolume(std::size_t i) const {
65 ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
66 return current_mix_volumes.at(i);
67}
68
69const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
70ServerSplitterDestinationData::CurrentMixVolumes() const {
71 return current_mix_volumes;
72}
73
74const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
75ServerSplitterDestinationData::LastMixVolumes() const {
76 return last_mix_volumes;
77}
78
79void ServerSplitterDestinationData::MarkDirty() {
80 needs_update = true;
81}
82
83void ServerSplitterDestinationData::UpdateInternalState() {
84 if (in_use && needs_update) {
85 std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
86 }
87 needs_update = false;
88}
89
90ServerSplitterInfo::ServerSplitterInfo(s32 id) : id(id) {}
91ServerSplitterInfo::~ServerSplitterInfo() = default;
92
93void ServerSplitterInfo::InitializeInfos() {
94 send_length = 0;
95 head = nullptr;
96 new_connection = true;
97}
98
99void ServerSplitterInfo::ClearNewConnectionFlag() {
100 new_connection = false;
101}
102
103std::size_t ServerSplitterInfo::Update(SplitterInfo::InInfoPrams& header) {
104 if (header.send_id != id) {
105 return 0;
106 }
107
108 sample_rate = header.sample_rate;
109 new_connection = true;
110 // We need to update the size here due to the splitter bug being present and providing an
111 // incorrect size. We're suppose to also update the header here but we just ignore and continue
112 return (sizeof(s32_le) * (header.length - 1)) + (sizeof(s32_le) * 3);
113}
114
115ServerSplitterDestinationData* ServerSplitterInfo::GetHead() {
116 return head;
117}
118
119const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const {
120 return head;
121}
122
123ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
124 auto current_head = head;
125 for (std::size_t i = 0; i < depth; i++) {
126 if (current_head == nullptr) {
127 return nullptr;
128 }
129 current_head = current_head->GetNextDestination();
130 }
131 return current_head;
132}
133
134const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
135 auto current_head = head;
136 for (std::size_t i = 0; i < depth; i++) {
137 if (current_head == nullptr) {
138 return nullptr;
139 }
140 current_head = current_head->GetNextDestination();
141 }
142 return current_head;
143}
144
145bool ServerSplitterInfo::HasNewConnection() const {
146 return new_connection;
147}
148
149s32 ServerSplitterInfo::GetLength() const {
150 return send_length;
151}
152
153void ServerSplitterInfo::SetHead(ServerSplitterDestinationData* new_head) {
154 head = new_head;
155}
156
157void ServerSplitterInfo::SetHeadDepth(s32 length) {
158 send_length = length;
159}
160
161SplitterContext::SplitterContext() = default;
162SplitterContext::~SplitterContext() = default;
163
164void SplitterContext::Initialize(BehaviorInfo& behavior_info, std::size_t _info_count,
165 std::size_t _data_count) {
166 if (!behavior_info.IsSplitterSupported() || _data_count == 0 || _info_count == 0) {
167 Setup(0, 0, false);
168 return;
169 }
170 // Only initialize if we're using splitters
171 Setup(_info_count, _data_count, behavior_info.IsSplitterBugFixed());
172}
173
174bool SplitterContext::Update(const std::vector<u8>& input, std::size_t& input_offset,
175 std::size_t& bytes_read) {
176 const auto UpdateOffsets = [&](std::size_t read) {
177 input_offset += read;
178 bytes_read += read;
179 };
180
181 if (info_count == 0 || data_count == 0) {
182 bytes_read = 0;
183 return true;
184 }
185
186 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
187 sizeof(SplitterInfo::InHeader))) {
188 LOG_ERROR(Audio, "Buffer is an invalid size!");
189 return false;
190 }
191 SplitterInfo::InHeader header{};
192 std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InHeader));
193 UpdateOffsets(sizeof(SplitterInfo::InHeader));
194
195 if (header.magic != SplitterMagic::SplitterHeader) {
196 LOG_ERROR(Audio, "Invalid header magic! Expecting {:X} but got {:X}",
197 SplitterMagic::SplitterHeader, header.magic);
198 return false;
199 }
200
201 // Clear all connections
202 for (auto& info : infos) {
203 info.ClearNewConnectionFlag();
204 }
205
206 UpdateInfo(input, input_offset, bytes_read, header.info_count);
207 UpdateData(input, input_offset, bytes_read, header.data_count);
208 const auto aligned_bytes_read = Common::AlignUp(bytes_read, 16);
209 input_offset += aligned_bytes_read - bytes_read;
210 bytes_read = aligned_bytes_read;
211 return true;
212}
213
214bool SplitterContext::UsingSplitter() const {
215 return info_count > 0 && data_count > 0;
216}
217
218ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) {
219 ASSERT(i < info_count);
220 return infos.at(i);
221}
222
223const ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) const {
224 ASSERT(i < info_count);
225 return infos.at(i);
226}
227
228ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) {
229 ASSERT(i < data_count);
230 return datas.at(i);
231}
232
233const ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) const {
234 ASSERT(i < data_count);
235 return datas.at(i);
236}
237
238ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
239 std::size_t data) {
240 ASSERT(info < info_count);
241 auto& cur_info = GetInfo(info);
242 return cur_info.GetData(data);
243}
244
245const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
246 std::size_t data) const {
247 ASSERT(info < info_count);
248 auto& cur_info = GetInfo(info);
249 return cur_info.GetData(data);
250}
251
252void SplitterContext::UpdateInternalState() {
253 if (data_count == 0) {
254 return;
255 }
256
257 for (auto& data : datas) {
258 data.UpdateInternalState();
259 }
260}
261
262std::size_t SplitterContext::GetInfoCount() const {
263 return info_count;
264}
265
266std::size_t SplitterContext::GetDataCount() const {
267 return data_count;
268}
269
270void SplitterContext::Setup(std::size_t _info_count, std::size_t _data_count,
271 bool is_splitter_bug_fixed) {
272
273 info_count = _info_count;
274 data_count = _data_count;
275
276 for (std::size_t i = 0; i < info_count; i++) {
277 auto& splitter = infos.emplace_back(static_cast<s32>(i));
278 splitter.InitializeInfos();
279 }
280 for (std::size_t i = 0; i < data_count; i++) {
281 datas.emplace_back(static_cast<s32>(i));
282 }
283
284 bug_fixed = is_splitter_bug_fixed;
285}
286
287bool SplitterContext::UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
288 std::size_t& bytes_read, s32 in_splitter_count) {
289 const auto UpdateOffsets = [&](std::size_t read) {
290 input_offset += read;
291 bytes_read += read;
292 };
293
294 for (s32 i = 0; i < in_splitter_count; i++) {
295 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
296 sizeof(SplitterInfo::InInfoPrams))) {
297 LOG_ERROR(Audio, "Buffer is an invalid size!");
298 return false;
299 }
300 SplitterInfo::InInfoPrams header{};
301 std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InInfoPrams));
302
303 // Logged as warning as these don't actually cause a bailout for some reason
304 if (header.magic != SplitterMagic::InfoHeader) {
305 LOG_ERROR(Audio, "Bad splitter data header");
306 break;
307 }
308
309 if (header.send_id < 0 || header.send_id > info_count) {
310 LOG_ERROR(Audio, "Bad splitter data id");
311 break;
312 }
313
314 UpdateOffsets(sizeof(SplitterInfo::InInfoPrams));
315 auto& info = GetInfo(header.send_id);
316 if (!RecomposeDestination(info, header, input, input_offset)) {
317 LOG_ERROR(Audio, "Failed to recompose destination for splitter!");
318 return false;
319 }
320 const std::size_t read = info.Update(header);
321 bytes_read += read;
322 input_offset += read;
323 }
324 return true;
325}
326
327bool SplitterContext::UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
328 std::size_t& bytes_read, s32 in_data_count) {
329 const auto UpdateOffsets = [&](std::size_t read) {
330 input_offset += read;
331 bytes_read += read;
332 };
333
334 for (s32 i = 0; i < in_data_count; i++) {
335 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
336 sizeof(SplitterInfo::InDestinationParams))) {
337 LOG_ERROR(Audio, "Buffer is an invalid size!");
338 return false;
339 }
340 SplitterInfo::InDestinationParams header{};
341 std::memcpy(&header, input.data() + input_offset,
342 sizeof(SplitterInfo::InDestinationParams));
343 UpdateOffsets(sizeof(SplitterInfo::InDestinationParams));
344
345 // Logged as warning as these don't actually cause a bailout for some reason
346 if (header.magic != SplitterMagic::DataHeader) {
347 LOG_ERROR(Audio, "Bad splitter data header");
348 break;
349 }
350
351 if (header.splitter_id < 0 || header.splitter_id > data_count) {
352 LOG_ERROR(Audio, "Bad splitter data id");
353 break;
354 }
355 GetData(header.splitter_id).Update(header);
356 }
357 return true;
358}
359
360bool SplitterContext::RecomposeDestination(ServerSplitterInfo& info,
361 SplitterInfo::InInfoPrams& header,
362 const std::vector<u8>& input,
363 const std::size_t& input_offset) {
364 // Clear our current destinations
365 auto* current_head = info.GetHead();
366 while (current_head != nullptr) {
367 auto next_head = current_head->GetNextDestination();
368 current_head->SetNextDestination(nullptr);
369 current_head = next_head;
370 }
371 info.SetHead(nullptr);
372
373 s32 size = header.length;
374 // If the splitter bug is present, calculate fixed size
375 if (!bug_fixed) {
376 if (info_count > 0) {
377 const auto factor = data_count / info_count;
378 size = std::min(header.length, static_cast<s32>(factor));
379 } else {
380 size = 0;
381 }
382 }
383
384 if (size < 1) {
385 LOG_ERROR(Audio, "Invalid splitter info size! size={:X}", size);
386 return true;
387 }
388
389 auto* start_head = &GetData(header.resource_id_base);
390 current_head = start_head;
391 std::vector<s32_le> resource_ids(size - 1);
392 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
393 resource_ids.size() * sizeof(s32_le))) {
394 LOG_ERROR(Audio, "Buffer is an invalid size!");
395 return false;
396 }
397 std::memcpy(resource_ids.data(), input.data() + input_offset,
398 resource_ids.size() * sizeof(s32_le));
399
400 for (auto resource_id : resource_ids) {
401 auto* head = &GetData(resource_id);
402 current_head->SetNextDestination(head);
403 current_head = head;
404 }
405
406 info.SetHead(start_head);
407 info.SetHeadDepth(size);
408
409 return true;
410}
411
412NodeStates::NodeStates() = default;
413NodeStates::~NodeStates() = default;
414
415void NodeStates::Initialize(std::size_t node_count_) {
416 // Setup our work parameters
417 node_count = node_count_;
418 was_node_found.resize(node_count);
419 was_node_completed.resize(node_count);
420 index_list.resize(node_count);
421 index_stack.Reset(node_count * node_count);
422}
423
424bool NodeStates::Tsort(EdgeMatrix& edge_matrix) {
425 return DepthFirstSearch(edge_matrix);
426}
427
428std::size_t NodeStates::GetIndexPos() const {
429 return index_pos;
430}
431
432const std::vector<s32>& NodeStates::GetIndexList() const {
433 return index_list;
434}
435
436void NodeStates::PushTsortResult(s32 index) {
437 ASSERT(index < node_count);
438 index_list[index_pos++] = index;
439}
440
441bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
442 ResetState();
443 for (std::size_t i = 0; i < node_count; i++) {
444 const auto node_id = static_cast<s32>(i);
445
446 // If we don't have a state, send to our index stack for work
447 if (GetState(i) == NodeStates::State::NoState) {
448 index_stack.push(node_id);
449 }
450
451 // While we have work to do in our stack
452 while (index_stack.Count() > 0) {
453 // Get the current node
454 const auto current_stack_index = index_stack.top();
455 // Check if we've seen the node yet
456 const auto index_state = GetState(current_stack_index);
457 if (index_state == NodeStates::State::NoState) {
458 // Mark the node as seen
459 UpdateState(NodeStates::State::InFound, current_stack_index);
460 } else if (index_state == NodeStates::State::InFound) {
461 // We've seen this node before, mark it as completed
462 UpdateState(NodeStates::State::InCompleted, current_stack_index);
463 // Update our index list
464 PushTsortResult(current_stack_index);
465 // Pop the stack
466 index_stack.pop();
467 continue;
468 } else if (index_state == NodeStates::State::InCompleted) {
469 // If our node is already sorted, clear it
470 index_stack.pop();
471 continue;
472 }
473
474 const auto node_count = edge_matrix.GetNodeCount();
475 for (s32 j = 0; j < static_cast<s32>(node_count); j++) {
476 // Check if our node is connected to our edge matrix
477 if (!edge_matrix.Connected(current_stack_index, j)) {
478 continue;
479 }
480
481 // Check if our node exists
482 const auto node_state = GetState(j);
483 if (node_state == NodeStates::State::NoState) {
484 // Add more work
485 index_stack.push(j);
486 } else if (node_state == NodeStates::State::InFound) {
487 UNREACHABLE_MSG("Node start marked as found");
488 ResetState();
489 return false;
490 }
491 }
492 }
493 }
494 return true;
495}
496
497void NodeStates::ResetState() {
498 // Reset to the start of our index stack
499 index_pos = 0;
500 for (std::size_t i = 0; i < node_count; i++) {
501 // Mark all nodes as not found
502 was_node_found[i] = false;
503 // Mark all nodes as uncompleted
504 was_node_completed[i] = false;
505 // Mark all indexes as invalid
506 index_list[i] = -1;
507 }
508}
509
510void NodeStates::UpdateState(NodeStates::State state, std::size_t i) {
511 switch (state) {
512 case NodeStates::State::NoState:
513 was_node_found[i] = false;
514 was_node_completed[i] = false;
515 break;
516 case NodeStates::State::InFound:
517 was_node_found[i] = true;
518 was_node_completed[i] = false;
519 break;
520 case NodeStates::State::InCompleted:
521 was_node_found[i] = false;
522 was_node_completed[i] = true;
523 break;
524 }
525}
526
527NodeStates::State NodeStates::GetState(std::size_t i) {
528 ASSERT(i < node_count);
529 if (was_node_found[i]) {
530 // If our node exists in our found list
531 return NodeStates::State::InFound;
532 } else if (was_node_completed[i]) {
533 // If node is in the completed list
534 return NodeStates::State::InCompleted;
535 } else {
536 // If in neither
537 return NodeStates::State::NoState;
538 }
539}
540
541NodeStates::Stack::Stack() = default;
542NodeStates::Stack::~Stack() = default;
543
544void NodeStates::Stack::Reset(std::size_t size) {
545 // Mark our stack as empty
546 stack.resize(size);
547 stack_size = size;
548 stack_pos = 0;
549 std::fill(stack.begin(), stack.end(), 0);
550}
551
552void NodeStates::Stack::push(s32 val) {
553 ASSERT(stack_pos < stack_size);
554 stack[stack_pos++] = val;
555}
556
557std::size_t NodeStates::Stack::Count() const {
558 return stack_pos;
559}
560
561s32 NodeStates::Stack::top() const {
562 ASSERT(stack_pos > 0);
563 return stack[stack_pos - 1];
564}
565
566s32 NodeStates::Stack::pop() {
567 ASSERT(stack_pos > 0);
568 stack_pos--;
569 return stack[stack_pos];
570}
571
572EdgeMatrix::EdgeMatrix() = default;
573EdgeMatrix::~EdgeMatrix() = default;
574
575void EdgeMatrix::Initialize(std::size_t _node_count) {
576 node_count = _node_count;
577 edge_matrix.resize(node_count * node_count);
578}
579
580bool EdgeMatrix::Connected(s32 a, s32 b) {
581 return GetState(a, b);
582}
583
584void EdgeMatrix::Connect(s32 a, s32 b) {
585 SetState(a, b, true);
586}
587
588void EdgeMatrix::Disconnect(s32 a, s32 b) {
589 SetState(a, b, false);
590}
591
592void EdgeMatrix::RemoveEdges(s32 edge) {
593 for (std::size_t i = 0; i < node_count; i++) {
594 SetState(edge, static_cast<s32>(i), false);
595 }
596}
597
598std::size_t EdgeMatrix::GetNodeCount() const {
599 return node_count;
600}
601
602void EdgeMatrix::SetState(s32 a, s32 b, bool state) {
603 ASSERT(InRange(a, b));
604 edge_matrix.at(a * node_count + b) = state;
605}
606
607bool EdgeMatrix::GetState(s32 a, s32 b) {
608 ASSERT(InRange(a, b));
609 return edge_matrix.at(a * node_count + b);
610}
611
612bool EdgeMatrix::InRange(s32 a, s32 b) const {
613 const std::size_t pos = a * node_count + b;
614 return pos < (node_count * node_count);
615}
616
617} // namespace AudioCore
diff --git a/src/audio_core/splitter_context.h b/src/audio_core/splitter_context.h
new file mode 100644
index 000000000..ea6239fdb
--- /dev/null
+++ b/src/audio_core/splitter_context.h
@@ -0,0 +1,221 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <stack>
8#include <vector>
9#include "audio_core/common.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "common/swap.h"
13
14namespace AudioCore {
15class BehaviorInfo;
16
17class EdgeMatrix {
18public:
19 EdgeMatrix();
20 ~EdgeMatrix();
21
22 void Initialize(std::size_t _node_count);
23 bool Connected(s32 a, s32 b);
24 void Connect(s32 a, s32 b);
25 void Disconnect(s32 a, s32 b);
26 void RemoveEdges(s32 edge);
27 std::size_t GetNodeCount() const;
28
29private:
30 void SetState(s32 a, s32 b, bool state);
31 bool GetState(s32 a, s32 b);
32
33 bool InRange(s32 a, s32 b) const;
34 std::vector<bool> edge_matrix{};
35 std::size_t node_count{};
36};
37
38class NodeStates {
39public:
40 enum class State {
41 NoState = 0,
42 InFound = 1,
43 InCompleted = 2,
44 };
45
46 // Looks to be a fixed size stack. Placed within the NodeStates class based on symbols
47 class Stack {
48 public:
49 Stack();
50 ~Stack();
51
52 void Reset(std::size_t size);
53 void push(s32 val);
54 std::size_t Count() const;
55 s32 top() const;
56 s32 pop();
57
58 private:
59 std::vector<s32> stack{};
60 std::size_t stack_size{};
61 std::size_t stack_pos{};
62 };
63 NodeStates();
64 ~NodeStates();
65
66 void Initialize(std::size_t _node_count);
67 bool Tsort(EdgeMatrix& edge_matrix);
68 std::size_t GetIndexPos() const;
69 const std::vector<s32>& GetIndexList() const;
70
71private:
72 void PushTsortResult(s32 index);
73 bool DepthFirstSearch(EdgeMatrix& edge_matrix);
74 void ResetState();
75 void UpdateState(NodeStates::State state, std::size_t i);
76 NodeStates::State GetState(std::size_t i);
77
78 std::size_t node_count{};
79 std::vector<bool> was_node_found{};
80 std::vector<bool> was_node_completed{};
81 std::size_t index_pos{};
82 std::vector<s32> index_list{};
83 NodeStates::Stack index_stack{};
84};
85
86enum class SplitterMagic : u32_le {
87 SplitterHeader = Common::MakeMagic('S', 'N', 'D', 'H'),
88 DataHeader = Common::MakeMagic('S', 'N', 'D', 'D'),
89 InfoHeader = Common::MakeMagic('S', 'N', 'D', 'I'),
90};
91
92class SplitterInfo {
93public:
94 struct InHeader {
95 SplitterMagic magic{};
96 s32_le info_count{};
97 s32_le data_count{};
98 INSERT_PADDING_WORDS(5);
99 };
100 static_assert(sizeof(SplitterInfo::InHeader) == 0x20,
101 "SplitterInfo::InHeader is an invalid size");
102
103 struct InInfoPrams {
104 SplitterMagic magic{};
105 s32_le send_id{};
106 s32_le sample_rate{};
107 s32_le length{};
108 s32_le resource_id_base{};
109 };
110 static_assert(sizeof(SplitterInfo::InInfoPrams) == 0x14,
111 "SplitterInfo::InInfoPrams is an invalid size");
112
113 struct InDestinationParams {
114 SplitterMagic magic{};
115 s32_le splitter_id{};
116 std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> mix_volumes{};
117 s32_le mix_id{};
118 bool in_use{};
119 INSERT_PADDING_BYTES(3);
120 };
121 static_assert(sizeof(SplitterInfo::InDestinationParams) == 0x70,
122 "SplitterInfo::InDestinationParams is an invalid size");
123};
124
125class ServerSplitterDestinationData {
126public:
127 explicit ServerSplitterDestinationData(s32 id);
128 ~ServerSplitterDestinationData();
129
130 void Update(SplitterInfo::InDestinationParams& header);
131
132 ServerSplitterDestinationData* GetNextDestination();
133 const ServerSplitterDestinationData* GetNextDestination() const;
134 void SetNextDestination(ServerSplitterDestinationData* dest);
135 bool ValidMixId() const;
136 s32 GetMixId() const;
137 bool IsConfigured() const;
138 float GetMixVolume(std::size_t i) const;
139 const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& CurrentMixVolumes() const;
140 const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& LastMixVolumes() const;
141 void MarkDirty();
142 void UpdateInternalState();
143
144private:
145 bool needs_update{};
146 bool in_use{};
147 s32 id{};
148 s32 mix_id{};
149 std::array<float, AudioCommon::MAX_MIX_BUFFERS> current_mix_volumes{};
150 std::array<float, AudioCommon::MAX_MIX_BUFFERS> last_mix_volumes{};
151 ServerSplitterDestinationData* next = nullptr;
152};
153
154class ServerSplitterInfo {
155public:
156 explicit ServerSplitterInfo(s32 id);
157 ~ServerSplitterInfo();
158
159 void InitializeInfos();
160 void ClearNewConnectionFlag();
161 std::size_t Update(SplitterInfo::InInfoPrams& header);
162
163 ServerSplitterDestinationData* GetHead();
164 const ServerSplitterDestinationData* GetHead() const;
165 ServerSplitterDestinationData* GetData(std::size_t depth);
166 const ServerSplitterDestinationData* GetData(std::size_t depth) const;
167
168 bool HasNewConnection() const;
169 s32 GetLength() const;
170
171 void SetHead(ServerSplitterDestinationData* new_head);
172 void SetHeadDepth(s32 length);
173
174private:
175 s32 sample_rate{};
176 s32 id{};
177 s32 send_length{};
178 ServerSplitterDestinationData* head = nullptr;
179 bool new_connection{};
180};
181
182class SplitterContext {
183public:
184 SplitterContext();
185 ~SplitterContext();
186
187 void Initialize(BehaviorInfo& behavior_info, std::size_t splitter_count,
188 std::size_t data_count);
189
190 bool Update(const std::vector<u8>& input, std::size_t& input_offset, std::size_t& bytes_read);
191 bool UsingSplitter() const;
192
193 ServerSplitterInfo& GetInfo(std::size_t i);
194 const ServerSplitterInfo& GetInfo(std::size_t i) const;
195 ServerSplitterDestinationData& GetData(std::size_t i);
196 const ServerSplitterDestinationData& GetData(std::size_t i) const;
197 ServerSplitterDestinationData* GetDestinationData(std::size_t info, std::size_t data);
198 const ServerSplitterDestinationData* GetDestinationData(std::size_t info,
199 std::size_t data) const;
200 void UpdateInternalState();
201
202 std::size_t GetInfoCount() const;
203 std::size_t GetDataCount() const;
204
205private:
206 void Setup(std::size_t info_count, std::size_t data_count, bool is_splitter_bug_fixed);
207 bool UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
208 std::size_t& bytes_read, s32 in_splitter_count);
209 bool UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
210 std::size_t& bytes_read, s32 in_data_count);
211 bool RecomposeDestination(ServerSplitterInfo& info, SplitterInfo::InInfoPrams& header,
212 const std::vector<u8>& input, const std::size_t& input_offset);
213
214 std::vector<ServerSplitterInfo> infos{};
215 std::vector<ServerSplitterDestinationData> datas{};
216
217 std::size_t info_count{};
218 std::size_t data_count{};
219 bool bug_fixed{};
220};
221} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 7be5d5087..cb33926bc 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -104,11 +104,7 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
104 104
105 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 105 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
106 106
107 const auto time_stretch_delta = Settings::values.enable_audio_stretching.GetValue() 107 core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer) - ns_late, release_event, {});
108 ? std::chrono::nanoseconds::zero()
109 : ns_late;
110 const auto future_time = GetBufferReleaseNS(*active_buffer) - time_stretch_delta;
111 core_timing.ScheduleEvent(future_time, release_event, {});
112} 108}
113 109
114void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) { 110void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) {
diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp
new file mode 100644
index 000000000..1d8f69844
--- /dev/null
+++ b/src/audio_core/voice_context.cpp
@@ -0,0 +1,526 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/behavior_info.h"
6#include "audio_core/voice_context.h"
7#include "core/memory.h"
8
9namespace AudioCore {
10
11ServerVoiceChannelResource::ServerVoiceChannelResource(s32 id) : id(id) {}
12ServerVoiceChannelResource::~ServerVoiceChannelResource() = default;
13
14bool ServerVoiceChannelResource::InUse() const {
15 return in_use;
16}
17
18float ServerVoiceChannelResource::GetCurrentMixVolumeAt(std::size_t i) const {
19 ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
20 return mix_volume.at(i);
21}
22
23float ServerVoiceChannelResource::GetLastMixVolumeAt(std::size_t i) const {
24 ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
25 return last_mix_volume.at(i);
26}
27
28void ServerVoiceChannelResource::Update(VoiceChannelResource::InParams& in_params) {
29 in_use = in_params.in_use;
30 // Update our mix volumes only if it's in use
31 if (in_params.in_use) {
32 mix_volume = in_params.mix_volume;
33 }
34}
35
36void ServerVoiceChannelResource::UpdateLastMixVolumes() {
37 last_mix_volume = mix_volume;
38}
39
40const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
41ServerVoiceChannelResource::GetCurrentMixVolume() const {
42 return mix_volume;
43}
44
45const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
46ServerVoiceChannelResource::GetLastMixVolume() const {
47 return last_mix_volume;
48}
49
50ServerVoiceInfo::ServerVoiceInfo() {
51 Initialize();
52}
53ServerVoiceInfo::~ServerVoiceInfo() = default;
54
55void ServerVoiceInfo::Initialize() {
56 in_params.in_use = false;
57 in_params.node_id = 0;
58 in_params.id = 0;
59 in_params.current_playstate = ServerPlayState::Stop;
60 in_params.priority = 255;
61 in_params.sample_rate = 0;
62 in_params.sample_format = SampleFormat::Invalid;
63 in_params.channel_count = 0;
64 in_params.pitch = 0.0f;
65 in_params.volume = 0.0f;
66 in_params.last_volume = 0.0f;
67 in_params.biquad_filter.fill({});
68 in_params.wave_buffer_count = 0;
69 in_params.wave_bufffer_head = 0;
70 in_params.mix_id = AudioCommon::NO_MIX;
71 in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
72 in_params.additional_params_address = 0;
73 in_params.additional_params_size = 0;
74 in_params.is_new = false;
75 out_params.played_sample_count = 0;
76 out_params.wave_buffer_consumed = 0;
77 in_params.voice_drop_flag = false;
78 in_params.buffer_mapped = false;
79 in_params.wave_buffer_flush_request_count = 0;
80 in_params.was_biquad_filter_enabled.fill(false);
81
82 for (auto& wave_buffer : in_params.wave_buffer) {
83 wave_buffer.start_sample_offset = 0;
84 wave_buffer.end_sample_offset = 0;
85 wave_buffer.is_looping = false;
86 wave_buffer.end_of_stream = false;
87 wave_buffer.buffer_address = 0;
88 wave_buffer.buffer_size = 0;
89 wave_buffer.context_address = 0;
90 wave_buffer.context_size = 0;
91 wave_buffer.sent_to_dsp = true;
92 }
93
94 stored_samples.clear();
95}
96
97void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
98 BehaviorInfo& behavior_info) {
99 in_params.in_use = voice_in.is_in_use;
100 in_params.id = voice_in.id;
101 in_params.node_id = voice_in.node_id;
102 in_params.last_playstate = in_params.current_playstate;
103 switch (voice_in.play_state) {
104 case PlayState::Paused:
105 in_params.current_playstate = ServerPlayState::Paused;
106 break;
107 case PlayState::Stopped:
108 if (in_params.current_playstate != ServerPlayState::Stop) {
109 in_params.current_playstate = ServerPlayState::RequestStop;
110 }
111 break;
112 case PlayState::Started:
113 in_params.current_playstate = ServerPlayState::Play;
114 break;
115 default:
116 UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state);
117 break;
118 }
119
120 in_params.priority = voice_in.priority;
121 in_params.sorting_order = voice_in.sorting_order;
122 in_params.sample_rate = voice_in.sample_rate;
123 in_params.sample_format = voice_in.sample_format;
124 in_params.channel_count = voice_in.channel_count;
125 in_params.pitch = voice_in.pitch;
126 in_params.volume = voice_in.volume;
127 in_params.biquad_filter = voice_in.biquad_filter;
128 in_params.wave_buffer_count = voice_in.wave_buffer_count;
129 in_params.wave_bufffer_head = voice_in.wave_buffer_head;
130 if (behavior_info.IsFlushVoiceWaveBuffersSupported()) {
131 in_params.wave_buffer_flush_request_count += voice_in.wave_buffer_flush_request_count;
132 }
133 in_params.mix_id = voice_in.mix_id;
134 if (behavior_info.IsSplitterSupported()) {
135 in_params.splitter_info_id = voice_in.splitter_info_id;
136 } else {
137 in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
138 }
139
140 std::memcpy(in_params.voice_channel_resource_id.data(),
141 voice_in.voice_channel_resource_ids.data(),
142 sizeof(s32) * in_params.voice_channel_resource_id.size());
143
144 if (behavior_info.IsVoicePlayedSampleCountResetAtLoopPointSupported()) {
145 in_params.behavior_flags.is_played_samples_reset_at_loop_point =
146 voice_in.behavior_flags.is_played_samples_reset_at_loop_point;
147 } else {
148 in_params.behavior_flags.is_played_samples_reset_at_loop_point.Assign(0);
149 }
150 if (behavior_info.IsVoicePitchAndSrcSkippedSupported()) {
151 in_params.behavior_flags.is_pitch_and_src_skipped =
152 voice_in.behavior_flags.is_pitch_and_src_skipped;
153 } else {
154 in_params.behavior_flags.is_pitch_and_src_skipped.Assign(0);
155 }
156
157 if (voice_in.is_voice_drop_flag_clear_requested) {
158 in_params.voice_drop_flag = false;
159 }
160
161 if (in_params.additional_params_address != voice_in.additional_params_address ||
162 in_params.additional_params_size != voice_in.additional_params_size) {
163 in_params.additional_params_address = voice_in.additional_params_address;
164 in_params.additional_params_size = voice_in.additional_params_size;
165 // TODO(ogniK): Reattach buffer, do we actually need to? Maybe just signal to the DSP that
166 // our context is new
167 }
168}
169
170void ServerVoiceInfo::UpdateWaveBuffers(
171 const VoiceInfo::InParams& voice_in,
172 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states,
173 BehaviorInfo& behavior_info) {
174 if (voice_in.is_new) {
175 // Initialize our wave buffers
176 for (auto& wave_buffer : in_params.wave_buffer) {
177 wave_buffer.start_sample_offset = 0;
178 wave_buffer.end_sample_offset = 0;
179 wave_buffer.is_looping = false;
180 wave_buffer.end_of_stream = false;
181 wave_buffer.buffer_address = 0;
182 wave_buffer.buffer_size = 0;
183 wave_buffer.context_address = 0;
184 wave_buffer.context_size = 0;
185 wave_buffer.sent_to_dsp = true;
186 }
187
188 // Mark all our wave buffers as invalid
189 for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count);
190 channel++) {
191 for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) {
192 is_valid = false;
193 }
194 }
195 }
196
197 // Update our wave buffers
198 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
199 // Assume that we have at least 1 channel voice state
200 const auto have_valid_wave_buffer = voice_states[0]->is_wave_buffer_valid[i];
201
202 UpdateWaveBuffer(in_params.wave_buffer[i], voice_in.wave_buffer[i], in_params.sample_format,
203 have_valid_wave_buffer, behavior_info);
204 }
205}
206
207void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
208 const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
209 bool is_buffer_valid, BehaviorInfo& behavior_info) {
210 if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) {
211 out_wavebuffer.buffer_address = 0;
212 out_wavebuffer.buffer_size = 0;
213 }
214
215 if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) {
216 // Validate sample offset sizings
217 if (sample_format == SampleFormat::Pcm16) {
218 const auto buffer_size = in_wave_buffer.buffer_size;
219 if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 ||
220 (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) ||
221 (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) {
222 // TODO(ogniK): Write error info
223 return;
224 }
225 }
226 // TODO(ogniK): ADPCM Size error
227
228 out_wavebuffer.sent_to_dsp = false;
229 out_wavebuffer.start_sample_offset = in_wave_buffer.start_sample_offset;
230 out_wavebuffer.end_sample_offset = in_wave_buffer.end_sample_offset;
231 out_wavebuffer.is_looping = in_wave_buffer.is_looping;
232 out_wavebuffer.end_of_stream = in_wave_buffer.end_of_stream;
233
234 out_wavebuffer.buffer_address = in_wave_buffer.buffer_address;
235 out_wavebuffer.buffer_size = in_wave_buffer.buffer_size;
236 out_wavebuffer.context_address = in_wave_buffer.context_address;
237 out_wavebuffer.context_size = in_wave_buffer.context_size;
238 in_params.buffer_mapped =
239 in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0;
240 // TODO(ogniK): Pool mapper attachment
241 // TODO(ogniK): IsAdpcmLoopContextBugFixed
242 }
243}
244
245void ServerVoiceInfo::WriteOutStatus(
246 VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
247 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) {
248 if (voice_in.is_new) {
249 in_params.is_new = true;
250 voice_out.wave_buffer_consumed = 0;
251 voice_out.played_sample_count = 0;
252 voice_out.voice_dropped = false;
253 } else if (!in_params.is_new) {
254 voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed;
255 voice_out.played_sample_count = voice_states[0]->played_sample_count;
256 voice_out.voice_dropped = in_params.voice_drop_flag;
257 } else {
258 voice_out.wave_buffer_consumed = 0;
259 voice_out.played_sample_count = 0;
260 voice_out.voice_dropped = false;
261 }
262}
263
264const ServerVoiceInfo::InParams& ServerVoiceInfo::GetInParams() const {
265 return in_params;
266}
267
268ServerVoiceInfo::InParams& ServerVoiceInfo::GetInParams() {
269 return in_params;
270}
271
272const ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() const {
273 return out_params;
274}
275
276ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() {
277 return out_params;
278}
279
280bool ServerVoiceInfo::ShouldSkip() const {
281 // TODO(ogniK): Handle unmapped wave buffers or parameters
282 return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag;
283}
284
285bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) {
286 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> dsp_voice_states{};
287 if (in_params.is_new) {
288 ResetResources(voice_context);
289 in_params.last_volume = in_params.volume;
290 in_params.is_new = false;
291 }
292
293 const s32 channel_count = in_params.channel_count;
294 for (s32 i = 0; i < channel_count; i++) {
295 const auto channel_resource = in_params.voice_channel_resource_id[i];
296 dsp_voice_states[i] =
297 &voice_context.GetDspSharedState(static_cast<std::size_t>(channel_resource));
298 }
299 return UpdateParametersForCommandGeneration(dsp_voice_states);
300}
301
302void ServerVoiceInfo::ResetResources(VoiceContext& voice_context) {
303 const s32 channel_count = in_params.channel_count;
304 for (s32 i = 0; i < channel_count; i++) {
305 const auto channel_resource = in_params.voice_channel_resource_id[i];
306 auto& dsp_state =
307 voice_context.GetDspSharedState(static_cast<std::size_t>(channel_resource));
308 dsp_state = {};
309 voice_context.GetChannelResource(static_cast<std::size_t>(channel_resource))
310 .UpdateLastMixVolumes();
311 }
312}
313
314bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
315 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states) {
316 const s32 channel_count = in_params.channel_count;
317 if (in_params.wave_buffer_flush_request_count > 0) {
318 FlushWaveBuffers(in_params.wave_buffer_flush_request_count, dsp_voice_states,
319 channel_count);
320 in_params.wave_buffer_flush_request_count = 0;
321 }
322
323 switch (in_params.current_playstate) {
324 case ServerPlayState::Play: {
325 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
326 if (!in_params.wave_buffer[i].sent_to_dsp) {
327 for (s32 channel = 0; channel < channel_count; channel++) {
328 dsp_voice_states[channel]->is_wave_buffer_valid[i] = true;
329 }
330 in_params.wave_buffer[i].sent_to_dsp = true;
331 }
332 }
333 in_params.should_depop = false;
334 return HasValidWaveBuffer(dsp_voice_states[0]);
335 }
336 case ServerPlayState::Paused:
337 case ServerPlayState::Stop: {
338 in_params.should_depop = in_params.last_playstate == ServerPlayState::Play;
339 return in_params.should_depop;
340 }
341 case ServerPlayState::RequestStop: {
342 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
343 in_params.wave_buffer[i].sent_to_dsp = true;
344 for (s32 channel = 0; channel < channel_count; channel++) {
345 auto* dsp_state = dsp_voice_states[channel];
346
347 if (dsp_state->is_wave_buffer_valid[i]) {
348 dsp_state->wave_buffer_index =
349 (dsp_state->wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
350 dsp_state->wave_buffer_consumed++;
351 }
352
353 dsp_state->is_wave_buffer_valid[i] = false;
354 }
355 }
356
357 for (s32 channel = 0; channel < channel_count; channel++) {
358 auto* dsp_state = dsp_voice_states[channel];
359 dsp_state->offset = 0;
360 dsp_state->played_sample_count = 0;
361 dsp_state->fraction = 0;
362 dsp_state->sample_history.fill(0);
363 dsp_state->context = {};
364 }
365
366 in_params.current_playstate = ServerPlayState::Stop;
367 in_params.should_depop = in_params.last_playstate == ServerPlayState::Play;
368 return in_params.should_depop;
369 }
370 default:
371 UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate);
372 }
373
374 return false;
375}
376
377void ServerVoiceInfo::FlushWaveBuffers(
378 u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
379 s32 channel_count) {
380 auto wave_head = in_params.wave_bufffer_head;
381
382 for (u8 i = 0; i < flush_count; i++) {
383 in_params.wave_buffer[wave_head].sent_to_dsp = true;
384 for (s32 channel = 0; channel < channel_count; channel++) {
385 auto* dsp_state = dsp_voice_states[channel];
386 dsp_state->wave_buffer_consumed++;
387 dsp_state->is_wave_buffer_valid[wave_head] = false;
388 dsp_state->wave_buffer_index =
389 (dsp_state->wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
390 }
391 wave_head = (wave_head + 1) % AudioCommon::MAX_WAVE_BUFFERS;
392 }
393}
394
395bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const {
396 const auto& valid_wb = state->is_wave_buffer_valid;
397 return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
398}
399
400VoiceContext::VoiceContext(std::size_t voice_count) : voice_count(voice_count) {
401 for (std::size_t i = 0; i < voice_count; i++) {
402 voice_channel_resources.emplace_back(static_cast<s32>(i));
403 sorted_voice_info.push_back(&voice_info.emplace_back());
404 voice_states.emplace_back();
405 dsp_voice_states.emplace_back();
406 }
407}
408
409VoiceContext::~VoiceContext() {
410 sorted_voice_info.clear();
411}
412
413std::size_t VoiceContext::GetVoiceCount() const {
414 return voice_count;
415}
416
417ServerVoiceChannelResource& VoiceContext::GetChannelResource(std::size_t i) {
418 ASSERT(i < voice_count);
419 return voice_channel_resources.at(i);
420}
421
422const ServerVoiceChannelResource& VoiceContext::GetChannelResource(std::size_t i) const {
423 ASSERT(i < voice_count);
424 return voice_channel_resources.at(i);
425}
426
427VoiceState& VoiceContext::GetState(std::size_t i) {
428 ASSERT(i < voice_count);
429 return voice_states.at(i);
430}
431
432const VoiceState& VoiceContext::GetState(std::size_t i) const {
433 ASSERT(i < voice_count);
434 return voice_states.at(i);
435}
436
437VoiceState& VoiceContext::GetDspSharedState(std::size_t i) {
438 ASSERT(i < voice_count);
439 return dsp_voice_states.at(i);
440}
441
442const VoiceState& VoiceContext::GetDspSharedState(std::size_t i) const {
443 ASSERT(i < voice_count);
444 return dsp_voice_states.at(i);
445}
446
447ServerVoiceInfo& VoiceContext::GetInfo(std::size_t i) {
448 ASSERT(i < voice_count);
449 return voice_info.at(i);
450}
451
452const ServerVoiceInfo& VoiceContext::GetInfo(std::size_t i) const {
453 ASSERT(i < voice_count);
454 return voice_info.at(i);
455}
456
457ServerVoiceInfo& VoiceContext::GetSortedInfo(std::size_t i) {
458 ASSERT(i < voice_count);
459 return *sorted_voice_info.at(i);
460}
461
462const ServerVoiceInfo& VoiceContext::GetSortedInfo(std::size_t i) const {
463 ASSERT(i < voice_count);
464 return *sorted_voice_info.at(i);
465}
466
467s32 VoiceContext::DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer, s32 channel,
468 s32 channel_count, s32 buffer_offset, s32 sample_count,
469 Core::Memory::Memory& memory) {
470 if (wave_buffer->buffer_address == 0) {
471 return 0;
472 }
473 if (wave_buffer->buffer_size == 0) {
474 return 0;
475 }
476 if (wave_buffer->end_sample_offset < wave_buffer->start_sample_offset) {
477 return 0;
478 }
479
480 const auto samples_remaining =
481 (wave_buffer->end_sample_offset - wave_buffer->start_sample_offset) - buffer_offset;
482 const auto start_offset = (wave_buffer->start_sample_offset + buffer_offset) * channel_count;
483 const auto buffer_pos = wave_buffer->buffer_address + start_offset;
484
485 s16* buffer_data = reinterpret_cast<s16*>(memory.GetPointer(buffer_pos));
486
487 const auto samples_processed = std::min(sample_count, samples_remaining);
488
489 // Fast path
490 if (channel_count == 1) {
491 for (std::size_t i = 0; i < samples_processed; i++) {
492 output_buffer[i] = buffer_data[i];
493 }
494 } else {
495 for (std::size_t i = 0; i < samples_processed; i++) {
496 output_buffer[i] = buffer_data[i * channel_count + channel];
497 }
498 }
499
500 return samples_processed;
501}
502
503void VoiceContext::SortInfo() {
504 for (std::size_t i = 0; i < voice_count; i++) {
505 sorted_voice_info[i] = &voice_info[i];
506 }
507
508 std::sort(sorted_voice_info.begin(), sorted_voice_info.end(),
509 [](const ServerVoiceInfo* lhs, const ServerVoiceInfo* rhs) {
510 const auto& lhs_in = lhs->GetInParams();
511 const auto& rhs_in = rhs->GetInParams();
512 // Sort by priority
513 if (lhs_in.priority != rhs_in.priority) {
514 return lhs_in.priority > rhs_in.priority;
515 } else {
516 // If the priorities match, sort by sorting order
517 return lhs_in.sorting_order > rhs_in.sorting_order;
518 }
519 });
520}
521
522void VoiceContext::UpdateStateByDspShared() {
523 voice_states = dsp_voice_states;
524}
525
526} // namespace AudioCore
diff --git a/src/audio_core/voice_context.h b/src/audio_core/voice_context.h
new file mode 100644
index 000000000..59d3d7dfb
--- /dev/null
+++ b/src/audio_core/voice_context.h
@@ -0,0 +1,296 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "audio_core/algorithm/interpolate.h"
9#include "audio_core/codec.h"
10#include "audio_core/common.h"
11#include "common/bit_field.h"
12#include "common/common_funcs.h"
13#include "common/common_types.h"
14
15namespace Core::Memory {
16class Memory;
17}
18
19namespace AudioCore {
20
21class BehaviorInfo;
22class VoiceContext;
23
24enum class SampleFormat : u8 {
25 Invalid = 0,
26 Pcm8 = 1,
27 Pcm16 = 2,
28 Pcm24 = 3,
29 Pcm32 = 4,
30 PcmFloat = 5,
31 Adpcm = 6,
32};
33
34enum class PlayState : u8 {
35 Started = 0,
36 Stopped = 1,
37 Paused = 2,
38};
39
40enum class ServerPlayState {
41 Play = 0,
42 Stop = 1,
43 RequestStop = 2,
44 Paused = 3,
45};
46
47struct BiquadFilterParameter {
48 bool enabled{};
49 INSERT_PADDING_BYTES(1);
50 std::array<s16, 3> numerator{};
51 std::array<s16, 2> denominator{};
52};
53static_assert(sizeof(BiquadFilterParameter) == 0xc, "BiquadFilterParameter is an invalid size");
54
55struct WaveBuffer {
56 u64_le buffer_address{};
57 u64_le buffer_size{};
58 s32_le start_sample_offset{};
59 s32_le end_sample_offset{};
60 u8 is_looping{};
61 u8 end_of_stream{};
62 u8 sent_to_server{};
63 INSERT_PADDING_BYTES(5);
64 u64 context_address{};
65 u64 context_size{};
66 INSERT_PADDING_BYTES(8);
67};
68static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size");
69
70struct ServerWaveBuffer {
71 VAddr buffer_address{};
72 std::size_t buffer_size{};
73 s32 start_sample_offset{};
74 s32 end_sample_offset{};
75 bool is_looping{};
76 bool end_of_stream{};
77 VAddr context_address{};
78 std::size_t context_size{};
79 bool sent_to_dsp{true};
80};
81
82struct BehaviorFlags {
83 BitField<0, 1, u16> is_played_samples_reset_at_loop_point;
84 BitField<1, 1, u16> is_pitch_and_src_skipped;
85};
86static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
87
88struct ADPCMContext {
89 u16 header{};
90 s16 yn1{};
91 s16 yn2{};
92};
93static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
94
95struct VoiceState {
96 s64 played_sample_count{};
97 s32 offset{};
98 s32 wave_buffer_index{};
99 std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{};
100 s32 wave_buffer_consumed{};
101 std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{};
102 s32 fraction{};
103 VAddr context_address{};
104 Codec::ADPCM_Coeff coeff{};
105 ADPCMContext context{};
106 std::array<s64, 2> biquad_filter_state{};
107 std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{};
108 u32 external_context_size{};
109 bool is_external_context_used{};
110 bool voice_dropped{};
111};
112
113class VoiceChannelResource {
114public:
115 struct InParams {
116 s32_le id{};
117 std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> mix_volume{};
118 bool in_use{};
119 INSERT_PADDING_BYTES(11);
120 };
121 static_assert(sizeof(VoiceChannelResource::InParams) == 0x70, "InParams is an invalid size");
122};
123
124class ServerVoiceChannelResource {
125public:
126 explicit ServerVoiceChannelResource(s32 id);
127 ~ServerVoiceChannelResource();
128
129 bool InUse() const;
130 float GetCurrentMixVolumeAt(std::size_t i) const;
131 float GetLastMixVolumeAt(std::size_t i) const;
132 void Update(VoiceChannelResource::InParams& in_params);
133 void UpdateLastMixVolumes();
134
135 const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& GetCurrentMixVolume() const;
136 const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& GetLastMixVolume() const;
137
138private:
139 s32 id{};
140 std::array<float, AudioCommon::MAX_MIX_BUFFERS> mix_volume{};
141 std::array<float, AudioCommon::MAX_MIX_BUFFERS> last_mix_volume{};
142 bool in_use{};
143};
144
145class VoiceInfo {
146public:
147 struct InParams {
148 s32_le id{};
149 u32_le node_id{};
150 u8 is_new{};
151 u8 is_in_use{};
152 PlayState play_state{};
153 SampleFormat sample_format{};
154 s32_le sample_rate{};
155 s32_le priority{};
156 s32_le sorting_order{};
157 s32_le channel_count{};
158 float_le pitch{};
159 float_le volume{};
160 std::array<BiquadFilterParameter, 2> biquad_filter{};
161 s32_le wave_buffer_count{};
162 s16_le wave_buffer_head{};
163 INSERT_PADDING_BYTES(6);
164 u64_le additional_params_address{};
165 u64_le additional_params_size{};
166 s32_le mix_id{};
167 s32_le splitter_info_id{};
168 std::array<WaveBuffer, 4> wave_buffer{};
169 std::array<u32_le, 6> voice_channel_resource_ids{};
170 // TODO(ogniK): Remaining flags
171 u8 is_voice_drop_flag_clear_requested{};
172 u8 wave_buffer_flush_request_count{};
173 INSERT_PADDING_BYTES(2);
174 BehaviorFlags behavior_flags{};
175 INSERT_PADDING_BYTES(16);
176 };
177 static_assert(sizeof(VoiceInfo::InParams) == 0x170, "InParams is an invalid size");
178
179 struct OutParams {
180 u64_le played_sample_count{};
181 u32_le wave_buffer_consumed{};
182 u8 voice_dropped{};
183 INSERT_PADDING_BYTES(3);
184 };
185 static_assert(sizeof(VoiceInfo::OutParams) == 0x10, "OutParams is an invalid size");
186};
187
188class ServerVoiceInfo {
189public:
190 struct InParams {
191 bool in_use{};
192 bool is_new{};
193 bool should_depop{};
194 SampleFormat sample_format{};
195 s32 sample_rate{};
196 s32 channel_count{};
197 s32 id{};
198 s32 node_id{};
199 s32 mix_id{};
200 ServerPlayState current_playstate{};
201 ServerPlayState last_playstate{};
202 s32 priority{};
203 s32 sorting_order{};
204 float pitch{};
205 float volume{};
206 float last_volume{};
207 std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{};
208 s32 wave_buffer_count{};
209 s16 wave_bufffer_head{};
210 INSERT_PADDING_BYTES(2);
211 BehaviorFlags behavior_flags{};
212 VAddr additional_params_address{};
213 std::size_t additional_params_size{};
214 std::array<ServerWaveBuffer, AudioCommon::MAX_WAVE_BUFFERS> wave_buffer{};
215 std::array<s32, AudioCommon::MAX_CHANNEL_COUNT> voice_channel_resource_id{};
216 s32 splitter_info_id{};
217 u8 wave_buffer_flush_request_count{};
218 bool voice_drop_flag{};
219 bool buffer_mapped{};
220 std::array<bool, AudioCommon::MAX_BIQUAD_FILTERS> was_biquad_filter_enabled{};
221 };
222
223 struct OutParams {
224 s64 played_sample_count{};
225 s32 wave_buffer_consumed{};
226 };
227
228 ServerVoiceInfo();
229 ~ServerVoiceInfo();
230 void Initialize();
231 void UpdateParameters(const VoiceInfo::InParams& voice_in, BehaviorInfo& behavior_info);
232 void UpdateWaveBuffers(const VoiceInfo::InParams& voice_in,
233 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states,
234 BehaviorInfo& behavior_info);
235 void UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, const WaveBuffer& in_wave_buffer,
236 SampleFormat sample_format, bool is_buffer_valid,
237 BehaviorInfo& behavior_info);
238 void WriteOutStatus(VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
239 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states);
240
241 const InParams& GetInParams() const;
242 InParams& GetInParams();
243
244 const OutParams& GetOutParams() const;
245 OutParams& GetOutParams();
246
247 bool ShouldSkip() const;
248 bool UpdateForCommandGeneration(VoiceContext& voice_context);
249 void ResetResources(VoiceContext& voice_context);
250 bool UpdateParametersForCommandGeneration(
251 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states);
252 void FlushWaveBuffers(u8 flush_count,
253 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
254 s32 channel_count);
255
256private:
257 std::vector<s16> stored_samples;
258 InParams in_params{};
259 OutParams out_params{};
260
261 bool HasValidWaveBuffer(const VoiceState* state) const;
262};
263
264class VoiceContext {
265public:
266 VoiceContext(std::size_t voice_count);
267 ~VoiceContext();
268
269 std::size_t GetVoiceCount() const;
270 ServerVoiceChannelResource& GetChannelResource(std::size_t i);
271 const ServerVoiceChannelResource& GetChannelResource(std::size_t i) const;
272 VoiceState& GetState(std::size_t i);
273 const VoiceState& GetState(std::size_t i) const;
274 VoiceState& GetDspSharedState(std::size_t i);
275 const VoiceState& GetDspSharedState(std::size_t i) const;
276 ServerVoiceInfo& GetInfo(std::size_t i);
277 const ServerVoiceInfo& GetInfo(std::size_t i) const;
278 ServerVoiceInfo& GetSortedInfo(std::size_t i);
279 const ServerVoiceInfo& GetSortedInfo(std::size_t i) const;
280
281 s32 DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer, s32 channel,
282 s32 channel_count, s32 buffer_offset, s32 sample_count,
283 Core::Memory::Memory& memory);
284 void SortInfo();
285 void UpdateStateByDspShared();
286
287private:
288 std::size_t voice_count{};
289 std::vector<ServerVoiceChannelResource> voice_channel_resources{};
290 std::vector<VoiceState> voice_states{};
291 std::vector<VoiceState> dsp_voice_states{};
292 std::vector<ServerVoiceInfo> voice_info{};
293 std::vector<ServerVoiceInfo*> sorted_voice_info{};
294};
295
296} // namespace AudioCore
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b96ca9374..d0c405ec7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -126,6 +126,8 @@ add_library(core STATIC
126 file_sys/vfs_vector.h 126 file_sys/vfs_vector.h
127 file_sys/xts_archive.cpp 127 file_sys/xts_archive.cpp
128 file_sys/xts_archive.h 128 file_sys/xts_archive.h
129 frontend/applets/controller.cpp
130 frontend/applets/controller.h
129 frontend/applets/error.cpp 131 frontend/applets/error.cpp
130 frontend/applets/error.h 132 frontend/applets/error.h
131 frontend/applets/general_frontend.cpp 133 frontend/applets/general_frontend.cpp
@@ -244,6 +246,8 @@ add_library(core STATIC
244 hle/service/am/applet_oe.h 246 hle/service/am/applet_oe.h
245 hle/service/am/applets/applets.cpp 247 hle/service/am/applets/applets.cpp
246 hle/service/am/applets/applets.h 248 hle/service/am/applets/applets.h
249 hle/service/am/applets/controller.cpp
250 hle/service/am/applets/controller.h
247 hle/service/am/applets/error.cpp 251 hle/service/am/applets/error.cpp
248 hle/service/am/applets/error.h 252 hle/service/am/applets/error.h
249 hle/service/am/applets/general_backend.cpp 253 hle/service/am/applets/general_backend.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c2c0eec0b..df81e8e2c 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -188,7 +188,6 @@ struct System::Impl {
188 if (!gpu_core) { 188 if (!gpu_core) {
189 return ResultStatus::ErrorVideoCore; 189 return ResultStatus::ErrorVideoCore;
190 } 190 }
191 gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
192 191
193 is_powered_on = true; 192 is_powered_on = true;
194 exit_lock = false; 193 exit_lock = false;
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
new file mode 100644
index 000000000..4505da758
--- /dev/null
+++ b/src/core/frontend/applets/controller.cpp
@@ -0,0 +1,81 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/frontend/applets/controller.h"
9#include "core/hle/service/hid/controllers/npad.h"
10#include "core/hle/service/hid/hid.h"
11#include "core/hle/service/sm/sm.h"
12
13namespace Core::Frontend {
14
15ControllerApplet::~ControllerApplet() = default;
16
17DefaultControllerApplet::~DefaultControllerApplet() = default;
18
19void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
20 ControllerParameters parameters) const {
21 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
22
23 auto& npad =
24 Core::System::GetInstance()
25 .ServiceManager()
26 .GetService<Service::HID::Hid>("hid")
27 ->GetAppletResource()
28 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
29
30 auto& players = Settings::values.players;
31
32 const std::size_t min_supported_players =
33 parameters.enable_single_mode ? 1 : parameters.min_players;
34
35 // Disconnect Handheld first.
36 npad.DisconnectNPadAtIndex(8);
37
38 // Deduce the best configuration based on the input parameters.
39 for (std::size_t index = 0; index < players.size() - 2; ++index) {
40 // First, disconnect all controllers regardless of the value of keep_controllers_connected.
41 // This makes it easy to connect the desired controllers.
42 npad.DisconnectNPadAtIndex(index);
43
44 // Only connect the minimum number of required players.
45 if (index >= min_supported_players) {
46 continue;
47 }
48
49 // Connect controllers based on the following priority list from highest to lowest priority:
50 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
51 if (parameters.allow_pro_controller) {
52 npad.AddNewControllerAt(
53 npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
54 } else if (parameters.allow_dual_joycons) {
55 npad.AddNewControllerAt(
56 npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
57 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
58 // Assign left joycons to even player indices and right joycons to odd player indices.
59 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
60 // a right Joycon for Player 2 in 2 Player Assist mode.
61 if (index % 2 == 0) {
62 npad.AddNewControllerAt(
63 npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
64 } else {
65 npad.AddNewControllerAt(
66 npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
67 }
68 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
69 !Settings::values.use_docked_mode) {
70 // We should *never* reach here under any normal circumstances.
71 npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
72 index);
73 } else {
74 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
75 }
76 }
77
78 callback();
79}
80
81} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
new file mode 100644
index 000000000..a227f15cd
--- /dev/null
+++ b/src/core/frontend/applets/controller.h
@@ -0,0 +1,48 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9#include "common/common_types.h"
10
11namespace Core::Frontend {
12
13using BorderColor = std::array<u8, 4>;
14using ExplainText = std::array<char, 0x81>;
15
16struct ControllerParameters {
17 s8 min_players{};
18 s8 max_players{};
19 bool keep_controllers_connected{};
20 bool enable_single_mode{};
21 bool enable_border_color{};
22 std::vector<BorderColor> border_colors{};
23 bool enable_explain_text{};
24 std::vector<ExplainText> explain_text{};
25 bool allow_pro_controller{};
26 bool allow_handheld{};
27 bool allow_dual_joycons{};
28 bool allow_left_joycon{};
29 bool allow_right_joycon{};
30};
31
32class ControllerApplet {
33public:
34 virtual ~ControllerApplet();
35
36 virtual void ReconfigureControllers(std::function<void()> callback,
37 ControllerParameters parameters) const = 0;
38};
39
40class DefaultControllerApplet final : public ControllerApplet {
41public:
42 ~DefaultControllerApplet() override;
43
44 void ReconfigureControllers(std::function<void()> callback,
45 ControllerParameters parameters) const override;
46};
47
48} // namespace Core::Frontend
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index c3261f3e6..4e0800f9a 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/applets/controller.h"
8#include "core/frontend/applets/error.h" 9#include "core/frontend/applets/error.h"
9#include "core/frontend/applets/general_frontend.h" 10#include "core/frontend/applets/general_frontend.h"
10#include "core/frontend/applets/profile_select.h" 11#include "core/frontend/applets/profile_select.h"
@@ -15,6 +16,7 @@
15#include "core/hle/kernel/writable_event.h" 16#include "core/hle/kernel/writable_event.h"
16#include "core/hle/service/am/am.h" 17#include "core/hle/service/am/am.h"
17#include "core/hle/service/am/applets/applets.h" 18#include "core/hle/service/am/applets/applets.h"
19#include "core/hle/service/am/applets/controller.h"
18#include "core/hle/service/am/applets/error.h" 20#include "core/hle/service/am/applets/error.h"
19#include "core/hle/service/am/applets/general_backend.h" 21#include "core/hle/service/am/applets/general_backend.h"
20#include "core/hle/service/am/applets/profile_select.h" 22#include "core/hle/service/am/applets/profile_select.h"
@@ -140,14 +142,14 @@ void Applet::Initialize() {
140 142
141AppletFrontendSet::AppletFrontendSet() = default; 143AppletFrontendSet::AppletFrontendSet() = default;
142 144
143AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, 145AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce,
146 ErrorApplet error, ParentalControlsApplet parental_controls,
144 PhotoViewer photo_viewer, ProfileSelect profile_select, 147 PhotoViewer photo_viewer, ProfileSelect profile_select,
145 SoftwareKeyboard software_keyboard, WebBrowser web_browser, 148 SoftwareKeyboard software_keyboard, WebBrowser web_browser)
146 ECommerceApplet e_commerce) 149 : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)},
147 : parental_controls{std::move(parental_controls)}, error{std::move(error)}, 150 parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)},
148 photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)}, 151 profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)},
149 software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)}, 152 web_browser{std::move(web_browser)} {}
150 e_commerce{std::move(e_commerce)} {}
151 153
152AppletFrontendSet::~AppletFrontendSet() = default; 154AppletFrontendSet::~AppletFrontendSet() = default;
153 155
@@ -164,20 +166,37 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
164} 166}
165 167
166void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { 168void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
167 if (set.parental_controls != nullptr) 169 if (set.controller != nullptr) {
168 frontend.parental_controls = std::move(set.parental_controls); 170 frontend.controller = std::move(set.controller);
169 if (set.error != nullptr) 171 }
172
173 if (set.e_commerce != nullptr) {
174 frontend.e_commerce = std::move(set.e_commerce);
175 }
176
177 if (set.error != nullptr) {
170 frontend.error = std::move(set.error); 178 frontend.error = std::move(set.error);
171 if (set.photo_viewer != nullptr) 179 }
180
181 if (set.parental_controls != nullptr) {
182 frontend.parental_controls = std::move(set.parental_controls);
183 }
184
185 if (set.photo_viewer != nullptr) {
172 frontend.photo_viewer = std::move(set.photo_viewer); 186 frontend.photo_viewer = std::move(set.photo_viewer);
173 if (set.profile_select != nullptr) 187 }
188
189 if (set.profile_select != nullptr) {
174 frontend.profile_select = std::move(set.profile_select); 190 frontend.profile_select = std::move(set.profile_select);
175 if (set.software_keyboard != nullptr) 191 }
192
193 if (set.software_keyboard != nullptr) {
176 frontend.software_keyboard = std::move(set.software_keyboard); 194 frontend.software_keyboard = std::move(set.software_keyboard);
177 if (set.web_browser != nullptr) 195 }
196
197 if (set.web_browser != nullptr) {
178 frontend.web_browser = std::move(set.web_browser); 198 frontend.web_browser = std::move(set.web_browser);
179 if (set.e_commerce != nullptr) 199 }
180 frontend.e_commerce = std::move(set.e_commerce);
181} 200}
182 201
183void AppletManager::SetDefaultAppletFrontendSet() { 202void AppletManager::SetDefaultAppletFrontendSet() {
@@ -186,15 +205,23 @@ void AppletManager::SetDefaultAppletFrontendSet() {
186} 205}
187 206
188void AppletManager::SetDefaultAppletsIfMissing() { 207void AppletManager::SetDefaultAppletsIfMissing() {
189 if (frontend.parental_controls == nullptr) { 208 if (frontend.controller == nullptr) {
190 frontend.parental_controls = 209 frontend.controller = std::make_unique<Core::Frontend::DefaultControllerApplet>();
191 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); 210 }
211
212 if (frontend.e_commerce == nullptr) {
213 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
192 } 214 }
193 215
194 if (frontend.error == nullptr) { 216 if (frontend.error == nullptr) {
195 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 217 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
196 } 218 }
197 219
220 if (frontend.parental_controls == nullptr) {
221 frontend.parental_controls =
222 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
223 }
224
198 if (frontend.photo_viewer == nullptr) { 225 if (frontend.photo_viewer == nullptr) {
199 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); 226 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
200 } 227 }
@@ -211,10 +238,6 @@ void AppletManager::SetDefaultAppletsIfMissing() {
211 if (frontend.web_browser == nullptr) { 238 if (frontend.web_browser == nullptr) {
212 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); 239 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
213 } 240 }
214
215 if (frontend.e_commerce == nullptr) {
216 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
217 }
218} 241}
219 242
220void AppletManager::ClearAll() { 243void AppletManager::ClearAll() {
@@ -225,6 +248,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
225 switch (id) { 248 switch (id) {
226 case AppletId::Auth: 249 case AppletId::Auth:
227 return std::make_shared<Auth>(system, *frontend.parental_controls); 250 return std::make_shared<Auth>(system, *frontend.parental_controls);
251 case AppletId::Controller:
252 return std::make_shared<Controller>(system, *frontend.controller);
228 case AppletId::Error: 253 case AppletId::Error:
229 return std::make_shared<Error>(system, *frontend.error); 254 return std::make_shared<Error>(system, *frontend.error);
230 case AppletId::ProfileSelect: 255 case AppletId::ProfileSelect:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index e75be86a2..a1f4cf897 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -17,6 +17,7 @@ class System;
17} 17}
18 18
19namespace Core::Frontend { 19namespace Core::Frontend {
20class ControllerApplet;
20class ECommerceApplet; 21class ECommerceApplet;
21class ErrorApplet; 22class ErrorApplet;
22class ParentalControlsApplet; 23class ParentalControlsApplet;
@@ -155,19 +156,20 @@ protected:
155}; 156};
156 157
157struct AppletFrontendSet { 158struct AppletFrontendSet {
158 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; 159 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
160 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
159 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 161 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
162 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
160 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 163 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
161 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; 164 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
162 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; 165 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
163 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; 166 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
164 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
165 167
166 AppletFrontendSet(); 168 AppletFrontendSet();
167 AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, 169 AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error,
168 PhotoViewer photo_viewer, ProfileSelect profile_select, 170 ParentalControlsApplet parental_controls, PhotoViewer photo_viewer,
169 SoftwareKeyboard software_keyboard, WebBrowser web_browser, 171 ProfileSelect profile_select, SoftwareKeyboard software_keyboard,
170 ECommerceApplet e_commerce); 172 WebBrowser web_browser);
171 ~AppletFrontendSet(); 173 ~AppletFrontendSet();
172 174
173 AppletFrontendSet(const AppletFrontendSet&) = delete; 175 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -176,13 +178,14 @@ struct AppletFrontendSet {
176 AppletFrontendSet(AppletFrontendSet&&) noexcept; 178 AppletFrontendSet(AppletFrontendSet&&) noexcept;
177 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; 179 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
178 180
179 ParentalControlsApplet parental_controls; 181 ControllerApplet controller;
182 ECommerceApplet e_commerce;
180 ErrorApplet error; 183 ErrorApplet error;
184 ParentalControlsApplet parental_controls;
181 PhotoViewer photo_viewer; 185 PhotoViewer photo_viewer;
182 ProfileSelect profile_select; 186 ProfileSelect profile_select;
183 SoftwareKeyboard software_keyboard; 187 SoftwareKeyboard software_keyboard;
184 WebBrowser web_browser; 188 WebBrowser web_browser;
185 ECommerceApplet e_commerce;
186}; 189};
187 190
188class AppletManager { 191class AppletManager {
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp
new file mode 100644
index 000000000..2151da783
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.cpp
@@ -0,0 +1,210 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstring>
7
8#include "common/assert.h"
9#include "common/logging/log.h"
10#include "common/string_util.h"
11#include "core/core.h"
12#include "core/frontend/applets/controller.h"
13#include "core/hle/result.h"
14#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applets/controller.h"
16#include "core/hle/service/hid/controllers/npad.h"
17
18namespace Service::AM::Applets {
19
20// This error code (0x183ACA) is thrown when the applet fails to initialize.
21[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
22// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2.
23[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
24
25static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
26 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
27 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
28 HID::Controller_NPad::NPadType npad_style_set;
29 npad_style_set.raw = private_arg.style_set;
30
31 return {
32 .min_players = std::max(s8(1), header.player_count_min),
33 .max_players = header.player_count_max,
34 .keep_controllers_connected = header.enable_take_over_connection,
35 .enable_single_mode = header.enable_single_mode,
36 .enable_border_color = header.enable_identification_color,
37 .border_colors = identification_colors,
38 .enable_explain_text = enable_text,
39 .explain_text = text,
40 .allow_pro_controller = npad_style_set.pro_controller == 1,
41 .allow_handheld = npad_style_set.handheld == 1,
42 .allow_dual_joycons = npad_style_set.joycon_dual == 1,
43 .allow_left_joycon = npad_style_set.joycon_left == 1,
44 .allow_right_joycon = npad_style_set.joycon_right == 1,
45 };
46}
47
48Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_)
49 : Applet{system_.Kernel()}, frontend(frontend_) {}
50
51Controller::~Controller() = default;
52
53void Controller::Initialize() {
54 Applet::Initialize();
55
56 LOG_INFO(Service_HID, "Initializing Controller Applet.");
57
58 LOG_DEBUG(Service_HID,
59 "Initializing Applet with common_args: arg_version={}, lib_version={}, "
60 "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
61 common_args.arguments_version, common_args.library_version,
62 common_args.play_startup_sound, common_args.size, common_args.system_tick,
63 common_args.theme_color);
64
65 library_applet_version = LibraryAppletVersion{common_args.library_version};
66
67 const auto private_arg_storage = broker.PopNormalDataToApplet();
68 ASSERT(private_arg_storage != nullptr);
69
70 const auto& private_arg = private_arg_storage->GetData();
71 ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
72
73 std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate));
74 ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
75 "Unknown ControllerSupportArgPrivate revision={} with size={}",
76 library_applet_version, controller_private_arg.arg_private_size);
77
78 switch (controller_private_arg.mode) {
79 case ControllerSupportMode::ShowControllerSupport: {
80 const auto user_arg_storage = broker.PopNormalDataToApplet();
81 ASSERT(user_arg_storage != nullptr);
82
83 const auto& user_arg = user_arg_storage->GetData();
84 switch (library_applet_version) {
85 case LibraryAppletVersion::Version3:
86 case LibraryAppletVersion::Version4:
87 case LibraryAppletVersion::Version5:
88 ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
89 std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld));
90 break;
91 case LibraryAppletVersion::Version7:
92 ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
93 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
94 break;
95 default:
96 UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
97 library_applet_version, controller_private_arg.arg_size);
98 ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
99 std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
100 break;
101 }
102 break;
103 }
104 case ControllerSupportMode::ShowControllerStrapGuide:
105 case ControllerSupportMode::ShowControllerFirmwareUpdate:
106 default: {
107 UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
108 break;
109 }
110 }
111}
112
113bool Controller::TransactionComplete() const {
114 return complete;
115}
116
117ResultCode Controller::GetStatus() const {
118 return status;
119}
120
121void Controller::ExecuteInteractive() {
122 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
123}
124
125void Controller::Execute() {
126 switch (controller_private_arg.mode) {
127 case ControllerSupportMode::ShowControllerSupport: {
128 const auto parameters = [this] {
129 switch (library_applet_version) {
130 case LibraryAppletVersion::Version3:
131 case LibraryAppletVersion::Version4:
132 case LibraryAppletVersion::Version5:
133 return ConvertToFrontendParameters(
134 controller_private_arg, controller_user_arg_old.header,
135 controller_user_arg_old.enable_explain_text,
136 std::vector<IdentificationColor>(
137 controller_user_arg_old.identification_colors.begin(),
138 controller_user_arg_old.identification_colors.end()),
139 std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
140 controller_user_arg_old.explain_text.end()));
141 case LibraryAppletVersion::Version7:
142 default:
143 return ConvertToFrontendParameters(
144 controller_private_arg, controller_user_arg_new.header,
145 controller_user_arg_new.enable_explain_text,
146 std::vector<IdentificationColor>(
147 controller_user_arg_new.identification_colors.begin(),
148 controller_user_arg_new.identification_colors.end()),
149 std::vector<ExplainText>(controller_user_arg_new.explain_text.begin(),
150 controller_user_arg_new.explain_text.end()));
151 }
152 }();
153
154 is_single_mode = parameters.enable_single_mode;
155
156 LOG_DEBUG(Service_HID,
157 "Controller Parameters: min_players={}, max_players={}, "
158 "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, "
159 "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, "
160 "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}",
161 parameters.min_players, parameters.max_players,
162 parameters.keep_controllers_connected, parameters.enable_single_mode,
163 parameters.enable_border_color, parameters.enable_explain_text,
164 parameters.allow_pro_controller, parameters.allow_handheld,
165 parameters.allow_dual_joycons, parameters.allow_left_joycon,
166 parameters.allow_right_joycon);
167
168 frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters);
169 break;
170 }
171 case ControllerSupportMode::ShowControllerStrapGuide:
172 case ControllerSupportMode::ShowControllerFirmwareUpdate:
173 default: {
174 ConfigurationComplete();
175 break;
176 }
177 }
178}
179
180void Controller::ConfigurationComplete() {
181 ControllerSupportResultInfo result_info{};
182
183 const auto& players = Settings::values.players;
184
185 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
186 // Otherwise, only count connected players from P1-P8.
187 result_info.player_count =
188 is_single_mode ? 1
189 : static_cast<s8>(std::count_if(
190 players.begin(), players.end() - 2,
191 [](Settings::PlayerInput player) { return player.connected; }));
192
193 result_info.selected_id = HID::Controller_NPad::IndexToNPad(
194 std::distance(players.begin(),
195 std::find_if(players.begin(), players.end(),
196 [](Settings::PlayerInput player) { return player.connected; })));
197
198 result_info.result = 0;
199
200 LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}",
201 result_info.player_count, result_info.selected_id, result_info.result);
202
203 complete = true;
204 out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo));
205 std::memcpy(out_data.data(), &result_info, out_data.size());
206 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out_data)));
207 broker.SignalStateChanged();
208}
209
210} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h
new file mode 100644
index 000000000..f7bb3fba9
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.h
@@ -0,0 +1,123 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "core/hle/result.h"
13#include "core/hle/service/am/applets/applets.h"
14
15namespace Core {
16class System;
17}
18
19namespace Service::AM::Applets {
20
21using IdentificationColor = std::array<u8, 4>;
22using ExplainText = std::array<char, 0x81>;
23
24enum class LibraryAppletVersion : u32_le {
25 Version3 = 0x3, // 1.0.0 - 2.3.0
26 Version4 = 0x4, // 3.0.0 - 5.1.0
27 Version5 = 0x5, // 6.0.0 - 7.0.1
28 Version7 = 0x7, // 8.0.0+
29};
30
31enum class ControllerSupportMode : u8 {
32 ShowControllerSupport = 0,
33 ShowControllerStrapGuide = 1,
34 ShowControllerFirmwareUpdate = 2,
35};
36
37enum class ControllerSupportCaller : u8 {
38 Application = 0,
39 System = 1,
40};
41
42struct ControllerSupportArgPrivate {
43 u32 arg_private_size{};
44 u32 arg_size{};
45 bool flag_0{};
46 bool flag_1{};
47 ControllerSupportMode mode{};
48 ControllerSupportCaller caller{};
49 u32 style_set{};
50 u32 joy_hold_type{};
51};
52static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
53 "ControllerSupportArgPrivate has incorrect size.");
54
55struct ControllerSupportArgHeader {
56 s8 player_count_min{};
57 s8 player_count_max{};
58 bool enable_take_over_connection{};
59 bool enable_left_justify{};
60 bool enable_permit_joy_dual{};
61 bool enable_single_mode{};
62 bool enable_identification_color{};
63};
64static_assert(sizeof(ControllerSupportArgHeader) == 0x7,
65 "ControllerSupportArgHeader has incorrect size.");
66
67// LibraryAppletVersion 0x3, 0x4, 0x5
68struct ControllerSupportArgOld {
69 ControllerSupportArgHeader header{};
70 std::array<IdentificationColor, 4> identification_colors{};
71 bool enable_explain_text{};
72 std::array<ExplainText, 4> explain_text{};
73};
74static_assert(sizeof(ControllerSupportArgOld) == 0x21C,
75 "ControllerSupportArgOld has incorrect size.");
76
77// LibraryAppletVersion 0x7
78struct ControllerSupportArgNew {
79 ControllerSupportArgHeader header{};
80 std::array<IdentificationColor, 8> identification_colors{};
81 bool enable_explain_text{};
82 std::array<ExplainText, 8> explain_text{};
83};
84static_assert(sizeof(ControllerSupportArgNew) == 0x430,
85 "ControllerSupportArgNew has incorrect size.");
86
87struct ControllerSupportResultInfo {
88 s8 player_count{};
89 INSERT_PADDING_BYTES(3);
90 u32 selected_id{};
91 u32 result{};
92};
93static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
94 "ControllerSupportResultInfo has incorrect size.");
95
96class Controller final : public Applet {
97public:
98 explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_);
99 ~Controller() override;
100
101 void Initialize() override;
102
103 bool TransactionComplete() const override;
104 ResultCode GetStatus() const override;
105 void ExecuteInteractive() override;
106 void Execute() override;
107
108 void ConfigurationComplete();
109
110private:
111 const Core::Frontend::ControllerApplet& frontend;
112
113 LibraryAppletVersion library_applet_version;
114 ControllerSupportArgPrivate controller_private_arg;
115 ControllerSupportArgOld controller_user_arg_old;
116 ControllerSupportArgNew controller_user_arg_new;
117 bool complete{false};
118 ResultCode status{RESULT_SUCCESS};
119 bool is_single_mode{false};
120 std::vector<u8> out_data;
121};
122
123} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index d8359abaa..a2d3ded7b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -26,7 +26,7 @@ namespace Service::Audio {
26 26
27class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 27class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
28public: 28public:
29 explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params, 29 explicit IAudioRenderer(Core::System& system, AudioCommon::AudioRendererParameter audren_params,
30 const std::size_t instance_number) 30 const std::size_t instance_number)
31 : ServiceFramework("IAudioRenderer") { 31 : ServiceFramework("IAudioRenderer") {
32 // clang-format off 32 // clang-format off
@@ -94,14 +94,15 @@ private:
94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { 94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
95 LOG_DEBUG(Service_Audio, "(STUBBED) called"); 95 LOG_DEBUG(Service_Audio, "(STUBBED) called");
96 96
97 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer()); 97 std::vector<u8> output_params(ctx.GetWriteBufferSize());
98 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
98 99
99 if (result.Succeeded()) { 100 if (result.IsSuccess()) {
100 ctx.WriteBuffer(result.Unwrap()); 101 ctx.WriteBuffer(output_params);
101 } 102 }
102 103
103 IPC::ResponseBuilder rb{ctx, 2}; 104 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(result.Code()); 105 rb.Push(result);
105 } 106 }
106 107
107 void Start(Kernel::HLERequestContext& ctx) { 108 void Start(Kernel::HLERequestContext& ctx) {
@@ -346,7 +347,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
346 OpenAudioRendererImpl(ctx); 347 OpenAudioRendererImpl(ctx);
347} 348}
348 349
349static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) { 350static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
350 // +1 represents the final mix. 351 // +1 represents the final mix.
351 return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + 352 return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
352 1; 353 1;
@@ -375,7 +376,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
375 constexpr u64 upsampler_manager_size = 0x48; 376 constexpr u64 upsampler_manager_size = 0x48;
376 377
377 // Calculates the part of the size that relates to mix buffers. 378 // Calculates the part of the size that relates to mix buffers.
378 const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) { 379 const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
379 // As of 8.0.0 this is the maximum on voice channels. 380 // As of 8.0.0 this is the maximum on voice channels.
380 constexpr u64 max_voice_channels = 6; 381 constexpr u64 max_voice_channels = 6;
381 382
@@ -397,7 +398,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
397 }; 398 };
398 399
399 // Calculates the portion of the size related to the mix data (and the sorting thereof). 400 // Calculates the portion of the size related to the mix data (and the sorting thereof).
400 const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) { 401 const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
401 // The size of the mixing info data structure. 402 // The size of the mixing info data structure.
402 constexpr u64 mix_info_size = 0x940; 403 constexpr u64 mix_info_size = 0x940;
403 404
@@ -447,7 +448,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
447 }; 448 };
448 449
449 // Calculates the part of the size related to voice channel info. 450 // Calculates the part of the size related to voice channel info.
450 const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) { 451 const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
451 constexpr u64 voice_info_size = 0x220; 452 constexpr u64 voice_info_size = 0x220;
452 constexpr u64 voice_resource_size = 0xD0; 453 constexpr u64 voice_resource_size = 0xD0;
453 454
@@ -461,7 +462,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
461 }; 462 };
462 463
463 // Calculates the part of the size related to memory pools. 464 // Calculates the part of the size related to memory pools.
464 const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) { 465 const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
465 const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); 466 const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
466 const u64 memory_pool_info_size = 0x20; 467 const u64 memory_pool_info_size = 0x20;
467 return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); 468 return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
@@ -469,7 +470,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
469 470
470 // Calculates the part of the size related to the splitter context. 471 // Calculates the part of the size related to the splitter context.
471 const auto calculate_splitter_context_size = 472 const auto calculate_splitter_context_size =
472 [](const AudioCore::AudioRendererParameter& params) -> u64 { 473 [](const AudioCommon::AudioRendererParameter& params) -> u64 {
473 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { 474 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
474 return 0; 475 return 0;
475 } 476 }
@@ -488,27 +489,29 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
488 }; 489 };
489 490
490 // Calculates the part of the size related to the upsampler info. 491 // Calculates the part of the size related to the upsampler info.
491 const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) { 492 const auto calculate_upsampler_info_size =
492 constexpr u64 upsampler_info_size = 0x280; 493 [](const AudioCommon::AudioRendererParameter& params) {
493 // Yes, using the buffer size over info alignment size is intentional here. 494 constexpr u64 upsampler_info_size = 0x280;
494 return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count), 495 // Yes, using the buffer size over info alignment size is intentional here.
495 buffer_alignment_size); 496 return Common::AlignUp(upsampler_info_size *
496 }; 497 (u64{params.submix_count} + params.sink_count),
498 buffer_alignment_size);
499 };
497 500
498 // Calculates the part of the size related to effect info. 501 // Calculates the part of the size related to effect info.
499 const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) { 502 const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
500 constexpr u64 effect_info_size = 0x2B0; 503 constexpr u64 effect_info_size = 0x2B0;
501 return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); 504 return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
502 }; 505 };
503 506
504 // Calculates the part of the size related to audio sink info. 507 // Calculates the part of the size related to audio sink info.
505 const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) { 508 const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
506 const u64 sink_info_size = 0x170; 509 const u64 sink_info_size = 0x170;
507 return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); 510 return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
508 }; 511 };
509 512
510 // Calculates the part of the size related to voice state info. 513 // Calculates the part of the size related to voice state info.
511 const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) { 514 const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
512 const u64 voice_state_size = 0x100; 515 const u64 voice_state_size = 0x100;
513 const u64 additional_size = buffer_alignment_size - 1; 516 const u64 additional_size = buffer_alignment_size - 1;
514 return Common::AlignUp(voice_state_size * params.voice_count + additional_size, 517 return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
@@ -516,7 +519,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
516 }; 519 };
517 520
518 // Calculates the part of the size related to performance statistics. 521 // Calculates the part of the size related to performance statistics.
519 const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) { 522 const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
520 // Extra size value appended to the end of the calculation. 523 // Extra size value appended to the end of the calculation.
521 constexpr u64 appended = 128; 524 constexpr u64 appended = 128;
522 525
@@ -543,79 +546,81 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
543 }; 546 };
544 547
545 // Calculates the part of the size that relates to the audio command buffer. 548 // Calculates the part of the size that relates to the audio command buffer.
546 const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) { 549 const auto calculate_command_buffer_size =
547 constexpr u64 alignment = (buffer_alignment_size - 1) * 2; 550 [](const AudioCommon::AudioRendererParameter& params) {
551 constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
548 552
549 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { 553 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
550 constexpr u64 command_buffer_size = 0x18000; 554 constexpr u64 command_buffer_size = 0x18000;
551 555
552 return command_buffer_size + alignment; 556 return command_buffer_size + alignment;
553 } 557 }
554 558
555 // When the variadic command buffer is supported, this means 559 // When the variadic command buffer is supported, this means
556 // the command generator for the audio renderer can issue commands 560 // the command generator for the audio renderer can issue commands
557 // that are (as one would expect), variable in size. So what we need to do 561 // that are (as one would expect), variable in size. So what we need to do
558 // is determine the maximum possible size for a few command data structures 562 // is determine the maximum possible size for a few command data structures
559 // then multiply them by the amount of present commands indicated by the given 563 // then multiply them by the amount of present commands indicated by the given
560 // respective audio parameters. 564 // respective audio parameters.
561 565
562 constexpr u64 max_biquad_filters = 2; 566 constexpr u64 max_biquad_filters = 2;
563 constexpr u64 max_mix_buffers = 24; 567 constexpr u64 max_mix_buffers = 24;
564 568
565 constexpr u64 biquad_filter_command_size = 0x2C; 569 constexpr u64 biquad_filter_command_size = 0x2C;
566 570
567 constexpr u64 depop_mix_command_size = 0x24; 571 constexpr u64 depop_mix_command_size = 0x24;
568 constexpr u64 depop_setup_command_size = 0x50; 572 constexpr u64 depop_setup_command_size = 0x50;
569 573
570 constexpr u64 effect_command_max_size = 0x540; 574 constexpr u64 effect_command_max_size = 0x540;
571 575
572 constexpr u64 mix_command_size = 0x1C; 576 constexpr u64 mix_command_size = 0x1C;
573 constexpr u64 mix_ramp_command_size = 0x24; 577 constexpr u64 mix_ramp_command_size = 0x24;
574 constexpr u64 mix_ramp_grouped_command_size = 0x13C; 578 constexpr u64 mix_ramp_grouped_command_size = 0x13C;
575 579
576 constexpr u64 perf_command_size = 0x28; 580 constexpr u64 perf_command_size = 0x28;
577 581
578 constexpr u64 sink_command_size = 0x130; 582 constexpr u64 sink_command_size = 0x130;
579 583
580 constexpr u64 submix_command_max_size = 584 constexpr u64 submix_command_max_size =
581 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; 585 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
582 586
583 constexpr u64 volume_command_size = 0x1C; 587 constexpr u64 volume_command_size = 0x1C;
584 constexpr u64 volume_ramp_command_size = 0x20; 588 constexpr u64 volume_ramp_command_size = 0x20;
585 589
586 constexpr u64 voice_biquad_filter_command_size = 590 constexpr u64 voice_biquad_filter_command_size =
587 biquad_filter_command_size * max_biquad_filters; 591 biquad_filter_command_size * max_biquad_filters;
588 constexpr u64 voice_data_command_size = 0x9C; 592 constexpr u64 voice_data_command_size = 0x9C;
589 const u64 voice_command_max_size = 593 const u64 voice_command_max_size =
590 (params.splitter_count * depop_setup_command_size) + 594 (params.splitter_count * depop_setup_command_size) +
591 (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size + 595 (voice_data_command_size + voice_biquad_filter_command_size +
592 mix_ramp_grouped_command_size); 596 volume_ramp_command_size + mix_ramp_grouped_command_size);
593 597
594 // Now calculate the individual elements that comprise the size and add them together. 598 // Now calculate the individual elements that comprise the size and add them together.
595 const u64 effect_commands_size = params.effect_count * effect_command_max_size; 599 const u64 effect_commands_size = params.effect_count * effect_command_max_size;
596 600
597 const u64 final_mix_commands_size = 601 const u64 final_mix_commands_size =
598 depop_mix_command_size + volume_command_size * max_mix_buffers; 602 depop_mix_command_size + volume_command_size * max_mix_buffers;
599 603
600 const u64 perf_commands_size = 604 const u64 perf_commands_size =
601 perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); 605 perf_command_size *
606 (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
602 607
603 const u64 sink_commands_size = params.sink_count * sink_command_size; 608 const u64 sink_commands_size = params.sink_count * sink_command_size;
604 609
605 const u64 splitter_commands_size = 610 const u64 splitter_commands_size =
606 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; 611 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
607 612
608 const u64 submix_commands_size = params.submix_count * submix_command_max_size; 613 const u64 submix_commands_size = params.submix_count * submix_command_max_size;
609 614
610 const u64 voice_commands_size = params.voice_count * voice_command_max_size; 615 const u64 voice_commands_size = params.voice_count * voice_command_max_size;
611 616
612 return effect_commands_size + final_mix_commands_size + perf_commands_size + 617 return effect_commands_size + final_mix_commands_size + perf_commands_size +
613 sink_commands_size + splitter_commands_size + submix_commands_size + 618 sink_commands_size + splitter_commands_size + submix_commands_size +
614 voice_commands_size + alignment; 619 voice_commands_size + alignment;
615 }; 620 };
616 621
617 IPC::RequestParser rp{ctx}; 622 IPC::RequestParser rp{ctx};
618 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); 623 const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
619 624
620 u64 size = 0; 625 u64 size = 0;
621 size += calculate_mix_buffer_sizes(params); 626 size += calculate_mix_buffer_sizes(params);
@@ -681,7 +686,7 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c
681 686
682void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { 687void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
683 IPC::RequestParser rp{ctx}; 688 IPC::RequestParser rp{ctx};
684 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); 689 const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
685 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 690 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
686 691
687 rb.Push(RESULT_SUCCESS); 692 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e742497e1..7818c098f 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -193,7 +193,8 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
193 controller.battery_level[0] = BATTERY_FULL; 193 controller.battery_level[0] = BATTERY_FULL;
194 controller.battery_level[1] = BATTERY_FULL; 194 controller.battery_level[1] = BATTERY_FULL;
195 controller.battery_level[2] = BATTERY_FULL; 195 controller.battery_level[2] = BATTERY_FULL;
196 styleset_changed_events[controller_idx].writable->Signal(); 196
197 SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
197} 198}
198 199
199void Controller_NPad::OnInit() { 200void Controller_NPad::OnInit() {
@@ -518,13 +519,17 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
518 last_processed_vibration = vibrations.back(); 519 last_processed_vibration = vibrations.back();
519} 520}
520 521
522Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
523 return last_processed_vibration;
524}
525
521std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { 526std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; 527 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
523 return styleset_event.readable; 528 return styleset_event.readable;
524} 529}
525 530
526Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 531void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
527 return last_processed_vibration; 532 styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal();
528} 533}
529 534
530void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { 535void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
@@ -534,7 +539,7 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz
534void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, 539void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
535 bool connected) { 540 bool connected) {
536 if (!connected) { 541 if (!connected) {
537 DisconnectNPad(IndexToNPad(npad_index)); 542 DisconnectNPadAtIndex(npad_index);
538 return; 543 return;
539 } 544 }
540 545
@@ -554,16 +559,19 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz
554} 559}
555 560
556void Controller_NPad::DisconnectNPad(u32 npad_id) { 561void Controller_NPad::DisconnectNPad(u32 npad_id) {
557 const auto npad_index = NPadIdToIndex(npad_id); 562 DisconnectNPadAtIndex(NPadIdToIndex(npad_id));
558 connected_controllers[npad_index].is_connected = false; 563}
564
565void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) {
559 Settings::values.players[npad_index].connected = false; 566 Settings::values.players[npad_index].connected = false;
567 connected_controllers[npad_index].is_connected = false;
560 568
561 auto& controller = shared_memory_entries[npad_index]; 569 auto& controller = shared_memory_entries[npad_index];
562 controller.joy_styles.raw = 0; // Zero out 570 controller.joy_styles.raw = 0; // Zero out
563 controller.device_type.raw = 0; 571 controller.device_type.raw = 0;
564 controller.properties.raw = 0; 572 controller.properties.raw = 0;
565 573
566 styleset_changed_events[npad_index].writable->Signal(); 574 SignalStyleSetChangedEvent(IndexToNPad(npad_index));
567} 575}
568 576
569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 577void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -666,13 +674,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
666} 674}
667 675
668void Controller_NPad::DisconnectAllConnectedControllers() { 676void Controller_NPad::DisconnectAllConnectedControllers() {
669 for (ControllerHolder& controller : connected_controllers) { 677 for (auto& controller : connected_controllers) {
670 controller.is_connected = false; 678 controller.is_connected = false;
671 } 679 }
672} 680}
673 681
674void Controller_NPad::ConnectAllDisconnectedControllers() { 682void Controller_NPad::ConnectAllDisconnectedControllers() {
675 for (ControllerHolder& controller : connected_controllers) { 683 for (auto& controller : connected_controllers) {
676 if (controller.type != NPadControllerType::None && !controller.is_connected) { 684 if (controller.type != NPadControllerType::None && !controller.is_connected) {
677 controller.is_connected = true; 685 controller.is_connected = true;
678 } 686 }
@@ -680,7 +688,7 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
680} 688}
681 689
682void Controller_NPad::ClearAllControllers() { 690void Controller_NPad::ClearAllControllers() {
683 for (ControllerHolder& controller : connected_controllers) { 691 for (auto& controller : connected_controllers) {
684 controller.type = NPadControllerType::None; 692 controller.type = NPadControllerType::None;
685 controller.is_connected = false; 693 controller.is_connected = false;
686 } 694 }
@@ -728,92 +736,4 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
728 return false; 736 return false;
729} 737}
730 738
731Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
732 NPadControllerType priority) const {
733 if (IsControllerSupported(priority)) {
734 return priority;
735 }
736 const auto is_docked = Settings::values.use_docked_mode;
737 if (is_docked && priority == NPadControllerType::Handheld) {
738 priority = NPadControllerType::JoyDual;
739 if (IsControllerSupported(priority)) {
740 return priority;
741 }
742 }
743 std::vector<NPadControllerType> priority_list;
744 switch (priority) {
745 case NPadControllerType::ProController:
746 priority_list.push_back(NPadControllerType::JoyDual);
747 if (!is_docked) {
748 priority_list.push_back(NPadControllerType::Handheld);
749 }
750 priority_list.push_back(NPadControllerType::JoyLeft);
751 priority_list.push_back(NPadControllerType::JoyRight);
752 priority_list.push_back(NPadControllerType::Pokeball);
753 break;
754 case NPadControllerType::Handheld:
755 priority_list.push_back(NPadControllerType::JoyDual);
756 priority_list.push_back(NPadControllerType::ProController);
757 priority_list.push_back(NPadControllerType::JoyLeft);
758 priority_list.push_back(NPadControllerType::JoyRight);
759 priority_list.push_back(NPadControllerType::Pokeball);
760 break;
761 case NPadControllerType::JoyDual:
762 if (!is_docked) {
763 priority_list.push_back(NPadControllerType::Handheld);
764 }
765 priority_list.push_back(NPadControllerType::ProController);
766 priority_list.push_back(NPadControllerType::JoyLeft);
767 priority_list.push_back(NPadControllerType::JoyRight);
768 priority_list.push_back(NPadControllerType::Pokeball);
769 break;
770 case NPadControllerType::JoyLeft:
771 priority_list.push_back(NPadControllerType::JoyRight);
772 priority_list.push_back(NPadControllerType::JoyDual);
773 if (!is_docked) {
774 priority_list.push_back(NPadControllerType::Handheld);
775 }
776 priority_list.push_back(NPadControllerType::ProController);
777 priority_list.push_back(NPadControllerType::Pokeball);
778 break;
779 case NPadControllerType::JoyRight:
780 priority_list.push_back(NPadControllerType::JoyLeft);
781 priority_list.push_back(NPadControllerType::JoyDual);
782 if (!is_docked) {
783 priority_list.push_back(NPadControllerType::Handheld);
784 }
785 priority_list.push_back(NPadControllerType::ProController);
786 priority_list.push_back(NPadControllerType::Pokeball);
787 break;
788 case NPadControllerType::Pokeball:
789 priority_list.push_back(NPadControllerType::JoyLeft);
790 priority_list.push_back(NPadControllerType::JoyRight);
791 priority_list.push_back(NPadControllerType::JoyDual);
792 if (!is_docked) {
793 priority_list.push_back(NPadControllerType::Handheld);
794 }
795 priority_list.push_back(NPadControllerType::ProController);
796 break;
797 default:
798 priority_list.push_back(NPadControllerType::JoyDual);
799 if (!is_docked) {
800 priority_list.push_back(NPadControllerType::Handheld);
801 }
802 priority_list.push_back(NPadControllerType::ProController);
803 priority_list.push_back(NPadControllerType::JoyLeft);
804 priority_list.push_back(NPadControllerType::JoyRight);
805 priority_list.push_back(NPadControllerType::JoyDual);
806 break;
807 }
808
809 const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
810 [this](auto type) { return IsControllerSupported(type); });
811 if (iter == priority_list.end()) {
812 UNIMPLEMENTED_MSG("Could not find supported controller!");
813 return priority;
814 }
815
816 return *iter;
817}
818
819} // namespace Service::HID 739} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ad25c6fbf..e9788da8d 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -115,15 +115,19 @@ public:
115 void VibrateController(const std::vector<u32>& controller_ids, 115 void VibrateController(const std::vector<u32>& controller_ids,
116 const std::vector<Vibration>& vibrations); 116 const std::vector<Vibration>& vibrations);
117 117
118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
119 Vibration GetLastVibration() const; 118 Vibration GetLastVibration() const;
120 119
120 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
121 void SignalStyleSetChangedEvent(u32 npad_id) const;
122
121 // Adds a new controller at an index. 123 // Adds a new controller at an index.
122 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); 124 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
123 // Adds a new controller at an index with connection status. 125 // Adds a new controller at an index with connection status.
124 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); 126 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
125 127
126 void DisconnectNPad(u32 npad_id); 128 void DisconnectNPad(u32 npad_id);
129 void DisconnectNPadAtIndex(std::size_t index);
130
127 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 131 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
128 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 132 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
129 LedPattern GetLedPattern(u32 npad_id); 133 LedPattern GetLedPattern(u32 npad_id);
@@ -315,7 +319,6 @@ private:
315 319
316 void InitNewlyAddedController(std::size_t controller_idx); 320 void InitNewlyAddedController(std::size_t controller_idx);
317 bool IsControllerSupported(NPadControllerType controller) const; 321 bool IsControllerSupported(NPadControllerType controller) const;
318 NPadControllerType DecideBestController(NPadControllerType priority) const;
319 void RequestPadStateUpdate(u32 npad_id); 322 void RequestPadStateUpdate(u32 npad_id);
320 323
321 u32 press_state{}; 324 u32 press_state{};
diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h
index 31ef6b821..2d53e52b6 100644
--- a/src/core/hle/service/sockets/blocking_worker.h
+++ b/src/core/hle/service/sockets/blocking_worker.h
@@ -29,7 +29,7 @@ namespace Service::Sockets {
29 * Worker abstraction to execute blocking calls on host without blocking the guest thread 29 * Worker abstraction to execute blocking calls on host without blocking the guest thread
30 * 30 *
31 * @tparam Service Service where the work is executed 31 * @tparam Service Service where the work is executed
32 * @tparam ...Types Types of work to execute 32 * @tparam Types Types of work to execute
33 */ 33 */
34template <class Service, class... Types> 34template <class Service, class... Types>
35class BlockingWorker { 35class BlockingWorker {
@@ -109,9 +109,8 @@ private:
109 while (keep_running) { 109 while (keep_running) {
110 work_event.Wait(); 110 work_event.Wait();
111 111
112 const auto visit_fn = [service, &keep_running](auto&& w) { 112 const auto visit_fn = [service, &keep_running]<typename T>(T&& w) {
113 using T = std::decay_t<decltype(w)>; 113 if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) {
114 if constexpr (std::is_same_v<T, std::monostate>) {
115 keep_running = false; 114 keep_running = false;
116 } else { 115 } else {
117 w.Execute(service); 116 w.Execute(service);
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 803505452..7b9dd42d8 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -491,7 +491,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u
491 for (PollFD& pollfd : fds) { 491 for (PollFD& pollfd : fds) {
492 ASSERT(pollfd.revents == 0); 492 ASSERT(pollfd.revents == 0);
493 493
494 if (pollfd.fd > MAX_FD || pollfd.fd < 0) { 494 if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
495 LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd); 495 LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
496 pollfd.revents = 0; 496 pollfd.revents = 0;
497 return {0, Errno::SUCCESS}; 497 return {0, Errno::SUCCESS};
@@ -764,6 +764,7 @@ std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>&
764 SockAddrIn guest_addr_in; 764 SockAddrIn guest_addr_in;
765 std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in)); 765 std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in));
766 addr_in = Translate(guest_addr_in); 766 addr_in = Translate(guest_addr_in);
767 p_addr_in = &addr_in;
767 } 768 }
768 769
769 return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in)); 770 return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
@@ -795,7 +796,7 @@ s32 BSD::FindFreeFileDescriptorHandle() noexcept {
795} 796}
796 797
797bool BSD::IsFileDescriptorValid(s32 fd) const noexcept { 798bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
798 if (fd > MAX_FD || fd < 0) { 799 if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
799 LOG_ERROR(Service, "Invalid file descriptor handle={}", fd); 800 LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
800 return false; 801 return false;
801 } 802 }
@@ -809,7 +810,7 @@ bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
809bool BSD::IsBlockingSocket(s32 fd) const noexcept { 810bool BSD::IsBlockingSocket(s32 fd) const noexcept {
810 // Inform invalid sockets as non-blocking 811 // Inform invalid sockets as non-blocking
811 // This way we avoid using a worker thread as it will fail without blocking host 812 // This way we avoid using a worker thread as it will fail without blocking host
812 if (fd > MAX_FD || fd < 0) { 813 if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
813 return false; 814 return false;
814 } 815 }
815 if (!file_descriptors[fd]) { 816 if (!file_descriptors[fd]) {
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index 2be8f642d..139743e1d 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -131,21 +131,21 @@ u16 TranslatePollEventsToGuest(u16 flags) {
131Network::SockAddrIn Translate(SockAddrIn value) { 131Network::SockAddrIn Translate(SockAddrIn value) {
132 ASSERT(value.len == 0 || value.len == sizeof(value)); 132 ASSERT(value.len == 0 || value.len == sizeof(value));
133 133
134 Network::SockAddrIn result; 134 return {
135 result.family = Translate(static_cast<Domain>(value.family)); 135 .family = Translate(static_cast<Domain>(value.family)),
136 result.ip = value.ip; 136 .ip = value.ip,
137 result.portno = value.portno >> 8 | value.portno << 8; 137 .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
138 return result; 138 };
139} 139}
140 140
141SockAddrIn Translate(Network::SockAddrIn value) { 141SockAddrIn Translate(Network::SockAddrIn value) {
142 SockAddrIn result; 142 return {
143 result.len = sizeof(result); 143 .len = sizeof(SockAddrIn),
144 result.family = static_cast<u8>(Translate(value.family)); 144 .family = static_cast<u8>(Translate(value.family)),
145 result.portno = value.portno >> 8 | value.portno << 8; 145 .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
146 result.ip = value.ip; 146 .ip = value.ip,
147 result.zeroes = {}; 147 .zeroes = {},
148 return result; 148 };
149} 149}
150 150
151Network::ShutdownHow Translate(ShutdownHow how) { 151Network::ShutdownHow Translate(ShutdownHow how) {
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index 74759ea7d..c6c423c4b 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -283,7 +283,7 @@ void Adapter::Reset() {
283 } 283 }
284} 284}
285 285
286bool Adapter::DeviceConnected(std::size_t port) { 286bool Adapter::DeviceConnected(std::size_t port) const {
287 return adapter_controllers_status[port] != ControllerTypes::None; 287 return adapter_controllers_status[port] != ControllerTypes::None;
288} 288}
289 289
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index bed81915c..20e97d283 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -76,7 +76,7 @@ public:
76 void EndConfiguration(); 76 void EndConfiguration();
77 77
78 /// Returns true if there is a device connected to port 78 /// Returns true if there is a device connected to port
79 bool DeviceConnected(std::size_t port); 79 bool DeviceConnected(std::size_t port) const;
80 80
81 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); 81 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
82 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; 82 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 1c8d8523a..92e9e8e89 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,7 +15,7 @@ namespace InputCommon {
15 15
16class GCButton final : public Input::ButtonDevice { 16class GCButton final : public Input::ButtonDevice {
17public: 17public:
18 explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter) 18 explicit GCButton(int port_, int button_, const GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {} 19 : port(port_), button(button_), gcadapter(adapter) {}
20 20
21 ~GCButton() override; 21 ~GCButton() override;
@@ -30,13 +30,13 @@ public:
30private: 30private:
31 const int port; 31 const int port;
32 const int button; 32 const int button;
33 GCAdapter::Adapter* gcadapter; 33 const GCAdapter::Adapter* gcadapter;
34}; 34};
35 35
36class GCAxisButton final : public Input::ButtonDevice { 36class GCAxisButton final : public Input::ButtonDevice {
37public: 37public:
38 explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_, 38 explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
39 GCAdapter::Adapter* adapter) 39 const GCAdapter::Adapter* adapter)
40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), 40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
41 gcadapter(adapter), 41 gcadapter(adapter),
42 origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {} 42 origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {}
@@ -60,7 +60,7 @@ private:
60 const int axis; 60 const int axis;
61 float threshold; 61 float threshold;
62 bool trigger_if_greater; 62 bool trigger_if_greater;
63 GCAdapter::Adapter* gcadapter; 63 const GCAdapter::Adapter* gcadapter;
64 const float origin_value; 64 const float origin_value;
65}; 65};
66 66
@@ -149,8 +149,8 @@ void GCButtonFactory::EndConfiguration() {
149 149
150class GCAnalog final : public Input::AnalogDevice { 150class GCAnalog final : public Input::AnalogDevice {
151public: 151public:
152 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter, 152 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_,
153 float range_) 153 const GCAdapter::Adapter* adapter, float range_)
154 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 154 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
155 origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))), 155 origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))),
156 origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))), 156 origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))),
@@ -212,7 +212,7 @@ private:
212 const int axis_x; 212 const int axis_x;
213 const int axis_y; 213 const int axis_y;
214 const float deadzone; 214 const float deadzone;
215 GCAdapter::Adapter* gcadapter; 215 const GCAdapter::Adapter* gcadapter;
216 const float origin_value_x; 216 const float origin_value_x;
217 const float origin_value_y; 217 const float origin_value_y;
218 const float range; 218 const float range;
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index b5dc68902..e7edd733f 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -51,46 +51,43 @@ public:
51 bool is_written = false, bool use_fast_cbuf = false) { 51 bool is_written = false, bool use_fast_cbuf = false) {
52 std::lock_guard lock{mutex}; 52 std::lock_guard lock{mutex};
53 53
54 auto& memory_manager = system.GPU().MemoryManager(); 54 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
55 const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); 55 if (!cpu_addr) {
56 if (!cpu_addr_opt) {
57 return GetEmptyBuffer(size); 56 return GetEmptyBuffer(size);
58 } 57 }
59 const VAddr cpu_addr = *cpu_addr_opt;
60 58
61 // Cache management is a big overhead, so only cache entries with a given size. 59 // Cache management is a big overhead, so only cache entries with a given size.
62 // TODO: Figure out which size is the best for given games. 60 // TODO: Figure out which size is the best for given games.
63 constexpr std::size_t max_stream_size = 0x800; 61 constexpr std::size_t max_stream_size = 0x800;
64 if (use_fast_cbuf || size < max_stream_size) { 62 if (use_fast_cbuf || size < max_stream_size) {
65 if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) { 63 if (!is_written && !IsRegionWritten(*cpu_addr, *cpu_addr + size - 1)) {
66 const bool is_granular = memory_manager.IsGranularRange(gpu_addr, size); 64 const bool is_granular = gpu_memory.IsGranularRange(gpu_addr, size);
67 if (use_fast_cbuf) { 65 if (use_fast_cbuf) {
68 u8* dest; 66 u8* dest;
69 if (is_granular) { 67 if (is_granular) {
70 dest = memory_manager.GetPointer(gpu_addr); 68 dest = gpu_memory.GetPointer(gpu_addr);
71 } else { 69 } else {
72 staging_buffer.resize(size); 70 staging_buffer.resize(size);
73 dest = staging_buffer.data(); 71 dest = staging_buffer.data();
74 memory_manager.ReadBlockUnsafe(gpu_addr, dest, size); 72 gpu_memory.ReadBlockUnsafe(gpu_addr, dest, size);
75 } 73 }
76 return ConstBufferUpload(dest, size); 74 return ConstBufferUpload(dest, size);
77 } 75 }
78 if (is_granular) { 76 if (is_granular) {
79 u8* const host_ptr = memory_manager.GetPointer(gpu_addr); 77 u8* const host_ptr = gpu_memory.GetPointer(gpu_addr);
80 return StreamBufferUpload(size, alignment, [host_ptr, size](u8* dest) { 78 return StreamBufferUpload(size, alignment, [host_ptr, size](u8* dest) {
81 std::memcpy(dest, host_ptr, size); 79 std::memcpy(dest, host_ptr, size);
82 }); 80 });
83 } else { 81 } else {
84 return StreamBufferUpload( 82 return StreamBufferUpload(size, alignment, [this, gpu_addr, size](u8* dest) {
85 size, alignment, [&memory_manager, gpu_addr, size](u8* dest) { 83 gpu_memory.ReadBlockUnsafe(gpu_addr, dest, size);
86 memory_manager.ReadBlockUnsafe(gpu_addr, dest, size); 84 });
87 });
88 } 85 }
89 } 86 }
90 } 87 }
91 88
92 Buffer* const block = GetBlock(cpu_addr, size); 89 Buffer* const block = GetBlock(*cpu_addr, size);
93 MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size); 90 MapInterval* const map = MapAddress(block, gpu_addr, *cpu_addr, size);
94 if (!map) { 91 if (!map) {
95 return GetEmptyBuffer(size); 92 return GetEmptyBuffer(size);
96 } 93 }
@@ -106,7 +103,7 @@ public:
106 } 103 }
107 } 104 }
108 105
109 return BufferInfo{block->Handle(), block->Offset(cpu_addr), block->Address()}; 106 return BufferInfo{block->Handle(), block->Offset(*cpu_addr), block->Address()};
110 } 107 }
111 108
112 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. 109 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset.
@@ -262,9 +259,11 @@ public:
262 virtual BufferInfo GetEmptyBuffer(std::size_t size) = 0; 259 virtual BufferInfo GetEmptyBuffer(std::size_t size) = 0;
263 260
264protected: 261protected:
265 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, 262 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
266 std::unique_ptr<StreamBuffer> stream_buffer) 263 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
267 : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)} {} 264 std::unique_ptr<StreamBuffer> stream_buffer_)
265 : rasterizer{rasterizer_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_},
266 stream_buffer{std::move(stream_buffer_)}, stream_buffer_handle{stream_buffer->Handle()} {}
268 267
269 ~BufferCache() = default; 268 ~BufferCache() = default;
270 269
@@ -326,14 +325,13 @@ private:
326 MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) { 325 MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) {
327 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); 326 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size);
328 if (overlaps.empty()) { 327 if (overlaps.empty()) {
329 auto& memory_manager = system.GPU().MemoryManager();
330 const VAddr cpu_addr_end = cpu_addr + size; 328 const VAddr cpu_addr_end = cpu_addr + size;
331 if (memory_manager.IsGranularRange(gpu_addr, size)) { 329 if (gpu_memory.IsGranularRange(gpu_addr, size)) {
332 u8* host_ptr = memory_manager.GetPointer(gpu_addr); 330 u8* const host_ptr = gpu_memory.GetPointer(gpu_addr);
333 block->Upload(block->Offset(cpu_addr), size, host_ptr); 331 block->Upload(block->Offset(cpu_addr), size, host_ptr);
334 } else { 332 } else {
335 staging_buffer.resize(size); 333 staging_buffer.resize(size);
336 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); 334 gpu_memory.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size);
337 block->Upload(block->Offset(cpu_addr), size, staging_buffer.data()); 335 block->Upload(block->Offset(cpu_addr), size, staging_buffer.data());
338 } 336 }
339 return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr)); 337 return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr));
@@ -392,7 +390,7 @@ private:
392 continue; 390 continue;
393 } 391 }
394 staging_buffer.resize(size); 392 staging_buffer.resize(size);
395 system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size); 393 cpu_memory.ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size);
396 block->Upload(block->Offset(interval.lower()), size, staging_buffer.data()); 394 block->Upload(block->Offset(interval.lower()), size, staging_buffer.data());
397 } 395 }
398 } 396 }
@@ -431,7 +429,7 @@ private:
431 const std::size_t size = map->end - map->start; 429 const std::size_t size = map->end - map->start;
432 staging_buffer.resize(size); 430 staging_buffer.resize(size);
433 block->Download(block->Offset(map->start), size, staging_buffer.data()); 431 block->Download(block->Offset(map->start), size, staging_buffer.data());
434 system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size); 432 cpu_memory.WriteBlockUnsafe(map->start, staging_buffer.data(), size);
435 map->MarkAsModified(false, 0); 433 map->MarkAsModified(false, 0);
436 } 434 }
437 435
@@ -567,7 +565,8 @@ private:
567 } 565 }
568 566
569 VideoCore::RasterizerInterface& rasterizer; 567 VideoCore::RasterizerInterface& rasterizer;
570 Core::System& system; 568 Tegra::MemoryManager& gpu_memory;
569 Core::Memory::Memory& cpu_memory;
571 570
572 std::unique_ptr<StreamBuffer> stream_buffer; 571 std::unique_ptr<StreamBuffer> stream_buffer;
573 BufferType stream_buffer_handle; 572 BufferType stream_buffer_handle;
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 06cc12d5a..de6991ef6 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -74,8 +74,6 @@ public:
74 } 74 }
75 75
76 void WaitPendingFences() { 76 void WaitPendingFences() {
77 auto& gpu{system.GPU()};
78 auto& memory_manager{gpu.MemoryManager()};
79 while (!fences.empty()) { 77 while (!fences.empty()) {
80 TFence& current_fence = fences.front(); 78 TFence& current_fence = fences.front();
81 if (ShouldWait()) { 79 if (ShouldWait()) {
@@ -83,8 +81,8 @@ public:
83 } 81 }
84 PopAsyncFlushes(); 82 PopAsyncFlushes();
85 if (current_fence->IsSemaphore()) { 83 if (current_fence->IsSemaphore()) {
86 memory_manager.template Write<u32>(current_fence->GetAddress(), 84 gpu_memory.template Write<u32>(current_fence->GetAddress(),
87 current_fence->GetPayload()); 85 current_fence->GetPayload());
88 } else { 86 } else {
89 gpu.IncrementSyncPoint(current_fence->GetPayload()); 87 gpu.IncrementSyncPoint(current_fence->GetPayload());
90 } 88 }
@@ -93,13 +91,13 @@ public:
93 } 91 }
94 92
95protected: 93protected:
96 FenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 94 explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
97 TTextureCache& texture_cache, TTBufferCache& buffer_cache, 95 TTextureCache& texture_cache_, TTBufferCache& buffer_cache_,
98 TQueryCache& query_cache) 96 TQueryCache& query_cache_)
99 : system{system}, rasterizer{rasterizer}, texture_cache{texture_cache}, 97 : rasterizer{rasterizer_}, gpu{gpu_}, gpu_memory{gpu.MemoryManager()},
100 buffer_cache{buffer_cache}, query_cache{query_cache} {} 98 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {}
101 99
102 virtual ~FenceManager() {} 100 virtual ~FenceManager() = default;
103 101
104 /// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is 102 /// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is
105 /// true 103 /// true
@@ -113,16 +111,15 @@ protected:
113 /// Waits until a fence has been signalled by the host GPU. 111 /// Waits until a fence has been signalled by the host GPU.
114 virtual void WaitFence(TFence& fence) = 0; 112 virtual void WaitFence(TFence& fence) = 0;
115 113
116 Core::System& system;
117 VideoCore::RasterizerInterface& rasterizer; 114 VideoCore::RasterizerInterface& rasterizer;
115 Tegra::GPU& gpu;
116 Tegra::MemoryManager& gpu_memory;
118 TTextureCache& texture_cache; 117 TTextureCache& texture_cache;
119 TTBufferCache& buffer_cache; 118 TTBufferCache& buffer_cache;
120 TQueryCache& query_cache; 119 TQueryCache& query_cache;
121 120
122private: 121private:
123 void TryReleasePendingFences() { 122 void TryReleasePendingFences() {
124 auto& gpu{system.GPU()};
125 auto& memory_manager{gpu.MemoryManager()};
126 while (!fences.empty()) { 123 while (!fences.empty()) {
127 TFence& current_fence = fences.front(); 124 TFence& current_fence = fences.front();
128 if (ShouldWait() && !IsFenceSignaled(current_fence)) { 125 if (ShouldWait() && !IsFenceSignaled(current_fence)) {
@@ -130,8 +127,8 @@ private:
130 } 127 }
131 PopAsyncFlushes(); 128 PopAsyncFlushes();
132 if (current_fence->IsSemaphore()) { 129 if (current_fence->IsSemaphore()) {
133 memory_manager.template Write<u32>(current_fence->GetAddress(), 130 gpu_memory.template Write<u32>(current_fence->GetAddress(),
134 current_fence->GetPayload()); 131 current_fence->GetPayload());
135 } else { 132 } else {
136 gpu.IncrementSyncPoint(current_fence->GetPayload()); 133 gpu.IncrementSyncPoint(current_fence->GetPayload());
137 } 134 }
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index acb6e6d46..4bb9256e9 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -28,8 +28,8 @@ namespace Tegra {
28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); 28MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
29 29
30GPU::GPU(Core::System& system_, bool is_async_) 30GPU::GPU(Core::System& system_, bool is_async_)
31 : system{system_}, dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)}, 31 : system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
32 memory_manager{std::make_unique<Tegra::MemoryManager>(system)}, 32 dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)},
33 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)}, 33 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
34 fermi_2d{std::make_unique<Engines::Fermi2D>()}, 34 fermi_2d{std::make_unique<Engines::Fermi2D>()},
35 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)}, 35 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index c7d11deb2..2d15d1c6f 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -347,12 +347,11 @@ private:
347 347
348protected: 348protected:
349 Core::System& system; 349 Core::System& system;
350 std::unique_ptr<Tegra::MemoryManager> memory_manager;
350 std::unique_ptr<Tegra::DmaPusher> dma_pusher; 351 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
351 std::unique_ptr<VideoCore::RendererBase> renderer; 352 std::unique_ptr<VideoCore::RendererBase> renderer;
352 353
353private: 354private:
354 std::unique_ptr<Tegra::MemoryManager> memory_manager;
355
356 /// Mapping of command subchannels to their bound engine ids 355 /// Mapping of command subchannels to their bound engine ids
357 std::array<EngineID, 8> bound_engines = {}; 356 std::array<EngineID, 8> bound_engines = {};
358 /// 3D engine 357 /// 3D engine
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 0d3a88765..d13a66bb6 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -95,10 +95,12 @@ template <class QueryCache, class CachedQuery, class CounterStream, class HostCo
95 class QueryPool> 95 class QueryPool>
96class QueryCacheBase { 96class QueryCacheBase {
97public: 97public:
98 explicit QueryCacheBase(Core::System& system, VideoCore::RasterizerInterface& rasterizer) 98 explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
99 : system{system}, rasterizer{rasterizer}, streams{{CounterStream{ 99 Tegra::Engines::Maxwell3D& maxwell3d_,
100 static_cast<QueryCache&>(*this), 100 Tegra::MemoryManager& gpu_memory_)
101 VideoCore::QueryType::SamplesPassed}}} {} 101 : rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
102 gpu_memory{gpu_memory_}, streams{{CounterStream{static_cast<QueryCache&>(*this),
103 VideoCore::QueryType::SamplesPassed}}} {}
102 104
103 void InvalidateRegion(VAddr addr, std::size_t size) { 105 void InvalidateRegion(VAddr addr, std::size_t size) {
104 std::unique_lock lock{mutex}; 106 std::unique_lock lock{mutex};
@@ -118,29 +120,27 @@ public:
118 */ 120 */
119 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) { 121 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) {
120 std::unique_lock lock{mutex}; 122 std::unique_lock lock{mutex};
121 auto& memory_manager = system.GPU().MemoryManager(); 123 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
122 const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); 124 ASSERT(cpu_addr);
123 ASSERT(cpu_addr_opt);
124 VAddr cpu_addr = *cpu_addr_opt;
125 125
126 CachedQuery* query = TryGet(cpu_addr); 126 CachedQuery* query = TryGet(*cpu_addr);
127 if (!query) { 127 if (!query) {
128 ASSERT_OR_EXECUTE(cpu_addr_opt, return;); 128 ASSERT_OR_EXECUTE(cpu_addr, return;);
129 const auto host_ptr = memory_manager.GetPointer(gpu_addr); 129 u8* const host_ptr = gpu_memory.GetPointer(gpu_addr);
130 130
131 query = Register(type, cpu_addr, host_ptr, timestamp.has_value()); 131 query = Register(type, *cpu_addr, host_ptr, timestamp.has_value());
132 } 132 }
133 133
134 query->BindCounter(Stream(type).Current(), timestamp); 134 query->BindCounter(Stream(type).Current(), timestamp);
135 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { 135 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
136 AsyncFlushQuery(cpu_addr); 136 AsyncFlushQuery(*cpu_addr);
137 } 137 }
138 } 138 }
139 139
140 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. 140 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
141 void UpdateCounters() { 141 void UpdateCounters() {
142 std::unique_lock lock{mutex}; 142 std::unique_lock lock{mutex};
143 const auto& regs = system.GPU().Maxwell3D().regs; 143 const auto& regs = maxwell3d.regs;
144 Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable); 144 Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable);
145 } 145 }
146 146
@@ -270,8 +270,9 @@ private:
270 static constexpr std::uintptr_t PAGE_SIZE = 4096; 270 static constexpr std::uintptr_t PAGE_SIZE = 4096;
271 static constexpr unsigned PAGE_BITS = 12; 271 static constexpr unsigned PAGE_BITS = 12;
272 272
273 Core::System& system;
274 VideoCore::RasterizerInterface& rasterizer; 273 VideoCore::RasterizerInterface& rasterizer;
274 Tegra::Engines::Maxwell3D& maxwell3d;
275 Tegra::MemoryManager& gpu_memory;
275 276
276 std::recursive_mutex mutex; 277 std::recursive_mutex mutex;
277 278
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 3cbdac8e7..b3e0919f8 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -106,11 +106,8 @@ public:
106 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {} 106 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
107 107
108 /// Initialize disk cached resources for the game being emulated 108 /// Initialize disk cached resources for the game being emulated
109 virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, 109 virtual void LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading,
110 const DiskResourceLoadCallback& callback = {}) {} 110 const DiskResourceLoadCallback& callback) {}
111
112 /// Initializes renderer dirty flags
113 virtual void SetupDirtyFlags() {}
114 111
115 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. 112 /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
116 GuestDriverProfile& AccessGuestDriverProfile() { 113 GuestDriverProfile& AccessGuestDriverProfile() {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index e866d8f2f..b1c4cd62f 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -59,9 +59,10 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst
59 static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); 59 static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size));
60} 60}
61 61
62OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, 62OGLBufferCache::OGLBufferCache(VideoCore::RasterizerInterface& rasterizer,
63 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
63 const Device& device_, std::size_t stream_size) 64 const Device& device_, std::size_t stream_size)
64 : GenericBufferCache{rasterizer, system, 65 : GenericBufferCache{rasterizer, gpu_memory, cpu_memory,
65 std::make_unique<OGLStreamBuffer>(device_, stream_size, true)}, 66 std::make_unique<OGLStreamBuffer>(device_, stream_size, true)},
66 device{device_} { 67 device{device_} {
67 if (!device.HasFastBufferSubData()) { 68 if (!device.HasFastBufferSubData()) {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 88fdc0536..f75b32e31 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -52,7 +52,8 @@ private:
52using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>; 52using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>;
53class OGLBufferCache final : public GenericBufferCache { 53class OGLBufferCache final : public GenericBufferCache {
54public: 54public:
55 explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, 55 explicit OGLBufferCache(VideoCore::RasterizerInterface& rasterizer,
56 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
56 const Device& device, std::size_t stream_size); 57 const Device& device, std::size_t stream_size);
57 ~OGLBufferCache(); 58 ~OGLBufferCache();
58 59
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index 3d2588dd2..b532fdcc2 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -45,11 +45,10 @@ void GLInnerFence::Wait() {
45 glClientWaitSync(sync_object.handle, 0, GL_TIMEOUT_IGNORED); 45 glClientWaitSync(sync_object.handle, 0, GL_TIMEOUT_IGNORED);
46} 46}
47 47
48FenceManagerOpenGL::FenceManagerOpenGL(Core::System& system, 48FenceManagerOpenGL::FenceManagerOpenGL(VideoCore::RasterizerInterface& rasterizer, Tegra::GPU& gpu,
49 VideoCore::RasterizerInterface& rasterizer,
50 TextureCacheOpenGL& texture_cache, 49 TextureCacheOpenGL& texture_cache,
51 OGLBufferCache& buffer_cache, QueryCache& query_cache) 50 OGLBufferCache& buffer_cache, QueryCache& query_cache)
52 : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache) {} 51 : GenericFenceManager{rasterizer, gpu, texture_cache, buffer_cache, query_cache} {}
53 52
54Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) { 53Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) {
55 return std::make_shared<GLInnerFence>(value, is_stubbed); 54 return std::make_shared<GLInnerFence>(value, is_stubbed);
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index 1686cf5c8..da1dcdace 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -37,9 +37,9 @@ using GenericFenceManager =
37 37
38class FenceManagerOpenGL final : public GenericFenceManager { 38class FenceManagerOpenGL final : public GenericFenceManager {
39public: 39public:
40 FenceManagerOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 40 explicit FenceManagerOpenGL(VideoCore::RasterizerInterface& rasterizer, Tegra::GPU& gpu,
41 TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache, 41 TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache,
42 QueryCache& query_cache); 42 QueryCache& query_cache);
43 43
44protected: 44protected:
45 Fence CreateFence(u32 value, bool is_stubbed) override; 45 Fence CreateFence(u32 value, bool is_stubbed) override;
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index d7ba57aca..2bb8ec2b8 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -30,12 +30,13 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
30 30
31} // Anonymous namespace 31} // Anonymous namespace
32 32
33QueryCache::QueryCache(Core::System& system, RasterizerOpenGL& gl_rasterizer) 33QueryCache::QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
34 Tegra::MemoryManager& gpu_memory)
34 : VideoCommon::QueryCacheBase< 35 : VideoCommon::QueryCacheBase<
35 QueryCache, CachedQuery, CounterStream, HostCounter, 36 QueryCache, CachedQuery, CounterStream, HostCounter,
36 std::vector<OGLQuery>>{system, 37 std::vector<OGLQuery>>{static_cast<VideoCore::RasterizerInterface&>(rasterizer),
37 static_cast<VideoCore::RasterizerInterface&>(gl_rasterizer)}, 38 maxwell3d, gpu_memory},
38 gl_rasterizer{gl_rasterizer} {} 39 gl_rasterizer{rasterizer} {}
39 40
40QueryCache::~QueryCache() = default; 41QueryCache::~QueryCache() = default;
41 42
diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h
index d8e7052a1..dd626b66b 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.h
+++ b/src/video_core/renderer_opengl/gl_query_cache.h
@@ -29,7 +29,8 @@ using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
29class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, 29class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream,
30 HostCounter, std::vector<OGLQuery>> { 30 HostCounter, std::vector<OGLQuery>> {
31public: 31public:
32 explicit QueryCache(Core::System& system, RasterizerOpenGL& rasterizer); 32 explicit QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
33 Tegra::MemoryManager& gpu_memory);
33 ~QueryCache(); 34 ~QueryCache();
34 35
35 OGLQuery AllocateQuery(VideoCore::QueryType type); 36 OGLQuery AllocateQuery(VideoCore::QueryType type);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4af5824cd..bbb2eb17c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -153,16 +153,19 @@ void UpdateBindlessPointers(GLenum target, GLuint64EXT* pointers, std::size_t nu
153 153
154} // Anonymous namespace 154} // Anonymous namespace
155 155
156RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, 156RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_,
157 const Device& device, ScreenInfo& info, 157 Core::Memory::Memory& cpu_memory, const Device& device_,
158 ProgramManager& program_manager, StateTracker& state_tracker) 158 ScreenInfo& screen_info_, ProgramManager& program_manager_,
159 : RasterizerAccelerated{system.Memory()}, device{device}, texture_cache{system, *this, device, 159 StateTracker& state_tracker_)
160 state_tracker}, 160 : RasterizerAccelerated{cpu_memory}, gpu(gpu_), maxwell3d(gpu.Maxwell3D()),
161 shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, 161 kepler_compute(gpu.KeplerCompute()), gpu_memory(gpu.MemoryManager()), device(device_),
162 buffer_cache{*this, system, device, STREAM_BUFFER_SIZE}, 162 screen_info(screen_info_), program_manager(program_manager_), state_tracker(state_tracker_),
163 fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system}, 163 texture_cache(*this, maxwell3d, gpu_memory, device, state_tracker),
164 screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker}, 164 shader_cache(*this, emu_window, gpu, maxwell3d, kepler_compute, gpu_memory, device),
165 async_shaders{emu_window} { 165 query_cache(*this, maxwell3d, gpu_memory),
166 buffer_cache(*this, gpu_memory, cpu_memory, device, STREAM_BUFFER_SIZE),
167 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
168 async_shaders(emu_window) {
166 CheckExtensions(); 169 CheckExtensions();
167 170
168 unified_uniform_buffer.Create(); 171 unified_uniform_buffer.Create();
@@ -196,8 +199,7 @@ void RasterizerOpenGL::CheckExtensions() {
196} 199}
197 200
198void RasterizerOpenGL::SetupVertexFormat() { 201void RasterizerOpenGL::SetupVertexFormat() {
199 auto& gpu = system.GPU().Maxwell3D(); 202 auto& flags = maxwell3d.dirty.flags;
200 auto& flags = gpu.dirty.flags;
201 if (!flags[Dirty::VertexFormats]) { 203 if (!flags[Dirty::VertexFormats]) {
202 return; 204 return;
203 } 205 }
@@ -217,7 +219,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
217 } 219 }
218 flags[Dirty::VertexFormat0 + index] = false; 220 flags[Dirty::VertexFormat0 + index] = false;
219 221
220 const auto attrib = gpu.regs.vertex_attrib_format[index]; 222 const auto attrib = maxwell3d.regs.vertex_attrib_format[index];
221 const auto gl_index = static_cast<GLuint>(index); 223 const auto gl_index = static_cast<GLuint>(index);
222 224
223 // Disable constant attributes. 225 // Disable constant attributes.
@@ -241,8 +243,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
241} 243}
242 244
243void RasterizerOpenGL::SetupVertexBuffer() { 245void RasterizerOpenGL::SetupVertexBuffer() {
244 auto& gpu = system.GPU().Maxwell3D(); 246 auto& flags = maxwell3d.dirty.flags;
245 auto& flags = gpu.dirty.flags;
246 if (!flags[Dirty::VertexBuffers]) { 247 if (!flags[Dirty::VertexBuffers]) {
247 return; 248 return;
248 } 249 }
@@ -253,7 +254,7 @@ void RasterizerOpenGL::SetupVertexBuffer() {
253 const bool use_unified_memory = device.HasVertexBufferUnifiedMemory(); 254 const bool use_unified_memory = device.HasVertexBufferUnifiedMemory();
254 255
255 // Upload all guest vertex arrays sequentially to our buffer 256 // Upload all guest vertex arrays sequentially to our buffer
256 const auto& regs = gpu.regs; 257 const auto& regs = maxwell3d.regs;
257 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) { 258 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) {
258 if (!flags[Dirty::VertexBuffer0 + index]) { 259 if (!flags[Dirty::VertexBuffer0 + index]) {
259 continue; 260 continue;
@@ -290,14 +291,13 @@ void RasterizerOpenGL::SetupVertexBuffer() {
290} 291}
291 292
292void RasterizerOpenGL::SetupVertexInstances() { 293void RasterizerOpenGL::SetupVertexInstances() {
293 auto& gpu = system.GPU().Maxwell3D(); 294 auto& flags = maxwell3d.dirty.flags;
294 auto& flags = gpu.dirty.flags;
295 if (!flags[Dirty::VertexInstances]) { 295 if (!flags[Dirty::VertexInstances]) {
296 return; 296 return;
297 } 297 }
298 flags[Dirty::VertexInstances] = false; 298 flags[Dirty::VertexInstances] = false;
299 299
300 const auto& regs = gpu.regs; 300 const auto& regs = maxwell3d.regs;
301 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { 301 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) {
302 if (!flags[Dirty::VertexInstance0 + index]) { 302 if (!flags[Dirty::VertexInstance0 + index]) {
303 continue; 303 continue;
@@ -313,7 +313,7 @@ void RasterizerOpenGL::SetupVertexInstances() {
313 313
314GLintptr RasterizerOpenGL::SetupIndexBuffer() { 314GLintptr RasterizerOpenGL::SetupIndexBuffer() {
315 MICROPROFILE_SCOPE(OpenGL_Index); 315 MICROPROFILE_SCOPE(OpenGL_Index);
316 const auto& regs = system.GPU().Maxwell3D().regs; 316 const auto& regs = maxwell3d.regs;
317 const std::size_t size = CalculateIndexBufferSize(); 317 const std::size_t size = CalculateIndexBufferSize();
318 const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); 318 const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size);
319 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle); 319 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle);
@@ -322,15 +322,14 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() {
322 322
323void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { 323void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
324 MICROPROFILE_SCOPE(OpenGL_Shader); 324 MICROPROFILE_SCOPE(OpenGL_Shader);
325 auto& gpu = system.GPU().Maxwell3D();
326 u32 clip_distances = 0; 325 u32 clip_distances = 0;
327 326
328 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 327 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
329 const auto& shader_config = gpu.regs.shader_config[index]; 328 const auto& shader_config = maxwell3d.regs.shader_config[index];
330 const auto program{static_cast<Maxwell::ShaderProgram>(index)}; 329 const auto program{static_cast<Maxwell::ShaderProgram>(index)};
331 330
332 // Skip stages that are not enabled 331 // Skip stages that are not enabled
333 if (!gpu.regs.IsShaderConfigEnabled(index)) { 332 if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
334 switch (program) { 333 switch (program) {
335 case Maxwell::ShaderProgram::Geometry: 334 case Maxwell::ShaderProgram::Geometry:
336 program_manager.UseGeometryShader(0); 335 program_manager.UseGeometryShader(0);
@@ -391,11 +390,11 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
391 } 390 }
392 391
393 SyncClipEnabled(clip_distances); 392 SyncClipEnabled(clip_distances);
394 gpu.dirty.flags[Dirty::Shaders] = false; 393 maxwell3d.dirty.flags[Dirty::Shaders] = false;
395} 394}
396 395
397std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 396std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
398 const auto& regs = system.GPU().Maxwell3D().regs; 397 const auto& regs = maxwell3d.regs;
399 398
400 std::size_t size = 0; 399 std::size_t size = 0;
401 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 400 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -413,34 +412,27 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
413} 412}
414 413
415std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const { 414std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
416 const auto& regs = system.GPU().Maxwell3D().regs; 415 return static_cast<std::size_t>(maxwell3d.regs.index_array.count) *
417 416 static_cast<std::size_t>(maxwell3d.regs.index_array.FormatSizeInBytes());
418 return static_cast<std::size_t>(regs.index_array.count) *
419 static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
420} 417}
421 418
422void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading, 419void RasterizerOpenGL::LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading,
423 const VideoCore::DiskResourceLoadCallback& callback) { 420 const VideoCore::DiskResourceLoadCallback& callback) {
424 shader_cache.LoadDiskCache(stop_loading, callback); 421 shader_cache.LoadDiskCache(title_id, stop_loading, callback);
425}
426
427void RasterizerOpenGL::SetupDirtyFlags() {
428 state_tracker.Initialize();
429} 422}
430 423
431void RasterizerOpenGL::ConfigureFramebuffers() { 424void RasterizerOpenGL::ConfigureFramebuffers() {
432 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 425 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
433 auto& gpu = system.GPU().Maxwell3D(); 426 if (!maxwell3d.dirty.flags[VideoCommon::Dirty::RenderTargets]) {
434 if (!gpu.dirty.flags[VideoCommon::Dirty::RenderTargets]) {
435 return; 427 return;
436 } 428 }
437 gpu.dirty.flags[VideoCommon::Dirty::RenderTargets] = false; 429 maxwell3d.dirty.flags[VideoCommon::Dirty::RenderTargets] = false;
438 430
439 texture_cache.GuardRenderTargets(true); 431 texture_cache.GuardRenderTargets(true);
440 432
441 View depth_surface = texture_cache.GetDepthBufferSurface(true); 433 View depth_surface = texture_cache.GetDepthBufferSurface(true);
442 434
443 const auto& regs = gpu.regs; 435 const auto& regs = maxwell3d.regs;
444 UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0); 436 UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
445 437
446 // Bind the framebuffer surfaces 438 // Bind the framebuffer surfaces
@@ -472,8 +464,7 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
472} 464}
473 465
474void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil) { 466void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil) {
475 auto& gpu = system.GPU().Maxwell3D(); 467 const auto& regs = maxwell3d.regs;
476 const auto& regs = gpu.regs;
477 468
478 texture_cache.GuardRenderTargets(true); 469 texture_cache.GuardRenderTargets(true);
479 View color_surface; 470 View color_surface;
@@ -523,12 +514,11 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color, bool using_de
523} 514}
524 515
525void RasterizerOpenGL::Clear() { 516void RasterizerOpenGL::Clear() {
526 const auto& gpu = system.GPU().Maxwell3D(); 517 if (!maxwell3d.ShouldExecute()) {
527 if (!gpu.ShouldExecute()) {
528 return; 518 return;
529 } 519 }
530 520
531 const auto& regs = gpu.regs; 521 const auto& regs = maxwell3d.regs;
532 bool use_color{}; 522 bool use_color{};
533 bool use_depth{}; 523 bool use_depth{};
534 bool use_stencil{}; 524 bool use_stencil{};
@@ -593,7 +583,6 @@ void RasterizerOpenGL::Clear() {
593 583
594void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { 584void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
595 MICROPROFILE_SCOPE(OpenGL_Drawing); 585 MICROPROFILE_SCOPE(OpenGL_Drawing);
596 auto& gpu = system.GPU().Maxwell3D();
597 586
598 query_cache.UpdateCounters(); 587 query_cache.UpdateCounters();
599 588
@@ -641,7 +630,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
641 630
642 if (invalidated) { 631 if (invalidated) {
643 // When the stream buffer has been invalidated, we have to consider vertex buffers as dirty 632 // When the stream buffer has been invalidated, we have to consider vertex buffers as dirty
644 auto& dirty = gpu.dirty.flags; 633 auto& dirty = maxwell3d.dirty.flags;
645 dirty[Dirty::VertexBuffers] = true; 634 dirty[Dirty::VertexBuffers] = true;
646 for (int index = Dirty::VertexBuffer0; index <= Dirty::VertexBuffer31; ++index) { 635 for (int index = Dirty::VertexBuffer0; index <= Dirty::VertexBuffer31; ++index) {
647 dirty[index] = true; 636 dirty[index] = true;
@@ -662,7 +651,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
662 // Setup emulation uniform buffer. 651 // Setup emulation uniform buffer.
663 if (!device.UseAssemblyShaders()) { 652 if (!device.UseAssemblyShaders()) {
664 MaxwellUniformData ubo; 653 MaxwellUniformData ubo;
665 ubo.SetFromRegs(gpu); 654 ubo.SetFromRegs(maxwell3d);
666 const auto info = 655 const auto info =
667 buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); 656 buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment());
668 glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset, 657 glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset,
@@ -671,7 +660,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
671 660
672 // Setup shaders and their used resources. 661 // Setup shaders and their used resources.
673 texture_cache.GuardSamplers(true); 662 texture_cache.GuardSamplers(true);
674 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology); 663 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology);
675 SetupShaders(primitive_mode); 664 SetupShaders(primitive_mode);
676 texture_cache.GuardSamplers(false); 665 texture_cache.GuardSamplers(false);
677 666
@@ -688,14 +677,14 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
688 677
689 BeginTransformFeedback(primitive_mode); 678 BeginTransformFeedback(primitive_mode);
690 679
691 const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance); 680 const GLuint base_instance = static_cast<GLuint>(maxwell3d.regs.vb_base_instance);
692 const GLsizei num_instances = 681 const GLsizei num_instances =
693 static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1); 682 static_cast<GLsizei>(is_instanced ? maxwell3d.mme_draw.instance_count : 1);
694 if (is_indexed) { 683 if (is_indexed) {
695 const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base); 684 const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vb_element_base);
696 const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count); 685 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.index_array.count);
697 const GLvoid* offset = reinterpret_cast<const GLvoid*>(index_buffer_offset); 686 const GLvoid* offset = reinterpret_cast<const GLvoid*>(index_buffer_offset);
698 const GLenum format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format); 687 const GLenum format = MaxwellToGL::IndexFormat(maxwell3d.regs.index_array.format);
699 if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { 688 if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
700 glDrawElements(primitive_mode, num_vertices, format, offset); 689 glDrawElements(primitive_mode, num_vertices, format, offset);
701 } else if (num_instances == 1 && base_instance == 0) { 690 } else if (num_instances == 1 && base_instance == 0) {
@@ -714,8 +703,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
714 base_instance); 703 base_instance);
715 } 704 }
716 } else { 705 } else {
717 const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first); 706 const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vertex_buffer.first);
718 const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count); 707 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.vertex_buffer.count);
719 if (num_instances == 1 && base_instance == 0) { 708 if (num_instances == 1 && base_instance == 0) {
720 glDrawArrays(primitive_mode, base_vertex, num_vertices); 709 glDrawArrays(primitive_mode, base_vertex, num_vertices);
721 } else if (base_instance == 0) { 710 } else if (base_instance == 0) {
@@ -730,7 +719,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
730 719
731 ++num_queued_commands; 720 ++num_queued_commands;
732 721
733 system.GPU().TickWork(); 722 gpu.TickWork();
734} 723}
735 724
736void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { 725void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
@@ -753,7 +742,8 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
753 742
754 buffer_cache.Unmap(); 743 buffer_cache.Unmap();
755 744
756 const auto& launch_desc = system.GPU().KeplerCompute().launch_description; 745 const auto& launch_desc = kepler_compute.launch_description;
746 program_manager.BindCompute(kernel->GetHandle());
757 glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z); 747 glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
758 ++num_queued_commands; 748 ++num_queued_commands;
759} 749}
@@ -815,17 +805,14 @@ void RasterizerOpenGL::SyncGuestHost() {
815} 805}
816 806
817void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { 807void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) {
818 auto& gpu{system.GPU()};
819 if (!gpu.IsAsync()) { 808 if (!gpu.IsAsync()) {
820 auto& memory_manager{gpu.MemoryManager()}; 809 gpu_memory.Write<u32>(addr, value);
821 memory_manager.Write<u32>(addr, value);
822 return; 810 return;
823 } 811 }
824 fence_manager.SignalSemaphore(addr, value); 812 fence_manager.SignalSemaphore(addr, value);
825} 813}
826 814
827void RasterizerOpenGL::SignalSyncPoint(u32 value) { 815void RasterizerOpenGL::SignalSyncPoint(u32 value) {
828 auto& gpu{system.GPU()};
829 if (!gpu.IsAsync()) { 816 if (!gpu.IsAsync()) {
830 gpu.IncrementSyncPoint(value); 817 gpu.IncrementSyncPoint(value);
831 return; 818 return;
@@ -834,7 +821,6 @@ void RasterizerOpenGL::SignalSyncPoint(u32 value) {
834} 821}
835 822
836void RasterizerOpenGL::ReleaseFences() { 823void RasterizerOpenGL::ReleaseFences() {
837 auto& gpu{system.GPU()};
838 if (!gpu.IsAsync()) { 824 if (!gpu.IsAsync()) {
839 return; 825 return;
840 } 826 }
@@ -920,7 +906,7 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* sh
920 GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV}; 906 GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV};
921 907
922 MICROPROFILE_SCOPE(OpenGL_UBO); 908 MICROPROFILE_SCOPE(OpenGL_UBO);
923 const auto& stages = system.GPU().Maxwell3D().state.shader_stages; 909 const auto& stages = maxwell3d.state.shader_stages;
924 const auto& shader_stage = stages[stage_index]; 910 const auto& shader_stage = stages[stage_index];
925 const auto& entries = shader->GetEntries(); 911 const auto& entries = shader->GetEntries();
926 const bool use_unified = entries.use_unified_uniforms; 912 const bool use_unified = entries.use_unified_uniforms;
@@ -945,7 +931,7 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* sh
945 931
946void RasterizerOpenGL::SetupComputeConstBuffers(Shader* kernel) { 932void RasterizerOpenGL::SetupComputeConstBuffers(Shader* kernel) {
947 MICROPROFILE_SCOPE(OpenGL_UBO); 933 MICROPROFILE_SCOPE(OpenGL_UBO);
948 const auto& launch_desc = system.GPU().KeplerCompute().launch_description; 934 const auto& launch_desc = kepler_compute.launch_description;
949 const auto& entries = kernel->GetEntries(); 935 const auto& entries = kernel->GetEntries();
950 const bool use_unified = entries.use_unified_uniforms; 936 const bool use_unified = entries.use_unified_uniforms;
951 937
@@ -1018,9 +1004,7 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* sh
1018 GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV, 1004 GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV,
1019 }; 1005 };
1020 1006
1021 auto& gpu{system.GPU()}; 1007 const auto& cbufs{maxwell3d.state.shader_stages[stage_index]};
1022 auto& memory_manager{gpu.MemoryManager()};
1023 const auto& cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]};
1024 const auto& entries{shader->GetEntries().global_memory_entries}; 1008 const auto& entries{shader->GetEntries().global_memory_entries};
1025 1009
1026 std::array<GLuint64EXT, 32> pointers; 1010 std::array<GLuint64EXT, 32> pointers;
@@ -1030,8 +1014,8 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* sh
1030 u32 binding = assembly_shaders ? 0 : device.GetBaseBindings(stage_index).shader_storage_buffer; 1014 u32 binding = assembly_shaders ? 0 : device.GetBaseBindings(stage_index).shader_storage_buffer;
1031 for (const auto& entry : entries) { 1015 for (const auto& entry : entries) {
1032 const GPUVAddr addr{cbufs.const_buffers[entry.cbuf_index].address + entry.cbuf_offset}; 1016 const GPUVAddr addr{cbufs.const_buffers[entry.cbuf_index].address + entry.cbuf_offset};
1033 const GPUVAddr gpu_addr{memory_manager.Read<u64>(addr)}; 1017 const GPUVAddr gpu_addr{gpu_memory.Read<u64>(addr)};
1034 const u32 size{memory_manager.Read<u32>(addr + 8)}; 1018 const u32 size{gpu_memory.Read<u32>(addr + 8)};
1035 SetupGlobalMemory(binding, entry, gpu_addr, size, &pointers[binding]); 1019 SetupGlobalMemory(binding, entry, gpu_addr, size, &pointers[binding]);
1036 ++binding; 1020 ++binding;
1037 } 1021 }
@@ -1041,9 +1025,7 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* sh
1041} 1025}
1042 1026
1043void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) { 1027void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) {
1044 auto& gpu{system.GPU()}; 1028 const auto& cbufs{kepler_compute.launch_description.const_buffer_config};
1045 auto& memory_manager{gpu.MemoryManager()};
1046 const auto& cbufs{gpu.KeplerCompute().launch_description.const_buffer_config};
1047 const auto& entries{kernel->GetEntries().global_memory_entries}; 1029 const auto& entries{kernel->GetEntries().global_memory_entries};
1048 1030
1049 std::array<GLuint64EXT, 32> pointers; 1031 std::array<GLuint64EXT, 32> pointers;
@@ -1052,8 +1034,8 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) {
1052 u32 binding = 0; 1034 u32 binding = 0;
1053 for (const auto& entry : entries) { 1035 for (const auto& entry : entries) {
1054 const GPUVAddr addr{cbufs[entry.cbuf_index].Address() + entry.cbuf_offset}; 1036 const GPUVAddr addr{cbufs[entry.cbuf_index].Address() + entry.cbuf_offset};
1055 const GPUVAddr gpu_addr{memory_manager.Read<u64>(addr)}; 1037 const GPUVAddr gpu_addr{gpu_memory.Read<u64>(addr)};
1056 const u32 size{memory_manager.Read<u32>(addr + 8)}; 1038 const u32 size{gpu_memory.Read<u32>(addr + 8)};
1057 SetupGlobalMemory(binding, entry, gpu_addr, size, &pointers[binding]); 1039 SetupGlobalMemory(binding, entry, gpu_addr, size, &pointers[binding]);
1058 ++binding; 1040 ++binding;
1059 } 1041 }
@@ -1077,7 +1059,6 @@ void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& e
1077 1059
1078void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader) { 1060void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader) {
1079 MICROPROFILE_SCOPE(OpenGL_Texture); 1061 MICROPROFILE_SCOPE(OpenGL_Texture);
1080 const auto& maxwell3d = system.GPU().Maxwell3D();
1081 u32 binding = device.GetBaseBindings(stage_index).sampler; 1062 u32 binding = device.GetBaseBindings(stage_index).sampler;
1082 for (const auto& entry : shader->GetEntries().samplers) { 1063 for (const auto& entry : shader->GetEntries().samplers) {
1083 const auto shader_type = static_cast<ShaderType>(stage_index); 1064 const auto shader_type = static_cast<ShaderType>(stage_index);
@@ -1090,11 +1071,10 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader
1090 1071
1091void RasterizerOpenGL::SetupComputeTextures(Shader* kernel) { 1072void RasterizerOpenGL::SetupComputeTextures(Shader* kernel) {
1092 MICROPROFILE_SCOPE(OpenGL_Texture); 1073 MICROPROFILE_SCOPE(OpenGL_Texture);
1093 const auto& compute = system.GPU().KeplerCompute();
1094 u32 binding = 0; 1074 u32 binding = 0;
1095 for (const auto& entry : kernel->GetEntries().samplers) { 1075 for (const auto& entry : kernel->GetEntries().samplers) {
1096 for (std::size_t i = 0; i < entry.size; ++i) { 1076 for (std::size_t i = 0; i < entry.size; ++i) {
1097 const auto texture = GetTextureInfo(compute, entry, ShaderType::Compute, i); 1077 const auto texture = GetTextureInfo(kepler_compute, entry, ShaderType::Compute, i);
1098 SetupTexture(binding++, texture, entry); 1078 SetupTexture(binding++, texture, entry);
1099 } 1079 }
1100 } 1080 }
@@ -1118,20 +1098,18 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu
1118} 1098}
1119 1099
1120void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, Shader* shader) { 1100void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, Shader* shader) {
1121 const auto& maxwell3d = system.GPU().Maxwell3D();
1122 u32 binding = device.GetBaseBindings(stage_index).image; 1101 u32 binding = device.GetBaseBindings(stage_index).image;
1123 for (const auto& entry : shader->GetEntries().images) { 1102 for (const auto& entry : shader->GetEntries().images) {
1124 const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); 1103 const auto shader_type = static_cast<ShaderType>(stage_index);
1125 const auto tic = GetTextureInfo(maxwell3d, entry, shader_type).tic; 1104 const auto tic = GetTextureInfo(maxwell3d, entry, shader_type).tic;
1126 SetupImage(binding++, tic, entry); 1105 SetupImage(binding++, tic, entry);
1127 } 1106 }
1128} 1107}
1129 1108
1130void RasterizerOpenGL::SetupComputeImages(Shader* shader) { 1109void RasterizerOpenGL::SetupComputeImages(Shader* shader) {
1131 const auto& compute = system.GPU().KeplerCompute();
1132 u32 binding = 0; 1110 u32 binding = 0;
1133 for (const auto& entry : shader->GetEntries().images) { 1111 for (const auto& entry : shader->GetEntries().images) {
1134 const auto tic = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute).tic; 1112 const auto tic = GetTextureInfo(kepler_compute, entry, ShaderType::Compute).tic;
1135 SetupImage(binding++, tic, entry); 1113 SetupImage(binding++, tic, entry);
1136 } 1114 }
1137} 1115}
@@ -1151,9 +1129,8 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
1151} 1129}
1152 1130
1153void RasterizerOpenGL::SyncViewport() { 1131void RasterizerOpenGL::SyncViewport() {
1154 auto& gpu = system.GPU().Maxwell3D(); 1132 auto& flags = maxwell3d.dirty.flags;
1155 auto& flags = gpu.dirty.flags; 1133 const auto& regs = maxwell3d.regs;
1156 const auto& regs = gpu.regs;
1157 1134
1158 const bool dirty_viewport = flags[Dirty::Viewports]; 1135 const bool dirty_viewport = flags[Dirty::Viewports];
1159 const bool dirty_clip_control = flags[Dirty::ClipControl]; 1136 const bool dirty_clip_control = flags[Dirty::ClipControl];
@@ -1225,25 +1202,23 @@ void RasterizerOpenGL::SyncViewport() {
1225} 1202}
1226 1203
1227void RasterizerOpenGL::SyncDepthClamp() { 1204void RasterizerOpenGL::SyncDepthClamp() {
1228 auto& gpu = system.GPU().Maxwell3D(); 1205 auto& flags = maxwell3d.dirty.flags;
1229 auto& flags = gpu.dirty.flags;
1230 if (!flags[Dirty::DepthClampEnabled]) { 1206 if (!flags[Dirty::DepthClampEnabled]) {
1231 return; 1207 return;
1232 } 1208 }
1233 flags[Dirty::DepthClampEnabled] = false; 1209 flags[Dirty::DepthClampEnabled] = false;
1234 1210
1235 oglEnable(GL_DEPTH_CLAMP, gpu.regs.view_volume_clip_control.depth_clamp_disabled == 0); 1211 oglEnable(GL_DEPTH_CLAMP, maxwell3d.regs.view_volume_clip_control.depth_clamp_disabled == 0);
1236} 1212}
1237 1213
1238void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { 1214void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
1239 auto& gpu = system.GPU().Maxwell3D(); 1215 auto& flags = maxwell3d.dirty.flags;
1240 auto& flags = gpu.dirty.flags;
1241 if (!flags[Dirty::ClipDistances] && !flags[Dirty::Shaders]) { 1216 if (!flags[Dirty::ClipDistances] && !flags[Dirty::Shaders]) {
1242 return; 1217 return;
1243 } 1218 }
1244 flags[Dirty::ClipDistances] = false; 1219 flags[Dirty::ClipDistances] = false;
1245 1220
1246 clip_mask &= gpu.regs.clip_distance_enabled; 1221 clip_mask &= maxwell3d.regs.clip_distance_enabled;
1247 if (clip_mask == last_clip_distance_mask) { 1222 if (clip_mask == last_clip_distance_mask) {
1248 return; 1223 return;
1249 } 1224 }
@@ -1259,9 +1234,8 @@ void RasterizerOpenGL::SyncClipCoef() {
1259} 1234}
1260 1235
1261void RasterizerOpenGL::SyncCullMode() { 1236void RasterizerOpenGL::SyncCullMode() {
1262 auto& gpu = system.GPU().Maxwell3D(); 1237 auto& flags = maxwell3d.dirty.flags;
1263 auto& flags = gpu.dirty.flags; 1238 const auto& regs = maxwell3d.regs;
1264 const auto& regs = gpu.regs;
1265 1239
1266 if (flags[Dirty::CullTest]) { 1240 if (flags[Dirty::CullTest]) {
1267 flags[Dirty::CullTest] = false; 1241 flags[Dirty::CullTest] = false;
@@ -1276,26 +1250,24 @@ void RasterizerOpenGL::SyncCullMode() {
1276} 1250}
1277 1251
1278void RasterizerOpenGL::SyncPrimitiveRestart() { 1252void RasterizerOpenGL::SyncPrimitiveRestart() {
1279 auto& gpu = system.GPU().Maxwell3D(); 1253 auto& flags = maxwell3d.dirty.flags;
1280 auto& flags = gpu.dirty.flags;
1281 if (!flags[Dirty::PrimitiveRestart]) { 1254 if (!flags[Dirty::PrimitiveRestart]) {
1282 return; 1255 return;
1283 } 1256 }
1284 flags[Dirty::PrimitiveRestart] = false; 1257 flags[Dirty::PrimitiveRestart] = false;
1285 1258
1286 if (gpu.regs.primitive_restart.enabled) { 1259 if (maxwell3d.regs.primitive_restart.enabled) {
1287 glEnable(GL_PRIMITIVE_RESTART); 1260 glEnable(GL_PRIMITIVE_RESTART);
1288 glPrimitiveRestartIndex(gpu.regs.primitive_restart.index); 1261 glPrimitiveRestartIndex(maxwell3d.regs.primitive_restart.index);
1289 } else { 1262 } else {
1290 glDisable(GL_PRIMITIVE_RESTART); 1263 glDisable(GL_PRIMITIVE_RESTART);
1291 } 1264 }
1292} 1265}
1293 1266
1294void RasterizerOpenGL::SyncDepthTestState() { 1267void RasterizerOpenGL::SyncDepthTestState() {
1295 auto& gpu = system.GPU().Maxwell3D(); 1268 auto& flags = maxwell3d.dirty.flags;
1296 auto& flags = gpu.dirty.flags; 1269 const auto& regs = maxwell3d.regs;
1297 1270
1298 const auto& regs = gpu.regs;
1299 if (flags[Dirty::DepthMask]) { 1271 if (flags[Dirty::DepthMask]) {
1300 flags[Dirty::DepthMask] = false; 1272 flags[Dirty::DepthMask] = false;
1301 glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE); 1273 glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE);
@@ -1313,14 +1285,13 @@ void RasterizerOpenGL::SyncDepthTestState() {
1313} 1285}
1314 1286
1315void RasterizerOpenGL::SyncStencilTestState() { 1287void RasterizerOpenGL::SyncStencilTestState() {
1316 auto& gpu = system.GPU().Maxwell3D(); 1288 auto& flags = maxwell3d.dirty.flags;
1317 auto& flags = gpu.dirty.flags;
1318 if (!flags[Dirty::StencilTest]) { 1289 if (!flags[Dirty::StencilTest]) {
1319 return; 1290 return;
1320 } 1291 }
1321 flags[Dirty::StencilTest] = false; 1292 flags[Dirty::StencilTest] = false;
1322 1293
1323 const auto& regs = gpu.regs; 1294 const auto& regs = maxwell3d.regs;
1324 oglEnable(GL_STENCIL_TEST, regs.stencil_enable); 1295 oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
1325 1296
1326 glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), 1297 glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
@@ -1345,25 +1316,24 @@ void RasterizerOpenGL::SyncStencilTestState() {
1345} 1316}
1346 1317
1347void RasterizerOpenGL::SyncRasterizeEnable() { 1318void RasterizerOpenGL::SyncRasterizeEnable() {
1348 auto& gpu = system.GPU().Maxwell3D(); 1319 auto& flags = maxwell3d.dirty.flags;
1349 auto& flags = gpu.dirty.flags;
1350 if (!flags[Dirty::RasterizeEnable]) { 1320 if (!flags[Dirty::RasterizeEnable]) {
1351 return; 1321 return;
1352 } 1322 }
1353 flags[Dirty::RasterizeEnable] = false; 1323 flags[Dirty::RasterizeEnable] = false;
1354 1324
1355 oglEnable(GL_RASTERIZER_DISCARD, gpu.regs.rasterize_enable == 0); 1325 oglEnable(GL_RASTERIZER_DISCARD, maxwell3d.regs.rasterize_enable == 0);
1356} 1326}
1357 1327
1358void RasterizerOpenGL::SyncPolygonModes() { 1328void RasterizerOpenGL::SyncPolygonModes() {
1359 auto& gpu = system.GPU().Maxwell3D(); 1329 auto& flags = maxwell3d.dirty.flags;
1360 auto& flags = gpu.dirty.flags;
1361 if (!flags[Dirty::PolygonModes]) { 1330 if (!flags[Dirty::PolygonModes]) {
1362 return; 1331 return;
1363 } 1332 }
1364 flags[Dirty::PolygonModes] = false; 1333 flags[Dirty::PolygonModes] = false;
1365 1334
1366 if (gpu.regs.fill_rectangle) { 1335 const auto& regs = maxwell3d.regs;
1336 if (regs.fill_rectangle) {
1367 if (!GLAD_GL_NV_fill_rectangle) { 1337 if (!GLAD_GL_NV_fill_rectangle) {
1368 LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported"); 1338 LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported");
1369 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 1339 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -1376,27 +1346,26 @@ void RasterizerOpenGL::SyncPolygonModes() {
1376 return; 1346 return;
1377 } 1347 }
1378 1348
1379 if (gpu.regs.polygon_mode_front == gpu.regs.polygon_mode_back) { 1349 if (regs.polygon_mode_front == regs.polygon_mode_back) {
1380 flags[Dirty::PolygonModeFront] = false; 1350 flags[Dirty::PolygonModeFront] = false;
1381 flags[Dirty::PolygonModeBack] = false; 1351 flags[Dirty::PolygonModeBack] = false;
1382 glPolygonMode(GL_FRONT_AND_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front)); 1352 glPolygonMode(GL_FRONT_AND_BACK, MaxwellToGL::PolygonMode(regs.polygon_mode_front));
1383 return; 1353 return;
1384 } 1354 }
1385 1355
1386 if (flags[Dirty::PolygonModeFront]) { 1356 if (flags[Dirty::PolygonModeFront]) {
1387 flags[Dirty::PolygonModeFront] = false; 1357 flags[Dirty::PolygonModeFront] = false;
1388 glPolygonMode(GL_FRONT, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front)); 1358 glPolygonMode(GL_FRONT, MaxwellToGL::PolygonMode(regs.polygon_mode_front));
1389 } 1359 }
1390 1360
1391 if (flags[Dirty::PolygonModeBack]) { 1361 if (flags[Dirty::PolygonModeBack]) {
1392 flags[Dirty::PolygonModeBack] = false; 1362 flags[Dirty::PolygonModeBack] = false;
1393 glPolygonMode(GL_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_back)); 1363 glPolygonMode(GL_BACK, MaxwellToGL::PolygonMode(regs.polygon_mode_back));
1394 } 1364 }
1395} 1365}
1396 1366
1397void RasterizerOpenGL::SyncColorMask() { 1367void RasterizerOpenGL::SyncColorMask() {
1398 auto& gpu = system.GPU().Maxwell3D(); 1368 auto& flags = maxwell3d.dirty.flags;
1399 auto& flags = gpu.dirty.flags;
1400 if (!flags[Dirty::ColorMasks]) { 1369 if (!flags[Dirty::ColorMasks]) {
1401 return; 1370 return;
1402 } 1371 }
@@ -1405,7 +1374,7 @@ void RasterizerOpenGL::SyncColorMask() {
1405 const bool force = flags[Dirty::ColorMaskCommon]; 1374 const bool force = flags[Dirty::ColorMaskCommon];
1406 flags[Dirty::ColorMaskCommon] = false; 1375 flags[Dirty::ColorMaskCommon] = false;
1407 1376
1408 const auto& regs = gpu.regs; 1377 const auto& regs = maxwell3d.regs;
1409 if (regs.color_mask_common) { 1378 if (regs.color_mask_common) {
1410 if (!force && !flags[Dirty::ColorMask0]) { 1379 if (!force && !flags[Dirty::ColorMask0]) {
1411 return; 1380 return;
@@ -1430,33 +1399,30 @@ void RasterizerOpenGL::SyncColorMask() {
1430} 1399}
1431 1400
1432void RasterizerOpenGL::SyncMultiSampleState() { 1401void RasterizerOpenGL::SyncMultiSampleState() {
1433 auto& gpu = system.GPU().Maxwell3D(); 1402 auto& flags = maxwell3d.dirty.flags;
1434 auto& flags = gpu.dirty.flags;
1435 if (!flags[Dirty::MultisampleControl]) { 1403 if (!flags[Dirty::MultisampleControl]) {
1436 return; 1404 return;
1437 } 1405 }
1438 flags[Dirty::MultisampleControl] = false; 1406 flags[Dirty::MultisampleControl] = false;
1439 1407
1440 const auto& regs = system.GPU().Maxwell3D().regs; 1408 const auto& regs = maxwell3d.regs;
1441 oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage); 1409 oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage);
1442 oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one); 1410 oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one);
1443} 1411}
1444 1412
1445void RasterizerOpenGL::SyncFragmentColorClampState() { 1413void RasterizerOpenGL::SyncFragmentColorClampState() {
1446 auto& gpu = system.GPU().Maxwell3D(); 1414 auto& flags = maxwell3d.dirty.flags;
1447 auto& flags = gpu.dirty.flags;
1448 if (!flags[Dirty::FragmentClampColor]) { 1415 if (!flags[Dirty::FragmentClampColor]) {
1449 return; 1416 return;
1450 } 1417 }
1451 flags[Dirty::FragmentClampColor] = false; 1418 flags[Dirty::FragmentClampColor] = false;
1452 1419
1453 glClampColor(GL_CLAMP_FRAGMENT_COLOR, gpu.regs.frag_color_clamp ? GL_TRUE : GL_FALSE); 1420 glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d.regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
1454} 1421}
1455 1422
1456void RasterizerOpenGL::SyncBlendState() { 1423void RasterizerOpenGL::SyncBlendState() {
1457 auto& gpu = system.GPU().Maxwell3D(); 1424 auto& flags = maxwell3d.dirty.flags;
1458 auto& flags = gpu.dirty.flags; 1425 const auto& regs = maxwell3d.regs;
1459 const auto& regs = gpu.regs;
1460 1426
1461 if (flags[Dirty::BlendColor]) { 1427 if (flags[Dirty::BlendColor]) {
1462 flags[Dirty::BlendColor] = false; 1428 flags[Dirty::BlendColor] = false;
@@ -1513,14 +1479,13 @@ void RasterizerOpenGL::SyncBlendState() {
1513} 1479}
1514 1480
1515void RasterizerOpenGL::SyncLogicOpState() { 1481void RasterizerOpenGL::SyncLogicOpState() {
1516 auto& gpu = system.GPU().Maxwell3D(); 1482 auto& flags = maxwell3d.dirty.flags;
1517 auto& flags = gpu.dirty.flags;
1518 if (!flags[Dirty::LogicOp]) { 1483 if (!flags[Dirty::LogicOp]) {
1519 return; 1484 return;
1520 } 1485 }
1521 flags[Dirty::LogicOp] = false; 1486 flags[Dirty::LogicOp] = false;
1522 1487
1523 const auto& regs = gpu.regs; 1488 const auto& regs = maxwell3d.regs;
1524 if (regs.logic_op.enable) { 1489 if (regs.logic_op.enable) {
1525 glEnable(GL_COLOR_LOGIC_OP); 1490 glEnable(GL_COLOR_LOGIC_OP);
1526 glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation)); 1491 glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation));
@@ -1530,14 +1495,13 @@ void RasterizerOpenGL::SyncLogicOpState() {
1530} 1495}
1531 1496
1532void RasterizerOpenGL::SyncScissorTest() { 1497void RasterizerOpenGL::SyncScissorTest() {
1533 auto& gpu = system.GPU().Maxwell3D(); 1498 auto& flags = maxwell3d.dirty.flags;
1534 auto& flags = gpu.dirty.flags;
1535 if (!flags[Dirty::Scissors]) { 1499 if (!flags[Dirty::Scissors]) {
1536 return; 1500 return;
1537 } 1501 }
1538 flags[Dirty::Scissors] = false; 1502 flags[Dirty::Scissors] = false;
1539 1503
1540 const auto& regs = gpu.regs; 1504 const auto& regs = maxwell3d.regs;
1541 for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) { 1505 for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) {
1542 if (!flags[Dirty::Scissor0 + index]) { 1506 if (!flags[Dirty::Scissor0 + index]) {
1543 continue; 1507 continue;
@@ -1556,16 +1520,15 @@ void RasterizerOpenGL::SyncScissorTest() {
1556} 1520}
1557 1521
1558void RasterizerOpenGL::SyncPointState() { 1522void RasterizerOpenGL::SyncPointState() {
1559 auto& gpu = system.GPU().Maxwell3D(); 1523 auto& flags = maxwell3d.dirty.flags;
1560 auto& flags = gpu.dirty.flags;
1561 if (!flags[Dirty::PointSize]) { 1524 if (!flags[Dirty::PointSize]) {
1562 return; 1525 return;
1563 } 1526 }
1564 flags[Dirty::PointSize] = false; 1527 flags[Dirty::PointSize] = false;
1565 1528
1566 oglEnable(GL_POINT_SPRITE, gpu.regs.point_sprite_enable); 1529 oglEnable(GL_POINT_SPRITE, maxwell3d.regs.point_sprite_enable);
1567 1530
1568 if (gpu.regs.vp_point_size.enable) { 1531 if (maxwell3d.regs.vp_point_size.enable) {
1569 // By definition of GL_POINT_SIZE, it only matters if GL_PROGRAM_POINT_SIZE is disabled. 1532 // By definition of GL_POINT_SIZE, it only matters if GL_PROGRAM_POINT_SIZE is disabled.
1570 glEnable(GL_PROGRAM_POINT_SIZE); 1533 glEnable(GL_PROGRAM_POINT_SIZE);
1571 return; 1534 return;
@@ -1573,32 +1536,30 @@ void RasterizerOpenGL::SyncPointState() {
1573 1536
1574 // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid 1537 // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
1575 // in OpenGL). 1538 // in OpenGL).
1576 glPointSize(std::max(1.0f, gpu.regs.point_size)); 1539 glPointSize(std::max(1.0f, maxwell3d.regs.point_size));
1577 glDisable(GL_PROGRAM_POINT_SIZE); 1540 glDisable(GL_PROGRAM_POINT_SIZE);
1578} 1541}
1579 1542
1580void RasterizerOpenGL::SyncLineState() { 1543void RasterizerOpenGL::SyncLineState() {
1581 auto& gpu = system.GPU().Maxwell3D(); 1544 auto& flags = maxwell3d.dirty.flags;
1582 auto& flags = gpu.dirty.flags;
1583 if (!flags[Dirty::LineWidth]) { 1545 if (!flags[Dirty::LineWidth]) {
1584 return; 1546 return;
1585 } 1547 }
1586 flags[Dirty::LineWidth] = false; 1548 flags[Dirty::LineWidth] = false;
1587 1549
1588 const auto& regs = gpu.regs; 1550 const auto& regs = maxwell3d.regs;
1589 oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable); 1551 oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable);
1590 glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased); 1552 glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased);
1591} 1553}
1592 1554
1593void RasterizerOpenGL::SyncPolygonOffset() { 1555void RasterizerOpenGL::SyncPolygonOffset() {
1594 auto& gpu = system.GPU().Maxwell3D(); 1556 auto& flags = maxwell3d.dirty.flags;
1595 auto& flags = gpu.dirty.flags;
1596 if (!flags[Dirty::PolygonOffset]) { 1557 if (!flags[Dirty::PolygonOffset]) {
1597 return; 1558 return;
1598 } 1559 }
1599 flags[Dirty::PolygonOffset] = false; 1560 flags[Dirty::PolygonOffset] = false;
1600 1561
1601 const auto& regs = gpu.regs; 1562 const auto& regs = maxwell3d.regs;
1602 oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable); 1563 oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable);
1603 oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable); 1564 oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable);
1604 oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable); 1565 oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable);
@@ -1612,14 +1573,13 @@ void RasterizerOpenGL::SyncPolygonOffset() {
1612} 1573}
1613 1574
1614void RasterizerOpenGL::SyncAlphaTest() { 1575void RasterizerOpenGL::SyncAlphaTest() {
1615 auto& gpu = system.GPU().Maxwell3D(); 1576 auto& flags = maxwell3d.dirty.flags;
1616 auto& flags = gpu.dirty.flags;
1617 if (!flags[Dirty::AlphaTest]) { 1577 if (!flags[Dirty::AlphaTest]) {
1618 return; 1578 return;
1619 } 1579 }
1620 flags[Dirty::AlphaTest] = false; 1580 flags[Dirty::AlphaTest] = false;
1621 1581
1622 const auto& regs = gpu.regs; 1582 const auto& regs = maxwell3d.regs;
1623 if (regs.alpha_test_enabled && regs.rt_control.count > 1) { 1583 if (regs.alpha_test_enabled && regs.rt_control.count > 1) {
1624 LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested"); 1584 LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested");
1625 } 1585 }
@@ -1633,20 +1593,19 @@ void RasterizerOpenGL::SyncAlphaTest() {
1633} 1593}
1634 1594
1635void RasterizerOpenGL::SyncFramebufferSRGB() { 1595void RasterizerOpenGL::SyncFramebufferSRGB() {
1636 auto& gpu = system.GPU().Maxwell3D(); 1596 auto& flags = maxwell3d.dirty.flags;
1637 auto& flags = gpu.dirty.flags;
1638 if (!flags[Dirty::FramebufferSRGB]) { 1597 if (!flags[Dirty::FramebufferSRGB]) {
1639 return; 1598 return;
1640 } 1599 }
1641 flags[Dirty::FramebufferSRGB] = false; 1600 flags[Dirty::FramebufferSRGB] = false;
1642 1601
1643 oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb); 1602 oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb);
1644} 1603}
1645 1604
1646void RasterizerOpenGL::SyncTransformFeedback() { 1605void RasterizerOpenGL::SyncTransformFeedback() {
1647 // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal 1606 // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
1648 // when this is required. 1607 // when this is required.
1649 const auto& regs = system.GPU().Maxwell3D().regs; 1608 const auto& regs = maxwell3d.regs;
1650 1609
1651 static constexpr std::size_t STRIDE = 3; 1610 static constexpr std::size_t STRIDE = 3;
1652 std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs; 1611 std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs;
@@ -1698,7 +1657,7 @@ void RasterizerOpenGL::SyncTransformFeedback() {
1698} 1657}
1699 1658
1700void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) { 1659void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
1701 const auto& regs = system.GPU().Maxwell3D().regs; 1660 const auto& regs = maxwell3d.regs;
1702 if (regs.tfb_enabled == 0) { 1661 if (regs.tfb_enabled == 0) {
1703 return; 1662 return;
1704 } 1663 }
@@ -1741,7 +1700,7 @@ void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
1741} 1700}
1742 1701
1743void RasterizerOpenGL::EndTransformFeedback() { 1702void RasterizerOpenGL::EndTransformFeedback() {
1744 const auto& regs = system.GPU().Maxwell3D().regs; 1703 const auto& regs = maxwell3d.regs;
1745 if (regs.tfb_enabled == 0) { 1704 if (regs.tfb_enabled == 0) {
1746 return; 1705 return;
1747 } 1706 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index ccc6f50f6..f451404b2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -36,8 +36,8 @@
36#include "video_core/shader/async_shaders.h" 36#include "video_core/shader/async_shaders.h"
37#include "video_core/textures/texture.h" 37#include "video_core/textures/texture.h"
38 38
39namespace Core { 39namespace Core::Memory {
40class System; 40class Memory;
41} 41}
42 42
43namespace Core::Frontend { 43namespace Core::Frontend {
@@ -55,9 +55,10 @@ struct DrawParameters;
55 55
56class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { 56class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
57public: 57public:
58 explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, 58 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
59 const Device& device, ScreenInfo& info, 59 Core::Memory::Memory& cpu_memory, const Device& device,
60 ProgramManager& program_manager, StateTracker& state_tracker); 60 ScreenInfo& screen_info, ProgramManager& program_manager,
61 StateTracker& state_tracker);
61 ~RasterizerOpenGL() override; 62 ~RasterizerOpenGL() override;
62 63
63 void Draw(bool is_indexed, bool is_instanced) override; 64 void Draw(bool is_indexed, bool is_instanced) override;
@@ -83,9 +84,8 @@ public:
83 const Tegra::Engines::Fermi2D::Config& copy_config) override; 84 const Tegra::Engines::Fermi2D::Config& copy_config) override;
84 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 85 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
85 u32 pixel_stride) override; 86 u32 pixel_stride) override;
86 void LoadDiskResources(const std::atomic_bool& stop_loading, 87 void LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading,
87 const VideoCore::DiskResourceLoadCallback& callback) override; 88 const VideoCore::DiskResourceLoadCallback& callback) override;
88 void SetupDirtyFlags() override;
89 89
90 /// Returns true when there are commands queued to the OpenGL server. 90 /// Returns true when there are commands queued to the OpenGL server.
91 bool AnyCommandQueued() const { 91 bool AnyCommandQueued() const {
@@ -237,7 +237,15 @@ private:
237 237
238 void SetupShaders(GLenum primitive_mode); 238 void SetupShaders(GLenum primitive_mode);
239 239
240 Tegra::GPU& gpu;
241 Tegra::Engines::Maxwell3D& maxwell3d;
242 Tegra::Engines::KeplerCompute& kepler_compute;
243 Tegra::MemoryManager& gpu_memory;
244
240 const Device& device; 245 const Device& device;
246 ScreenInfo& screen_info;
247 ProgramManager& program_manager;
248 StateTracker& state_tracker;
241 249
242 TextureCacheOpenGL texture_cache; 250 TextureCacheOpenGL texture_cache;
243 ShaderCacheOpenGL shader_cache; 251 ShaderCacheOpenGL shader_cache;
@@ -247,10 +255,6 @@ private:
247 OGLBufferCache buffer_cache; 255 OGLBufferCache buffer_cache;
248 FenceManagerOpenGL fence_manager; 256 FenceManagerOpenGL fence_manager;
249 257
250 Core::System& system;
251 ScreenInfo& screen_info;
252 ProgramManager& program_manager;
253 StateTracker& state_tracker;
254 VideoCommon::Shader::AsyncShaders async_shaders; 258 VideoCommon::Shader::AsyncShaders async_shaders;
255 259
256 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 260 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index a07d56ef0..bd56bed0c 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -239,12 +239,11 @@ std::unique_ptr<Shader> Shader::CreateStageFromMemory(
239 ProgramCode code_b, VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr) { 239 ProgramCode code_b, VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr) {
240 const auto shader_type = GetShaderType(program_type); 240 const auto shader_type = GetShaderType(program_type);
241 241
242 auto& gpu = params.system.GPU(); 242 auto& gpu = params.gpu;
243 gpu.ShaderNotify().MarkSharderBuilding(); 243 gpu.ShaderNotify().MarkSharderBuilding();
244 244
245 auto registry = std::make_shared<Registry>(shader_type, gpu.Maxwell3D()); 245 auto registry = std::make_shared<Registry>(shader_type, gpu.Maxwell3D());
246 if (!async_shaders.IsShaderAsync(params.system.GPU()) || 246 if (!async_shaders.IsShaderAsync(gpu) || !params.device.UseAsynchronousShaders()) {
247 !params.device.UseAsynchronousShaders()) {
248 const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry); 247 const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
249 // TODO(Rodrigo): Handle VertexA shaders 248 // TODO(Rodrigo): Handle VertexA shaders
250 // std::optional<ShaderIR> ir_b; 249 // std::optional<ShaderIR> ir_b;
@@ -287,11 +286,10 @@ std::unique_ptr<Shader> Shader::CreateStageFromMemory(
287 286
288std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params, 287std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params,
289 ProgramCode code) { 288 ProgramCode code) {
290 auto& gpu = params.system.GPU(); 289 auto& gpu = params.gpu;
291 gpu.ShaderNotify().MarkSharderBuilding(); 290 gpu.ShaderNotify().MarkSharderBuilding();
292 291
293 auto& engine = gpu.KeplerCompute(); 292 auto registry = std::make_shared<Registry>(ShaderType::Compute, params.engine);
294 auto registry = std::make_shared<Registry>(ShaderType::Compute, engine);
295 const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry); 293 const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
296 const u64 uid = params.unique_identifier; 294 const u64 uid = params.unique_identifier;
297 auto program = BuildShader(params.device, ShaderType::Compute, uid, ir, *registry); 295 auto program = BuildShader(params.device, ShaderType::Compute, uid, ir, *registry);
@@ -320,15 +318,20 @@ std::unique_ptr<Shader> Shader::CreateFromCache(const ShaderParameters& params,
320 precompiled_shader.registry, precompiled_shader.entries, precompiled_shader.program)); 318 precompiled_shader.registry, precompiled_shader.entries, precompiled_shader.program));
321} 319}
322 320
323ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, 321ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer,
324 Core::Frontend::EmuWindow& emu_window, const Device& device) 322 Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
325 : VideoCommon::ShaderCache<Shader>{rasterizer}, system{system}, 323 Tegra::Engines::Maxwell3D& maxwell3d_,
326 emu_window{emu_window}, device{device}, disk_cache{system} {} 324 Tegra::Engines::KeplerCompute& kepler_compute_,
325 Tegra::MemoryManager& gpu_memory_, const Device& device_)
326 : VideoCommon::ShaderCache<Shader>{rasterizer}, emu_window{emu_window_}, gpu{gpu_},
327 gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_},
328 kepler_compute{kepler_compute_}, device{device_} {}
327 329
328ShaderCacheOpenGL::~ShaderCacheOpenGL() = default; 330ShaderCacheOpenGL::~ShaderCacheOpenGL() = default;
329 331
330void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, 332void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop_loading,
331 const VideoCore::DiskResourceLoadCallback& callback) { 333 const VideoCore::DiskResourceLoadCallback& callback) {
334 disk_cache.BindTitleID(title_id);
332 const std::optional transferable = disk_cache.LoadTransferable(); 335 const std::optional transferable = disk_cache.LoadTransferable();
333 if (!transferable) { 336 if (!transferable) {
334 return; 337 return;
@@ -481,21 +484,19 @@ ProgramSharedPtr ShaderCacheOpenGL::GeneratePrecompiledProgram(
481 484
482Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program, 485Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
483 VideoCommon::Shader::AsyncShaders& async_shaders) { 486 VideoCommon::Shader::AsyncShaders& async_shaders) {
484 if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) { 487 if (!maxwell3d.dirty.flags[Dirty::Shaders]) {
485 auto* last_shader = last_shaders[static_cast<std::size_t>(program)]; 488 auto* last_shader = last_shaders[static_cast<std::size_t>(program)];
486 if (last_shader->IsBuilt()) { 489 if (last_shader->IsBuilt()) {
487 return last_shader; 490 return last_shader;
488 } 491 }
489 } 492 }
490 493
491 auto& memory_manager{system.GPU().MemoryManager()}; 494 const GPUVAddr address{GetShaderAddress(maxwell3d, program)};
492 const GPUVAddr address{GetShaderAddress(system, program)};
493 495
494 if (device.UseAsynchronousShaders() && async_shaders.HasCompletedWork()) { 496 if (device.UseAsynchronousShaders() && async_shaders.HasCompletedWork()) {
495 auto completed_work = async_shaders.GetCompletedWork(); 497 auto completed_work = async_shaders.GetCompletedWork();
496 for (auto& work : completed_work) { 498 for (auto& work : completed_work) {
497 Shader* shader = TryGet(work.cpu_address); 499 Shader* shader = TryGet(work.cpu_address);
498 auto& gpu = system.GPU();
499 gpu.ShaderNotify().MarkShaderComplete(); 500 gpu.ShaderNotify().MarkShaderComplete();
500 if (shader == nullptr) { 501 if (shader == nullptr) {
501 continue; 502 continue;
@@ -507,14 +508,13 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
507 shader->AsyncGLASMBuilt(std::move(work.program.glasm)); 508 shader->AsyncGLASMBuilt(std::move(work.program.glasm));
508 } 509 }
509 510
511 auto& registry = shader->GetRegistry();
512
510 ShaderDiskCacheEntry entry; 513 ShaderDiskCacheEntry entry;
511 entry.type = work.shader_type; 514 entry.type = work.shader_type;
512 entry.code = std::move(work.code); 515 entry.code = std::move(work.code);
513 entry.code_b = std::move(work.code_b); 516 entry.code_b = std::move(work.code_b);
514 entry.unique_identifier = work.uid; 517 entry.unique_identifier = work.uid;
515
516 auto& registry = shader->GetRegistry();
517
518 entry.bound_buffer = registry.GetBoundBuffer(); 518 entry.bound_buffer = registry.GetBoundBuffer();
519 entry.graphics_info = registry.GetGraphicsInfo(); 519 entry.graphics_info = registry.GetGraphicsInfo();
520 entry.keys = registry.GetKeys(); 520 entry.keys = registry.GetKeys();
@@ -525,28 +525,28 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
525 } 525 }
526 526
527 // Look up shader in the cache based on address 527 // Look up shader in the cache based on address
528 const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; 528 const std::optional<VAddr> cpu_addr{gpu_memory.GpuToCpuAddress(address)};
529 if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) { 529 if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) {
530 return last_shaders[static_cast<std::size_t>(program)] = shader; 530 return last_shaders[static_cast<std::size_t>(program)] = shader;
531 } 531 }
532 532
533 const auto host_ptr{memory_manager.GetPointer(address)}; 533 const u8* const host_ptr{gpu_memory.GetPointer(address)};
534 534
535 // No shader found - create a new one 535 // No shader found - create a new one
536 ProgramCode code{GetShaderCode(memory_manager, address, host_ptr, false)}; 536 ProgramCode code{GetShaderCode(gpu_memory, address, host_ptr, false)};
537 ProgramCode code_b; 537 ProgramCode code_b;
538 if (program == Maxwell::ShaderProgram::VertexA) { 538 if (program == Maxwell::ShaderProgram::VertexA) {
539 const GPUVAddr address_b{GetShaderAddress(system, Maxwell::ShaderProgram::VertexB)}; 539 const GPUVAddr address_b{GetShaderAddress(maxwell3d, Maxwell::ShaderProgram::VertexB)};
540 const u8* host_ptr_b = memory_manager.GetPointer(address_b); 540 const u8* host_ptr_b = gpu_memory.GetPointer(address_b);
541 code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false); 541 code_b = GetShaderCode(gpu_memory, address_b, host_ptr_b, false);
542 } 542 }
543 const std::size_t code_size = code.size() * sizeof(u64); 543 const std::size_t code_size = code.size() * sizeof(u64);
544 544
545 const u64 unique_identifier = GetUniqueIdentifier( 545 const u64 unique_identifier = GetUniqueIdentifier(
546 GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); 546 GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b);
547 547
548 const ShaderParameters params{system, disk_cache, device, 548 const ShaderParameters params{gpu, maxwell3d, disk_cache, device,
549 *cpu_addr, host_ptr, unique_identifier}; 549 *cpu_addr, host_ptr, unique_identifier};
550 550
551 std::unique_ptr<Shader> shader; 551 std::unique_ptr<Shader> shader;
552 const auto found = runtime_cache.find(unique_identifier); 552 const auto found = runtime_cache.find(unique_identifier);
@@ -568,21 +568,20 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
568} 568}
569 569
570Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { 570Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
571 auto& memory_manager{system.GPU().MemoryManager()}; 571 const std::optional<VAddr> cpu_addr{gpu_memory.GpuToCpuAddress(code_addr)};
572 const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)};
573 572
574 if (Shader* const kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get()) { 573 if (Shader* const kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get()) {
575 return kernel; 574 return kernel;
576 } 575 }
577 576
578 const auto host_ptr{memory_manager.GetPointer(code_addr)};
579 // No kernel found, create a new one 577 // No kernel found, create a new one
580 ProgramCode code{GetShaderCode(memory_manager, code_addr, host_ptr, true)}; 578 const u8* host_ptr{gpu_memory.GetPointer(code_addr)};
579 ProgramCode code{GetShaderCode(gpu_memory, code_addr, host_ptr, true)};
581 const std::size_t code_size{code.size() * sizeof(u64)}; 580 const std::size_t code_size{code.size() * sizeof(u64)};
582 const u64 unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; 581 const u64 unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)};
583 582
584 const ShaderParameters params{system, disk_cache, device, 583 const ShaderParameters params{gpu, kepler_compute, disk_cache, device,
585 *cpu_addr, host_ptr, unique_identifier}; 584 *cpu_addr, host_ptr, unique_identifier};
586 585
587 std::unique_ptr<Shader> kernel; 586 std::unique_ptr<Shader> kernel;
588 const auto found = runtime_cache.find(unique_identifier); 587 const auto found = runtime_cache.find(unique_identifier);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 7528ac686..1708af06a 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -25,8 +25,8 @@
25#include "video_core/shader/shader_ir.h" 25#include "video_core/shader/shader_ir.h"
26#include "video_core/shader_cache.h" 26#include "video_core/shader_cache.h"
27 27
28namespace Core { 28namespace Tegra {
29class System; 29class MemoryManager;
30} 30}
31 31
32namespace Core::Frontend { 32namespace Core::Frontend {
@@ -57,11 +57,12 @@ struct PrecompiledShader {
57}; 57};
58 58
59struct ShaderParameters { 59struct ShaderParameters {
60 Core::System& system; 60 Tegra::GPU& gpu;
61 Tegra::Engines::ConstBufferEngineInterface& engine;
61 ShaderDiskCacheOpenGL& disk_cache; 62 ShaderDiskCacheOpenGL& disk_cache;
62 const Device& device; 63 const Device& device;
63 VAddr cpu_addr; 64 VAddr cpu_addr;
64 u8* host_ptr; 65 const u8* host_ptr;
65 u64 unique_identifier; 66 u64 unique_identifier;
66}; 67};
67 68
@@ -118,12 +119,14 @@ private:
118 119
119class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> { 120class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> {
120public: 121public:
121 explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, 122 explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::Frontend::EmuWindow& emu_window,
122 Core::Frontend::EmuWindow& emu_window, const Device& device); 123 Tegra::GPU& gpu, Tegra::Engines::Maxwell3D& maxwell3d,
124 Tegra::Engines::KeplerCompute& kepler_compute,
125 Tegra::MemoryManager& gpu_memory, const Device& device);
123 ~ShaderCacheOpenGL() override; 126 ~ShaderCacheOpenGL() override;
124 127
125 /// Loads disk cache for the current game 128 /// Loads disk cache for the current game
126 void LoadDiskCache(const std::atomic_bool& stop_loading, 129 void LoadDiskCache(u64 title_id, const std::atomic_bool& stop_loading,
127 const VideoCore::DiskResourceLoadCallback& callback); 130 const VideoCore::DiskResourceLoadCallback& callback);
128 131
129 /// Gets the current specified shader stage program 132 /// Gets the current specified shader stage program
@@ -138,9 +141,13 @@ private:
138 const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry, 141 const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry,
139 const std::unordered_set<GLenum>& supported_formats); 142 const std::unordered_set<GLenum>& supported_formats);
140 143
141 Core::System& system;
142 Core::Frontend::EmuWindow& emu_window; 144 Core::Frontend::EmuWindow& emu_window;
145 Tegra::GPU& gpu;
146 Tegra::MemoryManager& gpu_memory;
147 Tegra::Engines::Maxwell3D& maxwell3d;
148 Tegra::Engines::KeplerCompute& kepler_compute;
143 const Device& device; 149 const Device& device;
150
144 ShaderDiskCacheOpenGL disk_cache; 151 ShaderDiskCacheOpenGL disk_cache;
145 std::unordered_map<u64, PrecompiledShader> runtime_cache; 152 std::unordered_map<u64, PrecompiledShader> runtime_cache;
146 153
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 40c0877c1..166ee34e1 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -206,13 +206,17 @@ bool ShaderDiskCacheEntry::Save(Common::FS::IOFile& file) const {
206 flat_bindless_samplers.size(); 206 flat_bindless_samplers.size();
207} 207}
208 208
209ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} 209ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL() = default;
210 210
211ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default; 211ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
212 212
213void ShaderDiskCacheOpenGL::BindTitleID(u64 title_id_) {
214 title_id = title_id_;
215}
216
213std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() { 217std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
214 // Skip games without title id 218 // Skip games without title id
215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; 219 const bool has_title_id = title_id != 0;
216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) { 220 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
217 return std::nullopt; 221 return std::nullopt;
218 } 222 }
@@ -474,7 +478,7 @@ std::string ShaderDiskCacheOpenGL::GetBaseDir() const {
474} 478}
475 479
476std::string ShaderDiskCacheOpenGL::GetTitleID() const { 480std::string ShaderDiskCacheOpenGL::GetTitleID() const {
477 return fmt::format("{:016X}", system.CurrentProcess()->GetTitleID()); 481 return fmt::format("{:016X}", title_id);
478} 482}
479 483
480} // namespace OpenGL 484} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index db2bb73bc..aef841c1d 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -21,10 +21,6 @@
21#include "video_core/engines/shader_type.h" 21#include "video_core/engines/shader_type.h"
22#include "video_core/shader/registry.h" 22#include "video_core/shader/registry.h"
23 23
24namespace Core {
25class System;
26}
27
28namespace Common::FS { 24namespace Common::FS {
29class IOFile; 25class IOFile;
30} 26}
@@ -70,9 +66,12 @@ struct ShaderDiskCachePrecompiled {
70 66
71class ShaderDiskCacheOpenGL { 67class ShaderDiskCacheOpenGL {
72public: 68public:
73 explicit ShaderDiskCacheOpenGL(Core::System& system); 69 explicit ShaderDiskCacheOpenGL();
74 ~ShaderDiskCacheOpenGL(); 70 ~ShaderDiskCacheOpenGL();
75 71
72 /// Binds a title ID for all future operations.
73 void BindTitleID(u64 title_id);
74
76 /// Loads transferable cache. If file has a old version or on failure, it deletes the file. 75 /// Loads transferable cache. If file has a old version or on failure, it deletes the file.
77 std::optional<std::vector<ShaderDiskCacheEntry>> LoadTransferable(); 76 std::optional<std::vector<ShaderDiskCacheEntry>> LoadTransferable();
78 77
@@ -157,8 +156,6 @@ private:
157 return LoadArrayFromPrecompiled(&object, 1); 156 return LoadArrayFromPrecompiled(&object, 1);
158 } 157 }
159 158
160 Core::System& system;
161
162 // Stores whole precompiled cache which will be read from or saved to the precompiled chache 159 // Stores whole precompiled cache which will be read from or saved to the precompiled chache
163 // file 160 // file
164 FileSys::VectorVfsFile precompiled_cache_virtual_file; 161 FileSys::VectorVfsFile precompiled_cache_virtual_file;
@@ -168,8 +165,11 @@ private:
168 // Stored transferable shaders 165 // Stored transferable shaders
169 std::unordered_set<u64> stored_transferable; 166 std::unordered_set<u64> stored_transferable;
170 167
168 /// Title ID to operate on
169 u64 title_id = 0;
170
171 // The cache has been loaded at boot 171 // The cache has been loaded at boot
172 bool is_usable{}; 172 bool is_usable = false;
173}; 173};
174 174
175} // namespace OpenGL 175} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index d24fad3de..6bcf831f2 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -214,10 +214,8 @@ void SetupDirtyMisc(Tables& tables) {
214 214
215} // Anonymous namespace 215} // Anonymous namespace
216 216
217StateTracker::StateTracker(Core::System& system) : system{system} {} 217StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags} {
218 218 auto& dirty = gpu.Maxwell3D().dirty;
219void StateTracker::Initialize() {
220 auto& dirty = system.GPU().Maxwell3D().dirty;
221 auto& tables = dirty.tables; 219 auto& tables = dirty.tables;
222 SetupDirtyRenderTargets(tables); 220 SetupDirtyRenderTargets(tables);
223 SetupDirtyColorMasks(tables); 221 SetupDirtyColorMasks(tables);
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 0f823288e..9d127548f 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -13,8 +13,8 @@
13#include "video_core/dirty_flags.h" 13#include "video_core/dirty_flags.h"
14#include "video_core/engines/maxwell_3d.h" 14#include "video_core/engines/maxwell_3d.h"
15 15
16namespace Core { 16namespace Tegra {
17class System; 17class GPU;
18} 18}
19 19
20namespace OpenGL { 20namespace OpenGL {
@@ -90,9 +90,7 @@ static_assert(Last <= std::numeric_limits<u8>::max());
90 90
91class StateTracker { 91class StateTracker {
92public: 92public:
93 explicit StateTracker(Core::System& system); 93 explicit StateTracker(Tegra::GPU& gpu);
94
95 void Initialize();
96 94
97 void BindIndexBuffer(GLuint new_index_buffer) { 95 void BindIndexBuffer(GLuint new_index_buffer) {
98 if (index_buffer == new_index_buffer) { 96 if (index_buffer == new_index_buffer) {
@@ -103,7 +101,6 @@ public:
103 } 101 }
104 102
105 void NotifyScreenDrawVertexArray() { 103 void NotifyScreenDrawVertexArray() {
106 auto& flags = system.GPU().Maxwell3D().dirty.flags;
107 flags[OpenGL::Dirty::VertexFormats] = true; 104 flags[OpenGL::Dirty::VertexFormats] = true;
108 flags[OpenGL::Dirty::VertexFormat0 + 0] = true; 105 flags[OpenGL::Dirty::VertexFormat0 + 0] = true;
109 flags[OpenGL::Dirty::VertexFormat0 + 1] = true; 106 flags[OpenGL::Dirty::VertexFormat0 + 1] = true;
@@ -117,98 +114,81 @@ public:
117 } 114 }
118 115
119 void NotifyPolygonModes() { 116 void NotifyPolygonModes() {
120 auto& flags = system.GPU().Maxwell3D().dirty.flags;
121 flags[OpenGL::Dirty::PolygonModes] = true; 117 flags[OpenGL::Dirty::PolygonModes] = true;
122 flags[OpenGL::Dirty::PolygonModeFront] = true; 118 flags[OpenGL::Dirty::PolygonModeFront] = true;
123 flags[OpenGL::Dirty::PolygonModeBack] = true; 119 flags[OpenGL::Dirty::PolygonModeBack] = true;
124 } 120 }
125 121
126 void NotifyViewport0() { 122 void NotifyViewport0() {
127 auto& flags = system.GPU().Maxwell3D().dirty.flags;
128 flags[OpenGL::Dirty::Viewports] = true; 123 flags[OpenGL::Dirty::Viewports] = true;
129 flags[OpenGL::Dirty::Viewport0] = true; 124 flags[OpenGL::Dirty::Viewport0] = true;
130 } 125 }
131 126
132 void NotifyScissor0() { 127 void NotifyScissor0() {
133 auto& flags = system.GPU().Maxwell3D().dirty.flags;
134 flags[OpenGL::Dirty::Scissors] = true; 128 flags[OpenGL::Dirty::Scissors] = true;
135 flags[OpenGL::Dirty::Scissor0] = true; 129 flags[OpenGL::Dirty::Scissor0] = true;
136 } 130 }
137 131
138 void NotifyColorMask0() { 132 void NotifyColorMask0() {
139 auto& flags = system.GPU().Maxwell3D().dirty.flags;
140 flags[OpenGL::Dirty::ColorMasks] = true; 133 flags[OpenGL::Dirty::ColorMasks] = true;
141 flags[OpenGL::Dirty::ColorMask0] = true; 134 flags[OpenGL::Dirty::ColorMask0] = true;
142 } 135 }
143 136
144 void NotifyBlend0() { 137 void NotifyBlend0() {
145 auto& flags = system.GPU().Maxwell3D().dirty.flags;
146 flags[OpenGL::Dirty::BlendStates] = true; 138 flags[OpenGL::Dirty::BlendStates] = true;
147 flags[OpenGL::Dirty::BlendState0] = true; 139 flags[OpenGL::Dirty::BlendState0] = true;
148 } 140 }
149 141
150 void NotifyFramebuffer() { 142 void NotifyFramebuffer() {
151 auto& flags = system.GPU().Maxwell3D().dirty.flags;
152 flags[VideoCommon::Dirty::RenderTargets] = true; 143 flags[VideoCommon::Dirty::RenderTargets] = true;
153 } 144 }
154 145
155 void NotifyFrontFace() { 146 void NotifyFrontFace() {
156 auto& flags = system.GPU().Maxwell3D().dirty.flags;
157 flags[OpenGL::Dirty::FrontFace] = true; 147 flags[OpenGL::Dirty::FrontFace] = true;
158 } 148 }
159 149
160 void NotifyCullTest() { 150 void NotifyCullTest() {
161 auto& flags = system.GPU().Maxwell3D().dirty.flags;
162 flags[OpenGL::Dirty::CullTest] = true; 151 flags[OpenGL::Dirty::CullTest] = true;
163 } 152 }
164 153
165 void NotifyDepthMask() { 154 void NotifyDepthMask() {
166 auto& flags = system.GPU().Maxwell3D().dirty.flags;
167 flags[OpenGL::Dirty::DepthMask] = true; 155 flags[OpenGL::Dirty::DepthMask] = true;
168 } 156 }
169 157
170 void NotifyDepthTest() { 158 void NotifyDepthTest() {
171 auto& flags = system.GPU().Maxwell3D().dirty.flags;
172 flags[OpenGL::Dirty::DepthTest] = true; 159 flags[OpenGL::Dirty::DepthTest] = true;
173 } 160 }
174 161
175 void NotifyStencilTest() { 162 void NotifyStencilTest() {
176 auto& flags = system.GPU().Maxwell3D().dirty.flags;
177 flags[OpenGL::Dirty::StencilTest] = true; 163 flags[OpenGL::Dirty::StencilTest] = true;
178 } 164 }
179 165
180 void NotifyPolygonOffset() { 166 void NotifyPolygonOffset() {
181 auto& flags = system.GPU().Maxwell3D().dirty.flags;
182 flags[OpenGL::Dirty::PolygonOffset] = true; 167 flags[OpenGL::Dirty::PolygonOffset] = true;
183 } 168 }
184 169
185 void NotifyRasterizeEnable() { 170 void NotifyRasterizeEnable() {
186 auto& flags = system.GPU().Maxwell3D().dirty.flags;
187 flags[OpenGL::Dirty::RasterizeEnable] = true; 171 flags[OpenGL::Dirty::RasterizeEnable] = true;
188 } 172 }
189 173
190 void NotifyFramebufferSRGB() { 174 void NotifyFramebufferSRGB() {
191 auto& flags = system.GPU().Maxwell3D().dirty.flags;
192 flags[OpenGL::Dirty::FramebufferSRGB] = true; 175 flags[OpenGL::Dirty::FramebufferSRGB] = true;
193 } 176 }
194 177
195 void NotifyLogicOp() { 178 void NotifyLogicOp() {
196 auto& flags = system.GPU().Maxwell3D().dirty.flags;
197 flags[OpenGL::Dirty::LogicOp] = true; 179 flags[OpenGL::Dirty::LogicOp] = true;
198 } 180 }
199 181
200 void NotifyClipControl() { 182 void NotifyClipControl() {
201 auto& flags = system.GPU().Maxwell3D().dirty.flags;
202 flags[OpenGL::Dirty::ClipControl] = true; 183 flags[OpenGL::Dirty::ClipControl] = true;
203 } 184 }
204 185
205 void NotifyAlphaTest() { 186 void NotifyAlphaTest() {
206 auto& flags = system.GPU().Maxwell3D().dirty.flags;
207 flags[OpenGL::Dirty::AlphaTest] = true; 187 flags[OpenGL::Dirty::AlphaTest] = true;
208 } 188 }
209 189
210private: 190private:
211 Core::System& system; 191 Tegra::Engines::Maxwell3D::DirtyState::Flags& flags;
212 192
213 GLuint index_buffer = 0; 193 GLuint index_buffer = 0;
214}; 194};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index f403f388a..a863ef218 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -532,10 +532,12 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
532 return texture_view; 532 return texture_view;
533} 533}
534 534
535TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system, 535TextureCacheOpenGL::TextureCacheOpenGL(VideoCore::RasterizerInterface& rasterizer,
536 VideoCore::RasterizerInterface& rasterizer, 536 Tegra::Engines::Maxwell3D& maxwell3d,
537 const Device& device, StateTracker& state_tracker) 537 Tegra::MemoryManager& gpu_memory, const Device& device,
538 : TextureCacheBase{system, rasterizer, device.HasASTC()}, state_tracker{state_tracker} { 538 StateTracker& state_tracker_)
539 : TextureCacheBase{rasterizer, maxwell3d, gpu_memory, device.HasASTC()}, state_tracker{
540 state_tracker_} {
539 src_framebuffer.Create(); 541 src_framebuffer.Create();
540 dst_framebuffer.Create(); 542 dst_framebuffer.Create();
541} 543}
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index de8f18489..7787134fc 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -129,8 +129,10 @@ private:
129 129
130class TextureCacheOpenGL final : public TextureCacheBase { 130class TextureCacheOpenGL final : public TextureCacheBase {
131public: 131public:
132 explicit TextureCacheOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 132 explicit TextureCacheOpenGL(VideoCore::RasterizerInterface& rasterizer,
133 const Device& device, StateTracker& state_tracker); 133 Tegra::Engines::Maxwell3D& maxwell3d,
134 Tegra::MemoryManager& gpu_memory, const Device& device,
135 StateTracker& state_tracker);
134 ~TextureCacheOpenGL(); 136 ~TextureCacheOpenGL();
135 137
136protected: 138protected:
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index b759c2dba..a4c5b8f74 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -275,11 +275,13 @@ public:
275 } 275 }
276}; 276};
277 277
278RendererOpenGL::RendererOpenGL(Core::System& system_, Core::Frontend::EmuWindow& emu_window_, 278RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
279 Tegra::GPU& gpu_, 279 Core::Frontend::EmuWindow& emu_window_,
280 std::unique_ptr<Core::Frontend::GraphicsContext> context_) 280 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
281 : RendererBase{emu_window_, std::move(context_)}, system{system_}, 281 std::unique_ptr<Core::Frontend::GraphicsContext> context)
282 emu_window{emu_window_}, gpu{gpu_}, program_manager{device}, has_debug_tool{HasDebugTool()} {} 282 : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_},
283 emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device},
284 has_debug_tool{HasDebugTool()} {}
283 285
284RendererOpenGL::~RendererOpenGL() = default; 286RendererOpenGL::~RendererOpenGL() = default;
285 287
@@ -386,7 +388,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
386 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; 388 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
387 const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)}; 389 const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)};
388 const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel}; 390 const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel};
389 u8* const host_ptr{system.Memory().GetPointer(framebuffer_addr)}; 391 u8* const host_ptr{cpu_memory.GetPointer(framebuffer_addr)};
390 rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes); 392 rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes);
391 393
392 // TODO(Rodrigo): Read this from HLE 394 // TODO(Rodrigo): Read this from HLE
@@ -471,7 +473,6 @@ void RendererOpenGL::AddTelemetryFields() {
471 LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor); 473 LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
472 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); 474 LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
473 475
474 auto& telemetry_session = system.TelemetrySession();
475 constexpr auto user_system = Common::Telemetry::FieldType::UserSystem; 476 constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
476 telemetry_session.AddField(user_system, "GPU_Vendor", gpu_vendor); 477 telemetry_session.AddField(user_system, "GPU_Vendor", gpu_vendor);
477 telemetry_session.AddField(user_system, "GPU_Model", gpu_model); 478 telemetry_session.AddField(user_system, "GPU_Model", gpu_model);
@@ -482,8 +483,8 @@ void RendererOpenGL::CreateRasterizer() {
482 if (rasterizer) { 483 if (rasterizer) {
483 return; 484 return;
484 } 485 }
485 rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, device, screen_info, 486 rasterizer = std::make_unique<RasterizerOpenGL>(emu_window, gpu, cpu_memory, device,
486 program_manager, state_tracker); 487 screen_info, program_manager, state_tracker);
487} 488}
488 489
489void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, 490void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 52ea76b7d..5329577fb 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -16,16 +16,25 @@
16 16
17namespace Core { 17namespace Core {
18class System; 18class System;
19} 19class TelemetrySession;
20} // namespace Core
20 21
21namespace Core::Frontend { 22namespace Core::Frontend {
22class EmuWindow; 23class EmuWindow;
23} 24}
24 25
26namespace Core::Memory {
27class Memory;
28}
29
25namespace Layout { 30namespace Layout {
26struct FramebufferLayout; 31struct FramebufferLayout;
27} 32}
28 33
34namespace Tegra {
35class GPU;
36}
37
29namespace OpenGL { 38namespace OpenGL {
30 39
31/// Structure used for storing information about the textures for the Switch screen 40/// Structure used for storing information about the textures for the Switch screen
@@ -56,7 +65,8 @@ class FrameMailbox;
56 65
57class RendererOpenGL final : public VideoCore::RendererBase { 66class RendererOpenGL final : public VideoCore::RendererBase {
58public: 67public:
59 explicit RendererOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, 68 explicit RendererOpenGL(Core::TelemetrySession& telemetry_session,
69 Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory,
60 Tegra::GPU& gpu, 70 Tegra::GPU& gpu,
61 std::unique_ptr<Core::Frontend::GraphicsContext> context); 71 std::unique_ptr<Core::Frontend::GraphicsContext> context);
62 ~RendererOpenGL() override; 72 ~RendererOpenGL() override;
@@ -94,12 +104,13 @@ private:
94 104
95 bool Present(int timeout_ms); 105 bool Present(int timeout_ms);
96 106
97 Core::System& system; 107 Core::TelemetrySession& telemetry_session;
98 Core::Frontend::EmuWindow& emu_window; 108 Core::Frontend::EmuWindow& emu_window;
109 Core::Memory::Memory& cpu_memory;
99 Tegra::GPU& gpu; 110 Tegra::GPU& gpu;
100 const Device device;
101 111
102 StateTracker state_tracker{system}; 112 const Device device;
113 StateTracker state_tracker{gpu};
103 114
104 // OpenGL object IDs 115 // OpenGL object IDs
105 OGLBuffer vertex_buffer; 116 OGLBuffer vertex_buffer;
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index ae46e0444..0e4583986 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -86,7 +86,7 @@ Common::DynamicLibrary OpenVulkanLibrary() {
86 if (!library.Open(filename.c_str())) { 86 if (!library.Open(filename.c_str())) {
87 // Android devices may not have libvulkan.so.1, only libvulkan.so. 87 // Android devices may not have libvulkan.so.1, only libvulkan.so.
88 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); 88 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
89 library.Open(filename.c_str()); 89 (void)library.Open(filename.c_str());
90 } 90 }
91#endif 91#endif
92 return library; 92 return library;
@@ -237,10 +237,12 @@ std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_ext
237 237
238} // Anonymous namespace 238} // Anonymous namespace
239 239
240RendererVulkan::RendererVulkan(Core::System& system_, Core::Frontend::EmuWindow& emu_window, 240RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
241 Tegra::GPU& gpu_, 241 Core::Frontend::EmuWindow& emu_window,
242 Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
242 std::unique_ptr<Core::Frontend::GraphicsContext> context) 243 std::unique_ptr<Core::Frontend::GraphicsContext> context)
243 : RendererBase{emu_window, std::move(context)}, system{system_}, gpu{gpu_} {} 244 : RendererBase{emu_window, std::move(context)}, telemetry_session{telemetry_session_},
245 cpu_memory{cpu_memory_}, gpu{gpu_} {}
244 246
245RendererVulkan::~RendererVulkan() { 247RendererVulkan::~RendererVulkan() {
246 ShutDown(); 248 ShutDown();
@@ -304,15 +306,15 @@ bool RendererVulkan::Init() {
304 swapchain = std::make_unique<VKSwapchain>(*surface, *device); 306 swapchain = std::make_unique<VKSwapchain>(*surface, *device);
305 swapchain->Create(framebuffer.width, framebuffer.height, false); 307 swapchain->Create(framebuffer.width, framebuffer.height, false);
306 308
307 state_tracker = std::make_unique<StateTracker>(system); 309 state_tracker = std::make_unique<StateTracker>(gpu);
308 310
309 scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker); 311 scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker);
310 312
311 rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device, 313 rasterizer = std::make_unique<RasterizerVulkan>(
312 *resource_manager, *memory_manager, 314 render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, *device,
313 *state_tracker, *scheduler); 315 *resource_manager, *memory_manager, *state_tracker, *scheduler);
314 316
315 blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device, 317 blit_screen = std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
316 *resource_manager, *memory_manager, *swapchain, 318 *resource_manager, *memory_manager, *swapchain,
317 *scheduler, screen_info); 319 *scheduler, screen_info);
318 320
@@ -440,8 +442,7 @@ void RendererVulkan::Report() const {
440 LOG_INFO(Render_Vulkan, "Device: {}", model_name); 442 LOG_INFO(Render_Vulkan, "Device: {}", model_name);
441 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); 443 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
442 444
443 auto& telemetry_session = system.TelemetrySession(); 445 static constexpr auto field = Common::Telemetry::FieldType::UserSystem;
444 constexpr auto field = Common::Telemetry::FieldType::UserSystem;
445 telemetry_session.AddField(field, "GPU_Vendor", vendor_name); 446 telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
446 telemetry_session.AddField(field, "GPU_Model", model_name); 447 telemetry_session.AddField(field, "GPU_Model", model_name);
447 telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name); 448 telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 13debbbc0..ddff77942 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -14,7 +14,15 @@
14#include "video_core/renderer_vulkan/wrapper.h" 14#include "video_core/renderer_vulkan/wrapper.h"
15 15
16namespace Core { 16namespace Core {
17class System; 17class TelemetrySession;
18}
19
20namespace Core::Memory {
21class Memory;
22}
23
24namespace Tegra {
25class GPU;
18} 26}
19 27
20namespace Vulkan { 28namespace Vulkan {
@@ -38,7 +46,8 @@ struct VKScreenInfo {
38 46
39class RendererVulkan final : public VideoCore::RendererBase { 47class RendererVulkan final : public VideoCore::RendererBase {
40public: 48public:
41 explicit RendererVulkan(Core::System& system, Core::Frontend::EmuWindow& emu_window, 49 explicit RendererVulkan(Core::TelemetrySession& telemtry_session,
50 Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory,
42 Tegra::GPU& gpu, 51 Tegra::GPU& gpu,
43 std::unique_ptr<Core::Frontend::GraphicsContext> context); 52 std::unique_ptr<Core::Frontend::GraphicsContext> context);
44 ~RendererVulkan() override; 53 ~RendererVulkan() override;
@@ -59,7 +68,8 @@ private:
59 68
60 void Report() const; 69 void Report() const;
61 70
62 Core::System& system; 71 Core::TelemetrySession& telemetry_session;
72 Core::Memory::Memory& cpu_memory;
63 Tegra::GPU& gpu; 73 Tegra::GPU& gpu;
64 74
65 Common::DynamicLibrary library; 75 Common::DynamicLibrary library;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index a551e3de8..2bea7b24d 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -210,14 +210,16 @@ struct VKBlitScreen::BufferData {
210 // Unaligned image data goes here 210 // Unaligned image data goes here
211}; 211};
212 212
213VKBlitScreen::VKBlitScreen(Core::System& system, Core::Frontend::EmuWindow& render_window, 213VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
214 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, 214 Core::Frontend::EmuWindow& render_window_,
215 VKResourceManager& resource_manager, VKMemoryManager& memory_manager, 215 VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_,
216 VKSwapchain& swapchain, VKScheduler& scheduler, 216 VKResourceManager& resource_manager_, VKMemoryManager& memory_manager_,
217 const VKScreenInfo& screen_info) 217 VKSwapchain& swapchain_, VKScheduler& scheduler_,
218 : system{system}, render_window{render_window}, rasterizer{rasterizer}, device{device}, 218 const VKScreenInfo& screen_info_)
219 resource_manager{resource_manager}, memory_manager{memory_manager}, swapchain{swapchain}, 219 : cpu_memory{cpu_memory_}, render_window{render_window_},
220 scheduler{scheduler}, image_count{swapchain.GetImageCount()}, screen_info{screen_info} { 220 rasterizer{rasterizer_}, device{device_}, resource_manager{resource_manager_},
221 memory_manager{memory_manager_}, swapchain{swapchain_}, scheduler{scheduler_},
222 image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
221 watches.resize(image_count); 223 watches.resize(image_count);
222 std::generate(watches.begin(), watches.end(), 224 std::generate(watches.begin(), watches.end(),
223 []() { return std::make_unique<VKFenceWatch>(); }); 225 []() { return std::make_unique<VKFenceWatch>(); });
@@ -259,7 +261,7 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
259 const auto pixel_format = 261 const auto pixel_format =
260 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format); 262 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format);
261 const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset; 263 const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
262 const auto host_ptr = system.Memory().GetPointer(framebuffer_addr); 264 const auto host_ptr = cpu_memory.GetPointer(framebuffer_addr);
263 rasterizer.FlushRegion(ToCacheAddr(host_ptr), GetSizeInBytes(framebuffer)); 265 rasterizer.FlushRegion(ToCacheAddr(host_ptr), GetSizeInBytes(framebuffer));
264 266
265 // TODO(Rodrigo): Read this from HLE 267 // TODO(Rodrigo): Read this from HLE
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 243640fab..838d38f69 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -15,6 +15,10 @@ namespace Core {
15class System; 15class System;
16} 16}
17 17
18namespace Core::Memory {
19class Memory;
20}
21
18namespace Core::Frontend { 22namespace Core::Frontend {
19class EmuWindow; 23class EmuWindow;
20} 24}
@@ -39,7 +43,8 @@ class VKSwapchain;
39 43
40class VKBlitScreen final { 44class VKBlitScreen final {
41public: 45public:
42 explicit VKBlitScreen(Core::System& system, Core::Frontend::EmuWindow& render_window, 46 explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
47 Core::Frontend::EmuWindow& render_window,
43 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, 48 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
44 VKResourceManager& resource_manager, VKMemoryManager& memory_manager, 49 VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
45 VKSwapchain& swapchain, VKScheduler& scheduler, 50 VKSwapchain& swapchain, VKScheduler& scheduler,
@@ -81,7 +86,7 @@ private:
81 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, 86 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,
82 std::size_t image_index) const; 87 std::size_t image_index) const;
83 88
84 Core::System& system; 89 Core::Memory::Memory& cpu_memory;
85 Core::Frontend::EmuWindow& render_window; 90 Core::Frontend::EmuWindow& render_window;
86 VideoCore::RasterizerInterface& rasterizer; 91 VideoCore::RasterizerInterface& rasterizer;
87 const VKDevice& device; 92 const VKDevice& device;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 1d2f8b557..d9d3da9ea 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -145,14 +145,15 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst
145 }); 145 });
146} 146}
147 147
148VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, 148VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer,
149 const VKDevice& device, VKMemoryManager& memory_manager, 149 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
150 VKScheduler& scheduler, VKStagingBufferPool& staging_pool) 150 const VKDevice& device_, VKMemoryManager& memory_manager_,
151 : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, system, 151 VKScheduler& scheduler_, VKStagingBufferPool& staging_pool_)
152 CreateStreamBuffer(device, 152 : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, gpu_memory, cpu_memory,
153 scheduler)}, 153 CreateStreamBuffer(device_,
154 device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{ 154 scheduler_)},
155 staging_pool} {} 155 device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{
156 staging_pool_} {}
156 157
157VKBufferCache::~VKBufferCache() = default; 158VKBufferCache::~VKBufferCache() = default;
158 159
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 991ee451c..7fb5ceedf 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -13,10 +13,6 @@
13#include "video_core/renderer_vulkan/vk_stream_buffer.h" 13#include "video_core/renderer_vulkan/vk_stream_buffer.h"
14#include "video_core/renderer_vulkan/wrapper.h" 14#include "video_core/renderer_vulkan/wrapper.h"
15 15
16namespace Core {
17class System;
18}
19
20namespace Vulkan { 16namespace Vulkan {
21 17
22class VKDevice; 18class VKDevice;
@@ -53,7 +49,8 @@ private:
53 49
54class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { 50class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> {
55public: 51public:
56 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, 52 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer,
53 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
57 const VKDevice& device, VKMemoryManager& memory_manager, 54 const VKDevice& device, VKMemoryManager& memory_manager,
58 VKScheduler& scheduler, VKStagingBufferPool& staging_pool); 55 VKScheduler& scheduler, VKStagingBufferPool& staging_pool);
59 ~VKBufferCache(); 56 ~VKBufferCache();
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index d7f65d435..55a8348fc 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -71,12 +71,12 @@ bool InnerFence::IsEventSignalled() const {
71 } 71 }
72} 72}
73 73
74VKFenceManager::VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 74VKFenceManager::VKFenceManager(VideoCore::RasterizerInterface& rasterizer, Tegra::GPU& gpu,
75 const VKDevice& device, VKScheduler& scheduler, 75 Tegra::MemoryManager& memory_manager, VKTextureCache& texture_cache,
76 VKTextureCache& texture_cache, VKBufferCache& buffer_cache, 76 VKBufferCache& buffer_cache, VKQueryCache& query_cache,
77 VKQueryCache& query_cache) 77 const VKDevice& device_, VKScheduler& scheduler_)
78 : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache), 78 : GenericFenceManager(rasterizer, gpu, texture_cache, buffer_cache, query_cache),
79 device{device}, scheduler{scheduler} {} 79 device{device_}, scheduler{scheduler_} {}
80 80
81Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) { 81Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) {
82 return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed); 82 return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed);
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
index 043fe7947..1547d6d30 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -55,10 +55,10 @@ using GenericFenceManager =
55 55
56class VKFenceManager final : public GenericFenceManager { 56class VKFenceManager final : public GenericFenceManager {
57public: 57public:
58 explicit VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 58 explicit VKFenceManager(VideoCore::RasterizerInterface& rasterizer, Tegra::GPU& gpu,
59 const VKDevice& device, VKScheduler& scheduler, 59 Tegra::MemoryManager& memory_manager, VKTextureCache& texture_cache,
60 VKTextureCache& texture_cache, VKBufferCache& buffer_cache, 60 VKBufferCache& buffer_cache, VKQueryCache& query_cache,
61 VKQueryCache& query_cache); 61 const VKDevice& device, VKScheduler& scheduler);
62 62
63protected: 63protected:
64 Fence CreateFence(u32 value, bool is_stubbed) override; 64 Fence CreateFence(u32 value, bool is_stubbed) override;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index cfdcdd6ab..5c038f4bc 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -135,64 +135,56 @@ bool ComputePipelineCacheKey::operator==(const ComputePipelineCacheKey& rhs) con
135 return std::memcmp(&rhs, this, sizeof *this) == 0; 135 return std::memcmp(&rhs, this, sizeof *this) == 0;
136} 136}
137 137
138Shader::Shader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, 138Shader::Shader(Tegra::Engines::ConstBufferEngineInterface& engine, Tegra::Engines::ShaderType stage,
139 VideoCommon::Shader::ProgramCode program_code, u32 main_offset) 139 GPUVAddr gpu_addr_, VAddr cpu_addr, VideoCommon::Shader::ProgramCode program_code_,
140 : gpu_addr{gpu_addr}, program_code{std::move(program_code)}, 140 u32 main_offset)
141 registry{stage, GetEngine(system, stage)}, shader_ir{this->program_code, main_offset, 141 : gpu_addr(gpu_addr_), program_code(std::move(program_code_)), registry(stage, engine),
142 compiler_settings, registry}, 142 shader_ir(program_code, main_offset, compiler_settings, registry),
143 entries{GenerateShaderEntries(shader_ir)} {} 143 entries(GenerateShaderEntries(shader_ir)) {}
144 144
145Shader::~Shader() = default; 145Shader::~Shader() = default;
146 146
147Tegra::Engines::ConstBufferEngineInterface& Shader::GetEngine(Core::System& system, 147VKPipelineCache::VKPipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu_,
148 Tegra::Engines::ShaderType stage) { 148 Tegra::Engines::Maxwell3D& maxwell3d_,
149 if (stage == ShaderType::Compute) { 149 Tegra::Engines::KeplerCompute& kepler_compute_,
150 return system.GPU().KeplerCompute(); 150 Tegra::MemoryManager& gpu_memory_, const VKDevice& device_,
151 } else { 151 VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_,
152 return system.GPU().Maxwell3D(); 152 VKUpdateDescriptorQueue& update_descriptor_queue_,
153 } 153 VKRenderPassCache& renderpass_cache_)
154} 154 : VideoCommon::ShaderCache<Shader>{rasterizer}, gpu{gpu_}, maxwell3d{maxwell3d_},
155 155 kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, device{device_},
156VKPipelineCache::VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer, 156 scheduler{scheduler_}, descriptor_pool{descriptor_pool_},
157 const VKDevice& device, VKScheduler& scheduler, 157 update_descriptor_queue{update_descriptor_queue_}, renderpass_cache{renderpass_cache_} {}
158 VKDescriptorPool& descriptor_pool,
159 VKUpdateDescriptorQueue& update_descriptor_queue,
160 VKRenderPassCache& renderpass_cache)
161 : VideoCommon::ShaderCache<Shader>{rasterizer}, system{system}, device{device},
162 scheduler{scheduler}, descriptor_pool{descriptor_pool},
163 update_descriptor_queue{update_descriptor_queue}, renderpass_cache{renderpass_cache} {}
164 158
165VKPipelineCache::~VKPipelineCache() = default; 159VKPipelineCache::~VKPipelineCache() = default;
166 160
167std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { 161std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
168 const auto& gpu = system.GPU().Maxwell3D();
169
170 std::array<Shader*, Maxwell::MaxShaderProgram> shaders{}; 162 std::array<Shader*, Maxwell::MaxShaderProgram> shaders{};
163
171 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 164 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
172 const auto program{static_cast<Maxwell::ShaderProgram>(index)}; 165 const auto program{static_cast<Maxwell::ShaderProgram>(index)};
173 166
174 // Skip stages that are not enabled 167 // Skip stages that are not enabled
175 if (!gpu.regs.IsShaderConfigEnabled(index)) { 168 if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
176 continue; 169 continue;
177 } 170 }
178 171
179 auto& memory_manager{system.GPU().MemoryManager()}; 172 const GPUVAddr gpu_addr{GetShaderAddress(maxwell3d, program)};
180 const GPUVAddr program_addr{GetShaderAddress(system, program)}; 173 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
181 const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
182 ASSERT(cpu_addr); 174 ASSERT(cpu_addr);
183 175
184 Shader* result = cpu_addr ? TryGet(*cpu_addr) : null_shader.get(); 176 Shader* result = cpu_addr ? TryGet(*cpu_addr) : null_shader.get();
185 if (!result) { 177 if (!result) {
186 const auto host_ptr{memory_manager.GetPointer(program_addr)}; 178 const u8* const host_ptr{gpu_memory.GetPointer(gpu_addr)};
187 179
188 // No shader found - create a new one 180 // No shader found - create a new one
189 constexpr u32 stage_offset = STAGE_MAIN_OFFSET; 181 static constexpr u32 stage_offset = STAGE_MAIN_OFFSET;
190 const auto stage = static_cast<ShaderType>(index == 0 ? 0 : index - 1); 182 const auto stage = static_cast<ShaderType>(index == 0 ? 0 : index - 1);
191 ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, false); 183 ProgramCode code = GetShaderCode(gpu_memory, gpu_addr, host_ptr, false);
192 const std::size_t size_in_bytes = code.size() * sizeof(u64); 184 const std::size_t size_in_bytes = code.size() * sizeof(u64);
193 185
194 auto shader = std::make_unique<Shader>(system, stage, program_addr, std::move(code), 186 auto shader = std::make_unique<Shader>(maxwell3d, stage, gpu_addr, *cpu_addr,
195 stage_offset); 187 std::move(code), stage_offset);
196 result = shader.get(); 188 result = shader.get();
197 189
198 if (cpu_addr) { 190 if (cpu_addr) {
@@ -215,11 +207,11 @@ VKGraphicsPipeline* VKPipelineCache::GetGraphicsPipeline(
215 } 207 }
216 last_graphics_key = key; 208 last_graphics_key = key;
217 209
218 if (device.UseAsynchronousShaders() && async_shaders.IsShaderAsync(system.GPU())) { 210 if (device.UseAsynchronousShaders() && async_shaders.IsShaderAsync(gpu)) {
219 std::unique_lock lock{pipeline_cache}; 211 std::unique_lock lock{pipeline_cache};
220 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key); 212 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
221 if (is_cache_miss) { 213 if (is_cache_miss) {
222 system.GPU().ShaderNotify().MarkSharderBuilding(); 214 gpu.ShaderNotify().MarkSharderBuilding();
223 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash()); 215 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
224 const auto [program, bindings] = DecompileShaders(key.fixed_state); 216 const auto [program, bindings] = DecompileShaders(key.fixed_state);
225 async_shaders.QueueVulkanShader(this, device, scheduler, descriptor_pool, 217 async_shaders.QueueVulkanShader(this, device, scheduler, descriptor_pool,
@@ -233,13 +225,13 @@ VKGraphicsPipeline* VKPipelineCache::GetGraphicsPipeline(
233 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key); 225 const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
234 auto& entry = pair->second; 226 auto& entry = pair->second;
235 if (is_cache_miss) { 227 if (is_cache_miss) {
236 system.GPU().ShaderNotify().MarkSharderBuilding(); 228 gpu.ShaderNotify().MarkSharderBuilding();
237 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash()); 229 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
238 const auto [program, bindings] = DecompileShaders(key.fixed_state); 230 const auto [program, bindings] = DecompileShaders(key.fixed_state);
239 entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool, 231 entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool,
240 update_descriptor_queue, renderpass_cache, key, 232 update_descriptor_queue, renderpass_cache, key,
241 bindings, program); 233 bindings, program);
242 system.GPU().ShaderNotify().MarkShaderComplete(); 234 gpu.ShaderNotify().MarkShaderComplete();
243 } 235 }
244 last_graphics_pipeline = entry.get(); 236 last_graphics_pipeline = entry.get();
245 return last_graphics_pipeline; 237 return last_graphics_pipeline;
@@ -255,22 +247,21 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
255 } 247 }
256 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash()); 248 LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
257 249
258 auto& memory_manager = system.GPU().MemoryManager(); 250 const GPUVAddr gpu_addr = key.shader;
259 const auto program_addr = key.shader;
260 251
261 const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); 252 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
262 ASSERT(cpu_addr); 253 ASSERT(cpu_addr);
263 254
264 Shader* shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get(); 255 Shader* shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get();
265 if (!shader) { 256 if (!shader) {
266 // No shader found - create a new one 257 // No shader found - create a new one
267 const auto host_ptr = memory_manager.GetPointer(program_addr); 258 const auto host_ptr = gpu_memory.GetPointer(gpu_addr);
268 259
269 ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, true); 260 ProgramCode code = GetShaderCode(gpu_memory, gpu_addr, host_ptr, true);
270 const std::size_t size_in_bytes = code.size() * sizeof(u64); 261 const std::size_t size_in_bytes = code.size() * sizeof(u64);
271 262
272 auto shader_info = std::make_unique<Shader>(system, ShaderType::Compute, program_addr, 263 auto shader_info = std::make_unique<Shader>(kepler_compute, ShaderType::Compute, gpu_addr,
273 std::move(code), KERNEL_MAIN_OFFSET); 264 *cpu_addr, std::move(code), KERNEL_MAIN_OFFSET);
274 shader = shader_info.get(); 265 shader = shader_info.get();
275 266
276 if (cpu_addr) { 267 if (cpu_addr) {
@@ -298,7 +289,7 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
298} 289}
299 290
300void VKPipelineCache::EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline) { 291void VKPipelineCache::EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline) {
301 system.GPU().ShaderNotify().MarkShaderComplete(); 292 gpu.ShaderNotify().MarkShaderComplete();
302 std::unique_lock lock{pipeline_cache}; 293 std::unique_lock lock{pipeline_cache};
303 graphics_cache.at(pipeline->GetCacheKey()) = std::move(pipeline); 294 graphics_cache.at(pipeline->GetCacheKey()) = std::move(pipeline);
304} 295}
@@ -339,9 +330,6 @@ void VKPipelineCache::OnShaderRemoval(Shader* shader) {
339 330
340std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> 331std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>>
341VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) { 332VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
342 auto& memory_manager = system.GPU().MemoryManager();
343 const auto& gpu = system.GPU().Maxwell3D();
344
345 Specialization specialization; 333 Specialization specialization;
346 if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points || 334 if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points ||
347 device.IsExtExtendedDynamicStateSupported()) { 335 device.IsExtExtendedDynamicStateSupported()) {
@@ -364,12 +352,12 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
364 const auto program_enum = static_cast<Maxwell::ShaderProgram>(index); 352 const auto program_enum = static_cast<Maxwell::ShaderProgram>(index);
365 353
366 // Skip stages that are not enabled 354 // Skip stages that are not enabled
367 if (!gpu.regs.IsShaderConfigEnabled(index)) { 355 if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
368 continue; 356 continue;
369 } 357 }
370 358
371 const GPUVAddr gpu_addr = GetShaderAddress(system, program_enum); 359 const GPUVAddr gpu_addr = GetShaderAddress(maxwell3d, program_enum);
372 const std::optional<VAddr> cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr); 360 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
373 Shader* const shader = cpu_addr ? TryGet(*cpu_addr) : null_shader.get(); 361 Shader* const shader = cpu_addr ? TryGet(*cpu_addr) : null_shader.get();
374 362
375 const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5 363 const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index c04829e77..1a31fd9f6 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -85,7 +85,8 @@ namespace Vulkan {
85 85
86class Shader { 86class Shader {
87public: 87public:
88 explicit Shader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, 88 explicit Shader(Tegra::Engines::ConstBufferEngineInterface& engine,
89 Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, VAddr cpu_addr,
89 VideoCommon::Shader::ProgramCode program_code, u32 main_offset); 90 VideoCommon::Shader::ProgramCode program_code, u32 main_offset);
90 ~Shader(); 91 ~Shader();
91 92
@@ -97,22 +98,19 @@ public:
97 return shader_ir; 98 return shader_ir;
98 } 99 }
99 100
100 const VideoCommon::Shader::Registry& GetRegistry() const {
101 return registry;
102 }
103
104 const VideoCommon::Shader::ShaderIR& GetIR() const { 101 const VideoCommon::Shader::ShaderIR& GetIR() const {
105 return shader_ir; 102 return shader_ir;
106 } 103 }
107 104
105 const VideoCommon::Shader::Registry& GetRegistry() const {
106 return registry;
107 }
108
108 const ShaderEntries& GetEntries() const { 109 const ShaderEntries& GetEntries() const {
109 return entries; 110 return entries;
110 } 111 }
111 112
112private: 113private:
113 static Tegra::Engines::ConstBufferEngineInterface& GetEngine(Core::System& system,
114 Tegra::Engines::ShaderType stage);
115
116 GPUVAddr gpu_addr{}; 114 GPUVAddr gpu_addr{};
117 VideoCommon::Shader::ProgramCode program_code; 115 VideoCommon::Shader::ProgramCode program_code;
118 VideoCommon::Shader::Registry registry; 116 VideoCommon::Shader::Registry registry;
@@ -122,9 +120,11 @@ private:
122 120
123class VKPipelineCache final : public VideoCommon::ShaderCache<Shader> { 121class VKPipelineCache final : public VideoCommon::ShaderCache<Shader> {
124public: 122public:
125 explicit VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer, 123 explicit VKPipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu,
126 const VKDevice& device, VKScheduler& scheduler, 124 Tegra::Engines::Maxwell3D& maxwell3d,
127 VKDescriptorPool& descriptor_pool, 125 Tegra::Engines::KeplerCompute& kepler_compute,
126 Tegra::MemoryManager& gpu_memory, const VKDevice& device,
127 VKScheduler& scheduler, VKDescriptorPool& descriptor_pool,
128 VKUpdateDescriptorQueue& update_descriptor_queue, 128 VKUpdateDescriptorQueue& update_descriptor_queue,
129 VKRenderPassCache& renderpass_cache); 129 VKRenderPassCache& renderpass_cache);
130 ~VKPipelineCache() override; 130 ~VKPipelineCache() override;
@@ -145,7 +145,11 @@ private:
145 std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders( 145 std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders(
146 const FixedPipelineState& fixed_state); 146 const FixedPipelineState& fixed_state);
147 147
148 Core::System& system; 148 Tegra::GPU& gpu;
149 Tegra::Engines::Maxwell3D& maxwell3d;
150 Tegra::Engines::KeplerCompute& kepler_compute;
151 Tegra::MemoryManager& gpu_memory;
152
149 const VKDevice& device; 153 const VKDevice& device;
150 VKScheduler& scheduler; 154 VKScheduler& scheduler;
151 VKDescriptorPool& descriptor_pool; 155 VKDescriptorPool& descriptor_pool;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 6cd63d090..5a97c959d 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -68,10 +68,11 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
68 usage[pool_index * GROW_STEP + static_cast<std::ptrdiff_t>(query.second)] = false; 68 usage[pool_index * GROW_STEP + static_cast<std::ptrdiff_t>(query.second)] = false;
69} 69}
70 70
71VKQueryCache::VKQueryCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 71VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
72 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
72 const VKDevice& device, VKScheduler& scheduler) 73 const VKDevice& device, VKScheduler& scheduler)
73 : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter, 74 : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
74 QueryPool>{system, rasterizer}, 75 QueryPool>{rasterizer, maxwell3d, gpu_memory},
75 device{device}, scheduler{scheduler} { 76 device{device}, scheduler{scheduler} {
76 for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) { 77 for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) {
77 query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i)); 78 query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i));
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h
index 40119e6d3..9be996e55 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.h
+++ b/src/video_core/renderer_vulkan/vk_query_cache.h
@@ -56,7 +56,8 @@ class VKQueryCache final
56 : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter, 56 : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
57 QueryPool> { 57 QueryPool> {
58public: 58public:
59 explicit VKQueryCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 59 explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
60 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
60 const VKDevice& device, VKScheduler& scheduler); 61 const VKDevice& device, VKScheduler& scheduler);
61 ~VKQueryCache(); 62 ~VKQueryCache();
62 63
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index ff1b52eab..bafebe294 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -381,28 +381,30 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const {
381 } 381 }
382} 382}
383 383
384RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& renderer, 384RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_,
385 VKScreenInfo& screen_info, const VKDevice& device, 385 Tegra::MemoryManager& gpu_memory_,
386 VKResourceManager& resource_manager, 386 Core::Memory::Memory& cpu_memory, VKScreenInfo& screen_info_,
387 VKMemoryManager& memory_manager, StateTracker& state_tracker, 387 const VKDevice& device_, VKResourceManager& resource_manager_,
388 VKScheduler& scheduler) 388 VKMemoryManager& memory_manager_, StateTracker& state_tracker_,
389 : RasterizerAccelerated{system.Memory()}, system{system}, render_window{renderer}, 389 VKScheduler& scheduler_)
390 screen_info{screen_info}, device{device}, resource_manager{resource_manager}, 390 : RasterizerAccelerated(cpu_memory), gpu(gpu_), gpu_memory(gpu_memory_),
391 memory_manager{memory_manager}, state_tracker{state_tracker}, scheduler{scheduler}, 391 maxwell3d(gpu.Maxwell3D()), kepler_compute(gpu.KeplerCompute()), screen_info(screen_info_),
392 device(device_), resource_manager(resource_manager_), memory_manager(memory_manager_),
393 state_tracker(state_tracker_), scheduler(scheduler_),
392 staging_pool(device, memory_manager, scheduler), descriptor_pool(device), 394 staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
393 update_descriptor_queue(device, scheduler), renderpass_cache(device), 395 update_descriptor_queue(device, scheduler), renderpass_cache(device),
394 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 396 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
395 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 397 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
396 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 398 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
397 texture_cache(system, *this, device, resource_manager, memory_manager, scheduler, 399 texture_cache(*this, maxwell3d, gpu_memory, device, resource_manager, memory_manager,
398 staging_pool), 400 scheduler, staging_pool),
399 pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue, 401 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
400 renderpass_cache), 402 descriptor_pool, update_descriptor_queue, renderpass_cache),
401 buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool), 403 buffer_cache(*this, gpu_memory, cpu_memory, device, memory_manager, scheduler, staging_pool),
402 sampler_cache(device), 404 sampler_cache(device), query_cache(*this, maxwell3d, gpu_memory, device, scheduler),
403 fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache), 405 fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device,
404 query_cache(system, *this, device, scheduler), 406 scheduler),
405 wfi_event{device.GetLogical().CreateNewEvent()}, async_shaders{renderer} { 407 wfi_event(device.GetLogical().CreateNewEvent()), async_shaders(emu_window) {
406 scheduler.SetQueryCache(query_cache); 408 scheduler.SetQueryCache(query_cache);
407 if (device.UseAsynchronousShaders()) { 409 if (device.UseAsynchronousShaders()) {
408 async_shaders.AllocateWorkers(); 410 async_shaders.AllocateWorkers();
@@ -414,15 +416,13 @@ RasterizerVulkan::~RasterizerVulkan() = default;
414void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { 416void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
415 MICROPROFILE_SCOPE(Vulkan_Drawing); 417 MICROPROFILE_SCOPE(Vulkan_Drawing);
416 418
419 SCOPE_EXIT({ gpu.TickWork(); });
417 FlushWork(); 420 FlushWork();
418 421
419 query_cache.UpdateCounters(); 422 query_cache.UpdateCounters();
420 423
421 SCOPE_EXIT({ system.GPU().TickWork(); });
422
423 const auto& gpu = system.GPU().Maxwell3D();
424 GraphicsPipelineCacheKey key; 424 GraphicsPipelineCacheKey key;
425 key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported()); 425 key.fixed_state.Fill(maxwell3d.regs, device.IsExtExtendedDynamicStateSupported());
426 426
427 buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); 427 buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
428 428
@@ -480,8 +480,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
480void RasterizerVulkan::Clear() { 480void RasterizerVulkan::Clear() {
481 MICROPROFILE_SCOPE(Vulkan_Clearing); 481 MICROPROFILE_SCOPE(Vulkan_Clearing);
482 482
483 const auto& gpu = system.GPU().Maxwell3D(); 483 if (!maxwell3d.ShouldExecute()) {
484 if (!system.GPU().Maxwell3D().ShouldExecute()) {
485 return; 484 return;
486 } 485 }
487 486
@@ -490,7 +489,7 @@ void RasterizerVulkan::Clear() {
490 489
491 query_cache.UpdateCounters(); 490 query_cache.UpdateCounters();
492 491
493 const auto& regs = gpu.regs; 492 const auto& regs = maxwell3d.regs;
494 const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || 493 const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
495 regs.clear_buffers.A; 494 regs.clear_buffers.A;
496 const bool use_depth = regs.clear_buffers.Z; 495 const bool use_depth = regs.clear_buffers.Z;
@@ -559,7 +558,7 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
559 558
560 query_cache.UpdateCounters(); 559 query_cache.UpdateCounters();
561 560
562 const auto& launch_desc = system.GPU().KeplerCompute().launch_description; 561 const auto& launch_desc = kepler_compute.launch_description;
563 auto& pipeline = pipeline_cache.GetComputePipeline({ 562 auto& pipeline = pipeline_cache.GetComputePipeline({
564 .shader = code_addr, 563 .shader = code_addr,
565 .shared_memory_size = launch_desc.shared_alloc, 564 .shared_memory_size = launch_desc.shared_alloc,
@@ -655,16 +654,14 @@ void RasterizerVulkan::SyncGuestHost() {
655} 654}
656 655
657void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) { 656void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) {
658 auto& gpu{system.GPU()};
659 if (!gpu.IsAsync()) { 657 if (!gpu.IsAsync()) {
660 gpu.MemoryManager().Write<u32>(addr, value); 658 gpu_memory.Write<u32>(addr, value);
661 return; 659 return;
662 } 660 }
663 fence_manager.SignalSemaphore(addr, value); 661 fence_manager.SignalSemaphore(addr, value);
664} 662}
665 663
666void RasterizerVulkan::SignalSyncPoint(u32 value) { 664void RasterizerVulkan::SignalSyncPoint(u32 value) {
667 auto& gpu{system.GPU()};
668 if (!gpu.IsAsync()) { 665 if (!gpu.IsAsync()) {
669 gpu.IncrementSyncPoint(value); 666 gpu.IncrementSyncPoint(value);
670 return; 667 return;
@@ -673,7 +670,6 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) {
673} 670}
674 671
675void RasterizerVulkan::ReleaseFences() { 672void RasterizerVulkan::ReleaseFences() {
676 auto& gpu{system.GPU()};
677 if (!gpu.IsAsync()) { 673 if (!gpu.IsAsync()) {
678 return; 674 return;
679 } 675 }
@@ -751,10 +747,6 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
751 return true; 747 return true;
752} 748}
753 749
754void RasterizerVulkan::SetupDirtyFlags() {
755 state_tracker.Initialize();
756}
757
758void RasterizerVulkan::FlushWork() { 750void RasterizerVulkan::FlushWork() {
759 static constexpr u32 DRAWS_TO_DISPATCH = 4096; 751 static constexpr u32 DRAWS_TO_DISPATCH = 4096;
760 752
@@ -778,10 +770,9 @@ void RasterizerVulkan::FlushWork() {
778 770
779RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments(bool is_clear) { 771RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments(bool is_clear) {
780 MICROPROFILE_SCOPE(Vulkan_RenderTargets); 772 MICROPROFILE_SCOPE(Vulkan_RenderTargets);
781 auto& maxwell3d = system.GPU().Maxwell3D();
782 auto& dirty = maxwell3d.dirty.flags;
783 auto& regs = maxwell3d.regs;
784 773
774 const auto& regs = maxwell3d.regs;
775 auto& dirty = maxwell3d.dirty.flags;
785 const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets]; 776 const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets];
786 dirty[VideoCommon::Dirty::RenderTargets] = false; 777 dirty[VideoCommon::Dirty::RenderTargets] = false;
787 778
@@ -844,7 +835,7 @@ std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers(
844 return true; 835 return true;
845 }; 836 };
846 837
847 const auto& regs = system.GPU().Maxwell3D().regs; 838 const auto& regs = maxwell3d.regs;
848 const std::size_t num_attachments = static_cast<std::size_t>(regs.rt_control.count); 839 const std::size_t num_attachments = static_cast<std::size_t>(regs.rt_control.count);
849 for (std::size_t index = 0; index < num_attachments; ++index) { 840 for (std::size_t index = 0; index < num_attachments; ++index) {
850 if (try_push(color_attachments[index])) { 841 if (try_push(color_attachments[index])) {
@@ -880,13 +871,12 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
880 bool is_instanced) { 871 bool is_instanced) {
881 MICROPROFILE_SCOPE(Vulkan_Geometry); 872 MICROPROFILE_SCOPE(Vulkan_Geometry);
882 873
883 const auto& gpu = system.GPU().Maxwell3D(); 874 const auto& regs = maxwell3d.regs;
884 const auto& regs = gpu.regs;
885 875
886 SetupVertexArrays(buffer_bindings); 876 SetupVertexArrays(buffer_bindings);
887 877
888 const u32 base_instance = regs.vb_base_instance; 878 const u32 base_instance = regs.vb_base_instance;
889 const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; 879 const u32 num_instances = is_instanced ? maxwell3d.mme_draw.instance_count : 1;
890 const u32 base_vertex = is_indexed ? regs.vb_element_base : regs.vertex_buffer.first; 880 const u32 base_vertex = is_indexed ? regs.vb_element_base : regs.vertex_buffer.first;
891 const u32 num_vertices = is_indexed ? regs.index_array.count : regs.vertex_buffer.count; 881 const u32 num_vertices = is_indexed ? regs.index_array.count : regs.vertex_buffer.count;
892 882
@@ -947,7 +937,7 @@ void RasterizerVulkan::SetupImageTransitions(
947} 937}
948 938
949void RasterizerVulkan::UpdateDynamicStates() { 939void RasterizerVulkan::UpdateDynamicStates() {
950 auto& regs = system.GPU().Maxwell3D().regs; 940 auto& regs = maxwell3d.regs;
951 UpdateViewportsState(regs); 941 UpdateViewportsState(regs);
952 UpdateScissorsState(regs); 942 UpdateScissorsState(regs);
953 UpdateDepthBias(regs); 943 UpdateDepthBias(regs);
@@ -968,7 +958,7 @@ void RasterizerVulkan::UpdateDynamicStates() {
968} 958}
969 959
970void RasterizerVulkan::BeginTransformFeedback() { 960void RasterizerVulkan::BeginTransformFeedback() {
971 const auto& regs = system.GPU().Maxwell3D().regs; 961 const auto& regs = maxwell3d.regs;
972 if (regs.tfb_enabled == 0) { 962 if (regs.tfb_enabled == 0) {
973 return; 963 return;
974 } 964 }
@@ -1000,7 +990,7 @@ void RasterizerVulkan::BeginTransformFeedback() {
1000} 990}
1001 991
1002void RasterizerVulkan::EndTransformFeedback() { 992void RasterizerVulkan::EndTransformFeedback() {
1003 const auto& regs = system.GPU().Maxwell3D().regs; 993 const auto& regs = maxwell3d.regs;
1004 if (regs.tfb_enabled == 0) { 994 if (regs.tfb_enabled == 0) {
1005 return; 995 return;
1006 } 996 }
@@ -1013,7 +1003,7 @@ void RasterizerVulkan::EndTransformFeedback() {
1013} 1003}
1014 1004
1015void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) { 1005void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
1016 const auto& regs = system.GPU().Maxwell3D().regs; 1006 const auto& regs = maxwell3d.regs;
1017 1007
1018 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 1008 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
1019 const auto& vertex_array = regs.vertex_array[index]; 1009 const auto& vertex_array = regs.vertex_array[index];
@@ -1039,7 +1029,7 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar
1039 if (params.num_vertices == 0) { 1029 if (params.num_vertices == 0) {
1040 return; 1030 return;
1041 } 1031 }
1042 const auto& regs = system.GPU().Maxwell3D().regs; 1032 const auto& regs = maxwell3d.regs;
1043 switch (regs.draw.topology) { 1033 switch (regs.draw.topology) {
1044 case Maxwell::PrimitiveTopology::Quads: { 1034 case Maxwell::PrimitiveTopology::Quads: {
1045 if (!params.is_indexed) { 1035 if (!params.is_indexed) {
@@ -1087,8 +1077,7 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar
1087 1077
1088void RasterizerVulkan::SetupGraphicsConstBuffers(const ShaderEntries& entries, std::size_t stage) { 1078void RasterizerVulkan::SetupGraphicsConstBuffers(const ShaderEntries& entries, std::size_t stage) {
1089 MICROPROFILE_SCOPE(Vulkan_ConstBuffers); 1079 MICROPROFILE_SCOPE(Vulkan_ConstBuffers);
1090 const auto& gpu = system.GPU().Maxwell3D(); 1080 const auto& shader_stage = maxwell3d.state.shader_stages[stage];
1091 const auto& shader_stage = gpu.state.shader_stages[stage];
1092 for (const auto& entry : entries.const_buffers) { 1081 for (const auto& entry : entries.const_buffers) {
1093 SetupConstBuffer(entry, shader_stage.const_buffers[entry.GetIndex()]); 1082 SetupConstBuffer(entry, shader_stage.const_buffers[entry.GetIndex()]);
1094 } 1083 }
@@ -1096,8 +1085,7 @@ void RasterizerVulkan::SetupGraphicsConstBuffers(const ShaderEntries& entries, s
1096 1085
1097void RasterizerVulkan::SetupGraphicsGlobalBuffers(const ShaderEntries& entries, std::size_t stage) { 1086void RasterizerVulkan::SetupGraphicsGlobalBuffers(const ShaderEntries& entries, std::size_t stage) {
1098 MICROPROFILE_SCOPE(Vulkan_GlobalBuffers); 1087 MICROPROFILE_SCOPE(Vulkan_GlobalBuffers);
1099 auto& gpu{system.GPU()}; 1088 const auto& cbufs{maxwell3d.state.shader_stages[stage]};
1100 const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage]};
1101 1089
1102 for (const auto& entry : entries.global_buffers) { 1090 for (const auto& entry : entries.global_buffers) {
1103 const auto addr = cbufs.const_buffers[entry.GetCbufIndex()].address + entry.GetCbufOffset(); 1091 const auto addr = cbufs.const_buffers[entry.GetCbufIndex()].address + entry.GetCbufOffset();
@@ -1107,19 +1095,17 @@ void RasterizerVulkan::SetupGraphicsGlobalBuffers(const ShaderEntries& entries,
1107 1095
1108void RasterizerVulkan::SetupGraphicsUniformTexels(const ShaderEntries& entries, std::size_t stage) { 1096void RasterizerVulkan::SetupGraphicsUniformTexels(const ShaderEntries& entries, std::size_t stage) {
1109 MICROPROFILE_SCOPE(Vulkan_Textures); 1097 MICROPROFILE_SCOPE(Vulkan_Textures);
1110 const auto& gpu = system.GPU().Maxwell3D();
1111 for (const auto& entry : entries.uniform_texels) { 1098 for (const auto& entry : entries.uniform_texels) {
1112 const auto image = GetTextureInfo(gpu, entry, stage).tic; 1099 const auto image = GetTextureInfo(maxwell3d, entry, stage).tic;
1113 SetupUniformTexels(image, entry); 1100 SetupUniformTexels(image, entry);
1114 } 1101 }
1115} 1102}
1116 1103
1117void RasterizerVulkan::SetupGraphicsTextures(const ShaderEntries& entries, std::size_t stage) { 1104void RasterizerVulkan::SetupGraphicsTextures(const ShaderEntries& entries, std::size_t stage) {
1118 MICROPROFILE_SCOPE(Vulkan_Textures); 1105 MICROPROFILE_SCOPE(Vulkan_Textures);
1119 const auto& gpu = system.GPU().Maxwell3D();
1120 for (const auto& entry : entries.samplers) { 1106 for (const auto& entry : entries.samplers) {
1121 for (std::size_t i = 0; i < entry.size; ++i) { 1107 for (std::size_t i = 0; i < entry.size; ++i) {
1122 const auto texture = GetTextureInfo(gpu, entry, stage, i); 1108 const auto texture = GetTextureInfo(maxwell3d, entry, stage, i);
1123 SetupTexture(texture, entry); 1109 SetupTexture(texture, entry);
1124 } 1110 }
1125 } 1111 }
@@ -1127,25 +1113,23 @@ void RasterizerVulkan::SetupGraphicsTextures(const ShaderEntries& entries, std::
1127 1113
1128void RasterizerVulkan::SetupGraphicsStorageTexels(const ShaderEntries& entries, std::size_t stage) { 1114void RasterizerVulkan::SetupGraphicsStorageTexels(const ShaderEntries& entries, std::size_t stage) {
1129 MICROPROFILE_SCOPE(Vulkan_Textures); 1115 MICROPROFILE_SCOPE(Vulkan_Textures);
1130 const auto& gpu = system.GPU().Maxwell3D();
1131 for (const auto& entry : entries.storage_texels) { 1116 for (const auto& entry : entries.storage_texels) {
1132 const auto image = GetTextureInfo(gpu, entry, stage).tic; 1117 const auto image = GetTextureInfo(maxwell3d, entry, stage).tic;
1133 SetupStorageTexel(image, entry); 1118 SetupStorageTexel(image, entry);
1134 } 1119 }
1135} 1120}
1136 1121
1137void RasterizerVulkan::SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage) { 1122void RasterizerVulkan::SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage) {
1138 MICROPROFILE_SCOPE(Vulkan_Images); 1123 MICROPROFILE_SCOPE(Vulkan_Images);
1139 const auto& gpu = system.GPU().Maxwell3D();
1140 for (const auto& entry : entries.images) { 1124 for (const auto& entry : entries.images) {
1141 const auto tic = GetTextureInfo(gpu, entry, stage).tic; 1125 const auto tic = GetTextureInfo(maxwell3d, entry, stage).tic;
1142 SetupImage(tic, entry); 1126 SetupImage(tic, entry);
1143 } 1127 }
1144} 1128}
1145 1129
1146void RasterizerVulkan::SetupComputeConstBuffers(const ShaderEntries& entries) { 1130void RasterizerVulkan::SetupComputeConstBuffers(const ShaderEntries& entries) {
1147 MICROPROFILE_SCOPE(Vulkan_ConstBuffers); 1131 MICROPROFILE_SCOPE(Vulkan_ConstBuffers);
1148 const auto& launch_desc = system.GPU().KeplerCompute().launch_description; 1132 const auto& launch_desc = kepler_compute.launch_description;
1149 for (const auto& entry : entries.const_buffers) { 1133 for (const auto& entry : entries.const_buffers) {
1150 const auto& config = launch_desc.const_buffer_config[entry.GetIndex()]; 1134 const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
1151 const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value(); 1135 const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
@@ -1159,7 +1143,7 @@ void RasterizerVulkan::SetupComputeConstBuffers(const ShaderEntries& entries) {
1159 1143
1160void RasterizerVulkan::SetupComputeGlobalBuffers(const ShaderEntries& entries) { 1144void RasterizerVulkan::SetupComputeGlobalBuffers(const ShaderEntries& entries) {
1161 MICROPROFILE_SCOPE(Vulkan_GlobalBuffers); 1145 MICROPROFILE_SCOPE(Vulkan_GlobalBuffers);
1162 const auto cbufs{system.GPU().KeplerCompute().launch_description.const_buffer_config}; 1146 const auto& cbufs{kepler_compute.launch_description.const_buffer_config};
1163 for (const auto& entry : entries.global_buffers) { 1147 for (const auto& entry : entries.global_buffers) {
1164 const auto addr{cbufs[entry.GetCbufIndex()].Address() + entry.GetCbufOffset()}; 1148 const auto addr{cbufs[entry.GetCbufIndex()].Address() + entry.GetCbufOffset()};
1165 SetupGlobalBuffer(entry, addr); 1149 SetupGlobalBuffer(entry, addr);
@@ -1168,19 +1152,17 @@ void RasterizerVulkan::SetupComputeGlobalBuffers(const ShaderEntries& entries) {
1168 1152
1169void RasterizerVulkan::SetupComputeUniformTexels(const ShaderEntries& entries) { 1153void RasterizerVulkan::SetupComputeUniformTexels(const ShaderEntries& entries) {
1170 MICROPROFILE_SCOPE(Vulkan_Textures); 1154 MICROPROFILE_SCOPE(Vulkan_Textures);
1171 const auto& gpu = system.GPU().KeplerCompute();
1172 for (const auto& entry : entries.uniform_texels) { 1155 for (const auto& entry : entries.uniform_texels) {
1173 const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic; 1156 const auto image = GetTextureInfo(kepler_compute, entry, ComputeShaderIndex).tic;
1174 SetupUniformTexels(image, entry); 1157 SetupUniformTexels(image, entry);
1175 } 1158 }
1176} 1159}
1177 1160
1178void RasterizerVulkan::SetupComputeTextures(const ShaderEntries& entries) { 1161void RasterizerVulkan::SetupComputeTextures(const ShaderEntries& entries) {
1179 MICROPROFILE_SCOPE(Vulkan_Textures); 1162 MICROPROFILE_SCOPE(Vulkan_Textures);
1180 const auto& gpu = system.GPU().KeplerCompute();
1181 for (const auto& entry : entries.samplers) { 1163 for (const auto& entry : entries.samplers) {
1182 for (std::size_t i = 0; i < entry.size; ++i) { 1164 for (std::size_t i = 0; i < entry.size; ++i) {
1183 const auto texture = GetTextureInfo(gpu, entry, ComputeShaderIndex, i); 1165 const auto texture = GetTextureInfo(kepler_compute, entry, ComputeShaderIndex, i);
1184 SetupTexture(texture, entry); 1166 SetupTexture(texture, entry);
1185 } 1167 }
1186 } 1168 }
@@ -1188,18 +1170,16 @@ void RasterizerVulkan::SetupComputeTextures(const ShaderEntries& entries) {
1188 1170
1189void RasterizerVulkan::SetupComputeStorageTexels(const ShaderEntries& entries) { 1171void RasterizerVulkan::SetupComputeStorageTexels(const ShaderEntries& entries) {
1190 MICROPROFILE_SCOPE(Vulkan_Textures); 1172 MICROPROFILE_SCOPE(Vulkan_Textures);
1191 const auto& gpu = system.GPU().KeplerCompute();
1192 for (const auto& entry : entries.storage_texels) { 1173 for (const auto& entry : entries.storage_texels) {
1193 const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic; 1174 const auto image = GetTextureInfo(kepler_compute, entry, ComputeShaderIndex).tic;
1194 SetupStorageTexel(image, entry); 1175 SetupStorageTexel(image, entry);
1195 } 1176 }
1196} 1177}
1197 1178
1198void RasterizerVulkan::SetupComputeImages(const ShaderEntries& entries) { 1179void RasterizerVulkan::SetupComputeImages(const ShaderEntries& entries) {
1199 MICROPROFILE_SCOPE(Vulkan_Images); 1180 MICROPROFILE_SCOPE(Vulkan_Images);
1200 const auto& gpu = system.GPU().KeplerCompute();
1201 for (const auto& entry : entries.images) { 1181 for (const auto& entry : entries.images) {
1202 const auto tic = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic; 1182 const auto tic = GetTextureInfo(kepler_compute, entry, ComputeShaderIndex).tic;
1203 SetupImage(tic, entry); 1183 SetupImage(tic, entry);
1204 } 1184 }
1205} 1185}
@@ -1223,9 +1203,8 @@ void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry,
1223} 1203}
1224 1204
1225void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) { 1205void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) {
1226 auto& memory_manager{system.GPU().MemoryManager()}; 1206 const u64 actual_addr = gpu_memory.Read<u64>(address);
1227 const auto actual_addr = memory_manager.Read<u64>(address); 1207 const u32 size = gpu_memory.Read<u32>(address + 8);
1228 const auto size = memory_manager.Read<u32>(address + 8);
1229 1208
1230 if (size == 0) { 1209 if (size == 0) {
1231 // Sometimes global memory pointers don't have a proper size. Upload a dummy entry 1210 // Sometimes global memory pointers don't have a proper size. Upload a dummy entry
@@ -1508,7 +1487,7 @@ std::size_t RasterizerVulkan::CalculateComputeStreamBufferSize() const {
1508} 1487}
1509 1488
1510std::size_t RasterizerVulkan::CalculateVertexArraysSize() const { 1489std::size_t RasterizerVulkan::CalculateVertexArraysSize() const {
1511 const auto& regs = system.GPU().Maxwell3D().regs; 1490 const auto& regs = maxwell3d.regs;
1512 1491
1513 std::size_t size = 0; 1492 std::size_t size = 0;
1514 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 1493 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -1523,9 +1502,8 @@ std::size_t RasterizerVulkan::CalculateVertexArraysSize() const {
1523} 1502}
1524 1503
1525std::size_t RasterizerVulkan::CalculateIndexBufferSize() const { 1504std::size_t RasterizerVulkan::CalculateIndexBufferSize() const {
1526 const auto& regs = system.GPU().Maxwell3D().regs; 1505 return static_cast<std::size_t>(maxwell3d.regs.index_array.count) *
1527 return static_cast<std::size_t>(regs.index_array.count) * 1506 static_cast<std::size_t>(maxwell3d.regs.index_array.FormatSizeInBytes());
1528 static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
1529} 1507}
1530 1508
1531std::size_t RasterizerVulkan::CalculateConstBufferSize( 1509std::size_t RasterizerVulkan::CalculateConstBufferSize(
@@ -1540,7 +1518,7 @@ std::size_t RasterizerVulkan::CalculateConstBufferSize(
1540} 1518}
1541 1519
1542RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions) const { 1520RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions) const {
1543 const auto& regs = system.GPU().Maxwell3D().regs; 1521 const auto& regs = maxwell3d.regs;
1544 const std::size_t num_attachments = static_cast<std::size_t>(regs.rt_control.count); 1522 const std::size_t num_attachments = static_cast<std::size_t>(regs.rt_control.count);
1545 1523
1546 RenderPassParams params; 1524 RenderPassParams params;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index f640ba649..16251d0f6 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -106,7 +106,8 @@ struct ImageView {
106 106
107class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { 107class RasterizerVulkan final : public VideoCore::RasterizerAccelerated {
108public: 108public:
109 explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window, 109 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
110 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
110 VKScreenInfo& screen_info, const VKDevice& device, 111 VKScreenInfo& screen_info, const VKDevice& device,
111 VKResourceManager& resource_manager, VKMemoryManager& memory_manager, 112 VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
112 StateTracker& state_tracker, VKScheduler& scheduler); 113 StateTracker& state_tracker, VKScheduler& scheduler);
@@ -135,7 +136,6 @@ public:
135 const Tegra::Engines::Fermi2D::Config& copy_config) override; 136 const Tegra::Engines::Fermi2D::Config& copy_config) override;
136 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 137 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
137 u32 pixel_stride) override; 138 u32 pixel_stride) override;
138 void SetupDirtyFlags() override;
139 139
140 VideoCommon::Shader::AsyncShaders& GetAsyncShaders() { 140 VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
141 return async_shaders; 141 return async_shaders;
@@ -279,8 +279,11 @@ private:
279 279
280 VkBuffer DefaultBuffer(); 280 VkBuffer DefaultBuffer();
281 281
282 Core::System& system; 282 Tegra::GPU& gpu;
283 Core::Frontend::EmuWindow& render_window; 283 Tegra::MemoryManager& gpu_memory;
284 Tegra::Engines::Maxwell3D& maxwell3d;
285 Tegra::Engines::KeplerCompute& kepler_compute;
286
284 VKScreenInfo& screen_info; 287 VKScreenInfo& screen_info;
285 const VKDevice& device; 288 const VKDevice& device;
286 VKResourceManager& resource_manager; 289 VKResourceManager& resource_manager;
@@ -300,8 +303,8 @@ private:
300 VKPipelineCache pipeline_cache; 303 VKPipelineCache pipeline_cache;
301 VKBufferCache buffer_cache; 304 VKBufferCache buffer_cache;
302 VKSamplerCache sampler_cache; 305 VKSamplerCache sampler_cache;
303 VKFenceManager fence_manager;
304 VKQueryCache query_cache; 306 VKQueryCache query_cache;
307 VKFenceManager fence_manager;
305 308
306 vk::Buffer default_buffer; 309 vk::Buffer default_buffer;
307 VKMemoryCommit default_buffer_commit; 310 VKMemoryCommit default_buffer_commit;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 4bd1009f9..5d2c4a796 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -132,12 +132,9 @@ void SetupDirtyStencilTestEnable(Tables& tables) {
132 132
133} // Anonymous namespace 133} // Anonymous namespace
134 134
135StateTracker::StateTracker(Core::System& system) 135StateTracker::StateTracker(Tegra::GPU& gpu)
136 : system{system}, invalidation_flags{MakeInvalidationFlags()} {} 136 : flags{gpu.Maxwell3D().dirty.flags}, invalidation_flags{MakeInvalidationFlags()} {
137 137 auto& tables = gpu.Maxwell3D().dirty.tables;
138void StateTracker::Initialize() {
139 auto& dirty = system.GPU().Maxwell3D().dirty;
140 auto& tables = dirty.tables;
141 SetupDirtyRenderTargets(tables); 138 SetupDirtyRenderTargets(tables);
142 SetupDirtyViewports(tables); 139 SetupDirtyViewports(tables);
143 SetupDirtyScissors(tables); 140 SetupDirtyScissors(tables);
@@ -155,9 +152,4 @@ void StateTracker::Initialize() {
155 SetupDirtyStencilTestEnable(tables); 152 SetupDirtyStencilTestEnable(tables);
156} 153}
157 154
158void StateTracker::InvalidateCommandBufferState() {
159 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
160 current_topology = INVALID_TOPOLOGY;
161}
162
163} // namespace Vulkan 155} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 13a6ce786..1de789e57 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -45,11 +45,12 @@ class StateTracker {
45 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 45 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
46 46
47public: 47public:
48 explicit StateTracker(Core::System& system); 48 explicit StateTracker(Tegra::GPU& gpu);
49 49
50 void Initialize(); 50 void InvalidateCommandBufferState() {
51 51 flags |= invalidation_flags;
52 void InvalidateCommandBufferState(); 52 current_topology = INVALID_TOPOLOGY;
53 }
53 54
54 bool TouchViewports() { 55 bool TouchViewports() {
55 return Exchange(Dirty::Viewports, false); 56 return Exchange(Dirty::Viewports, false);
@@ -121,13 +122,12 @@ private:
121 static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u); 122 static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
122 123
123 bool Exchange(std::size_t id, bool new_value) const noexcept { 124 bool Exchange(std::size_t id, bool new_value) const noexcept {
124 auto& flags = system.GPU().Maxwell3D().dirty.flags;
125 const bool is_dirty = flags[id]; 125 const bool is_dirty = flags[id];
126 flags[id] = new_value; 126 flags[id] = new_value;
127 return is_dirty; 127 return is_dirty;
128 } 128 }
129 129
130 Core::System& system; 130 Tegra::Engines::Maxwell3D::DirtyState::Flags& flags;
131 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; 131 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
132 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY; 132 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
133}; 133};
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
index a5526a3f5..3c9171a5e 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -57,9 +57,9 @@ u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
57 57
58} // Anonymous namespace 58} // Anonymous namespace
59 59
60VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler, 60VKStreamBuffer::VKStreamBuffer(const VKDevice& device_, VKScheduler& scheduler_,
61 VkBufferUsageFlags usage) 61 VkBufferUsageFlags usage)
62 : device{device}, scheduler{scheduler} { 62 : device{device_}, scheduler{scheduler_} {
63 CreateBuffers(usage); 63 CreateBuffers(usage);
64 ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); 64 ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE);
65 ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE); 65 ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE);
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 2c6f54101..06182d909 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -188,13 +188,13 @@ u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, Tegra::Texture::Swizzl
188 188
189} // Anonymous namespace 189} // Anonymous namespace
190 190
191CachedSurface::CachedSurface(Core::System& system, const VKDevice& device, 191CachedSurface::CachedSurface(const VKDevice& device, VKResourceManager& resource_manager,
192 VKResourceManager& resource_manager, VKMemoryManager& memory_manager, 192 VKMemoryManager& memory_manager, VKScheduler& scheduler,
193 VKScheduler& scheduler, VKStagingBufferPool& staging_pool, 193 VKStagingBufferPool& staging_pool, GPUVAddr gpu_addr,
194 GPUVAddr gpu_addr, const SurfaceParams& params) 194 const SurfaceParams& params)
195 : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, system{system}, 195 : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, device{device},
196 device{device}, resource_manager{resource_manager}, 196 resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler},
197 memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} { 197 staging_pool{staging_pool} {
198 if (params.IsBuffer()) { 198 if (params.IsBuffer()) {
199 buffer = CreateBuffer(device, params, host_memory_size); 199 buffer = CreateBuffer(device, params, host_memory_size);
200 commit = memory_manager.Commit(buffer, false); 200 commit = memory_manager.Commit(buffer, false);
@@ -490,19 +490,21 @@ VkImageView CachedSurfaceView::GetAttachment() {
490 return *render_target; 490 return *render_target;
491} 491}
492 492
493VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 493VKTextureCache::VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
494 const VKDevice& device, VKResourceManager& resource_manager, 494 Tegra::Engines::Maxwell3D& maxwell3d,
495 VKMemoryManager& memory_manager, VKScheduler& scheduler, 495 Tegra::MemoryManager& gpu_memory, const VKDevice& device_,
496 VKStagingBufferPool& staging_pool) 496 VKResourceManager& resource_manager_,
497 : TextureCache(system, rasterizer, device.IsOptimalAstcSupported()), device{device}, 497 VKMemoryManager& memory_manager_, VKScheduler& scheduler_,
498 resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler}, 498 VKStagingBufferPool& staging_pool_)
499 staging_pool{staging_pool} {} 499 : TextureCache(rasterizer, maxwell3d, gpu_memory, device_.IsOptimalAstcSupported()),
500 device{device_}, resource_manager{resource_manager_},
501 memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{staging_pool_} {}
500 502
501VKTextureCache::~VKTextureCache() = default; 503VKTextureCache::~VKTextureCache() = default;
502 504
503Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) { 505Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
504 return std::make_shared<CachedSurface>(system, device, resource_manager, memory_manager, 506 return std::make_shared<CachedSurface>(device, resource_manager, memory_manager, scheduler,
505 scheduler, staging_pool, gpu_addr, params); 507 staging_pool, gpu_addr, params);
506} 508}
507 509
508void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface, 510void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 807e26c8a..e47d02c41 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -15,10 +15,6 @@
15#include "video_core/texture_cache/surface_base.h" 15#include "video_core/texture_cache/surface_base.h"
16#include "video_core/texture_cache/texture_cache.h" 16#include "video_core/texture_cache/texture_cache.h"
17 17
18namespace Core {
19class System;
20}
21
22namespace VideoCore { 18namespace VideoCore {
23class RasterizerInterface; 19class RasterizerInterface;
24} 20}
@@ -45,10 +41,10 @@ class CachedSurface final : public VideoCommon::SurfaceBase<View> {
45 friend CachedSurfaceView; 41 friend CachedSurfaceView;
46 42
47public: 43public:
48 explicit CachedSurface(Core::System& system, const VKDevice& device, 44 explicit CachedSurface(const VKDevice& device, VKResourceManager& resource_manager,
49 VKResourceManager& resource_manager, VKMemoryManager& memory_manager, 45 VKMemoryManager& memory_manager, VKScheduler& scheduler,
50 VKScheduler& scheduler, VKStagingBufferPool& staging_pool, 46 VKStagingBufferPool& staging_pool, GPUVAddr gpu_addr,
51 GPUVAddr gpu_addr, const SurfaceParams& params); 47 const SurfaceParams& params);
52 ~CachedSurface(); 48 ~CachedSurface();
53 49
54 void UploadTexture(const std::vector<u8>& staging_buffer) override; 50 void UploadTexture(const std::vector<u8>& staging_buffer) override;
@@ -101,7 +97,6 @@ private:
101 97
102 VkImageSubresourceRange GetImageSubresourceRange() const; 98 VkImageSubresourceRange GetImageSubresourceRange() const;
103 99
104 Core::System& system;
105 const VKDevice& device; 100 const VKDevice& device;
106 VKResourceManager& resource_manager; 101 VKResourceManager& resource_manager;
107 VKMemoryManager& memory_manager; 102 VKMemoryManager& memory_manager;
@@ -201,7 +196,8 @@ private:
201 196
202class VKTextureCache final : public TextureCacheBase { 197class VKTextureCache final : public TextureCacheBase {
203public: 198public:
204 explicit VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 199 explicit VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
200 Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
205 const VKDevice& device, VKResourceManager& resource_manager, 201 const VKDevice& device, VKResourceManager& resource_manager,
206 VKMemoryManager& memory_manager, VKScheduler& scheduler, 202 VKMemoryManager& memory_manager, VKScheduler& scheduler,
207 VKStagingBufferPool& staging_pool); 203 VKStagingBufferPool& staging_pool);
diff --git a/src/video_core/shader/memory_util.cpp b/src/video_core/shader/memory_util.cpp
index 5071c83ca..e18ccba8e 100644
--- a/src/video_core/shader/memory_util.cpp
+++ b/src/video_core/shader/memory_util.cpp
@@ -16,11 +16,10 @@
16 16
17namespace VideoCommon::Shader { 17namespace VideoCommon::Shader {
18 18
19GPUVAddr GetShaderAddress(Core::System& system, 19GPUVAddr GetShaderAddress(Tegra::Engines::Maxwell3D& maxwell3d,
20 Tegra::Engines::Maxwell3D::Regs::ShaderProgram program) { 20 Tegra::Engines::Maxwell3D::Regs::ShaderProgram program) {
21 const auto& gpu{system.GPU().Maxwell3D()}; 21 const auto& shader_config{maxwell3d.regs.shader_config[static_cast<std::size_t>(program)]};
22 const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]}; 22 return maxwell3d.regs.code_address.CodeAddress() + shader_config.offset;
23 return gpu.regs.code_address.CodeAddress() + shader_config.offset;
24} 23}
25 24
26bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) { 25bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
diff --git a/src/video_core/shader/memory_util.h b/src/video_core/shader/memory_util.h
index be90d24fd..4624d38e6 100644
--- a/src/video_core/shader/memory_util.h
+++ b/src/video_core/shader/memory_util.h
@@ -11,10 +11,6 @@
11#include "video_core/engines/maxwell_3d.h" 11#include "video_core/engines/maxwell_3d.h"
12#include "video_core/engines/shader_type.h" 12#include "video_core/engines/shader_type.h"
13 13
14namespace Core {
15class System;
16}
17
18namespace Tegra { 14namespace Tegra {
19class MemoryManager; 15class MemoryManager;
20} 16}
@@ -27,7 +23,7 @@ constexpr u32 STAGE_MAIN_OFFSET = 10;
27constexpr u32 KERNEL_MAIN_OFFSET = 0; 23constexpr u32 KERNEL_MAIN_OFFSET = 0;
28 24
29/// Gets the address for the specified shader stage program 25/// Gets the address for the specified shader stage program
30GPUVAddr GetShaderAddress(Core::System& system, 26GPUVAddr GetShaderAddress(Tegra::Engines::Maxwell3D& maxwell3d,
31 Tegra::Engines::Maxwell3D::Regs::ShaderProgram program); 27 Tegra::Engines::Maxwell3D::Regs::ShaderProgram program);
32 28
33/// Gets if the current instruction offset is a scheduler instruction 29/// Gets if the current instruction offset is a scheduler instruction
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index e614a92df..e8515321b 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -163,13 +163,11 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl
163 return params; 163 return params;
164} 164}
165 165
166SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) { 166SurfaceParams SurfaceParams::CreateForDepthBuffer(Tegra::Engines::Maxwell3D& maxwell3d) {
167 const auto& regs = system.GPU().Maxwell3D().regs; 167 const auto& regs = maxwell3d.regs;
168
169 const auto block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U); 168 const auto block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
170 const bool is_layered = regs.zeta_layers > 1 && block_depth == 0; 169 const bool is_layered = regs.zeta_layers > 1 && block_depth == 0;
171 const auto pixel_format = PixelFormatFromDepthFormat(regs.zeta.format); 170 const auto pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
172
173 return { 171 return {
174 .is_tiled = regs.zeta.memory_layout.type == 172 .is_tiled = regs.zeta.memory_layout.type ==
175 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear, 173 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear,
@@ -191,8 +189,9 @@ SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) {
191 }; 189 };
192} 190}
193 191
194SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) { 192SurfaceParams SurfaceParams::CreateForFramebuffer(Tegra::Engines::Maxwell3D& maxwell3d,
195 const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; 193 std::size_t index) {
194 const auto& config{maxwell3d.regs.rt[index]};
196 SurfaceParams params; 195 SurfaceParams params;
197 params.is_tiled = 196 params.is_tiled =
198 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 197 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h
index 118aa689e..4466c3c34 100644
--- a/src/video_core/texture_cache/surface_params.h
+++ b/src/video_core/texture_cache/surface_params.h
@@ -33,10 +33,11 @@ public:
33 const VideoCommon::Shader::Image& entry); 33 const VideoCommon::Shader::Image& entry);
34 34
35 /// Creates SurfaceCachedParams for a depth buffer configuration. 35 /// Creates SurfaceCachedParams for a depth buffer configuration.
36 static SurfaceParams CreateForDepthBuffer(Core::System& system); 36 static SurfaceParams CreateForDepthBuffer(Tegra::Engines::Maxwell3D& maxwell3d);
37 37
38 /// Creates SurfaceCachedParams from a framebuffer configuration. 38 /// Creates SurfaceCachedParams from a framebuffer configuration.
39 static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); 39 static SurfaceParams CreateForFramebuffer(Tegra::Engines::Maxwell3D& maxwell3d,
40 std::size_t index);
40 41
41 /// Creates SurfaceCachedParams from a Fermi2D surface configuration. 42 /// Creates SurfaceCachedParams from a Fermi2D surface configuration.
42 static SurfaceParams CreateForFermiCopySurface( 43 static SurfaceParams CreateForFermiCopySurface(
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 96c4e4cc2..ea835c59f 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -135,8 +135,7 @@ public:
135 return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); 135 return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
136 } 136 }
137 137
138 const std::optional<VAddr> cpu_addr = 138 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
139 system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
140 if (!cpu_addr) { 139 if (!cpu_addr) {
141 return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); 140 return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
142 } 141 }
@@ -160,8 +159,7 @@ public:
160 if (!gpu_addr) { 159 if (!gpu_addr) {
161 return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); 160 return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
162 } 161 }
163 const std::optional<VAddr> cpu_addr = 162 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
164 system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
165 if (!cpu_addr) { 163 if (!cpu_addr) {
166 return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); 164 return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
167 } 165 }
@@ -183,11 +181,11 @@ public:
183 181
184 TView GetDepthBufferSurface(bool preserve_contents) { 182 TView GetDepthBufferSurface(bool preserve_contents) {
185 std::lock_guard lock{mutex}; 183 std::lock_guard lock{mutex};
186 auto& maxwell3d = system.GPU().Maxwell3D(); 184 auto& dirty = maxwell3d.dirty;
187 if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer]) { 185 if (!dirty.flags[VideoCommon::Dirty::ZetaBuffer]) {
188 return depth_buffer.view; 186 return depth_buffer.view;
189 } 187 }
190 maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer] = false; 188 dirty.flags[VideoCommon::Dirty::ZetaBuffer] = false;
191 189
192 const auto& regs{maxwell3d.regs}; 190 const auto& regs{maxwell3d.regs};
193 const auto gpu_addr{regs.zeta.Address()}; 191 const auto gpu_addr{regs.zeta.Address()};
@@ -195,13 +193,12 @@ public:
195 SetEmptyDepthBuffer(); 193 SetEmptyDepthBuffer();
196 return {}; 194 return {};
197 } 195 }
198 const std::optional<VAddr> cpu_addr = 196 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
199 system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
200 if (!cpu_addr) { 197 if (!cpu_addr) {
201 SetEmptyDepthBuffer(); 198 SetEmptyDepthBuffer();
202 return {}; 199 return {};
203 } 200 }
204 const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)}; 201 const auto depth_params{SurfaceParams::CreateForDepthBuffer(maxwell3d)};
205 auto surface_view = GetSurface(gpu_addr, *cpu_addr, depth_params, preserve_contents, true); 202 auto surface_view = GetSurface(gpu_addr, *cpu_addr, depth_params, preserve_contents, true);
206 if (depth_buffer.target) 203 if (depth_buffer.target)
207 depth_buffer.target->MarkAsRenderTarget(false, NO_RT); 204 depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
@@ -215,7 +212,6 @@ public:
215 TView GetColorBufferSurface(std::size_t index, bool preserve_contents) { 212 TView GetColorBufferSurface(std::size_t index, bool preserve_contents) {
216 std::lock_guard lock{mutex}; 213 std::lock_guard lock{mutex};
217 ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); 214 ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
218 auto& maxwell3d = system.GPU().Maxwell3D();
219 if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index]) { 215 if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index]) {
220 return render_targets[index].view; 216 return render_targets[index].view;
221 } 217 }
@@ -235,15 +231,14 @@ public:
235 return {}; 231 return {};
236 } 232 }
237 233
238 const std::optional<VAddr> cpu_addr = 234 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
239 system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
240 if (!cpu_addr) { 235 if (!cpu_addr) {
241 SetEmptyColorBuffer(index); 236 SetEmptyColorBuffer(index);
242 return {}; 237 return {};
243 } 238 }
244 239
245 auto surface_view = 240 auto surface_view =
246 GetSurface(gpu_addr, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index), 241 GetSurface(gpu_addr, *cpu_addr, SurfaceParams::CreateForFramebuffer(maxwell3d, index),
247 preserve_contents, true); 242 preserve_contents, true);
248 if (render_targets[index].target) { 243 if (render_targets[index].target) {
249 auto& surface = render_targets[index].target; 244 auto& surface = render_targets[index].target;
@@ -300,9 +295,8 @@ public:
300 const GPUVAddr dst_gpu_addr = dst_config.Address(); 295 const GPUVAddr dst_gpu_addr = dst_config.Address();
301 DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr); 296 DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr);
302 297
303 const auto& memory_manager = system.GPU().MemoryManager(); 298 const std::optional<VAddr> dst_cpu_addr = gpu_memory.GpuToCpuAddress(dst_gpu_addr);
304 const std::optional<VAddr> dst_cpu_addr = memory_manager.GpuToCpuAddress(dst_gpu_addr); 299 const std::optional<VAddr> src_cpu_addr = gpu_memory.GpuToCpuAddress(src_gpu_addr);
305 const std::optional<VAddr> src_cpu_addr = memory_manager.GpuToCpuAddress(src_gpu_addr);
306 std::pair dst_surface = GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false); 300 std::pair dst_surface = GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false);
307 TView src_surface = GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false).second; 301 TView src_surface = GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false).second;
308 ImageBlit(src_surface, dst_surface.second, copy_config); 302 ImageBlit(src_surface, dst_surface.second, copy_config);
@@ -358,9 +352,11 @@ public:
358 } 352 }
359 353
360protected: 354protected:
361 explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 355 explicit TextureCache(VideoCore::RasterizerInterface& rasterizer_,
362 bool is_astc_supported) 356 Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_,
363 : system{system}, is_astc_supported{is_astc_supported}, rasterizer{rasterizer} { 357 bool is_astc_supported_)
358 : is_astc_supported{is_astc_supported_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
359 gpu_memory{gpu_memory_} {
364 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 360 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
365 SetEmptyColorBuffer(i); 361 SetEmptyColorBuffer(i);
366 } 362 }
@@ -395,7 +391,7 @@ protected:
395 virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0; 391 virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0;
396 392
397 void ManageRenderTargetUnregister(TSurface& surface) { 393 void ManageRenderTargetUnregister(TSurface& surface) {
398 auto& dirty = system.GPU().Maxwell3D().dirty; 394 auto& dirty = maxwell3d.dirty;
399 const u32 index = surface->GetRenderTarget(); 395 const u32 index = surface->GetRenderTarget();
400 if (index == DEPTH_RT) { 396 if (index == DEPTH_RT) {
401 dirty.flags[VideoCommon::Dirty::ZetaBuffer] = true; 397 dirty.flags[VideoCommon::Dirty::ZetaBuffer] = true;
@@ -408,8 +404,7 @@ protected:
408 void Register(TSurface surface) { 404 void Register(TSurface surface) {
409 const GPUVAddr gpu_addr = surface->GetGpuAddr(); 405 const GPUVAddr gpu_addr = surface->GetGpuAddr();
410 const std::size_t size = surface->GetSizeInBytes(); 406 const std::size_t size = surface->GetSizeInBytes();
411 const std::optional<VAddr> cpu_addr = 407 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
412 system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
413 if (!cpu_addr) { 408 if (!cpu_addr) {
414 LOG_CRITICAL(HW_GPU, "Failed to register surface with unmapped gpu_address 0x{:016x}", 409 LOG_CRITICAL(HW_GPU, "Failed to register surface with unmapped gpu_address 0x{:016x}",
415 gpu_addr); 410 gpu_addr);
@@ -459,7 +454,6 @@ protected:
459 return new_surface; 454 return new_surface;
460 } 455 }
461 456
462 Core::System& system;
463 const bool is_astc_supported; 457 const bool is_astc_supported;
464 458
465private: 459private:
@@ -954,8 +948,7 @@ private:
954 * @param params The parameters on the candidate surface. 948 * @param params The parameters on the candidate surface.
955 **/ 949 **/
956 Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) { 950 Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) {
957 const std::optional<VAddr> cpu_addr = 951 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
958 system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
959 952
960 if (!cpu_addr) { 953 if (!cpu_addr) {
961 Deduction result{}; 954 Deduction result{};
@@ -1112,7 +1105,7 @@ private:
1112 1105
1113 void LoadSurface(const TSurface& surface) { 1106 void LoadSurface(const TSurface& surface) {
1114 staging_cache.GetBuffer(0).resize(surface->GetHostSizeInBytes()); 1107 staging_cache.GetBuffer(0).resize(surface->GetHostSizeInBytes());
1115 surface->LoadBuffer(system.GPU().MemoryManager(), staging_cache); 1108 surface->LoadBuffer(gpu_memory, staging_cache);
1116 surface->UploadTexture(staging_cache.GetBuffer(0)); 1109 surface->UploadTexture(staging_cache.GetBuffer(0));
1117 surface->MarkAsModified(false, Tick()); 1110 surface->MarkAsModified(false, Tick());
1118 } 1111 }
@@ -1123,7 +1116,7 @@ private:
1123 } 1116 }
1124 staging_cache.GetBuffer(0).resize(surface->GetHostSizeInBytes()); 1117 staging_cache.GetBuffer(0).resize(surface->GetHostSizeInBytes());
1125 surface->DownloadTexture(staging_cache.GetBuffer(0)); 1118 surface->DownloadTexture(staging_cache.GetBuffer(0));
1126 surface->FlushBuffer(system.GPU().MemoryManager(), staging_cache); 1119 surface->FlushBuffer(gpu_memory, staging_cache);
1127 surface->MarkAsModified(false, Tick()); 1120 surface->MarkAsModified(false, Tick());
1128 } 1121 }
1129 1122
@@ -1253,6 +1246,8 @@ private:
1253 } 1246 }
1254 1247
1255 VideoCore::RasterizerInterface& rasterizer; 1248 VideoCore::RasterizerInterface& rasterizer;
1249 Tegra::Engines::Maxwell3D& maxwell3d;
1250 Tegra::MemoryManager& gpu_memory;
1256 1251
1257 FormatLookupTable format_lookup_table; 1252 FormatLookupTable format_lookup_table;
1258 FormatCompatibility format_compatibility; 1253 FormatCompatibility format_compatibility;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 4e3a092c7..a14df06a3 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -21,14 +21,17 @@ namespace {
21std::unique_ptr<VideoCore::RendererBase> CreateRenderer( 21std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
22 Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu, 22 Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
23 std::unique_ptr<Core::Frontend::GraphicsContext> context) { 23 std::unique_ptr<Core::Frontend::GraphicsContext> context) {
24 auto& telemetry_session = system.TelemetrySession();
25 auto& cpu_memory = system.Memory();
26
24 switch (Settings::values.renderer_backend.GetValue()) { 27 switch (Settings::values.renderer_backend.GetValue()) {
25 case Settings::RendererBackend::OpenGL: 28 case Settings::RendererBackend::OpenGL:
26 return std::make_unique<OpenGL::RendererOpenGL>(system, emu_window, gpu, 29 return std::make_unique<OpenGL::RendererOpenGL>(telemetry_session, emu_window, cpu_memory,
27 std::move(context)); 30 gpu, std::move(context));
28#ifdef HAS_VULKAN 31#ifdef HAS_VULKAN
29 case Settings::RendererBackend::Vulkan: 32 case Settings::RendererBackend::Vulkan:
30 return std::make_unique<Vulkan::RendererVulkan>(system, emu_window, gpu, 33 return std::make_unique<Vulkan::RendererVulkan>(telemetry_session, emu_window, cpu_memory,
31 std::move(context)); 34 gpu, std::move(context));
32#endif 35#endif
33 default: 36 default:
34 return nullptr; 37 return nullptr;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 3ea4e5601..cc0291b15 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -9,6 +9,9 @@ add_executable(yuzu
9 about_dialog.cpp 9 about_dialog.cpp
10 about_dialog.h 10 about_dialog.h
11 aboutdialog.ui 11 aboutdialog.ui
12 applets/controller.cpp
13 applets/controller.h
14 applets/controller.ui
12 applets/error.cpp 15 applets/error.cpp
13 applets/error.h 16 applets/error.h
14 applets/profile_select.cpp 17 applets/profile_select.cpp
@@ -62,12 +65,15 @@ add_executable(yuzu
62 configuration/configure_input.cpp 65 configuration/configure_input.cpp
63 configuration/configure_input.h 66 configuration/configure_input.h
64 configuration/configure_input.ui 67 configuration/configure_input.ui
65 configuration/configure_input_player.cpp
66 configuration/configure_input_player.h
67 configuration/configure_input_player.ui
68 configuration/configure_input_advanced.cpp 68 configuration/configure_input_advanced.cpp
69 configuration/configure_input_advanced.h 69 configuration/configure_input_advanced.h
70 configuration/configure_input_advanced.ui 70 configuration/configure_input_advanced.ui
71 configuration/configure_input_dialog.cpp
72 configuration/configure_input_dialog.h
73 configuration/configure_input_dialog.ui
74 configuration/configure_input_player.cpp
75 configuration/configure_input_player.h
76 configuration/configure_input_player.ui
71 configuration/configure_motion_touch.cpp 77 configuration/configure_motion_touch.cpp
72 configuration/configure_motion_touch.h 78 configuration/configure_motion_touch.h
73 configuration/configure_motion_touch.ui 79 configuration/configure_motion_touch.ui
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
new file mode 100644
index 000000000..9d45f2a01
--- /dev/null
+++ b/src/yuzu/applets/controller.cpp
@@ -0,0 +1,601 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/assert.h"
8#include "common/string_util.h"
9#include "core/core.h"
10#include "core/hle/lock.h"
11#include "core/hle/service/hid/controllers/npad.h"
12#include "core/hle/service/hid/hid.h"
13#include "core/hle/service/sm/sm.h"
14#include "ui_controller.h"
15#include "yuzu/applets/controller.h"
16#include "yuzu/configuration/configure_input_dialog.h"
17#include "yuzu/main.h"
18
19namespace {
20
21constexpr std::array<std::array<bool, 4>, 8> led_patterns = {{
22 {1, 0, 0, 0},
23 {1, 1, 0, 0},
24 {1, 1, 1, 0},
25 {1, 1, 1, 1},
26 {1, 0, 0, 1},
27 {1, 0, 1, 0},
28 {1, 0, 1, 1},
29 {0, 1, 1, 0},
30}};
31
32void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
33 bool connected) {
34 Core::System& system{Core::System::GetInstance()};
35
36 if (!system.IsPoweredOn()) {
37 return;
38 }
39
40 Service::SM::ServiceManager& sm = system.ServiceManager();
41
42 auto& npad =
43 sm.GetService<Service::HID::Hid>("hid")
44 ->GetAppletResource()
45 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
46
47 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
48}
49
50// Returns true if the given controller type is compatible with the given parameters.
51bool IsControllerCompatible(Settings::ControllerType controller_type,
52 Core::Frontend::ControllerParameters parameters) {
53 switch (controller_type) {
54 case Settings::ControllerType::ProController:
55 return parameters.allow_pro_controller;
56 case Settings::ControllerType::DualJoyconDetached:
57 return parameters.allow_dual_joycons;
58 case Settings::ControllerType::LeftJoycon:
59 return parameters.allow_left_joycon;
60 case Settings::ControllerType::RightJoycon:
61 return parameters.allow_right_joycon;
62 case Settings::ControllerType::Handheld:
63 return parameters.enable_single_mode && parameters.allow_handheld;
64 default:
65 return false;
66 }
67}
68
69/// Maps the controller type combobox index to Controller Type enum
70constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
71 switch (index) {
72 case 0:
73 default:
74 return Settings::ControllerType::ProController;
75 case 1:
76 return Settings::ControllerType::DualJoyconDetached;
77 case 2:
78 return Settings::ControllerType::LeftJoycon;
79 case 3:
80 return Settings::ControllerType::RightJoycon;
81 case 4:
82 return Settings::ControllerType::Handheld;
83 }
84}
85
86/// Maps the Controller Type enum to controller type combobox index
87constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
88 switch (type) {
89 case Settings::ControllerType::ProController:
90 default:
91 return 0;
92 case Settings::ControllerType::DualJoyconDetached:
93 return 1;
94 case Settings::ControllerType::LeftJoycon:
95 return 2;
96 case Settings::ControllerType::RightJoycon:
97 return 3;
98 case Settings::ControllerType::Handheld:
99 return 4;
100 }
101}
102
103} // namespace
104
105QtControllerSelectorDialog::QtControllerSelectorDialog(
106 QWidget* parent, Core::Frontend::ControllerParameters parameters_,
107 InputCommon::InputSubsystem* input_subsystem_)
108 : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
109 parameters(std::move(parameters_)), input_subsystem(input_subsystem_) {
110 ui->setupUi(this);
111
112 player_widgets = {
113 ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4,
114 ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8,
115 };
116
117 player_groupboxes = {
118 ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected,
119 ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected,
120 ui->groupPlayer7Connected, ui->groupPlayer8Connected,
121 };
122
123 connected_controller_icons = {
124 ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4,
125 ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8,
126 };
127
128 led_patterns_boxes = {{
129 {ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3,
130 ui->checkboxPlayer1LED4},
131 {ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3,
132 ui->checkboxPlayer2LED4},
133 {ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3,
134 ui->checkboxPlayer3LED4},
135 {ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3,
136 ui->checkboxPlayer4LED4},
137 {ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3,
138 ui->checkboxPlayer5LED4},
139 {ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3,
140 ui->checkboxPlayer6LED4},
141 {ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3,
142 ui->checkboxPlayer7LED4},
143 {ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3,
144 ui->checkboxPlayer8LED4},
145 }};
146
147 explain_text_labels = {
148 ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain,
149 ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain,
150 ui->labelPlayer7Explain, ui->labelPlayer8Explain,
151 };
152
153 emulated_controllers = {
154 ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated,
155 ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated,
156 ui->comboPlayer7Emulated, ui->comboPlayer8Emulated,
157 };
158
159 player_labels = {
160 ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4,
161 ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8,
162 };
163
164 connected_controller_labels = {
165 ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3,
166 ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6,
167 ui->labelConnectedPlayer7, ui->labelConnectedPlayer8,
168 };
169
170 connected_controller_checkboxes = {
171 ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
172 ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
173 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
174 };
175
176 // Setup/load everything prior to setting up connections.
177 // This avoids unintentionally changing the states of elements while loading them in.
178 SetSupportedControllers();
179 DisableUnsupportedPlayers();
180 LoadConfiguration();
181
182 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
183 SetExplainText(i);
184 UpdateControllerIcon(i);
185 UpdateLEDPattern(i);
186 UpdateBorderColor(i);
187
188 connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
189 if (checked) {
190 for (std::size_t index = 0; index <= i; ++index) {
191 connected_controller_checkboxes[index]->setChecked(checked);
192 }
193 } else {
194 for (std::size_t index = i; index < NUM_PLAYERS; ++index) {
195 connected_controller_checkboxes[index]->setChecked(checked);
196 }
197 }
198 });
199
200 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
201 [this, i](int) {
202 UpdateControllerIcon(i);
203 UpdateControllerState(i);
204 UpdateLEDPattern(i);
205 CheckIfParametersMet();
206 });
207
208 connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) {
209 player_groupboxes[i]->setChecked(state == Qt::Checked);
210 UpdateControllerIcon(i);
211 UpdateControllerState(i);
212 UpdateLEDPattern(i);
213 UpdateBorderColor(i);
214 CheckIfParametersMet();
215 });
216
217 if (i == 0) {
218 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
219 [this](int index) {
220 UpdateDockedState(GetControllerTypeFromIndex(index) ==
221 Settings::ControllerType::Handheld);
222 });
223 }
224 }
225
226 connect(ui->inputConfigButton, &QPushButton::clicked, this,
227 &QtControllerSelectorDialog::CallConfigureInputDialog);
228
229 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
230 &QtControllerSelectorDialog::ApplyConfiguration);
231
232 // If keep_controllers_connected is false, forcefully disconnect all controllers
233 if (!parameters.keep_controllers_connected) {
234 for (auto player : player_groupboxes) {
235 player->setChecked(false);
236 }
237 }
238
239 CheckIfParametersMet();
240
241 resize(0, 0);
242}
243
244QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
245
246void QtControllerSelectorDialog::ApplyConfiguration() {
247 // Update the controller state once more, just to be sure they are properly applied.
248 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
249 UpdateControllerState(index);
250 }
251
252 const bool pre_docked_mode = Settings::values.use_docked_mode;
253 Settings::values.use_docked_mode = ui->radioDocked->isChecked();
254 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
255
256 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
257}
258
259void QtControllerSelectorDialog::LoadConfiguration() {
260 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
261 const auto connected = Settings::values.players[index].connected ||
262 (index == 0 && Settings::values.players[8].connected);
263 player_groupboxes[index]->setChecked(connected);
264 connected_controller_checkboxes[index]->setChecked(connected);
265 emulated_controllers[index]->setCurrentIndex(
266 GetIndexFromControllerType(Settings::values.players[index].controller_type));
267 }
268
269 UpdateDockedState(Settings::values.players[8].connected);
270
271 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
272}
273
274void QtControllerSelectorDialog::CallConfigureInputDialog() {
275 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
276
277 ConfigureInputDialog dialog(this, max_supported_players, input_subsystem);
278
279 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
280 Qt::WindowSystemMenuHint);
281 dialog.setWindowModality(Qt::WindowModal);
282 dialog.exec();
283
284 dialog.ApplyConfiguration();
285
286 LoadConfiguration();
287 CheckIfParametersMet();
288}
289
290void QtControllerSelectorDialog::CheckIfParametersMet() {
291 // Here, we check and validate the current configuration against all applicable parameters.
292 const auto num_connected_players = static_cast<int>(
293 std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
294 [this](const QGroupBox* player) { return player->isChecked(); }));
295
296 const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
297 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
298
299 // First, check against the number of connected players.
300 if (num_connected_players < min_supported_players ||
301 num_connected_players > max_supported_players) {
302 parameters_met = false;
303 ui->buttonBox->setEnabled(parameters_met);
304 return;
305 }
306
307 // Next, check against all connected controllers.
308 const auto all_controllers_compatible = [this] {
309 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
310 // Skip controllers that are not used, we only care about the currently connected ones.
311 if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) {
312 continue;
313 }
314
315 const auto compatible = IsControllerCompatible(
316 GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()),
317 parameters);
318
319 // If any controller is found to be incompatible, return false early.
320 if (!compatible) {
321 return false;
322 }
323 }
324
325 // Reaching here means all currently connected controllers are compatible.
326 return true;
327 }();
328
329 if (!all_controllers_compatible) {
330 parameters_met = false;
331 ui->buttonBox->setEnabled(parameters_met);
332 return;
333 }
334
335 parameters_met = true;
336 ui->buttonBox->setEnabled(parameters_met);
337}
338
339void QtControllerSelectorDialog::SetSupportedControllers() {
340 const QString theme = [this] {
341 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
342 return QStringLiteral("_dark");
343 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
344 return QStringLiteral("_midnight");
345 } else {
346 return QString{};
347 }
348 }();
349
350 if (parameters.enable_single_mode && parameters.allow_handheld) {
351 ui->controllerSupported1->setStyleSheet(
352 QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme));
353 } else {
354 ui->controllerSupported1->setStyleSheet(
355 QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme));
356 }
357
358 if (parameters.allow_dual_joycons) {
359 ui->controllerSupported2->setStyleSheet(
360 QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme));
361 } else {
362 ui->controllerSupported2->setStyleSheet(
363 QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme));
364 }
365
366 if (parameters.allow_left_joycon) {
367 ui->controllerSupported3->setStyleSheet(
368 QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme));
369 } else {
370 ui->controllerSupported3->setStyleSheet(
371 QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme));
372 }
373
374 if (parameters.allow_right_joycon) {
375 ui->controllerSupported4->setStyleSheet(
376 QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme));
377 } else {
378 ui->controllerSupported4->setStyleSheet(
379 QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
380 }
381
382 if (parameters.allow_pro_controller) {
383 ui->controllerSupported5->setStyleSheet(
384 QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
385 } else {
386 ui->controllerSupported5->setStyleSheet(
387 QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ")
388 .arg(theme));
389 }
390
391 // enable_single_mode overrides min_players and max_players.
392 if (parameters.enable_single_mode) {
393 ui->numberSupportedLabel->setText(QStringLiteral("1"));
394 return;
395 }
396
397 if (parameters.min_players == parameters.max_players) {
398 ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players));
399 } else {
400 ui->numberSupportedLabel->setText(
401 QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players));
402 }
403}
404
405void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
406 if (!player_groupboxes[player_index]->isChecked()) {
407 connected_controller_icons[player_index]->setStyleSheet(QString{});
408 player_labels[player_index]->show();
409 return;
410 }
411
412 const QString stylesheet = [this, player_index] {
413 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) {
414 case Settings::ControllerType::ProController:
415 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
416 case Settings::ControllerType::DualJoyconDetached:
417 return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
418 case Settings::ControllerType::LeftJoycon:
419 return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
420 case Settings::ControllerType::RightJoycon:
421 return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
422 case Settings::ControllerType::Handheld:
423 return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
424 default:
425 return QString{};
426 }
427 }();
428
429 const QString theme = [this] {
430 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
431 return QStringLiteral("_dark");
432 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
433 return QStringLiteral("_midnight");
434 } else {
435 return QString{};
436 }
437 }();
438
439 connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme));
440 player_labels[player_index]->hide();
441}
442
443void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
444 auto& player = Settings::values.players[player_index];
445
446 player.controller_type =
447 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
448 player.connected = player_groupboxes[player_index]->isChecked();
449
450 // Player 2-8
451 if (player_index != 0) {
452 UpdateController(player.controller_type, player_index, player.connected);
453 return;
454 }
455
456 // Player 1 and Handheld
457 auto& handheld = Settings::values.players[8];
458 // If Handheld is selected, copy all the settings from Player 1 to Handheld.
459 if (player.controller_type == Settings::ControllerType::Handheld) {
460 handheld = player;
461 handheld.connected = player_groupboxes[player_index]->isChecked();
462 player.connected = false; // Disconnect Player 1
463 } else {
464 player.connected = player_groupboxes[player_index]->isChecked();
465 handheld.connected = false; // Disconnect Handheld
466 }
467
468 UpdateController(player.controller_type, player_index, player.connected);
469 UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
470}
471
472void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
473 if (!player_groupboxes[player_index]->isChecked() ||
474 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) ==
475 Settings::ControllerType::Handheld) {
476 led_patterns_boxes[player_index][0]->setChecked(false);
477 led_patterns_boxes[player_index][1]->setChecked(false);
478 led_patterns_boxes[player_index][2]->setChecked(false);
479 led_patterns_boxes[player_index][3]->setChecked(false);
480 return;
481 }
482
483 led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]);
484 led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]);
485 led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]);
486 led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]);
487}
488
489void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
490 if (!parameters.enable_border_color ||
491 player_index >= static_cast<std::size_t>(parameters.max_players) ||
492 player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) {
493 return;
494 }
495
496 player_groupboxes[player_index]->setStyleSheet(
497 player_groupboxes[player_index]->styleSheet().append(
498 QStringLiteral("QGroupBox#groupPlayer%1Connected:checked "
499 "{ border: 1px solid rgba(%2, %3, %4, %5); }")
500 .arg(player_index + 1)
501 .arg(parameters.border_colors[player_index][0])
502 .arg(parameters.border_colors[player_index][1])
503 .arg(parameters.border_colors[player_index][2])
504 .arg(parameters.border_colors[player_index][3])));
505}
506
507void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) {
508 if (!parameters.enable_explain_text ||
509 player_index >= static_cast<std::size_t>(parameters.max_players)) {
510 return;
511 }
512
513 explain_text_labels[player_index]->setText(QString::fromStdString(
514 Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(),
515 parameters.explain_text[player_index].size())));
516}
517
518void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
519 // Disallow changing the console mode if the controller type is handheld.
520 ui->radioDocked->setEnabled(!is_handheld);
521 ui->radioUndocked->setEnabled(!is_handheld);
522
523 ui->radioDocked->setChecked(Settings::values.use_docked_mode);
524 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
525
526 // Also force into undocked mode if the controller type is handheld.
527 if (is_handheld) {
528 ui->radioUndocked->setChecked(true);
529 }
530}
531
532void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
533 const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
534
535 switch (max_supported_players) {
536 case 0:
537 default:
538 UNREACHABLE();
539 return;
540 case 1:
541 ui->widgetSpacer->hide();
542 ui->widgetSpacer2->hide();
543 ui->widgetSpacer3->hide();
544 ui->widgetSpacer4->hide();
545 break;
546 case 2:
547 ui->widgetSpacer->hide();
548 ui->widgetSpacer2->hide();
549 ui->widgetSpacer3->hide();
550 break;
551 case 3:
552 ui->widgetSpacer->hide();
553 ui->widgetSpacer2->hide();
554 break;
555 case 4:
556 ui->widgetSpacer->hide();
557 break;
558 case 5:
559 case 6:
560 case 7:
561 case 8:
562 break;
563 }
564
565 for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
566 // Disconnect any unsupported players here and disable or hide them if applicable.
567 Settings::values.players[index].connected = false;
568 UpdateController(Settings::values.players[index].controller_type, index, false);
569 // Hide the player widgets when max_supported_controllers is less than or equal to 4.
570 if (max_supported_players <= 4) {
571 player_widgets[index]->hide();
572 }
573
574 // Disable and hide the following to prevent these from interaction.
575 player_widgets[index]->setDisabled(true);
576 connected_controller_checkboxes[index]->setDisabled(true);
577 connected_controller_labels[index]->hide();
578 connected_controller_checkboxes[index]->hide();
579 }
580}
581
582QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
583 connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
584 &GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
585 connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
586 &QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
587}
588
589QtControllerSelector::~QtControllerSelector() = default;
590
591void QtControllerSelector::ReconfigureControllers(
592 std::function<void()> callback, Core::Frontend::ControllerParameters parameters) const {
593 this->callback = std::move(callback);
594 emit MainWindowReconfigureControllers(parameters);
595}
596
597void QtControllerSelector::MainWindowReconfigureFinished() {
598 // Acquire the HLE mutex
599 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
600 callback();
601}
diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h
new file mode 100644
index 000000000..2d6d588c6
--- /dev/null
+++ b/src/yuzu/applets/controller.h
@@ -0,0 +1,133 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9#include <QDialog>
10#include "core/frontend/applets/controller.h"
11
12class GMainWindow;
13class QCheckBox;
14class QComboBox;
15class QDialogButtonBox;
16class QGroupBox;
17class QLabel;
18
19namespace InputCommon {
20class InputSubsystem;
21}
22
23namespace Ui {
24class QtControllerSelectorDialog;
25}
26
27class QtControllerSelectorDialog final : public QDialog {
28 Q_OBJECT
29
30public:
31 explicit QtControllerSelectorDialog(QWidget* parent,
32 Core::Frontend::ControllerParameters parameters_,
33 InputCommon::InputSubsystem* input_subsystem_);
34 ~QtControllerSelectorDialog() override;
35
36private:
37 // Applies the current configuration.
38 void ApplyConfiguration();
39
40 // Loads the current input configuration into the frontend applet.
41 void LoadConfiguration();
42
43 // Initializes the "Configure Input" Dialog.
44 void CallConfigureInputDialog();
45
46 // Checks the current configuration against the given parameters and
47 // sets the value of parameters_met.
48 void CheckIfParametersMet();
49
50 // Sets the controller icons for "Supported Controller Types".
51 void SetSupportedControllers();
52
53 // Updates the controller icons per player.
54 void UpdateControllerIcon(std::size_t player_index);
55
56 // Updates the controller state (type and connection status) per player.
57 void UpdateControllerState(std::size_t player_index);
58
59 // Updates the LED pattern per player.
60 void UpdateLEDPattern(std::size_t player_index);
61
62 // Updates the border color per player.
63 void UpdateBorderColor(std::size_t player_index);
64
65 // Sets the "Explain Text" per player.
66 void SetExplainText(std::size_t player_index);
67
68 // Updates the console mode.
69 void UpdateDockedState(bool is_handheld);
70
71 // Disables and disconnects unsupported players based on the given parameters.
72 void DisableUnsupportedPlayers();
73
74 std::unique_ptr<Ui::QtControllerSelectorDialog> ui;
75
76 // Parameters sent in from the backend HLE applet.
77 Core::Frontend::ControllerParameters parameters;
78
79 InputCommon::InputSubsystem* input_subsystem;
80
81 // This is true if and only if all parameters are met. Otherwise, this is false.
82 // This determines whether the "OK" button can be clicked to exit the applet.
83 bool parameters_met{false};
84
85 static constexpr std::size_t NUM_PLAYERS = 8;
86
87 // Widgets encapsulating the groupboxes and comboboxes per player.
88 std::array<QWidget*, NUM_PLAYERS> player_widgets;
89
90 // Groupboxes encapsulating the controller icons and LED patterns per player.
91 std::array<QGroupBox*, NUM_PLAYERS> player_groupboxes;
92
93 // Icons for currently connected controllers/players.
94 std::array<QWidget*, NUM_PLAYERS> connected_controller_icons;
95
96 // Labels that represent the player numbers in place of the controller icons.
97 std::array<QLabel*, NUM_PLAYERS> player_labels;
98
99 // LED patterns for currently connected controllers/players.
100 std::array<std::array<QCheckBox*, 4>, NUM_PLAYERS> led_patterns_boxes;
101
102 // Labels representing additional information known as "Explain Text" per player.
103 std::array<QLabel*, NUM_PLAYERS> explain_text_labels;
104
105 // Comboboxes with a list of emulated controllers per player.
106 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
107
108 // Labels representing the number of connected controllers
109 // above the "Connected Controllers" checkboxes.
110 std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;
111
112 // Checkboxes representing the "Connected Controllers".
113 std::array<QCheckBox*, NUM_PLAYERS> connected_controller_checkboxes;
114};
115
116class QtControllerSelector final : public QObject, public Core::Frontend::ControllerApplet {
117 Q_OBJECT
118
119public:
120 explicit QtControllerSelector(GMainWindow& parent);
121 ~QtControllerSelector() override;
122
123 void ReconfigureControllers(std::function<void()> callback,
124 Core::Frontend::ControllerParameters parameters) const override;
125
126signals:
127 void MainWindowReconfigureControllers(Core::Frontend::ControllerParameters parameters) const;
128
129private:
130 void MainWindowReconfigureFinished();
131
132 mutable std::function<void()> callback;
133};
diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui
new file mode 100644
index 000000000..c4108a979
--- /dev/null
+++ b/src/yuzu/applets/controller.ui
@@ -0,0 +1,2672 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>QtControllerSelectorDialog</class>
4 <widget class="QDialog" name="QtControllerSelectorDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>839</width>
10 <height>630</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Controller Applet</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
20 <property name="leftMargin">
21 <number>0</number>
22 </property>
23 <property name="topMargin">
24 <number>0</number>
25 </property>
26 <property name="rightMargin">
27 <number>0</number>
28 </property>
29 <property name="bottomMargin">
30 <number>0</number>
31 </property>
32 <item>
33 <widget class="QWidget" name="mainControllerApplet" native="true">
34 <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,3,0">
35 <property name="spacing">
36 <number>0</number>
37 </property>
38 <property name="leftMargin">
39 <number>0</number>
40 </property>
41 <property name="topMargin">
42 <number>0</number>
43 </property>
44 <property name="rightMargin">
45 <number>0</number>
46 </property>
47 <property name="bottomMargin">
48 <number>0</number>
49 </property>
50 <item>
51 <widget class="QWidget" name="topControllerApplet" native="true">
52 <layout class="QHBoxLayout" name="horizontalLayout">
53 <property name="spacing">
54 <number>10</number>
55 </property>
56 <property name="leftMargin">
57 <number>0</number>
58 </property>
59 <property name="topMargin">
60 <number>10</number>
61 </property>
62 <property name="rightMargin">
63 <number>0</number>
64 </property>
65 <property name="bottomMargin">
66 <number>10</number>
67 </property>
68 <item>
69 <spacer name="controllerAppletHorizontalSpacer2">
70 <property name="orientation">
71 <enum>Qt::Horizontal</enum>
72 </property>
73 <property name="sizeHint" stdset="0">
74 <size>
75 <width>40</width>
76 <height>20</height>
77 </size>
78 </property>
79 </spacer>
80 </item>
81 <item>
82 <widget class="QWidget" name="controllersSupported" native="true">
83 <property name="minimumSize">
84 <size>
85 <width>70</width>
86 <height>70</height>
87 </size>
88 </property>
89 <property name="maximumSize">
90 <size>
91 <width>70</width>
92 <height>70</height>
93 </size>
94 </property>
95 <layout class="QVBoxLayout" name="verticalLayout_21">
96 <property name="leftMargin">
97 <number>0</number>
98 </property>
99 <property name="topMargin">
100 <number>0</number>
101 </property>
102 <property name="rightMargin">
103 <number>0</number>
104 </property>
105 <property name="bottomMargin">
106 <number>0</number>
107 </property>
108 <item>
109 <widget class="QLabel" name="controllersSupportedLabel">
110 <property name="minimumSize">
111 <size>
112 <width>70</width>
113 <height>70</height>
114 </size>
115 </property>
116 <property name="maximumSize">
117 <size>
118 <width>70</width>
119 <height>70</height>
120 </size>
121 </property>
122 <property name="font">
123 <font>
124 <weight>75</weight>
125 <bold>true</bold>
126 </font>
127 </property>
128 <property name="text">
129 <string>Supported Controller Types:</string>
130 </property>
131 <property name="alignment">
132 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
133 </property>
134 <property name="wordWrap">
135 <bool>true</bool>
136 </property>
137 </widget>
138 </item>
139 </layout>
140 </widget>
141 </item>
142 <item>
143 <widget class="QWidget" name="controllerSupported1" native="true">
144 <property name="sizePolicy">
145 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
146 <horstretch>0</horstretch>
147 <verstretch>0</verstretch>
148 </sizepolicy>
149 </property>
150 <property name="minimumSize">
151 <size>
152 <width>70</width>
153 <height>70</height>
154 </size>
155 </property>
156 <property name="maximumSize">
157 <size>
158 <width>70</width>
159 <height>70</height>
160 </size>
161 </property>
162 <property name="styleSheet">
163 <string notr="true"/>
164 </property>
165 </widget>
166 </item>
167 <item>
168 <widget class="QWidget" name="controllerSupported2" native="true">
169 <property name="minimumSize">
170 <size>
171 <width>70</width>
172 <height>70</height>
173 </size>
174 </property>
175 <property name="maximumSize">
176 <size>
177 <width>70</width>
178 <height>70</height>
179 </size>
180 </property>
181 <property name="styleSheet">
182 <string notr="true"/>
183 </property>
184 </widget>
185 </item>
186 <item>
187 <widget class="QWidget" name="controllerSupported3" native="true">
188 <property name="minimumSize">
189 <size>
190 <width>70</width>
191 <height>70</height>
192 </size>
193 </property>
194 <property name="maximumSize">
195 <size>
196 <width>70</width>
197 <height>70</height>
198 </size>
199 </property>
200 <property name="styleSheet">
201 <string notr="true"/>
202 </property>
203 </widget>
204 </item>
205 <item>
206 <widget class="QWidget" name="controllerSupported4" native="true">
207 <property name="minimumSize">
208 <size>
209 <width>70</width>
210 <height>70</height>
211 </size>
212 </property>
213 <property name="maximumSize">
214 <size>
215 <width>70</width>
216 <height>70</height>
217 </size>
218 </property>
219 <property name="styleSheet">
220 <string notr="true"/>
221 </property>
222 </widget>
223 </item>
224 <item>
225 <widget class="QWidget" name="controllerSupported5" native="true">
226 <property name="minimumSize">
227 <size>
228 <width>70</width>
229 <height>70</height>
230 </size>
231 </property>
232 <property name="maximumSize">
233 <size>
234 <width>70</width>
235 <height>70</height>
236 </size>
237 </property>
238 <property name="styleSheet">
239 <string notr="true"/>
240 </property>
241 </widget>
242 </item>
243 <item>
244 <widget class="QWidget" name="playersSupported" native="true">
245 <property name="minimumSize">
246 <size>
247 <width>70</width>
248 <height>70</height>
249 </size>
250 </property>
251 <property name="maximumSize">
252 <size>
253 <width>70</width>
254 <height>70</height>
255 </size>
256 </property>
257 <layout class="QVBoxLayout" name="verticalLayout_20">
258 <property name="spacing">
259 <number>0</number>
260 </property>
261 <property name="leftMargin">
262 <number>0</number>
263 </property>
264 <property name="topMargin">
265 <number>16</number>
266 </property>
267 <property name="rightMargin">
268 <number>14</number>
269 </property>
270 <property name="bottomMargin">
271 <number>16</number>
272 </property>
273 <item>
274 <widget class="QLabel" name="maxSupportedLabel">
275 <property name="font">
276 <font>
277 <weight>75</weight>
278 <bold>true</bold>
279 </font>
280 </property>
281 <property name="text">
282 <string>Players:</string>
283 </property>
284 <property name="alignment">
285 <set>Qt::AlignCenter</set>
286 </property>
287 <property name="wordWrap">
288 <bool>false</bool>
289 </property>
290 </widget>
291 </item>
292 <item>
293 <widget class="QLabel" name="numberSupportedLabel">
294 <property name="font">
295 <font>
296 <pointsize>14</pointsize>
297 </font>
298 </property>
299 <property name="text">
300 <string>1 - 8</string>
301 </property>
302 <property name="alignment">
303 <set>Qt::AlignCenter</set>
304 </property>
305 </widget>
306 </item>
307 </layout>
308 </widget>
309 </item>
310 <item>
311 <spacer name="controllerAppletHorizontalSpacer3">
312 <property name="orientation">
313 <enum>Qt::Horizontal</enum>
314 </property>
315 <property name="sizeHint" stdset="0">
316 <size>
317 <width>40</width>
318 <height>20</height>
319 </size>
320 </property>
321 </spacer>
322 </item>
323 </layout>
324 </widget>
325 </item>
326 <item>
327 <widget class="QWidget" name="middleControllerApplet" native="true">
328 <layout class="QVBoxLayout" name="verticalLayout_3">
329 <property name="spacing">
330 <number>0</number>
331 </property>
332 <property name="leftMargin">
333 <number>0</number>
334 </property>
335 <property name="topMargin">
336 <number>0</number>
337 </property>
338 <property name="rightMargin">
339 <number>0</number>
340 </property>
341 <property name="bottomMargin">
342 <number>0</number>
343 </property>
344 <item>
345 <layout class="QGridLayout" name="gridLayout">
346 <property name="spacing">
347 <number>5</number>
348 </property>
349 <item row="1" column="7">
350 <widget class="QWidget" name="widgetPlayer4" native="true">
351 <layout class="QVBoxLayout" name="verticalLayout_27">
352 <property name="spacing">
353 <number>5</number>
354 </property>
355 <property name="leftMargin">
356 <number>0</number>
357 </property>
358 <property name="topMargin">
359 <number>0</number>
360 </property>
361 <property name="rightMargin">
362 <number>0</number>
363 </property>
364 <property name="bottomMargin">
365 <number>0</number>
366 </property>
367 <item alignment="Qt::AlignHCenter">
368 <widget class="QGroupBox" name="groupPlayer4Connected">
369 <property name="minimumSize">
370 <size>
371 <width>100</width>
372 <height>100</height>
373 </size>
374 </property>
375 <property name="maximumSize">
376 <size>
377 <width>100</width>
378 <height>100</height>
379 </size>
380 </property>
381 <property name="title">
382 <string/>
383 </property>
384 <property name="checkable">
385 <bool>true</bool>
386 </property>
387 <property name="checked">
388 <bool>false</bool>
389 </property>
390 <layout class="QVBoxLayout" name="verticalLayout_7" stretch="1,0">
391 <property name="spacing">
392 <number>7</number>
393 </property>
394 <property name="leftMargin">
395 <number>14</number>
396 </property>
397 <property name="topMargin">
398 <number>7</number>
399 </property>
400 <property name="rightMargin">
401 <number>14</number>
402 </property>
403 <property name="bottomMargin">
404 <number>4</number>
405 </property>
406 <item>
407 <widget class="QWidget" name="controllerPlayer4" native="true">
408 <property name="styleSheet">
409 <string notr="true"/>
410 </property>
411 <layout class="QVBoxLayout" name="verticalLayout_15">
412 <property name="topMargin">
413 <number>16</number>
414 </property>
415 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
416 <widget class="QLabel" name="labelPlayer4">
417 <property name="text">
418 <string>P4</string>
419 </property>
420 </widget>
421 </item>
422 </layout>
423 </widget>
424 </item>
425 <item alignment="Qt::AlignHCenter">
426 <widget class="QWidget" name="Player4LEDs" native="true">
427 <property name="enabled">
428 <bool>false</bool>
429 </property>
430 <property name="minimumSize">
431 <size>
432 <width>0</width>
433 <height>10</height>
434 </size>
435 </property>
436 <layout class="QHBoxLayout" name="horizontalLayout_10">
437 <property name="spacing">
438 <number>4</number>
439 </property>
440 <property name="leftMargin">
441 <number>0</number>
442 </property>
443 <property name="topMargin">
444 <number>0</number>
445 </property>
446 <property name="rightMargin">
447 <number>0</number>
448 </property>
449 <property name="bottomMargin">
450 <number>0</number>
451 </property>
452 <item>
453 <widget class="QCheckBox" name="checkboxPlayer4LED1"/>
454 </item>
455 <item>
456 <widget class="QCheckBox" name="checkboxPlayer4LED2"/>
457 </item>
458 <item>
459 <widget class="QCheckBox" name="checkboxPlayer4LED3"/>
460 </item>
461 <item>
462 <widget class="QCheckBox" name="checkboxPlayer4LED4"/>
463 </item>
464 </layout>
465 </widget>
466 </item>
467 </layout>
468 </widget>
469 </item>
470 <item>
471 <widget class="QWidget" name="Player4Explain" native="true">
472 <property name="minimumSize">
473 <size>
474 <width>0</width>
475 <height>10</height>
476 </size>
477 </property>
478 <property name="maximumSize">
479 <size>
480 <width>150</width>
481 <height>16777215</height>
482 </size>
483 </property>
484 <layout class="QVBoxLayout" name="verticalLayout_39">
485 <property name="spacing">
486 <number>0</number>
487 </property>
488 <property name="leftMargin">
489 <number>0</number>
490 </property>
491 <property name="topMargin">
492 <number>0</number>
493 </property>
494 <property name="rightMargin">
495 <number>0</number>
496 </property>
497 <property name="bottomMargin">
498 <number>0</number>
499 </property>
500 <item>
501 <widget class="QLabel" name="labelPlayer4Explain">
502 <property name="alignment">
503 <set>Qt::AlignCenter</set>
504 </property>
505 </widget>
506 </item>
507 </layout>
508 </widget>
509 </item>
510 <item>
511 <widget class="QComboBox" name="comboPlayer4Emulated">
512 <item>
513 <property name="text">
514 <string>Pro Controller</string>
515 </property>
516 </item>
517 <item>
518 <property name="text">
519 <string>Dual Joycons</string>
520 </property>
521 </item>
522 <item>
523 <property name="text">
524 <string>Left Joycon</string>
525 </property>
526 </item>
527 <item>
528 <property name="text">
529 <string>Right Joycon</string>
530 </property>
531 </item>
532 </widget>
533 </item>
534 <item>
535 <widget class="QComboBox" name="comboPlayer4Profile">
536 <item>
537 <property name="text">
538 <string>Use Current Config</string>
539 </property>
540 </item>
541 </widget>
542 </item>
543 </layout>
544 </widget>
545 </item>
546 <item row="1" column="3">
547 <widget class="QWidget" name="widgetPlayer2" native="true">
548 <layout class="QVBoxLayout" name="verticalLayout_29">
549 <property name="spacing">
550 <number>5</number>
551 </property>
552 <property name="leftMargin">
553 <number>0</number>
554 </property>
555 <property name="topMargin">
556 <number>0</number>
557 </property>
558 <property name="rightMargin">
559 <number>0</number>
560 </property>
561 <property name="bottomMargin">
562 <number>0</number>
563 </property>
564 <item alignment="Qt::AlignHCenter">
565 <widget class="QGroupBox" name="groupPlayer2Connected">
566 <property name="minimumSize">
567 <size>
568 <width>100</width>
569 <height>100</height>
570 </size>
571 </property>
572 <property name="maximumSize">
573 <size>
574 <width>100</width>
575 <height>100</height>
576 </size>
577 </property>
578 <property name="title">
579 <string/>
580 </property>
581 <property name="checkable">
582 <bool>true</bool>
583 </property>
584 <property name="checked">
585 <bool>false</bool>
586 </property>
587 <layout class="QVBoxLayout" name="verticalLayout_5" stretch="1,0">
588 <property name="spacing">
589 <number>7</number>
590 </property>
591 <property name="leftMargin">
592 <number>14</number>
593 </property>
594 <property name="topMargin">
595 <number>7</number>
596 </property>
597 <property name="rightMargin">
598 <number>14</number>
599 </property>
600 <property name="bottomMargin">
601 <number>4</number>
602 </property>
603 <item>
604 <widget class="QWidget" name="controllerPlayer2" native="true">
605 <property name="styleSheet">
606 <string notr="true"/>
607 </property>
608 <layout class="QVBoxLayout" name="verticalLayout_13">
609 <property name="topMargin">
610 <number>16</number>
611 </property>
612 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
613 <widget class="QLabel" name="labelPlayer2">
614 <property name="text">
615 <string>P2</string>
616 </property>
617 </widget>
618 </item>
619 </layout>
620 </widget>
621 </item>
622 <item alignment="Qt::AlignHCenter">
623 <widget class="QWidget" name="Player2LEDs" native="true">
624 <property name="enabled">
625 <bool>false</bool>
626 </property>
627 <property name="minimumSize">
628 <size>
629 <width>0</width>
630 <height>10</height>
631 </size>
632 </property>
633 <layout class="QHBoxLayout" name="horizontalLayout_8">
634 <property name="spacing">
635 <number>4</number>
636 </property>
637 <property name="leftMargin">
638 <number>0</number>
639 </property>
640 <property name="topMargin">
641 <number>0</number>
642 </property>
643 <property name="rightMargin">
644 <number>0</number>
645 </property>
646 <property name="bottomMargin">
647 <number>0</number>
648 </property>
649 <item>
650 <widget class="QCheckBox" name="checkboxPlayer2LED1"/>
651 </item>
652 <item>
653 <widget class="QCheckBox" name="checkboxPlayer2LED2"/>
654 </item>
655 <item>
656 <widget class="QCheckBox" name="checkboxPlayer2LED3"/>
657 </item>
658 <item>
659 <widget class="QCheckBox" name="checkboxPlayer2LED4"/>
660 </item>
661 </layout>
662 </widget>
663 </item>
664 </layout>
665 </widget>
666 </item>
667 <item>
668 <widget class="QWidget" name="Player2Explain" native="true">
669 <property name="minimumSize">
670 <size>
671 <width>0</width>
672 <height>10</height>
673 </size>
674 </property>
675 <property name="maximumSize">
676 <size>
677 <width>150</width>
678 <height>16777215</height>
679 </size>
680 </property>
681 <layout class="QVBoxLayout" name="verticalLayout_37">
682 <property name="spacing">
683 <number>0</number>
684 </property>
685 <property name="leftMargin">
686 <number>0</number>
687 </property>
688 <property name="topMargin">
689 <number>0</number>
690 </property>
691 <property name="rightMargin">
692 <number>0</number>
693 </property>
694 <property name="bottomMargin">
695 <number>0</number>
696 </property>
697 <item>
698 <widget class="QLabel" name="labelPlayer2Explain">
699 <property name="alignment">
700 <set>Qt::AlignCenter</set>
701 </property>
702 </widget>
703 </item>
704 </layout>
705 </widget>
706 </item>
707 <item>
708 <widget class="QComboBox" name="comboPlayer2Emulated">
709 <item>
710 <property name="text">
711 <string>Pro Controller</string>
712 </property>
713 </item>
714 <item>
715 <property name="text">
716 <string>Dual Joycons</string>
717 </property>
718 </item>
719 <item>
720 <property name="text">
721 <string>Left Joycon</string>
722 </property>
723 </item>
724 <item>
725 <property name="text">
726 <string>Right Joycon</string>
727 </property>
728 </item>
729 </widget>
730 </item>
731 <item>
732 <widget class="QComboBox" name="comboPlayer2Profile">
733 <item>
734 <property name="text">
735 <string>Use Current Config</string>
736 </property>
737 </item>
738 </widget>
739 </item>
740 </layout>
741 </widget>
742 </item>
743 <item row="1" column="1">
744 <widget class="QWidget" name="widgetPlayer1" native="true">
745 <layout class="QVBoxLayout" name="verticalLayout_30">
746 <property name="spacing">
747 <number>5</number>
748 </property>
749 <property name="leftMargin">
750 <number>0</number>
751 </property>
752 <property name="topMargin">
753 <number>0</number>
754 </property>
755 <property name="rightMargin">
756 <number>0</number>
757 </property>
758 <property name="bottomMargin">
759 <number>0</number>
760 </property>
761 <item alignment="Qt::AlignHCenter">
762 <widget class="QGroupBox" name="groupPlayer1Connected">
763 <property name="minimumSize">
764 <size>
765 <width>100</width>
766 <height>100</height>
767 </size>
768 </property>
769 <property name="maximumSize">
770 <size>
771 <width>100</width>
772 <height>100</height>
773 </size>
774 </property>
775 <property name="title">
776 <string/>
777 </property>
778 <property name="checkable">
779 <bool>true</bool>
780 </property>
781 <property name="checked">
782 <bool>false</bool>
783 </property>
784 <layout class="QVBoxLayout" name="verticalLayout_4" stretch="1,0">
785 <property name="spacing">
786 <number>7</number>
787 </property>
788 <property name="leftMargin">
789 <number>14</number>
790 </property>
791 <property name="topMargin">
792 <number>7</number>
793 </property>
794 <property name="rightMargin">
795 <number>14</number>
796 </property>
797 <property name="bottomMargin">
798 <number>4</number>
799 </property>
800 <item>
801 <widget class="QWidget" name="controllerPlayer1" native="true">
802 <property name="styleSheet">
803 <string notr="true"/>
804 </property>
805 <layout class="QVBoxLayout" name="verticalLayout_12">
806 <property name="topMargin">
807 <number>16</number>
808 </property>
809 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
810 <widget class="QLabel" name="labelPlayer1">
811 <property name="text">
812 <string>P1</string>
813 </property>
814 </widget>
815 </item>
816 </layout>
817 </widget>
818 </item>
819 <item alignment="Qt::AlignHCenter">
820 <widget class="QWidget" name="Player1LEDs" native="true">
821 <property name="enabled">
822 <bool>false</bool>
823 </property>
824 <property name="minimumSize">
825 <size>
826 <width>0</width>
827 <height>10</height>
828 </size>
829 </property>
830 <layout class="QHBoxLayout" name="horizontalLayout_2">
831 <property name="spacing">
832 <number>4</number>
833 </property>
834 <property name="leftMargin">
835 <number>0</number>
836 </property>
837 <property name="topMargin">
838 <number>0</number>
839 </property>
840 <property name="rightMargin">
841 <number>0</number>
842 </property>
843 <property name="bottomMargin">
844 <number>0</number>
845 </property>
846 <item>
847 <widget class="QCheckBox" name="checkboxPlayer1LED1">
848 <property name="layoutDirection">
849 <enum>Qt::LeftToRight</enum>
850 </property>
851 </widget>
852 </item>
853 <item>
854 <widget class="QCheckBox" name="checkboxPlayer1LED2"/>
855 </item>
856 <item>
857 <widget class="QCheckBox" name="checkboxPlayer1LED3"/>
858 </item>
859 <item>
860 <widget class="QCheckBox" name="checkboxPlayer1LED4"/>
861 </item>
862 </layout>
863 </widget>
864 </item>
865 </layout>
866 </widget>
867 </item>
868 <item>
869 <widget class="QWidget" name="Player1Explain" native="true">
870 <property name="minimumSize">
871 <size>
872 <width>0</width>
873 <height>10</height>
874 </size>
875 </property>
876 <property name="maximumSize">
877 <size>
878 <width>150</width>
879 <height>16777215</height>
880 </size>
881 </property>
882 <layout class="QVBoxLayout" name="verticalLayout_36">
883 <property name="spacing">
884 <number>0</number>
885 </property>
886 <property name="leftMargin">
887 <number>0</number>
888 </property>
889 <property name="topMargin">
890 <number>0</number>
891 </property>
892 <property name="rightMargin">
893 <number>0</number>
894 </property>
895 <property name="bottomMargin">
896 <number>0</number>
897 </property>
898 <item>
899 <widget class="QLabel" name="labelPlayer1Explain">
900 <property name="alignment">
901 <set>Qt::AlignCenter</set>
902 </property>
903 </widget>
904 </item>
905 </layout>
906 </widget>
907 </item>
908 <item>
909 <widget class="QComboBox" name="comboPlayer1Emulated">
910 <item>
911 <property name="text">
912 <string>Pro Controller</string>
913 </property>
914 </item>
915 <item>
916 <property name="text">
917 <string>Dual Joycons</string>
918 </property>
919 </item>
920 <item>
921 <property name="text">
922 <string>Left Joycon</string>
923 </property>
924 </item>
925 <item>
926 <property name="text">
927 <string>Right Joycon</string>
928 </property>
929 </item>
930 <item>
931 <property name="text">
932 <string>Handheld</string>
933 </property>
934 </item>
935 </widget>
936 </item>
937 <item>
938 <widget class="QComboBox" name="comboPlayer1Profile">
939 <item>
940 <property name="text">
941 <string>Use Current Config</string>
942 </property>
943 </item>
944 </widget>
945 </item>
946 </layout>
947 </widget>
948 </item>
949 <item row="1" column="8">
950 <widget class="QWidget" name="widgetSpacer2" native="true">
951 <property name="minimumSize">
952 <size>
953 <width>25</width>
954 <height>0</height>
955 </size>
956 </property>
957 <layout class="QVBoxLayout" name="verticalLayout_31">
958 <property name="spacing">
959 <number>0</number>
960 </property>
961 <property name="leftMargin">
962 <number>0</number>
963 </property>
964 <property name="topMargin">
965 <number>0</number>
966 </property>
967 <property name="rightMargin">
968 <number>0</number>
969 </property>
970 <property name="bottomMargin">
971 <number>0</number>
972 </property>
973 <item>
974 <spacer name="controllerAppletHorizontalSpacer8">
975 <property name="orientation">
976 <enum>Qt::Horizontal</enum>
977 </property>
978 <property name="sizeHint" stdset="0">
979 <size>
980 <width>25</width>
981 <height>20</height>
982 </size>
983 </property>
984 </spacer>
985 </item>
986 </layout>
987 </widget>
988 </item>
989 <item row="1" column="4">
990 <widget class="QWidget" name="widgetSpacer4" native="true">
991 <layout class="QVBoxLayout" name="verticalLayout_33">
992 <property name="spacing">
993 <number>0</number>
994 </property>
995 <property name="leftMargin">
996 <number>0</number>
997 </property>
998 <property name="topMargin">
999 <number>0</number>
1000 </property>
1001 <property name="rightMargin">
1002 <number>0</number>
1003 </property>
1004 <property name="bottomMargin">
1005 <number>0</number>
1006 </property>
1007 <item>
1008 <spacer name="controllerAppletHorizontalSpacer6">
1009 <property name="orientation">
1010 <enum>Qt::Horizontal</enum>
1011 </property>
1012 <property name="sizeHint" stdset="0">
1013 <size>
1014 <width>0</width>
1015 <height>20</height>
1016 </size>
1017 </property>
1018 </spacer>
1019 </item>
1020 </layout>
1021 </widget>
1022 </item>
1023 <item row="1" column="6">
1024 <widget class="QWidget" name="widgetSpacer3" native="true">
1025 <layout class="QVBoxLayout" name="verticalLayout_32">
1026 <property name="spacing">
1027 <number>0</number>
1028 </property>
1029 <property name="leftMargin">
1030 <number>0</number>
1031 </property>
1032 <property name="topMargin">
1033 <number>0</number>
1034 </property>
1035 <property name="rightMargin">
1036 <number>0</number>
1037 </property>
1038 <property name="bottomMargin">
1039 <number>0</number>
1040 </property>
1041 <item>
1042 <spacer name="controllerAppletHorizontalSpacer7">
1043 <property name="orientation">
1044 <enum>Qt::Horizontal</enum>
1045 </property>
1046 <property name="sizeHint" stdset="0">
1047 <size>
1048 <width>0</width>
1049 <height>20</height>
1050 </size>
1051 </property>
1052 </spacer>
1053 </item>
1054 </layout>
1055 </widget>
1056 </item>
1057 <item row="1" column="5">
1058 <widget class="QWidget" name="widgetPlayer3" native="true">
1059 <layout class="QVBoxLayout" name="verticalLayout_28">
1060 <property name="spacing">
1061 <number>5</number>
1062 </property>
1063 <property name="leftMargin">
1064 <number>0</number>
1065 </property>
1066 <property name="topMargin">
1067 <number>0</number>
1068 </property>
1069 <property name="rightMargin">
1070 <number>0</number>
1071 </property>
1072 <property name="bottomMargin">
1073 <number>0</number>
1074 </property>
1075 <item alignment="Qt::AlignHCenter">
1076 <widget class="QGroupBox" name="groupPlayer3Connected">
1077 <property name="minimumSize">
1078 <size>
1079 <width>100</width>
1080 <height>100</height>
1081 </size>
1082 </property>
1083 <property name="maximumSize">
1084 <size>
1085 <width>100</width>
1086 <height>100</height>
1087 </size>
1088 </property>
1089 <property name="title">
1090 <string/>
1091 </property>
1092 <property name="checkable">
1093 <bool>true</bool>
1094 </property>
1095 <property name="checked">
1096 <bool>false</bool>
1097 </property>
1098 <layout class="QVBoxLayout" name="verticalLayout_6" stretch="1,0">
1099 <property name="spacing">
1100 <number>7</number>
1101 </property>
1102 <property name="leftMargin">
1103 <number>14</number>
1104 </property>
1105 <property name="topMargin">
1106 <number>7</number>
1107 </property>
1108 <property name="rightMargin">
1109 <number>14</number>
1110 </property>
1111 <property name="bottomMargin">
1112 <number>4</number>
1113 </property>
1114 <item>
1115 <widget class="QWidget" name="controllerPlayer3" native="true">
1116 <property name="styleSheet">
1117 <string notr="true"/>
1118 </property>
1119 <layout class="QVBoxLayout" name="verticalLayout_14">
1120 <property name="topMargin">
1121 <number>16</number>
1122 </property>
1123 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1124 <widget class="QLabel" name="labelPlayer3">
1125 <property name="text">
1126 <string>P3</string>
1127 </property>
1128 </widget>
1129 </item>
1130 </layout>
1131 </widget>
1132 </item>
1133 <item alignment="Qt::AlignHCenter">
1134 <widget class="QWidget" name="Player3LEDs" native="true">
1135 <property name="enabled">
1136 <bool>false</bool>
1137 </property>
1138 <property name="minimumSize">
1139 <size>
1140 <width>0</width>
1141 <height>10</height>
1142 </size>
1143 </property>
1144 <layout class="QHBoxLayout" name="horizontalLayout_9">
1145 <property name="spacing">
1146 <number>4</number>
1147 </property>
1148 <property name="leftMargin">
1149 <number>0</number>
1150 </property>
1151 <property name="topMargin">
1152 <number>0</number>
1153 </property>
1154 <property name="rightMargin">
1155 <number>0</number>
1156 </property>
1157 <property name="bottomMargin">
1158 <number>0</number>
1159 </property>
1160 <item>
1161 <widget class="QCheckBox" name="checkboxPlayer3LED1"/>
1162 </item>
1163 <item>
1164 <widget class="QCheckBox" name="checkboxPlayer3LED2"/>
1165 </item>
1166 <item>
1167 <widget class="QCheckBox" name="checkboxPlayer3LED3"/>
1168 </item>
1169 <item>
1170 <widget class="QCheckBox" name="checkboxPlayer3LED4"/>
1171 </item>
1172 </layout>
1173 </widget>
1174 </item>
1175 </layout>
1176 </widget>
1177 </item>
1178 <item>
1179 <widget class="QWidget" name="Player3Explain" native="true">
1180 <property name="minimumSize">
1181 <size>
1182 <width>0</width>
1183 <height>10</height>
1184 </size>
1185 </property>
1186 <property name="maximumSize">
1187 <size>
1188 <width>150</width>
1189 <height>16777215</height>
1190 </size>
1191 </property>
1192 <layout class="QVBoxLayout" name="verticalLayout_38">
1193 <property name="spacing">
1194 <number>0</number>
1195 </property>
1196 <property name="leftMargin">
1197 <number>0</number>
1198 </property>
1199 <property name="topMargin">
1200 <number>0</number>
1201 </property>
1202 <property name="rightMargin">
1203 <number>0</number>
1204 </property>
1205 <property name="bottomMargin">
1206 <number>0</number>
1207 </property>
1208 <item>
1209 <widget class="QLabel" name="labelPlayer3Explain">
1210 <property name="alignment">
1211 <set>Qt::AlignCenter</set>
1212 </property>
1213 </widget>
1214 </item>
1215 </layout>
1216 </widget>
1217 </item>
1218 <item>
1219 <widget class="QComboBox" name="comboPlayer3Emulated">
1220 <property name="editable">
1221 <bool>false</bool>
1222 </property>
1223 <item>
1224 <property name="text">
1225 <string>Pro Controller</string>
1226 </property>
1227 </item>
1228 <item>
1229 <property name="text">
1230 <string>Dual Joycons</string>
1231 </property>
1232 </item>
1233 <item>
1234 <property name="text">
1235 <string>Left Joycon</string>
1236 </property>
1237 </item>
1238 <item>
1239 <property name="text">
1240 <string>Right Joycon</string>
1241 </property>
1242 </item>
1243 </widget>
1244 </item>
1245 <item>
1246 <widget class="QComboBox" name="comboPlayer3Profile">
1247 <item>
1248 <property name="text">
1249 <string>Use Current Config</string>
1250 </property>
1251 </item>
1252 </widget>
1253 </item>
1254 </layout>
1255 </widget>
1256 </item>
1257 <item row="0" column="1">
1258 <widget class="QWidget" name="widgetSpacer5" native="true">
1259 <property name="minimumSize">
1260 <size>
1261 <width>0</width>
1262 <height>25</height>
1263 </size>
1264 </property>
1265 <layout class="QVBoxLayout" name="verticalLayout_34">
1266 <property name="spacing">
1267 <number>0</number>
1268 </property>
1269 <property name="leftMargin">
1270 <number>0</number>
1271 </property>
1272 <property name="topMargin">
1273 <number>0</number>
1274 </property>
1275 <property name="rightMargin">
1276 <number>0</number>
1277 </property>
1278 <property name="bottomMargin">
1279 <number>0</number>
1280 </property>
1281 <item>
1282 <spacer name="controllerAppletVerticalSpacer3">
1283 <property name="orientation">
1284 <enum>Qt::Vertical</enum>
1285 </property>
1286 <property name="sizeHint" stdset="0">
1287 <size>
1288 <width>20</width>
1289 <height>25</height>
1290 </size>
1291 </property>
1292 </spacer>
1293 </item>
1294 </layout>
1295 </widget>
1296 </item>
1297 <item row="6" column="5">
1298 <widget class="QWidget" name="widgetPlayer7" native="true">
1299 <layout class="QVBoxLayout" name="verticalLayout_25">
1300 <property name="spacing">
1301 <number>5</number>
1302 </property>
1303 <property name="leftMargin">
1304 <number>0</number>
1305 </property>
1306 <property name="topMargin">
1307 <number>0</number>
1308 </property>
1309 <property name="rightMargin">
1310 <number>0</number>
1311 </property>
1312 <property name="bottomMargin">
1313 <number>0</number>
1314 </property>
1315 <item alignment="Qt::AlignHCenter">
1316 <widget class="QGroupBox" name="groupPlayer7Connected">
1317 <property name="minimumSize">
1318 <size>
1319 <width>100</width>
1320 <height>100</height>
1321 </size>
1322 </property>
1323 <property name="maximumSize">
1324 <size>
1325 <width>100</width>
1326 <height>100</height>
1327 </size>
1328 </property>
1329 <property name="title">
1330 <string/>
1331 </property>
1332 <property name="checkable">
1333 <bool>true</bool>
1334 </property>
1335 <property name="checked">
1336 <bool>false</bool>
1337 </property>
1338 <layout class="QVBoxLayout" name="verticalLayout_10" stretch="1,0">
1339 <property name="spacing">
1340 <number>7</number>
1341 </property>
1342 <property name="leftMargin">
1343 <number>14</number>
1344 </property>
1345 <property name="topMargin">
1346 <number>7</number>
1347 </property>
1348 <property name="rightMargin">
1349 <number>14</number>
1350 </property>
1351 <property name="bottomMargin">
1352 <number>4</number>
1353 </property>
1354 <item>
1355 <widget class="QWidget" name="controllerPlayer7" native="true">
1356 <property name="styleSheet">
1357 <string notr="true"/>
1358 </property>
1359 <layout class="QVBoxLayout" name="verticalLayout_18">
1360 <property name="topMargin">
1361 <number>16</number>
1362 </property>
1363 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1364 <widget class="QLabel" name="labelPlayer7">
1365 <property name="text">
1366 <string>P7</string>
1367 </property>
1368 </widget>
1369 </item>
1370 </layout>
1371 </widget>
1372 </item>
1373 <item alignment="Qt::AlignHCenter">
1374 <widget class="QWidget" name="Player7LEDs" native="true">
1375 <property name="enabled">
1376 <bool>false</bool>
1377 </property>
1378 <property name="minimumSize">
1379 <size>
1380 <width>0</width>
1381 <height>10</height>
1382 </size>
1383 </property>
1384 <layout class="QHBoxLayout" name="horizontalLayout_13">
1385 <property name="spacing">
1386 <number>4</number>
1387 </property>
1388 <property name="leftMargin">
1389 <number>0</number>
1390 </property>
1391 <property name="topMargin">
1392 <number>0</number>
1393 </property>
1394 <property name="rightMargin">
1395 <number>0</number>
1396 </property>
1397 <property name="bottomMargin">
1398 <number>0</number>
1399 </property>
1400 <item>
1401 <widget class="QCheckBox" name="checkboxPlayer7LED1"/>
1402 </item>
1403 <item>
1404 <widget class="QCheckBox" name="checkboxPlayer7LED2"/>
1405 </item>
1406 <item>
1407 <widget class="QCheckBox" name="checkboxPlayer7LED3"/>
1408 </item>
1409 <item>
1410 <widget class="QCheckBox" name="checkboxPlayer7LED4"/>
1411 </item>
1412 </layout>
1413 </widget>
1414 </item>
1415 </layout>
1416 </widget>
1417 </item>
1418 <item>
1419 <widget class="QWidget" name="Player7Explain" native="true">
1420 <property name="minimumSize">
1421 <size>
1422 <width>0</width>
1423 <height>10</height>
1424 </size>
1425 </property>
1426 <property name="maximumSize">
1427 <size>
1428 <width>150</width>
1429 <height>16777215</height>
1430 </size>
1431 </property>
1432 <layout class="QVBoxLayout" name="verticalLayout_42">
1433 <property name="spacing">
1434 <number>0</number>
1435 </property>
1436 <property name="leftMargin">
1437 <number>0</number>
1438 </property>
1439 <property name="topMargin">
1440 <number>0</number>
1441 </property>
1442 <property name="rightMargin">
1443 <number>0</number>
1444 </property>
1445 <property name="bottomMargin">
1446 <number>0</number>
1447 </property>
1448 <item>
1449 <widget class="QLabel" name="labelPlayer7Explain">
1450 <property name="alignment">
1451 <set>Qt::AlignCenter</set>
1452 </property>
1453 </widget>
1454 </item>
1455 </layout>
1456 </widget>
1457 </item>
1458 <item>
1459 <widget class="QComboBox" name="comboPlayer7Emulated">
1460 <item>
1461 <property name="text">
1462 <string>Pro Controller</string>
1463 </property>
1464 </item>
1465 <item>
1466 <property name="text">
1467 <string>Dual Joycons</string>
1468 </property>
1469 </item>
1470 <item>
1471 <property name="text">
1472 <string>Left Joycon</string>
1473 </property>
1474 </item>
1475 <item>
1476 <property name="text">
1477 <string>Right Joycon</string>
1478 </property>
1479 </item>
1480 </widget>
1481 </item>
1482 <item>
1483 <widget class="QComboBox" name="comboPlayer7Profile">
1484 <item>
1485 <property name="text">
1486 <string>Use Current Config</string>
1487 </property>
1488 </item>
1489 </widget>
1490 </item>
1491 </layout>
1492 </widget>
1493 </item>
1494 <item row="6" column="7">
1495 <widget class="QWidget" name="widgetPlayer8" native="true">
1496 <layout class="QVBoxLayout" name="verticalLayout_26">
1497 <property name="spacing">
1498 <number>5</number>
1499 </property>
1500 <property name="leftMargin">
1501 <number>0</number>
1502 </property>
1503 <property name="topMargin">
1504 <number>0</number>
1505 </property>
1506 <property name="rightMargin">
1507 <number>0</number>
1508 </property>
1509 <property name="bottomMargin">
1510 <number>0</number>
1511 </property>
1512 <item alignment="Qt::AlignHCenter">
1513 <widget class="QGroupBox" name="groupPlayer8Connected">
1514 <property name="minimumSize">
1515 <size>
1516 <width>100</width>
1517 <height>100</height>
1518 </size>
1519 </property>
1520 <property name="maximumSize">
1521 <size>
1522 <width>100</width>
1523 <height>100</height>
1524 </size>
1525 </property>
1526 <property name="title">
1527 <string/>
1528 </property>
1529 <property name="checkable">
1530 <bool>true</bool>
1531 </property>
1532 <property name="checked">
1533 <bool>false</bool>
1534 </property>
1535 <layout class="QVBoxLayout" name="verticalLayout_11" stretch="1,0">
1536 <property name="spacing">
1537 <number>7</number>
1538 </property>
1539 <property name="leftMargin">
1540 <number>14</number>
1541 </property>
1542 <property name="topMargin">
1543 <number>7</number>
1544 </property>
1545 <property name="rightMargin">
1546 <number>14</number>
1547 </property>
1548 <property name="bottomMargin">
1549 <number>4</number>
1550 </property>
1551 <item>
1552 <widget class="QWidget" name="controllerPlayer8" native="true">
1553 <property name="styleSheet">
1554 <string notr="true"/>
1555 </property>
1556 <layout class="QVBoxLayout" name="verticalLayout_19">
1557 <property name="topMargin">
1558 <number>16</number>
1559 </property>
1560 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1561 <widget class="QLabel" name="labelPlayer8">
1562 <property name="text">
1563 <string>P8</string>
1564 </property>
1565 </widget>
1566 </item>
1567 </layout>
1568 </widget>
1569 </item>
1570 <item alignment="Qt::AlignHCenter">
1571 <widget class="QWidget" name="Player8LEDs" native="true">
1572 <property name="enabled">
1573 <bool>false</bool>
1574 </property>
1575 <property name="minimumSize">
1576 <size>
1577 <width>0</width>
1578 <height>10</height>
1579 </size>
1580 </property>
1581 <layout class="QHBoxLayout" name="horizontalLayout_14">
1582 <property name="spacing">
1583 <number>4</number>
1584 </property>
1585 <property name="leftMargin">
1586 <number>0</number>
1587 </property>
1588 <property name="topMargin">
1589 <number>0</number>
1590 </property>
1591 <property name="rightMargin">
1592 <number>0</number>
1593 </property>
1594 <property name="bottomMargin">
1595 <number>0</number>
1596 </property>
1597 <item>
1598 <widget class="QCheckBox" name="checkboxPlayer8LED1"/>
1599 </item>
1600 <item>
1601 <widget class="QCheckBox" name="checkboxPlayer8LED2"/>
1602 </item>
1603 <item>
1604 <widget class="QCheckBox" name="checkboxPlayer8LED3"/>
1605 </item>
1606 <item>
1607 <widget class="QCheckBox" name="checkboxPlayer8LED4"/>
1608 </item>
1609 </layout>
1610 </widget>
1611 </item>
1612 </layout>
1613 </widget>
1614 </item>
1615 <item>
1616 <widget class="QWidget" name="Player8Explain" native="true">
1617 <property name="minimumSize">
1618 <size>
1619 <width>0</width>
1620 <height>10</height>
1621 </size>
1622 </property>
1623 <property name="maximumSize">
1624 <size>
1625 <width>150</width>
1626 <height>16777215</height>
1627 </size>
1628 </property>
1629 <layout class="QVBoxLayout" name="verticalLayout_35">
1630 <property name="spacing">
1631 <number>0</number>
1632 </property>
1633 <property name="leftMargin">
1634 <number>0</number>
1635 </property>
1636 <property name="topMargin">
1637 <number>0</number>
1638 </property>
1639 <property name="rightMargin">
1640 <number>0</number>
1641 </property>
1642 <property name="bottomMargin">
1643 <number>0</number>
1644 </property>
1645 <item>
1646 <widget class="QLabel" name="labelPlayer8Explain">
1647 <property name="alignment">
1648 <set>Qt::AlignCenter</set>
1649 </property>
1650 </widget>
1651 </item>
1652 </layout>
1653 </widget>
1654 </item>
1655 <item>
1656 <widget class="QComboBox" name="comboPlayer8Emulated">
1657 <item>
1658 <property name="text">
1659 <string>Pro Controller</string>
1660 </property>
1661 </item>
1662 <item>
1663 <property name="text">
1664 <string>Dual Joycons</string>
1665 </property>
1666 </item>
1667 <item>
1668 <property name="text">
1669 <string>Left Joycon</string>
1670 </property>
1671 </item>
1672 <item>
1673 <property name="text">
1674 <string>Right Joycon</string>
1675 </property>
1676 </item>
1677 </widget>
1678 </item>
1679 <item>
1680 <widget class="QComboBox" name="comboPlayer8Profile">
1681 <item>
1682 <property name="text">
1683 <string>Use Current Config</string>
1684 </property>
1685 </item>
1686 </widget>
1687 </item>
1688 </layout>
1689 </widget>
1690 </item>
1691 <item row="6" column="1">
1692 <widget class="QWidget" name="widgetPlayer5" native="true">
1693 <layout class="QVBoxLayout" name="verticalLayout_23">
1694 <property name="spacing">
1695 <number>5</number>
1696 </property>
1697 <property name="leftMargin">
1698 <number>0</number>
1699 </property>
1700 <property name="topMargin">
1701 <number>0</number>
1702 </property>
1703 <property name="rightMargin">
1704 <number>0</number>
1705 </property>
1706 <property name="bottomMargin">
1707 <number>0</number>
1708 </property>
1709 <item alignment="Qt::AlignHCenter">
1710 <widget class="QGroupBox" name="groupPlayer5Connected">
1711 <property name="minimumSize">
1712 <size>
1713 <width>100</width>
1714 <height>100</height>
1715 </size>
1716 </property>
1717 <property name="maximumSize">
1718 <size>
1719 <width>100</width>
1720 <height>100</height>
1721 </size>
1722 </property>
1723 <property name="title">
1724 <string/>
1725 </property>
1726 <property name="checkable">
1727 <bool>true</bool>
1728 </property>
1729 <property name="checked">
1730 <bool>false</bool>
1731 </property>
1732 <layout class="QVBoxLayout" name="verticalLayout_8" stretch="1,0">
1733 <property name="spacing">
1734 <number>7</number>
1735 </property>
1736 <property name="leftMargin">
1737 <number>14</number>
1738 </property>
1739 <property name="topMargin">
1740 <number>7</number>
1741 </property>
1742 <property name="rightMargin">
1743 <number>14</number>
1744 </property>
1745 <property name="bottomMargin">
1746 <number>4</number>
1747 </property>
1748 <item>
1749 <widget class="QWidget" name="controllerPlayer5" native="true">
1750 <property name="styleSheet">
1751 <string notr="true"/>
1752 </property>
1753 <layout class="QVBoxLayout" name="verticalLayout_16">
1754 <property name="topMargin">
1755 <number>16</number>
1756 </property>
1757 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1758 <widget class="QLabel" name="labelPlayer5">
1759 <property name="text">
1760 <string>P5</string>
1761 </property>
1762 </widget>
1763 </item>
1764 </layout>
1765 </widget>
1766 </item>
1767 <item alignment="Qt::AlignHCenter">
1768 <widget class="QWidget" name="Player5LEDs" native="true">
1769 <property name="enabled">
1770 <bool>false</bool>
1771 </property>
1772 <property name="minimumSize">
1773 <size>
1774 <width>0</width>
1775 <height>10</height>
1776 </size>
1777 </property>
1778 <layout class="QHBoxLayout" name="horizontalLayout_11">
1779 <property name="spacing">
1780 <number>4</number>
1781 </property>
1782 <property name="leftMargin">
1783 <number>0</number>
1784 </property>
1785 <property name="topMargin">
1786 <number>0</number>
1787 </property>
1788 <property name="rightMargin">
1789 <number>0</number>
1790 </property>
1791 <property name="bottomMargin">
1792 <number>0</number>
1793 </property>
1794 <item>
1795 <widget class="QCheckBox" name="checkboxPlayer5LED1">
1796 <property name="layoutDirection">
1797 <enum>Qt::LeftToRight</enum>
1798 </property>
1799 </widget>
1800 </item>
1801 <item>
1802 <widget class="QCheckBox" name="checkboxPlayer5LED2"/>
1803 </item>
1804 <item>
1805 <widget class="QCheckBox" name="checkboxPlayer5LED3"/>
1806 </item>
1807 <item>
1808 <widget class="QCheckBox" name="checkboxPlayer5LED4"/>
1809 </item>
1810 </layout>
1811 </widget>
1812 </item>
1813 </layout>
1814 </widget>
1815 </item>
1816 <item>
1817 <widget class="QWidget" name="Player5Explain" native="true">
1818 <property name="minimumSize">
1819 <size>
1820 <width>0</width>
1821 <height>10</height>
1822 </size>
1823 </property>
1824 <property name="maximumSize">
1825 <size>
1826 <width>150</width>
1827 <height>16777215</height>
1828 </size>
1829 </property>
1830 <layout class="QVBoxLayout" name="verticalLayout_40">
1831 <property name="spacing">
1832 <number>0</number>
1833 </property>
1834 <property name="leftMargin">
1835 <number>0</number>
1836 </property>
1837 <property name="topMargin">
1838 <number>0</number>
1839 </property>
1840 <property name="rightMargin">
1841 <number>0</number>
1842 </property>
1843 <property name="bottomMargin">
1844 <number>0</number>
1845 </property>
1846 <item>
1847 <widget class="QLabel" name="labelPlayer5Explain">
1848 <property name="alignment">
1849 <set>Qt::AlignCenter</set>
1850 </property>
1851 </widget>
1852 </item>
1853 </layout>
1854 </widget>
1855 </item>
1856 <item>
1857 <widget class="QComboBox" name="comboPlayer5Emulated">
1858 <item>
1859 <property name="text">
1860 <string>Pro Controller</string>
1861 </property>
1862 </item>
1863 <item>
1864 <property name="text">
1865 <string>Dual Joycons</string>
1866 </property>
1867 </item>
1868 <item>
1869 <property name="text">
1870 <string>Left Joycon</string>
1871 </property>
1872 </item>
1873 <item>
1874 <property name="text">
1875 <string>Right Joycon</string>
1876 </property>
1877 </item>
1878 </widget>
1879 </item>
1880 <item>
1881 <widget class="QComboBox" name="comboPlayer5Profile">
1882 <item>
1883 <property name="text">
1884 <string>Use Current Config</string>
1885 </property>
1886 </item>
1887 </widget>
1888 </item>
1889 </layout>
1890 </widget>
1891 </item>
1892 <item row="6" column="3">
1893 <widget class="QWidget" name="widgetPlayer6" native="true">
1894 <layout class="QVBoxLayout" name="verticalLayout_24">
1895 <property name="spacing">
1896 <number>5</number>
1897 </property>
1898 <property name="leftMargin">
1899 <number>0</number>
1900 </property>
1901 <property name="topMargin">
1902 <number>0</number>
1903 </property>
1904 <property name="rightMargin">
1905 <number>0</number>
1906 </property>
1907 <property name="bottomMargin">
1908 <number>0</number>
1909 </property>
1910 <item alignment="Qt::AlignHCenter">
1911 <widget class="QGroupBox" name="groupPlayer6Connected">
1912 <property name="minimumSize">
1913 <size>
1914 <width>100</width>
1915 <height>100</height>
1916 </size>
1917 </property>
1918 <property name="maximumSize">
1919 <size>
1920 <width>100</width>
1921 <height>100</height>
1922 </size>
1923 </property>
1924 <property name="title">
1925 <string/>
1926 </property>
1927 <property name="checkable">
1928 <bool>true</bool>
1929 </property>
1930 <property name="checked">
1931 <bool>false</bool>
1932 </property>
1933 <layout class="QVBoxLayout" name="verticalLayout_9" stretch="1,0">
1934 <property name="spacing">
1935 <number>7</number>
1936 </property>
1937 <property name="leftMargin">
1938 <number>14</number>
1939 </property>
1940 <property name="topMargin">
1941 <number>7</number>
1942 </property>
1943 <property name="rightMargin">
1944 <number>14</number>
1945 </property>
1946 <property name="bottomMargin">
1947 <number>4</number>
1948 </property>
1949 <item>
1950 <widget class="QWidget" name="controllerPlayer6" native="true">
1951 <property name="styleSheet">
1952 <string notr="true"/>
1953 </property>
1954 <layout class="QVBoxLayout" name="verticalLayout_17">
1955 <property name="topMargin">
1956 <number>16</number>
1957 </property>
1958 <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
1959 <widget class="QLabel" name="labelPlayer6">
1960 <property name="text">
1961 <string>P6</string>
1962 </property>
1963 </widget>
1964 </item>
1965 </layout>
1966 </widget>
1967 </item>
1968 <item alignment="Qt::AlignHCenter">
1969 <widget class="QWidget" name="Player6LEDs" native="true">
1970 <property name="enabled">
1971 <bool>false</bool>
1972 </property>
1973 <property name="minimumSize">
1974 <size>
1975 <width>0</width>
1976 <height>10</height>
1977 </size>
1978 </property>
1979 <layout class="QHBoxLayout" name="horizontalLayout_12">
1980 <property name="spacing">
1981 <number>4</number>
1982 </property>
1983 <property name="leftMargin">
1984 <number>0</number>
1985 </property>
1986 <property name="topMargin">
1987 <number>0</number>
1988 </property>
1989 <property name="rightMargin">
1990 <number>0</number>
1991 </property>
1992 <property name="bottomMargin">
1993 <number>0</number>
1994 </property>
1995 <item>
1996 <widget class="QCheckBox" name="checkboxPlayer6LED1"/>
1997 </item>
1998 <item>
1999 <widget class="QCheckBox" name="checkboxPlayer6LED2"/>
2000 </item>
2001 <item>
2002 <widget class="QCheckBox" name="checkboxPlayer6LED3"/>
2003 </item>
2004 <item>
2005 <widget class="QCheckBox" name="checkboxPlayer6LED4"/>
2006 </item>
2007 </layout>
2008 </widget>
2009 </item>
2010 </layout>
2011 </widget>
2012 </item>
2013 <item>
2014 <widget class="QWidget" name="Player6Explain" native="true">
2015 <property name="minimumSize">
2016 <size>
2017 <width>0</width>
2018 <height>10</height>
2019 </size>
2020 </property>
2021 <property name="maximumSize">
2022 <size>
2023 <width>150</width>
2024 <height>16777215</height>
2025 </size>
2026 </property>
2027 <layout class="QVBoxLayout" name="verticalLayout_41">
2028 <property name="spacing">
2029 <number>0</number>
2030 </property>
2031 <property name="leftMargin">
2032 <number>0</number>
2033 </property>
2034 <property name="topMargin">
2035 <number>0</number>
2036 </property>
2037 <property name="rightMargin">
2038 <number>0</number>
2039 </property>
2040 <property name="bottomMargin">
2041 <number>0</number>
2042 </property>
2043 <item>
2044 <widget class="QLabel" name="labelPlayer6Explain">
2045 <property name="alignment">
2046 <set>Qt::AlignCenter</set>
2047 </property>
2048 </widget>
2049 </item>
2050 </layout>
2051 </widget>
2052 </item>
2053 <item>
2054 <widget class="QComboBox" name="comboPlayer6Emulated">
2055 <item>
2056 <property name="text">
2057 <string>Pro Controller</string>
2058 </property>
2059 </item>
2060 <item>
2061 <property name="text">
2062 <string>Dual Joycons</string>
2063 </property>
2064 </item>
2065 <item>
2066 <property name="text">
2067 <string>Left Joycon</string>
2068 </property>
2069 </item>
2070 <item>
2071 <property name="text">
2072 <string>Right Joycon</string>
2073 </property>
2074 </item>
2075 </widget>
2076 </item>
2077 <item>
2078 <widget class="QComboBox" name="comboPlayer6Profile">
2079 <item>
2080 <property name="text">
2081 <string>Use Current Config</string>
2082 </property>
2083 </item>
2084 </widget>
2085 </item>
2086 </layout>
2087 </widget>
2088 </item>
2089 <item row="10" column="1">
2090 <widget class="QWidget" name="widgetSpacer" native="true">
2091 <property name="minimumSize">
2092 <size>
2093 <width>0</width>
2094 <height>25</height>
2095 </size>
2096 </property>
2097 <layout class="QVBoxLayout" name="verticalLayout_22">
2098 <property name="spacing">
2099 <number>0</number>
2100 </property>
2101 <property name="leftMargin">
2102 <number>0</number>
2103 </property>
2104 <property name="topMargin">
2105 <number>0</number>
2106 </property>
2107 <property name="rightMargin">
2108 <number>0</number>
2109 </property>
2110 <property name="bottomMargin">
2111 <number>0</number>
2112 </property>
2113 <item>
2114 <spacer name="controllerAppletVerticalSpacer">
2115 <property name="orientation">
2116 <enum>Qt::Vertical</enum>
2117 </property>
2118 <property name="sizeHint" stdset="0">
2119 <size>
2120 <width>20</width>
2121 <height>25</height>
2122 </size>
2123 </property>
2124 </spacer>
2125 </item>
2126 </layout>
2127 </widget>
2128 </item>
2129 <item row="1" column="2">
2130 <widget class="QWidget" name="widgetSpacer6" native="true">
2131 <layout class="QHBoxLayout" name="horizontalLayout_15">
2132 <property name="spacing">
2133 <number>0</number>
2134 </property>
2135 <property name="leftMargin">
2136 <number>0</number>
2137 </property>
2138 <property name="topMargin">
2139 <number>0</number>
2140 </property>
2141 <property name="rightMargin">
2142 <number>0</number>
2143 </property>
2144 <property name="bottomMargin">
2145 <number>0</number>
2146 </property>
2147 <item>
2148 <spacer name="controllerAppletHorizontalSpacer5">
2149 <property name="orientation">
2150 <enum>Qt::Horizontal</enum>
2151 </property>
2152 <property name="sizeHint" stdset="0">
2153 <size>
2154 <width>0</width>
2155 <height>20</height>
2156 </size>
2157 </property>
2158 </spacer>
2159 </item>
2160 </layout>
2161 </widget>
2162 </item>
2163 <item row="1" column="0">
2164 <widget class="QWidget" name="widgetSpacer7" native="true">
2165 <property name="minimumSize">
2166 <size>
2167 <width>25</width>
2168 <height>0</height>
2169 </size>
2170 </property>
2171 <layout class="QHBoxLayout" name="horizontalLayout_16">
2172 <property name="spacing">
2173 <number>0</number>
2174 </property>
2175 <property name="leftMargin">
2176 <number>0</number>
2177 </property>
2178 <property name="topMargin">
2179 <number>0</number>
2180 </property>
2181 <property name="rightMargin">
2182 <number>0</number>
2183 </property>
2184 <property name="bottomMargin">
2185 <number>0</number>
2186 </property>
2187 <item>
2188 <spacer name="controllerAppletHorizontalSpacer4">
2189 <property name="orientation">
2190 <enum>Qt::Horizontal</enum>
2191 </property>
2192 <property name="sizeHint" stdset="0">
2193 <size>
2194 <width>25</width>
2195 <height>20</height>
2196 </size>
2197 </property>
2198 </spacer>
2199 </item>
2200 </layout>
2201 </widget>
2202 </item>
2203 <item row="2" column="1">
2204 <widget class="QWidget" name="widgetSpacer9" native="true">
2205 <property name="minimumSize">
2206 <size>
2207 <width>0</width>
2208 <height>25</height>
2209 </size>
2210 </property>
2211 <layout class="QHBoxLayout" name="horizontalLayout_17">
2212 <property name="spacing">
2213 <number>0</number>
2214 </property>
2215 <property name="leftMargin">
2216 <number>0</number>
2217 </property>
2218 <property name="topMargin">
2219 <number>0</number>
2220 </property>
2221 <property name="rightMargin">
2222 <number>0</number>
2223 </property>
2224 <property name="bottomMargin">
2225 <number>0</number>
2226 </property>
2227 <item>
2228 <spacer name="controllerAppletVerticalSpacer2">
2229 <property name="orientation">
2230 <enum>Qt::Vertical</enum>
2231 </property>
2232 <property name="sizeHint" stdset="0">
2233 <size>
2234 <width>20</width>
2235 <height>25</height>
2236 </size>
2237 </property>
2238 </spacer>
2239 </item>
2240 </layout>
2241 </widget>
2242 </item>
2243 </layout>
2244 </item>
2245 </layout>
2246 </widget>
2247 </item>
2248 <item>
2249 <widget class="QWidget" name="bottomControllerApplet" native="true">
2250 <layout class="QHBoxLayout" name="horizontalLayout_6">
2251 <property name="spacing">
2252 <number>15</number>
2253 </property>
2254 <property name="leftMargin">
2255 <number>15</number>
2256 </property>
2257 <property name="topMargin">
2258 <number>8</number>
2259 </property>
2260 <property name="rightMargin">
2261 <number>15</number>
2262 </property>
2263 <property name="bottomMargin">
2264 <number>15</number>
2265 </property>
2266 <item>
2267 <widget class="QGroupBox" name="handheldGroup">
2268 <property name="maximumSize">
2269 <size>
2270 <width>16777215</width>
2271 <height>16777215</height>
2272 </size>
2273 </property>
2274 <property name="title">
2275 <string>Console Mode</string>
2276 </property>
2277 <layout class="QHBoxLayout" name="horizontalLayout_3">
2278 <property name="spacing">
2279 <number>6</number>
2280 </property>
2281 <property name="leftMargin">
2282 <number>6</number>
2283 </property>
2284 <property name="topMargin">
2285 <number>6</number>
2286 </property>
2287 <property name="rightMargin">
2288 <number>3</number>
2289 </property>
2290 <property name="bottomMargin">
2291 <number>6</number>
2292 </property>
2293 <item>
2294 <widget class="QRadioButton" name="radioDocked">
2295 <property name="text">
2296 <string>Docked</string>
2297 </property>
2298 <property name="checked">
2299 <bool>true</bool>
2300 </property>
2301 </widget>
2302 </item>
2303 <item>
2304 <widget class="QRadioButton" name="radioUndocked">
2305 <property name="text">
2306 <string>Undocked</string>
2307 </property>
2308 </widget>
2309 </item>
2310 </layout>
2311 </widget>
2312 </item>
2313 <item>
2314 <widget class="QGroupBox" name="vibrationGroup">
2315 <property name="title">
2316 <string>Vibration</string>
2317 </property>
2318 <property name="checkable">
2319 <bool>true</bool>
2320 </property>
2321 <layout class="QHBoxLayout" name="horizontalLayout_5">
2322 <property name="leftMargin">
2323 <number>3</number>
2324 </property>
2325 <property name="topMargin">
2326 <number>3</number>
2327 </property>
2328 <property name="rightMargin">
2329 <number>3</number>
2330 </property>
2331 <property name="bottomMargin">
2332 <number>3</number>
2333 </property>
2334 <item>
2335 <widget class="QSpinBox" name="vibrationSpin">
2336 <property name="minimumSize">
2337 <size>
2338 <width>65</width>
2339 <height>0</height>
2340 </size>
2341 </property>
2342 <property name="maximumSize">
2343 <size>
2344 <width>65</width>
2345 <height>16777215</height>
2346 </size>
2347 </property>
2348 <property name="suffix">
2349 <string>%</string>
2350 </property>
2351 <property name="minimum">
2352 <number>1</number>
2353 </property>
2354 <property name="maximum">
2355 <number>200</number>
2356 </property>
2357 <property name="value">
2358 <number>100</number>
2359 </property>
2360 </widget>
2361 </item>
2362 </layout>
2363 </widget>
2364 </item>
2365 <item>
2366 <widget class="QGroupBox" name="motionGroup">
2367 <property name="title">
2368 <string>Motion</string>
2369 </property>
2370 <property name="checkable">
2371 <bool>true</bool>
2372 </property>
2373 <layout class="QHBoxLayout" name="horizontalLayout_4">
2374 <property name="leftMargin">
2375 <number>3</number>
2376 </property>
2377 <property name="topMargin">
2378 <number>3</number>
2379 </property>
2380 <property name="rightMargin">
2381 <number>3</number>
2382 </property>
2383 <property name="bottomMargin">
2384 <number>3</number>
2385 </property>
2386 <item>
2387 <widget class="QPushButton" name="motionButton">
2388 <property name="minimumSize">
2389 <size>
2390 <width>57</width>
2391 <height>0</height>
2392 </size>
2393 </property>
2394 <property name="maximumSize">
2395 <size>
2396 <width>55</width>
2397 <height>16777215</height>
2398 </size>
2399 </property>
2400 <property name="styleSheet">
2401 <string notr="true">min-width: 55px;</string>
2402 </property>
2403 <property name="text">
2404 <string>Configure</string>
2405 </property>
2406 </widget>
2407 </item>
2408 </layout>
2409 </widget>
2410 </item>
2411 <item>
2412 <widget class="QGroupBox" name="inputConfigGroup">
2413 <property name="title">
2414 <string>Input Config</string>
2415 </property>
2416 <layout class="QHBoxLayout" name="horizontalLayout_7">
2417 <property name="leftMargin">
2418 <number>3</number>
2419 </property>
2420 <property name="topMargin">
2421 <number>3</number>
2422 </property>
2423 <property name="rightMargin">
2424 <number>3</number>
2425 </property>
2426 <property name="bottomMargin">
2427 <number>3</number>
2428 </property>
2429 <item>
2430 <widget class="QPushButton" name="inputConfigButton">
2431 <property name="maximumSize">
2432 <size>
2433 <width>65</width>
2434 <height>16777215</height>
2435 </size>
2436 </property>
2437 <property name="styleSheet">
2438 <string notr="true">min-width: 55px;</string>
2439 </property>
2440 <property name="text">
2441 <string>Open</string>
2442 </property>
2443 </widget>
2444 </item>
2445 </layout>
2446 </widget>
2447 </item>
2448 <item>
2449 <widget class="QWidget" name="connectedControllers" native="true">
2450 <layout class="QGridLayout" name="gridLayout_2">
2451 <property name="leftMargin">
2452 <number>5</number>
2453 </property>
2454 <property name="topMargin">
2455 <number>0</number>
2456 </property>
2457 <property name="rightMargin">
2458 <number>0</number>
2459 </property>
2460 <property name="bottomMargin">
2461 <number>0</number>
2462 </property>
2463 <property name="spacing">
2464 <number>3</number>
2465 </property>
2466 <item row="1" column="4">
2467 <widget class="QCheckBox" name="checkboxPlayer4Connected">
2468 <property name="text">
2469 <string/>
2470 </property>
2471 </widget>
2472 </item>
2473 <item row="1" column="0">
2474 <widget class="QLabel" name="labelControllers">
2475 <property name="text">
2476 <string>Controllers</string>
2477 </property>
2478 </widget>
2479 </item>
2480 <item row="1" column="2">
2481 <widget class="QCheckBox" name="checkboxPlayer2Connected">
2482 <property name="text">
2483 <string/>
2484 </property>
2485 </widget>
2486 </item>
2487 <item row="0" column="1">
2488 <widget class="QLabel" name="labelConnectedPlayer1">
2489 <property name="text">
2490 <string>1</string>
2491 </property>
2492 <property name="alignment">
2493 <set>Qt::AlignCenter</set>
2494 </property>
2495 </widget>
2496 </item>
2497 <item row="1" column="3">
2498 <widget class="QCheckBox" name="checkboxPlayer3Connected">
2499 <property name="text">
2500 <string/>
2501 </property>
2502 </widget>
2503 </item>
2504 <item row="1" column="1">
2505 <widget class="QCheckBox" name="checkboxPlayer1Connected">
2506 <property name="layoutDirection">
2507 <enum>Qt::LeftToRight</enum>
2508 </property>
2509 <property name="checked">
2510 <bool>false</bool>
2511 </property>
2512 </widget>
2513 </item>
2514 <item row="0" column="2">
2515 <widget class="QLabel" name="labelConnectedPlayer2">
2516 <property name="text">
2517 <string>2</string>
2518 </property>
2519 <property name="alignment">
2520 <set>Qt::AlignCenter</set>
2521 </property>
2522 </widget>
2523 </item>
2524 <item row="0" column="4">
2525 <widget class="QLabel" name="labelConnectedPlayer4">
2526 <property name="text">
2527 <string>4</string>
2528 </property>
2529 <property name="alignment">
2530 <set>Qt::AlignCenter</set>
2531 </property>
2532 </widget>
2533 </item>
2534 <item row="0" column="3">
2535 <widget class="QLabel" name="labelConnectedPlayer3">
2536 <property name="text">
2537 <string>3</string>
2538 </property>
2539 <property name="alignment">
2540 <set>Qt::AlignCenter</set>
2541 </property>
2542 </widget>
2543 </item>
2544 <item row="0" column="0">
2545 <widget class="QLabel" name="labelConnected">
2546 <property name="text">
2547 <string>Connected</string>
2548 </property>
2549 </widget>
2550 </item>
2551 <item row="1" column="7">
2552 <widget class="QCheckBox" name="checkboxPlayer7Connected">
2553 <property name="text">
2554 <string/>
2555 </property>
2556 </widget>
2557 </item>
2558 <item row="0" column="5">
2559 <widget class="QLabel" name="labelConnectedPlayer5">
2560 <property name="text">
2561 <string>5</string>
2562 </property>
2563 <property name="alignment">
2564 <set>Qt::AlignCenter</set>
2565 </property>
2566 </widget>
2567 </item>
2568 <item row="1" column="6">
2569 <widget class="QCheckBox" name="checkboxPlayer6Connected">
2570 <property name="text">
2571 <string/>
2572 </property>
2573 </widget>
2574 </item>
2575 <item row="0" column="7">
2576 <widget class="QLabel" name="labelConnectedPlayer7">
2577 <property name="text">
2578 <string>7</string>
2579 </property>
2580 <property name="alignment">
2581 <set>Qt::AlignCenter</set>
2582 </property>
2583 </widget>
2584 </item>
2585 <item row="1" column="5">
2586 <widget class="QCheckBox" name="checkboxPlayer5Connected">
2587 <property name="text">
2588 <string/>
2589 </property>
2590 </widget>
2591 </item>
2592 <item row="0" column="6">
2593 <widget class="QLabel" name="labelConnectedPlayer6">
2594 <property name="text">
2595 <string>6</string>
2596 </property>
2597 <property name="alignment">
2598 <set>Qt::AlignCenter</set>
2599 </property>
2600 </widget>
2601 </item>
2602 <item row="0" column="8">
2603 <widget class="QLabel" name="labelConnectedPlayer8">
2604 <property name="text">
2605 <string>8</string>
2606 </property>
2607 <property name="alignment">
2608 <set>Qt::AlignCenter</set>
2609 </property>
2610 </widget>
2611 </item>
2612 <item row="1" column="8">
2613 <widget class="QCheckBox" name="checkboxPlayer8Connected">
2614 <property name="text">
2615 <string/>
2616 </property>
2617 </widget>
2618 </item>
2619 </layout>
2620 </widget>
2621 </item>
2622 <item>
2623 <spacer name="controllerAppletHorizontalSpacer">
2624 <property name="orientation">
2625 <enum>Qt::Horizontal</enum>
2626 </property>
2627 <property name="sizeHint" stdset="0">
2628 <size>
2629 <width>0</width>
2630 <height>20</height>
2631 </size>
2632 </property>
2633 </spacer>
2634 </item>
2635 <item alignment="Qt::AlignBottom">
2636 <widget class="QDialogButtonBox" name="buttonBox">
2637 <property name="enabled">
2638 <bool>true</bool>
2639 </property>
2640 <property name="standardButtons">
2641 <set>QDialogButtonBox::Ok</set>
2642 </property>
2643 </widget>
2644 </item>
2645 </layout>
2646 </widget>
2647 </item>
2648 </layout>
2649 </widget>
2650 </item>
2651 </layout>
2652 </widget>
2653 <resources/>
2654 <connections>
2655 <connection>
2656 <sender>buttonBox</sender>
2657 <signal>accepted()</signal>
2658 <receiver>QtControllerSelectorDialog</receiver>
2659 <slot>accept()</slot>
2660 <hints>
2661 <hint type="sourcelabel">
2662 <x>20</x>
2663 <y>20</y>
2664 </hint>
2665 <hint type="destinationlabel">
2666 <x>20</x>
2667 <y>20</y>
2668 </hint>
2669 </hints>
2670 </connection>
2671 </connections>
2672</ui>
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 21707e451..caa2d06d3 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -30,6 +30,7 @@
30#include "common/scope_exit.h" 30#include "common/scope_exit.h"
31#include "core/core.h" 31#include "core/core.h"
32#include "core/frontend/framebuffer_layout.h" 32#include "core/frontend/framebuffer_layout.h"
33#include "core/hle/kernel/process.h"
33#include "core/settings.h" 34#include "core/settings.h"
34#include "input_common/keyboard.h" 35#include "input_common/keyboard.h"
35#include "input_common/main.h" 36#include "input_common/main.h"
@@ -63,7 +64,8 @@ void EmuThread::run() {
63 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 64 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
64 65
65 system.Renderer().Rasterizer().LoadDiskResources( 66 system.Renderer().Rasterizer().LoadDiskResources(
66 stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { 67 system.CurrentProcess()->GetTitleID(), stop_run,
68 [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
67 emit LoadProgress(stage, value, total); 69 emit LoadProgress(stage, value, total);
68 }); 70 });
69 71
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index ae3e31762..7ea17a4db 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -70,7 +70,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
70 70
71ConfigureInput::~ConfigureInput() = default; 71ConfigureInput::~ConfigureInput() = default;
72 72
73void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) { 73void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
74 std::size_t max_players) {
74 player_controllers = { 75 player_controllers = {
75 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem), 76 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
76 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem), 77 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
@@ -93,6 +94,11 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
93 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, 94 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
94 }; 95 };
95 96
97 std::array<QLabel*, 8> player_connected_labels = {
98 ui->label, ui->label_3, ui->label_4, ui->label_5,
99 ui->label_6, ui->label_7, ui->label_8, ui->label_9,
100 };
101
96 for (std::size_t i = 0; i < player_tabs.size(); ++i) { 102 for (std::size_t i = 0; i < player_tabs.size(); ++i) {
97 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); 103 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
98 player_tabs[i]->layout()->addWidget(player_controllers[i]); 104 player_tabs[i]->layout()->addWidget(player_controllers[i]);
@@ -112,6 +118,13 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
112 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { 118 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
113 player_controllers[i]->ConnectPlayer(state == Qt::Checked); 119 player_controllers[i]->ConnectPlayer(state == Qt::Checked);
114 }); 120 });
121
122 // Remove/hide all the elements that exceed max_players, if applicable.
123 if (i >= max_players) {
124 ui->tabWidget->removeTab(static_cast<int>(max_players));
125 player_connected[i]->hide();
126 player_connected_labels[i]->hide();
127 }
115 } 128 }
116 // Only the first player can choose handheld mode so connect the signal just to player 1 129 // Only the first player can choose handheld mode so connect the signal just to player 1
117 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, 130 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
@@ -175,8 +188,7 @@ void ConfigureInput::RetranslateUI() {
175 188
176void ConfigureInput::LoadConfiguration() { 189void ConfigureInput::LoadConfiguration() {
177 LoadPlayerControllerIndices(); 190 LoadPlayerControllerIndices();
178 UpdateDockedState(Settings::values.players[0].controller_type == 191 UpdateDockedState(Settings::values.players[8].connected);
179 Settings::ControllerType::Handheld);
180 192
181 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); 193 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
182} 194}
@@ -208,14 +220,14 @@ void ConfigureInput::RestoreDefaults() {
208} 220}
209 221
210void ConfigureInput::UpdateDockedState(bool is_handheld) { 222void ConfigureInput::UpdateDockedState(bool is_handheld) {
211 // If the controller type is handheld only, disallow changing docked mode 223 // Disallow changing the console mode if the controller type is handheld.
212 ui->radioDocked->setEnabled(!is_handheld); 224 ui->radioDocked->setEnabled(!is_handheld);
213 ui->radioUndocked->setEnabled(!is_handheld); 225 ui->radioUndocked->setEnabled(!is_handheld);
214 226
215 ui->radioDocked->setChecked(Settings::values.use_docked_mode); 227 ui->radioDocked->setChecked(Settings::values.use_docked_mode);
216 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); 228 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
217 229
218 // If its handheld only, force docked mode off (since you can't play handheld in a dock) 230 // Also force into undocked mode if the controller type is handheld.
219 if (is_handheld) { 231 if (is_handheld) {
220 ui->radioUndocked->setChecked(true); 232 ui->radioUndocked->setChecked(true);
221 } 233 }
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index d08a24f96..0e8b2fd4e 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -37,7 +37,7 @@ public:
37 ~ConfigureInput() override; 37 ~ConfigureInput() override;
38 38
39 /// Initializes the input dialog with the given input subsystem. 39 /// Initializes the input dialog with the given input subsystem.
40 void Initialize(InputCommon::InputSubsystem* input_subsystem_); 40 void Initialize(InputCommon::InputSubsystem* input_subsystem_, std::size_t max_players = 8);
41 41
42 /// Save all button configurations to settings file. 42 /// Save all button configurations to settings file.
43 void ApplyConfiguration(); 43 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_input_dialog.cpp b/src/yuzu/configuration/configure_input_dialog.cpp
new file mode 100644
index 000000000..1866003c2
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_dialog.cpp
@@ -0,0 +1,37 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "ui_configure_input_dialog.h"
6#include "yuzu/configuration/configure_input_dialog.h"
7
8ConfigureInputDialog::ConfigureInputDialog(QWidget* parent, std::size_t max_players,
9 InputCommon::InputSubsystem* input_subsystem)
10 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputDialog>()),
11 input_widget(new ConfigureInput(this)) {
12 ui->setupUi(this);
13
14 input_widget->Initialize(input_subsystem, max_players);
15
16 ui->inputLayout->addWidget(input_widget);
17
18 RetranslateUI();
19}
20
21ConfigureInputDialog::~ConfigureInputDialog() = default;
22
23void ConfigureInputDialog::ApplyConfiguration() {
24 input_widget->ApplyConfiguration();
25}
26
27void ConfigureInputDialog::changeEvent(QEvent* event) {
28 if (event->type() == QEvent::LanguageChange) {
29 RetranslateUI();
30 }
31
32 QDialog::changeEvent(event);
33}
34
35void ConfigureInputDialog::RetranslateUI() {
36 ui->retranslateUi(this);
37}
diff --git a/src/yuzu/configuration/configure_input_dialog.h b/src/yuzu/configuration/configure_input_dialog.h
new file mode 100644
index 000000000..d1bd865f9
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_dialog.h
@@ -0,0 +1,38 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QDialog>
9#include "yuzu/configuration/configure_input.h"
10
11class QPushButton;
12
13namespace InputCommon {
14class InputSubsystem;
15}
16
17namespace Ui {
18class ConfigureInputDialog;
19}
20
21class ConfigureInputDialog : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureInputDialog(QWidget* parent, std::size_t max_players,
26 InputCommon::InputSubsystem* input_subsystem);
27 ~ConfigureInputDialog() override;
28
29 void ApplyConfiguration();
30
31private:
32 void changeEvent(QEvent* event) override;
33 void RetranslateUI();
34
35 std::unique_ptr<Ui::ConfigureInputDialog> ui;
36
37 ConfigureInput* input_widget;
38};
diff --git a/src/yuzu/configuration/configure_input_dialog.ui b/src/yuzu/configuration/configure_input_dialog.ui
new file mode 100644
index 000000000..b92ddb200
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_dialog.ui
@@ -0,0 +1,57 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputDialog</class>
4 <widget class="QDialog" name="ConfigureInputDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>70</width>
10 <height>540</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Input</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <property name="spacing">
18 <number>2</number>
19 </property>
20 <property name="leftMargin">
21 <number>9</number>
22 </property>
23 <property name="topMargin">
24 <number>9</number>
25 </property>
26 <property name="rightMargin">
27 <number>9</number>
28 </property>
29 <property name="bottomMargin">
30 <number>9</number>
31 </property>
32 <item>
33 <layout class="QHBoxLayout" name="inputLayout"/>
34 </item>
35 <item>
36 <layout class="QHBoxLayout" name="horizontalLayout">
37 <item>
38 <widget class="QDialogButtonBox" name="buttonBox">
39 <property name="standardButtons">
40 <set>QDialogButtonBox::Ok</set>
41 </property>
42 </widget>
43 </item>
44 </layout>
45 </item>
46 </layout>
47 </widget>
48 <resources/>
49 <connections>
50 <connection>
51 <sender>buttonBox</sender>
52 <signal>accepted()</signal>
53 <receiver>ConfigureInputDialog</receiver>
54 <slot>accept()</slot>
55 </connection>
56 </connections>
57</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 13ecb3dc5..a53578837 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -646,10 +646,10 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
646 const auto& device = input_devices[ui->comboDevices->currentIndex()]; 646 const auto& device = input_devices[ui->comboDevices->currentIndex()];
647 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); 647 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
648 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); 648 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
649 for (int i = 0; i < buttons_param.size(); ++i) { 649 for (std::size_t i = 0; i < buttons_param.size(); ++i) {
650 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)]; 650 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
651 } 651 }
652 for (int i = 0; i < analogs_param.size(); ++i) { 652 for (std::size_t i = 0; i < analogs_param.size(); ++i) {
653 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)]; 653 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
654 } 654 }
655 655
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a1b61d119..68ad43a80 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -11,6 +11,7 @@
11#endif 11#endif
12 12
13// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 13// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
14#include "applets/controller.h"
14#include "applets/error.h" 15#include "applets/error.h"
15#include "applets/profile_select.h" 16#include "applets/profile_select.h"
16#include "applets/software_keyboard.h" 17#include "applets/software_keyboard.h"
@@ -19,7 +20,9 @@
19#include "configuration/configure_per_game.h" 20#include "configuration/configure_per_game.h"
20#include "core/file_sys/vfs.h" 21#include "core/file_sys/vfs.h"
21#include "core/file_sys/vfs_real.h" 22#include "core/file_sys/vfs_real.h"
23#include "core/frontend/applets/controller.h"
22#include "core/frontend/applets/general_frontend.h" 24#include "core/frontend/applets/general_frontend.h"
25#include "core/frontend/applets/software_keyboard.h"
23#include "core/hle/service/acc/profile_manager.h" 26#include "core/hle/service/acc/profile_manager.h"
24#include "core/hle/service/am/applet_ae.h" 27#include "core/hle/service/am/applet_ae.h"
25#include "core/hle/service/am/applet_oe.h" 28#include "core/hle/service/am/applet_oe.h"
@@ -84,7 +87,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
84#include "core/file_sys/romfs.h" 87#include "core/file_sys/romfs.h"
85#include "core/file_sys/savedata_factory.h" 88#include "core/file_sys/savedata_factory.h"
86#include "core/file_sys/submission_package.h" 89#include "core/file_sys/submission_package.h"
87#include "core/frontend/applets/software_keyboard.h"
88#include "core/hle/kernel/process.h" 90#include "core/hle/kernel/process.h"
89#include "core/hle/service/am/am.h" 91#include "core/hle/service/am/am.h"
90#include "core/hle/service/filesystem/filesystem.h" 92#include "core/hle/service/filesystem/filesystem.h"
@@ -283,6 +285,23 @@ GMainWindow::~GMainWindow() {
283 delete render_window; 285 delete render_window;
284} 286}
285 287
288void GMainWindow::ControllerSelectorReconfigureControllers(
289 const Core::Frontend::ControllerParameters& parameters) {
290 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
291 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
292 Qt::WindowSystemMenuHint);
293 dialog.setWindowModality(Qt::WindowModal);
294 dialog.exec();
295
296 emit ControllerSelectorReconfigureFinished();
297
298 // Don't forget to apply settings.
299 Settings::Apply();
300 config->Save();
301
302 UpdateStatusButtons();
303}
304
286void GMainWindow::ProfileSelectorSelectProfile() { 305void GMainWindow::ProfileSelectorSelectProfile() {
287 const Service::Account::ProfileManager manager; 306 const Service::Account::ProfileManager manager;
288 int index = 0; 307 int index = 0;
@@ -291,10 +310,12 @@ void GMainWindow::ProfileSelectorSelectProfile() {
291 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 310 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
292 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); 311 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
293 dialog.setWindowModality(Qt::WindowModal); 312 dialog.setWindowModality(Qt::WindowModal);
313
294 if (dialog.exec() == QDialog::Rejected) { 314 if (dialog.exec() == QDialog::Rejected) {
295 emit ProfileSelectorFinishedSelection(std::nullopt); 315 emit ProfileSelectorFinishedSelection(std::nullopt);
296 return; 316 return;
297 } 317 }
318
298 index = dialog.GetIndex(); 319 index = dialog.GetIndex();
299 } 320 }
300 321
@@ -966,13 +987,14 @@ bool GMainWindow::LoadROM(const QString& filename) {
966 system.SetFilesystem(vfs); 987 system.SetFilesystem(vfs);
967 988
968 system.SetAppletFrontendSet({ 989 system.SetAppletFrontendSet({
969 nullptr, // Parental Controls 990 std::make_unique<QtControllerSelector>(*this), // Controller Selector
970 std::make_unique<QtErrorDisplay>(*this), // 991 nullptr, // E-Commerce
971 nullptr, // Photo Viewer 992 std::make_unique<QtErrorDisplay>(*this), // Error Display
972 std::make_unique<QtProfileSelector>(*this), // 993 nullptr, // Parental Controls
973 std::make_unique<QtSoftwareKeyboard>(*this), // 994 nullptr, // Photo Viewer
974 std::make_unique<QtWebBrowser>(*this), // 995 std::make_unique<QtProfileSelector>(*this), // Profile Selector
975 nullptr, // E-Commerce 996 std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
997 std::make_unique<QtWebBrowser>(*this), // Web Browser
976 }); 998 });
977 999
978 system.RegisterHostThread(); 1000 system.RegisterHostThread();
@@ -2047,6 +2069,7 @@ void GMainWindow::OnStartGame() {
2047 2069
2048 emu_thread->SetRunning(true); 2070 emu_thread->SetRunning(true);
2049 2071
2072 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
2050 qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>( 2073 qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
2051 "Core::Frontend::SoftwareKeyboardParameters"); 2074 "Core::Frontend::SoftwareKeyboardParameters");
2052 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); 2075 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 0ce66a1ca..afcfa68a9 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -37,6 +37,7 @@ enum class InstalledEntryType;
37class GameListPlaceholder; 37class GameListPlaceholder;
38 38
39namespace Core::Frontend { 39namespace Core::Frontend {
40struct ControllerParameters;
40struct SoftwareKeyboardParameters; 41struct SoftwareKeyboardParameters;
41} // namespace Core::Frontend 42} // namespace Core::Frontend
42 43
@@ -116,9 +117,12 @@ signals:
116 117
117 void UpdateInstallProgress(); 118 void UpdateInstallProgress();
118 119
120 void ControllerSelectorReconfigureFinished();
121
119 void ErrorDisplayFinished(); 122 void ErrorDisplayFinished();
120 123
121 void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); 124 void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
125
122 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); 126 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
123 void SoftwareKeyboardFinishedCheckDialog(); 127 void SoftwareKeyboardFinishedCheckDialog();
124 128
@@ -127,6 +131,8 @@ signals:
127 131
128public slots: 132public slots:
129 void OnLoadComplete(); 133 void OnLoadComplete();
134 void ControllerSelectorReconfigureControllers(
135 const Core::Frontend::ControllerParameters& parameters);
130 void ErrorDisplayDisplayError(QString body); 136 void ErrorDisplayDisplayError(QString body);
131 void ProfileSelectorSelectProfile(); 137 void ProfileSelectorSelectProfile();
132 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); 138 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 4f00c804d..e960b5413 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -26,6 +26,7 @@
26#include "core/file_sys/registered_cache.h" 26#include "core/file_sys/registered_cache.h"
27#include "core/file_sys/vfs_real.h" 27#include "core/file_sys/vfs_real.h"
28#include "core/gdbstub/gdbstub.h" 28#include "core/gdbstub/gdbstub.h"
29#include "core/hle/kernel/process.h"
29#include "core/hle/service/filesystem/filesystem.h" 30#include "core/hle/service/filesystem/filesystem.h"
30#include "core/loader/loader.h" 31#include "core/loader/loader.h"
31#include "core/settings.h" 32#include "core/settings.h"
@@ -235,7 +236,9 @@ int main(int argc, char** argv) {
235 // Core is loaded, start the GPU (makes the GPU contexts current to this thread) 236 // Core is loaded, start the GPU (makes the GPU contexts current to this thread)
236 system.GPU().Start(); 237 system.GPU().Start();
237 238
238 system.Renderer().Rasterizer().LoadDiskResources(); 239 system.Renderer().Rasterizer().LoadDiskResources(
240 system.CurrentProcess()->GetTitleID(), false,
241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
239 242
240 std::thread render_thread([&emu_window] { emu_window->Present(); }); 243 std::thread render_thread([&emu_window] { emu_window->Present(); });
241 system.Run(); 244 system.Run();
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 7acf0caad..5798ce43a 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -255,7 +255,6 @@ int main(int argc, char** argv) {
255 "SDLHideTester"); 255 "SDLHideTester");
256 256
257 system.GPU().Start(); 257 system.GPU().Start();
258 system.Renderer().Rasterizer().LoadDiskResources();
259 258
260 system.Run(); 259 system.Run();
261 while (!finished) { 260 while (!finished) {