summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/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
codec.cpp
hle/dsp.cpp
hle/filter.cpp
+ hle/mixers.cpp
hle/pipe.cpp
hle/source.cpp
interpolate.cpp
@@ -16,6 +17,7 @@ set(HEADERS
hle/common.h
hle/dsp.h
hle/filter.h
+ hle/mixers.h
hle/pipe.h
hle/source.h
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 @@
#include <memory>
#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/mixers.h"
#include "audio_core/hle/pipe.h"
#include "audio_core/hle/source.h"
#include "audio_core/sink.h"
@@ -14,6 +15,8 @@
namespace DSP {
namespace HLE {
+// Region management
+
std::array<SharedMemory, 2> g_regions;
static size_t CurrentRegionIndex() {
@@ -41,16 +44,57 @@ static SharedMemory& WriteRegion() {
return g_regions[1 - CurrentRegionIndex()];
}
+// Audio processing and mixing
+
static std::array<Source, num_sources> sources = {
Source(0), Source(1), Source(2), Source(3), Source(4), Source(5),
Source(6), Source(7), Source(8), Source(9), Source(10), Source(11),
Source(12), Source(13), Source(14), Source(15), Source(16), Source(17),
Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
};
+static Mixers mixers;
+
+static StereoFrame16 GenerateCurrentFrame() {
+ SharedMemory& read = ReadRegion();
+ SharedMemory& write = WriteRegion();
+
+ std::array<QuadFrame32, 3> intermediate_mixes = {};
+
+ // Generate intermediate mixes
+ for (size_t i = 0; i < num_sources; i++) {
+ write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
+ for (size_t mix = 0; mix < 3; mix++) {
+ sources[i].MixInto(intermediate_mixes[mix], mix);
+ }
+ }
+
+ // Generate final mix
+ write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes);
+
+ StereoFrame16 output_frame = mixers.GetOutput();
+
+ // Write current output frame to the shared memory region
+ for (size_t samplei = 0; samplei < output_frame.size(); samplei++) {
+ for (size_t channeli = 0; channeli < output_frame[0].size(); channeli++) {
+ write.final_samples.pcm16[samplei][channeli] = s16_le(output_frame[samplei][channeli]);
+ }
+ }
+
+ return output_frame;
+}
+
+// Audio output
static std::unique_ptr<AudioCore::Sink> sink;
static AudioCore::TimeStretcher time_stretcher;
+static void OutputCurrentFrame(const StereoFrame16& frame) {
+ time_stretcher.AddSamples(&frame[0][0], frame.size());
+ sink->EnqueueSamples(time_stretcher.Process(sink->SamplesInQueue()));
+}
+
+// Public Interface
+
void Init() {
DSP::HLE::ResetPipes();
@@ -58,6 +102,8 @@ void Init() {
source.Reset();
}
+ mixers.Reset();
+
time_stretcher.Reset();
if (sink) {
time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
@@ -75,17 +121,12 @@ void Shutdown() {
}
bool Tick() {
- SharedMemory& read = ReadRegion();
- SharedMemory& write = WriteRegion();
+ StereoFrame16 current_frame = {};
- std::array<QuadFrame32, 3> intermediate_mixes = {};
+ // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region)
+ current_frame = GenerateCurrentFrame();
- for (size_t i = 0; i < num_sources; i++) {
- write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
- for (size_t mix = 0; mix < 3; mix++) {
- sources[i].MixInto(intermediate_mixes[mix], mix);
- }
- }
+ OutputCurrentFrame(current_frame);
return true;
}
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);
/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
/// When the application writes to this region it has no effect.
struct FinalMixSamples {
- s16_le pcm16[2 * samples_per_frame];
+ s16_le pcm16[samples_per_frame][2];
};
ASSERT_DSP_STRUCT(FinalMixSamples, 640);
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstddef>
+
+#include "audio_core/hle/common.h"
+#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/mixers.h"
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/math_util.h"
+
+namespace DSP {
+namespace HLE {
+
+void Mixers::Reset() {
+ current_frame.fill({});
+ state = {};
+}
+
+DspStatus Mixers::Tick(DspConfiguration& config,
+ const IntermediateMixSamples& read_samples,
+ IntermediateMixSamples& write_samples,
+ const std::array<QuadFrame32, 3>& input)
+{
+ ParseConfig(config);
+
+ AuxReturn(read_samples);
+ AuxSend(write_samples, input);
+
+ MixCurrentFrame();
+
+ return GetCurrentStatus();
+}
+
+void Mixers::ParseConfig(DspConfiguration& config) {
+ if (!config.dirty_raw) {
+ return;
+ }
+
+ if (config.mixer1_enabled_dirty) {
+ config.mixer1_enabled_dirty.Assign(0);
+ state.mixer1_enabled = config.mixer1_enabled != 0;
+ LOG_TRACE(Audio_DSP, "mixers mixer1_enabled = %hu", config.mixer1_enabled);
+ }
+
+ if (config.mixer2_enabled_dirty) {
+ config.mixer2_enabled_dirty.Assign(0);
+ state.mixer2_enabled = config.mixer2_enabled != 0;
+ LOG_TRACE(Audio_DSP, "mixers mixer2_enabled = %hu", config.mixer2_enabled);
+ }
+
+ if (config.volume_0_dirty) {
+ config.volume_0_dirty.Assign(0);
+ state.intermediate_mixer_volume[0] = config.volume[0];
+ LOG_TRACE(Audio_DSP, "mixers volume[0] = %f", config.volume[0]);
+ }
+
+ if (config.volume_1_dirty) {
+ config.volume_1_dirty.Assign(0);
+ state.intermediate_mixer_volume[1] = config.volume[1];
+ LOG_TRACE(Audio_DSP, "mixers volume[1] = %f", config.volume[1]);
+ }
+
+ if (config.volume_2_dirty) {
+ config.volume_2_dirty.Assign(0);
+ state.intermediate_mixer_volume[2] = config.volume[2];
+ LOG_TRACE(Audio_DSP, "mixers volume[2] = %f", config.volume[2]);
+ }
+
+ if (config.output_format_dirty) {
+ config.output_format_dirty.Assign(0);
+ state.output_format = config.output_format;
+ LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format));
+ }
+
+ if (config.headphones_connected_dirty) {
+ config.headphones_connected_dirty.Assign(0);
+ // Do nothing.
+ // (Note: Whether headphones are connected does affect coefficients used for surround sound.)
+ LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected);
+ }
+
+ if (config.dirty_raw) {
+ LOG_DEBUG(Audio_DSP, "mixers remaining_dirty=%x", config.dirty_raw);
+ }
+
+ config.dirty_raw = 0;
+}
+
+static s16 ClampToS16(s32 value) {
+ return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767));
+}
+
+static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) {
+ return {
+ ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
+ ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))
+ };
+}
+
+void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) {
+ // TODO(merry): Limiter. (Currently we're performing final mixing assuming a disabled limiter.)
+
+ switch (state.output_format) {
+ case OutputFormat::Mono:
+ std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
+ [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> {
+ // Downmix to mono
+ s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2));
+ // Mix into current frame
+ return AddAndClampToS16(accumulator, { mono, mono });
+ });
+ return;
+
+ case OutputFormat::Surround:
+ // TODO(merry): Implement surround sound.
+ // fallthrough
+
+ case OutputFormat::Stereo:
+ std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
+ [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> {
+ // Downmix to stereo
+ s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2]));
+ s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3]));
+ // Mix into current frame
+ return AddAndClampToS16(accumulator, { left, right });
+ });
+ return;
+ }
+
+ UNREACHABLE_MSG("Invalid output_format %zu", static_cast<size_t>(state.output_format));
+}
+
+void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
+ // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32.
+
+ if (state.mixer1_enabled) {
+ for (size_t sample = 0; sample < samples_per_frame; sample++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample];
+ }
+ }
+ }
+
+ if (state.mixer2_enabled) {
+ for (size_t sample = 0; sample < samples_per_frame; sample++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample];
+ }
+ }
+ }
+}
+
+void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) {
+ // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32.
+
+ state.intermediate_mix_buffer[0] = input[0];
+
+ if (state.mixer1_enabled) {
+ for (size_t sample = 0; sample < samples_per_frame; sample++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ write_samples.mix1.pcm32[channel][sample] = input[1][sample][channel];
+ }
+ }
+ } else {
+ state.intermediate_mix_buffer[1] = input[1];
+ }
+
+ if (state.mixer2_enabled) {
+ for (size_t sample = 0; sample < samples_per_frame; sample++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ write_samples.mix2.pcm32[channel][sample] = input[2][sample][channel];
+ }
+ }
+ } else {
+ state.intermediate_mix_buffer[2] = input[2];
+ }
+}
+
+void Mixers::MixCurrentFrame() {
+ current_frame.fill({});
+
+ for (size_t mix = 0; mix < 3; mix++) {
+ DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]);
+ }
+
+ // TODO(merry): Compressor. (We currently assume a disabled compressor.)
+}
+
+DspStatus Mixers::GetCurrentStatus() const {
+ DspStatus status;
+ status.unknown = 0;
+ status.dropped_frames = 0;
+ return status;
+}
+
+} // namespace HLE
+} // 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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "audio_core/hle/common.h"
+#include "audio_core/hle/dsp.h"
+
+namespace DSP {
+namespace HLE {
+
+class Mixers final {
+public:
+ Mixers() {
+ Reset();
+ }
+
+ void Reset();
+
+ DspStatus Tick(DspConfiguration& config,
+ const IntermediateMixSamples& read_samples,
+ IntermediateMixSamples& write_samples,
+ const std::array<QuadFrame32, 3>& input);
+
+ StereoFrame16 GetOutput() const {
+ return current_frame;
+ }
+
+private:
+ StereoFrame16 current_frame = {};
+
+ using OutputFormat = DspConfiguration::OutputFormat;
+
+ struct {
+ std::array<float, 3> intermediate_mixer_volume = {};
+
+ bool mixer1_enabled = false;
+ bool mixer2_enabled = false;
+ std::array<QuadFrame32, 3> intermediate_mix_buffer = {};
+
+ OutputFormat output_format = OutputFormat::Stereo;
+
+ } state;
+
+ /// INTERNAL: Update our internal state based on the current config.
+ void ParseConfig(DspConfiguration& config);
+ /// INTERNAL: Read samples from shared memory that have been modified by the ARM11.
+ void AuxReturn(const IntermediateMixSamples& read_samples);
+ /// INTERNAL: Write samples to shared memory for the ARM11 to modify.
+ void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
+ /// INTERNAL: Mix current_frame.
+ void MixCurrentFrame();
+ /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame.
+ void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples);
+ /// INTERNAL: Generate DspStatus based on internal state.
+ DspStatus GetCurrentStatus() const;
+};
+
+} // namespace HLE
+} // 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
util/spinbox.cpp
util/util.cpp
bootmanager.cpp
+ configure_audio.cpp
configure_debug.cpp
configure_dialog.cpp
configure_general.cpp
@@ -51,6 +52,7 @@ set(HEADERS
util/spinbox.h
util/util.h
bootmanager.h
+ configure_audio.h
configure_debug.h
configure_dialog.h
configure_general.h
@@ -69,6 +71,7 @@ set(UIS
debugger/profiler.ui
debugger/registers.ui
configure.ui
+ configure_audio.ui
configure_debug.ui
configure_general.ui
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 @@
<string>Input</string>
</attribute>
</widget>
+ <widget class="ConfigureAudio" name="audioTab">
+ <attribute name="title">
+ <string>Audio</string>
+ </attribute>
+ </widget>
<widget class="ConfigureDebug" name="debugTab">
<attribute name="title">
<string>Debug</string>
@@ -53,6 +58,12 @@
<container>1</container>
</customwidget>
<customwidget>
+ <class>ConfigureAudio</class>
+ <extends>QWidget</extends>
+ <header>configure_audio.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
<class>ConfigureDebug</class>
<extends>QWidget</extends>
<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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/sink_details.h"
+
+#include "citra_qt/configure_audio.h"
+#include "ui_configure_audio.h"
+
+#include "core/settings.h"
+
+ConfigureAudio::ConfigureAudio(QWidget* parent) :
+ QWidget(parent),
+ ui(std::make_unique<Ui::ConfigureAudio>())
+{
+ ui->setupUi(this);
+
+ ui->output_sink_combo_box->clear();
+ ui->output_sink_combo_box->addItem("auto");
+ for (const auto& sink_detail : AudioCore::g_sink_details) {
+ ui->output_sink_combo_box->addItem(sink_detail.id);
+ }
+
+ this->setConfiguration();
+}
+
+ConfigureAudio::~ConfigureAudio() {
+}
+
+void ConfigureAudio::setConfiguration() {
+ int new_sink_index = 0;
+ for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
+ if (ui->output_sink_combo_box->itemText(index).toStdString() == Settings::values.sink_id) {
+ new_sink_index = index;
+ break;
+ }
+ }
+ ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
+}
+
+void ConfigureAudio::applyConfiguration() {
+ Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString();
+ Settings::Apply();
+}
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+
+namespace Ui {
+class ConfigureAudio;
+}
+
+class ConfigureAudio : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureAudio(QWidget* parent = nullptr);
+ ~ConfigureAudio();
+
+ void applyConfiguration();
+
+private:
+ void setConfiguration();
+
+ std::unique_ptr<Ui::ConfigureAudio> ui;
+};
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<ui version="4.0">
+ <class>ConfigureAudio</class>
+ <widget class="QWidget" name="ConfigureAudio">
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox">
+ <property name="title">
+ <string>Audio</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel">
+ <property name="text">
+ <string>Output Engine:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="output_sink_combo_box">
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources />
+ <connections />
+</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() {
void ConfigureDialog::applyConfiguration() {
ui->generalTab->applyConfiguration();
+ ui->audioTab->applyConfiguration();
ui->debugTab->applyConfiguration();
}
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:
/// This timer is used to redraw the widget's contents continuously. To save resources, it only
/// runs while the widget is visible.
QTimer update_timer;
+ /// Scale the coordinate system appropriately when physical DPI != logical DPI.
+ qreal x_scale, y_scale;
};
#endif
@@ -220,11 +222,17 @@ MicroProfileWidget::MicroProfileWidget(QWidget* parent) : QWidget(parent) {
MicroProfileInitUI();
connect(&update_timer, SIGNAL(timeout()), SLOT(update()));
+
+ QPainter painter(this);
+ x_scale = qreal(painter.device()->physicalDpiX()) / qreal(painter.device()->logicalDpiX());
+ y_scale = qreal(painter.device()->physicalDpiY()) / qreal(painter.device()->logicalDpiY());
}
void MicroProfileWidget::paintEvent(QPaintEvent* ev) {
QPainter painter(this);
+ painter.scale(x_scale, y_scale);
+
painter.setBackground(Qt::black);
painter.eraseRect(rect());
@@ -248,24 +256,24 @@ void MicroProfileWidget::hideEvent(QHideEvent* ev) {
}
void MicroProfileWidget::mouseMoveEvent(QMouseEvent* ev) {
- MicroProfileMousePosition(ev->x(), ev->y(), 0);
+ MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
ev->accept();
}
void MicroProfileWidget::mousePressEvent(QMouseEvent* ev) {
- MicroProfileMousePosition(ev->x(), ev->y(), 0);
+ MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
ev->accept();
}
void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) {
- MicroProfileMousePosition(ev->x(), ev->y(), 0);
+ MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, 0);
MicroProfileMouseButton(ev->buttons() & Qt::LeftButton, ev->buttons() & Qt::RightButton);
ev->accept();
}
void MicroProfileWidget::wheelEvent(QWheelEvent* ev) {
- MicroProfileMousePosition(ev->x(), ev->y(), ev->delta() / 120);
+ MicroProfileMousePosition(ev->x() / x_scale, ev->y() / y_scale, ev->delta() / 120);
ev->accept();
}
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
hle/kernel/timer.cpp
hle/kernel/vm_manager.cpp
hle/service/ac_u.cpp
+ hle/service/act_a.cpp
hle/service/act_u.cpp
hle/service/am/am.cpp
hle/service/am/am_app.cpp
@@ -176,6 +177,7 @@ set(HEADERS
hle/kernel/vm_manager.h
hle/result.h
hle/service/ac_u.h
+ hle/service/act_a.h
hle/service/act_u.h
hle/service/am/am.h
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 @@
#include "common/common_types.h"
#include "core/arm/skyeye_common/arm_regformat.h"
+#include "core/arm/skyeye_common/vfp/asm_vfp.h"
namespace Core {
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) {
// SMUAD and SMLAD
if (BIT(op2, 1) == 0) {
- RD = (product1 + product2);
+ u32 rd_val = (product1 + product2);
if (inst_cream->Ra != 15) {
- RD += cpu->Reg[inst_cream->Ra];
+ rd_val += cpu->Reg[inst_cream->Ra];
if (ARMul_AddOverflowQ(product1 + product2, cpu->Reg[inst_cream->Ra]))
cpu->Cpsr |= (1 << 27);
}
+ RD = rd_val;
+
if (ARMul_AddOverflowQ(product1, product2))
cpu->Cpsr |= (1 << 27);
}
// SMUSD and SMLSD
else {
- RD = (product1 - product2);
+ u32 rd_val = (product1 - product2);
if (inst_cream->Ra != 15) {
- RD += cpu->Reg[inst_cream->Ra];
+ rd_val += cpu->Reg[inst_cream->Ra];
if (ARMul_AddOverflowQ(product1 - product2, cpu->Reg[inst_cream->Ra]))
cpu->Cpsr |= (1 << 27);
}
+
+ RD = rd_val;
}
}
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) {
SharedPtr<Thread> thread = thread_res.MoveFrom();
+ thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
+
// Run new "main" thread
SwitchContext(thread.get());
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/act_a.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace ACT_A
+
+namespace ACT_A {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x041300C2, nullptr, "UpdateMiiImage"},
+ {0x041B0142, nullptr, "AgreeEula"},
+ {0x04210042, nullptr, "UploadMii"},
+ {0x04230082, nullptr, "ValidateMailAddress"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable);
+}
+
+} // 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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace ACT_A
+
+namespace ACT_A {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "act:a";
+ }
+};
+
+} // 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 @@
namespace ACT_U {
const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010084, nullptr, "Initialize"},
+ {0x00020040, nullptr, "GetErrorCode"},
{0x000600C2, nullptr, "GetAccountDataBlock"},
+ {0x000D0040, nullptr, "GenerateUuid"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
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) {
cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 0; // Not using headphones?
+ cmd_buff[2] = 0; // Not using headphones
- LOG_WARNING(Service_DSP, "(STUBBED) called");
+ LOG_DEBUG(Service_DSP, "called");
}
/**
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 @@
#include "core/hle/service/service.h"
#include "core/hle/service/ac_u.h"
+#include "core/hle/service/act_a.h"
#include "core/hle/service/act_u.h"
#include "core/hle/service/csnd_snd.h"
#include "core/hle/service/dlp_srvr.h"
@@ -119,6 +120,7 @@ void Init() {
Service::PTM::Init();
AddService(new AC_U::Interface);
+ AddService(new ACT_A::Interface);
AddService(new ACT_U::Interface);
AddService(new CSND_SND::Interface);
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
CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(
name, entry_point, priority, arg, processor_id, stack_top));
+
+ thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000
+
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
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:
#endif
}
-void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages)
-{
+static std::string ReplacePattern(const std::string& input, const std::string& pattern, const std::string& replacement) {
+ size_t start = input.find(pattern);
+ if (start == std::string::npos)
+ return input;
+
+ std::string ret = input;
+ ret.replace(start, pattern.length(), replacement);
+ return ret;
+}
+
+static std::string GetTevStageConfigSourceString(const Pica::Regs::TevStageConfig::Source& source) {
using Source = Pica::Regs::TevStageConfig::Source;
+ static const std::map<Source, std::string> source_map = {
+ { Source::PrimaryColor, "PrimaryColor" },
+ { Source::PrimaryFragmentColor, "PrimaryFragmentColor" },
+ { Source::SecondaryFragmentColor, "SecondaryFragmentColor" },
+ { Source::Texture0, "Texture0" },
+ { Source::Texture1, "Texture1" },
+ { Source::Texture2, "Texture2" },
+ { Source::Texture3, "Texture3" },
+ { Source::PreviousBuffer, "PreviousBuffer" },
+ { Source::Constant, "Constant" },
+ { Source::Previous, "Previous" },
+ };
+
+ const auto src_it = source_map.find(source);
+ if (src_it == source_map.end())
+ return "Unknown";
+
+ return src_it->second;
+}
+
+static std::string GetTevStageConfigColorSourceString(const Pica::Regs::TevStageConfig::Source& source, const Pica::Regs::TevStageConfig::ColorModifier modifier) {
using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier;
+ static const std::map<ColorModifier, std::string> color_modifier_map = {
+ { ColorModifier::SourceColor, "%source.rgb" },
+ { ColorModifier::OneMinusSourceColor, "(1.0 - %source.rgb)" },
+ { ColorModifier::SourceAlpha, "%source.aaa" },
+ { ColorModifier::OneMinusSourceAlpha, "(1.0 - %source.aaa)" },
+ { ColorModifier::SourceRed, "%source.rrr" },
+ { ColorModifier::OneMinusSourceRed, "(1.0 - %source.rrr)" },
+ { ColorModifier::SourceGreen, "%source.ggg" },
+ { ColorModifier::OneMinusSourceGreen, "(1.0 - %source.ggg)" },
+ { ColorModifier::SourceBlue, "%source.bbb" },
+ { ColorModifier::OneMinusSourceBlue, "(1.0 - %source.bbb)" },
+ };
+
+ auto src_str = GetTevStageConfigSourceString(source);
+ auto modifier_it = color_modifier_map.find(modifier);
+ std::string modifier_str = "%source.????";
+ if (modifier_it != color_modifier_map.end())
+ modifier_str = modifier_it->second;
+
+ return ReplacePattern(modifier_str, "%source", src_str);
+}
+
+static std::string GetTevStageConfigAlphaSourceString(const Pica::Regs::TevStageConfig::Source& source, const Pica::Regs::TevStageConfig::AlphaModifier modifier) {
using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier;
+ static const std::map<AlphaModifier, std::string> alpha_modifier_map = {
+ { AlphaModifier::SourceAlpha, "%source.a" },
+ { AlphaModifier::OneMinusSourceAlpha, "(1.0 - %source.a)" },
+ { AlphaModifier::SourceRed, "%source.r" },
+ { AlphaModifier::OneMinusSourceRed, "(1.0 - %source.r)" },
+ { AlphaModifier::SourceGreen, "%source.g" },
+ { AlphaModifier::OneMinusSourceGreen, "(1.0 - %source.g)" },
+ { AlphaModifier::SourceBlue, "%source.b" },
+ { AlphaModifier::OneMinusSourceBlue, "(1.0 - %source.b)" },
+ };
+
+ auto src_str = GetTevStageConfigSourceString(source);
+ auto modifier_it = alpha_modifier_map.find(modifier);
+ std::string modifier_str = "%source.????";
+ if (modifier_it != alpha_modifier_map.end())
+ modifier_str = modifier_it->second;
+
+ return ReplacePattern(modifier_str, "%source", src_str);
+}
+
+static std::string GetTevStageConfigOperationString(const Pica::Regs::TevStageConfig::Operation& operation) {
using Operation = Pica::Regs::TevStageConfig::Operation;
+ static const std::map<Operation, std::string> combiner_map = {
+ { Operation::Replace, "%source1" },
+ { Operation::Modulate, "(%source1 * %source2)" },
+ { Operation::Add, "(%source1 + %source2)" },
+ { Operation::AddSigned, "(%source1 + %source2) - 0.5" },
+ { Operation::Lerp, "lerp(%source1, %source2, %source3)" },
+ { Operation::Subtract, "(%source1 - %source2)" },
+ { Operation::Dot3_RGB, "dot(%source1, %source2)" },
+ { Operation::MultiplyThenAdd, "((%source1 * %source2) + %source3)" },
+ { Operation::AddThenMultiply, "((%source1 + %source2) * %source3)" },
+ };
- std::string stage_info = "Tev setup:\n";
- for (size_t index = 0; index < stages.size(); ++index) {
- const auto& tev_stage = stages[index];
+ const auto op_it = combiner_map.find(operation);
+ if (op_it == combiner_map.end())
+ return "Unknown op (%source1, %source2, %source3)";
- static const std::map<Source, std::string> source_map = {
- { Source::PrimaryColor, "PrimaryColor" },
- { Source::Texture0, "Texture0" },
- { Source::Texture1, "Texture1" },
- { Source::Texture2, "Texture2" },
- { Source::Constant, "Constant" },
- { Source::Previous, "Previous" },
- };
+ return op_it->second;
+}
- static const std::map<ColorModifier, std::string> color_modifier_map = {
- { ColorModifier::SourceColor, { "%source.rgb" } },
- { ColorModifier::SourceAlpha, { "%source.aaa" } },
- };
- static const std::map<AlphaModifier, std::string> alpha_modifier_map = {
- { AlphaModifier::SourceAlpha, "%source.a" },
- { AlphaModifier::OneMinusSourceAlpha, "(255 - %source.a)" },
- };
+std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage) {
+ auto op_str = GetTevStageConfigOperationString(tev_stage.color_op);
+ op_str = ReplacePattern(op_str, "%source1", GetTevStageConfigColorSourceString(tev_stage.color_source1, tev_stage.color_modifier1));
+ op_str = ReplacePattern(op_str, "%source2", GetTevStageConfigColorSourceString(tev_stage.color_source2, tev_stage.color_modifier2));
+ return ReplacePattern(op_str, "%source3", GetTevStageConfigColorSourceString(tev_stage.color_source3, tev_stage.color_modifier3));
+}
- static const std::map<Operation, std::string> combiner_map = {
- { Operation::Replace, "%source1" },
- { Operation::Modulate, "(%source1 * %source2) / 255" },
- { Operation::Add, "(%source1 + %source2)" },
- { Operation::Lerp, "lerp(%source1, %source2, %source3)" },
- };
+std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage) {
+ auto op_str = GetTevStageConfigOperationString(tev_stage.alpha_op);
+ op_str = ReplacePattern(op_str, "%source1", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source1, tev_stage.alpha_modifier1));
+ op_str = ReplacePattern(op_str, "%source2", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source2, tev_stage.alpha_modifier2));
+ return ReplacePattern(op_str, "%source3", GetTevStageConfigAlphaSourceString(tev_stage.alpha_source3, tev_stage.alpha_modifier3));
+}
- static auto ReplacePattern =
- [](const std::string& input, const std::string& pattern, const std::string& replacement) -> std::string {
- size_t start = input.find(pattern);
- if (start == std::string::npos)
- return input;
-
- std::string ret = input;
- ret.replace(start, pattern.length(), replacement);
- return ret;
- };
- static auto GetColorSourceStr =
- [](const Source& src, const ColorModifier& modifier) {
- auto src_it = source_map.find(src);
- std::string src_str = "Unknown";
- if (src_it != source_map.end())
- src_str = src_it->second;
-
- auto modifier_it = color_modifier_map.find(modifier);
- std::string modifier_str = "%source.????";
- if (modifier_it != color_modifier_map.end())
- modifier_str = modifier_it->second;
-
- return ReplacePattern(modifier_str, "%source", src_str);
- };
- static auto GetColorCombinerStr =
- [](const Regs::TevStageConfig& tev_stage) {
- auto op_it = combiner_map.find(tev_stage.color_op);
- std::string op_str = "Unknown op (%source1, %source2, %source3)";
- if (op_it != combiner_map.end())
- op_str = op_it->second;
-
- op_str = ReplacePattern(op_str, "%source1", GetColorSourceStr(tev_stage.color_source1, tev_stage.color_modifier1));
- op_str = ReplacePattern(op_str, "%source2", GetColorSourceStr(tev_stage.color_source2, tev_stage.color_modifier2));
- return ReplacePattern(op_str, "%source3", GetColorSourceStr(tev_stage.color_source3, tev_stage.color_modifier3));
- };
- static auto GetAlphaSourceStr =
- [](const Source& src, const AlphaModifier& modifier) {
- auto src_it = source_map.find(src);
- std::string src_str = "Unknown";
- if (src_it != source_map.end())
- src_str = src_it->second;
-
- auto modifier_it = alpha_modifier_map.find(modifier);
- std::string modifier_str = "%source.????";
- if (modifier_it != alpha_modifier_map.end())
- modifier_str = modifier_it->second;
-
- return ReplacePattern(modifier_str, "%source", src_str);
- };
- static auto GetAlphaCombinerStr =
- [](const Regs::TevStageConfig& tev_stage) {
- auto op_it = combiner_map.find(tev_stage.alpha_op);
- std::string op_str = "Unknown op (%source1, %source2, %source3)";
- if (op_it != combiner_map.end())
- op_str = op_it->second;
-
- op_str = ReplacePattern(op_str, "%source1", GetAlphaSourceStr(tev_stage.alpha_source1, tev_stage.alpha_modifier1));
- op_str = ReplacePattern(op_str, "%source2", GetAlphaSourceStr(tev_stage.alpha_source2, tev_stage.alpha_modifier2));
- return ReplacePattern(op_str, "%source3", GetAlphaSourceStr(tev_stage.alpha_source3, tev_stage.alpha_modifier3));
- };
-
- stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n";
+void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages) {
+ std::string stage_info = "Tev setup:\n";
+ for (size_t index = 0; index < stages.size(); ++index) {
+ const auto& tev_stage = stages[index];
+ stage_info += "Stage " + std::to_string(index) + ": " + GetTevStageConfigColorCombinerString(tev_stage) + " " + GetTevStageConfigAlphaCombinerString(tev_stage) + "\n";
}
-
LOG_TRACE(HW_GPU, "%s", stage_info.c_str());
}
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
void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
-void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages);
+std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
+std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
+
+/// Dumps the Tev stage config to log at trace level
+void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages);
/**
* 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) {
// Sync fixed function OpenGL state
SyncCullMode();
- SyncDepthModifiers();
SyncBlendEnabled();
SyncBlendFuncs();
SyncBlendColor();
@@ -259,8 +258,10 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
// Depth modifiers
case PICA_REG_INDEX(viewport_depth_range):
+ SyncDepthScale();
+ break;
case PICA_REG_INDEX(viewport_depth_near_plane):
- SyncDepthModifiers();
+ SyncDepthOffset();
break;
// Depth buffering
@@ -880,6 +881,8 @@ void RasterizerOpenGL::SetShader() {
glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
// Update uniforms
+ SyncDepthScale();
+ SyncDepthOffset();
SyncAlphaTest();
SyncCombinerColor();
auto& tev_stages = Pica::g_state.regs.GetTevStages();
@@ -922,13 +925,20 @@ void RasterizerOpenGL::SyncCullMode() {
}
}
-void RasterizerOpenGL::SyncDepthModifiers() {
+void RasterizerOpenGL::SyncDepthScale() {
float depth_scale = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32();
- float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_near_plane).ToFloat32();
+ if (depth_scale != uniform_block_data.data.depth_scale) {
+ uniform_block_data.data.depth_scale = depth_scale;
+ uniform_block_data.dirty = true;
+ }
+}
- uniform_block_data.data.depth_scale = depth_scale;
- uniform_block_data.data.depth_offset = depth_offset;
- uniform_block_data.dirty = true;
+void RasterizerOpenGL::SyncDepthOffset() {
+ float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_near_plane).ToFloat32();
+ if (depth_offset != uniform_block_data.data.depth_offset) {
+ uniform_block_data.data.depth_offset = depth_offset;
+ uniform_block_data.dirty = true;
+ }
}
void RasterizerOpenGL::SyncBlendEnabled() {
@@ -937,6 +947,8 @@ void RasterizerOpenGL::SyncBlendEnabled() {
void RasterizerOpenGL::SyncBlendFuncs() {
const auto& regs = Pica::g_state.regs;
+ state.blend.rgb_equation = PicaToGL::BlendEquation(regs.output_merger.alpha_blending.blend_equation_rgb);
+ state.blend.a_equation = PicaToGL::BlendEquation(regs.output_merger.alpha_blending.blend_equation_a);
state.blend.src_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_rgb);
state.blend.dst_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_rgb);
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:
/// Syncs the cull mode to match the PICA register
void SyncCullMode();
- /// Syncs the depth scale and offset to match the PICA registers
- void SyncDepthModifiers();
+ /// Syncs the depth scale to match the PICA register
+ void SyncDepthScale();
+
+ /// Syncs the depth offset to match the PICA register
+ void SyncDepthOffset();
/// Syncs the blend enabled status to match the PICA register
void SyncBlendEnabled();
@@ -413,7 +416,7 @@ private:
UniformData data;
bool lut_dirty[6];
bool dirty;
- } uniform_block_data;
+ } uniform_block_data = {};
std::array<SamplerInfo, 3> texture_samplers;
OGLVertexArray vertex_array;
@@ -422,5 +425,5 @@ private:
OGLFramebuffer framebuffer;
std::array<OGLTexture, 6> lighting_luts;
- std::array<std::array<GLvec4, 256>, 6> lighting_lut_data;
+ std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{};
};
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() {
stencil.action_stencil_fail = GL_KEEP;
blend.enabled = false;
+ blend.rgb_equation = GL_FUNC_ADD;
+ blend.a_equation = GL_FUNC_ADD;
blend.src_rgb_func = GL_ONE;
blend.dst_rgb_func = GL_ZERO;
blend.src_a_func = GL_ONE;
@@ -165,6 +167,11 @@ void OpenGLState::Apply() const {
blend.src_a_func, blend.dst_a_func);
}
+ if (blend.rgb_equation != cur_state.blend.rgb_equation ||
+ blend.a_equation != cur_state.blend.a_equation) {
+ glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
+ }
+
if (logic_op != cur_state.logic_op) {
glLogicOp(logic_op);
}
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:
struct {
bool enabled; // GL_BLEND
+ GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
+ GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
GLenum src_rgb_func; // GL_BLEND_SRC_RGB
GLenum dst_rgb_func; // GL_BLEND_DST_RGB
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) {
return gl_mode;
}
+inline GLenum BlendEquation(Pica::Regs::BlendEquation equation) {
+ static const GLenum blend_equation_table[] = {
+ GL_FUNC_ADD, // BlendEquation::Add
+ GL_FUNC_SUBTRACT, // BlendEquation::Subtract
+ GL_FUNC_REVERSE_SUBTRACT, // BlendEquation::ReverseSubtract
+ GL_MIN, // BlendEquation::Min
+ GL_MAX, // BlendEquation::Max
+ };
+
+ // Range check table for input
+ if (static_cast<size_t>(equation) >= ARRAY_SIZE(blend_equation_table)) {
+ LOG_CRITICAL(Render_OpenGL, "Unknown blend equation %d", equation);
+ UNREACHABLE();
+
+ return GL_FUNC_ADD;
+ }
+
+ return blend_equation_table[(unsigned)equation];
+}
+
inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) {
static const GLenum blend_func_table[] = {
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));
OutputVertex ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num_attributes) {
auto& config = g_state.regs.vs;
+ auto& setup = g_state.vs;
MICROPROFILE_SCOPE(GPU_Shader);
@@ -81,11 +82,11 @@ OutputVertex ShaderSetup::Run(UnitState<false>& state, const InputVertex& input,
#ifdef ARCHITECTURE_x86_64
if (VideoCore::g_shader_jit_enabled)
- jit_shader->Run(&state.registers, g_state.regs.vs.main_offset);
+ jit_shader->Run(setup, state, config.main_offset);
else
- RunInterpreter(state);
+ RunInterpreter(setup, state, config.main_offset);
#else
- RunInterpreter(state);
+ RunInterpreter(setup, state, config.main_offset);
#endif // ARCHITECTURE_x86_64
// Setup output data
@@ -156,7 +157,7 @@ DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_
state.conditional_code[0] = false;
state.conditional_code[1] = false;
- RunInterpreter(state);
+ RunInterpreter(setup, state, config.main_offset);
return state.debug;
}
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 {
static size_t InputOffset(const SourceRegister& reg) {
switch (reg.GetRegisterType()) {
case RegisterType::Input:
- return offsetof(UnitState::Registers, input) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+ return offsetof(UnitState, registers.input) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
case RegisterType::Temporary:
- return offsetof(UnitState::Registers, temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+ return offsetof(UnitState, registers.temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
default:
UNREACHABLE();
@@ -297,10 +297,10 @@ struct UnitState {
static size_t OutputOffset(const DestRegister& reg) {
switch (reg.GetRegisterType()) {
case RegisterType::Output:
- return offsetof(UnitState::Registers, output) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+ return offsetof(UnitState, registers.output) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
case RegisterType::Temporary:
- return offsetof(UnitState::Registers, temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
+ return offsetof(UnitState, registers.temporary) + reg.GetIndex()*sizeof(Math::Vec4<float24>);
default:
UNREACHABLE();
@@ -323,6 +323,23 @@ struct ShaderSetup {
std::array<Math::Vec4<u8>, 4> i;
} uniforms;
+ static size_t UniformOffset(RegisterType type, unsigned index) {
+ switch (type) {
+ case RegisterType::FloatUniform:
+ return offsetof(ShaderSetup, uniforms.f) + index*sizeof(Math::Vec4<float24>);
+
+ case RegisterType::BoolUniform:
+ return offsetof(ShaderSetup, uniforms.b) + index*sizeof(bool);
+
+ case RegisterType::IntUniform:
+ return offsetof(ShaderSetup, uniforms.i) + index*sizeof(Math::Vec4<u8>);
+
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+ }
+
std::array<u32, 1024> program_code;
std::array<u32, 1024> swizzle_data;
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 {
};
template<bool Debug>
-void RunInterpreter(UnitState<Debug>& state) {
+void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset) {
// TODO: Is there a maximal size for this?
boost::container::static_vector<CallStackElement, 16> call_stack;
- u32 program_counter = g_state.regs.vs.main_offset;
+ u32 program_counter = offset;
const auto& uniforms = g_state.vs.uniforms;
const auto& swizzle_data = g_state.vs.swizzle_data;
@@ -647,8 +647,8 @@ void RunInterpreter(UnitState<Debug>& state) {
}
// Explicit instantiation
-template void RunInterpreter(UnitState<false>& state);
-template void RunInterpreter(UnitState<true>& state);
+template void RunInterpreter(const ShaderSetup& setup, UnitState<false>& state, unsigned offset);
+template void RunInterpreter(const ShaderSetup& setup, UnitState<true>& state, unsigned offset);
} // namespace
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 {
template <bool Debug> struct UnitState;
template<bool Debug>
-void RunInterpreter(UnitState<Debug>& state);
+void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset);
} // namespace
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] = {
// purposes, as documented below:
/// Pointer to the uniform memory
-static const X64Reg UNIFORMS = R9;
+static const X64Reg SETUP = R9;
/// The two 32-bit VS address offset registers set by the MOVA instruction
static const X64Reg ADDROFFS_REG_0 = R10;
static const X64Reg ADDROFFS_REG_1 = R11;
@@ -117,7 +117,7 @@ static const X64Reg COND0 = R13;
/// Result of the previous CMP instruction for the Y-component comparison
static const X64Reg COND1 = R14;
/// Pointer to the UnitState instance for the current VS unit
-static const X64Reg REGISTERS = R15;
+static const X64Reg STATE = R15;
/// SIMD scratch register
static const X64Reg SCRATCH = XMM0;
/// Loaded with the first swizzled source register, otherwise can be used as a scratch register
@@ -136,7 +136,7 @@ static const X64Reg NEGBIT = XMM15;
// State registers that must not be modified by external functions calls
// Scratch registers, e.g., SRC1 and SCRATCH, have to be saved on the side if needed
static const BitSet32 persistent_regs = {
- UNIFORMS, REGISTERS, // Pointers to register blocks
+ SETUP, STATE, // Pointers to register blocks
ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, // Cached registers
ONE+16, NEGBIT+16, // Constants
};
@@ -177,10 +177,10 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe
size_t src_offset;
if (src_reg.GetRegisterType() == RegisterType::FloatUniform) {
- src_ptr = UNIFORMS;
- src_offset = src_reg.GetIndex() * sizeof(float24) * 4;
+ src_ptr = SETUP;
+ src_offset = ShaderSetup::UniformOffset(RegisterType::FloatUniform, src_reg.GetIndex());
} else {
- src_ptr = REGISTERS;
+ src_ptr = STATE;
src_offset = UnitState<false>::InputOffset(src_reg);
}
@@ -264,11 +264,11 @@ void JitShader::Compile_DestEnable(Instruction instr,X64Reg src) {
// If all components are enabled, write the result to the destination register
if (swiz.dest_mask == NO_DEST_REG_MASK) {
// Store dest back to memory
- MOVAPS(MDisp(REGISTERS, dest_offset_disp), src);
+ MOVAPS(MDisp(STATE, dest_offset_disp), src);
} else {
// Not all components are enabled, so mask the result when storing to the destination register...
- MOVAPS(SCRATCH, MDisp(REGISTERS, dest_offset_disp));
+ MOVAPS(SCRATCH, MDisp(STATE, dest_offset_disp));
if (Common::GetCPUCaps().sse4_1) {
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) {
}
// Store dest back to memory
- MOVAPS(MDisp(REGISTERS, dest_offset_disp), SCRATCH);
+ MOVAPS(MDisp(STATE, dest_offset_disp), SCRATCH);
}
}
@@ -336,8 +336,8 @@ void JitShader::Compile_EvaluateCondition(Instruction instr) {
}
void JitShader::Compile_UniformCondition(Instruction instr) {
- int offset = offsetof(decltype(g_state.vs.uniforms), b) + (instr.flow_control.bool_uniform_id * sizeof(bool));
- CMP(sizeof(bool) * 8, MDisp(UNIFORMS, offset), Imm8(0));
+ int offset = ShaderSetup::UniformOffset(RegisterType::BoolUniform, instr.flow_control.bool_uniform_id);
+ CMP(sizeof(bool) * 8, MDisp(SETUP, offset), Imm8(0));
}
BitSet32 JitShader::PersistentCallerSavedRegs() {
@@ -714,8 +714,8 @@ void JitShader::Compile_LOOP(Instruction instr) {
looping = true;
- int offset = offsetof(decltype(g_state.vs.uniforms), i) + (instr.flow_control.int_uniform_id * sizeof(Math::Vec4<u8>));
- MOV(32, R(LOOPCOUNT), MDisp(UNIFORMS, offset));
+ int offset = ShaderSetup::UniformOffset(RegisterType::IntUniform, instr.flow_control.int_uniform_id);
+ MOV(32, R(LOOPCOUNT), MDisp(SETUP, offset));
MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT));
SHR(32, R(LOOPCOUNT_REG), Imm8(8));
AND(32, R(LOOPCOUNT_REG), Imm32(0xff)); // Y-component is the start
@@ -826,8 +826,8 @@ void JitShader::Compile() {
// The stack pointer is 8 modulo 16 at the entry of a procedure
ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
- MOV(PTRBITS, R(REGISTERS), R(ABI_PARAM1));
- MOV(PTRBITS, R(UNIFORMS), ImmPtr(&g_state.vs.uniforms));
+ MOV(PTRBITS, R(SETUP), R(ABI_PARAM1));
+ MOV(PTRBITS, R(STATE), R(ABI_PARAM2));
// Zero address/loop registers
XOR(64, R(ADDROFFS_REG_0), R(ADDROFFS_REG_0));
@@ -845,7 +845,7 @@ void JitShader::Compile() {
MOVAPS(NEGBIT, MatR(RAX));
// Jump to start of the shader program
- JMPptr(R(ABI_PARAM2));
+ JMPptr(R(ABI_PARAM3));
// Compile entire program
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 {
public:
JitShader();
- void Run(void* registers, unsigned offset) const {
- program(registers, code_ptr[offset]);
+ void Run(const ShaderSetup& setup, UnitState<false>& state, unsigned offset) const {
+ program(&setup, &state, code_ptr[offset]);
}
void Compile();
@@ -117,7 +117,7 @@ private:
/// Branches that need to be fixed up once the entire shader program is compiled
std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches;
- using CompiledShader = void(void* registers, const u8* start_addr);
+ using CompiledShader = void(const void* setup, void* state, const u8* start_addr);
CompiledShader* program = nullptr;
};