diff options
Diffstat (limited to 'src')
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 { | |||
| 13 | namespace { | 15 | namespace { |
| 14 | constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00; | 16 | constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00; |
| 15 | constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL; | 17 | constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL; |
| 18 | using DelayLineTimes = std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>; | ||
| 19 | |||
| 20 | constexpr DelayLineTimes FDN_MIN_DELAY_LINE_TIMES{5.0f, 6.0f, 13.0f, 14.0f}; | ||
| 21 | constexpr DelayLineTimes FDN_MAX_DELAY_LINE_TIMES{45.704f, 82.782f, 149.94f, 271.58f}; | ||
| 22 | constexpr DelayLineTimes DECAY0_MAX_DELAY_LINE_TIMES{17.0f, 13.0f, 9.0f, 7.0f}; | ||
| 23 | constexpr DelayLineTimes DECAY1_MAX_DELAY_LINE_TIMES{19.0f, 11.0f, 10.0f, 6.0f}; | ||
| 24 | constexpr 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}; | ||
| 28 | constexpr 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 | ||
| 17 | template <std::size_t N> | 33 | template <std::size_t N> |
| 18 | void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) { | 34 | void 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 | ||
| 84 | float 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 | |||
| 93 | float SinD(float degrees) { | ||
| 94 | return std::sin(degrees * std::numbers::pi_v<float> / 180.0f); | ||
| 95 | } | ||
| 96 | |||
| 97 | float CosD(float degrees) { | ||
| 98 | return std::cos(degrees * std::numbers::pi_v<float> / 180.0f); | ||
| 99 | } | ||
| 100 | |||
| 101 | float ToFloat(s32 sample) { | ||
| 102 | return static_cast<float>(sample) / 65536.f; | ||
| 103 | } | ||
| 104 | |||
| 105 | s32 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 | |||
| 118 | constexpr 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 | |||
| 121 | constexpr 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 | |||
| 124 | constexpr 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 | |||
| 127 | constexpr 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 | |||
| 130 | template <std::size_t CHANNEL_COUNT> | ||
| 131 | void 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 | ||
| 70 | CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_, | 234 | CommandGenerator::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 | ||
| 377 | void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, | 540 | void 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 | ||
| 727 | void 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 | |||
| 782 | void 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 | |||
| 531 | void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume, | 854 | void 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; | |||
| 21 | class EffectContext; | 21 | class EffectContext; |
| 22 | class EffectBase; | 22 | class EffectBase; |
| 23 | struct AuxInfoDSP; | 23 | struct AuxInfoDSP; |
| 24 | struct I3dl2ReverbParams; | ||
| 25 | struct I3dl2ReverbState; | ||
| 24 | using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>; | 26 | using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>; |
| 25 | 27 | ||
| 26 | class CommandGenerator { | 28 | class 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 |
| 35 | constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY; | 35 | constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY; |
| 36 | constexpr f32 I3DL2REVERB_MAX_LEVEL = 5000.0f; | ||
| 37 | constexpr f32 I3DL2REVERB_MIN_REFLECTION_DURATION = 0.02f; | ||
| 38 | constexpr std::size_t I3DL2REVERB_TAPS = 20; | ||
| 39 | constexpr std::size_t I3DL2REVERB_DELAY_LINE_COUNT = 4; | ||
| 40 | using Fractional = s32; | ||
| 41 | |||
| 42 | template <typename T> | ||
| 43 | constexpr Fractional ToFractional(T x) { | ||
| 44 | return static_cast<Fractional>(x * static_cast<T>(0x4000)); | ||
| 45 | } | ||
| 46 | |||
| 47 | constexpr Fractional MultiplyFractional(Fractional lhs, Fractional rhs) { | ||
| 48 | return static_cast<Fractional>(static_cast<s64>(lhs) * rhs >> 14); | ||
| 49 | } | ||
| 50 | |||
| 51 | constexpr s32 FractionalToFixed(Fractional x) { | ||
| 52 | const auto s = x & (1 << 13); | ||
| 53 | return static_cast<s32>(x >> 14) + s; | ||
| 54 | } | ||
| 55 | |||
| 56 | constexpr s32 CalculateDelaySamples(s32 sample_rate_khz, float time) { | ||
| 57 | return FractionalToFixed(MultiplyFractional(ToFractional(sample_rate_khz), ToFractional(time))); | ||
| 58 | } | ||
| 36 | 59 | ||
| 37 | static constexpr u32 VersionFromRevision(u32_le rev) { | 60 | static 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 | |||
| 4 | namespace AudioCore { | ||
| 5 | DelayLineBase::DelayLineBase() = default; | ||
| 6 | DelayLineBase::~DelayLineBase() = default; | ||
| 7 | |||
| 8 | void 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 | |||
| 17 | void 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 | |||
| 25 | s32 DelayLineBase::GetDelay() const { | ||
| 26 | return delay; | ||
| 27 | } | ||
| 28 | |||
| 29 | s32 DelayLineBase::GetMaxDelay() const { | ||
| 30 | return max_delay; | ||
| 31 | } | ||
| 32 | |||
| 33 | f32 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 | |||
| 42 | f32 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 | |||
| 57 | float* DelayLineBase::GetInput() { | ||
| 58 | return input; | ||
| 59 | } | ||
| 60 | |||
| 61 | const float* DelayLineBase::GetInput() const { | ||
| 62 | return input; | ||
| 63 | } | ||
| 64 | |||
| 65 | f32 DelayLineBase::GetOutputSample() const { | ||
| 66 | return *output; | ||
| 67 | } | ||
| 68 | |||
| 69 | void DelayLineBase::Clear() { | ||
| 70 | std::memset(buffer, 0, sizeof(float) * max_delay); | ||
| 71 | } | ||
| 72 | |||
| 73 | void DelayLineBase::Reset() { | ||
| 74 | buffer = nullptr; | ||
| 75 | buffer_end = nullptr; | ||
| 76 | max_delay = 0; | ||
| 77 | input = nullptr; | ||
| 78 | output = nullptr; | ||
| 79 | delay = 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | DelayLineAllPass::DelayLineAllPass() = default; | ||
| 83 | DelayLineAllPass::~DelayLineAllPass() = default; | ||
| 84 | |||
| 85 | void DelayLineAllPass::Initialize(u32 delay_, float coeffcient_, f32* src_buffer) { | ||
| 86 | DelayLineBase::Initialize(delay_, src_buffer); | ||
| 87 | SetCoefficient(coeffcient_); | ||
| 88 | } | ||
| 89 | |||
| 90 | void DelayLineAllPass::SetCoefficient(float coeffcient_) { | ||
| 91 | coefficient = coeffcient_; | ||
| 92 | } | ||
| 93 | |||
| 94 | f32 DelayLineAllPass::Tick(f32 sample) { | ||
| 95 | const auto temp = sample - coefficient * *output; | ||
| 96 | return coefficient * temp + DelayLineBase::Tick(temp); | ||
| 97 | } | ||
| 98 | |||
| 99 | void 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 | |||
| 5 | namespace AudioCore { | ||
| 6 | |||
| 7 | class DelayLineBase { | ||
| 8 | public: | ||
| 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 | |||
| 24 | protected: | ||
| 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 | |||
| 33 | class DelayLineAllPass final : public DelayLineBase { | ||
| 34 | public: | ||
| 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 | |||
| 43 | private: | ||
| 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 | ||
| 93 | std::vector<u8>& EffectBase::GetWorkBuffer() { | ||
| 94 | return work_buffer; | ||
| 95 | } | ||
| 96 | |||
| 97 | const std::vector<u8>& EffectBase::GetWorkBuffer() const { | ||
| 98 | return work_buffer; | ||
| 99 | } | ||
| 100 | |||
| 93 | EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {} | 101 | EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {} |
| 94 | EffectI3dl2Reverb::~EffectI3dl2Reverb() = default; | 102 | EffectI3dl2Reverb::~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 | ||
| 146 | I3dl2ReverbState& EffectI3dl2Reverb::GetState() { | ||
| 147 | return state; | ||
| 148 | } | ||
| 149 | |||
| 150 | const I3dl2ReverbState& EffectI3dl2Reverb::GetState() const { | ||
| 151 | return state; | ||
| 152 | } | ||
| 153 | |||
| 132 | EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {} | 154 | EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {} |
| 133 | EffectBiquadFilter::~EffectBiquadFilter() = default; | 155 | EffectBiquadFilter::~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 | ||
| 198 | protected: | 201 | protected: |
| 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 | ||
| 206 | template <typename T> | 210 | template <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 | ||
| 236 | struct 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 | |||
| 232 | class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> { | 257 | class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> { |
| 233 | public: | 258 | public: |
| 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 | |||
| 240 | private: | 268 | private: |
| 241 | bool skipped = false; | 269 | bool skipped = false; |
| 270 | I3dl2ReverbState state{}; | ||
| 242 | }; | 271 | }; |
| 243 | 272 | ||
| 244 | class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> { | 273 | class 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 | ||
| 43 | using namespace std; | 45 | using namespace std; |
| 44 | 46 | ||
| 45 | typedef uint8_t uint8; | ||
| 46 | typedef uint32_t uint32; | ||
| 47 | typedef uint64_t uint64; | ||
| 48 | |||
| 49 | namespace Common { | 47 | namespace Common { |
| 50 | 48 | ||
| 51 | static uint64 UNALIGNED_LOAD64(const char* p) { | 49 | static 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 | ||
| 57 | static uint32 UNALIGNED_LOAD32(const char* p) { | 55 | static 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 | ||
| 79 | static uint64 Fetch64(const char* p) { | 77 | static 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 | ||
| 83 | static uint32 Fetch32(const char* p) { | 81 | static 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. |
| 88 | static const uint64 k0 = 0xc3a5c85c97cb3127ULL; | 86 | static constexpr u64 k0 = 0xc3a5c85c97cb3127ULL; |
| 89 | static const uint64 k1 = 0xb492b66fbe98f273ULL; | 87 | static constexpr u64 k1 = 0xb492b66fbe98f273ULL; |
| 90 | static const uint64 k2 = 0x9ae16a3b2f90404fULL; | 88 | static 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. |
| 94 | static uint64 Rotate(uint64 val, int shift) { | 92 | static 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 | ||
| 99 | static uint64 ShiftMix(uint64 val) { | 97 | static u64 ShiftMix(u64 val) { |
| 100 | return val ^ (val >> 47); | 98 | return val ^ (val >> 47); |
| 101 | } | 99 | } |
| 102 | 100 | ||
| 103 | static uint64 HashLen16(uint64 u, uint64 v) { | 101 | static u64 HashLen16(u64 u, u64 v) { |
| 104 | return Hash128to64(uint128(u, v)); | 102 | return Hash128to64(u128{u, v}); |
| 105 | } | 103 | } |
| 106 | 104 | ||
| 107 | static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { | 105 | static 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 | ||
| 117 | static uint64 HashLen0to16(const char* s, std::size_t len) { | 115 | static 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. |
| 144 | static uint64 HashLen17to32(const char* s, std::size_t len) { | 142 | static 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. |
| 155 | static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, | 153 | static 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. |
| 167 | static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) { | 164 | static 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. |
| 173 | static uint64 HashLen33to64(const char* s, std::size_t len) { | 170 | static 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 | ||
| 194 | uint64 CityHash64(const char* s, std::size_t len) { | 191 | u64 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 | ||
| 232 | uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) { | 229 | u64 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 | ||
| 236 | uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) { | 233 | u64 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. |
| 242 | static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) { | 239 | static 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 | ||
| 272 | uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) { | 269 | u128 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 | ||
| 334 | uint128 CityHash128(const char* s, std::size_t len) { | 331 | u128 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 | ||
| 68 | namespace Common { | 66 | namespace Common { |
| 69 | 67 | ||
| 70 | using 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 | |||
| 14 | namespace Common { | ||
| 15 | |||
| 16 | #ifdef _MSC_VER | ||
| 17 | |||
| 18 | u64 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 | |||
| 31 | u64 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 | |||
| 41 | u128 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 | |||
| 53 | std::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 | ||
| 10 | namespace Common { | 21 | namespace 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 | ||
| 24 | namespace { | ||
| 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 | |||
| 73 | namespace Common { | 15 | namespace Common { |
| 74 | 16 | ||
| 75 | u64 EstimateRDTSCFrequency() { | 17 | u64 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 | |||
| 13 | namespace Core::Timing { | ||
| 14 | |||
| 15 | constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / Hardware::BASE_CLOCK_RATE; | ||
| 16 | |||
| 17 | s64 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 | |||
| 29 | s64 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 | |||
| 41 | s64 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 | |||
| 46 | u64 msToClockCycles(std::chrono::milliseconds ns) { | ||
| 47 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); | ||
| 48 | return Common::Divide128On32(temp, 1000).first; | ||
| 49 | } | ||
| 50 | |||
| 51 | u64 usToClockCycles(std::chrono::microseconds ns) { | ||
| 52 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); | ||
| 53 | return Common::Divide128On32(temp, 1000000).first; | ||
| 54 | } | ||
| 55 | |||
| 56 | u64 nsToClockCycles(std::chrono::nanoseconds ns) { | ||
| 57 | const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ); | ||
| 58 | return Common::Divide128On32(temp, 1000000000).first; | ||
| 59 | } | ||
| 60 | |||
| 61 | u64 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 | |||
| 66 | std::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 | |||
| 72 | std::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 | |||
| 78 | std::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 | ||
| 10 | namespace Core::Timing { | 12 | namespace Core::Timing { |
| 11 | 13 | ||
| 12 | s64 msToCycles(std::chrono::milliseconds ms); | 14 | namespace detail { |
| 13 | s64 usToCycles(std::chrono::microseconds us); | 15 | constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000; |
| 14 | s64 nsToCycles(std::chrono::nanoseconds ns); | 16 | constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000; |
| 15 | u64 msToClockCycles(std::chrono::milliseconds ns); | 17 | } // namespace detail |
| 16 | u64 usToClockCycles(std::chrono::microseconds ns); | 18 | |
| 17 | u64 nsToClockCycles(std::chrono::nanoseconds ns); | 19 | [[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) { |
| 18 | std::chrono::milliseconds CyclesToMs(s64 cycles); | 20 | return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED; |
| 19 | std::chrono::nanoseconds CyclesToNs(s64 cycles); | 21 | } |
| 20 | std::chrono::microseconds CyclesToUs(s64 cycles); | 22 | |
| 21 | 23 | [[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) { | |
| 22 | u64 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 | ||
| 36 | class ControllerApplet { | 37 | class 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 | ||
| 22 | namespace Service::HID { | 22 | namespace Service::HID { |
| 23 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | 23 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; |
| 24 | constexpr s32 HID_TRIGGER_MAX = 0x7fff; | ||
| 24 | [[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | 25 | [[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; |
| 25 | constexpr std::size_t NPAD_OFFSET = 0x9A00; | 26 | constexpr std::size_t NPAD_OFFSET = 0x9A00; |
| 26 | constexpr u32 BATTERY_FULL = 2; | 27 | constexpr 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 | ||
| 409 | void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 434 | void 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 | ||
| 282 | void TimeManager::Shutdown() { | ||
| 283 | impl.reset(); | ||
| 284 | } | ||
| 285 | |||
| 282 | void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { | 286 | void 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() { | |||
| 82 | void Mouse::MouseMove(int x, int y, int center_x, int center_y) { | 87 | void 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 | ||
| 345 | struct PlayerInput { | 346 | struct 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 @@ | |||
| 1 | add_executable(tests | 1 | add_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 | |||
| 9 | constexpr char msg[] = "The blue frogs are singing under the crimson sky.\n" | ||
| 10 | "It is time to run, Robert."; | ||
| 11 | |||
| 12 | using namespace Common; | ||
| 13 | |||
| 14 | TEST_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 | ||
| 38 | CDmaPusher::~CDmaPusher() = default; | 38 | CDmaPusher::~CDmaPusher() = default; |
| 39 | 39 | ||
| 40 | void CDmaPusher::Push(ChCommandHeaderList&& entries) { | 40 | void CDmaPusher::ProcessEntries(ChCommandHeaderList&& entries) { |
| 41 | cdma_queue.push(std::move(entries)); | 41 | for (const auto& value : entries) { |
| 42 | } | ||
| 43 | |||
| 44 | void CDmaPusher::DispatchCalls() { | ||
| 45 | while (!cdma_queue.empty()) { | ||
| 46 | Step(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void 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() { | |||
| 102 | void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) { | 86 | void 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 | ||
| 164 | void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 state_offset, | 148 | void 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 @@ | |||
| 16 | namespace Tegra { | 14 | namespace Tegra { |
| 17 | 15 | ||
| 18 | class GPU; | 16 | class GPU; |
| 17 | class Host1x; | ||
| 19 | class Nvdec; | 18 | class Nvdec; |
| 20 | class Vic; | 19 | class Vic; |
| 21 | class Host1x; | ||
| 22 | 20 | ||
| 23 | enum class ChSubmissionMode : u32 { | 21 | enum 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 | ||
| 51 | enum class ChMethod : u32 { | ||
| 52 | Empty = 0, | ||
| 53 | SetMethod = 0x10, | ||
| 54 | SetData = 0x11, | ||
| 55 | }; | ||
| 56 | |||
| 57 | union ChCommandHeader { | 49 | union 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 | }; |
| 63 | static_assert(sizeof(ChCommandHeader) == sizeof(u32), "ChCommand header is an invalid size"); | 55 | static_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 | ||
| 97 | private: | ||
| 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 | ||
| 114 | private: | ||
| 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 | ||
| 46 | void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { | 46 | void 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 | ||
| 51 | void Codec::StateWrite(u32 offset, u64 arguments) { | 53 | void Codec::StateWrite(u32 offset, u64 arguments) { |
| @@ -55,7 +57,6 @@ void Codec::StateWrite(u32 offset, u64 arguments) { | |||
| 55 | 57 | ||
| 56 | void Codec::Decode() { | 58 | void 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 | ||
| 13 | Nvdec::~Nvdec() = default; | 13 | Nvdec::~Nvdec() = default; |
| 14 | 14 | ||
| 15 | void Nvdec::ProcessMethod(Method method, const std::vector<u32>& arguments) { | 15 | void 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" { | |||
| 18 | namespace Tegra { | 18 | namespace Tegra { |
| 19 | 19 | ||
| 20 | Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_) | 20 | Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_) |
| 21 | : gpu(gpu_), nvdec_processor(std::move(nvdec_processor_)) {} | 21 | : gpu(gpu_), |
| 22 | Vic::~Vic() = default; | 22 | nvdec_processor(std::move(nvdec_processor_)), converted_frame_buffer{nullptr, av_free} {} |
| 23 | 23 | ||
| 24 | void Vic::VicStateWrite(u32 offset, u32 arguments) { | 24 | Vic::~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 | ||
| 29 | void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) { | 26 | void 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 | ||
| 54 | void Vic::Execute() { | 50 | void 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 { | |||
| 15 | class GPU; | 15 | class GPU; |
| 16 | class Nvdec; | 16 | class Nvdec; |
| 17 | 17 | ||
| 18 | struct PlaneOffsets { | ||
| 19 | u32 luma_offset{}; | ||
| 20 | u32 chroma_u_offset{}; | ||
| 21 | u32 chroma_v_offset{}; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct 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 | }; | ||
| 53 | static_assert(sizeof(VicRegisters) == 0x7A0, "VicRegisters is an invalid size"); | ||
| 54 | |||
| 55 | class Vic { | 18 | class Vic { |
| 56 | public: | 19 | public: |
| 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 | ||
| 72 | private: | 35 | private: |
| 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 | ||
| 31 | GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_) | 31 | GPU::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 | ||
| 501 | void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 499 | void 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 | ||
| 16 | namespace Vulkan { | 17 | namespace Vulkan { |
| 17 | 18 | ||
| 18 | namespace { | 19 | namespace { |
| 19 | 20 | ||
| 20 | constexpr std::size_t POINT = 0; | 21 | constexpr size_t POINT = 0; |
| 21 | constexpr std::size_t LINE = 1; | 22 | constexpr size_t LINE = 1; |
| 22 | constexpr std::size_t POLYGON = 2; | 23 | constexpr size_t POLYGON = 2; |
| 23 | constexpr std::array POLYGON_OFFSET_ENABLE_LUT = { | 24 | constexpr 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 | ||
| 43 | void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) { | 44 | void 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 | ||
| 105 | void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { | 118 | void 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 | ||
| 144 | void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) { | 157 | void 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 | ||
| 181 | std::size_t FixedPipelineState::Hash() const noexcept { | 194 | size_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 | ||
| 186 | bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { | 199 | bool 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 | ||
| 225 | template <> | 223 | template <> |
| 226 | struct hash<Vulkan::FixedPipelineState> { | 224 | struct 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 | ||
| 204 | void BufferCacheRuntime::BindBuffer(VkBuffer buffer, u32 offset, u32 size) { | ||
| 205 | update_descriptor_queue.AddBuffer(buffer, offset, size); | ||
| 206 | } | ||
| 207 | |||
| 208 | void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle) { | 204 | void 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 { | |||
| 16 | class Device; | 17 | class Device; |
| 17 | class VKDescriptorPool; | 18 | class VKDescriptorPool; |
| 18 | class VKScheduler; | 19 | class VKScheduler; |
| 19 | class VKUpdateDescriptorQueue; | ||
| 20 | 20 | ||
| 21 | class BufferCacheRuntime; | 21 | class BufferCacheRuntime; |
| 22 | 22 | ||
| @@ -86,7 +86,9 @@ public: | |||
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | private: | 88 | private: |
| 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 | ||
| 20 | namespace Vulkan { | 20 | namespace Vulkan { |
| 21 | |||
| 22 | namespace { | 21 | namespace { |
| 23 | |||
| 24 | using namespace Dirty; | 22 | using namespace Dirty; |
| 25 | using namespace VideoCommon::Dirty; | 23 | using namespace VideoCommon::Dirty; |
| 26 | using Tegra::Engines::Maxwell3D; | 24 | using 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 | ||
| 129 | void 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 | |||
| 137 | void 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 | |||
| 146 | void SetupDirtyVertexAttributes(Tables& tables) { | ||
| 147 | FillBlock(tables[0], OFF(vertex_attrib_format), NUM(vertex_attrib_format), VertexAttributes); | ||
| 148 | } | ||
| 149 | |||
| 150 | void 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 | ||
| 133 | StateTracker::StateTracker(Tegra::GPU& gpu) | 159 | StateTracker::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 | }; |
| 40 | static_assert(Last <= std::numeric_limits<u8>::max()); | 45 | static_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 | |||
| 20 | VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default; | 20 | VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default; |
| 21 | 21 | ||
| 22 | void VKUpdateDescriptorQueue::TickFrame() { | 22 | void VKUpdateDescriptorQueue::TickFrame() { |
| 23 | payload.clear(); | 23 | payload_cursor = payload.data(); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | void VKUpdateDescriptorQueue::Acquire() { | 26 | void 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 | ||
| 39 | void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, | 39 | void 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; | |||
| 16 | class VKScheduler; | 15 | class VKScheduler; |
| 17 | 16 | ||
| 18 | struct DescriptorUpdateEntry { | 17 | struct 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 | ||
| 71 | private: | 72 | private: |
| 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 | ||
| 425 | Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( | 431 | Settings::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 | ||
| 1083 | void 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 | |||
| 1066 | void ConfigureInputPlayer::UpdateMotionButtons() { | 1129 | void 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 | ||
| 1165 | void 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 | |||
| 1097 | void ConfigureInputPlayer::UpdateMappingWithDefaults() { | 1195 | void 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 | ||
| 43 | QAction* ControllerDialog::toggleViewAction() { | 43 | QAction* 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 = |