summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/command_generator.cpp357
-rw-r--r--src/audio_core/command_generator.h5
-rw-r--r--src/audio_core/common.h23
-rw-r--r--src/audio_core/delay_line.cpp104
-rw-r--r--src/audio_core/delay_line.h46
-rw-r--r--src/audio_core/effect_context.cpp22
-rw-r--r--src/audio_core/effect_context.h31
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/cityhash.cpp178
-rw-r--r--src/common/cityhash.h34
-rw-r--r--src/common/uint128.cpp71
-rw-r--r--src/common/uint128.h105
-rw-r--r--src/common/wall_clock.cpp2
-rw-r--r--src/common/x64/native_clock.cpp58
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/core_timing_util.cpp84
-rw-r--r--src/core/core_timing_util.h61
-rw-r--r--src/core/frontend/applets/controller.h1
-rw-r--r--src/core/hle/kernel/kernel.cpp9
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp63
-rw-r--r--src/core/hle/service/hid/controllers/npad.h26
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp7
-rw-r--r--src/core/hle/service/time/time_manager.cpp4
-rw-r--r--src/core/hle/service/time/time_manager.h2
-rw-r--r--src/input_common/mouse/mouse_input.cpp26
-rw-r--r--src/input_common/settings.h1
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/cityhash.cpp22
-rw-r--r--src/video_core/cdma_pusher.cpp63
-rw-r--r--src/video_core/cdma_pusher.h33
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp7
-rw-r--r--src/video_core/command_classes/nvdec.cpp8
-rw-r--r--src/video_core/command_classes/nvdec.h2
-rw-r--r--src/video_core/command_classes/vic.cpp45
-rw-r--r--src/video_core/command_classes/vic.h51
-rw-r--r--src/video_core/gpu.cpp6
-rw-r--r--src/video_core/gpu_thread.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp83
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h18
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp34
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.h28
-rw-r--r--src/yuzu/applets/controller.cpp9
-rw-r--r--src/yuzu/configuration/config.cpp9
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp10
-rw-r--r--src/yuzu/configuration/configure_filesystem.h1
-rw-r--r--src/yuzu/configuration/configure_filesystem.ui35
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp106
-rw-r--r--src/yuzu/configuration/configure_input_player.h6
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp3
-rw-r--r--src/yuzu/debugger/controller.cpp2
-rw-r--r--src/yuzu_cmd/config.cpp3
62 files changed, 1303 insertions, 663 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index d1d177b51..a0ae07752 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -15,6 +15,8 @@ add_library(audio_core STATIC
15 command_generator.cpp 15 command_generator.cpp
16 command_generator.h 16 command_generator.h
17 common.h 17 common.h
18 delay_line.cpp
19 delay_line.h
18 effect_context.cpp 20 effect_context.cpp
19 effect_context.h 21 effect_context.h
20 info_updater.cpp 22 info_updater.cpp
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index 5b1065520..437cc5ccd 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cmath>
6#include <numbers>
5#include "audio_core/algorithm/interpolate.h" 7#include "audio_core/algorithm/interpolate.h"
6#include "audio_core/command_generator.h" 8#include "audio_core/command_generator.h"
7#include "audio_core/effect_context.h" 9#include "audio_core/effect_context.h"
@@ -13,6 +15,20 @@ namespace AudioCore {
13namespace { 15namespace {
14constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00; 16constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
15constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL; 17constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
18using DelayLineTimes = std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>;
19
20constexpr DelayLineTimes FDN_MIN_DELAY_LINE_TIMES{5.0f, 6.0f, 13.0f, 14.0f};
21constexpr DelayLineTimes FDN_MAX_DELAY_LINE_TIMES{45.704f, 82.782f, 149.94f, 271.58f};
22constexpr DelayLineTimes DECAY0_MAX_DELAY_LINE_TIMES{17.0f, 13.0f, 9.0f, 7.0f};
23constexpr DelayLineTimes DECAY1_MAX_DELAY_LINE_TIMES{19.0f, 11.0f, 10.0f, 6.0f};
24constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_TAP_TIMES{
25 0.017136f, 0.059154f, 0.161733f, 0.390186f, 0.425262f, 0.455411f, 0.689737f,
26 0.745910f, 0.833844f, 0.859502f, 0.000000f, 0.075024f, 0.168788f, 0.299901f,
27 0.337443f, 0.371903f, 0.599011f, 0.716741f, 0.817859f, 0.851664f};
28constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_GAIN{
29 0.67096f, 0.61027f, 1.0f, 0.35680f, 0.68361f, 0.65978f, 0.51939f,
30 0.24712f, 0.45945f, 0.45021f, 0.64196f, 0.54879f, 0.92925f, 0.38270f,
31 0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
16 32
17template <std::size_t N> 33template <std::size_t N>
18void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) { 34void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
@@ -65,6 +81,154 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
65 } 81 }
66} 82}
67 83
84float Pow10(float x) {
85 if (x >= 0.0f) {
86 return 1.0f;
87 } else if (x <= -5.3f) {
88 return 0.0f;
89 }
90 return std::pow(10.0f, x);
91}
92
93float SinD(float degrees) {
94 return std::sin(degrees * std::numbers::pi_v<float> / 180.0f);
95}
96
97float CosD(float degrees) {
98 return std::cos(degrees * std::numbers::pi_v<float> / 180.0f);
99}
100
101float ToFloat(s32 sample) {
102 return static_cast<float>(sample) / 65536.f;
103}
104
105s32 ToS32(float sample) {
106 constexpr auto min = -8388608.0f;
107 constexpr auto max = 8388607.f;
108 float rescaled_sample = sample * 65536.0f;
109 if (rescaled_sample < min) {
110 rescaled_sample = min;
111 }
112 if (rescaled_sample > max) {
113 rescaled_sample = max;
114 }
115 return static_cast<s32>(rescaled_sample);
116}
117
118constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
120
121constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
122 1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
123
124constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
125 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
126
127constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
128 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
129
130template <std::size_t CHANNEL_COUNT>
131void ApplyReverbGeneric(I3dl2ReverbState& state,
132 const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input,
133 const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output,
134 s32 sample_count) {
135
136 auto GetTapLookup = []() {
137 if constexpr (CHANNEL_COUNT == 1) {
138 return REVERB_TAP_INDEX_1CH;
139 } else if constexpr (CHANNEL_COUNT == 2) {
140 return REVERB_TAP_INDEX_2CH;
141 } else if constexpr (CHANNEL_COUNT == 4) {
142 return REVERB_TAP_INDEX_4CH;
143 } else if constexpr (CHANNEL_COUNT == 6) {
144 return REVERB_TAP_INDEX_6CH;
145 }
146 };
147
148 const auto& tap_index_lut = GetTapLookup();
149 for (s32 sample = 0; sample < sample_count; sample++) {
150 std::array<f32, CHANNEL_COUNT> out_samples{};
151 std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fsamp{};
152 std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> mixed{};
153 std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> osamp{};
154
155 // Mix everything into a single sample
156 s32 temp_mixed_sample = 0;
157 for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
158 temp_mixed_sample += input[i][sample];
159 }
160 const auto current_sample = ToFloat(temp_mixed_sample);
161 const auto early_tap = state.early_delay_line.TapOut(state.early_to_late_taps);
162
163 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_TAPS; i++) {
164 const auto tapped_samp =
165 state.early_delay_line.TapOut(state.early_tap_steps[i]) * EARLY_GAIN[i];
166 out_samples[tap_index_lut[i]] += tapped_samp;
167
168 if constexpr (CHANNEL_COUNT == 6) {
169 // handle lfe
170 out_samples[5] += tapped_samp;
171 }
172 }
173
174 state.lowpass_0 = current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1;
175 state.early_delay_line.Tick(state.lowpass_0);
176
177 for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
178 out_samples[i] *= state.early_gain;
179 }
180
181 // Two channel seems to apply a latet gain, we require to save this
182 f32 filter{};
183 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
184 filter = state.fdn_delay_line[i].GetOutputSample();
185 const auto computed = filter * state.lpf_coefficients[0][i] + state.shelf_filter[i];
186 state.shelf_filter[i] =
187 filter * state.lpf_coefficients[1][i] + computed * state.lpf_coefficients[2][i];
188 fsamp[i] = computed;
189 }
190
191 // Mixing matrix
192 mixed[0] = fsamp[1] + fsamp[2];
193 mixed[1] = -fsamp[0] - fsamp[3];
194 mixed[2] = fsamp[0] - fsamp[3];
195 mixed[3] = fsamp[1] - fsamp[2];
196
197 if constexpr (CHANNEL_COUNT == 2) {
198 for (auto& mix : mixed) {
199 mix *= (filter * state.late_gain);
200 }
201 }
202
203 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
204 const auto late = early_tap * state.late_gain;
205 osamp[i] = state.decay_delay_line0[i].Tick(late + mixed[i]);
206 osamp[i] = state.decay_delay_line1[i].Tick(osamp[i]);
207 state.fdn_delay_line[i].Tick(osamp[i]);
208 }
209
210 if constexpr (CHANNEL_COUNT == 1) {
211 output[0][sample] = ToS32(state.dry_gain * ToFloat(input[0][sample]) +
212 (out_samples[0] + osamp[0] + osamp[1]));
213 } else if constexpr (CHANNEL_COUNT == 2 || CHANNEL_COUNT == 4) {
214 for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
215 output[i][sample] =
216 ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
217 }
218 } else if constexpr (CHANNEL_COUNT == 6) {
219 const auto temp_center = state.center_delay_line.Tick(0.5f * (osamp[2] - osamp[3]));
220 for (std::size_t i = 0; i < 4; i++) {
221 output[i][sample] =
222 ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
223 }
224 output[4][sample] =
225 ToS32(state.dry_gain * ToFloat(input[4][sample]) + (out_samples[4] + temp_center));
226 output[5][sample] =
227 ToS32(state.dry_gain * ToFloat(input[5][sample]) + (out_samples[5] + osamp[3]));
228 }
229 }
230}
231
68} // namespace 232} // namespace
69 233
70CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_, 234CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
@@ -271,11 +435,10 @@ void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voic
271 } 435 }
272 436
273 // Generate biquad filter 437 // Generate biquad filter
274 // GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter, 438 // GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
275 // dsp_state.biquad_filter_state, 439 // dsp_state.biquad_filter_state,
276 // mix_buffer_count + channel, mix_buffer_count + 440 // mix_buffer_count + channel, mix_buffer_count + channel,
277 // channel, worker_params.sample_count, 441 // worker_params.sample_count, voice_info.GetInParams().node_id);
278 // voice_info.GetInParams().node_id);
279 } 442 }
280} 443}
281 444
@@ -376,21 +539,54 @@ void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
376 539
377void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, 540void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
378 bool enabled) { 541 bool enabled) {
379 if (!enabled) { 542 auto* reverb = dynamic_cast<EffectI3dl2Reverb*>(info);
543 const auto& params = reverb->GetParams();
544 auto& state = reverb->GetState();
545 const auto channel_count = params.channel_count;
546
547 if (channel_count != 1 && channel_count != 2 && channel_count != 4 && channel_count != 6) {
380 return; 548 return;
381 } 549 }
382 const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams(); 550
383 const auto channel_count = params.channel_count; 551 std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{};
552 std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{};
553
554 const auto status = params.status;
384 for (s32 i = 0; i < channel_count; i++) { 555 for (s32 i = 0; i < channel_count; i++) {
385 // TODO(ogniK): Actually implement reverb 556 input[i] = GetMixBuffer(mix_buffer_offset + params.input[i]);
386 /* 557 output[i] = GetMixBuffer(mix_buffer_offset + params.output[i]);
387 if (params.input[i] != params.output[i]) { 558 }
388 const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); 559
389 auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); 560 if (enabled) {
390 ApplyMix<1>(output, input, 32768, worker_params.sample_count); 561 if (status == ParameterStatus::Initialized) {
391 }*/ 562 InitializeI3dl2Reverb(reverb->GetParams(), state, info->GetWorkBuffer());
392 auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); 563 } else if (status == ParameterStatus::Updating) {
393 std::memset(output, 0, worker_params.sample_count * sizeof(s32)); 564 UpdateI3dl2Reverb(reverb->GetParams(), state, false);
565 }
566 }
567
568 if (enabled) {
569 switch (channel_count) {
570 case 1:
571 ApplyReverbGeneric<1>(state, input, output, worker_params.sample_count);
572 break;
573 case 2:
574 ApplyReverbGeneric<2>(state, input, output, worker_params.sample_count);
575 break;
576 case 4:
577 ApplyReverbGeneric<4>(state, input, output, worker_params.sample_count);
578 break;
579 case 6:
580 ApplyReverbGeneric<6>(state, input, output, worker_params.sample_count);
581 break;
582 }
583 } else {
584 for (s32 i = 0; i < channel_count; i++) {
585 // Only copy if the buffer input and output do not match!
586 if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) {
587 std::memcpy(output[i], input[i], worker_params.sample_count * sizeof(s32));
588 }
589 }
394 } 590 }
395} 591}
396 592
@@ -528,6 +724,133 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3
528 return sample_count; 724 return sample_count;
529} 725}
530 726
727void CommandGenerator::InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
728 std::vector<u8>& work_buffer) {
729 // Reset state
730 state.lowpass_0 = 0.0f;
731 state.lowpass_1 = 0.0f;
732 state.lowpass_2 = 0.0f;
733
734 state.early_delay_line.Reset();
735 state.early_tap_steps.fill(0);
736 state.early_gain = 0.0f;
737 state.late_gain = 0.0f;
738 state.early_to_late_taps = 0;
739 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
740 state.fdn_delay_line[i].Reset();
741 state.decay_delay_line0[i].Reset();
742 state.decay_delay_line1[i].Reset();
743 }
744 state.last_reverb_echo = 0.0f;
745 state.center_delay_line.Reset();
746 for (auto& coef : state.lpf_coefficients) {
747 coef.fill(0.0f);
748 }
749 state.shelf_filter.fill(0.0f);
750 state.dry_gain = 0.0f;
751
752 const auto sample_rate = info.sample_rate / 1000;
753 f32* work_buffer_ptr = reinterpret_cast<f32*>(work_buffer.data());
754
755 s32 delay_samples{};
756 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
757 delay_samples =
758 AudioCommon::CalculateDelaySamples(sample_rate, FDN_MAX_DELAY_LINE_TIMES[i]);
759 state.fdn_delay_line[i].Initialize(delay_samples, work_buffer_ptr);
760 work_buffer_ptr += delay_samples + 1;
761
762 delay_samples =
763 AudioCommon::CalculateDelaySamples(sample_rate, DECAY0_MAX_DELAY_LINE_TIMES[i]);
764 state.decay_delay_line0[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
765 work_buffer_ptr += delay_samples + 1;
766
767 delay_samples =
768 AudioCommon::CalculateDelaySamples(sample_rate, DECAY1_MAX_DELAY_LINE_TIMES[i]);
769 state.decay_delay_line1[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
770 work_buffer_ptr += delay_samples + 1;
771 }
772 delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 5.0f);
773 state.center_delay_line.Initialize(delay_samples, work_buffer_ptr);
774 work_buffer_ptr += delay_samples + 1;
775
776 delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 400.0f);
777 state.early_delay_line.Initialize(delay_samples, work_buffer_ptr);
778
779 UpdateI3dl2Reverb(info, state, true);
780}
781
782void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
783 bool should_clear) {
784
785 state.dry_gain = info.dry_gain;
786 state.shelf_filter.fill(0.0f);
787 state.lowpass_0 = 0.0f;
788 state.early_gain = Pow10(std::min(info.room + info.reflection, 5000.0f) / 2000.0f);
789 state.late_gain = Pow10(std::min(info.room + info.reverb, 5000.0f) / 2000.0f);
790
791 const auto sample_rate = info.sample_rate / 1000;
792 const f32 hf_gain = Pow10(info.room_hf / 2000.0f);
793 if (hf_gain >= 1.0f) {
794 state.lowpass_2 = 1.0f;
795 state.lowpass_1 = 0.0f;
796 } else {
797 const auto a = 1.0f - hf_gain;
798 const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference /
799 static_cast<f32>(info.sample_rate)));
800 const auto c = std::sqrt(b * b - 4.0f * a * a);
801
802 state.lowpass_1 = (b - c) / (2.0f * a);
803 state.lowpass_2 = 1.0f - state.lowpass_1;
804 }
805 state.early_to_late_taps = AudioCommon::CalculateDelaySamples(
806 sample_rate, 1000.0f * (info.reflection_delay + info.reverb_delay));
807
808 state.last_reverb_echo = 0.6f * info.diffusion * 0.01f;
809 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
810 const auto length =
811 FDN_MIN_DELAY_LINE_TIMES[i] +
812 (info.density / 100.0f) * (FDN_MAX_DELAY_LINE_TIMES[i] - FDN_MIN_DELAY_LINE_TIMES[i]);
813 state.fdn_delay_line[i].SetDelay(AudioCommon::CalculateDelaySamples(sample_rate, length));
814
815 const auto delay_sample_counts = state.fdn_delay_line[i].GetDelay() +
816 state.decay_delay_line0[i].GetDelay() +
817 state.decay_delay_line1[i].GetDelay();
818
819 float a = (-60.0f * static_cast<f32>(delay_sample_counts)) /
820 (info.decay_time * static_cast<f32>(info.sample_rate));
821 float b = a / info.hf_decay_ratio;
822 float c = CosD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate)) /
823 SinD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate));
824 float d = Pow10((b - a) / 40.0f);
825 float e = Pow10((b + a) / 40.0f) * 0.7071f;
826
827 state.lpf_coefficients[0][i] = e * ((d * c) + 1.0f) / (c + d);
828 state.lpf_coefficients[1][i] = e * (1.0f - (d * c)) / (c + d);
829 state.lpf_coefficients[2][i] = (c - d) / (c + d);
830
831 state.decay_delay_line0[i].SetCoefficient(state.last_reverb_echo);
832 state.decay_delay_line1[i].SetCoefficient(-0.9f * state.last_reverb_echo);
833 }
834
835 if (should_clear) {
836 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
837 state.fdn_delay_line[i].Clear();
838 state.decay_delay_line0[i].Clear();
839 state.decay_delay_line1[i].Clear();
840 }
841 state.early_delay_line.Clear();
842 state.center_delay_line.Clear();
843 }
844
845 const auto max_early_delay = state.early_delay_line.GetMaxDelay();
846 const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f);
847 for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) {
848 const auto length = AudioCommon::CalculateDelaySamples(
849 sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]);
850 state.early_tap_steps[tap] = std::min(length, max_early_delay);
851 }
852}
853
531void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume, 854void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
532 s32 channel, s32 node_id) { 855 s32 channel, s32 node_id) {
533 const auto last = static_cast<s32>(last_volume * 32768.0f); 856 const auto last = static_cast<s32>(last_volume * 32768.0f);
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h
index b937350b1..2ebb755b0 100644
--- a/src/audio_core/command_generator.h
+++ b/src/audio_core/command_generator.h
@@ -21,6 +21,8 @@ class ServerMixInfo;
21class EffectContext; 21class EffectContext;
22class EffectBase; 22class EffectBase;
23struct AuxInfoDSP; 23struct AuxInfoDSP;
24struct I3dl2ReverbParams;
25struct I3dl2ReverbState;
24using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>; 26using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
25 27
26class CommandGenerator { 28class CommandGenerator {
@@ -80,6 +82,9 @@ private:
80 s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data, 82 s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
81 u32 sample_count, u32 read_offset, u32 read_count); 83 u32 sample_count, u32 read_offset, u32 read_count);
82 84
85 void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
86 std::vector<u8>& work_buffer);
87 void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
83 // DSP Code 88 // DSP Code
84 s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, 89 s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
85 s32 channel, std::size_t mix_offset); 90 s32 channel, std::size_t mix_offset);
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index ec59a3ba9..fe546c55d 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -33,6 +33,29 @@ constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this
33// and our const ends up being 0x3f04, the 4 bytes are most 33// and our const ends up being 0x3f04, the 4 bytes are most
34// likely the sample history 34// likely the sample history
35constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY; 35constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
36constexpr f32 I3DL2REVERB_MAX_LEVEL = 5000.0f;
37constexpr f32 I3DL2REVERB_MIN_REFLECTION_DURATION = 0.02f;
38constexpr std::size_t I3DL2REVERB_TAPS = 20;
39constexpr std::size_t I3DL2REVERB_DELAY_LINE_COUNT = 4;
40using Fractional = s32;
41
42template <typename T>
43constexpr Fractional ToFractional(T x) {
44 return static_cast<Fractional>(x * static_cast<T>(0x4000));
45}
46
47constexpr Fractional MultiplyFractional(Fractional lhs, Fractional rhs) {
48 return static_cast<Fractional>(static_cast<s64>(lhs) * rhs >> 14);
49}
50
51constexpr s32 FractionalToFixed(Fractional x) {
52 const auto s = x & (1 << 13);
53 return static_cast<s32>(x >> 14) + s;
54}
55
56constexpr s32 CalculateDelaySamples(s32 sample_rate_khz, float time) {
57 return FractionalToFixed(MultiplyFractional(ToFractional(sample_rate_khz), ToFractional(time)));
58}
36 59
37static constexpr u32 VersionFromRevision(u32_le rev) { 60static constexpr u32 VersionFromRevision(u32_le rev) {
38 // "REV7" -> 7 61 // "REV7" -> 7
diff --git a/src/audio_core/delay_line.cpp b/src/audio_core/delay_line.cpp
new file mode 100644
index 000000000..f4e4dd8d2
--- /dev/null
+++ b/src/audio_core/delay_line.cpp
@@ -0,0 +1,104 @@
1#include <cstring>
2#include "audio_core/delay_line.h"
3
4namespace AudioCore {
5DelayLineBase::DelayLineBase() = default;
6DelayLineBase::~DelayLineBase() = default;
7
8void DelayLineBase::Initialize(s32 max_delay_, float* src_buffer) {
9 buffer = src_buffer;
10 buffer_end = buffer + max_delay_;
11 max_delay = max_delay_;
12 output = buffer;
13 SetDelay(max_delay_);
14 Clear();
15}
16
17void DelayLineBase::SetDelay(s32 new_delay) {
18 if (max_delay < new_delay) {
19 return;
20 }
21 delay = new_delay;
22 input = (buffer + ((output - buffer) + new_delay) % (max_delay + 1));
23}
24
25s32 DelayLineBase::GetDelay() const {
26 return delay;
27}
28
29s32 DelayLineBase::GetMaxDelay() const {
30 return max_delay;
31}
32
33f32 DelayLineBase::TapOut(s32 last_sample) {
34 const float* ptr = input - (last_sample + 1);
35 if (ptr < buffer) {
36 ptr += (max_delay + 1);
37 }
38
39 return *ptr;
40}
41
42f32 DelayLineBase::Tick(f32 sample) {
43 *(input++) = sample;
44 const auto out_sample = *(output++);
45
46 if (buffer_end < input) {
47 input = buffer;
48 }
49
50 if (buffer_end < output) {
51 output = buffer;
52 }
53
54 return out_sample;
55}
56
57float* DelayLineBase::GetInput() {
58 return input;
59}
60
61const float* DelayLineBase::GetInput() const {
62 return input;
63}
64
65f32 DelayLineBase::GetOutputSample() const {
66 return *output;
67}
68
69void DelayLineBase::Clear() {
70 std::memset(buffer, 0, sizeof(float) * max_delay);
71}
72
73void DelayLineBase::Reset() {
74 buffer = nullptr;
75 buffer_end = nullptr;
76 max_delay = 0;
77 input = nullptr;
78 output = nullptr;
79 delay = 0;
80}
81
82DelayLineAllPass::DelayLineAllPass() = default;
83DelayLineAllPass::~DelayLineAllPass() = default;
84
85void DelayLineAllPass::Initialize(u32 delay_, float coeffcient_, f32* src_buffer) {
86 DelayLineBase::Initialize(delay_, src_buffer);
87 SetCoefficient(coeffcient_);
88}
89
90void DelayLineAllPass::SetCoefficient(float coeffcient_) {
91 coefficient = coeffcient_;
92}
93
94f32 DelayLineAllPass::Tick(f32 sample) {
95 const auto temp = sample - coefficient * *output;
96 return coefficient * temp + DelayLineBase::Tick(temp);
97}
98
99void DelayLineAllPass::Reset() {
100 coefficient = 0.0f;
101 DelayLineBase::Reset();
102}
103
104} // namespace AudioCore
diff --git a/src/audio_core/delay_line.h b/src/audio_core/delay_line.h
new file mode 100644
index 000000000..cafddd432
--- /dev/null
+++ b/src/audio_core/delay_line.h
@@ -0,0 +1,46 @@
1#pragma once
2
3#include "common/common_types.h"
4
5namespace AudioCore {
6
7class DelayLineBase {
8public:
9 DelayLineBase();
10 ~DelayLineBase();
11
12 void Initialize(s32 max_delay_, float* src_buffer);
13 void SetDelay(s32 new_delay);
14 s32 GetDelay() const;
15 s32 GetMaxDelay() const;
16 f32 TapOut(s32 last_sample);
17 f32 Tick(f32 sample);
18 float* GetInput();
19 const float* GetInput() const;
20 f32 GetOutputSample() const;
21 void Clear();
22 void Reset();
23
24protected:
25 float* buffer{nullptr};
26 float* buffer_end{nullptr};
27 s32 max_delay{};
28 float* input{nullptr};
29 float* output{nullptr};
30 s32 delay{};
31};
32
33class DelayLineAllPass final : public DelayLineBase {
34public:
35 DelayLineAllPass();
36 ~DelayLineAllPass();
37
38 void Initialize(u32 delay, float coeffcient_, f32* src_buffer);
39 void SetCoefficient(float coeffcient_);
40 f32 Tick(f32 sample);
41 void Reset();
42
43private:
44 float coefficient{};
45};
46} // namespace AudioCore
diff --git a/src/audio_core/effect_context.cpp b/src/audio_core/effect_context.cpp
index f770b9608..89e4573c7 100644
--- a/src/audio_core/effect_context.cpp
+++ b/src/audio_core/effect_context.cpp
@@ -90,6 +90,14 @@ s32 EffectBase::GetProcessingOrder() const {
90 return processing_order; 90 return processing_order;
91} 91}
92 92
93std::vector<u8>& EffectBase::GetWorkBuffer() {
94 return work_buffer;
95}
96
97const std::vector<u8>& EffectBase::GetWorkBuffer() const {
98 return work_buffer;
99}
100
93EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {} 101EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
94EffectI3dl2Reverb::~EffectI3dl2Reverb() = default; 102EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
95 103
@@ -117,6 +125,12 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
117 usage = UsageState::Initialized; 125 usage = UsageState::Initialized;
118 params.status = ParameterStatus::Initialized; 126 params.status = ParameterStatus::Initialized;
119 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0; 127 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
128 if (!skipped) {
129 auto& cur_work_buffer = GetWorkBuffer();
130 // Has two buffers internally
131 cur_work_buffer.resize(in_params.buffer_size * 2);
132 std::fill(cur_work_buffer.begin(), cur_work_buffer.end(), 0);
133 }
120 } 134 }
121} 135}
122 136
@@ -129,6 +143,14 @@ void EffectI3dl2Reverb::UpdateForCommandGeneration() {
129 GetParams().status = ParameterStatus::Updated; 143 GetParams().status = ParameterStatus::Updated;
130} 144}
131 145
146I3dl2ReverbState& EffectI3dl2Reverb::GetState() {
147 return state;
148}
149
150const I3dl2ReverbState& EffectI3dl2Reverb::GetState() const {
151 return state;
152}
153
132EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {} 154EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
133EffectBiquadFilter::~EffectBiquadFilter() = default; 155EffectBiquadFilter::~EffectBiquadFilter() = default;
134 156
diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h
index c5e0b398c..5e0655dd7 100644
--- a/src/audio_core/effect_context.h
+++ b/src/audio_core/effect_context.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <vector> 9#include <vector>
10#include "audio_core/common.h" 10#include "audio_core/common.h"
11#include "audio_core/delay_line.h"
11#include "common/common_funcs.h" 12#include "common/common_funcs.h"
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/swap.h" 14#include "common/swap.h"
@@ -194,6 +195,8 @@ public:
194 [[nodiscard]] bool IsEnabled() const; 195 [[nodiscard]] bool IsEnabled() const;
195 [[nodiscard]] s32 GetMixID() const; 196 [[nodiscard]] s32 GetMixID() const;
196 [[nodiscard]] s32 GetProcessingOrder() const; 197 [[nodiscard]] s32 GetProcessingOrder() const;
198 [[nodiscard]] std::vector<u8>& GetWorkBuffer();
199 [[nodiscard]] const std::vector<u8>& GetWorkBuffer() const;
197 200
198protected: 201protected:
199 UsageState usage{UsageState::Invalid}; 202 UsageState usage{UsageState::Invalid};
@@ -201,6 +204,7 @@ protected:
201 s32 mix_id{}; 204 s32 mix_id{};
202 s32 processing_order{}; 205 s32 processing_order{};
203 bool enabled = false; 206 bool enabled = false;
207 std::vector<u8> work_buffer{};
204}; 208};
205 209
206template <typename T> 210template <typename T>
@@ -212,7 +216,7 @@ public:
212 return internal_params; 216 return internal_params;
213 } 217 }
214 218
215 const I3dl2ReverbParams& GetParams() const { 219 const T& GetParams() const {
216 return internal_params; 220 return internal_params;
217 } 221 }
218 222
@@ -229,6 +233,27 @@ public:
229 void UpdateForCommandGeneration() override; 233 void UpdateForCommandGeneration() override;
230}; 234};
231 235
236struct I3dl2ReverbState {
237 f32 lowpass_0{};
238 f32 lowpass_1{};
239 f32 lowpass_2{};
240
241 DelayLineBase early_delay_line{};
242 std::array<u32, AudioCommon::I3DL2REVERB_TAPS> early_tap_steps{};
243 f32 early_gain{};
244 f32 late_gain{};
245
246 u32 early_to_late_taps{};
247 std::array<DelayLineBase, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fdn_delay_line{};
248 std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line0{};
249 std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line1{};
250 f32 last_reverb_echo{};
251 DelayLineBase center_delay_line{};
252 std::array<std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>, 3> lpf_coefficients{};
253 std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> shelf_filter{};
254 f32 dry_gain{};
255};
256
232class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> { 257class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
233public: 258public:
234 explicit EffectI3dl2Reverb(); 259 explicit EffectI3dl2Reverb();
@@ -237,8 +262,12 @@ public:
237 void Update(EffectInfo::InParams& in_params) override; 262 void Update(EffectInfo::InParams& in_params) override;
238 void UpdateForCommandGeneration() override; 263 void UpdateForCommandGeneration() override;
239 264
265 I3dl2ReverbState& GetState();
266 const I3dl2ReverbState& GetState() const;
267
240private: 268private:
241 bool skipped = false; 269 bool skipped = false;
270 I3dl2ReverbState state{};
242}; 271};
243 272
244class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> { 273class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 263c457cd..b657506b1 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -168,7 +168,6 @@ add_library(common STATIC
168 time_zone.cpp 168 time_zone.cpp
169 time_zone.h 169 time_zone.h
170 tree.h 170 tree.h
171 uint128.cpp
172 uint128.h 171 uint128.h
173 uuid.cpp 172 uuid.cpp
174 uuid.h 173 uuid.h
diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp
index 4e1d874b5..66218fc21 100644
--- a/src/common/cityhash.cpp
+++ b/src/common/cityhash.cpp
@@ -28,8 +28,10 @@
28// compromising on hash quality. 28// compromising on hash quality.
29 29
30#include <algorithm> 30#include <algorithm>
31#include <string.h> // for memcpy and memset 31#include <cstring>
32#include "cityhash.h" 32#include <utility>
33
34#include "common/cityhash.h"
33#include "common/swap.h" 35#include "common/swap.h"
34 36
35// #include "config.h" 37// #include "config.h"
@@ -42,21 +44,17 @@
42 44
43using namespace std; 45using namespace std;
44 46
45typedef uint8_t uint8;
46typedef uint32_t uint32;
47typedef uint64_t uint64;
48
49namespace Common { 47namespace Common {
50 48
51static uint64 UNALIGNED_LOAD64(const char* p) { 49static u64 unaligned_load64(const char* p) {
52 uint64 result; 50 u64 result;
53 memcpy(&result, p, sizeof(result)); 51 std::memcpy(&result, p, sizeof(result));
54 return result; 52 return result;
55} 53}
56 54
57static uint32 UNALIGNED_LOAD32(const char* p) { 55static u32 unaligned_load32(const char* p) {
58 uint32 result; 56 u32 result;
59 memcpy(&result, p, sizeof(result)); 57 std::memcpy(&result, p, sizeof(result));
60 return result; 58 return result;
61} 59}
62 60
@@ -76,64 +74,64 @@ static uint32 UNALIGNED_LOAD32(const char* p) {
76#endif 74#endif
77#endif 75#endif
78 76
79static uint64 Fetch64(const char* p) { 77static u64 Fetch64(const char* p) {
80 return uint64_in_expected_order(UNALIGNED_LOAD64(p)); 78 return uint64_in_expected_order(unaligned_load64(p));
81} 79}
82 80
83static uint32 Fetch32(const char* p) { 81static u32 Fetch32(const char* p) {
84 return uint32_in_expected_order(UNALIGNED_LOAD32(p)); 82 return uint32_in_expected_order(unaligned_load32(p));
85} 83}
86 84
87// Some primes between 2^63 and 2^64 for various uses. 85// Some primes between 2^63 and 2^64 for various uses.
88static const uint64 k0 = 0xc3a5c85c97cb3127ULL; 86static constexpr u64 k0 = 0xc3a5c85c97cb3127ULL;
89static const uint64 k1 = 0xb492b66fbe98f273ULL; 87static constexpr u64 k1 = 0xb492b66fbe98f273ULL;
90static const uint64 k2 = 0x9ae16a3b2f90404fULL; 88static constexpr u64 k2 = 0x9ae16a3b2f90404fULL;
91 89
92// Bitwise right rotate. Normally this will compile to a single 90// Bitwise right rotate. Normally this will compile to a single
93// instruction, especially if the shift is a manifest constant. 91// instruction, especially if the shift is a manifest constant.
94static uint64 Rotate(uint64 val, int shift) { 92static u64 Rotate(u64 val, int shift) {
95 // Avoid shifting by 64: doing so yields an undefined result. 93 // Avoid shifting by 64: doing so yields an undefined result.
96 return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); 94 return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
97} 95}
98 96
99static uint64 ShiftMix(uint64 val) { 97static u64 ShiftMix(u64 val) {
100 return val ^ (val >> 47); 98 return val ^ (val >> 47);
101} 99}
102 100
103static uint64 HashLen16(uint64 u, uint64 v) { 101static u64 HashLen16(u64 u, u64 v) {
104 return Hash128to64(uint128(u, v)); 102 return Hash128to64(u128{u, v});
105} 103}
106 104
107static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { 105static u64 HashLen16(u64 u, u64 v, u64 mul) {
108 // Murmur-inspired hashing. 106 // Murmur-inspired hashing.
109 uint64 a = (u ^ v) * mul; 107 u64 a = (u ^ v) * mul;
110 a ^= (a >> 47); 108 a ^= (a >> 47);
111 uint64 b = (v ^ a) * mul; 109 u64 b = (v ^ a) * mul;
112 b ^= (b >> 47); 110 b ^= (b >> 47);
113 b *= mul; 111 b *= mul;
114 return b; 112 return b;
115} 113}
116 114
117static uint64 HashLen0to16(const char* s, std::size_t len) { 115static u64 HashLen0to16(const char* s, size_t len) {
118 if (len >= 8) { 116 if (len >= 8) {
119 uint64 mul = k2 + len * 2; 117 u64 mul = k2 + len * 2;
120 uint64 a = Fetch64(s) + k2; 118 u64 a = Fetch64(s) + k2;
121 uint64 b = Fetch64(s + len - 8); 119 u64 b = Fetch64(s + len - 8);
122 uint64 c = Rotate(b, 37) * mul + a; 120 u64 c = Rotate(b, 37) * mul + a;
123 uint64 d = (Rotate(a, 25) + b) * mul; 121 u64 d = (Rotate(a, 25) + b) * mul;
124 return HashLen16(c, d, mul); 122 return HashLen16(c, d, mul);
125 } 123 }
126 if (len >= 4) { 124 if (len >= 4) {
127 uint64 mul = k2 + len * 2; 125 u64 mul = k2 + len * 2;
128 uint64 a = Fetch32(s); 126 u64 a = Fetch32(s);
129 return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); 127 return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
130 } 128 }
131 if (len > 0) { 129 if (len > 0) {
132 uint8 a = s[0]; 130 u8 a = s[0];
133 uint8 b = s[len >> 1]; 131 u8 b = s[len >> 1];
134 uint8 c = s[len - 1]; 132 u8 c = s[len - 1];
135 uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8); 133 u32 y = static_cast<u32>(a) + (static_cast<u32>(b) << 8);
136 uint32 z = static_cast<uint32>(len) + (static_cast<uint32>(c) << 2); 134 u32 z = static_cast<u32>(len) + (static_cast<u32>(c) << 2);
137 return ShiftMix(y * k2 ^ z * k0) * k2; 135 return ShiftMix(y * k2 ^ z * k0) * k2;
138 } 136 }
139 return k2; 137 return k2;
@@ -141,22 +139,21 @@ static uint64 HashLen0to16(const char* s, std::size_t len) {
141 139
142// This probably works well for 16-byte strings as well, but it may be overkill 140// This probably works well for 16-byte strings as well, but it may be overkill
143// in that case. 141// in that case.
144static uint64 HashLen17to32(const char* s, std::size_t len) { 142static u64 HashLen17to32(const char* s, size_t len) {
145 uint64 mul = k2 + len * 2; 143 u64 mul = k2 + len * 2;
146 uint64 a = Fetch64(s) * k1; 144 u64 a = Fetch64(s) * k1;
147 uint64 b = Fetch64(s + 8); 145 u64 b = Fetch64(s + 8);
148 uint64 c = Fetch64(s + len - 8) * mul; 146 u64 c = Fetch64(s + len - 8) * mul;
149 uint64 d = Fetch64(s + len - 16) * k2; 147 u64 d = Fetch64(s + len - 16) * k2;
150 return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul); 148 return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul);
151} 149}
152 150
153// Return a 16-byte hash for 48 bytes. Quick and dirty. 151// Return a 16-byte hash for 48 bytes. Quick and dirty.
154// Callers do best to use "random-looking" values for a and b. 152// Callers do best to use "random-looking" values for a and b.
155static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, 153static pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) {
156 uint64 b) {
157 a += w; 154 a += w;
158 b = Rotate(b + a + z, 21); 155 b = Rotate(b + a + z, 21);
159 uint64 c = a; 156 u64 c = a;
160 a += x; 157 a += x;
161 a += y; 158 a += y;
162 b += Rotate(a, 44); 159 b += Rotate(a, 44);
@@ -164,34 +161,34 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y,
164} 161}
165 162
166// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. 163// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
167static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) { 164static pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) {
168 return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a, 165 return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a,
169 b); 166 b);
170} 167}
171 168
172// Return an 8-byte hash for 33 to 64 bytes. 169// Return an 8-byte hash for 33 to 64 bytes.
173static uint64 HashLen33to64(const char* s, std::size_t len) { 170static u64 HashLen33to64(const char* s, size_t len) {
174 uint64 mul = k2 + len * 2; 171 u64 mul = k2 + len * 2;
175 uint64 a = Fetch64(s) * k2; 172 u64 a = Fetch64(s) * k2;
176 uint64 b = Fetch64(s + 8); 173 u64 b = Fetch64(s + 8);
177 uint64 c = Fetch64(s + len - 24); 174 u64 c = Fetch64(s + len - 24);
178 uint64 d = Fetch64(s + len - 32); 175 u64 d = Fetch64(s + len - 32);
179 uint64 e = Fetch64(s + 16) * k2; 176 u64 e = Fetch64(s + 16) * k2;
180 uint64 f = Fetch64(s + 24) * 9; 177 u64 f = Fetch64(s + 24) * 9;
181 uint64 g = Fetch64(s + len - 8); 178 u64 g = Fetch64(s + len - 8);
182 uint64 h = Fetch64(s + len - 16) * mul; 179 u64 h = Fetch64(s + len - 16) * mul;
183 uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; 180 u64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
184 uint64 v = ((a + g) ^ d) + f + 1; 181 u64 v = ((a + g) ^ d) + f + 1;
185 uint64 w = swap64((u + v) * mul) + h; 182 u64 w = swap64((u + v) * mul) + h;
186 uint64 x = Rotate(e + f, 42) + c; 183 u64 x = Rotate(e + f, 42) + c;
187 uint64 y = (swap64((v + w) * mul) + g) * mul; 184 u64 y = (swap64((v + w) * mul) + g) * mul;
188 uint64 z = e + f + c; 185 u64 z = e + f + c;
189 a = swap64((x + z) * mul + y) + b; 186 a = swap64((x + z) * mul + y) + b;
190 b = ShiftMix((z + a) * mul + d + h) * mul; 187 b = ShiftMix((z + a) * mul + d + h) * mul;
191 return b + x; 188 return b + x;
192} 189}
193 190
194uint64 CityHash64(const char* s, std::size_t len) { 191u64 CityHash64(const char* s, size_t len) {
195 if (len <= 32) { 192 if (len <= 32) {
196 if (len <= 16) { 193 if (len <= 16) {
197 return HashLen0to16(s, len); 194 return HashLen0to16(s, len);
@@ -204,15 +201,15 @@ uint64 CityHash64(const char* s, std::size_t len) {
204 201
205 // For strings over 64 bytes we hash the end first, and then as we 202 // For strings over 64 bytes we hash the end first, and then as we
206 // loop we keep 56 bytes of state: v, w, x, y, and z. 203 // loop we keep 56 bytes of state: v, w, x, y, and z.
207 uint64 x = Fetch64(s + len - 40); 204 u64 x = Fetch64(s + len - 40);
208 uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); 205 u64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
209 uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); 206 u64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
210 pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z); 207 pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
211 pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); 208 pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
212 x = x * k1 + Fetch64(s); 209 x = x * k1 + Fetch64(s);
213 210
214 // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. 211 // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
215 len = (len - 1) & ~static_cast<std::size_t>(63); 212 len = (len - 1) & ~static_cast<size_t>(63);
216 do { 213 do {
217 x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 214 x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
218 y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 215 y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
@@ -229,21 +226,21 @@ uint64 CityHash64(const char* s, std::size_t len) {
229 HashLen16(v.second, w.second) + x); 226 HashLen16(v.second, w.second) + x);
230} 227}
231 228
232uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) { 229u64 CityHash64WithSeed(const char* s, size_t len, u64 seed) {
233 return CityHash64WithSeeds(s, len, k2, seed); 230 return CityHash64WithSeeds(s, len, k2, seed);
234} 231}
235 232
236uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) { 233u64 CityHash64WithSeeds(const char* s, size_t len, u64 seed0, u64 seed1) {
237 return HashLen16(CityHash64(s, len) - seed0, seed1); 234 return HashLen16(CityHash64(s, len) - seed0, seed1);
238} 235}
239 236
240// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings 237// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
241// of any length representable in signed long. Based on City and Murmur. 238// of any length representable in signed long. Based on City and Murmur.
242static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) { 239static u128 CityMurmur(const char* s, size_t len, u128 seed) {
243 uint64 a = Uint128Low64(seed); 240 u64 a = seed[0];
244 uint64 b = Uint128High64(seed); 241 u64 b = seed[1];
245 uint64 c = 0; 242 u64 c = 0;
246 uint64 d = 0; 243 u64 d = 0;
247 signed long l = static_cast<long>(len) - 16; 244 signed long l = static_cast<long>(len) - 16;
248 if (l <= 0) { // len <= 16 245 if (l <= 0) { // len <= 16
249 a = ShiftMix(a * k1) * k1; 246 a = ShiftMix(a * k1) * k1;
@@ -266,20 +263,20 @@ static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
266 } 263 }
267 a = HashLen16(a, c); 264 a = HashLen16(a, c);
268 b = HashLen16(d, b); 265 b = HashLen16(d, b);
269 return uint128(a ^ b, HashLen16(b, a)); 266 return u128{a ^ b, HashLen16(b, a)};
270} 267}
271 268
272uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) { 269u128 CityHash128WithSeed(const char* s, size_t len, u128 seed) {
273 if (len < 128) { 270 if (len < 128) {
274 return CityMurmur(s, len, seed); 271 return CityMurmur(s, len, seed);
275 } 272 }
276 273
277 // We expect len >= 128 to be the common case. Keep 56 bytes of state: 274 // We expect len >= 128 to be the common case. Keep 56 bytes of state:
278 // v, w, x, y, and z. 275 // v, w, x, y, and z.
279 pair<uint64, uint64> v, w; 276 pair<u64, u64> v, w;
280 uint64 x = Uint128Low64(seed); 277 u64 x = seed[0];
281 uint64 y = Uint128High64(seed); 278 u64 y = seed[1];
282 uint64 z = len * k1; 279 u64 z = len * k1;
283 v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); 280 v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
284 v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); 281 v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
285 w.first = Rotate(y + z, 35) * k1 + x; 282 w.first = Rotate(y + z, 35) * k1 + x;
@@ -313,7 +310,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
313 w.first *= 9; 310 w.first *= 9;
314 v.first *= k0; 311 v.first *= k0;
315 // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. 312 // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
316 for (std::size_t tail_done = 0; tail_done < len;) { 313 for (size_t tail_done = 0; tail_done < len;) {
317 tail_done += 32; 314 tail_done += 32;
318 y = Rotate(x + y, 42) * k0 + v.second; 315 y = Rotate(x + y, 42) * k0 + v.second;
319 w.first += Fetch64(s + len - tail_done + 16); 316 w.first += Fetch64(s + len - tail_done + 16);
@@ -328,13 +325,12 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
328 // different 56-byte-to-8-byte hashes to get a 16-byte final result. 325 // different 56-byte-to-8-byte hashes to get a 16-byte final result.
329 x = HashLen16(x, v.first); 326 x = HashLen16(x, v.first);
330 y = HashLen16(y + z, w.first); 327 y = HashLen16(y + z, w.first);
331 return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); 328 return u128{HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)};
332} 329}
333 330
334uint128 CityHash128(const char* s, std::size_t len) { 331u128 CityHash128(const char* s, size_t len) {
335 return len >= 16 332 return len >= 16 ? CityHash128WithSeed(s + 16, len - 16, u128{Fetch64(s), Fetch64(s + 8) + k0})
336 ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) 333 : CityHash128WithSeed(s, len, u128{k0, k1});
337 : CityHash128WithSeed(s, len, uint128(k0, k1));
338} 334}
339 335
340} // namespace Common 336} // namespace Common
diff --git a/src/common/cityhash.h b/src/common/cityhash.h
index a00804e01..022d0f7cb 100644
--- a/src/common/cityhash.h
+++ b/src/common/cityhash.h
@@ -61,50 +61,38 @@
61 61
62#pragma once 62#pragma once
63 63
64#include <cstddef> 64#include "common/common_types.h"
65#include <cstdint>
66#include <utility>
67 65
68namespace Common { 66namespace Common {
69 67
70using uint128 = std::pair<uint64_t, uint64_t>;
71
72[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
73 return x.first;
74}
75[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
76 return x.second;
77}
78
79// Hash function for a byte array. 68// Hash function for a byte array.
80[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len); 69[[nodiscard]] u64 CityHash64(const char* buf, size_t len);
81 70
82// Hash function for a byte array. For convenience, a 64-bit seed is also 71// Hash function for a byte array. For convenience, a 64-bit seed is also
83// hashed into the result. 72// hashed into the result.
84[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed); 73[[nodiscard]] u64 CityHash64WithSeed(const char* buf, size_t len, u64 seed);
85 74
86// Hash function for a byte array. For convenience, two seeds are also 75// Hash function for a byte array. For convenience, two seeds are also
87// hashed into the result. 76// hashed into the result.
88[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, 77[[nodiscard]] u64 CityHash64WithSeeds(const char* buf, size_t len, u64 seed0, u64 seed1);
89 uint64_t seed1);
90 78
91// Hash function for a byte array. 79// Hash function for a byte array.
92[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len); 80[[nodiscard]] u128 CityHash128(const char* s, size_t len);
93 81
94// Hash function for a byte array. For convenience, a 128-bit seed is also 82// Hash function for a byte array. For convenience, a 128-bit seed is also
95// hashed into the result. 83// hashed into the result.
96[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed); 84[[nodiscard]] u128 CityHash128WithSeed(const char* s, size_t len, u128 seed);
97 85
98// Hash 128 input bits down to 64 bits of output. 86// Hash 128 input bits down to 64 bits of output.
99// This is intended to be a reasonably good hash function. 87// This is intended to be a reasonably good hash function.
100[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) { 88[[nodiscard]] inline u64 Hash128to64(const u128& x) {
101 // Murmur-inspired hashing. 89 // Murmur-inspired hashing.
102 const uint64_t kMul = 0x9ddfea08eb382d69ULL; 90 const u64 mul = 0x9ddfea08eb382d69ULL;
103 uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 91 u64 a = (x[0] ^ x[1]) * mul;
104 a ^= (a >> 47); 92 a ^= (a >> 47);
105 uint64_t b = (Uint128High64(x) ^ a) * kMul; 93 u64 b = (x[1] ^ a) * mul;
106 b ^= (b >> 47); 94 b ^= (b >> 47);
107 b *= kMul; 95 b *= mul;
108 return b; 96 return b;
109} 97}
110 98
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp
deleted file mode 100644
index 16bf7c828..000000000
--- a/src/common/uint128.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#ifdef _MSC_VER
6#include <intrin.h>
7
8#pragma intrinsic(_umul128)
9#pragma intrinsic(_udiv128)
10#endif
11#include <cstring>
12#include "common/uint128.h"
13
14namespace Common {
15
16#ifdef _MSC_VER
17
18u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
19 u128 r{};
20 r[0] = _umul128(a, b, &r[1]);
21 u64 remainder;
22#if _MSC_VER < 1923
23 return udiv128(r[1], r[0], d, &remainder);
24#else
25 return _udiv128(r[1], r[0], d, &remainder);
26#endif
27}
28
29#else
30
31u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
32 const u64 diva = a / d;
33 const u64 moda = a % d;
34 const u64 divb = b / d;
35 const u64 modb = b % d;
36 return diva * b + moda * divb + moda * modb / d;
37}
38
39#endif
40
41u128 Multiply64Into128(u64 a, u64 b) {
42 u128 result;
43#ifdef _MSC_VER
44 result[0] = _umul128(a, b, &result[1]);
45#else
46 unsigned __int128 tmp = a;
47 tmp *= b;
48 std::memcpy(&result, &tmp, sizeof(u128));
49#endif
50 return result;
51}
52
53std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
54 u64 remainder = dividend[0] % divisor;
55 u64 accum = dividend[0] / divisor;
56 if (dividend[1] == 0)
57 return {accum, remainder};
58 // We ignore dividend[1] / divisor as that overflows
59 const u64 first_segment = (dividend[1] % divisor) << 32;
60 accum += (first_segment / divisor) << 32;
61 const u64 second_segment = (first_segment % divisor) << 32;
62 accum += (second_segment / divisor);
63 remainder += second_segment % divisor;
64 if (remainder >= divisor) {
65 accum++;
66 remainder -= divisor;
67 }
68 return {accum, remainder};
69}
70
71} // namespace Common
diff --git a/src/common/uint128.h b/src/common/uint128.h
index 969259ab6..4780b2f9d 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -4,19 +4,118 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstring>
7#include <utility> 8#include <utility>
9
10#ifdef _MSC_VER
11#include <intrin.h>
12#pragma intrinsic(__umulh)
13#pragma intrinsic(_umul128)
14#pragma intrinsic(_udiv128)
15#else
16#include <x86intrin.h>
17#endif
18
8#include "common/common_types.h" 19#include "common/common_types.h"
9 20
10namespace Common { 21namespace Common {
11 22
12// This function multiplies 2 u64 values and divides it by a u64 value. 23// This function multiplies 2 u64 values and divides it by a u64 value.
13[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d); 24[[nodiscard]] static inline u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
25#ifdef _MSC_VER
26 u128 r{};
27 r[0] = _umul128(a, b, &r[1]);
28 u64 remainder;
29#if _MSC_VER < 1923
30 return udiv128(r[1], r[0], d, &remainder);
31#else
32 return _udiv128(r[1], r[0], d, &remainder);
33#endif
34#else
35 const u64 diva = a / d;
36 const u64 moda = a % d;
37 const u64 divb = b / d;
38 const u64 modb = b % d;
39 return diva * b + moda * divb + moda * modb / d;
40#endif
41}
14 42
15// This function multiplies 2 u64 values and produces a u128 value; 43// This function multiplies 2 u64 values and produces a u128 value;
16[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b); 44[[nodiscard]] static inline u128 Multiply64Into128(u64 a, u64 b) {
45 u128 result;
46#ifdef _MSC_VER
47 result[0] = _umul128(a, b, &result[1]);
48#else
49 unsigned __int128 tmp = a;
50 tmp *= b;
51 std::memcpy(&result, &tmp, sizeof(u128));
52#endif
53 return result;
54}
55
56[[nodiscard]] static inline u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
57#ifdef __SIZEOF_INT128__
58 const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
59 return static_cast<u64>(base / divisor);
60#elif defined(_M_X64) || defined(_M_ARM64)
61 std::array<u64, 2> r = {0, numerator};
62 u64 remainder;
63#if _MSC_VER < 1923
64 return udiv128(r[1], r[0], divisor, &remainder);
65#else
66 return _udiv128(r[1], r[0], divisor, &remainder);
67#endif
68#else
69 // This one is bit more inaccurate.
70 return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
71#endif
72}
73
74[[nodiscard]] static inline u64 MultiplyHigh(u64 a, u64 b) {
75#ifdef __SIZEOF_INT128__
76 return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
77#elif defined(_M_X64) || defined(_M_ARM64)
78 return __umulh(a, b); // MSVC
79#else
80 // Generic fallback
81 const u64 a_lo = u32(a);
82 const u64 a_hi = a >> 32;
83 const u64 b_lo = u32(b);
84 const u64 b_hi = b >> 32;
85
86 const u64 a_x_b_hi = a_hi * b_hi;
87 const u64 a_x_b_mid = a_hi * b_lo;
88 const u64 b_x_a_mid = b_hi * a_lo;
89 const u64 a_x_b_lo = a_lo * b_lo;
90
91 const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
92 static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
93 32;
94
95 const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
96
97 return multhi;
98#endif
99}
17 100
18// This function divides a u128 by a u32 value and produces two u64 values: 101// This function divides a u128 by a u32 value and produces two u64 values:
19// the result of division and the remainder 102// the result of division and the remainder
20[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor); 103[[nodiscard]] static inline std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
104 u64 remainder = dividend[0] % divisor;
105 u64 accum = dividend[0] / divisor;
106 if (dividend[1] == 0)
107 return {accum, remainder};
108 // We ignore dividend[1] / divisor as that overflows
109 const u64 first_segment = (dividend[1] % divisor) << 32;
110 accum += (first_segment / divisor) << 32;
111 const u64 second_segment = (first_segment % divisor) << 32;
112 accum += (second_segment / divisor);
113 remainder += second_segment % divisor;
114 if (remainder >= divisor) {
115 accum++;
116 remainder -= divisor;
117 }
118 return {accum, remainder};
119}
21 120
22} // namespace Common 121} // namespace Common
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index a8c143f85..49830b8ab 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstdint>
6
5#include "common/uint128.h" 7#include "common/uint128.h"
6#include "common/wall_clock.h" 8#include "common/wall_clock.h"
7 9
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index a65f6b832..87de40624 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -8,68 +8,10 @@
8#include <mutex> 8#include <mutex>
9#include <thread> 9#include <thread>
10 10
11#ifdef _MSC_VER
12#include <intrin.h>
13
14#pragma intrinsic(__umulh)
15#pragma intrinsic(_udiv128)
16#else
17#include <x86intrin.h>
18#endif
19
20#include "common/atomic_ops.h" 11#include "common/atomic_ops.h"
21#include "common/uint128.h" 12#include "common/uint128.h"
22#include "common/x64/native_clock.h" 13#include "common/x64/native_clock.h"
23 14
24namespace {
25
26[[nodiscard]] u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
27#ifdef __SIZEOF_INT128__
28 const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
29 return static_cast<u64>(base / divisor);
30#elif defined(_M_X64) || defined(_M_ARM64)
31 std::array<u64, 2> r = {0, numerator};
32 u64 remainder;
33#if _MSC_VER < 1923
34 return udiv128(r[1], r[0], divisor, &remainder);
35#else
36 return _udiv128(r[1], r[0], divisor, &remainder);
37#endif
38#else
39 // This one is bit more inaccurate.
40 return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
41#endif
42}
43
44[[nodiscard]] u64 MultiplyHigh(u64 a, u64 b) {
45#ifdef __SIZEOF_INT128__
46 return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
47#elif defined(_M_X64) || defined(_M_ARM64)
48 return __umulh(a, b); // MSVC
49#else
50 // Generic fallback
51 const u64 a_lo = u32(a);
52 const u64 a_hi = a >> 32;
53 const u64 b_lo = u32(b);
54 const u64 b_hi = b >> 32;
55
56 const u64 a_x_b_hi = a_hi * b_hi;
57 const u64 a_x_b_mid = a_hi * b_lo;
58 const u64 b_x_a_mid = b_hi * a_lo;
59 const u64 a_x_b_lo = a_lo * b_lo;
60
61 const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
62 static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
63 32;
64
65 const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
66
67 return multhi;
68#endif
69}
70
71} // namespace
72
73namespace Common { 15namespace Common {
74 16
75u64 EstimateRDTSCFrequency() { 17u64 EstimateRDTSCFrequency() {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e74e6a668..c6bdf72ec 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -19,7 +19,6 @@ add_library(core STATIC
19 core.h 19 core.h
20 core_timing.cpp 20 core_timing.cpp
21 core_timing.h 21 core_timing.h
22 core_timing_util.cpp
23 core_timing_util.h 22 core_timing_util.h
24 cpu_manager.cpp 23 cpu_manager.cpp
25 cpu_manager.h 24 cpu_manager.h
@@ -266,6 +265,7 @@ add_library(core STATIC
266 hle/service/am/applets/software_keyboard.h 265 hle/service/am/applets/software_keyboard.h
267 hle/service/am/applets/web_browser.cpp 266 hle/service/am/applets/web_browser.cpp
268 hle/service/am/applets/web_browser.h 267 hle/service/am/applets/web_browser.h
268 hle/service/am/applets/web_types.h
269 hle/service/am/idle.cpp 269 hle/service/am/idle.cpp
270 hle/service/am/idle.h 270 hle/service/am/idle.h
271 hle/service/am/omm.cpp 271 hle/service/am/omm.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 30f5e1128..de6305e2a 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -308,6 +308,9 @@ struct System::Impl {
308 // Close all CPU/threading state 308 // Close all CPU/threading state
309 cpu_manager.Shutdown(); 309 cpu_manager.Shutdown();
310 310
311 // Release the Time Manager's resources
312 time_manager.Shutdown();
313
311 // Shutdown kernel and core timing 314 // Shutdown kernel and core timing
312 core_timing.Shutdown(); 315 core_timing.Shutdown();
313 kernel.Shutdown(); 316 kernel.Shutdown();
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
deleted file mode 100644
index 8ce8e602e..000000000
--- a/src/core/core_timing_util.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
1// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include "core/core_timing_util.h"
6
7#include <cinttypes>
8#include <limits>
9#include "common/logging/log.h"
10#include "common/uint128.h"
11#include "core/hardware_properties.h"
12
13namespace Core::Timing {
14
15constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / Hardware::BASE_CLOCK_RATE;
16
17s64 msToCycles(std::chrono::milliseconds ms) {
18 if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
19 LOG_ERROR(Core_Timing, "Integer overflow, use max value");
20 return std::numeric_limits<s64>::max();
21 }
22 if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
23 LOG_DEBUG(Core_Timing, "Time very big, do rounding");
24 return Hardware::BASE_CLOCK_RATE * (ms.count() / 1000);
25 }
26 return (Hardware::BASE_CLOCK_RATE * ms.count()) / 1000;
27}
28
29s64 usToCycles(std::chrono::microseconds us) {
30 if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
31 LOG_ERROR(Core_Timing, "Integer overflow, use max value");
32 return std::numeric_limits<s64>::max();
33 }
34 if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
35 LOG_DEBUG(Core_Timing, "Time very big, do rounding");
36 return Hardware::BASE_CLOCK_RATE * (us.count() / 1000000);
37 }
38 return (Hardware::BASE_CLOCK_RATE * us.count()) / 1000000;
39}
40
41s64 nsToCycles(std::chrono::nanoseconds ns) {
42 const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE);
43 return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first;
44}
45
46u64 msToClockCycles(std::chrono::milliseconds ns) {
47 const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
48 return Common::Divide128On32(temp, 1000).first;
49}
50
51u64 usToClockCycles(std::chrono::microseconds ns) {
52 const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
53 return Common::Divide128On32(temp, 1000000).first;
54}
55
56u64 nsToClockCycles(std::chrono::nanoseconds ns) {
57 const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
58 return Common::Divide128On32(temp, 1000000000).first;
59}
60
61u64 CpuCyclesToClockCycles(u64 ticks) {
62 const u128 temporal = Common::Multiply64Into128(ticks, Hardware::CNTFREQ);
63 return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
64}
65
66std::chrono::milliseconds CyclesToMs(s64 cycles) {
67 const u128 temporal = Common::Multiply64Into128(cycles, 1000);
68 u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
69 return std::chrono::milliseconds(ms);
70}
71
72std::chrono::nanoseconds CyclesToNs(s64 cycles) {
73 const u128 temporal = Common::Multiply64Into128(cycles, 1000000000);
74 u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
75 return std::chrono::nanoseconds(ns);
76}
77
78std::chrono::microseconds CyclesToUs(s64 cycles) {
79 const u128 temporal = Common::Multiply64Into128(cycles, 1000000);
80 u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
81 return std::chrono::microseconds(us);
82}
83
84} // namespace Core::Timing
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index e4a046bf9..14c36a485 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -1,24 +1,59 @@
1// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+ 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#pragma once 5#pragma once
6 6
7#include <chrono> 7#include <chrono>
8
8#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hardware_properties.h"
9 11
10namespace Core::Timing { 12namespace Core::Timing {
11 13
12s64 msToCycles(std::chrono::milliseconds ms); 14namespace detail {
13s64 usToCycles(std::chrono::microseconds us); 15constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000;
14s64 nsToCycles(std::chrono::nanoseconds ns); 16constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000;
15u64 msToClockCycles(std::chrono::milliseconds ns); 17} // namespace detail
16u64 usToClockCycles(std::chrono::microseconds ns); 18
17u64 nsToClockCycles(std::chrono::nanoseconds ns); 19[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) {
18std::chrono::milliseconds CyclesToMs(s64 cycles); 20 return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED;
19std::chrono::nanoseconds CyclesToNs(s64 cycles); 21}
20std::chrono::microseconds CyclesToUs(s64 cycles); 22
21 23[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) {
22u64 CpuCyclesToClockCycles(u64 ticks); 24 return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000;
25}
26
27[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) {
28 return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000;
29}
30
31[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) {
32 return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED;
33}
34
35[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) {
36 return us.count() * detail::CNTFREQ_ADJUSTED / 1000;
37}
38
39[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) {
40 return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000;
41}
42
43[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) {
44 return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED;
45}
46
47[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) {
48 return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED);
49}
50
51[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) {
52 return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED);
53}
54
55[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) {
56 return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED);
57}
23 58
24} // namespace Core::Timing 59} // namespace Core::Timing
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index dff71d8d9..b0626a0f9 100644
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -31,6 +31,7 @@ struct ControllerParameters {
31 bool allow_dual_joycons{}; 31 bool allow_dual_joycons{};
32 bool allow_left_joycon{}; 32 bool allow_left_joycon{};
33 bool allow_right_joycon{}; 33 bool allow_right_joycon{};
34 bool allow_gamecube_controller{};
34}; 35};
35 36
36class ControllerApplet { 37class ControllerApplet {
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index b6e6f115e..39c5182c5 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -101,8 +101,6 @@ struct KernelCore::Impl {
101 101
102 current_process = nullptr; 102 current_process = nullptr;
103 103
104 system_resource_limit = nullptr;
105
106 global_handle_table.Clear(); 104 global_handle_table.Clear();
107 105
108 preemption_event = nullptr; 106 preemption_event = nullptr;
@@ -111,6 +109,13 @@ struct KernelCore::Impl {
111 109
112 exclusive_monitor.reset(); 110 exclusive_monitor.reset();
113 111
112 hid_shared_mem = nullptr;
113 font_shared_mem = nullptr;
114 irs_shared_mem = nullptr;
115 time_shared_mem = nullptr;
116
117 system_resource_limit = nullptr;
118
114 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others 119 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
115 next_host_thread_id = Core::Hardware::NUM_CPU_CORES; 120 next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
116 } 121 }
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index dbf198345..70b9f3824 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -21,6 +21,7 @@
21 21
22namespace Service::HID { 22namespace Service::HID {
23constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 23constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
24constexpr s32 HID_TRIGGER_MAX = 0x7fff;
24[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 25[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
25constexpr std::size_t NPAD_OFFSET = 0x9A00; 26constexpr std::size_t NPAD_OFFSET = 0x9A00;
26constexpr u32 BATTERY_FULL = 2; 27constexpr u32 BATTERY_FULL = 2;
@@ -48,6 +49,8 @@ Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
48 return NPadControllerType::JoyRight; 49 return NPadControllerType::JoyRight;
49 case Settings::ControllerType::Handheld: 50 case Settings::ControllerType::Handheld:
50 return NPadControllerType::Handheld; 51 return NPadControllerType::Handheld;
52 case Settings::ControllerType::GameCube:
53 return NPadControllerType::GameCube;
51 default: 54 default:
52 UNREACHABLE(); 55 UNREACHABLE();
53 return NPadControllerType::ProController; 56 return NPadControllerType::ProController;
@@ -67,6 +70,8 @@ Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
67 return Settings::ControllerType::RightJoycon; 70 return Settings::ControllerType::RightJoycon;
68 case NPadControllerType::Handheld: 71 case NPadControllerType::Handheld:
69 return Settings::ControllerType::Handheld; 72 return Settings::ControllerType::Handheld;
73 case NPadControllerType::GameCube:
74 return Settings::ControllerType::GameCube;
70 default: 75 default:
71 UNREACHABLE(); 76 UNREACHABLE();
72 return Settings::ControllerType::ProController; 77 return Settings::ControllerType::ProController;
@@ -209,6 +214,13 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
209 controller.assignment_mode = NpadAssignments::Single; 214 controller.assignment_mode = NpadAssignments::Single;
210 controller.footer_type = AppletFooterUiType::JoyRightHorizontal; 215 controller.footer_type = AppletFooterUiType::JoyRightHorizontal;
211 break; 216 break;
217 case NPadControllerType::GameCube:
218 controller.style_set.gamecube.Assign(1);
219 // The GC Controller behaves like a wired Pro Controller
220 controller.device_type.fullkey.Assign(1);
221 controller.system_properties.is_vertical.Assign(1);
222 controller.system_properties.use_plus.Assign(1);
223 break;
212 case NPadControllerType::Pokeball: 224 case NPadControllerType::Pokeball:
213 controller.style_set.palma.Assign(1); 225 controller.style_set.palma.Assign(1);
214 controller.device_type.palma.Assign(1); 226 controller.device_type.palma.Assign(1);
@@ -259,6 +271,7 @@ void Controller_NPad::OnInit() {
259 style.joycon_right.Assign(1); 271 style.joycon_right.Assign(1);
260 style.joycon_dual.Assign(1); 272 style.joycon_dual.Assign(1);
261 style.fullkey.Assign(1); 273 style.fullkey.Assign(1);
274 style.gamecube.Assign(1);
262 style.palma.Assign(1); 275 style.palma.Assign(1);
263 } 276 }
264 277
@@ -339,6 +352,7 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
339 auto& pad_state = npad_pad_states[controller_idx].pad_states; 352 auto& pad_state = npad_pad_states[controller_idx].pad_states;
340 auto& lstick_entry = npad_pad_states[controller_idx].l_stick; 353 auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
341 auto& rstick_entry = npad_pad_states[controller_idx].r_stick; 354 auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
355 auto& trigger_entry = npad_trigger_states[controller_idx];
342 const auto& button_state = buttons[controller_idx]; 356 const auto& button_state = buttons[controller_idx];
343 const auto& analog_state = sticks[controller_idx]; 357 const auto& analog_state = sticks[controller_idx];
344 const auto [stick_l_x_f, stick_l_y_f] = 358 const auto [stick_l_x_f, stick_l_y_f] =
@@ -404,6 +418,17 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
404 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); 418 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
405 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); 419 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
406 } 420 }
421
422 if (controller_type == NPadControllerType::GameCube) {
423 trigger_entry.l_analog = static_cast<s32>(
424 button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
425 trigger_entry.r_analog = static_cast<s32>(
426 button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
427 pad_state.zl.Assign(false);
428 pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
429 pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
430 pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
431 }
407} 432}
408 433
409void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 434void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
@@ -418,6 +443,11 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
418 &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states, 443 &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states,
419 &npad.system_ext_states}; 444 &npad.system_ext_states};
420 445
446 // There is the posibility to have more controllers with analog triggers
447 const std::array<TriggerGeneric*, 1> controller_triggers{
448 &npad.gc_trigger_states,
449 };
450
421 for (auto* main_controller : controller_npads) { 451 for (auto* main_controller : controller_npads) {
422 main_controller->common.entry_count = 16; 452 main_controller->common.entry_count = 16;
423 main_controller->common.total_entry_count = 17; 453 main_controller->common.total_entry_count = 17;
@@ -435,6 +465,21 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
435 cur_entry.timestamp2 = cur_entry.timestamp; 465 cur_entry.timestamp2 = cur_entry.timestamp;
436 } 466 }
437 467
468 for (auto* analog_trigger : controller_triggers) {
469 analog_trigger->entry_count = 16;
470 analog_trigger->total_entry_count = 17;
471
472 const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
473
474 analog_trigger->timestamp = core_timing.GetCPUTicks();
475 analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
476
477 auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
478
479 cur_entry.timestamp = last_entry.timestamp + 1;
480 cur_entry.timestamp2 = cur_entry.timestamp;
481 }
482
438 const auto& controller_type = connected_controllers[i].type; 483 const auto& controller_type = connected_controllers[i].type;
439 484
440 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { 485 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
@@ -444,6 +489,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
444 489
445 RequestPadStateUpdate(npad_index); 490 RequestPadStateUpdate(npad_index);
446 auto& pad_state = npad_pad_states[npad_index]; 491 auto& pad_state = npad_pad_states[npad_index];
492 auto& trigger_state = npad_trigger_states[npad_index];
447 493
448 auto& main_controller = 494 auto& main_controller =
449 npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index]; 495 npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index];
@@ -456,6 +502,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
456 auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index]; 502 auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];
457 auto& libnx_entry = 503 auto& libnx_entry =
458 npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index]; 504 npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index];
505 auto& trigger_entry =
506 npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];
459 507
460 libnx_entry.connection_status.raw = 0; 508 libnx_entry.connection_status.raw = 0;
461 libnx_entry.connection_status.is_connected.Assign(1); 509 libnx_entry.connection_status.is_connected.Assign(1);
@@ -524,6 +572,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
524 572
525 libnx_entry.connection_status.is_right_connected.Assign(1); 573 libnx_entry.connection_status.is_right_connected.Assign(1);
526 break; 574 break;
575 case NPadControllerType::GameCube:
576 main_controller.connection_status.raw = 0;
577 main_controller.connection_status.is_connected.Assign(1);
578 main_controller.connection_status.is_wired.Assign(1);
579 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
580 main_controller.pad.l_stick = pad_state.l_stick;
581 main_controller.pad.r_stick = pad_state.r_stick;
582 trigger_entry.l_analog = trigger_state.l_analog;
583 trigger_entry.r_analog = trigger_state.r_analog;
584
585 libnx_entry.connection_status.is_wired.Assign(1);
586 break;
527 case NPadControllerType::Pokeball: 587 case NPadControllerType::Pokeball:
528 pokeball_entry.connection_status.raw = 0; 588 pokeball_entry.connection_status.raw = 0;
529 pokeball_entry.connection_status.is_connected.Assign(1); 589 pokeball_entry.connection_status.is_connected.Assign(1);
@@ -674,6 +734,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
674 right_sixaxis_entry.orientation = motion_devices[1].orientation; 734 right_sixaxis_entry.orientation = motion_devices[1].orientation;
675 } 735 }
676 break; 736 break;
737 case NPadControllerType::GameCube:
677 case NPadControllerType::Pokeball: 738 case NPadControllerType::Pokeball:
678 break; 739 break;
679 } 740 }
@@ -1135,6 +1196,8 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
1135 return style.joycon_left; 1196 return style.joycon_left;
1136 case NPadControllerType::JoyRight: 1197 case NPadControllerType::JoyRight:
1137 return style.joycon_right; 1198 return style.joycon_right;
1199 case NPadControllerType::GameCube:
1200 return style.gamecube;
1138 case NPadControllerType::Pokeball: 1201 case NPadControllerType::Pokeball:
1139 return style.palma; 1202 return style.palma;
1140 default: 1203 default:
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 48bab988c..bc2e6779d 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -51,6 +51,7 @@ public:
51 JoyDual, 51 JoyDual,
52 JoyLeft, 52 JoyLeft,
53 JoyRight, 53 JoyRight,
54 GameCube,
54 Pokeball, 55 Pokeball,
55 }; 56 };
56 57
@@ -60,6 +61,7 @@ public:
60 JoyconDual = 5, 61 JoyconDual = 5,
61 JoyconLeft = 6, 62 JoyconLeft = 6,
62 JoyconRight = 7, 63 JoyconRight = 7,
64 GameCube = 8,
63 Pokeball = 9, 65 Pokeball = 9,
64 MaxNpadType = 10, 66 MaxNpadType = 10,
65 }; 67 };
@@ -389,6 +391,25 @@ private:
389 }; 391 };
390 static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size"); 392 static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
391 393
394 struct TriggerState {
395 s64_le timestamp{};
396 s64_le timestamp2{};
397 s32_le l_analog{};
398 s32_le r_analog{};
399 };
400 static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size");
401
402 struct TriggerGeneric {
403 INSERT_PADDING_BYTES(0x4);
404 s64_le timestamp;
405 INSERT_PADDING_BYTES(0x4);
406 s64_le total_entry_count;
407 s64_le last_entry_index;
408 s64_le entry_count;
409 std::array<TriggerState, 17> trigger{};
410 };
411 static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
412
392 struct NPadSystemProperties { 413 struct NPadSystemProperties {
393 union { 414 union {
394 s64_le raw{}; 415 s64_le raw{};
@@ -509,7 +530,9 @@ private:
509 AppletFooterUiType footer_type; 530 AppletFooterUiType footer_type;
510 // nfc_states needs to be checked switchbrew does not match with HW 531 // nfc_states needs to be checked switchbrew does not match with HW
511 NfcXcdHandle nfc_states; 532 NfcXcdHandle nfc_states;
512 INSERT_PADDING_BYTES(0xdef); 533 INSERT_PADDING_BYTES(0x8); // Mutex
534 TriggerGeneric gc_trigger_states;
535 INSERT_PADDING_BYTES(0xc1f);
513 }; 536 };
514 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); 537 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
515 538
@@ -560,6 +583,7 @@ private:
560 f32 sixaxis_fusion_parameter2{}; 583 f32 sixaxis_fusion_parameter2{};
561 bool sixaxis_at_rest{true}; 584 bool sixaxis_at_rest{true};
562 std::array<ControllerPad, 10> npad_pad_states{}; 585 std::array<ControllerPad, 10> npad_pad_states{};
586 std::array<TriggerState, 10> npad_trigger_states{};
563 bool is_in_lr_assignment_mode{false}; 587 bool is_in_lr_assignment_mode{false};
564 Core::System& system; 588 Core::System& system;
565}; 589};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 36970f828..ecba1dba1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -34,8 +34,7 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
34 case 0xa: { 34 case 0xa: {
35 if (command.length == 0x1c) { 35 if (command.length == 0x1c) {
36 LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); 36 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
37 Tegra::ChCommandHeaderList cmdlist(1); 37 Tegra::ChCommandHeaderList cmdlist{{0xDEADB33F}};
38 cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
39 system.GPU().PushCommandBuffer(cmdlist); 38 system.GPU().PushCommandBuffer(cmdlist);
40 } 39 }
41 return UnmapBuffer(input, output); 40 return UnmapBuffer(input, output);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 72499654c..70849a9bd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -28,8 +28,13 @@ NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve
28 return GetWaitbase(input, output); 28 return GetWaitbase(input, output);
29 case 0x9: 29 case 0x9:
30 return MapBuffer(input, output); 30 return MapBuffer(input, output);
31 case 0xa: 31 case 0xa: {
32 if (command.length == 0x1c) {
33 Tegra::ChCommandHeaderList cmdlist{{0xDEADB33F}};
34 system.GPU().PushCommandBuffer(cmdlist);
35 }
32 return UnmapBuffer(input, output); 36 return UnmapBuffer(input, output);
37 }
33 default: 38 default:
34 break; 39 break;
35 } 40 }
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index 858623e2b..1f7309f6b 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -279,6 +279,10 @@ const SharedMemory& TimeManager::GetSharedMemory() const {
279 return impl->shared_memory; 279 return impl->shared_memory;
280} 280}
281 281
282void TimeManager::Shutdown() {
283 impl.reset();
284}
285
282void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { 286void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
283 impl->UpdateLocalSystemClockTime(system, posix_time); 287 impl->UpdateLocalSystemClockTime(system, posix_time);
284} 288}
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
index 993c7c288..4db8cc0e1 100644
--- a/src/core/hle/service/time/time_manager.h
+++ b/src/core/hle/service/time/time_manager.h
@@ -61,6 +61,8 @@ public:
61 61
62 const SharedMemory& GetSharedMemory() const; 62 const SharedMemory& GetSharedMemory() const;
63 63
64 void Shutdown();
65
64 void SetupTimeZoneManager(std::string location_name, 66 void SetupTimeZoneManager(std::string location_name,
65 Clock::SteadyClockTimePoint time_zone_updated_time_point, 67 Clock::SteadyClockTimePoint time_zone_updated_time_point,
66 std::size_t total_location_name_count, u128 time_zone_rule_version, 68 std::size_t total_location_name_count, u128 time_zone_rule_version,
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp
index 67a584d53..b864d26f2 100644
--- a/src/input_common/mouse/mouse_input.cpp
+++ b/src/input_common/mouse/mouse_input.cpp
@@ -33,11 +33,16 @@ void Mouse::UpdateThread() {
33 info.motion.UpdateOrientation(update_time * 1000); 33 info.motion.UpdateOrientation(update_time * 1000);
34 info.tilt_speed = 0; 34 info.tilt_speed = 0;
35 info.data.motion = info.motion.GetMotion(); 35 info.data.motion = info.motion.GetMotion();
36 if (Settings::values.mouse_panning) {
37 info.last_mouse_change *= 0.96f;
38 info.data.axis = {static_cast<int>(16 * info.last_mouse_change.x),
39 static_cast<int>(16 * -info.last_mouse_change.y)};
40 }
36 } 41 }
37 if (configuring) { 42 if (configuring) {
38 UpdateYuzuSettings(); 43 UpdateYuzuSettings();
39 } 44 }
40 if (mouse_panning_timout++ > 8) { 45 if (mouse_panning_timout++ > 20) {
41 StopPanning(); 46 StopPanning();
42 } 47 }
43 std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); 48 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
@@ -82,16 +87,27 @@ void Mouse::StopPanning() {
82void Mouse::MouseMove(int x, int y, int center_x, int center_y) { 87void Mouse::MouseMove(int x, int y, int center_x, int center_y) {
83 for (MouseInfo& info : mouse_info) { 88 for (MouseInfo& info : mouse_info) {
84 if (Settings::values.mouse_panning) { 89 if (Settings::values.mouse_panning) {
85 const auto mouse_change = Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y); 90 auto mouse_change =
91 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
86 mouse_panning_timout = 0; 92 mouse_panning_timout = 0;
87 93
88 if (mouse_change.y == 0 && mouse_change.x == 0) { 94 if (mouse_change.y == 0 && mouse_change.x == 0) {
89 continue; 95 continue;
90 } 96 }
97 const auto mouse_change_length = mouse_change.Length();
98 if (mouse_change_length < 3.0f) {
99 mouse_change /= mouse_change_length / 3.0f;
100 }
101
102 info.last_mouse_change = (info.last_mouse_change * 0.91f) + (mouse_change * 0.09f);
103
104 const auto last_mouse_change_length = info.last_mouse_change.Length();
105 if (last_mouse_change_length > 8.0f) {
106 info.last_mouse_change /= last_mouse_change_length / 8.0f;
107 } else if (last_mouse_change_length < 1.0f) {
108 info.last_mouse_change = mouse_change / mouse_change.Length();
109 }
91 110
92 info.last_mouse_change = (info.last_mouse_change * 0.8f) + (mouse_change * 0.2f);
93 info.data.axis = {static_cast<int>(16 * info.last_mouse_change.x),
94 static_cast<int>(16 * -info.last_mouse_change.y)};
95 info.tilt_direction = info.last_mouse_change; 111 info.tilt_direction = info.last_mouse_change;
96 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity; 112 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
97 continue; 113 continue;
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
index 75486554b..a59f5d461 100644
--- a/src/input_common/settings.h
+++ b/src/input_common/settings.h
@@ -340,6 +340,7 @@ enum class ControllerType {
340 LeftJoycon, 340 LeftJoycon,
341 RightJoycon, 341 RightJoycon,
342 Handheld, 342 Handheld,
343 GameCube,
343}; 344};
344 345
345struct PlayerInput { 346struct PlayerInput {
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 6a5c18945..4ea0076e9 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,5 +1,6 @@
1add_executable(tests 1add_executable(tests
2 common/bit_field.cpp 2 common/bit_field.cpp
3 common/cityhash.cpp
3 common/fibers.cpp 4 common/fibers.cpp
4 common/param_package.cpp 5 common/param_package.cpp
5 common/ring_buffer.cpp 6 common/ring_buffer.cpp
diff --git a/src/tests/common/cityhash.cpp b/src/tests/common/cityhash.cpp
new file mode 100644
index 000000000..7a40b6c4a
--- /dev/null
+++ b/src/tests/common/cityhash.cpp
@@ -0,0 +1,22 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch2/catch.hpp>
6
7#include "common/cityhash.h"
8
9constexpr char msg[] = "The blue frogs are singing under the crimson sky.\n"
10 "It is time to run, Robert.";
11
12using namespace Common;
13
14TEST_CASE("CityHash", "[common]") {
15 // These test results were built against a known good version.
16 REQUIRE(CityHash64(msg, sizeof(msg)) == 0x92d5c2e9cbfbbc01);
17 REQUIRE(CityHash64WithSeed(msg, sizeof(msg), 0xdead) == 0xbfbe93f21a2820dd);
18 REQUIRE(CityHash64WithSeeds(msg, sizeof(msg), 0xbeef, 0xcafe) == 0xb343317955fc8a06);
19 REQUIRE(CityHash128(msg, sizeof(msg)) == u128{0x98e60d0423747eaa, 0xd8694c5b6fcaede9});
20 REQUIRE(CityHash128WithSeed(msg, sizeof(msg), {0xdead, 0xbeef}) ==
21 u128{0xf0307dba81199ebe, 0xd77764e0c4a9eb74});
22}
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
index 33b3c060b..a3fda1094 100644
--- a/src/video_core/cdma_pusher.cpp
+++ b/src/video_core/cdma_pusher.cpp
@@ -37,59 +37,43 @@ CDmaPusher::CDmaPusher(GPU& gpu_)
37 37
38CDmaPusher::~CDmaPusher() = default; 38CDmaPusher::~CDmaPusher() = default;
39 39
40void CDmaPusher::Push(ChCommandHeaderList&& entries) { 40void CDmaPusher::ProcessEntries(ChCommandHeaderList&& entries) {
41 cdma_queue.push(std::move(entries)); 41 for (const auto& value : entries) {
42}
43
44void CDmaPusher::DispatchCalls() {
45 while (!cdma_queue.empty()) {
46 Step();
47 }
48}
49
50void CDmaPusher::Step() {
51 const auto entries{cdma_queue.front()};
52 cdma_queue.pop();
53
54 std::vector<u32> values(entries.size());
55 std::memcpy(values.data(), entries.data(), entries.size() * sizeof(u32));
56
57 for (const u32 value : values) {
58 if (mask != 0) { 42 if (mask != 0) {
59 const auto lbs = static_cast<u32>(std::countr_zero(mask)); 43 const auto lbs = static_cast<u32>(std::countr_zero(mask));
60 mask &= ~(1U << lbs); 44 mask &= ~(1U << lbs);
61 ExecuteCommand(static_cast<u32>(offset + lbs), value); 45 ExecuteCommand(offset + lbs, value.raw);
62 continue; 46 continue;
63 } else if (count != 0) { 47 } else if (count != 0) {
64 --count; 48 --count;
65 ExecuteCommand(static_cast<u32>(offset), value); 49 ExecuteCommand(offset, value.raw);
66 if (incrementing) { 50 if (incrementing) {
67 ++offset; 51 ++offset;
68 } 52 }
69 continue; 53 continue;
70 } 54 }
71 const auto mode = static_cast<ChSubmissionMode>((value >> 28) & 0xf); 55 const auto mode = value.submission_mode.Value();
72 switch (mode) { 56 switch (mode) {
73 case ChSubmissionMode::SetClass: { 57 case ChSubmissionMode::SetClass: {
74 mask = value & 0x3f; 58 mask = value.value & 0x3f;
75 offset = (value >> 16) & 0xfff; 59 offset = value.method_offset;
76 current_class = static_cast<ChClassId>((value >> 6) & 0x3ff); 60 current_class = static_cast<ChClassId>((value.value >> 6) & 0x3ff);
77 break; 61 break;
78 } 62 }
79 case ChSubmissionMode::Incrementing: 63 case ChSubmissionMode::Incrementing:
80 case ChSubmissionMode::NonIncrementing: 64 case ChSubmissionMode::NonIncrementing:
81 count = value & 0xffff; 65 count = value.value;
82 offset = (value >> 16) & 0xfff; 66 offset = value.method_offset;
83 incrementing = mode == ChSubmissionMode::Incrementing; 67 incrementing = mode == ChSubmissionMode::Incrementing;
84 break; 68 break;
85 case ChSubmissionMode::Mask: 69 case ChSubmissionMode::Mask:
86 mask = value & 0xffff; 70 mask = value.value;
87 offset = (value >> 16) & 0xfff; 71 offset = value.method_offset;
88 break; 72 break;
89 case ChSubmissionMode::Immediate: { 73 case ChSubmissionMode::Immediate: {
90 const u32 data = value & 0xfff; 74 const u32 data = value.value & 0xfff;
91 offset = (value >> 16) & 0xfff; 75 offset = value.method_offset;
92 ExecuteCommand(static_cast<u32>(offset), data); 76 ExecuteCommand(offset, data);
93 break; 77 break;
94 } 78 }
95 default: 79 default:
@@ -102,8 +86,8 @@ void CDmaPusher::Step() {
102void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) { 86void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
103 switch (current_class) { 87 switch (current_class) {
104 case ChClassId::NvDec: 88 case ChClassId::NvDec:
105 ThiStateWrite(nvdec_thi_state, state_offset, {data}); 89 ThiStateWrite(nvdec_thi_state, offset, data);
106 switch (static_cast<ThiMethod>(state_offset)) { 90 switch (static_cast<ThiMethod>(offset)) {
107 case ThiMethod::IncSyncpt: { 91 case ThiMethod::IncSyncpt: {
108 LOG_DEBUG(Service_NVDRV, "NVDEC Class IncSyncpt Method"); 92 LOG_DEBUG(Service_NVDRV, "NVDEC Class IncSyncpt Method");
109 const auto syncpoint_id = static_cast<u32>(data & 0xFF); 93 const auto syncpoint_id = static_cast<u32>(data & 0xFF);
@@ -120,7 +104,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
120 LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}", 104 LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
121 static_cast<u32>(nvdec_thi_state.method_0)); 105 static_cast<u32>(nvdec_thi_state.method_0));
122 nvdec_processor->ProcessMethod(static_cast<Nvdec::Method>(nvdec_thi_state.method_0), 106 nvdec_processor->ProcessMethod(static_cast<Nvdec::Method>(nvdec_thi_state.method_0),
123 {data}); 107 data);
124 break; 108 break;
125 default: 109 default:
126 break; 110 break;
@@ -144,7 +128,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
144 case ThiMethod::SetMethod1: 128 case ThiMethod::SetMethod1:
145 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})", 129 LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
146 static_cast<u32>(vic_thi_state.method_0), data); 130 static_cast<u32>(vic_thi_state.method_0), data);
147 vic_processor->ProcessMethod(static_cast<Vic::Method>(vic_thi_state.method_0), {data}); 131 vic_processor->ProcessMethod(static_cast<Vic::Method>(vic_thi_state.method_0), data);
148 break; 132 break;
149 default: 133 default:
150 break; 134 break;
@@ -153,7 +137,7 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
153 case ChClassId::Host1x: 137 case ChClassId::Host1x:
154 // This device is mainly for syncpoint synchronization 138 // This device is mainly for syncpoint synchronization
155 LOG_DEBUG(Service_NVDRV, "Host1X Class Method"); 139 LOG_DEBUG(Service_NVDRV, "Host1X Class Method");
156 host1x_processor->ProcessMethod(static_cast<Host1x::Method>(state_offset), {data}); 140 host1x_processor->ProcessMethod(static_cast<Host1x::Method>(offset), data);
157 break; 141 break;
158 default: 142 default:
159 UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class)); 143 UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class));
@@ -161,10 +145,9 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
161 } 145 }
162} 146}
163 147
164void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 state_offset, 148void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 state_offset, u32 argument) {
165 const std::vector<u32>& arguments) { 149 u8* const offset_ptr = reinterpret_cast<u8*>(&state) + sizeof(u32) * state_offset;
166 u8* const state_offset_ptr = reinterpret_cast<u8*>(&state) + sizeof(u32) * state_offset; 150 std::memcpy(offset_ptr, &argument, sizeof(u32));
167 std::memcpy(state_offset_ptr, arguments.data(), sizeof(u32) * arguments.size());
168} 151}
169 152
170} // namespace Tegra 153} // namespace Tegra
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
index e5f212c1a..1bada44dd 100644
--- a/src/video_core/cdma_pusher.h
+++ b/src/video_core/cdma_pusher.h
@@ -5,9 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <unordered_map>
9#include <vector> 8#include <vector>
10#include <queue>
11 9
12#include "common/bit_field.h" 10#include "common/bit_field.h"
13#include "common/common_types.h" 11#include "common/common_types.h"
@@ -16,9 +14,9 @@
16namespace Tegra { 14namespace Tegra {
17 15
18class GPU; 16class GPU;
17class Host1x;
19class Nvdec; 18class Nvdec;
20class Vic; 19class Vic;
21class Host1x;
22 20
23enum class ChSubmissionMode : u32 { 21enum class ChSubmissionMode : u32 {
24 SetClass = 0, 22 SetClass = 0,
@@ -48,16 +46,10 @@ enum class ChClassId : u32 {
48 NvDec = 0xf0 46 NvDec = 0xf0
49}; 47};
50 48
51enum class ChMethod : u32 {
52 Empty = 0,
53 SetMethod = 0x10,
54 SetData = 0x11,
55};
56
57union ChCommandHeader { 49union ChCommandHeader {
58 u32 raw; 50 u32 raw;
59 BitField<0, 16, u32> value; 51 BitField<0, 16, u32> value;
60 BitField<16, 12, ChMethod> method_offset; 52 BitField<16, 12, u32> method_offset;
61 BitField<28, 4, ChSubmissionMode> submission_mode; 53 BitField<28, 4, ChSubmissionMode> submission_mode;
62}; 54};
63static_assert(sizeof(ChCommandHeader) == sizeof(u32), "ChCommand header is an invalid size"); 55static_assert(sizeof(ChCommandHeader) == sizeof(u32), "ChCommand header is an invalid size");
@@ -99,21 +91,15 @@ public:
99 explicit CDmaPusher(GPU& gpu_); 91 explicit CDmaPusher(GPU& gpu_);
100 ~CDmaPusher(); 92 ~CDmaPusher();
101 93
102 /// Push NVDEC command buffer entries into queue 94 /// Process the command entry
103 void Push(ChCommandHeaderList&& entries); 95 void ProcessEntries(ChCommandHeaderList&& entries);
104
105 /// Process queued command buffer entries
106 void DispatchCalls();
107
108 /// Process one queue element
109 void Step();
110 96
97private:
111 /// Invoke command class devices to execute the command based on the current state 98 /// Invoke command class devices to execute the command based on the current state
112 void ExecuteCommand(u32 state_offset, u32 data); 99 void ExecuteCommand(u32 state_offset, u32 data);
113 100
114private:
115 /// Write arguments value to the ThiRegisters member at the specified offset 101 /// Write arguments value to the ThiRegisters member at the specified offset
116 void ThiStateWrite(ThiRegisters& state, u32 state_offset, const std::vector<u32>& arguments); 102 void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument);
117 103
118 GPU& gpu; 104 GPU& gpu;
119 std::shared_ptr<Tegra::Nvdec> nvdec_processor; 105 std::shared_ptr<Tegra::Nvdec> nvdec_processor;
@@ -124,13 +110,10 @@ private:
124 ThiRegisters vic_thi_state{}; 110 ThiRegisters vic_thi_state{};
125 ThiRegisters nvdec_thi_state{}; 111 ThiRegisters nvdec_thi_state{};
126 112
127 s32 count{}; 113 u32 count{};
128 s32 offset{}; 114 u32 offset{};
129 u32 mask{}; 115 u32 mask{};
130 bool incrementing{}; 116 bool incrementing{};
131
132 // Queue of command lists to be processed
133 std::queue<ChCommandHeaderList> cdma_queue;
134}; 117};
135 118
136} // namespace Tegra 119} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 39bc923a5..d02dc6260 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -44,8 +44,10 @@ Codec::~Codec() {
44} 44}
45 45
46void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { 46void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
47 LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", codec); 47 if (current_codec != codec) {
48 current_codec = codec; 48 LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", static_cast<u32>(codec));
49 current_codec = codec;
50 }
49} 51}
50 52
51void Codec::StateWrite(u32 offset, u64 arguments) { 53void Codec::StateWrite(u32 offset, u64 arguments) {
@@ -55,7 +57,6 @@ void Codec::StateWrite(u32 offset, u64 arguments) {
55 57
56void Codec::Decode() { 58void Codec::Decode() {
57 bool is_first_frame = false; 59 bool is_first_frame = false;
58
59 if (!initialized) { 60 if (!initialized) {
60 if (current_codec == NvdecCommon::VideoCodec::H264) { 61 if (current_codec == NvdecCommon::VideoCodec::H264) {
61 av_codec = avcodec_find_decoder(AV_CODEC_ID_H264); 62 av_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/command_classes/nvdec.cpp
index 79e1f4e13..e4f919afd 100644
--- a/src/video_core/command_classes/nvdec.cpp
+++ b/src/video_core/command_classes/nvdec.cpp
@@ -12,16 +12,16 @@ Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {}
12 12
13Nvdec::~Nvdec() = default; 13Nvdec::~Nvdec() = default;
14 14
15void Nvdec::ProcessMethod(Method method, const std::vector<u32>& arguments) { 15void Nvdec::ProcessMethod(Method method, u32 argument) {
16 if (method == Method::SetVideoCodec) { 16 if (method == Method::SetVideoCodec) {
17 codec->StateWrite(static_cast<u32>(method), arguments[0]); 17 codec->StateWrite(static_cast<u32>(method), argument);
18 } else { 18 } else {
19 codec->StateWrite(static_cast<u32>(method), static_cast<u64>(arguments[0]) << 8); 19 codec->StateWrite(static_cast<u32>(method), static_cast<u64>(argument) << 8);
20 } 20 }
21 21
22 switch (method) { 22 switch (method) {
23 case Method::SetVideoCodec: 23 case Method::SetVideoCodec:
24 codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(arguments[0])); 24 codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(argument));
25 break; 25 break;
26 case Method::Execute: 26 case Method::Execute:
27 Execute(); 27 Execute();
diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/command_classes/nvdec.h
index e4877c533..e66be80b8 100644
--- a/src/video_core/command_classes/nvdec.h
+++ b/src/video_core/command_classes/nvdec.h
@@ -23,7 +23,7 @@ public:
23 ~Nvdec(); 23 ~Nvdec();
24 24
25 /// Writes the method into the state, Invoke Execute() if encountered 25 /// Writes the method into the state, Invoke Execute() if encountered
26 void ProcessMethod(Method method, const std::vector<u32>& arguments); 26 void ProcessMethod(Method method, u32 argument);
27 27
28 /// Return most recently decoded frame 28 /// Return most recently decoded frame
29 [[nodiscard]] AVFramePtr GetFrame(); 29 [[nodiscard]] AVFramePtr GetFrame();
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index 2b7569335..0a8b82f2b 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -18,18 +18,14 @@ extern "C" {
18namespace Tegra { 18namespace Tegra {
19 19
20Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_) 20Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_)
21 : gpu(gpu_), nvdec_processor(std::move(nvdec_processor_)) {} 21 : gpu(gpu_),
22Vic::~Vic() = default; 22 nvdec_processor(std::move(nvdec_processor_)), converted_frame_buffer{nullptr, av_free} {}
23 23
24void Vic::VicStateWrite(u32 offset, u32 arguments) { 24Vic::~Vic() = default;
25 u8* const state_offset = reinterpret_cast<u8*>(&vic_state) + offset * sizeof(u32);
26 std::memcpy(state_offset, &arguments, sizeof(u32));
27}
28 25
29void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) { 26void Vic::ProcessMethod(Method method, u32 argument) {
30 LOG_DEBUG(HW_GPU, "Vic method 0x{:X}", method); 27 LOG_DEBUG(HW_GPU, "Vic method 0x{:X}", static_cast<u32>(method));
31 VicStateWrite(static_cast<u32>(method), arguments[0]); 28 const u64 arg = static_cast<u64>(argument) << 8;
32 const u64 arg = static_cast<u64>(arguments[0]) << 8;
33 switch (method) { 29 switch (method) {
34 case Method::Execute: 30 case Method::Execute:
35 Execute(); 31 Execute();
@@ -53,8 +49,7 @@ void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {
53 49
54void Vic::Execute() { 50void Vic::Execute() {
55 if (output_surface_luma_address == 0) { 51 if (output_surface_luma_address == 0) {
56 LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Received 0x{:X}", 52 LOG_ERROR(Service_NVDRV, "VIC Luma address not set.");
57 vic_state.output_surface.luma_offset);
58 return; 53 return;
59 } 54 }
60 const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)}; 55 const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)};
@@ -89,8 +84,10 @@ void Vic::Execute() {
89 // Get Converted frame 84 // Get Converted frame
90 const std::size_t linear_size = frame->width * frame->height * 4; 85 const std::size_t linear_size = frame->width * frame->height * 4;
91 86
92 using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>; 87 // Only allocate frame_buffer once per stream, as the size is not expected to change
93 AVMallocPtr converted_frame_buffer{static_cast<u8*>(av_malloc(linear_size)), av_free}; 88 if (!converted_frame_buffer) {
89 converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(linear_size)), av_free};
90 }
94 91
95 const int converted_stride{frame->width * 4}; 92 const int converted_stride{frame->width * 4};
96 u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; 93 u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
@@ -104,12 +101,12 @@ void Vic::Execute() {
104 const u32 block_height = static_cast<u32>(config.block_linear_height_log2); 101 const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
105 const auto size = Tegra::Texture::CalculateSize(true, 4, frame->width, frame->height, 1, 102 const auto size = Tegra::Texture::CalculateSize(true, 4, frame->width, frame->height, 1,
106 block_height, 0); 103 block_height, 0);
107 std::vector<u8> swizzled_data(size); 104 luma_buffer.resize(size);
108 Tegra::Texture::SwizzleSubrect(frame->width, frame->height, frame->width * 4, 105 Tegra::Texture::SwizzleSubrect(frame->width, frame->height, frame->width * 4,
109 frame->width, 4, swizzled_data.data(), 106 frame->width, 4, luma_buffer.data(),
110 converted_frame_buffer.get(), block_height, 0, 0); 107 converted_frame_buffer.get(), block_height, 0, 0);
111 108
112 gpu.MemoryManager().WriteBlock(output_surface_luma_address, swizzled_data.data(), size); 109 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size);
113 } else { 110 } else {
114 // send pitch linear frame 111 // send pitch linear frame
115 gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr, 112 gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr,
@@ -132,15 +129,15 @@ void Vic::Execute() {
132 const auto stride = frame->linesize[0]; 129 const auto stride = frame->linesize[0];
133 const auto half_stride = frame->linesize[1]; 130 const auto half_stride = frame->linesize[1];
134 131
135 std::vector<u8> luma_buffer(aligned_width * surface_height); 132 luma_buffer.resize(aligned_width * surface_height);
136 std::vector<u8> chroma_buffer(aligned_width * half_height); 133 chroma_buffer.resize(aligned_width * half_height);
137 134
138 // Populate luma buffer 135 // Populate luma buffer
139 for (std::size_t y = 0; y < surface_height - 1; ++y) { 136 for (std::size_t y = 0; y < surface_height - 1; ++y) {
140 std::size_t src = y * stride; 137 const std::size_t src = y * stride;
141 std::size_t dst = y * aligned_width; 138 const std::size_t dst = y * aligned_width;
142 139
143 std::size_t size = surface_width; 140 const std::size_t size = surface_width;
144 141
145 for (std::size_t offset = 0; offset < size; ++offset) { 142 for (std::size_t offset = 0; offset < size; ++offset) {
146 luma_buffer[dst + offset] = luma_ptr[src + offset]; 143 luma_buffer[dst + offset] = luma_ptr[src + offset];
@@ -151,8 +148,8 @@ void Vic::Execute() {
151 148
152 // Populate chroma buffer from both channels with interleaving. 149 // Populate chroma buffer from both channels with interleaving.
153 for (std::size_t y = 0; y < half_height; ++y) { 150 for (std::size_t y = 0; y < half_height; ++y) {
154 std::size_t src = y * half_stride; 151 const std::size_t src = y * half_stride;
155 std::size_t dst = y * aligned_width; 152 const std::size_t dst = y * aligned_width;
156 153
157 for (std::size_t x = 0; x < half_width; ++x) { 154 for (std::size_t x = 0; x < half_width; ++x) {
158 chroma_buffer[dst + x * 2] = chroma_b_ptr[src + x]; 155 chroma_buffer[dst + x * 2] = chroma_b_ptr[src + x];
diff --git a/src/video_core/command_classes/vic.h b/src/video_core/command_classes/vic.h
index 8c4e284a1..f5a2ed100 100644
--- a/src/video_core/command_classes/vic.h
+++ b/src/video_core/command_classes/vic.h
@@ -15,43 +15,6 @@ namespace Tegra {
15class GPU; 15class GPU;
16class Nvdec; 16class Nvdec;
17 17
18struct PlaneOffsets {
19 u32 luma_offset{};
20 u32 chroma_u_offset{};
21 u32 chroma_v_offset{};
22};
23
24struct VicRegisters {
25 INSERT_PADDING_WORDS(64);
26 u32 nop{};
27 INSERT_PADDING_WORDS(15);
28 u32 pm_trigger{};
29 INSERT_PADDING_WORDS(47);
30 u32 set_application_id{};
31 u32 set_watchdog_timer{};
32 INSERT_PADDING_WORDS(17);
33 u32 context_save_area{};
34 u32 context_switch{};
35 INSERT_PADDING_WORDS(43);
36 u32 execute{};
37 INSERT_PADDING_WORDS(63);
38 std::array<std::array<PlaneOffsets, 8>, 8> surfacex_slots{};
39 u32 picture_index{};
40 u32 control_params{};
41 u32 config_struct_offset{};
42 u32 filter_struct_offset{};
43 u32 palette_offset{};
44 u32 hist_offset{};
45 u32 context_id{};
46 u32 fce_ucode_size{};
47 PlaneOffsets output_surface{};
48 u32 fce_ucode_offset{};
49 INSERT_PADDING_WORDS(4);
50 std::array<u32, 8> slot_context_id{};
51 INSERT_PADDING_WORDS(16);
52};
53static_assert(sizeof(VicRegisters) == 0x7A0, "VicRegisters is an invalid size");
54
55class Vic { 18class Vic {
56public: 19public:
57 enum class Method : u32 { 20 enum class Method : u32 {
@@ -67,14 +30,11 @@ public:
67 ~Vic(); 30 ~Vic();
68 31
69 /// Write to the device state. 32 /// Write to the device state.
70 void ProcessMethod(Method method, const std::vector<u32>& arguments); 33 void ProcessMethod(Method method, u32 argument);
71 34
72private: 35private:
73 void Execute(); 36 void Execute();
74 37
75 void VicStateWrite(u32 offset, u32 arguments);
76 VicRegisters vic_state{};
77
78 enum class VideoPixelFormat : u64_le { 38 enum class VideoPixelFormat : u64_le {
79 RGBA8 = 0x1f, 39 RGBA8 = 0x1f,
80 BGRA8 = 0x20, 40 BGRA8 = 0x20,
@@ -88,8 +48,6 @@ private:
88 BitField<9, 2, u64_le> chroma_loc_vert; 48 BitField<9, 2, u64_le> chroma_loc_vert;
89 BitField<11, 4, u64_le> block_linear_kind; 49 BitField<11, 4, u64_le> block_linear_kind;
90 BitField<15, 4, u64_le> block_linear_height_log2; 50 BitField<15, 4, u64_le> block_linear_height_log2;
91 BitField<19, 3, u64_le> reserved0;
92 BitField<22, 10, u64_le> reserved1;
93 BitField<32, 14, u64_le> surface_width_minus1; 51 BitField<32, 14, u64_le> surface_width_minus1;
94 BitField<46, 14, u64_le> surface_height_minus1; 52 BitField<46, 14, u64_le> surface_height_minus1;
95 }; 53 };
@@ -97,6 +55,13 @@ private:
97 GPU& gpu; 55 GPU& gpu;
98 std::shared_ptr<Tegra::Nvdec> nvdec_processor; 56 std::shared_ptr<Tegra::Nvdec> nvdec_processor;
99 57
58 /// Avoid reallocation of the following buffers every frame, as their
59 /// size does not change during a stream
60 using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>;
61 AVMallocPtr converted_frame_buffer;
62 std::vector<u8> luma_buffer;
63 std::vector<u8> chroma_buffer;
64
100 GPUVAddr config_struct_address{}; 65 GPUVAddr config_struct_address{};
101 GPUVAddr output_surface_luma_address{}; 66 GPUVAddr output_surface_luma_address{};
102 GPUVAddr output_surface_chroma_u_address{}; 67 GPUVAddr output_surface_chroma_u_address{};
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 2a9bd4121..51c63af4a 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -30,8 +30,7 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
30 30
31GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_) 31GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_)
32 : system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>(system)}, 32 : system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
33 dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)}, 33 dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)}, use_nvdec{use_nvdec_},
34 cdma_pusher{std::make_unique<Tegra::CDmaPusher>(*this)}, use_nvdec{use_nvdec_},
35 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)}, 34 maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
36 fermi_2d{std::make_unique<Engines::Fermi2D>()}, 35 fermi_2d{std::make_unique<Engines::Fermi2D>()},
37 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)}, 36 kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
@@ -494,8 +493,7 @@ void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
494 // TODO(ameerj): RE proper async nvdec operation 493 // TODO(ameerj): RE proper async nvdec operation
495 // gpu_thread.SubmitCommandBuffer(std::move(entries)); 494 // gpu_thread.SubmitCommandBuffer(std::move(entries));
496 495
497 cdma_pusher->Push(std::move(entries)); 496 cdma_pusher->ProcessEntries(std::move(entries));
498 cdma_pusher->DispatchCalls();
499} 497}
500 498
501void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 499void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 50319f1d5..eb0e43c0c 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -48,8 +48,7 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
48 dma_pusher.DispatchCalls(); 48 dma_pusher.DispatchCalls();
49 } else if (auto* command_list = std::get_if<SubmitChCommandEntries>(&next.data)) { 49 } else if (auto* command_list = std::get_if<SubmitChCommandEntries>(&next.data)) {
50 // NVDEC 50 // NVDEC
51 cdma_pusher.Push(std::move(command_list->entries)); 51 cdma_pusher.ProcessEntries(std::move(command_list->entries));
52 cdma_pusher.DispatchCalls();
53 } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) { 52 } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) {
54 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); 53 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
55 } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) { 54 } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 529570ff0..5cf7cd151 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -335,6 +335,10 @@ void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop
335 const VideoCore::DiskResourceLoadCallback& callback) { 335 const VideoCore::DiskResourceLoadCallback& callback) {
336 disk_cache.BindTitleID(title_id); 336 disk_cache.BindTitleID(title_id);
337 const std::optional transferable = disk_cache.LoadTransferable(); 337 const std::optional transferable = disk_cache.LoadTransferable();
338
339 LOG_INFO(Render_OpenGL, "Total Shader Count: {}",
340 transferable.has_value() ? transferable->size() : 0);
341
338 if (!transferable) { 342 if (!transferable) {
339 return; 343 return;
340 } 344 }
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 5be6dabd9..362278f01 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -12,14 +12,15 @@
12#include "common/cityhash.h" 12#include "common/cityhash.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 14#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
15#include "video_core/renderer_vulkan/vk_state_tracker.h"
15 16
16namespace Vulkan { 17namespace Vulkan {
17 18
18namespace { 19namespace {
19 20
20constexpr std::size_t POINT = 0; 21constexpr size_t POINT = 0;
21constexpr std::size_t LINE = 1; 22constexpr size_t LINE = 1;
22constexpr std::size_t POLYGON = 2; 23constexpr size_t POLYGON = 2;
23constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { 24constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
24 POINT, // Points 25 POINT, // Points
25 LINE, // Lines 26 LINE, // Lines
@@ -40,10 +41,14 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
40 41
41} // Anonymous namespace 42} // Anonymous namespace
42 43
43void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) { 44void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
44 const std::array enabled_lut = {regs.polygon_offset_point_enable, 45 bool has_extended_dynamic_state) {
45 regs.polygon_offset_line_enable, 46 const Maxwell& regs = maxwell3d.regs;
46 regs.polygon_offset_fill_enable}; 47 const std::array enabled_lut{
48 regs.polygon_offset_point_enable,
49 regs.polygon_offset_line_enable,
50 regs.polygon_offset_fill_enable,
51 };
47 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); 52 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
48 53
49 raw1 = 0; 54 raw1 = 0;
@@ -64,45 +69,53 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta
64 69
65 raw2 = 0; 70 raw2 = 0;
66 const auto test_func = 71 const auto test_func =
67 regs.alpha_test_enabled == 1 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always; 72 regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always;
68 alpha_test_func.Assign(PackComparisonOp(test_func)); 73 alpha_test_func.Assign(PackComparisonOp(test_func));
69 early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0); 74 early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0);
70 75
71 alpha_test_ref = Common::BitCast<u32>(regs.alpha_test_ref); 76 alpha_test_ref = Common::BitCast<u32>(regs.alpha_test_ref);
72 point_size = Common::BitCast<u32>(regs.point_size); 77 point_size = Common::BitCast<u32>(regs.point_size);
73 78
74 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 79 if (maxwell3d.dirty.flags[Dirty::InstanceDivisors]) {
75 binding_divisors[index] = 80 maxwell3d.dirty.flags[Dirty::InstanceDivisors] = false;
76 regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0; 81 for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
82 const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index);
83 binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0;
84 }
77 } 85 }
78 86 if (maxwell3d.dirty.flags[Dirty::VertexAttributes]) {
79 for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { 87 maxwell3d.dirty.flags[Dirty::VertexAttributes] = false;
80 const auto& input = regs.vertex_attrib_format[index]; 88 for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
81 auto& attribute = attributes[index]; 89 const auto& input = regs.vertex_attrib_format[index];
82 attribute.raw = 0; 90 auto& attribute = attributes[index];
83 attribute.enabled.Assign(input.IsConstant() ? 0 : 1); 91 attribute.raw = 0;
84 attribute.buffer.Assign(input.buffer); 92 attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
85 attribute.offset.Assign(input.offset); 93 attribute.buffer.Assign(input.buffer);
86 attribute.type.Assign(static_cast<u32>(input.type.Value())); 94 attribute.offset.Assign(input.offset);
87 attribute.size.Assign(static_cast<u32>(input.size.Value())); 95 attribute.type.Assign(static_cast<u32>(input.type.Value()));
88 attribute.binding_index_enabled.Assign(regs.vertex_array[index].IsEnabled() ? 1 : 0); 96 attribute.size.Assign(static_cast<u32>(input.size.Value()));
97 }
89 } 98 }
90 99 if (maxwell3d.dirty.flags[Dirty::Blending]) {
91 for (std::size_t index = 0; index < std::size(attachments); ++index) { 100 maxwell3d.dirty.flags[Dirty::Blending] = false;
92 attachments[index].Fill(regs, index); 101 for (size_t index = 0; index < attachments.size(); ++index) {
102 attachments[index].Refresh(regs, index);
103 }
104 }
105 if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) {
106 maxwell3d.dirty.flags[Dirty::ViewportSwizzles] = false;
107 const auto& transform = regs.viewport_transform;
108 std::ranges::transform(transform, viewport_swizzles.begin(), [](const auto& viewport) {
109 return static_cast<u16>(viewport.swizzle.raw);
110 });
93 } 111 }
94
95 const auto& transform = regs.viewport_transform;
96 std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
97 [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
98
99 if (!has_extended_dynamic_state) { 112 if (!has_extended_dynamic_state) {
100 no_extended_dynamic_state.Assign(1); 113 no_extended_dynamic_state.Assign(1);
101 dynamic_state.Fill(regs); 114 dynamic_state.Refresh(regs);
102 } 115 }
103} 116}
104 117
105void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { 118void FixedPipelineState::BlendingAttachment::Refresh(const Maxwell& regs, size_t index) {
106 const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; 119 const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index];
107 120
108 raw = 0; 121 raw = 0;
@@ -141,7 +154,7 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
141 enable.Assign(1); 154 enable.Assign(1);
142} 155}
143 156
144void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) { 157void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) {
145 u32 packed_front_face = PackFrontFace(regs.front_face); 158 u32 packed_front_face = PackFrontFace(regs.front_face);
146 if (regs.screen_y_control.triangle_rast_flip != 0) { 159 if (regs.screen_y_control.triangle_rast_flip != 0) {
147 // Flip front face 160 // Flip front face
@@ -178,9 +191,9 @@ void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
178 }); 191 });
179} 192}
180 193
181std::size_t FixedPipelineState::Hash() const noexcept { 194size_t FixedPipelineState::Hash() const noexcept {
182 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size()); 195 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
183 return static_cast<std::size_t>(hash); 196 return static_cast<size_t>(hash);
184} 197}
185 198
186bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { 199bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 465a55fdb..a0eb83a68 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -58,7 +58,7 @@ struct FixedPipelineState {
58 BitField<30, 1, u32> enable; 58 BitField<30, 1, u32> enable;
59 }; 59 };
60 60
61 void Fill(const Maxwell& regs, std::size_t index); 61 void Refresh(const Maxwell& regs, size_t index);
62 62
63 constexpr std::array<bool, 4> Mask() const noexcept { 63 constexpr std::array<bool, 4> Mask() const noexcept {
64 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; 64 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
@@ -96,8 +96,6 @@ struct FixedPipelineState {
96 BitField<6, 14, u32> offset; 96 BitField<6, 14, u32> offset;
97 BitField<20, 3, u32> type; 97 BitField<20, 3, u32> type;
98 BitField<23, 6, u32> size; 98 BitField<23, 6, u32> size;
99 // Not really an element of a vertex attribute, but it can be packed here
100 BitField<29, 1, u32> binding_index_enabled;
101 99
102 constexpr Maxwell::VertexAttribute::Type Type() const noexcept { 100 constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
103 return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); 101 return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
@@ -108,7 +106,7 @@ struct FixedPipelineState {
108 } 106 }
109 }; 107 };
110 108
111 template <std::size_t Position> 109 template <size_t Position>
112 union StencilFace { 110 union StencilFace {
113 BitField<Position + 0, 3, u32> action_stencil_fail; 111 BitField<Position + 0, 3, u32> action_stencil_fail;
114 BitField<Position + 3, 3, u32> action_depth_fail; 112 BitField<Position + 3, 3, u32> action_depth_fail;
@@ -152,7 +150,7 @@ struct FixedPipelineState {
152 // Vertex stride is a 12 bits value, we have 4 bits to spare per element 150 // Vertex stride is a 12 bits value, we have 4 bits to spare per element
153 std::array<u16, Maxwell::NumVertexArrays> vertex_strides; 151 std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
154 152
155 void Fill(const Maxwell& regs); 153 void Refresh(const Maxwell& regs);
156 154
157 Maxwell::ComparisonOp DepthTestFunc() const noexcept { 155 Maxwell::ComparisonOp DepthTestFunc() const noexcept {
158 return UnpackComparisonOp(depth_test_func); 156 return UnpackComparisonOp(depth_test_func);
@@ -199,9 +197,9 @@ struct FixedPipelineState {
199 std::array<u16, Maxwell::NumViewports> viewport_swizzles; 197 std::array<u16, Maxwell::NumViewports> viewport_swizzles;
200 DynamicState dynamic_state; 198 DynamicState dynamic_state;
201 199
202 void Fill(const Maxwell& regs, bool has_extended_dynamic_state); 200 void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state);
203 201
204 std::size_t Hash() const noexcept; 202 size_t Hash() const noexcept;
205 203
206 bool operator==(const FixedPipelineState& rhs) const noexcept; 204 bool operator==(const FixedPipelineState& rhs) const noexcept;
207 205
@@ -209,8 +207,8 @@ struct FixedPipelineState {
209 return !operator==(rhs); 207 return !operator==(rhs);
210 } 208 }
211 209
212 std::size_t Size() const noexcept { 210 size_t Size() const noexcept {
213 const std::size_t total_size = sizeof *this; 211 const size_t total_size = sizeof *this;
214 return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState)); 212 return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
215 } 213 }
216}; 214};
@@ -224,7 +222,7 @@ namespace std {
224 222
225template <> 223template <>
226struct hash<Vulkan::FixedPipelineState> { 224struct hash<Vulkan::FixedPipelineState> {
227 std::size_t operator()(const Vulkan::FixedPipelineState& k) const noexcept { 225 size_t operator()(const Vulkan::FixedPipelineState& k) const noexcept {
228 return k.Hash(); 226 return k.Hash();
229 } 227 }
230}; 228};
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 848eedd66..668633e7b 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -201,10 +201,6 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
201 }); 201 });
202} 202}
203 203
204void BufferCacheRuntime::BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
205 update_descriptor_queue.AddBuffer(buffer, offset, size);
206}
207
208void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle) { 204void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle) {
209 if (num_indices <= current_num_indices) { 205 if (num_indices <= current_num_indices) {
210 return; 206 return;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 041e6515c..982e92191 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -8,6 +8,7 @@
8#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
9#include "video_core/renderer_vulkan/vk_compute_pass.h" 9#include "video_core/renderer_vulkan/vk_compute_pass.h"
10#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 10#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
11#include "video_core/renderer_vulkan/vk_update_descriptor.h"
11#include "video_core/vulkan_common/vulkan_memory_allocator.h" 12#include "video_core/vulkan_common/vulkan_memory_allocator.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
13 14
@@ -16,7 +17,6 @@ namespace Vulkan {
16class Device; 17class Device;
17class VKDescriptorPool; 18class VKDescriptorPool;
18class VKScheduler; 19class VKScheduler;
19class VKUpdateDescriptorQueue;
20 20
21class BufferCacheRuntime; 21class BufferCacheRuntime;
22 22
@@ -86,7 +86,9 @@ public:
86 } 86 }
87 87
88private: 88private:
89 void BindBuffer(VkBuffer buffer, u32 offset, u32 size); 89 void BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
90 update_descriptor_queue.AddBuffer(buffer, offset, size);
91 }
90 92
91 void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle); 93 void ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle);
92 94
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index d50dca604..fc6dd83eb 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -221,9 +221,6 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program,
221 std::vector<VkVertexInputBindingDescription> vertex_bindings; 221 std::vector<VkVertexInputBindingDescription> vertex_bindings;
222 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; 222 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
223 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 223 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
224 if (state.attributes[index].binding_index_enabled == 0) {
225 continue;
226 }
227 const bool instanced = state.binding_divisors[index] != 0; 224 const bool instanced = state.binding_divisors[index] != 0;
228 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 225 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
229 vertex_bindings.push_back({ 226 vertex_bindings.push_back({
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 684d4e3a6..dfd38f575 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -267,8 +267,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
267 267
268 query_cache.UpdateCounters(); 268 query_cache.UpdateCounters();
269 269
270 GraphicsPipelineCacheKey key; 270 graphics_key.fixed_state.Refresh(maxwell3d, device.IsExtExtendedDynamicStateSupported());
271 key.fixed_state.Fill(maxwell3d.regs, device.IsExtExtendedDynamicStateSupported());
272 271
273 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; 272 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
274 273
@@ -276,14 +275,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
276 texture_cache.UpdateRenderTargets(false); 275 texture_cache.UpdateRenderTargets(false);
277 276
278 const auto shaders = pipeline_cache.GetShaders(); 277 const auto shaders = pipeline_cache.GetShaders();
279 key.shaders = GetShaderAddresses(shaders); 278 graphics_key.shaders = GetShaderAddresses(shaders);
279
280 SetupShaderDescriptors(shaders, is_indexed); 280 SetupShaderDescriptors(shaders, is_indexed);
281 281
282 const Framebuffer* const framebuffer = texture_cache.GetFramebuffer(); 282 const Framebuffer* const framebuffer = texture_cache.GetFramebuffer();
283 key.renderpass = framebuffer->RenderPass(); 283 graphics_key.renderpass = framebuffer->RenderPass();
284 284
285 auto* const pipeline = 285 VKGraphicsPipeline* const pipeline = pipeline_cache.GetGraphicsPipeline(
286 pipeline_cache.GetGraphicsPipeline(key, framebuffer->NumColorBuffers(), async_shaders); 286 graphics_key, framebuffer->NumColorBuffers(), async_shaders);
287 if (pipeline == nullptr || pipeline->GetHandle() == VK_NULL_HANDLE) { 287 if (pipeline == nullptr || pipeline->GetHandle() == VK_NULL_HANDLE) {
288 // Async graphics pipeline was not ready. 288 // Async graphics pipeline was not ready.
289 return; 289 return;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 7fc6741da..acea1ba2d 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -20,6 +20,7 @@
20#include "video_core/renderer_vulkan/vk_buffer_cache.h" 20#include "video_core/renderer_vulkan/vk_buffer_cache.h"
21#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 21#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
22#include "video_core/renderer_vulkan/vk_fence_manager.h" 22#include "video_core/renderer_vulkan/vk_fence_manager.h"
23#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
23#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 24#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
24#include "video_core/renderer_vulkan/vk_query_cache.h" 25#include "video_core/renderer_vulkan/vk_query_cache.h"
25#include "video_core/renderer_vulkan/vk_scheduler.h" 26#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -173,6 +174,8 @@ private:
173 VKUpdateDescriptorQueue update_descriptor_queue; 174 VKUpdateDescriptorQueue update_descriptor_queue;
174 BlitImageHelper blit_image; 175 BlitImageHelper blit_image;
175 176
177 GraphicsPipelineCacheKey graphics_key;
178
176 TextureCacheRuntime texture_cache_runtime; 179 TextureCacheRuntime texture_cache_runtime;
177 TextureCache texture_cache; 180 TextureCache texture_cache;
178 BufferCacheRuntime buffer_cache_runtime; 181 BufferCacheRuntime buffer_cache_runtime;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index e81fad007..956f86845 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -18,9 +18,7 @@
18#define NUM(field_name) (sizeof(Maxwell3D::Regs::field_name) / (sizeof(u32))) 18#define NUM(field_name) (sizeof(Maxwell3D::Regs::field_name) / (sizeof(u32)))
19 19
20namespace Vulkan { 20namespace Vulkan {
21
22namespace { 21namespace {
23
24using namespace Dirty; 22using namespace Dirty;
25using namespace VideoCommon::Dirty; 23using namespace VideoCommon::Dirty;
26using Tegra::Engines::Maxwell3D; 24using Tegra::Engines::Maxwell3D;
@@ -128,6 +126,34 @@ void SetupDirtyStencilTestEnable(Tables& tables) {
128 tables[0][OFF(stencil_enable)] = StencilTestEnable; 126 tables[0][OFF(stencil_enable)] = StencilTestEnable;
129} 127}
130 128
129void SetupDirtyBlending(Tables& tables) {
130 tables[0][OFF(color_mask_common)] = Blending;
131 tables[0][OFF(independent_blend_enable)] = Blending;
132 FillBlock(tables[0], OFF(color_mask), NUM(color_mask), Blending);
133 FillBlock(tables[0], OFF(blend), NUM(blend), Blending);
134 FillBlock(tables[0], OFF(independent_blend), NUM(independent_blend), Blending);
135}
136
137void SetupDirtyInstanceDivisors(Tables& tables) {
138 static constexpr size_t divisor_offset = 3;
139 for (size_t index = 0; index < Regs::NumVertexArrays; ++index) {
140 tables[0][OFF(instanced_arrays) + index] = InstanceDivisors;
141 tables[0][OFF(vertex_array) + index * NUM(vertex_array[0]) + divisor_offset] =
142 InstanceDivisors;
143 }
144}
145
146void SetupDirtyVertexAttributes(Tables& tables) {
147 FillBlock(tables[0], OFF(vertex_attrib_format), NUM(vertex_attrib_format), VertexAttributes);
148}
149
150void SetupDirtyViewportSwizzles(Tables& tables) {
151 static constexpr size_t swizzle_offset = 6;
152 for (size_t index = 0; index < Regs::NumViewports; ++index) {
153 tables[0][OFF(viewport_transform) + index * NUM(viewport_transform[0]) + swizzle_offset] =
154 ViewportSwizzles;
155 }
156}
131} // Anonymous namespace 157} // Anonymous namespace
132 158
133StateTracker::StateTracker(Tegra::GPU& gpu) 159StateTracker::StateTracker(Tegra::GPU& gpu)
@@ -148,6 +174,10 @@ StateTracker::StateTracker(Tegra::GPU& gpu)
148 SetupDirtyFrontFace(tables); 174 SetupDirtyFrontFace(tables);
149 SetupDirtyStencilOp(tables); 175 SetupDirtyStencilOp(tables);
150 SetupDirtyStencilTestEnable(tables); 176 SetupDirtyStencilTestEnable(tables);
177 SetupDirtyBlending(tables);
178 SetupDirtyInstanceDivisors(tables);
179 SetupDirtyVertexAttributes(tables);
180 SetupDirtyViewportSwizzles(tables);
151} 181}
152 182
153} // namespace Vulkan 183} // 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 c335d2bdf..84e918a71 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -35,6 +35,11 @@ enum : u8 {
35 StencilOp, 35 StencilOp,
36 StencilTestEnable, 36 StencilTestEnable,
37 37
38 Blending,
39 InstanceDivisors,
40 VertexAttributes,
41 ViewportSwizzles,
42
38 Last 43 Last
39}; 44};
40static_assert(Last <= std::numeric_limits<u8>::max()); 45static_assert(Last <= std::numeric_limits<u8>::max());
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
index f99273c6a..dc45fdcb1 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
@@ -20,20 +20,20 @@ VKUpdateDescriptorQueue::VKUpdateDescriptorQueue(const Device& device_, VKSchedu
20VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default; 20VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default;
21 21
22void VKUpdateDescriptorQueue::TickFrame() { 22void VKUpdateDescriptorQueue::TickFrame() {
23 payload.clear(); 23 payload_cursor = payload.data();
24} 24}
25 25
26void VKUpdateDescriptorQueue::Acquire() { 26void VKUpdateDescriptorQueue::Acquire() {
27 // Minimum number of entries required. 27 // Minimum number of entries required.
28 // This is the maximum number of entries a single draw call migth use. 28 // This is the maximum number of entries a single draw call migth use.
29 static constexpr std::size_t MIN_ENTRIES = 0x400; 29 static constexpr size_t MIN_ENTRIES = 0x400;
30 30
31 if (payload.size() + MIN_ENTRIES >= payload.max_size()) { 31 if (std::distance(payload.data(), payload_cursor) + MIN_ENTRIES >= payload.max_size()) {
32 LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); 32 LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread");
33 scheduler.WaitWorker(); 33 scheduler.WaitWorker();
34 payload.clear(); 34 payload_cursor = payload.data();
35 } 35 }
36 upload_start = &*payload.end(); 36 upload_start = payload_cursor;
37} 37}
38 38
39void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, 39void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template,
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h
index e214f7195..d35e77c44 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.h
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h
@@ -4,8 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <variant> 7#include <array>
8#include <boost/container/static_vector.hpp>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
11#include "video_core/vulkan_common/vulkan_wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -16,13 +15,15 @@ class Device;
16class VKScheduler; 15class VKScheduler;
17 16
18struct DescriptorUpdateEntry { 17struct DescriptorUpdateEntry {
19 DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {} 18 struct Empty {};
20 19
20 DescriptorUpdateEntry() = default;
21 DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {}
21 DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {} 22 DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {}
22
23 DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {} 23 DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {}
24 24
25 union { 25 union {
26 Empty empty{};
26 VkDescriptorImageInfo image; 27 VkDescriptorImageInfo image;
27 VkDescriptorBufferInfo buffer; 28 VkDescriptorBufferInfo buffer;
28 VkBufferView texel_buffer; 29 VkBufferView texel_buffer;
@@ -41,39 +42,40 @@ public:
41 void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); 42 void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set);
42 43
43 void AddSampledImage(VkImageView image_view, VkSampler sampler) { 44 void AddSampledImage(VkImageView image_view, VkSampler sampler) {
44 payload.emplace_back(VkDescriptorImageInfo{ 45 *(payload_cursor++) = VkDescriptorImageInfo{
45 .sampler = sampler, 46 .sampler = sampler,
46 .imageView = image_view, 47 .imageView = image_view,
47 .imageLayout = VK_IMAGE_LAYOUT_GENERAL, 48 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
48 }); 49 };
49 } 50 }
50 51
51 void AddImage(VkImageView image_view) { 52 void AddImage(VkImageView image_view) {
52 payload.emplace_back(VkDescriptorImageInfo{ 53 *(payload_cursor++) = VkDescriptorImageInfo{
53 .sampler = VK_NULL_HANDLE, 54 .sampler = VK_NULL_HANDLE,
54 .imageView = image_view, 55 .imageView = image_view,
55 .imageLayout = VK_IMAGE_LAYOUT_GENERAL, 56 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
56 }); 57 };
57 } 58 }
58 59
59 void AddBuffer(VkBuffer buffer, u64 offset, size_t size) { 60 void AddBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size) {
60 payload.emplace_back(VkDescriptorBufferInfo{ 61 *(payload_cursor++) = VkDescriptorBufferInfo{
61 .buffer = buffer, 62 .buffer = buffer,
62 .offset = offset, 63 .offset = offset,
63 .range = size, 64 .range = size,
64 }); 65 };
65 } 66 }
66 67
67 void AddTexelBuffer(VkBufferView texel_buffer) { 68 void AddTexelBuffer(VkBufferView texel_buffer) {
68 payload.emplace_back(texel_buffer); 69 *(payload_cursor++) = texel_buffer;
69 } 70 }
70 71
71private: 72private:
72 const Device& device; 73 const Device& device;
73 VKScheduler& scheduler; 74 VKScheduler& scheduler;
74 75
76 DescriptorUpdateEntry* payload_cursor = nullptr;
75 const DescriptorUpdateEntry* upload_start = nullptr; 77 const DescriptorUpdateEntry* upload_start = nullptr;
76 boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload; 78 std::array<DescriptorUpdateEntry, 0x10000> payload;
77}; 79};
78 80
79} // namespace Vulkan 81} // namespace Vulkan
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
index c680fd2c2..b92cd6886 100644
--- a/src/yuzu/applets/controller.cpp
+++ b/src/yuzu/applets/controller.cpp
@@ -67,6 +67,8 @@ bool IsControllerCompatible(Settings::ControllerType controller_type,
67 return parameters.allow_right_joycon; 67 return parameters.allow_right_joycon;
68 case Settings::ControllerType::Handheld: 68 case Settings::ControllerType::Handheld:
69 return parameters.enable_single_mode && parameters.allow_handheld; 69 return parameters.enable_single_mode && parameters.allow_handheld;
70 case Settings::ControllerType::GameCube:
71 return parameters.allow_gamecube_controller;
70 default: 72 default:
71 return false; 73 return false;
72 } 74 }
@@ -370,7 +372,7 @@ void QtControllerSelectorDialog::SetSupportedControllers() {
370 QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme)); 372 QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
371 } 373 }
372 374
373 if (parameters.allow_pro_controller) { 375 if (parameters.allow_pro_controller || parameters.allow_gamecube_controller) {
374 ui->controllerSupported5->setStyleSheet( 376 ui->controllerSupported5->setStyleSheet(
375 QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme)); 377 QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
376 } else { 378 } else {
@@ -420,6 +422,10 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index
420 Settings::ControllerType::Handheld); 422 Settings::ControllerType::Handheld);
421 emulated_controllers[player_index]->addItem(tr("Handheld")); 423 emulated_controllers[player_index]->addItem(tr("Handheld"));
422 } 424 }
425
426 pairs.emplace_back(emulated_controllers[player_index]->count(),
427 Settings::ControllerType::GameCube);
428 emulated_controllers[player_index]->addItem(tr("GameCube Controller"));
423} 429}
424 430
425Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( 431Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
@@ -461,6 +467,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
461 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), 467 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
462 player_index)) { 468 player_index)) {
463 case Settings::ControllerType::ProController: 469 case Settings::ControllerType::ProController:
470 case Settings::ControllerType::GameCube:
464 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); 471 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
465 case Settings::ControllerType::DualJoyconDetached: 472 case Settings::ControllerType::DualJoyconDetached:
466 return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); "); 473 return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 0635d13d0..3d6f64300 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -614,12 +614,6 @@ void Config::ReadDataStorageValues() {
614 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir))) 614 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)))
615 .toString() 615 .toString()
616 .toStdString()); 616 .toStdString());
617 FS::GetUserPath(FS::UserPath::CacheDir,
618 qt_config
619 ->value(QStringLiteral("cache_directory"),
620 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)))
621 .toString()
622 .toStdString());
623 Settings::values.gamecard_inserted = 617 Settings::values.gamecard_inserted =
624 ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); 618 ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
625 Settings::values.gamecard_current_game = 619 Settings::values.gamecard_current_game =
@@ -1218,9 +1212,6 @@ void Config::SaveDataStorageValues() {
1218 WriteSetting(QStringLiteral("dump_directory"), 1212 WriteSetting(QStringLiteral("dump_directory"),
1219 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)), 1213 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)),
1220 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir))); 1214 QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)));
1221 WriteSetting(QStringLiteral("cache_directory"),
1222 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)),
1223 QString::fromStdString(FS::GetUserPath(FS::UserPath::CacheDir)));
1224 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); 1215 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
1225 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, 1216 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
1226 false); 1217 false);
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index 7ab4a80f7..bde2d4620 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -26,8 +26,6 @@ ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
26 [this] { SetDirectory(DirectoryTarget::Dump, ui->dump_path_edit); }); 26 [this] { SetDirectory(DirectoryTarget::Dump, ui->dump_path_edit); });
27 connect(ui->load_path_button, &QToolButton::pressed, this, 27 connect(ui->load_path_button, &QToolButton::pressed, this,
28 [this] { SetDirectory(DirectoryTarget::Load, ui->load_path_edit); }); 28 [this] { SetDirectory(DirectoryTarget::Load, ui->load_path_edit); });
29 connect(ui->cache_directory_button, &QToolButton::pressed, this,
30 [this] { SetDirectory(DirectoryTarget::Cache, ui->cache_directory_edit); });
31 29
32 connect(ui->reset_game_list_cache, &QPushButton::pressed, this, 30 connect(ui->reset_game_list_cache, &QPushButton::pressed, this,
33 &ConfigureFilesystem::ResetMetadata); 31 &ConfigureFilesystem::ResetMetadata);
@@ -50,8 +48,6 @@ void ConfigureFilesystem::setConfiguration() {
50 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir))); 48 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir)));
51 ui->load_path_edit->setText( 49 ui->load_path_edit->setText(
52 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir))); 50 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir)));
53 ui->cache_directory_edit->setText(
54 QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)));
55 51
56 ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); 52 ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
57 ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); 53 ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
@@ -72,9 +68,6 @@ void ConfigureFilesystem::applyConfiguration() {
72 ui->dump_path_edit->text().toStdString()); 68 ui->dump_path_edit->text().toStdString());
73 Common::FS::GetUserPath(Common::FS::UserPath::LoadDir, 69 Common::FS::GetUserPath(Common::FS::UserPath::LoadDir,
74 ui->load_path_edit->text().toStdString()); 70 ui->load_path_edit->text().toStdString());
75 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir,
76 ui->cache_directory_edit->text().toStdString());
77 Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString();
78 71
79 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); 72 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
80 Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked(); 73 Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked();
@@ -103,9 +96,6 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
103 case DirectoryTarget::Load: 96 case DirectoryTarget::Load:
104 caption = tr("Select Mod Load Directory..."); 97 caption = tr("Select Mod Load Directory...");
105 break; 98 break;
106 case DirectoryTarget::Cache:
107 caption = tr("Select Cache Directory...");
108 break;
109 } 99 }
110 100
111 QString str; 101 QString str;
diff --git a/src/yuzu/configuration/configure_filesystem.h b/src/yuzu/configuration/configure_filesystem.h
index a79303760..2147cd405 100644
--- a/src/yuzu/configuration/configure_filesystem.h
+++ b/src/yuzu/configuration/configure_filesystem.h
@@ -32,7 +32,6 @@ private:
32 Gamecard, 32 Gamecard,
33 Dump, 33 Dump,
34 Load, 34 Load,
35 Cache,
36 }; 35 };
37 36
38 void SetDirectory(DirectoryTarget target, QLineEdit* edit); 37 void SetDirectory(DirectoryTarget target, QLineEdit* edit);
diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui
index 84bea0600..62b9abc7a 100644
--- a/src/yuzu/configuration/configure_filesystem.ui
+++ b/src/yuzu/configuration/configure_filesystem.ui
@@ -198,40 +198,7 @@
198 <string>Caching</string> 198 <string>Caching</string>
199 </property> 199 </property>
200 <layout class="QGridLayout" name="gridLayout_5"> 200 <layout class="QGridLayout" name="gridLayout_5">
201 <item row="0" column="0"> 201 <item row="0" column="0" colspan="2">
202 <widget class="QLabel" name="label_10">
203 <property name="text">
204 <string>Cache Directory</string>
205 </property>
206 </widget>
207 </item>
208 <item row="0" column="1">
209 <spacer name="horizontalSpacer_3">
210 <property name="orientation">
211 <enum>Qt::Horizontal</enum>
212 </property>
213 <property name="sizeType">
214 <enum>QSizePolicy::Fixed</enum>
215 </property>
216 <property name="sizeHint" stdset="0">
217 <size>
218 <width>40</width>
219 <height>20</height>
220 </size>
221 </property>
222 </spacer>
223 </item>
224 <item row="0" column="2">
225 <widget class="QLineEdit" name="cache_directory_edit"/>
226 </item>
227 <item row="0" column="3">
228 <widget class="QToolButton" name="cache_directory_button">
229 <property name="text">
230 <string>...</string>
231 </property>
232 </widget>
233 </item>
234 <item row="1" column="0" colspan="4">
235 <layout class="QHBoxLayout" name="horizontalLayout_2"> 202 <layout class="QHBoxLayout" name="horizontalLayout_2">
236 <item> 203 <item>
237 <widget class="QCheckBox" name="cache_game_list"> 204 <widget class="QCheckBox" name="cache_game_list">
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index c9d19c948..21d0d3449 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -467,10 +467,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
467 467
468 UpdateControllerIcon(); 468 UpdateControllerIcon();
469 UpdateControllerAvailableButtons(); 469 UpdateControllerAvailableButtons();
470 UpdateControllerEnabledButtons();
471 UpdateControllerButtonNames();
470 UpdateMotionButtons(); 472 UpdateMotionButtons();
471 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) { 473 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
472 UpdateControllerIcon(); 474 UpdateControllerIcon();
473 UpdateControllerAvailableButtons(); 475 UpdateControllerAvailableButtons();
476 UpdateControllerEnabledButtons();
477 UpdateControllerButtonNames();
474 UpdateMotionButtons(); 478 UpdateMotionButtons();
475 }); 479 });
476 480
@@ -558,9 +562,6 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
558 &ConfigureInputPlayer::SaveProfile); 562 &ConfigureInputPlayer::SaveProfile);
559 563
560 LoadConfiguration(); 564 LoadConfiguration();
561
562 // TODO(wwylele): enable this when we actually emulate it
563 ui->buttonHome->setEnabled(false);
564 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); 565 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
565 ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked()); 566 ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked());
566} 567}
@@ -924,6 +925,12 @@ void ConfigureInputPlayer::SetConnectableControllers() {
924 Settings::ControllerType::Handheld); 925 Settings::ControllerType::Handheld);
925 ui->comboControllerType->addItem(tr("Handheld")); 926 ui->comboControllerType->addItem(tr("Handheld"));
926 } 927 }
928
929 if (enable_all || npad_style_set.gamecube == 1) {
930 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
931 Settings::ControllerType::GameCube);
932 ui->comboControllerType->addItem(tr("GameCube Controller"));
933 }
927 }; 934 };
928 935
929 Core::System& system{Core::System::GetInstance()}; 936 Core::System& system{Core::System::GetInstance()};
@@ -1014,7 +1021,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1014 1021
1015 // List of all the widgets that will be hidden by any of the following layouts that need 1022 // List of all the widgets that will be hidden by any of the following layouts that need
1016 // "unhidden" after the controller type changes 1023 // "unhidden" after the controller type changes
1017 const std::array<QWidget*, 9> layout_show = { 1024 const std::array<QWidget*, 11> layout_show = {
1018 ui->buttonShoulderButtonsSLSR, 1025 ui->buttonShoulderButtonsSLSR,
1019 ui->horizontalSpacerShoulderButtonsWidget, 1026 ui->horizontalSpacerShoulderButtonsWidget,
1020 ui->horizontalSpacerShoulderButtonsWidget2, 1027 ui->horizontalSpacerShoulderButtonsWidget2,
@@ -1024,6 +1031,8 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1024 ui->buttonShoulderButtonsRight, 1031 ui->buttonShoulderButtonsRight,
1025 ui->buttonMiscButtonsPlusHome, 1032 ui->buttonMiscButtonsPlusHome,
1026 ui->bottomRight, 1033 ui->bottomRight,
1034 ui->buttonMiscButtonsMinusGroup,
1035 ui->buttonMiscButtonsScreenshotGroup,
1027 }; 1036 };
1028 1037
1029 for (auto* widget : layout_show) { 1038 for (auto* widget : layout_show) {
@@ -1056,6 +1065,14 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1056 ui->bottomLeft, 1065 ui->bottomLeft,
1057 }; 1066 };
1058 break; 1067 break;
1068 case Settings::ControllerType::GameCube:
1069 layout_hidden = {
1070 ui->buttonShoulderButtonsSLSR,
1071 ui->horizontalSpacerShoulderButtonsWidget2,
1072 ui->buttonMiscButtonsMinusGroup,
1073 ui->buttonMiscButtonsScreenshotGroup,
1074 };
1075 break;
1059 } 1076 }
1060 1077
1061 for (auto* widget : layout_hidden) { 1078 for (auto* widget : layout_hidden) {
@@ -1063,6 +1080,52 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1063 } 1080 }
1064} 1081}
1065 1082
1083void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1084 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1085 if (debug) {
1086 layout = Settings::ControllerType::ProController;
1087 }
1088
1089 // List of all the widgets that will be disabled by any of the following layouts that need
1090 // "enabled" after the controller type changes
1091 const std::array<QWidget*, 4> layout_enable = {
1092 ui->buttonHome,
1093 ui->buttonLStickPressedGroup,
1094 ui->groupRStickPressed,
1095 ui->buttonShoulderButtonsButtonLGroup,
1096 };
1097
1098 for (auto* widget : layout_enable) {
1099 widget->setEnabled(true);
1100 }
1101
1102 std::vector<QWidget*> layout_disable;
1103 switch (layout) {
1104 case Settings::ControllerType::ProController:
1105 case Settings::ControllerType::DualJoyconDetached:
1106 case Settings::ControllerType::Handheld:
1107 case Settings::ControllerType::LeftJoycon:
1108 case Settings::ControllerType::RightJoycon:
1109 // TODO(wwylele): enable this when we actually emulate it
1110 layout_disable = {
1111 ui->buttonHome,
1112 };
1113 break;
1114 case Settings::ControllerType::GameCube:
1115 layout_disable = {
1116 ui->buttonHome,
1117 ui->buttonLStickPressedGroup,
1118 ui->groupRStickPressed,
1119 ui->buttonShoulderButtonsButtonLGroup,
1120 };
1121 break;
1122 }
1123
1124 for (auto* widget : layout_disable) {
1125 widget->setEnabled(false);
1126 }
1127}
1128
1066void ConfigureInputPlayer::UpdateMotionButtons() { 1129void ConfigureInputPlayer::UpdateMotionButtons() {
1067 if (debug) { 1130 if (debug) {
1068 // Motion isn't used with the debug controller, hide both groupboxes. 1131 // Motion isn't used with the debug controller, hide both groupboxes.
@@ -1085,6 +1148,11 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
1085 ui->buttonMotionLeftGroup->hide(); 1148 ui->buttonMotionLeftGroup->hide();
1086 ui->buttonMotionRightGroup->show(); 1149 ui->buttonMotionRightGroup->show();
1087 break; 1150 break;
1151 case Settings::ControllerType::GameCube:
1152 // Hide both "Motion 1/2".
1153 ui->buttonMotionLeftGroup->hide();
1154 ui->buttonMotionRightGroup->hide();
1155 break;
1088 case Settings::ControllerType::DualJoyconDetached: 1156 case Settings::ControllerType::DualJoyconDetached:
1089 default: 1157 default:
1090 // Show both "Motion 1/2". 1158 // Show both "Motion 1/2".
@@ -1094,6 +1162,36 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
1094 } 1162 }
1095} 1163}
1096 1164
1165void ConfigureInputPlayer::UpdateControllerButtonNames() {
1166 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1167 if (debug) {
1168 layout = Settings::ControllerType::ProController;
1169 }
1170
1171 switch (layout) {
1172 case Settings::ControllerType::ProController:
1173 case Settings::ControllerType::DualJoyconDetached:
1174 case Settings::ControllerType::Handheld:
1175 case Settings::ControllerType::LeftJoycon:
1176 case Settings::ControllerType::RightJoycon:
1177 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus"));
1178 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL"));
1179 ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR"));
1180 ui->buttonShoulderButtonsRGroup->setTitle(tr("R"));
1181 ui->LStick->setTitle(tr("Left Stick"));
1182 ui->RStick->setTitle(tr("Right Stick"));
1183 break;
1184 case Settings::ControllerType::GameCube:
1185 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause"));
1186 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L"));
1187 ui->buttonShoulderButtonsZRGroup->setTitle(tr("R"));
1188 ui->buttonShoulderButtonsRGroup->setTitle(tr("Z"));
1189 ui->LStick->setTitle(tr("Control Stick"));
1190 ui->RStick->setTitle(tr("C-Stick"));
1191 break;
1192 }
1193}
1194
1097void ConfigureInputPlayer::UpdateMappingWithDefaults() { 1195void ConfigureInputPlayer::UpdateMappingWithDefaults() {
1098 if (ui->comboDevices->currentIndex() == 0) { 1196 if (ui->comboDevices->currentIndex() == 0) {
1099 return; 1197 return;
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index da2b89136..efe953fbc 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -143,9 +143,15 @@ private:
143 /// Hides and disables controller settings based on the current controller type. 143 /// Hides and disables controller settings based on the current controller type.
144 void UpdateControllerAvailableButtons(); 144 void UpdateControllerAvailableButtons();
145 145
146 /// Disables controller settings based on the current controller type.
147 void UpdateControllerEnabledButtons();
148
146 /// Shows or hides motion groupboxes based on the current controller type. 149 /// Shows or hides motion groupboxes based on the current controller type.
147 void UpdateMotionButtons(); 150 void UpdateMotionButtons();
148 151
152 /// Alters the button names based on the current controller type.
153 void UpdateControllerButtonNames();
154
149 /// Gets the default controller mapping for this device and auto configures the input to match. 155 /// Gets the default controller mapping for this device and auto configures the input to match.
150 void UpdateMappingWithDefaults(); 156 void UpdateMappingWithDefaults();
151 157
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 0e8a964d2..61ba91cef 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -227,6 +227,9 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) {
227 case Settings::ControllerType::RightJoycon: 227 case Settings::ControllerType::RightJoycon:
228 DrawRightController(p, center); 228 DrawRightController(p, center);
229 break; 229 break;
230 case Settings::ControllerType::GameCube:
231 DrawGCController(p, center);
232 break;
230 case Settings::ControllerType::ProController: 233 case Settings::ControllerType::ProController:
231 default: 234 default:
232 DrawProController(p, center); 235 DrawProController(p, center);
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index 85724a8f3..2731d948d 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -42,7 +42,7 @@ void ControllerDialog::refreshConfiguration() {
42 42
43QAction* ControllerDialog::toggleViewAction() { 43QAction* ControllerDialog::toggleViewAction() {
44 if (toggle_view_action == nullptr) { 44 if (toggle_view_action == nullptr) {
45 toggle_view_action = new QAction(windowTitle(), this); 45 toggle_view_action = new QAction(tr("&Controller P1"), this);
46 toggle_view_action->setCheckable(true); 46 toggle_view_action->setCheckable(true);
47 toggle_view_action->setChecked(isVisible()); 47 toggle_view_action->setChecked(isVisible());
48 connect(toggle_view_action, &QAction::toggled, this, &ControllerDialog::setVisible); 48 connect(toggle_view_action, &QAction::toggled, this, &ControllerDialog::setVisible);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index aa0a9f288..6d8bc5509 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -329,9 +329,6 @@ void Config::ReadValues() {
329 FS::GetUserPath( 329 FS::GetUserPath(
330 FS::UserPath::DumpDir, 330 FS::UserPath::DumpDir,
331 sdl2_config->Get("Data Storage", "dump_directory", FS::GetUserPath(FS::UserPath::DumpDir))); 331 sdl2_config->Get("Data Storage", "dump_directory", FS::GetUserPath(FS::UserPath::DumpDir)));
332 FS::GetUserPath(FS::UserPath::CacheDir,
333 sdl2_config->Get("Data Storage", "cache_directory",
334 FS::GetUserPath(FS::UserPath::CacheDir)));
335 Settings::values.gamecard_inserted = 332 Settings::values.gamecard_inserted =
336 sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); 333 sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
337 Settings::values.gamecard_current_game = 334 Settings::values.gamecard_current_game =