summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/hle/dsp.cpp59
-rw-r--r--src/audio_core/hle/dsp.h2
-rw-r--r--src/audio_core/hle/mixers.cpp201
-rw-r--r--src/audio_core/hle/mixers.h63
-rw-r--r--src/citra_qt/CMakeLists.txt3
-rw-r--r--src/citra_qt/configure.ui11
-rw-r--r--src/citra_qt/configure_audio.cpp44
-rw-r--r--src/citra_qt/configure_audio.h27
-rw-r--r--src/citra_qt/configure_audio.ui48
-rw-r--r--src/citra_qt/configure_dialog.cpp1
-rw-r--r--src/citra_qt/debugger/profiler.cpp16
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/arm_interface.h1
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp12
-rw-r--r--src/core/hle/kernel/thread.cpp2
-rw-r--r--src/core/hle/service/act_a.cpp26
-rw-r--r--src/core/hle/service/act_a.h23
-rw-r--r--src/core/hle/service/act_u.cpp3
-rw-r--r--src/core/hle/service/dsp_dsp.cpp4
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/svc.cpp3
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp199
-rw-r--r--src/video_core/debug_utils/debug_utils.h6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp26
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h11
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_state.h2
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h20
-rw-r--r--src/video_core/shader/shader.cpp9
-rw-r--r--src/video_core/shader/shader.h25
-rw-r--r--src/video_core/shader/shader_interpreter.cpp8
-rw-r--r--src/video_core/shader/shader_interpreter.h2
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp32
-rw-r--r--src/video_core/shader/shader_jit_x64.h6
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 @@
14namespace DSP { 15namespace DSP {
15namespace HLE { 16namespace HLE {
16 17
18// Region management
19
17std::array<SharedMemory, 2> g_regions; 20std::array<SharedMemory, 2> g_regions;
18 21
19static size_t CurrentRegionIndex() { 22static 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
44static std::array<Source, num_sources> sources = { 49static 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};
55static Mixers mixers;
56
57static 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
51static std::unique_ptr<AudioCore::Sink> sink; 88static std::unique_ptr<AudioCore::Sink> sink;
52static AudioCore::TimeStretcher time_stretcher; 89static AudioCore::TimeStretcher time_stretcher;
53 90
91static 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
54void Init() { 98void 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
77bool Tick() { 123bool 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.
430struct FinalMixSamples { 430struct FinalMixSamples {
431 s16_le pcm16[2 * samples_per_frame]; 431 s16_le pcm16[samples_per_frame][2];
432}; 432};
433ASSERT_DSP_STRUCT(FinalMixSamples, 640); 433ASSERT_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
15namespace DSP {
16namespace HLE {
17
18void Mixers::Reset() {
19 current_frame.fill({});
20 state = {};
21}
22
23DspStatus 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
38void 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
93static s16 ClampToS16(s32 value) {
94 return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767));
95}
96
97static 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
104void 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
137void 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
157void 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
183void 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
193DspStatus 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
12namespace DSP {
13namespace HLE {
14
15class Mixers final {
16public:
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
32private:
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
12ConfigureAudio::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
27ConfigureAudio::~ConfigureAudio() {
28}
29
30void 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
41void 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
10namespace Ui {
11class ConfigureAudio;
12}
13
14class ConfigureAudio : public QWidget {
15 Q_OBJECT
16
17public:
18 explicit ConfigureAudio(QWidget* parent = nullptr);
19 ~ConfigureAudio();
20
21 void applyConfiguration();
22
23private:
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
26void ConfigureDialog::applyConfiguration() { 26void 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
225void MicroProfileWidget::paintEvent(QPaintEvent* ev) { 231void 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
250void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) { 258void 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
255void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) { 263void 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
261void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) { 269void 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
267void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { 275void 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
10namespace Core { 11namespace 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
10namespace ACT_A {
11
12const 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
22Interface::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
12namespace ACT_A {
13
14class Interface : public Service::Interface {
15public:
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 @@
10namespace ACT_U { 10namespace ACT_U {
11 11
12const Interface::FunctionInfo FunctionTable[] = { 12const 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
699void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) 699static 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
709static 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
731static 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
755static 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
777static 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 = { 798std::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 = { 805std::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 = 812void 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
225void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); 225void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
226 226
227void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); 227std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
228std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
229
230/// Dumps the Tev stage config to log at trace level
231void 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
925void RasterizerOpenGL::SyncDepthModifiers() { 928void 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; 936void 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
934void RasterizerOpenGL::SyncBlendEnabled() { 944void RasterizerOpenGL::SyncBlendEnabled() {
@@ -937,6 +947,8 @@ void RasterizerOpenGL::SyncBlendEnabled() {
937 947
938void RasterizerOpenGL::SyncBlendFuncs() { 948void 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
81inline 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
81inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) { 101inline 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
65OutputVertex ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num_attributes) { 65OutputVertex 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
43template<bool Debug> 43template<bool Debug>
44void RunInterpreter(UnitState<Debug>& state) { 44void 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
650template void RunInterpreter(UnitState<false>& state); 650template void RunInterpreter(const ShaderSetup& setup, UnitState<false>& state, unsigned offset);
651template void RunInterpreter(UnitState<true>& state); 651template 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 {
11template <bool Debug> struct UnitState; 11template <bool Debug> struct UnitState;
12 12
13template<bool Debug> 13template<bool Debug>
14void RunInterpreter(UnitState<Debug>& state); 14void 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
105static const X64Reg UNIFORMS = R9; 105static 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
107static const X64Reg ADDROFFS_REG_0 = R10; 107static const X64Reg ADDROFFS_REG_0 = R10;
108static const X64Reg ADDROFFS_REG_1 = R11; 108static 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
118static const X64Reg COND1 = R14; 118static 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
120static const X64Reg REGISTERS = R15; 120static const X64Reg STATE = R15;
121/// SIMD scratch register 121/// SIMD scratch register
122static const X64Reg SCRATCH = XMM0; 122static 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
138static const BitSet32 persistent_regs = { 138static 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
338void JitShader::Compile_UniformCondition(Instruction instr) { 338void 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
343BitSet32 JitShader::PersistentCallerSavedRegs() { 343BitSet32 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 {
36public: 36public:
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