diff options
Diffstat (limited to 'src')
35 files changed, 754 insertions, 154 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index eba0a5697..a72a907ef 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -3,6 +3,7 @@ set(SRCS | |||
| 3 | codec.cpp | 3 | codec.cpp |
| 4 | hle/dsp.cpp | 4 | hle/dsp.cpp |
| 5 | hle/filter.cpp | 5 | hle/filter.cpp |
| 6 | hle/mixers.cpp | ||
| 6 | hle/pipe.cpp | 7 | hle/pipe.cpp |
| 7 | hle/source.cpp | 8 | hle/source.cpp |
| 8 | interpolate.cpp | 9 | interpolate.cpp |
| @@ -16,6 +17,7 @@ set(HEADERS | |||
| 16 | hle/common.h | 17 | hle/common.h |
| 17 | hle/dsp.h | 18 | hle/dsp.h |
| 18 | hle/filter.h | 19 | hle/filter.h |
| 20 | hle/mixers.h | ||
| 19 | hle/pipe.h | 21 | hle/pipe.h |
| 20 | hle/source.h | 22 | hle/source.h |
| 21 | interpolate.h | 23 | interpolate.h |
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp index 5113ad8ca..0640e1eff 100644 --- a/src/audio_core/hle/dsp.cpp +++ b/src/audio_core/hle/dsp.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | 7 | ||
| 8 | #include "audio_core/hle/dsp.h" | 8 | #include "audio_core/hle/dsp.h" |
| 9 | #include "audio_core/hle/mixers.h" | ||
| 9 | #include "audio_core/hle/pipe.h" | 10 | #include "audio_core/hle/pipe.h" |
| 10 | #include "audio_core/hle/source.h" | 11 | #include "audio_core/hle/source.h" |
| 11 | #include "audio_core/sink.h" | 12 | #include "audio_core/sink.h" |
| @@ -14,6 +15,8 @@ | |||
| 14 | namespace DSP { | 15 | namespace DSP { |
| 15 | namespace HLE { | 16 | namespace HLE { |
| 16 | 17 | ||
| 18 | // Region management | ||
| 19 | |||
| 17 | std::array<SharedMemory, 2> g_regions; | 20 | std::array<SharedMemory, 2> g_regions; |
| 18 | 21 | ||
| 19 | static size_t CurrentRegionIndex() { | 22 | static size_t CurrentRegionIndex() { |
| @@ -41,16 +44,57 @@ static SharedMemory& WriteRegion() { | |||
| 41 | return g_regions[1 - CurrentRegionIndex()]; | 44 | return g_regions[1 - CurrentRegionIndex()]; |
| 42 | } | 45 | } |
| 43 | 46 | ||
| 47 | // Audio processing and mixing | ||
| 48 | |||
| 44 | static std::array<Source, num_sources> sources = { | 49 | static std::array<Source, num_sources> sources = { |
| 45 | Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), | 50 | Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), |
| 46 | Source(6), Source(7), Source(8), Source(9), Source(10), Source(11), | 51 | Source(6), Source(7), Source(8), Source(9), Source(10), Source(11), |
| 47 | Source(12), Source(13), Source(14), Source(15), Source(16), Source(17), | 52 | Source(12), Source(13), Source(14), Source(15), Source(16), Source(17), |
| 48 | Source(18), Source(19), Source(20), Source(21), Source(22), Source(23) | 53 | Source(18), Source(19), Source(20), Source(21), Source(22), Source(23) |
| 49 | }; | 54 | }; |
| 55 | static Mixers mixers; | ||
| 56 | |||
| 57 | static StereoFrame16 GenerateCurrentFrame() { | ||
| 58 | SharedMemory& read = ReadRegion(); | ||
| 59 | SharedMemory& write = WriteRegion(); | ||
| 60 | |||
| 61 | std::array<QuadFrame32, 3> intermediate_mixes = {}; | ||
| 62 | |||
| 63 | // Generate intermediate mixes | ||
| 64 | for (size_t i = 0; i < num_sources; i++) { | ||
| 65 | write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); | ||
| 66 | for (size_t mix = 0; mix < 3; mix++) { | ||
| 67 | sources[i].MixInto(intermediate_mixes[mix], mix); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | // Generate final mix | ||
| 72 | write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes); | ||
| 73 | |||
| 74 | StereoFrame16 output_frame = mixers.GetOutput(); | ||
| 75 | |||
| 76 | // Write current output frame to the shared memory region | ||
| 77 | for (size_t samplei = 0; samplei < output_frame.size(); samplei++) { | ||
| 78 | for (size_t channeli = 0; channeli < output_frame[0].size(); channeli++) { | ||
| 79 | write.final_samples.pcm16[samplei][channeli] = s16_le(output_frame[samplei][channeli]); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | return output_frame; | ||
| 84 | } | ||
| 85 | |||
| 86 | // Audio output | ||
| 50 | 87 | ||
| 51 | static std::unique_ptr<AudioCore::Sink> sink; | 88 | static std::unique_ptr<AudioCore::Sink> sink; |
| 52 | static AudioCore::TimeStretcher time_stretcher; | 89 | static AudioCore::TimeStretcher time_stretcher; |
| 53 | 90 | ||
| 91 | static void OutputCurrentFrame(const StereoFrame16& frame) { | ||
| 92 | time_stretcher.AddSamples(&frame[0][0], frame.size()); | ||
| 93 | sink->EnqueueSamples(time_stretcher.Process(sink->SamplesInQueue())); | ||
| 94 | } | ||
| 95 | |||
| 96 | // Public Interface | ||
| 97 | |||
| 54 | void Init() { | 98 | void Init() { |
| 55 | DSP::HLE::ResetPipes(); | 99 | DSP::HLE::ResetPipes(); |
| 56 | 100 | ||
| @@ -58,6 +102,8 @@ void Init() { | |||
| 58 | source.Reset(); | 102 | source.Reset(); |
| 59 | } | 103 | } |
| 60 | 104 | ||
| 105 | mixers.Reset(); | ||
| 106 | |||
| 61 | time_stretcher.Reset(); | 107 | time_stretcher.Reset(); |
| 62 | if (sink) { | 108 | if (sink) { |
| 63 | time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate()); | 109 | time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate()); |
| @@ -75,17 +121,12 @@ void Shutdown() { | |||
| 75 | } | 121 | } |
| 76 | 122 | ||
| 77 | bool Tick() { | 123 | bool Tick() { |
| 78 | SharedMemory& read = ReadRegion(); | 124 | StereoFrame16 current_frame = {}; |
| 79 | SharedMemory& write = WriteRegion(); | ||
| 80 | 125 | ||
| 81 | std::array<QuadFrame32, 3> intermediate_mixes = {}; | 126 | // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region) |
| 127 | current_frame = GenerateCurrentFrame(); | ||
| 82 | 128 | ||
| 83 | for (size_t i = 0; i < num_sources; i++) { | 129 | OutputCurrentFrame(current_frame); |
| 84 | write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); | ||
| 85 | for (size_t mix = 0; mix < 3; mix++) { | ||
| 86 | sources[i].MixInto(intermediate_mixes[mix], mix); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | 130 | ||
| 90 | return true; | 131 | return true; |
| 91 | } | 132 | } |
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index f6e53f68f..9275cd7de 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h | |||
| @@ -428,7 +428,7 @@ ASSERT_DSP_STRUCT(DspStatus, 32); | |||
| 428 | /// Final mixed output in PCM16 stereo format, what you hear out of the speakers. | 428 | /// Final mixed output in PCM16 stereo format, what you hear out of the speakers. |
| 429 | /// When the application writes to this region it has no effect. | 429 | /// When the application writes to this region it has no effect. |
| 430 | struct FinalMixSamples { | 430 | struct FinalMixSamples { |
| 431 | s16_le pcm16[2 * samples_per_frame]; | 431 | s16_le pcm16[samples_per_frame][2]; |
| 432 | }; | 432 | }; |
| 433 | ASSERT_DSP_STRUCT(FinalMixSamples, 640); | 433 | ASSERT_DSP_STRUCT(FinalMixSamples, 640); |
| 434 | 434 | ||
diff --git a/src/audio_core/hle/mixers.cpp b/src/audio_core/hle/mixers.cpp new file mode 100644 index 000000000..18335f7f0 --- /dev/null +++ b/src/audio_core/hle/mixers.cpp | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstddef> | ||
| 6 | |||
| 7 | #include "audio_core/hle/common.h" | ||
| 8 | #include "audio_core/hle/dsp.h" | ||
| 9 | #include "audio_core/hle/mixers.h" | ||
| 10 | |||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/math_util.h" | ||
| 14 | |||
| 15 | namespace DSP { | ||
| 16 | namespace HLE { | ||
| 17 | |||
| 18 | void Mixers::Reset() { | ||
| 19 | current_frame.fill({}); | ||
| 20 | state = {}; | ||
| 21 | } | ||
| 22 | |||
| 23 | DspStatus Mixers::Tick(DspConfiguration& config, | ||
| 24 | const IntermediateMixSamples& read_samples, | ||
| 25 | IntermediateMixSamples& write_samples, | ||
| 26 | const std::array<QuadFrame32, 3>& input) | ||
| 27 | { | ||
| 28 | ParseConfig(config); | ||
| 29 | |||
| 30 | AuxReturn(read_samples); | ||
| 31 | AuxSend(write_samples, input); | ||
| 32 | |||
| 33 | MixCurrentFrame(); | ||
| 34 | |||
| 35 | return GetCurrentStatus(); | ||
| 36 | } | ||
| 37 | |||
| 38 | void Mixers::ParseConfig(DspConfiguration& config) { | ||
| 39 | if (!config.dirty_raw) { | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | |||
| 43 | if (config.mixer1_enabled_dirty) { | ||
| 44 | config.mixer1_enabled_dirty.Assign(0); | ||
| 45 | state.mixer1_enabled = config.mixer1_enabled != 0; | ||
| 46 | LOG_TRACE(Audio_DSP, "mixers mixer1_enabled = %hu", config.mixer1_enabled); | ||
| 47 | } | ||
| 48 | |||
| 49 | if (config.mixer2_enabled_dirty) { | ||
| 50 | config.mixer2_enabled_dirty.Assign(0); | ||
| 51 | state.mixer2_enabled = config.mixer2_enabled != 0; | ||
| 52 | LOG_TRACE(Audio_DSP, "mixers mixer2_enabled = %hu", config.mixer2_enabled); | ||
| 53 | } | ||
| 54 | |||
| 55 | if (config.volume_0_dirty) { | ||
| 56 | config.volume_0_dirty.Assign(0); | ||
| 57 | state.intermediate_mixer_volume[0] = config.volume[0]; | ||
| 58 | LOG_TRACE(Audio_DSP, "mixers volume[0] = %f", config.volume[0]); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (config.volume_1_dirty) { | ||
| 62 | config.volume_1_dirty.Assign(0); | ||
| 63 | state.intermediate_mixer_volume[1] = config.volume[1]; | ||
| 64 | LOG_TRACE(Audio_DSP, "mixers volume[1] = %f", config.volume[1]); | ||
| 65 | } | ||
| 66 | |||
| 67 | if (config.volume_2_dirty) { | ||
| 68 | config.volume_2_dirty.Assign(0); | ||
| 69 | state.intermediate_mixer_volume[2] = config.volume[2]; | ||
| 70 | LOG_TRACE(Audio_DSP, "mixers volume[2] = %f", config.volume[2]); | ||
| 71 | } | ||
| 72 | |||
| 73 | if (config.output_format_dirty) { | ||
| 74 | config.output_format_dirty.Assign(0); | ||
| 75 | state.output_format = config.output_format; | ||
| 76 | LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format)); | ||
| 77 | } | ||
| 78 | |||
| 79 | if (config.headphones_connected_dirty) { | ||
| 80 | config.headphones_connected_dirty.Assign(0); | ||
| 81 | // Do nothing. | ||
| 82 | // (Note: Whether headphones are connected does affect coefficients used for surround sound.) | ||
| 83 | LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected); | ||
| 84 | } | ||
| 85 | |||
| 86 | if (config.dirty_raw) { | ||
| 87 | LOG_DEBUG(Audio_DSP, "mixers remaining_dirty=%x", config.dirty_raw); | ||
| 88 | } | ||
| 89 | |||
| 90 | config.dirty_raw = 0; | ||
| 91 | } | ||
| 92 | |||
| 93 | static s16 ClampToS16(s32 value) { | ||
| 94 | return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767)); | ||
| 95 | } | ||
| 96 | |||
| 97 | static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) { | ||
| 98 | return { | ||
| 99 | ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), | ||
| 100 | ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1])) | ||
| 101 | }; | ||
| 102 | } | ||
| 103 | |||
| 104 | void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { | ||
| 105 | // TODO(merry): Limiter. (Currently we're performing final mixing assuming a disabled limiter.) | ||
| 106 | |||
| 107 | switch (state.output_format) { | ||
| 108 | case OutputFormat::Mono: | ||
| 109 | std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), | ||
| 110 | [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { | ||
| 111 | // Downmix to mono | ||
| 112 | s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2)); | ||
| 113 | // Mix into current frame | ||
| 114 | return AddAndClampToS16(accumulator, { mono, mono }); | ||
| 115 | }); | ||
| 116 | return; | ||
| 117 | |||
| 118 | case OutputFormat::Surround: | ||
| 119 | // TODO(merry): Implement surround sound. | ||
| 120 | // fallthrough | ||
| 121 | |||
| 122 | case OutputFormat::Stereo: | ||
| 123 | std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), | ||
| 124 | [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { | ||
| 125 | // Downmix to stereo | ||
| 126 | s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2])); | ||
| 127 | s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3])); | ||
| 128 | // Mix into current frame | ||
| 129 | return AddAndClampToS16(accumulator, { left, right }); | ||
| 130 | }); | ||
| 131 | return; | ||
| 132 | } | ||
| 133 | |||
| 134 | UNREACHABLE_MSG("Invalid output_format %zu", static_cast<size_t>(state.output_format)); | ||
| 135 | } | ||
| 136 | |||
| 137 | void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { | ||
| 138 | // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. | ||
| 139 | |||
| 140 | if (state.mixer1_enabled) { | ||
| 141 | for (size_t sample = 0; sample < samples_per_frame; sample++) { | ||
| 142 | for (size_t channel = 0; channel < 4; channel++) { | ||
| 143 | state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample]; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | if (state.mixer2_enabled) { | ||
| 149 | for (size_t sample = 0; sample < samples_per_frame; sample++) { | ||
| 150 | for (size_t channel = 0; channel < 4; channel++) { | ||
| 151 | state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample]; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) { | ||
| 158 | // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. | ||
| 159 | |||
| 160 | state.intermediate_mix_buffer[0] = input[0]; | ||
| 161 | |||
| 162 | if (state.mixer1_enabled) { | ||
| 163 | for (size_t sample = 0; sample < samples_per_frame; sample++) { | ||
| 164 | for (size_t channel = 0; channel < 4; channel++) { | ||
| 165 | write_samples.mix1.pcm32[channel][sample] = input[1][sample][channel]; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } else { | ||
| 169 | state.intermediate_mix_buffer[1] = input[1]; | ||
| 170 | } | ||
| 171 | |||
| 172 | if (state.mixer2_enabled) { | ||
| 173 | for (size_t sample = 0; sample < samples_per_frame; sample++) { | ||
| 174 | for (size_t channel = 0; channel < 4; channel++) { | ||
| 175 | write_samples.mix2.pcm32[channel][sample] = input[2][sample][channel]; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } else { | ||
| 179 | state.intermediate_mix_buffer[2] = input[2]; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | void Mixers::MixCurrentFrame() { | ||
| 184 | current_frame.fill({}); | ||
| 185 | |||
| 186 | for (size_t mix = 0; mix < 3; mix++) { | ||
| 187 | DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]); | ||
| 188 | } | ||
| 189 | |||
| 190 | // TODO(merry): Compressor. (We currently assume a disabled compressor.) | ||
| 191 | } | ||
| 192 | |||
| 193 | DspStatus Mixers::GetCurrentStatus() const { | ||
| 194 | DspStatus status; | ||
| 195 | status.unknown = 0; | ||
| 196 | status.dropped_frames = 0; | ||
| 197 | return status; | ||
| 198 | } | ||
| 199 | |||
| 200 | } // namespace HLE | ||
| 201 | } // namespace DSP | ||
diff --git a/src/audio_core/hle/mixers.h b/src/audio_core/hle/mixers.h new file mode 100644 index 000000000..b52952eb5 --- /dev/null +++ b/src/audio_core/hle/mixers.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | |||
| 9 | #include "audio_core/hle/common.h" | ||
| 10 | #include "audio_core/hle/dsp.h" | ||
| 11 | |||
| 12 | namespace DSP { | ||
| 13 | namespace HLE { | ||
| 14 | |||
| 15 | class Mixers final { | ||
| 16 | public: | ||
| 17 | Mixers() { | ||
| 18 | Reset(); | ||
| 19 | } | ||
| 20 | |||
| 21 | void Reset(); | ||
| 22 | |||
| 23 | DspStatus Tick(DspConfiguration& config, | ||
| 24 | const IntermediateMixSamples& read_samples, | ||
| 25 | IntermediateMixSamples& write_samples, | ||
| 26 | const std::array<QuadFrame32, 3>& input); | ||
| 27 | |||
| 28 | StereoFrame16 GetOutput() const { | ||
| 29 | return current_frame; | ||
| 30 | } | ||
| 31 | |||
| 32 | private: | ||
| 33 | StereoFrame16 current_frame = {}; | ||
| 34 | |||
| 35 | using OutputFormat = DspConfiguration::OutputFormat; | ||
| 36 | |||
| 37 | struct { | ||
| 38 | std::array<float, 3> intermediate_mixer_volume = {}; | ||
| 39 | |||
| 40 | bool mixer1_enabled = false; | ||
| 41 | bool mixer2_enabled = false; | ||
| 42 | std::array<QuadFrame32, 3> intermediate_mix_buffer = {}; | ||
| 43 | |||
| 44 | OutputFormat output_format = OutputFormat::Stereo; | ||
| 45 | |||
| 46 | } state; | ||
| 47 | |||
| 48 | /// INTERNAL: Update our internal state based on the current config. | ||
| 49 | void ParseConfig(DspConfiguration& config); | ||
| 50 | /// INTERNAL: Read samples from shared memory that have been modified by the ARM11. | ||
| 51 | void AuxReturn(const IntermediateMixSamples& read_samples); | ||
| 52 | /// INTERNAL: Write samples to shared memory for the ARM11 to modify. | ||
| 53 | void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); | ||
| 54 | /// INTERNAL: Mix current_frame. | ||
| 55 | void MixCurrentFrame(); | ||
| 56 | /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame. | ||
| 57 | void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples); | ||
| 58 | /// INTERNAL: Generate DspStatus based on internal state. | ||
| 59 | DspStatus GetCurrentStatus() const; | ||
| 60 | }; | ||
| 61 | |||
| 62 | } // namespace HLE | ||
| 63 | } // namespace DSP | ||
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 3f0099200..0a5d4624b 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -20,6 +20,7 @@ set(SRCS | |||
| 20 | util/spinbox.cpp | 20 | util/spinbox.cpp |
| 21 | util/util.cpp | 21 | util/util.cpp |
| 22 | bootmanager.cpp | 22 | bootmanager.cpp |
| 23 | configure_audio.cpp | ||
| 23 | configure_debug.cpp | 24 | configure_debug.cpp |
| 24 | configure_dialog.cpp | 25 | configure_dialog.cpp |
| 25 | configure_general.cpp | 26 | configure_general.cpp |
| @@ -51,6 +52,7 @@ set(HEADERS | |||
| 51 | util/spinbox.h | 52 | util/spinbox.h |
| 52 | util/util.h | 53 | util/util.h |
| 53 | bootmanager.h | 54 | bootmanager.h |
| 55 | configure_audio.h | ||
| 54 | configure_debug.h | 56 | configure_debug.h |
| 55 | configure_dialog.h | 57 | configure_dialog.h |
| 56 | configure_general.h | 58 | configure_general.h |
| @@ -69,6 +71,7 @@ set(UIS | |||
| 69 | debugger/profiler.ui | 71 | debugger/profiler.ui |
| 70 | debugger/registers.ui | 72 | debugger/registers.ui |
| 71 | configure.ui | 73 | configure.ui |
| 74 | configure_audio.ui | ||
| 72 | configure_debug.ui | 75 | configure_debug.ui |
| 73 | configure_general.ui | 76 | configure_general.ui |
| 74 | hotkeys.ui | 77 | hotkeys.ui |
diff --git a/src/citra_qt/configure.ui b/src/citra_qt/configure.ui index 6ae056ff9..e1624bbef 100644 --- a/src/citra_qt/configure.ui +++ b/src/citra_qt/configure.ui | |||
| @@ -29,6 +29,11 @@ | |||
| 29 | <string>Input</string> | 29 | <string>Input</string> |
| 30 | </attribute> | 30 | </attribute> |
| 31 | </widget> | 31 | </widget> |
| 32 | <widget class="ConfigureAudio" name="audioTab"> | ||
| 33 | <attribute name="title"> | ||
| 34 | <string>Audio</string> | ||
| 35 | </attribute> | ||
| 36 | </widget> | ||
| 32 | <widget class="ConfigureDebug" name="debugTab"> | 37 | <widget class="ConfigureDebug" name="debugTab"> |
| 33 | <attribute name="title"> | 38 | <attribute name="title"> |
| 34 | <string>Debug</string> | 39 | <string>Debug</string> |
| @@ -53,6 +58,12 @@ | |||
| 53 | <container>1</container> | 58 | <container>1</container> |
| 54 | </customwidget> | 59 | </customwidget> |
| 55 | <customwidget> | 60 | <customwidget> |
| 61 | <class>ConfigureAudio</class> | ||
| 62 | <extends>QWidget</extends> | ||
| 63 | <header>configure_audio.h</header> | ||
| 64 | <container>1</container> | ||
| 65 | </customwidget> | ||
| 66 | <customwidget> | ||
| 56 | <class>ConfigureDebug</class> | 67 | <class>ConfigureDebug</class> |
| 57 | <extends>QWidget</extends> | 68 | <extends>QWidget</extends> |
| 58 | <header>configure_debug.h</header> | 69 | <header>configure_debug.h</header> |
diff --git a/src/citra_qt/configure_audio.cpp b/src/citra_qt/configure_audio.cpp new file mode 100644 index 000000000..cedfa2f2a --- /dev/null +++ b/src/citra_qt/configure_audio.cpp | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "audio_core/sink_details.h" | ||
| 6 | |||
| 7 | #include "citra_qt/configure_audio.h" | ||
| 8 | #include "ui_configure_audio.h" | ||
| 9 | |||
| 10 | #include "core/settings.h" | ||
| 11 | |||
| 12 | ConfigureAudio::ConfigureAudio(QWidget* parent) : | ||
| 13 | QWidget(parent), | ||
| 14 | ui(std::make_unique<Ui::ConfigureAudio>()) | ||
| 15 | { | ||
| 16 | ui->setupUi(this); | ||
| 17 | |||
| 18 | ui->output_sink_combo_box->clear(); | ||
| 19 | ui->output_sink_combo_box->addItem("auto"); | ||
| 20 | for (const auto& sink_detail : AudioCore::g_sink_details) { | ||
| 21 | ui->output_sink_combo_box->addItem(sink_detail.id); | ||
| 22 | } | ||
| 23 | |||
| 24 | this->setConfiguration(); | ||
| 25 | } | ||
| 26 | |||
| 27 | ConfigureAudio::~ConfigureAudio() { | ||
| 28 | } | ||
| 29 | |||
| 30 | void ConfigureAudio::setConfiguration() { | ||
| 31 | int new_sink_index = 0; | ||
| 32 | for (int index = 0; index < ui->output_sink_combo_box->count(); index++) { | ||
| 33 | if (ui->output_sink_combo_box->itemText(index).toStdString() == Settings::values.sink_id) { | ||
| 34 | new_sink_index = index; | ||
| 35 | break; | ||
| 36 | } | ||
| 37 | } | ||
| 38 | ui->output_sink_combo_box->setCurrentIndex(new_sink_index); | ||
| 39 | } | ||
| 40 | |||
| 41 | void ConfigureAudio::applyConfiguration() { | ||
| 42 | Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString(); | ||
| 43 | Settings::Apply(); | ||
| 44 | } | ||
diff --git a/src/citra_qt/configure_audio.h b/src/citra_qt/configure_audio.h new file mode 100644 index 000000000..51df2e27b --- /dev/null +++ b/src/citra_qt/configure_audio.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <QWidget> | ||
| 9 | |||
| 10 | namespace Ui { | ||
| 11 | class ConfigureAudio; | ||
| 12 | } | ||
| 13 | |||
| 14 | class ConfigureAudio : public QWidget { | ||
| 15 | Q_OBJECT | ||
| 16 | |||
| 17 | public: | ||
| 18 | explicit ConfigureAudio(QWidget* parent = nullptr); | ||
| 19 | ~ConfigureAudio(); | ||
| 20 | |||
| 21 | void applyConfiguration(); | ||
| 22 | |||
| 23 | private: | ||
| 24 | void setConfiguration(); | ||
| 25 | |||
| 26 | std::unique_ptr<Ui::ConfigureAudio> ui; | ||
| 27 | }; | ||
diff --git a/src/citra_qt/configure_audio.ui b/src/citra_qt/configure_audio.ui new file mode 100644 index 000000000..d7f6946ca --- /dev/null +++ b/src/citra_qt/configure_audio.ui | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | |||
| 3 | <ui version="4.0"> | ||
| 4 | <class>ConfigureAudio</class> | ||
| 5 | <widget class="QWidget" name="ConfigureAudio"> | ||
| 6 | <layout class="QVBoxLayout"> | ||
| 7 | <item> | ||
| 8 | <widget class="QGroupBox"> | ||
| 9 | <property name="title"> | ||
| 10 | <string>Audio</string> | ||
| 11 | </property> | ||
| 12 | <layout class="QVBoxLayout"> | ||
| 13 | <item> | ||
| 14 | <layout class="QHBoxLayout"> | ||
| 15 | <item> | ||
| 16 | <widget class="QLabel"> | ||
| 17 | <property name="text"> | ||
| 18 | <string>Output Engine:</string> | ||
| 19 | </property> | ||
| 20 | </widget> | ||
| 21 | </item> | ||
| 22 | <item> | ||
| 23 | <widget class="QComboBox" name="output_sink_combo_box"> | ||
| 24 | </widget> | ||
| 25 | </item> | ||
| 26 | </layout> | ||
| 27 | </item> | ||
| 28 | </layout> | ||
| 29 | </widget> | ||
| 30 | </item> | ||
| 31 | <item> | ||
| 32 | <spacer> | ||
| 33 | <property name="orientation"> | ||
| 34 | <enum>Qt::Vertical</enum> | ||
| 35 | </property> | ||
| 36 | <property name="sizeHint" stdset="0"> | ||
| 37 | <size> | ||
| 38 | <width>20</width> | ||
| 39 | <height>40</height> | ||
| 40 | </size> | ||
| 41 | </property> | ||
| 42 | </spacer> | ||
| 43 | </item> | ||
| 44 | </layout> | ||
| 45 | </widget> | ||
| 46 | <resources /> | ||
| 47 | <connections /> | ||
| 48 | </ui> | ||
diff --git a/src/citra_qt/configure_dialog.cpp b/src/citra_qt/configure_dialog.cpp index 87c26c715..2f0317fe0 100644 --- a/src/citra_qt/configure_dialog.cpp +++ b/src/citra_qt/configure_dialog.cpp | |||
| @@ -25,5 +25,6 @@ void ConfigureDialog::setConfiguration() { | |||
| 25 | 25 | ||
| 26 | void ConfigureDialog::applyConfiguration() { | 26 | void ConfigureDialog::applyConfiguration() { |
| 27 | ui->generalTab->applyConfiguration(); | 27 | ui->generalTab->applyConfiguration(); |
| 28 | ui->audioTab->applyConfiguration(); | ||
| 28 | ui->debugTab->applyConfiguration(); | 29 | ui->debugTab->applyConfiguration(); |
| 29 | } | 30 | } |
diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 7bb010f77..585ac049a 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp | |||
| @@ -151,6 +151,8 @@ private: | |||
| 151 | /// This timer is used to redraw the widget's contents continuously. To save resources, it only | 151 | /// This timer is used to redraw the widget's contents continuously. To save resources, it only |
| 152 | /// runs while the widget is visible. | 152 | /// runs while the widget is visible. |
| 153 | QTimer update_timer; | 153 | QTimer update_timer; |
| 154 | /// Scale the coordinate system appropriately when physical DPI != logical DPI. | ||
| 155 | qreal x_scale, y_scale; | ||
| 154 | }; | 156 | }; |
| 155 | 157 | ||
| 156 | #endif | 158 | #endif |
| @@ -220,11 +222,17 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) { | |||
| 220 | MicroProfileInitUI(); | 222 | MicroProfileInitUI(); |
| 221 | 223 | ||
| 222 | connect(&update_timer, SIGNAL(timeout()), SLOT(update())); | 224 | connect(&update_timer, SIGNAL(timeout()), SLOT(update())); |
| 225 | |||
| 226 | QPainter painter(this); | ||
| 227 | x_scale = qreal(painter.device()->physicalDpiX()) / qreal(painter.device()->logicalDpiX()); | ||
| 228 | y_scale = qreal(painter.device()->physicalDpiY()) / qreal(painter.device()->logicalDpiY()); | ||
| 223 | } | 229 | } |
| 224 | 230 | ||
| 225 | void MicroProfileWidget::paintEvent(QPaintEvent* ev) { | 231 | void MicroProfileWidget::paintEvent(QPaintEvent* ev) { |
| 226 | QPainter painter(this); | 232 | QPainter painter(this); |
| 227 | 233 | ||
| 234 | painter.scale(x_scale, y_scale); | ||
| 235 | |||
| 228 | painter.setBackground(Qt::black); | 236 | painter.setBackground(Qt::black); |
| 229 | painter.eraseRect(rect()); | 237 | painter.eraseRect(rect()); |
| 230 | 238 | ||
| @@ -248,24 +256,24 @@ void MicroProfileWidget::hideEvent(QHideEvent* ev) { | |||
| 248 | } | 256 | } |
| 249 | 257 | ||
| 250 | void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) { | 258 | void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) { |
| 251 | MicroProfileMousePosition(ev->x(), ev->y(), 0); | 259 | MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); |
| 252 | ev->accept(); | 260 | ev->accept(); |
| 253 | } | 261 | } |
| 254 | 262 | ||
| 255 | void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) { | 263 | void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) { |
| 256 | MicroProfileMousePosition(ev->x(), ev->y(), 0); | 264 | MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); |
| 257 | MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); | 265 | MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); |
| 258 | ev->accept(); | 266 | ev->accept(); |
| 259 | } | 267 | } |
| 260 | 268 | ||
| 261 | void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { | 269 | void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { |
| 262 | MicroProfileMousePosition(ev->x(), ev->y(), 0); | 270 | MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0); |
| 263 | MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); | 271 | MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton); |
| 264 | ev->accept(); | 272 | ev->accept(); |
| 265 | } | 273 | } |
| 266 | 274 | ||
| 267 | void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { | 275 | void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { |
| 268 | MicroProfileMousePosition(ev->x(), ev->y(), ev->delta() / 120); | 276 | MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, ev->delta() / 120); |
| 269 | ev->accept(); | 277 | ev->accept(); |
| 270 | } | 278 | } |
| 271 | 279 | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f6a7566bf..12080a802 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -42,6 +42,7 @@ set(SRCS | |||
| 42 | hle/kernel/timer.cpp | 42 | hle/kernel/timer.cpp |
| 43 | hle/kernel/vm_manager.cpp | 43 | hle/kernel/vm_manager.cpp |
| 44 | hle/service/ac_u.cpp | 44 | hle/service/ac_u.cpp |
| 45 | hle/service/act_a.cpp | ||
| 45 | hle/service/act_u.cpp | 46 | hle/service/act_u.cpp |
| 46 | hle/service/am/am.cpp | 47 | hle/service/am/am.cpp |
| 47 | hle/service/am/am_app.cpp | 48 | hle/service/am/am_app.cpp |
| @@ -176,6 +177,7 @@ set(HEADERS | |||
| 176 | hle/kernel/vm_manager.h | 177 | hle/kernel/vm_manager.h |
| 177 | hle/result.h | 178 | hle/result.h |
| 178 | hle/service/ac_u.h | 179 | hle/service/ac_u.h |
| 180 | hle/service/act_a.h | ||
| 179 | hle/service/act_u.h | 181 | hle/service/act_u.h |
| 180 | hle/service/am/am.h | 182 | hle/service/am/am.h |
| 181 | hle/service/am/am_app.h | 183 | hle/service/am/am_app.h |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 533067d4f..d8abe5aeb 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/arm/skyeye_common/arm_regformat.h" | 8 | #include "core/arm/skyeye_common/arm_regformat.h" |
| 9 | #include "core/arm/skyeye_common/vfp/asm_vfp.h" | ||
| 9 | 10 | ||
| 10 | namespace Core { | 11 | namespace Core { |
| 11 | struct ThreadContext; | 12 | struct ThreadContext; |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 8d4b26815..cfc67287f 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -5527,28 +5527,32 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | |||
| 5527 | 5527 | ||
| 5528 | // SMUAD and SMLAD | 5528 | // SMUAD and SMLAD |
| 5529 | if (BIT(op2, 1) == 0) { | 5529 | if (BIT(op2, 1) == 0) { |
| 5530 | RD = (product1 + product2); | 5530 | u32 rd_val = (product1 + product2); |
| 5531 | 5531 | ||
| 5532 | if (inst_cream->Ra != 15) { | 5532 | if (inst_cream->Ra != 15) { |
| 5533 | RD += cpu->Reg[inst_cream->Ra]; | 5533 | rd_val += cpu->Reg[inst_cream->Ra]; |
| 5534 | 5534 | ||
| 5535 | if (ARMul_AddOverflowQ(product1 + product2, cpu->Reg[inst_cream->Ra])) | 5535 | if (ARMul_AddOverflowQ(product1 + product2, cpu->Reg[inst_cream->Ra])) |
| 5536 | cpu->Cpsr |= (1 << 27); | 5536 | cpu->Cpsr |= (1 << 27); |
| 5537 | } | 5537 | } |
| 5538 | 5538 | ||
| 5539 | RD = rd_val; | ||
| 5540 | |||
| 5539 | if (ARMul_AddOverflowQ(product1, product2)) | 5541 | if (ARMul_AddOverflowQ(product1, product2)) |
| 5540 | cpu->Cpsr |= (1 << 27); | 5542 | cpu->Cpsr |= (1 << 27); |
| 5541 | } | 5543 | } |
| 5542 | // SMUSD and SMLSD | 5544 | // SMUSD and SMLSD |
| 5543 | else { | 5545 | else { |
| 5544 | RD = (product1 - product2); | 5546 | u32 rd_val = (product1 - product2); |
| 5545 | 5547 | ||
| 5546 | if (inst_cream->Ra != 15) { | 5548 | if (inst_cream->Ra != 15) { |
| 5547 | RD += cpu->Reg[inst_cream->Ra]; | 5549 | rd_val += cpu->Reg[inst_cream->Ra]; |
| 5548 | 5550 | ||
| 5549 | if (ARMul_AddOverflowQ(product1 - product2, cpu->Reg[inst_cream->Ra])) | 5551 | if (ARMul_AddOverflowQ(product1 - product2, cpu->Reg[inst_cream->Ra])) |
| 5550 | cpu->Cpsr |= (1 << 27); | 5552 | cpu->Cpsr |= (1 << 27); |
| 5551 | } | 5553 | } |
| 5554 | |||
| 5555 | RD = rd_val; | ||
| 5552 | } | 5556 | } |
| 5553 | } | 5557 | } |
| 5554 | 5558 | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 68f026918..43def6146 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -526,6 +526,8 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { | |||
| 526 | 526 | ||
| 527 | SharedPtr<Thread> thread = thread_res.MoveFrom(); | 527 | SharedPtr<Thread> thread = thread_res.MoveFrom(); |
| 528 | 528 | ||
| 529 | thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 | ||
| 530 | |||
| 529 | // Run new "main" thread | 531 | // Run new "main" thread |
| 530 | SwitchContext(thread.get()); | 532 | SwitchContext(thread.get()); |
| 531 | 533 | ||
diff --git a/src/core/hle/service/act_a.cpp b/src/core/hle/service/act_a.cpp new file mode 100644 index 000000000..3a775fa90 --- /dev/null +++ b/src/core/hle/service/act_a.cpp | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/act_a.h" | ||
| 6 | |||
| 7 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 8 | // Namespace ACT_A | ||
| 9 | |||
| 10 | namespace ACT_A { | ||
| 11 | |||
| 12 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 13 | {0x041300C2, nullptr, "UpdateMiiImage"}, | ||
| 14 | {0x041B0142, nullptr, "AgreeEula"}, | ||
| 15 | {0x04210042, nullptr, "UploadMii"}, | ||
| 16 | {0x04230082, nullptr, "ValidateMailAddress"}, | ||
| 17 | }; | ||
| 18 | |||
| 19 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 20 | // Interface class | ||
| 21 | |||
| 22 | Interface::Interface() { | ||
| 23 | Register(FunctionTable); | ||
| 24 | } | ||
| 25 | |||
| 26 | } // namespace | ||
diff --git a/src/core/hle/service/act_a.h b/src/core/hle/service/act_a.h new file mode 100644 index 000000000..765cae644 --- /dev/null +++ b/src/core/hle/service/act_a.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace ACT_A | ||
| 11 | |||
| 12 | namespace ACT_A { | ||
| 13 | |||
| 14 | class Interface : public Service::Interface { | ||
| 15 | public: | ||
| 16 | Interface(); | ||
| 17 | |||
| 18 | std::string GetPortName() const override { | ||
| 19 | return "act:a"; | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace | ||
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp index b23d17fba..05de4d002 100644 --- a/src/core/hle/service/act_u.cpp +++ b/src/core/hle/service/act_u.cpp | |||
| @@ -10,7 +10,10 @@ | |||
| 10 | namespace ACT_U { | 10 | namespace ACT_U { |
| 11 | 11 | ||
| 12 | const Interface::FunctionInfo FunctionTable[] = { | 12 | const Interface::FunctionInfo FunctionTable[] = { |
| 13 | {0x00010084, nullptr, "Initialize"}, | ||
| 14 | {0x00020040, nullptr, "GetErrorCode"}, | ||
| 13 | {0x000600C2, nullptr, "GetAccountDataBlock"}, | 15 | {0x000600C2, nullptr, "GetAccountDataBlock"}, |
| 16 | {0x000D0040, nullptr, "GenerateUuid"}, | ||
| 14 | }; | 17 | }; |
| 15 | 18 | ||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 19 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 274fc751a..10730d7ac 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -440,9 +440,9 @@ static void GetHeadphoneStatus(Service::Interface* self) { | |||
| 440 | 440 | ||
| 441 | cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0); | 441 | cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0); |
| 442 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 442 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 443 | cmd_buff[2] = 0; // Not using headphones? | 443 | cmd_buff[2] = 0; // Not using headphones |
| 444 | 444 | ||
| 445 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | 445 | LOG_DEBUG(Service_DSP, "called"); |
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | /** | 448 | /** |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 0fe3a4d7a..d7e7d4fe3 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 9 | #include "core/hle/service/ac_u.h" | 9 | #include "core/hle/service/ac_u.h" |
| 10 | #include "core/hle/service/act_a.h" | ||
| 10 | #include "core/hle/service/act_u.h" | 11 | #include "core/hle/service/act_u.h" |
| 11 | #include "core/hle/service/csnd_snd.h" | 12 | #include "core/hle/service/csnd_snd.h" |
| 12 | #include "core/hle/service/dlp_srvr.h" | 13 | #include "core/hle/service/dlp_srvr.h" |
| @@ -119,6 +120,7 @@ void Init() { | |||
| 119 | Service::PTM::Init(); | 120 | Service::PTM::Init(); |
| 120 | 121 | ||
| 121 | AddService(new AC_U::Interface); | 122 | AddService(new AC_U::Interface); |
| 123 | AddService(new ACT_A::Interface); | ||
| 122 | AddService(new ACT_U::Interface); | 124 | AddService(new ACT_U::Interface); |
| 123 | AddService(new CSND_SND::Interface); | 125 | AddService(new CSND_SND::Interface); |
| 124 | AddService(new DLP_SRVR::Interface); | 126 | AddService(new DLP_SRVR::Interface); |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 3a53126c1..2bf122a6d 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -502,6 +502,9 @@ static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point | |||
| 502 | 502 | ||
| 503 | CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( | 503 | CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( |
| 504 | name, entry_point, priority, arg, processor_id, stack_top)); | 504 | name, entry_point, priority, arg, processor_id, stack_top)); |
| 505 | |||
| 506 | thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 | ||
| 507 | |||
| 505 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); | 508 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); |
| 506 | 509 | ||
| 507 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 510 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 2f645b441..871368323 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -696,106 +696,125 @@ finalise: | |||
| 696 | #endif | 696 | #endif |
| 697 | } | 697 | } |
| 698 | 698 | ||
| 699 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | 699 | static std::string ReplacePattern(const std::string& input, const std::string& pattern, const std::string& replacement) { |
| 700 | { | 700 | size_t start = input.find(pattern); |
| 701 | if (start == std::string::npos) | ||
| 702 | return input; | ||
| 703 | |||
| 704 | std::string ret = input; | ||
| 705 | ret.replace(start, pattern.length(), replacement); | ||
| 706 | return ret; | ||
| 707 | } | ||
| 708 | |||
| 709 | static std::string GetTevStageConfigSourceString(const Pica::Regs::TevStageConfig::Source& source) { | ||
| 701 | using Source = Pica::Regs::TevStageConfig::Source; | 710 | using Source = Pica::Regs::TevStageConfig::Source; |
| 711 | static const std::map<Source, std::string> source_map = { | ||
| 712 | { Source::PrimaryColor, "PrimaryColor" }, | ||
| 713 | { Source::PrimaryFragmentColor, "PrimaryFragmentColor" }, | ||
| 714 | { Source::SecondaryFragmentColor, "SecondaryFragmentColor" }, | ||
| 715 | { Source::Texture0, "Texture0" }, | ||
| 716 | { Source::Texture1, "Texture1" }, | ||
| 717 | { Source::Texture2, "Texture2" }, | ||
| 718 | { Source::Texture3, "Texture3" }, | ||
| 719 | { Source::PreviousBuffer, "PreviousBuffer" }, | ||
| 720 | { Source::Constant, "Constant" }, | ||
| 721 | { Source::Previous, "Previous" }, | ||
| 722 | }; | ||
| 723 | |||
| 724 | const auto src_it = source_map.find(source); | ||
| 725 | if (src_it == source_map.end()) | ||
| 726 | return "Unknown"; | ||
| 727 | |||
| 728 | return src_it->second; | ||
| 729 | } | ||
| 730 | |||
| 731 | static std::string GetTevStageConfigColorSourceString(const Pica::Regs::TevStageConfig::Source& source, const Pica::Regs::TevStageConfig::ColorModifier modifier) { | ||
| 702 | using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; | 732 | using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; |
| 733 | static const std::map<ColorModifier, std::string> color_modifier_map = { | ||
| 734 | { ColorModifier::SourceColor, "%source.rgb" }, | ||
| 735 | { ColorModifier::OneMinusSourceColor, "(1.0 - %source.rgb)" }, | ||
| 736 | { ColorModifier::SourceAlpha, "%source.aaa" }, | ||
| 737 | { ColorModifier::OneMinusSourceAlpha, "(1.0 - %source.aaa)" }, | ||
| 738 | { ColorModifier::SourceRed, "%source.rrr" }, | ||
| 739 | { ColorModifier::OneMinusSourceRed, "(1.0 - %source.rrr)" }, | ||
| 740 | { ColorModifier::SourceGreen, "%source.ggg" }, | ||
| 741 | { ColorModifier::OneMinusSourceGreen, "(1.0 - %source.ggg)" }, | ||
| 742 | { ColorModifier::SourceBlue, "%source.bbb" }, | ||
| 743 | { ColorModifier::OneMinusSourceBlue, "(1.0 - %source.bbb)" }, | ||
| 744 | }; | ||
| 745 | |||
| 746 | auto src_str = GetTevStageConfigSourceString(source); | ||
| 747 | auto modifier_it = color_modifier_map.find(modifier); | ||
| 748 | std::string modifier_str = "%source.????"; | ||
| 749 | if (modifier_it != color_modifier_map.end()) | ||
| 750 | modifier_str = modifier_it->second; | ||
| 751 | |||
| 752 | return ReplacePattern(modifier_str, "%source", src_str); | ||
| 753 | } | ||
| 754 | |||
| 755 | static std::string GetTevStageConfigAlphaSourceString(const Pica::Regs::TevStageConfig::Source& source, const Pica::Regs::TevStageConfig::AlphaModifier modifier) { | ||
| 703 | using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; | 756 | using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; |
| 757 | static const std::map<AlphaModifier, std::string> alpha_modifier_map = { | ||
| 758 | { AlphaModifier::SourceAlpha, "%source.a" }, | ||
| 759 | { AlphaModifier::OneMinusSourceAlpha, "(1.0 - %source.a)" }, | ||
| 760 | { AlphaModifier::SourceRed, "%source.r" }, | ||
| 761 | { AlphaModifier::OneMinusSourceRed, "(1.0 - %source.r)" }, | ||
| 762 | { AlphaModifier::SourceGreen, "%source.g" }, | ||
| 763 | { AlphaModifier::OneMinusSourceGreen, "(1.0 - %source.g)" }, | ||
| 764 | { AlphaModifier::SourceBlue, "%source.b" }, | ||
| 765 | { AlphaModifier::OneMinusSourceBlue, "(1.0 - %source.b)" }, | ||
| 766 | }; | ||
| 767 | |||
| 768 | auto src_str = GetTevStageConfigSourceString(source); | ||
| 769 | auto modifier_it = alpha_modifier_map.find(modifier); | ||
| 770 | std::string modifier_str = "%source.????"; | ||
| 771 | if (modifier_it != alpha_modifier_map.end()) | ||
| 772 | modifier_str = modifier_it->second; | ||
| 773 | |||
| 774 | return ReplacePattern(modifier_str, "%source", src_str); | ||
| 775 | } | ||
| 776 | |||
| 777 | static std::string GetTevStageConfigOperationString(const Pica::Regs::TevStageConfig::Operation& operation) { | ||
| 704 | using Operation = Pica::Regs::TevStageConfig::Operation; | 778 | using Operation = Pica::Regs::TevStageConfig::Operation; |
| 779 | static const std::map<Operation, std::string> combiner_map = { | ||
| 780 | { Operation::Replace, "%source1" }, | ||
| 781 | { Operation::Modulate, "(%source1 * %source2)" }, | ||
| 782 | { Operation::Add, "(%source1 + %source2)" }, | ||
| 783 | { Operation::AddSigned, "(%source1 + %source2) - 0.5" }, | ||
| 784 | { Operation::Lerp, "lerp(%source1, %source2, %source3)" }, | ||
| 785 | { Operation::Subtract, "(%source1 - %source2)" }, | ||
| 786 | { Operation::Dot3_RGB, "dot(%source1, %source2)" }, | ||
| 787 | { Operation::MultiplyThenAdd, "((%source1 * %source2) + %source3)" }, | ||
| 788 | { Operation::AddThenMultiply, "((%source1 + %source2) * %source3)" }, | ||
| 789 | }; | ||
| 705 | 790 | ||
| 706 | std::string stage_info = "Tev setup:\n"; | 791 | const auto op_it = combiner_map.find(operation); |
| 707 | for (size_t index = 0; index < stages.size(); ++index) { | 792 | if (op_it == combiner_map.end()) |
| 708 | const auto& tev_stage = stages[index]; | 793 | return "Unknown op (%source1, %source2, %source3)"; |
| 709 | 794 | ||
| 710 | static const std::map<Source, std::string> source_map = { | 795 | return op_it->second; |
| 711 | { Source::PrimaryColor, "PrimaryColor" }, | 796 | } |
| 712 | { Source::Texture0, "Texture0" }, | ||
| 713 | { Source::Texture1, "Texture1" }, | ||
| 714 | { Source::Texture2, "Texture2" }, | ||
| 715 | { Source::Constant, "Constant" }, | ||
| 716 | { Source::Previous, "Previous" }, | ||
| 717 | }; | ||
| 718 | 797 | ||
| 719 | static const std::map<ColorModifier, std::string> color_modifier_map = { | 798 | std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage) { |
| 720 | { ColorModifier::SourceColor, { "%source.rgb" } }, | 799 | auto op_str = GetTevStageConfigOperationString(tev_stage.color_op); |
| 721 | { ColorModifier::SourceAlpha, { "%source.aaa" } }, | 800 | op_str = ReplacePattern(op_str, "%source1", GetTevStageConfigColorSourceString(tev_stage.color_source1, tev_stage.color_modifier1)); |
| 722 | }; | 801 | op_str = ReplacePattern(op_str, "%source2", GetTevStageConfigColorSourceString(tev_stage.color_source2, tev_stage.color_modifier2)); |
| 723 | static const std::map<AlphaModifier, std::string> alpha_modifier_map = { | 802 | return ReplacePattern(op_str, "%source3", GetTevStageConfigColorSourceString(tev_stage.color_source3, tev_stage.color_modifier3)); |
| 724 | { AlphaModifier::SourceAlpha, "%source.a" }, | 803 | } |
| 725 | { AlphaModifier::OneMinusSourceAlpha, "(255 - %source.a)" }, | ||
| 726 | }; | ||
| 727 | 804 | ||
| 728 | static const std::map<Operation, std::string> combiner_map = { | 805 | std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage) { |
| 729 | { Operation::Replace, "%source1" }, | 806 | auto op_str = GetTevStageConfigOperationString(tev_stage.alpha_op); |
| 730 | { Operation::Modulate, "(%source1 * %source2) / 255" }, | 807 | op_str = ReplacePattern(op_str, "%source1", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source1, tev_stage.alpha_modifier1)); |
| 731 | { Operation::Add, "(%source1 + %source2)" }, | 808 | op_str = ReplacePattern(op_str, "%source2", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source2, tev_stage.alpha_modifier2)); |
| 732 | { Operation::Lerp, "lerp(%source1, %source2, %source3)" }, | 809 | return ReplacePattern(op_str, "%source3", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source3, tev_stage.alpha_modifier3)); |
| 733 | }; | 810 | } |
| 734 | 811 | ||
| 735 | static auto ReplacePattern = | 812 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages) { |
| 736 | [](const std::string& input, const std::string& pattern, const std::string& replacement) -> std::string { | 813 | std::string stage_info = "Tev setup:\n"; |
| 737 | size_t start = input.find(pattern); | 814 | for (size_t index = 0; index < stages.size(); ++index) { |
| 738 | if (start == std::string::npos) | 815 | const auto& tev_stage = stages[index]; |
| 739 | return input; | 816 | stage_info += "Stage " + std::to_string(index) + ": " + GetTevStageConfigColorCombinerString(tev_stage) + " " + GetTevStageConfigAlphaCombinerString(tev_stage) + "\n"; |
| 740 | |||
| 741 | std::string ret = input; | ||
| 742 | ret.replace(start, pattern.length(), replacement); | ||
| 743 | return ret; | ||
| 744 | }; | ||
| 745 | static auto GetColorSourceStr = | ||
| 746 | [](const Source& src, const ColorModifier& modifier) { | ||
| 747 | auto src_it = source_map.find(src); | ||
| 748 | std::string src_str = "Unknown"; | ||
| 749 | if (src_it != source_map.end()) | ||
| 750 | src_str = src_it->second; | ||
| 751 | |||
| 752 | auto modifier_it = color_modifier_map.find(modifier); | ||
| 753 | std::string modifier_str = "%source.????"; | ||
| 754 | if (modifier_it != color_modifier_map.end()) | ||
| 755 | modifier_str = modifier_it->second; | ||
| 756 | |||
| 757 | return ReplacePattern(modifier_str, "%source", src_str); | ||
| 758 | }; | ||
| 759 | static auto GetColorCombinerStr = | ||
| 760 | [](const Regs::TevStageConfig& tev_stage) { | ||
| 761 | auto op_it = combiner_map.find(tev_stage.color_op); | ||
| 762 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; | ||
| 763 | if (op_it != combiner_map.end()) | ||
| 764 | op_str = op_it->second; | ||
| 765 | |||
| 766 | op_str = ReplacePattern(op_str, "%source1", GetColorSourceStr(tev_stage.color_source1, tev_stage.color_modifier1)); | ||
| 767 | op_str = ReplacePattern(op_str, "%source2", GetColorSourceStr(tev_stage.color_source2, tev_stage.color_modifier2)); | ||
| 768 | return ReplacePattern(op_str, "%source3", GetColorSourceStr(tev_stage.color_source3, tev_stage.color_modifier3)); | ||
| 769 | }; | ||
| 770 | static auto GetAlphaSourceStr = | ||
| 771 | [](const Source& src, const AlphaModifier& modifier) { | ||
| 772 | auto src_it = source_map.find(src); | ||
| 773 | std::string src_str = "Unknown"; | ||
| 774 | if (src_it != source_map.end()) | ||
| 775 | src_str = src_it->second; | ||
| 776 | |||
| 777 | auto modifier_it = alpha_modifier_map.find(modifier); | ||
| 778 | std::string modifier_str = "%source.????"; | ||
| 779 | if (modifier_it != alpha_modifier_map.end()) | ||
| 780 | modifier_str = modifier_it->second; | ||
| 781 | |||
| 782 | return ReplacePattern(modifier_str, "%source", src_str); | ||
| 783 | }; | ||
| 784 | static auto GetAlphaCombinerStr = | ||
| 785 | [](const Regs::TevStageConfig& tev_stage) { | ||
| 786 | auto op_it = combiner_map.find(tev_stage.alpha_op); | ||
| 787 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; | ||
| 788 | if (op_it != combiner_map.end()) | ||
| 789 | op_str = op_it->second; | ||
| 790 | |||
| 791 | op_str = ReplacePattern(op_str, "%source1", GetAlphaSourceStr(tev_stage.alpha_source1, tev_stage.alpha_modifier1)); | ||
| 792 | op_str = ReplacePattern(op_str, "%source2", GetAlphaSourceStr(tev_stage.alpha_source2, tev_stage.alpha_modifier2)); | ||
| 793 | return ReplacePattern(op_str, "%source3", GetAlphaSourceStr(tev_stage.alpha_source3, tev_stage.alpha_modifier3)); | ||
| 794 | }; | ||
| 795 | |||
| 796 | stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n"; | ||
| 797 | } | 817 | } |
| 798 | |||
| 799 | LOG_TRACE(HW_GPU, "%s", stage_info.c_str()); | 818 | LOG_TRACE(HW_GPU, "%s", stage_info.c_str()); |
| 800 | } | 819 | } |
| 801 | 820 | ||
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index f628292a4..92e9734ae 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h | |||
| @@ -224,7 +224,11 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const Texture | |||
| 224 | 224 | ||
| 225 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); | 225 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); |
| 226 | 226 | ||
| 227 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); | 227 | std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage); |
| 228 | std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage); | ||
| 229 | |||
| 230 | /// Dumps the Tev stage config to log at trace level | ||
| 231 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages); | ||
| 228 | 232 | ||
| 229 | /** | 233 | /** |
| 230 | * Used in the vertex loader to merge access records. TODO: Investigate if actually useful. | 234 | * Used in the vertex loader to merge access records. TODO: Investigate if actually useful. |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index ed2e2f3ae..bcd1ae78d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -104,7 +104,6 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 104 | 104 | ||
| 105 | // Sync fixed function OpenGL state | 105 | // Sync fixed function OpenGL state |
| 106 | SyncCullMode(); | 106 | SyncCullMode(); |
| 107 | SyncDepthModifiers(); | ||
| 108 | SyncBlendEnabled(); | 107 | SyncBlendEnabled(); |
| 109 | SyncBlendFuncs(); | 108 | SyncBlendFuncs(); |
| 110 | SyncBlendColor(); | 109 | SyncBlendColor(); |
| @@ -259,8 +258,10 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | |||
| 259 | 258 | ||
| 260 | // Depth modifiers | 259 | // Depth modifiers |
| 261 | case PICA_REG_INDEX(viewport_depth_range): | 260 | case PICA_REG_INDEX(viewport_depth_range): |
| 261 | SyncDepthScale(); | ||
| 262 | break; | ||
| 262 | case PICA_REG_INDEX(viewport_depth_near_plane): | 263 | case PICA_REG_INDEX(viewport_depth_near_plane): |
| 263 | SyncDepthModifiers(); | 264 | SyncDepthOffset(); |
| 264 | break; | 265 | break; |
| 265 | 266 | ||
| 266 | // Depth buffering | 267 | // Depth buffering |
| @@ -880,6 +881,8 @@ void RasterizerOpenGL::SetShader() { | |||
| 880 | glUniformBlockBinding(current_shader->shader.handle, block_index, 0); | 881 | glUniformBlockBinding(current_shader->shader.handle, block_index, 0); |
| 881 | 882 | ||
| 882 | // Update uniforms | 883 | // Update uniforms |
| 884 | SyncDepthScale(); | ||
| 885 | SyncDepthOffset(); | ||
| 883 | SyncAlphaTest(); | 886 | SyncAlphaTest(); |
| 884 | SyncCombinerColor(); | 887 | SyncCombinerColor(); |
| 885 | auto& tev_stages = Pica::g_state.regs.GetTevStages(); | 888 | auto& tev_stages = Pica::g_state.regs.GetTevStages(); |
| @@ -922,13 +925,20 @@ void RasterizerOpenGL::SyncCullMode() { | |||
| 922 | } | 925 | } |
| 923 | } | 926 | } |
| 924 | 927 | ||
| 925 | void RasterizerOpenGL::SyncDepthModifiers() { | 928 | void RasterizerOpenGL::SyncDepthScale() { |
| 926 | float depth_scale = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32(); | 929 | float depth_scale = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32(); |
| 927 | float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_near_plane).ToFloat32(); | 930 | if (depth_scale != uniform_block_data.data.depth_scale) { |
| 931 | uniform_block_data.data.depth_scale = depth_scale; | ||
| 932 | uniform_block_data.dirty = true; | ||
| 933 | } | ||
| 934 | } | ||
| 928 | 935 | ||
| 929 | uniform_block_data.data.depth_scale = depth_scale; | 936 | void RasterizerOpenGL::SyncDepthOffset() { |
| 930 | uniform_block_data.data.depth_offset = depth_offset; | 937 | float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_near_plane).ToFloat32(); |
| 931 | uniform_block_data.dirty = true; | 938 | if (depth_offset != uniform_block_data.data.depth_offset) { |
| 939 | uniform_block_data.data.depth_offset = depth_offset; | ||
| 940 | uniform_block_data.dirty = true; | ||
| 941 | } | ||
| 932 | } | 942 | } |
| 933 | 943 | ||
| 934 | void RasterizerOpenGL::SyncBlendEnabled() { | 944 | void RasterizerOpenGL::SyncBlendEnabled() { |
| @@ -937,6 +947,8 @@ void RasterizerOpenGL::SyncBlendEnabled() { | |||
| 937 | 947 | ||
| 938 | void RasterizerOpenGL::SyncBlendFuncs() { | 948 | void RasterizerOpenGL::SyncBlendFuncs() { |
| 939 | const auto& regs = Pica::g_state.regs; | 949 | const auto& regs = Pica::g_state.regs; |
| 950 | state.blend.rgb_equation = PicaToGL::BlendEquation(regs.output_merger.alpha_blending.blend_equation_rgb); | ||
| 951 | state.blend.a_equation = PicaToGL::BlendEquation(regs.output_merger.alpha_blending.blend_equation_a); | ||
| 940 | state.blend.src_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_rgb); | 952 | state.blend.src_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_rgb); |
| 941 | state.blend.dst_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_rgb); | 953 | state.blend.dst_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_rgb); |
| 942 | state.blend.src_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_a); | 954 | state.blend.src_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_a); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index eed00011a..d70369400 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -339,8 +339,11 @@ private: | |||
| 339 | /// Syncs the cull mode to match the PICA register | 339 | /// Syncs the cull mode to match the PICA register |
| 340 | void SyncCullMode(); | 340 | void SyncCullMode(); |
| 341 | 341 | ||
| 342 | /// Syncs the depth scale and offset to match the PICA registers | 342 | /// Syncs the depth scale to match the PICA register |
| 343 | void SyncDepthModifiers(); | 343 | void SyncDepthScale(); |
| 344 | |||
| 345 | /// Syncs the depth offset to match the PICA register | ||
| 346 | void SyncDepthOffset(); | ||
| 344 | 347 | ||
| 345 | /// Syncs the blend enabled status to match the PICA register | 348 | /// Syncs the blend enabled status to match the PICA register |
| 346 | void SyncBlendEnabled(); | 349 | void SyncBlendEnabled(); |
| @@ -413,7 +416,7 @@ private: | |||
| 413 | UniformData data; | 416 | UniformData data; |
| 414 | bool lut_dirty[6]; | 417 | bool lut_dirty[6]; |
| 415 | bool dirty; | 418 | bool dirty; |
| 416 | } uniform_block_data; | 419 | } uniform_block_data = {}; |
| 417 | 420 | ||
| 418 | std::array<SamplerInfo, 3> texture_samplers; | 421 | std::array<SamplerInfo, 3> texture_samplers; |
| 419 | OGLVertexArray vertex_array; | 422 | OGLVertexArray vertex_array; |
| @@ -422,5 +425,5 @@ private: | |||
| 422 | OGLFramebuffer framebuffer; | 425 | OGLFramebuffer framebuffer; |
| 423 | 426 | ||
| 424 | std::array<OGLTexture, 6> lighting_luts; | 427 | std::array<OGLTexture, 6> lighting_luts; |
| 425 | std::array<std::array<GLvec4, 256>, 6> lighting_lut_data; | 428 | std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{}; |
| 426 | }; | 429 | }; |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 02cd9f417..fa141fc9a 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -36,6 +36,8 @@ OpenGLState::OpenGLState() { | |||
| 36 | stencil.action_stencil_fail = GL_KEEP; | 36 | stencil.action_stencil_fail = GL_KEEP; |
| 37 | 37 | ||
| 38 | blend.enabled = false; | 38 | blend.enabled = false; |
| 39 | blend.rgb_equation = GL_FUNC_ADD; | ||
| 40 | blend.a_equation = GL_FUNC_ADD; | ||
| 39 | blend.src_rgb_func = GL_ONE; | 41 | blend.src_rgb_func = GL_ONE; |
| 40 | blend.dst_rgb_func = GL_ZERO; | 42 | blend.dst_rgb_func = GL_ZERO; |
| 41 | blend.src_a_func = GL_ONE; | 43 | blend.src_a_func = GL_ONE; |
| @@ -165,6 +167,11 @@ void OpenGLState::Apply() const { | |||
| 165 | blend.src_a_func, blend.dst_a_func); | 167 | blend.src_a_func, blend.dst_a_func); |
| 166 | } | 168 | } |
| 167 | 169 | ||
| 170 | if (blend.rgb_equation != cur_state.blend.rgb_equation || | ||
| 171 | blend.a_equation != cur_state.blend.a_equation) { | ||
| 172 | glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); | ||
| 173 | } | ||
| 174 | |||
| 168 | if (logic_op != cur_state.logic_op) { | 175 | if (logic_op != cur_state.logic_op) { |
| 169 | glLogicOp(logic_op); | 176 | glLogicOp(logic_op); |
| 170 | } | 177 | } |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 24f20e47c..228727054 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -40,6 +40,8 @@ public: | |||
| 40 | 40 | ||
| 41 | struct { | 41 | struct { |
| 42 | bool enabled; // GL_BLEND | 42 | bool enabled; // GL_BLEND |
| 43 | GLenum rgb_equation; // GL_BLEND_EQUATION_RGB | ||
| 44 | GLenum a_equation; // GL_BLEND_EQUATION_ALPHA | ||
| 43 | GLenum src_rgb_func; // GL_BLEND_SRC_RGB | 45 | GLenum src_rgb_func; // GL_BLEND_SRC_RGB |
| 44 | GLenum dst_rgb_func; // GL_BLEND_DST_RGB | 46 | GLenum dst_rgb_func; // GL_BLEND_DST_RGB |
| 45 | GLenum src_a_func; // GL_BLEND_SRC_ALPHA | 47 | GLenum src_a_func; // GL_BLEND_SRC_ALPHA |
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 976d1f364..6dc2758c5 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h | |||
| @@ -78,6 +78,26 @@ inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) { | |||
| 78 | return gl_mode; | 78 | return gl_mode; |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | inline GLenum BlendEquation(Pica::Regs::BlendEquation equation) { | ||
| 82 | static const GLenum blend_equation_table[] = { | ||
| 83 | GL_FUNC_ADD, // BlendEquation::Add | ||
| 84 | GL_FUNC_SUBTRACT, // BlendEquation::Subtract | ||
| 85 | GL_FUNC_REVERSE_SUBTRACT, // BlendEquation::ReverseSubtract | ||
| 86 | GL_MIN, // BlendEquation::Min | ||
| 87 | GL_MAX, // BlendEquation::Max | ||
| 88 | }; | ||
| 89 | |||
| 90 | // Range check table for input | ||
| 91 | if (static_cast<size_t>(equation) >= ARRAY_SIZE(blend_equation_table)) { | ||
| 92 | LOG_CRITICAL(Render_OpenGL, "Unknown blend equation %d", equation); | ||
| 93 | UNREACHABLE(); | ||
| 94 | |||
| 95 | return GL_FUNC_ADD; | ||
| 96 | } | ||
| 97 | |||
| 98 | return blend_equation_table[(unsigned)equation]; | ||
| 99 | } | ||
| 100 | |||
| 81 | inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) { | 101 | inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) { |
| 82 | static const GLenum blend_func_table[] = { | 102 | static const GLenum blend_func_table[] = { |
| 83 | GL_ZERO, // BlendFactor::Zero | 103 | GL_ZERO, // BlendFactor::Zero |
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index e93a9d92a..161097610 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp | |||
| @@ -64,6 +64,7 @@ MICROPROFILE_DEFINE(GPU_Shader, "GPU", "Shader", MP_RGB(50, 50, 240)); | |||
| 64 | 64 | ||
| 65 | OutputVertex ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num_attributes) { | 65 | OutputVertex ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num_attributes) { |
| 66 | auto& config = g_state.regs.vs; | 66 | auto& config = g_state.regs.vs; |
| 67 | auto& setup = g_state.vs; | ||
| 67 | 68 | ||
| 68 | MICROPROFILE_SCOPE(GPU_Shader); | 69 | MICROPROFILE_SCOPE(GPU_Shader); |
| 69 | 70 | ||
| @@ -81,11 +82,11 @@ OutputVertex ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, | |||
| 81 | 82 | ||
| 82 | #ifdef ARCHITECTURE_x86_64 | 83 | #ifdef ARCHITECTURE_x86_64 |
| 83 | if (VideoCore::g_shader_jit_enabled) | 84 | if (VideoCore::g_shader_jit_enabled) |
| 84 | jit_shader->Run(&state.registers, g_state.regs.vs.main_offset); | 85 | jit_shader->Run(setup, state, config.main_offset); |
| 85 | else | 86 | else |
| 86 | RunInterpreter(state); | 87 | RunInterpreter(setup, state, config.main_offset); |
| 87 | #else | 88 | #else |
| 88 | RunInterpreter(state); | 89 | RunInterpreter(setup, state, config.main_offset); |
| 89 | #endif // ARCHITECTURE_x86_64 | 90 | #endif // ARCHITECTURE_x86_64 |
| 90 | 91 | ||
| 91 | // Setup output data | 92 | // Setup output data |
| @@ -156,7 +157,7 @@ DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_ | |||
| 156 | state.conditional_code[0] = false; | 157 | state.conditional_code[0] = false; |
| 157 | state.conditional_code[1] = false; | 158 | state.conditional_code[1] = false; |
| 158 | 159 | ||
| 159 | RunInterpreter(state); | 160 | RunInterpreter(setup, state, config.main_offset); |
| 160 | return state.debug; | 161 | return state.debug; |
| 161 | } | 162 | } |
| 162 | 163 | ||
diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h index 983e4a967..84898f21c 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h | |||
| @@ -283,10 +283,10 @@ struct UnitState { | |||
| 283 | static size_t InputOffset(const SourceRegister& reg) { | 283 | static size_t InputOffset(const SourceRegister& reg) { |
| 284 | switch (reg.GetRegisterType()) { | 284 | switch (reg.GetRegisterType()) { |
| 285 | case RegisterType::Input: | 285 | case RegisterType::Input: |
| 286 | return offsetof(UnitState::Registers, input) + reg.GetIndex()*sizeof(Math::Vec4<float24>); | 286 | return offsetof(UnitState, registers.input) + reg.GetIndex()*sizeof(Math::Vec4<float24>); |
| 287 | 287 | ||
| 288 | case RegisterType::Temporary: | 288 | case RegisterType::Temporary: |
| 289 | return offsetof(UnitState::Registers, temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>); | 289 | return offsetof(UnitState, registers.temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>); |
| 290 | 290 | ||
| 291 | default: | 291 | default: |
| 292 | UNREACHABLE(); | 292 | UNREACHABLE(); |
| @@ -297,10 +297,10 @@ struct UnitState { | |||
| 297 | static size_t OutputOffset(const DestRegister& reg) { | 297 | static size_t OutputOffset(const DestRegister& reg) { |
| 298 | switch (reg.GetRegisterType()) { | 298 | switch (reg.GetRegisterType()) { |
| 299 | case RegisterType::Output: | 299 | case RegisterType::Output: |
| 300 | return offsetof(UnitState::Registers, output) + reg.GetIndex()*sizeof(Math::Vec4<float24>); | 300 | return offsetof(UnitState, registers.output) + reg.GetIndex()*sizeof(Math::Vec4<float24>); |
| 301 | 301 | ||
| 302 | case RegisterType::Temporary: | 302 | case RegisterType::Temporary: |
| 303 | return offsetof(UnitState::Registers, temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>); | 303 | return offsetof(UnitState, registers.temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>); |
| 304 | 304 | ||
| 305 | default: | 305 | default: |
| 306 | UNREACHABLE(); | 306 | UNREACHABLE(); |
| @@ -323,6 +323,23 @@ struct ShaderSetup { | |||
| 323 | std::array<Math::Vec4<u8>, 4> i; | 323 | std::array<Math::Vec4<u8>, 4> i; |
| 324 | } uniforms; | 324 | } uniforms; |
| 325 | 325 | ||
| 326 | static size_t UniformOffset(RegisterType type, unsigned index) { | ||
| 327 | switch (type) { | ||
| 328 | case RegisterType::FloatUniform: | ||
| 329 | return offsetof(ShaderSetup, uniforms.f) + index*sizeof(Math::Vec4<float24>); | ||
| 330 | |||
| 331 | case RegisterType::BoolUniform: | ||
| 332 | return offsetof(ShaderSetup, uniforms.b) + index*sizeof(bool); | ||
| 333 | |||
| 334 | case RegisterType::IntUniform: | ||
| 335 | return offsetof(ShaderSetup, uniforms.i) + index*sizeof(Math::Vec4<u8>); | ||
| 336 | |||
| 337 | default: | ||
| 338 | UNREACHABLE(); | ||
| 339 | return 0; | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 326 | std::array<u32, 1024> program_code; | 343 | std::array<u32, 1024> program_code; |
| 327 | std::array<u32, 1024> swizzle_data; | 344 | std::array<u32, 1024> swizzle_data; |
| 328 | 345 | ||
diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp index 3a827d11f..714e8bfd5 100644 --- a/src/video_core/shader/shader_interpreter.cpp +++ b/src/video_core/shader/shader_interpreter.cpp | |||
| @@ -41,11 +41,11 @@ struct CallStackElement { | |||
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | template<bool Debug> | 43 | template<bool Debug> |
| 44 | void RunInterpreter(UnitState<Debug>& state) { | 44 | void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset) { |
| 45 | // TODO: Is there a maximal size for this? | 45 | // TODO: Is there a maximal size for this? |
| 46 | boost::container::static_vector<CallStackElement, 16> call_stack; | 46 | boost::container::static_vector<CallStackElement, 16> call_stack; |
| 47 | 47 | ||
| 48 | u32 program_counter = g_state.regs.vs.main_offset; | 48 | u32 program_counter = offset; |
| 49 | 49 | ||
| 50 | const auto& uniforms = g_state.vs.uniforms; | 50 | const auto& uniforms = g_state.vs.uniforms; |
| 51 | const auto& swizzle_data = g_state.vs.swizzle_data; | 51 | const auto& swizzle_data = g_state.vs.swizzle_data; |
| @@ -647,8 +647,8 @@ void RunInterpreter(UnitState<Debug>& state) { | |||
| 647 | } | 647 | } |
| 648 | 648 | ||
| 649 | // Explicit instantiation | 649 | // Explicit instantiation |
| 650 | template void RunInterpreter(UnitState<false>& state); | 650 | template void RunInterpreter(const ShaderSetup& setup, UnitState<false>& state, unsigned offset); |
| 651 | template void RunInterpreter(UnitState<true>& state); | 651 | template void RunInterpreter(const ShaderSetup& setup, UnitState<true>& state, unsigned offset); |
| 652 | 652 | ||
| 653 | } // namespace | 653 | } // namespace |
| 654 | 654 | ||
diff --git a/src/video_core/shader/shader_interpreter.h b/src/video_core/shader/shader_interpreter.h index 6048cdf3a..bb3ce1c6e 100644 --- a/src/video_core/shader/shader_interpreter.h +++ b/src/video_core/shader/shader_interpreter.h | |||
| @@ -11,7 +11,7 @@ namespace Shader { | |||
| 11 | template <bool Debug> struct UnitState; | 11 | template <bool Debug> struct UnitState; |
| 12 | 12 | ||
| 13 | template<bool Debug> | 13 | template<bool Debug> |
| 14 | void RunInterpreter(UnitState<Debug>& state); | 14 | void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset); |
| 15 | 15 | ||
| 16 | } // namespace | 16 | } // namespace |
| 17 | 17 | ||
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index 99f6c51eb..43e7e6b4c 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp | |||
| @@ -102,7 +102,7 @@ const JitFunction instr_table[64] = { | |||
| 102 | // purposes, as documented below: | 102 | // purposes, as documented below: |
| 103 | 103 | ||
| 104 | /// Pointer to the uniform memory | 104 | /// Pointer to the uniform memory |
| 105 | static const X64Reg UNIFORMS = R9; | 105 | static const X64Reg SETUP = R9; |
| 106 | /// The two 32-bit VS address offset registers set by the MOVA instruction | 106 | /// The two 32-bit VS address offset registers set by the MOVA instruction |
| 107 | static const X64Reg ADDROFFS_REG_0 = R10; | 107 | static const X64Reg ADDROFFS_REG_0 = R10; |
| 108 | static const X64Reg ADDROFFS_REG_1 = R11; | 108 | static const X64Reg ADDROFFS_REG_1 = R11; |
| @@ -117,7 +117,7 @@ static const X64Reg COND0 = R13; | |||
| 117 | /// Result of the previous CMP instruction for the Y-component comparison | 117 | /// Result of the previous CMP instruction for the Y-component comparison |
| 118 | static const X64Reg COND1 = R14; | 118 | static const X64Reg COND1 = R14; |
| 119 | /// Pointer to the UnitState instance for the current VS unit | 119 | /// Pointer to the UnitState instance for the current VS unit |
| 120 | static const X64Reg REGISTERS = R15; | 120 | static const X64Reg STATE = R15; |
| 121 | /// SIMD scratch register | 121 | /// SIMD scratch register |
| 122 | static const X64Reg SCRATCH = XMM0; | 122 | static const X64Reg SCRATCH = XMM0; |
| 123 | /// Loaded with the first swizzled source register, otherwise can be used as a scratch register | 123 | /// Loaded with the first swizzled source register, otherwise can be used as a scratch register |
| @@ -136,7 +136,7 @@ static const X64Reg NEGBIT = XMM15; | |||
| 136 | // State registers that must not be modified by external functions calls | 136 | // State registers that must not be modified by external functions calls |
| 137 | // Scratch registers, e.g., SRC1 and SCRATCH, have to be saved on the side if needed | 137 | // Scratch registers, e.g., SRC1 and SCRATCH, have to be saved on the side if needed |
| 138 | static const BitSet32 persistent_regs = { | 138 | static const BitSet32 persistent_regs = { |
| 139 | UNIFORMS, REGISTERS, // Pointers to register blocks | 139 | SETUP, STATE, // Pointers to register blocks |
| 140 | ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, // Cached registers | 140 | ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, // Cached registers |
| 141 | ONE+16, NEGBIT+16, // Constants | 141 | ONE+16, NEGBIT+16, // Constants |
| 142 | }; | 142 | }; |
| @@ -177,10 +177,10 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe | |||
| 177 | size_t src_offset; | 177 | size_t src_offset; |
| 178 | 178 | ||
| 179 | if (src_reg.GetRegisterType() == RegisterType::FloatUniform) { | 179 | if (src_reg.GetRegisterType() == RegisterType::FloatUniform) { |
| 180 | src_ptr = UNIFORMS; | 180 | src_ptr = SETUP; |
| 181 | src_offset = src_reg.GetIndex() * sizeof(float24) * 4; | 181 | src_offset = ShaderSetup::UniformOffset(RegisterType::FloatUniform, src_reg.GetIndex()); |
| 182 | } else { | 182 | } else { |
| 183 | src_ptr = REGISTERS; | 183 | src_ptr = STATE; |
| 184 | src_offset = UnitState<false>::InputOffset(src_reg); | 184 | src_offset = UnitState<false>::InputOffset(src_reg); |
| 185 | } | 185 | } |
| 186 | 186 | ||
| @@ -264,11 +264,11 @@ void JitShader::Compile_DestEnable(Instruction instr,X64Reg src) { | |||
| 264 | // If all components are enabled, write the result to the destination register | 264 | // If all components are enabled, write the result to the destination register |
| 265 | if (swiz.dest_mask == NO_DEST_REG_MASK) { | 265 | if (swiz.dest_mask == NO_DEST_REG_MASK) { |
| 266 | // Store dest back to memory | 266 | // Store dest back to memory |
| 267 | MOVAPS(MDisp(REGISTERS, dest_offset_disp), src); | 267 | MOVAPS(MDisp(STATE, dest_offset_disp), src); |
| 268 | 268 | ||
| 269 | } else { | 269 | } else { |
| 270 | // Not all components are enabled, so mask the result when storing to the destination register... | 270 | // Not all components are enabled, so mask the result when storing to the destination register... |
| 271 | MOVAPS(SCRATCH, MDisp(REGISTERS, dest_offset_disp)); | 271 | MOVAPS(SCRATCH, MDisp(STATE, dest_offset_disp)); |
| 272 | 272 | ||
| 273 | if (Common::GetCPUCaps().sse4_1) { | 273 | if (Common::GetCPUCaps().sse4_1) { |
| 274 | u8 mask = ((swiz.dest_mask & 1) << 3) | ((swiz.dest_mask & 8) >> 3) | ((swiz.dest_mask & 2) << 1) | ((swiz.dest_mask & 4) >> 1); | 274 | u8 mask = ((swiz.dest_mask & 1) << 3) | ((swiz.dest_mask & 8) >> 3) | ((swiz.dest_mask & 2) << 1) | ((swiz.dest_mask & 4) >> 1); |
| @@ -287,7 +287,7 @@ void JitShader::Compile_DestEnable(Instruction instr,X64Reg src) { | |||
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | // Store dest back to memory | 289 | // Store dest back to memory |
| 290 | MOVAPS(MDisp(REGISTERS, dest_offset_disp), SCRATCH); | 290 | MOVAPS(MDisp(STATE, dest_offset_disp), SCRATCH); |
| 291 | } | 291 | } |
| 292 | } | 292 | } |
| 293 | 293 | ||
| @@ -336,8 +336,8 @@ void JitShader::Compile_EvaluateCondition(Instruction instr) { | |||
| 336 | } | 336 | } |
| 337 | 337 | ||
| 338 | void JitShader::Compile_UniformCondition(Instruction instr) { | 338 | void JitShader::Compile_UniformCondition(Instruction instr) { |
| 339 | int offset = offsetof(decltype(g_state.vs.uniforms), b) + (instr.flow_control.bool_uniform_id * sizeof(bool)); | 339 | int offset = ShaderSetup::UniformOffset(RegisterType::BoolUniform, instr.flow_control.bool_uniform_id); |
| 340 | CMP(sizeof(bool) * 8, MDisp(UNIFORMS, offset), Imm8(0)); | 340 | CMP(sizeof(bool) * 8, MDisp(SETUP, offset), Imm8(0)); |
| 341 | } | 341 | } |
| 342 | 342 | ||
| 343 | BitSet32 JitShader::PersistentCallerSavedRegs() { | 343 | BitSet32 JitShader::PersistentCallerSavedRegs() { |
| @@ -714,8 +714,8 @@ void JitShader::Compile_LOOP(Instruction instr) { | |||
| 714 | 714 | ||
| 715 | looping = true; | 715 | looping = true; |
| 716 | 716 | ||
| 717 | int offset = offsetof(decltype(g_state.vs.uniforms), i) + (instr.flow_control.int_uniform_id * sizeof(Math::Vec4<u8>)); | 717 | int offset = ShaderSetup::UniformOffset(RegisterType::IntUniform, instr.flow_control.int_uniform_id); |
| 718 | MOV(32, R(LOOPCOUNT), MDisp(UNIFORMS, offset)); | 718 | MOV(32, R(LOOPCOUNT), MDisp(SETUP, offset)); |
| 719 | MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT)); | 719 | MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT)); |
| 720 | SHR(32, R(LOOPCOUNT_REG), Imm8(8)); | 720 | SHR(32, R(LOOPCOUNT_REG), Imm8(8)); |
| 721 | AND(32, R(LOOPCOUNT_REG), Imm32(0xff)); // Y-component is the start | 721 | AND(32, R(LOOPCOUNT_REG), Imm32(0xff)); // Y-component is the start |
| @@ -826,8 +826,8 @@ void JitShader::Compile() { | |||
| 826 | // The stack pointer is 8 modulo 16 at the entry of a procedure | 826 | // The stack pointer is 8 modulo 16 at the entry of a procedure |
| 827 | ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); | 827 | ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); |
| 828 | 828 | ||
| 829 | MOV(PTRBITS, R(REGISTERS), R(ABI_PARAM1)); | 829 | MOV(PTRBITS, R(SETUP), R(ABI_PARAM1)); |
| 830 | MOV(PTRBITS, R(UNIFORMS), ImmPtr(&g_state.vs.uniforms)); | 830 | MOV(PTRBITS, R(STATE), R(ABI_PARAM2)); |
| 831 | 831 | ||
| 832 | // Zero address/loop registers | 832 | // Zero address/loop registers |
| 833 | XOR(64, R(ADDROFFS_REG_0), R(ADDROFFS_REG_0)); | 833 | XOR(64, R(ADDROFFS_REG_0), R(ADDROFFS_REG_0)); |
| @@ -845,7 +845,7 @@ void JitShader::Compile() { | |||
| 845 | MOVAPS(NEGBIT, MatR(RAX)); | 845 | MOVAPS(NEGBIT, MatR(RAX)); |
| 846 | 846 | ||
| 847 | // Jump to start of the shader program | 847 | // Jump to start of the shader program |
| 848 | JMPptr(R(ABI_PARAM2)); | 848 | JMPptr(R(ABI_PARAM3)); |
| 849 | 849 | ||
| 850 | // Compile entire program | 850 | // Compile entire program |
| 851 | Compile_Block(static_cast<unsigned>(g_state.vs.program_code.size())); | 851 | Compile_Block(static_cast<unsigned>(g_state.vs.program_code.size())); |
diff --git a/src/video_core/shader/shader_jit_x64.h b/src/video_core/shader/shader_jit_x64.h index 30aa7ff30..5468459d4 100644 --- a/src/video_core/shader/shader_jit_x64.h +++ b/src/video_core/shader/shader_jit_x64.h | |||
| @@ -36,8 +36,8 @@ class JitShader : public Gen::XCodeBlock { | |||
| 36 | public: | 36 | public: |
| 37 | JitShader(); | 37 | JitShader(); |
| 38 | 38 | ||
| 39 | void Run(void* registers, unsigned offset) const { | 39 | void Run(const ShaderSetup& setup, UnitState<false>& state, unsigned offset) const { |
| 40 | program(registers, code_ptr[offset]); | 40 | program(&setup, &state, code_ptr[offset]); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | void Compile(); | 43 | void Compile(); |
| @@ -117,7 +117,7 @@ private: | |||
| 117 | /// Branches that need to be fixed up once the entire shader program is compiled | 117 | /// Branches that need to be fixed up once the entire shader program is compiled |
| 118 | std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches; | 118 | std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches; |
| 119 | 119 | ||
| 120 | using CompiledShader = void(void* registers, const u8* start_addr); | 120 | using CompiledShader = void(const void* setup, void* state, const u8* start_addr); |
| 121 | CompiledShader* program = nullptr; | 121 | CompiledShader* program = nullptr; |
| 122 | }; | 122 | }; |
| 123 | 123 | ||