diff options
76 files changed, 1447 insertions, 472 deletions
diff --git a/.gitmodules b/.gitmodules index db0905b3d..1f0b80768 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "soundtouch"] path = externals/soundtouch url = https://github.com/citra-emu/ext-soundtouch.git +[submodule "catch"] + path = externals/catch + url = https://github.com/philsquared/Catch.git diff --git a/.travis-build.sh b/.travis-build.sh index e06a4299b..511df04ac 100755 --- a/.travis-build.sh +++ b/.travis-build.sh @@ -18,9 +18,16 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then mkdir build && cd build cmake -DCITRA_FORCE_QT4=ON .. make -j4 + + ctest -VV -C Release elif [ "$TRAVIS_OS_NAME" = "osx" ]; then + set -o pipefail + export Qt5_DIR=$(brew --prefix)/opt/qt5 + mkdir build && cd build cmake .. -GXcode - xcodebuild -configuration Release | xcpretty -c && exit ${PIPESTATUS[0]} + xcodebuild -configuration Release | xcpretty -c + + ctest -VV -C Release fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f2898973..f7b0af115 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,6 +255,8 @@ endif() add_subdirectory(externals/soundtouch) +enable_testing() + add_subdirectory(src) # Install freedesktop.org metadata files, following those specifications: diff --git a/appveyor.yml b/appveyor.yml index 1edd7041f..fa4134384 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,7 +22,14 @@ before_build: - cmake -G "Visual Studio 14 2015 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 .. - cd .. -after_build: +build: + project: build/citra.sln + parallel: true + +test_script: + - cd build && ctest -VV -C Release && cd .. + +on_success: # copying the needed QT Dlls is now done post build. See the CMakeLists.txt file in the citra-qt folder - ps: > if (!"$env:APPVEYOR_PULL_REQUEST_TITLE" -and ("$env:APPVEYOR_REPO_BRANCH" -eq "master")) diff --git a/externals/catch b/externals/catch new file mode 160000 +Subproject c984fc3ecde60b59efa2203e82261acac8ac850 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de4fe716a..1e1245160 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(common) add_subdirectory(core) add_subdirectory(video_core) add_subdirectory(audio_core) +add_subdirectory(tests) if (ENABLE_SDL2) add_subdirectory(citra) endif() 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/citra.cpp b/src/citra/citra.cpp index b4501eb2e..e01216734 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -114,7 +114,13 @@ int main(int argc, char **argv) { System::Init(emu_window.get()); SCOPE_EXIT({ System::Shutdown(); }); - Loader::ResultStatus load_result = Loader::LoadFile(boot_filename); + std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(boot_filename); + if (!loader) { + LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", boot_filename.c_str()); + return -1; + } + + Loader::ResultStatus load_result = loader->Load(); if (Loader::ResultStatus::Success != load_result) { LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result); return -1; diff --git a/src/citra/config.cpp b/src/citra/config.cpp index c5cb4fb38..c64de8e22 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -77,8 +77,9 @@ void Config::ReadValues() { // Data Storage Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); - // System Region - Settings::values.region_value = sdl2_config->GetInteger("System Region", "region_value", 1); + // System + Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false); + Settings::values.region_value = sdl2_config->GetInteger("System", "region_value", 1); // Miscellaneous Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info"); 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/config.cpp b/src/citra_qt/config.cpp index b5bb75537..6e4ba3907 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -60,7 +60,8 @@ void Config::ReadValues() { Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); qt_config->endGroup(); - qt_config->beginGroup("System Region"); + qt_config->beginGroup("System"); + Settings::values.is_new_3ds = qt_config->value("is_new_3ds", false).toBool(); Settings::values.region_value = qt_config->value("region_value", 1).toInt(); qt_config->endGroup(); @@ -150,7 +151,8 @@ void Config::SaveValues() { qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); qt_config->endGroup(); - qt_config->beginGroup("System Region"); + qt_config->beginGroup("System"); + qt_config->setValue("is_new_3ds", Settings::values.is_new_3ds); qt_config->setValue("region_value", Settings::values.region_value); qt_config->endGroup(); 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/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index d4ac9c96e..570647539 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -132,30 +132,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d if (deep_scan && FileUtil::IsDirectory(physical_name)) { AddFstEntriesToGameList(physical_name, true); } else { - std::string filename_filename, filename_extension; - Common::SplitPath(physical_name, nullptr, &filename_filename, &filename_extension); - - Loader::FileType guessed_filetype = Loader::GuessFromExtension(filename_extension); - if (guessed_filetype == Loader::FileType::Unknown) - return true; - Loader::FileType filetype = Loader::IdentifyFile(physical_name); - if (filetype == Loader::FileType::Unknown) { - LOG_WARNING(Frontend, "File %s is of indeterminate type and is possibly corrupted.", physical_name.c_str()); + std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); + if (!loader) return true; - } - if (guessed_filetype != filetype) { - LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str()); - } std::vector<u8> smdh; - std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(FileUtil::IOFile(physical_name, "rb"), filetype, filename_filename, physical_name); - - if (loader) - loader->ReadIcon(smdh); + loader->ReadIcon(smdh); emit EntryReady({ new GameListItemPath(QString::fromStdString(physical_name), smdh), - new GameListItem(QString::fromStdString(Loader::GetFileTypeString(filetype))), + new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), new GameListItemSize(FileUtil::GetSize(physical_name)), }); } diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index 284f5da81..121f90b0c 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h @@ -15,52 +15,21 @@ #include "common/string_util.h" #include "common/color.h" -#include "core/loader/loader.h" +#include "core/loader/smdh.h" #include "video_core/utils.h" /** - * Tests if data is a valid SMDH by its length and magic number. - * @param smdh_data data buffer to test - * @return bool test result - */ -static bool IsValidSMDH(const std::vector<u8>& smdh_data) { - if (smdh_data.size() < sizeof(Loader::SMDH)) - return false; - - u32 magic; - memcpy(&magic, smdh_data.data(), 4); - - return Loader::MakeMagic('S', 'M', 'D', 'H') == magic; -} - -/** * Gets game icon from SMDH * @param sdmh SMDH data * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) * @return QPixmap game icon */ -static QPixmap GetIconFromSMDH(const Loader::SMDH& smdh, bool large) { - u32 size; - const u8* icon_data; - - if (large) { - size = 48; - icon_data = smdh.large_icon.data(); - } else { - size = 24; - icon_data = smdh.small_icon.data(); - } - - QImage icon(size, size, QImage::Format::Format_RGB888); - for (u32 x = 0; x < size; ++x) { - for (u32 y = 0; y < size; ++y) { - u32 coarse_y = y & ~7; - auto v = Color::DecodeRGB565( - icon_data + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * size * 2); - icon.setPixel(x, y, qRgb(v.r(), v.g(), v.b())); - } - } +static QPixmap GetQPixmapFromSMDH(const Loader::SMDH& smdh, bool large) { + std::vector<u16> icon_data = smdh.GetIcon(large); + const uchar* data = reinterpret_cast<const uchar*>(icon_data.data()); + int size = large ? 48 : 24; + QImage icon(data, size, size, QImage::Format::Format_RGB16); return QPixmap::fromImage(icon); } @@ -82,8 +51,8 @@ static QPixmap GetDefaultIcon(bool large) { * @param language title language * @return QString short title */ -static QString GetShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { - return QString::fromUtf16(smdh.titles[static_cast<int>(language)].short_title.data()); +static QString GetQStringShortTitleFromSMDH(const Loader::SMDH& smdh, Loader::SMDH::TitleLanguage language) { + return QString::fromUtf16(smdh.GetShortTitle(language).data()); } class GameListItem : public QStandardItem { @@ -112,7 +81,7 @@ public: { setData(game_path, FullPathRole); - if (!IsValidSMDH(smdh_data)) { + if (!Loader::IsValidSMDH(smdh_data)) { // SMDH is not valid, set a default icon setData(GetDefaultIcon(true), Qt::DecorationRole); return; @@ -122,10 +91,10 @@ public: memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); // Get icon from SMDH - setData(GetIconFromSMDH(smdh, true), Qt::DecorationRole); + setData(GetQPixmapFromSMDH(smdh, true), Qt::DecorationRole); // Get title form SMDH - setData(GetShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); + setData(GetQStringShortTitleFromSMDH(smdh, Loader::SMDH::TitleLanguage::English), TitleRole); } QVariant data(int role) const override { diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index a85c94a4b..6239160bc 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -272,7 +272,15 @@ bool GMainWindow::InitializeSystem() { } bool GMainWindow::LoadROM(const std::string& filename) { - Loader::ResultStatus result = Loader::LoadFile(filename); + std::unique_ptr<Loader::AppLoader> app_loader = Loader::GetLoader(filename); + if (!app_loader) { + LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filename.c_str()); + QMessageBox::critical(this, tr("Error while loading ROM!"), + tr("The ROM format is not supported.")); + return false; + } + + Loader::ResultStatus result = app_loader->Load(); if (Loader::ResultStatus::Success != result) { LOG_CRITICAL(Frontend, "Failed to load ROM!"); System::Shutdown(); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f6a7566bf..ed80cf0e4 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 @@ -120,6 +121,7 @@ set(SRCS loader/elf.cpp loader/loader.cpp loader/ncch.cpp + loader/smdh.cpp tracer/recorder.cpp memory.cpp settings.cpp @@ -176,6 +178,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 @@ -254,6 +257,7 @@ set(HEADERS loader/elf.h loader/loader.h loader/ncch.h + loader/smdh.h tracer/recorder.h tracer/citrace.h memory.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/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h index 210972917..68714800c 100644 --- a/src/core/arm/skyeye_common/vfp/vfp_helper.h +++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h @@ -271,8 +271,9 @@ inline int vfp_single_type(const vfp_single* s) // Unpack a single-precision float. Note that this returns the magnitude // of the single-precision float mantissa with the 1. if necessary, // aligned to bit 30. -inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr) +inline u32 vfp_single_unpack(vfp_single* s, s32 val, u32 fpscr) { + u32 exceptions = 0; s->sign = vfp_single_packed_sign(val) >> 16, s->exponent = vfp_single_packed_exponent(val); @@ -283,12 +284,13 @@ inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr) // If flush-to-zero mode is enabled, turn the denormal into zero. // On a VFPv2 architecture, the sign of the zero is always positive. - if ((*fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_single_type(s) & VFP_DENORMAL) != 0) { + if ((fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_single_type(s) & VFP_DENORMAL) != 0) { s->sign = 0; s->exponent = 0; s->significand = 0; - *fpscr |= FPSCR_IDC; + exceptions |= FPSCR_IDC; } + return exceptions; } // Re-pack a single-precision float. This assumes that the float is @@ -302,7 +304,7 @@ inline s32 vfp_single_pack(const vfp_single* s) } -u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, u32 exceptions, const char* func); +u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, const char* func); // Double-precision struct vfp_double { @@ -357,8 +359,9 @@ inline int vfp_double_type(const vfp_double* s) // Unpack a double-precision float. Note that this returns the magnitude // of the double-precision float mantissa with the 1. if necessary, // aligned to bit 62. -inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr) +inline u32 vfp_double_unpack(vfp_double* s, s64 val, u32 fpscr) { + u32 exceptions = 0; s->sign = vfp_double_packed_sign(val) >> 48; s->exponent = vfp_double_packed_exponent(val); @@ -369,12 +372,13 @@ inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr) // If flush-to-zero mode is enabled, turn the denormal into zero. // On a VFPv2 architecture, the sign of the zero is always positive. - if ((*fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_double_type(s) & VFP_DENORMAL) != 0) { + if ((fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_double_type(s) & VFP_DENORMAL) != 0) { s->sign = 0; s->exponent = 0; s->significand = 0; - *fpscr |= FPSCR_IDC; + exceptions |= FPSCR_IDC; } + return exceptions; } // Re-pack a double-precision float. This assumes that the float is @@ -447,4 +451,4 @@ inline u32 fls(u32 x) u32 vfp_double_multiply(vfp_double* vdd, vfp_double* vdn, vfp_double* vdm, u32 fpscr); u32 vfp_double_add(vfp_double* vdd, vfp_double* vdn, vfp_double *vdm, u32 fpscr); -u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, u32 exceptions, const char* func); +u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, const char* func); diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp index a2a625abc..1d5641810 100644 --- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp @@ -85,11 +85,12 @@ static void vfp_double_normalise_denormal(struct vfp_double *vd) vfp_double_dump("normalise_denormal: out", vd); } -u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func) +u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, u32 fpscr, const char *func) { u64 significand, incr; int exponent, shift, underflow; u32 rmode; + u32 exceptions = 0; vfp_double_dump("pack: in", vd); @@ -291,8 +292,9 @@ static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32 LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); vfp_double vdm, vdd, *vdp; int ret, tm; + u32 exceptions = 0; - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); tm = vfp_double_type(&vdm); if (tm & (VFP_NAN|VFP_INFINITY)) { @@ -369,7 +371,8 @@ sqrt_invalid: } vdd.significand = vfp_shiftright64jamming(vdd.significand, 1); - return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fsqrt"); + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fsqrt"); + return exceptions; } /* @@ -475,7 +478,7 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32 u32 exceptions = 0; LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); tm = vfp_double_type(&vdm); @@ -504,7 +507,8 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32 else vsd.exponent = vdm.exponent - (1023 - 127); - return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fcvts"); + exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fcvts"); + return exceptions; pack_nan: vfp_put_float(state, vfp_single_pack(&vsd), sd); @@ -514,6 +518,7 @@ pack_nan: static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) { struct vfp_double vdm; + u32 exceptions = 0; u32 m = vfp_get_float(state, dm); LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); @@ -521,12 +526,14 @@ static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32 vdm.exponent = 1023 + 63 - 1; vdm.significand = (u64)m; - return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fuito"); + exceptions |= vfp_double_normaliseround(state, dd, &vdm, fpscr, "fuito"); + return exceptions; } static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) { struct vfp_double vdm; + u32 exceptions = 0; u32 m = vfp_get_float(state, dm); LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); @@ -534,7 +541,8 @@ static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32 vdm.exponent = 1023 + 63 - 1; vdm.significand = vdm.sign ? (~m + 1) : m; - return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fsito"); + exceptions |= vfp_double_normaliseround(state, dd, &vdm, fpscr, "fsito"); + return exceptions; } static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32 fpscr) @@ -545,7 +553,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32 int tm; LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); /* * Do we have a denormalised number? @@ -634,7 +642,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32 int tm; LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); vfp_double_dump("VDM", &vdm); /* @@ -907,21 +915,21 @@ static u32 vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 fpscr, u32 negate, const char *func) { struct vfp_double vdd, vdp, vdn, vdm; - u32 exceptions; + u32 exceptions = 0; - vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); + exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); - exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr); + exceptions |= vfp_double_multiply(&vdp, &vdn, &vdm, fpscr); if (negate & NEG_MULTIPLY) vdp.sign = vfp_sign_negate(vdp.sign); - vfp_double_unpack(&vdn, vfp_get_double(state, dd), &fpscr); + exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dd), fpscr); if (vdn.exponent == 0 && vdn.significand != 0) vfp_double_normalise_denormal(&vdn); @@ -930,7 +938,8 @@ vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 f exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr); - return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, func); + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, func); + return exceptions; } /* @@ -979,19 +988,21 @@ static u32 vfp_double_fnmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpsc static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr) { struct vfp_double vdd, vdn, vdm; - u32 exceptions; + u32 exceptions = 0; LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); + exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); - exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); - return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fmul"); + exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); + + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fmul"); + return exceptions; } /* @@ -1000,21 +1011,22 @@ static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr) { struct vfp_double vdd, vdn, vdm; - u32 exceptions; + u32 exceptions = 0; LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); + exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); - exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); + exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); vdd.sign = vfp_sign_negate(vdd.sign); - return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fnmul"); + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fnmul"); + return exceptions; } /* @@ -1023,20 +1035,21 @@ static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpsc static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr) { struct vfp_double vdd, vdn, vdm; - u32 exceptions; + u32 exceptions = 0; LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); + exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); - exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr); + exceptions |= vfp_double_add(&vdd, &vdn, &vdm, fpscr); - return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fadd"); + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fadd"); + return exceptions; } /* @@ -1045,14 +1058,14 @@ static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr) { struct vfp_double vdd, vdn, vdm; - u32 exceptions; + u32 exceptions = 0; LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); + exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr); if (vdn.exponent == 0 && vdn.significand) vfp_double_normalise_denormal(&vdn); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); if (vdm.exponent == 0 && vdm.significand) vfp_double_normalise_denormal(&vdm); @@ -1061,9 +1074,10 @@ static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr */ vdm.sign = vfp_sign_negate(vdm.sign); - exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr); + exceptions |= vfp_double_add(&vdd, &vdn, &vdm, fpscr); - return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fsub"); + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fsub"); + return exceptions; } /* @@ -1076,8 +1090,8 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr int tm, tn; LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); - vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr); - vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr); + exceptions |= vfp_double_unpack(&vdn, vfp_get_double(state, dn), fpscr); + exceptions |= vfp_double_unpack(&vdm, vfp_get_double(state, dm), fpscr); vdd.sign = vdn.sign ^ vdm.sign; @@ -1146,16 +1160,18 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr } vdd.significand |= (reml != 0); } - return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fdiv"); + + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fdiv"); + return exceptions; vdn_nan: - exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr); + exceptions |= vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr); pack: vfp_put_double(state, vfp_double_pack(&vdd), dd); return exceptions; vdm_nan: - exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr); + exceptions |= vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr); goto pack; zero: @@ -1164,7 +1180,7 @@ zero: goto pack; divzero: - exceptions = FPSCR_DZC; + exceptions |= FPSCR_DZC; infinity: vdd.exponent = 2047; vdd.significand = 0; @@ -1172,7 +1188,8 @@ infinity: invalid: vfp_put_double(state, vfp_double_pack(&vfp_double_default_qnan), dd); - return FPSCR_IOC; + exceptions |= FPSCR_IOC; + return exceptions; } static struct op fops[] = { diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 6f6e0ca31..60264f9b3 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp @@ -89,10 +89,11 @@ static void vfp_single_normalise_denormal(struct vfp_single *vs) } -u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func) +u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs, u32 fpscr, const char *func) { u32 significand, incr, rmode; int exponent, shift, underflow; + u32 exceptions = 0; vfp_single_dump("pack: in", vs); @@ -334,8 +335,9 @@ static u32 vfp_single_fsqrt(ARMul_State* state, int sd, int unused, s32 m, u32 f { struct vfp_single vsm, vsd, *vsp; int ret, tm; + u32 exceptions = 0; - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); tm = vfp_single_type(&vsm); if (tm & (VFP_NAN|VFP_INFINITY)) { vsp = &vsd; @@ -408,7 +410,8 @@ sqrt_invalid: } vsd.significand = vfp_shiftright32jamming(vsd.significand, 1); - return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fsqrt"); + exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fsqrt"); + return exceptions; } /* @@ -503,7 +506,7 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f int tm; u32 exceptions = 0; - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); tm = vfp_single_type(&vsm); @@ -511,7 +514,7 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f * If we have a signalling NaN, signal invalid operation. */ if (tm == VFP_SNAN) - exceptions = FPSCR_IOC; + exceptions |= FPSCR_IOC; if (tm & VFP_DENORMAL) vfp_single_normalise_denormal(&vsm); @@ -532,7 +535,8 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f else vdd.exponent = vsm.exponent + (1023 - 127); - return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fcvtd"); + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fcvtd"); + return exceptions; pack_nan: vfp_put_double(state, vfp_double_pack(&vdd), dd); @@ -542,23 +546,27 @@ pack_nan: static u32 vfp_single_fuito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr) { struct vfp_single vs; + u32 exceptions = 0; vs.sign = 0; vs.exponent = 127 + 31 - 1; vs.significand = (u32)m; - return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fuito"); + exceptions |= vfp_single_normaliseround(state, sd, &vs, fpscr, "fuito"); + return exceptions; } static u32 vfp_single_fsito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr) { struct vfp_single vs; + u32 exceptions = 0; vs.sign = (m & 0x80000000) >> 16; vs.exponent = 127 + 31 - 1; vs.significand = vs.sign ? -m : m; - return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fsito"); + exceptions |= vfp_single_normaliseround(state, sd, &vs, fpscr, "fsito"); + return exceptions; } static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr) @@ -568,7 +576,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f int rmode = fpscr & FPSCR_RMODE_MASK; int tm; - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); vfp_single_dump("VSM", &vsm); /* @@ -583,7 +591,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f if (vsm.exponent >= 127 + 32) { d = vsm.sign ? 0 : 0xffffffff; - exceptions = FPSCR_IOC; + exceptions |= FPSCR_IOC; } else if (vsm.exponent >= 127) { int shift = 127 + 31 - vsm.exponent; u32 rem, incr = 0; @@ -660,7 +668,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f int rmode = fpscr & FPSCR_RMODE_MASK; int tm; - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); vfp_single_dump("VSM", &vsm); /* @@ -790,7 +798,7 @@ vfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn, /* * different signs -> invalid */ - exceptions = FPSCR_IOC; + exceptions |= FPSCR_IOC; vsp = &vfp_single_default_qnan; } else { /* @@ -937,27 +945,27 @@ static u32 vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr, u32 negate, const char *func) { vfp_single vsd, vsp, vsn, vsm; - u32 exceptions; + u32 exceptions = 0; s32 v; v = vfp_get_float(state, sn); LOG_TRACE(Core_ARM11, "s%u = %08x", sn, v); - vfp_single_unpack(&vsn, v, &fpscr); + exceptions |= vfp_single_unpack(&vsn, v, fpscr); if (vsn.exponent == 0 && vsn.significand) vfp_single_normalise_denormal(&vsn); - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); if (vsm.exponent == 0 && vsm.significand) vfp_single_normalise_denormal(&vsm); - exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr); + exceptions |= vfp_single_multiply(&vsp, &vsn, &vsm, fpscr); if (negate & NEG_MULTIPLY) vsp.sign = vfp_sign_negate(vsp.sign); v = vfp_get_float(state, sd); LOG_TRACE(Core_ARM11, "s%u = %08x", sd, v); - vfp_single_unpack(&vsn, v, &fpscr); + exceptions |= vfp_single_unpack(&vsn, v, fpscr); if (vsn.exponent == 0 && vsn.significand != 0) vfp_single_normalise_denormal(&vsn); @@ -966,7 +974,8 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr); - return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func); + exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, func); + return exceptions; } /* @@ -978,8 +987,10 @@ vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fp */ static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { + u32 exceptions = 0; LOG_TRACE(Core_ARM11, "s%u = %08x", sn, sd); - return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac"); + exceptions |= vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac"); + return exceptions; } /* @@ -1016,21 +1027,23 @@ static u32 vfp_single_fnmsc(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { struct vfp_single vsd, vsn, vsm; - u32 exceptions; + u32 exceptions = 0; s32 n = vfp_get_float(state, sn); LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n); - vfp_single_unpack(&vsn, n, &fpscr); + exceptions |= vfp_single_unpack(&vsn, n, fpscr); if (vsn.exponent == 0 && vsn.significand) vfp_single_normalise_denormal(&vsn); - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); if (vsm.exponent == 0 && vsm.significand) vfp_single_normalise_denormal(&vsm); - exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); - return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fmul"); + exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); + + exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fmul"); + return exceptions; } /* @@ -1039,22 +1052,24 @@ static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { struct vfp_single vsd, vsn, vsm; - u32 exceptions; + u32 exceptions = 0; s32 n = vfp_get_float(state, sn); LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n); - vfp_single_unpack(&vsn, n, &fpscr); + exceptions |= vfp_single_unpack(&vsn, n, fpscr); if (vsn.exponent == 0 && vsn.significand) vfp_single_normalise_denormal(&vsn); - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); if (vsm.exponent == 0 && vsm.significand) vfp_single_normalise_denormal(&vsm); - exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); + exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); vsd.sign = vfp_sign_negate(vsd.sign); - return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fnmul"); + + exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fnmul"); + return exceptions; } /* @@ -1063,7 +1078,7 @@ static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { struct vfp_single vsd, vsn, vsm; - u32 exceptions; + u32 exceptions = 0; s32 n = vfp_get_float(state, sn); LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n); @@ -1071,17 +1086,18 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) /* * Unpack and normalise denormals. */ - vfp_single_unpack(&vsn, n, &fpscr); + exceptions |= vfp_single_unpack(&vsn, n, fpscr); if (vsn.exponent == 0 && vsn.significand) vfp_single_normalise_denormal(&vsn); - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); if (vsm.exponent == 0 && vsm.significand) vfp_single_normalise_denormal(&vsm); - exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr); + exceptions |= vfp_single_add(&vsd, &vsn, &vsm, fpscr); - return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fadd"); + exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fadd"); + return exceptions; } /* @@ -1111,8 +1127,8 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) LOG_TRACE(Core_ARM11, "s%u = %08x", sn, n); - vfp_single_unpack(&vsn, n, &fpscr); - vfp_single_unpack(&vsm, m, &fpscr); + exceptions |= vfp_single_unpack(&vsn, n, fpscr); + exceptions |= vfp_single_unpack(&vsm, m, fpscr); vsd.sign = vsn.sign ^ vsm.sign; @@ -1178,16 +1194,17 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) if ((vsd.significand & 0x3f) == 0) vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32); - return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fdiv"); + exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fdiv"); + return exceptions; vsn_nan: - exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr); + exceptions |= vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr); pack: vfp_put_float(state, vfp_single_pack(&vsd), sd); return exceptions; vsm_nan: - exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr); + exceptions |= vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr); goto pack; zero: @@ -1196,7 +1213,7 @@ zero: goto pack; divzero: - exceptions = FPSCR_DZC; + exceptions |= FPSCR_DZC; infinity: vsd.exponent = 255; vsd.significand = 0; @@ -1204,7 +1221,8 @@ infinity: invalid: vfp_put_float(state, vfp_single_pack(&vfp_single_default_qnan), sd); - return FPSCR_IOC; + exceptions |= FPSCR_IOC; + return exceptions; } static struct op fops[] = { 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/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 1e289f38a..066146cff 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "core/hle/kernel/vm_manager.h" +#include "core/memory.h" #include "core/memory_setup.h" #include "core/mmio.h" 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/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 73fce6079..bbf170b71 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -14,6 +14,7 @@ #include "core/hle/service/apt/apt_u.h" #include "core/hle/service/apt/bcfnt/bcfnt.h" #include "core/hle/service/fs/archive.h" +#include "core/hle/service/ptm/ptm.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" @@ -33,6 +34,9 @@ static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter even static u32 cpu_percent; ///< CPU time available to the running application +// APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode +static u8 unknown_ns_state_field; + /// Parameter data to be returned in the next call to Glance/ReceiveParameter static MessageParameter next_parameter; @@ -258,6 +262,10 @@ void PrepareToStartApplication(Service::Interface* self) { u32 title_info4 = cmd_buff[4]; u32 flags = cmd_buff[5]; + if (flags & 0x00000100) { + unknown_ns_state_field = 1; + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_APT, "(STUBBED) called title_info1=0x%08X, title_info2=0x%08X, title_info3=0x%08X," @@ -373,6 +381,25 @@ void StartLibraryApplet(Service::Interface* self) { cmd_buff[1] = applet->Start(parameter).raw; } +void SetNSStateField(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + unknown_ns_state_field = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0x55, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_APT, "(STUBBED) unknown_ns_state_field=%u", unknown_ns_state_field); +} + +void GetNSStateField(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[8] = unknown_ns_state_field; + LOG_WARNING(Service_APT, "(STUBBED) unknown_ns_state_field=%u", unknown_ns_state_field); +} + void GetAppletInfo(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); auto app_id = static_cast<AppletId>(cmd_buff[1]); @@ -408,6 +435,29 @@ void GetStartupArgument(Service::Interface* self) { cmd_buff[2] = (parameter_size > 0) ? 1 : 0; } +void CheckNew3DSApp(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + if (unknown_ns_state_field) { + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + } else { + PTM::CheckNew3DS(self); + } + + cmd_buff[0] = IPC::MakeHeader(0x101, 2, 0); + LOG_WARNING(Service_APT, "(STUBBED) called"); +} + +void CheckNew3DS(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + PTM::CheckNew3DS(self); + + cmd_buff[0] = IPC::MakeHeader(0x102, 2, 0); + LOG_WARNING(Service_APT, "(STUBBED) called"); +} + void Init() { AddService(new APT_A_Interface); AddService(new APT_S_Interface); @@ -441,6 +491,7 @@ void Init() { lock = Kernel::Mutex::Create(false, "APT_U:Lock"); cpu_percent = 0; + unknown_ns_state_field = 0; // TODO(bunnei): Check if these are created in Initialize or on APT process startup. notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 1a1034fcc..ed7c47cca 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -376,6 +376,50 @@ void StartLibraryApplet(Service::Interface* self); */ void GetStartupArgument(Service::Interface* self); +/** + * APT::SetNSStateField service function + * Inputs: + * 1 : u8 NS state field + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * Note: + * This writes the input u8 to a NS state field. + */ +void SetNSStateField(Service::Interface* self); + +/** + * APT::GetNSStateField service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 8 : u8 NS state field + * Note: + * This returns a u8 NS state field(which can be set by cmd 0x00550040), at cmdreply+8. + */ +void GetNSStateField(Service::Interface* self); + +/** + * APT::CheckNew3DSApp service function + * Outputs: + * 1: Result code, 0 on success, otherwise error code + * 2: u8 output: 0 = Old3DS, 1 = New3DS. + * Note: + * This uses PTMSYSM:CheckNew3DS. + * When a certain NS state field is non-zero, the output value is zero, + * Otherwise the output is from PTMSYSM:CheckNew3DS. + * Normally this NS state field is zero, however this state field is set to 1 + * when APT:PrepareToStartApplication is used with flags bit8 is set. + */ +void CheckNew3DSApp(Service::Interface* self); + +/** + * Wrapper for PTMSYSM:CheckNew3DS + * APT::CheckNew3DS service function + * Outputs: + * 1: Result code, 0 on success, otherwise error code + * 2: u8 output: 0 = Old3DS, 1 = New3DS. + */ +void CheckNew3DS(Service::Interface* self); + /// Initialize the APT service void Init(); diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 9ff47701a..223c0a8bd 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -21,6 +21,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000D0080, ReceiveParameter, "ReceiveParameter"}, {0x000E0080, GlanceParameter, "GlanceParameter"}, {0x000F0100, CancelParameter, "CancelParameter"}, + {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"}, {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, @@ -32,7 +33,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, {0x00510080, GetStartupArgument, "GetStartupArgument"}, - {0x00550040, nullptr, "WriteInputToNsState?"}, + {0x00550040, SetNSStateField, "SetNSStateField?"}, + {0x00560000, GetNSStateField, "GetNSStateField?"}, + {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, + {0x01020000, CheckNew3DS, "CheckNew3DS"} }; APT_A_Interface::APT_A_Interface() { diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index ca54e593c..f5c52fa3d 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -29,7 +29,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, {0x00130000, nullptr, "GetPreparationState"}, {0x00140040, nullptr, "SetPreparationState"}, - {0x00150140, nullptr, "PrepareToStartApplication"}, + {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"}, {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, {0x00180040, PrepareToStartLibraryApplet,"PrepareToStartLibraryApplet"}, @@ -92,9 +92,11 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00520104, nullptr, "Wrap1"}, {0x00530104, nullptr, "Unwrap1"}, + {0x00550040, SetNSStateField, "SetNSStateField?" }, + {0x00560000, GetNSStateField, "GetNSStateField?" }, {0x00580002, nullptr, "GetProgramID"}, - {0x01010000, nullptr, "CheckNew3DSApp"}, - {0x01020000, nullptr, "CheckNew3DS"} + {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, + {0x01020000, CheckNew3DS, "CheckNew3DS"} }; APT_S_Interface::APT_S_Interface() { diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index 0e85c6d08..0e60bd34f 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -29,7 +29,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, {0x00130000, nullptr, "GetPreparationState"}, {0x00140040, nullptr, "SetPreparationState"}, - {0x00150140, nullptr, "PrepareToStartApplication"}, + {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"}, {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, @@ -92,9 +92,11 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00510080, GetStartupArgument, "GetStartupArgument"}, {0x00520104, nullptr, "Wrap1"}, {0x00530104, nullptr, "Unwrap1"}, + {0x00550040, SetNSStateField, "SetNSStateField?"}, + {0x00560000, GetNSStateField, "GetNSStateField?"}, {0x00580002, nullptr, "GetProgramID"}, - {0x01010000, nullptr, "CheckNew3DSApp"}, - {0x01020000, nullptr, "CheckNew3DS"} + {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"}, + {0x01020000, CheckNew3DS, "CheckNew3DS"} }; APT_U_Interface::APT_U_Interface() { diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 94f494690..e2c17d93b 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include "common/logging/log.h" - +#include "core/settings.h" #include "core/file_sys/file_backend.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/ptm/ptm.h" @@ -89,6 +89,20 @@ void IsLegacyPowerOff(Service::Interface* self) { LOG_WARNING(Service_PTM, "(STUBBED) called"); } +void CheckNew3DS(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + const bool is_new_3ds = Settings::values.is_new_3ds; + + if (is_new_3ds) { + LOG_CRITICAL(Service_PTM, "The option 'is_new_3ds' is enabled as part of the 'System' settings. Citra does not fully support New 3DS emulation yet!"); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = is_new_3ds ? 1 : 0; + + LOG_WARNING(Service_PTM, "(STUBBED) called isNew3DS = 0x%08x", static_cast<u32>(is_new_3ds)); +} + void Init() { AddService(new PTM_Play_Interface); AddService(new PTM_Sysm_Interface); diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h index 4cf7383d1..7ef8877c7 100644 --- a/src/core/hle/service/ptm/ptm.h +++ b/src/core/hle/service/ptm/ptm.h @@ -88,6 +88,14 @@ void GetTotalStepCount(Interface* self); */ void IsLegacyPowerOff(Interface* self); +/** + * PTM::CheckNew3DS service function + * Outputs: + * 1: Result code, 0 on success, otherwise error code + * 2: u8 output: 0 = Old3DS, 1 = New3DS. + */ +void CheckNew3DS(Interface* self); + /// Initialize the PTM service void Init(); diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp index fe76dd108..cc4ef1101 100644 --- a/src/core/hle/service/ptm/ptm_sysm.cpp +++ b/src/core/hle/service/ptm/ptm_sysm.cpp @@ -18,7 +18,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x040700C0, nullptr, "ShutdownAsync"}, {0x04080000, nullptr, "Awake"}, {0x04090080, nullptr, "RebootAsync"}, - {0x040A0000, nullptr, "CheckNew3DS"}, + {0x040A0000, CheckNew3DS, "CheckNew3DS"}, {0x08010640, nullptr, "SetInfoLEDPattern"}, {0x08020040, nullptr, "SetInfoLEDPatternHeader"}, {0x08030000, nullptr, "GetInfoLEDStatus"}, @@ -35,7 +35,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x080E0140, nullptr, "NotifyPlayEvent"}, {0x080F0000, IsLegacyPowerOff, "IsLegacyPowerOff"}, {0x08100000, nullptr, "ClearLegacyPowerOff"}, - {0x08110000, nullptr, "GetShellStatus"}, + {0x08110000, GetShellState, "GetShellState"}, {0x08120000, nullptr, "IsShutdownByBatteryEmpty"}, {0x08130000, nullptr, "FormatSavedata"}, {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"}, 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..0ce72de87 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -6,6 +6,7 @@ #include "common/logging/log.h" #include "common/microprofile.h" +#include "common/scope_exit.h" #include "common/string_util.h" #include "common/symbols.h" @@ -326,9 +327,9 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou } } - HLE::Reschedule(__func__); + SCOPE_EXIT({HLE::Reschedule("WaitSynchronizationN");}); // Reschedule after putting the threads to sleep. - // If thread should wait, then set its state to waiting and then reschedule... + // If thread should wait, then set its state to waiting if (wait_thread) { // Actually wait the current thread on each object if we decided to wait... @@ -502,6 +503,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/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 98e7ab48f..a16411e14 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -10,6 +10,7 @@ #include "core/file_sys/archive_romfs.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" +#include "core/hle/service/fs/archive.h" #include "core/loader/3dsx.h" #include "core/memory.h" @@ -263,6 +264,8 @@ ResultStatus AppLoader_THREEDSX::Load() { Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); + Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), Service::FS::ArchiveIdCode::RomFS); + is_loaded = true; return ResultStatus::Success; } diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index 3ee686703..90b20c61c 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h @@ -28,6 +28,14 @@ public: static FileType IdentifyType(FileUtil::IOFile& file); /** + * Returns the type of this file + * @return FileType corresponding to the loaded file + */ + FileType GetFileType() override { + return IdentifyType(file); + } + + /** * Load the bootable file * @return ResultStatus result of function */ diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index c6a5ebe99..cb3724f9d 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -28,6 +28,14 @@ public: static FileType IdentifyType(FileUtil::IOFile& file); /** + * Returns the type of this file + * @return FileType corresponding to the loaded file + */ + FileType GetFileType() override { + return IdentifyType(file); + } + + /** * Load the bootable file * @return ResultStatus result of function */ diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index af3f62248..9719d30d5 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -8,9 +8,7 @@ #include "common/logging/log.h" #include "common/string_util.h" -#include "core/file_sys/archive_romfs.h" #include "core/hle/kernel/process.h" -#include "core/hle/service/fs/archive.h" #include "core/loader/3dsx.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" @@ -67,6 +65,9 @@ FileType GuessFromExtension(const std::string& extension_) { if (extension == ".3dsx") return FileType::THREEDSX; + if (extension == ".cia") + return FileType::CIA; + return FileType::Unknown; } @@ -90,7 +91,15 @@ const char* GetFileTypeString(FileType type) { return "unknown"; } -std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, +/** + * Get a loader for a file with a specific type + * @param file The file to load + * @param type The type of the file + * @param filename the file name (without path) + * @param filepath the file full path (with name) + * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type + */ +static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type, const std::string& filename, const std::string& filepath) { switch (type) { @@ -108,15 +117,15 @@ std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, return std::make_unique<AppLoader_NCCH>(std::move(file), filepath); default: - return std::unique_ptr<AppLoader>(); + return nullptr; } } -ResultStatus LoadFile(const std::string& filename) { +std::unique_ptr<AppLoader> GetLoader(const std::string& filename) { FileUtil::IOFile file(filename, "rb"); if (!file.IsOpen()) { LOG_ERROR(Loader, "Failed to load file %s", filename.c_str()); - return ResultStatus::Error; + return nullptr; } std::string filename_filename, filename_extension; @@ -133,44 +142,7 @@ ResultStatus LoadFile(const std::string& filename) { LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type)); - std::unique_ptr<AppLoader> app_loader = GetLoader(std::move(file), type, filename_filename, filename); - - switch (type) { - - // 3DSX file format... - // or NCCH/NCSD container formats... - case FileType::THREEDSX: - case FileType::CXI: - case FileType::CCI: - { - // Load application and RomFS - ResultStatus result = app_loader->Load(); - if (ResultStatus::Success == result) { - Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*app_loader), Service::FS::ArchiveIdCode::RomFS); - return ResultStatus::Success; - } - return result; - } - - // Standard ELF file format... - case FileType::ELF: - return app_loader->Load(); - - // CIA file format... - case FileType::CIA: - return ResultStatus::ErrorNotImplemented; - - // Error occurred durring IdentifyFile... - case FileType::Error: - - // IdentifyFile could know identify file type... - case FileType::Unknown: - { - LOG_CRITICAL(Loader, "File %s is of unknown type.", filename.c_str()); - return ResultStatus::ErrorInvalidFormat; - } - } - return ResultStatus::Error; + return GetFileLoader(std::move(file), type, filename_filename, filename); } } // namespace Loader diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 9d3e9ed3b..77d87afe1 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -10,10 +10,8 @@ #include <string> #include <vector> -#include "common/common_funcs.h" #include "common/common_types.h" #include "common/file_util.h" -#include "common/swap.h" namespace Kernel { struct AddressMapping; @@ -80,51 +78,6 @@ constexpr u32 MakeMagic(char a, char b, char c, char d) { return a | b << 8 | c << 16 | d << 24; } -/// SMDH data structure that contains titles, icons etc. See https://www.3dbrew.org/wiki/SMDH -struct SMDH { - u32_le magic; - u16_le version; - INSERT_PADDING_BYTES(2); - - struct Title { - std::array<u16, 0x40> short_title; - std::array<u16, 0x80> long_title; - std::array<u16, 0x40> publisher; - }; - std::array<Title, 16> titles; - - std::array<u8, 16> ratings; - u32_le region_lockout; - u32_le match_maker_id; - u64_le match_maker_bit_id; - u32_le flags; - u16_le eula_version; - INSERT_PADDING_BYTES(2); - float_le banner_animation_frame; - u32_le cec_id; - INSERT_PADDING_BYTES(8); - - std::array<u8, 0x480> small_icon; - std::array<u8, 0x1200> large_icon; - - /// indicates the language used for each title entry - enum class TitleLanguage { - Japanese = 0, - English = 1, - French = 2, - German = 3, - Italian = 4, - Spanish = 5, - SimplifiedChinese = 6, - Korean= 7, - Dutch = 8, - Portuguese = 9, - Russian = 10, - TraditionalChinese = 11 - }; -}; -static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong"); - /// Interface for loading an application class AppLoader : NonCopyable { public: @@ -132,6 +85,12 @@ public: virtual ~AppLoader() { } /** + * Returns the type of this file + * @return FileType corresponding to the loaded file + */ + virtual FileType GetFileType() = 0; + + /** * Load the application * @return ResultStatus result of function */ @@ -197,20 +156,10 @@ protected: extern const std::initializer_list<Kernel::AddressMapping> default_address_mappings; /** - * Get a loader for a file with a specific type - * @param file The file to load - * @param type The type of the file - * @param filename the file name (without path) - * @param filepath the file full path (with name) - * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type - */ -std::unique_ptr<AppLoader> GetLoader(FileUtil::IOFile&& file, FileType type, const std::string& filename, const std::string& filepath); - -/** - * Identifies and loads a bootable file + * Identifies a bootable file and return a suitable loader * @param filename String filename of bootable file - * @return ResultStatus result of function + * @return best loader for this file */ -ResultStatus LoadFile(const std::string& filename); +std::unique_ptr<AppLoader> GetLoader(const std::string& filename); } // namespace diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 7391bdb26..fca091ff9 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -10,8 +10,10 @@ #include "common/string_util.h" #include "common/swap.h" +#include "core/file_sys/archive_romfs.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" +#include "core/hle/service/fs/archive.h" #include "core/loader/ncch.h" #include "core/memory.h" @@ -303,7 +305,12 @@ ResultStatus AppLoader_NCCH::Load() { is_loaded = true; // Set state to loaded - return LoadExec(); // Load the executable into memory for booting + result = LoadExec(); // Load the executable into memory for booting + if (ResultStatus::Success != result) + return result; + + Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this), Service::FS::ArchiveIdCode::RomFS); + return ResultStatus::Success; } ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index fd852c3de..75609ee57 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -174,6 +174,14 @@ public: static FileType IdentifyType(FileUtil::IOFile& file); /** + * Returns the type of this file + * @return FileType corresponding to the loaded file + */ + FileType GetFileType() override { + return IdentifyType(file); + } + + /** * Load the application * @return ResultStatus result of function */ diff --git a/src/core/loader/smdh.cpp b/src/core/loader/smdh.cpp new file mode 100644 index 000000000..2d014054a --- /dev/null +++ b/src/core/loader/smdh.cpp @@ -0,0 +1,54 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include <vector> + +#include "common/common_types.h" + +#include "core/loader/loader.h" +#include "core/loader/smdh.h" + +#include "video_core/utils.h" + +namespace Loader { + +bool IsValidSMDH(const std::vector<u8>& smdh_data) { + if (smdh_data.size() < sizeof(Loader::SMDH)) + return false; + + u32 magic; + memcpy(&magic, smdh_data.data(), sizeof(u32)); + + return Loader::MakeMagic('S', 'M', 'D', 'H') == magic; +} + +std::vector<u16> SMDH::GetIcon(bool large) const { + u32 size; + const u8* icon_data; + + if (large) { + size = 48; + icon_data = large_icon.data(); + } else { + size = 24; + icon_data = small_icon.data(); + } + + std::vector<u16> icon(size * size); + for (u32 x = 0; x < size; ++x) { + for (u32 y = 0; y < size; ++y) { + u32 coarse_y = y & ~7; + const u8* pixel = icon_data + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * size * 2; + icon[x + size * y] = (pixel[1] << 8) + pixel[0]; + } + } + return icon; +} + +std::array<u16, 0x40> SMDH::GetShortTitle(Loader::SMDH::TitleLanguage language) const { + return titles[static_cast<int>(language)].short_title; +} + +} // namespace diff --git a/src/core/loader/smdh.h b/src/core/loader/smdh.h new file mode 100644 index 000000000..2011abda2 --- /dev/null +++ b/src/core/loader/smdh.h @@ -0,0 +1,82 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <vector> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Loader { + +/** + * Tests if data is a valid SMDH by its length and magic number. + * @param smdh_data data buffer to test + * @return bool test result + */ +bool IsValidSMDH(const std::vector<u8>& smdh_data); + +/// SMDH data structure that contains titles, icons etc. See https://www.3dbrew.org/wiki/SMDH +struct SMDH { + u32_le magic; + u16_le version; + INSERT_PADDING_BYTES(2); + + struct Title { + std::array<u16, 0x40> short_title; + std::array<u16, 0x80> long_title; + std::array<u16, 0x40> publisher; + }; + std::array<Title, 16> titles; + + std::array<u8, 16> ratings; + u32_le region_lockout; + u32_le match_maker_id; + u64_le match_maker_bit_id; + u32_le flags; + u16_le eula_version; + INSERT_PADDING_BYTES(2); + float_le banner_animation_frame; + u32_le cec_id; + INSERT_PADDING_BYTES(8); + + std::array<u8, 0x480> small_icon; + std::array<u8, 0x1200> large_icon; + + /// indicates the language used for each title entry + enum class TitleLanguage { + Japanese = 0, + English = 1, + French = 2, + German = 3, + Italian = 4, + Spanish = 5, + SimplifiedChinese = 6, + Korean= 7, + Dutch = 8, + Portuguese = 9, + Russian = 10, + TraditionalChinese = 11 + }; + + /** + * Gets game icon from SMDH + * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) + * @return vector of RGB565 data + */ + std::vector<u16> GetIcon(bool large) const; + + /** + * Gets the short game title from SMDH + * @param language title language + * @return UTF-16 array of the short title + */ + std::array<u16, 0x40> GetShortTitle(Loader::SMDH::TitleLanguage language) const; +}; +static_assert(sizeof(SMDH) == 0x36C0, "SMDH structure size is wrong"); + +} // namespace diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h index 05f70a1fe..ee8ea7857 100644 --- a/src/core/memory_setup.h +++ b/src/core/memory_setup.h @@ -6,7 +6,7 @@ #include "common/common_types.h" -#include "core/memory.h" +#include "core/mmio.h" namespace Memory { diff --git a/src/core/settings.h b/src/core/settings.h index ce2a31164..ea72f4d9c 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -41,6 +41,9 @@ static const std::array<Values, NUM_INPUTS> All = {{ struct Values { + // CheckNew3DS + bool is_new_3ds; + // Controls std::array<int, NativeInput::NUM_INPUTS> input_mappings; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt new file mode 100644 index 000000000..457c55571 --- /dev/null +++ b/src/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +set(SRCS + tests.cpp + ) + +set(HEADERS + ) + +create_directory_groups(${SRCS} ${HEADERS}) + +include_directories(../../externals/catch/single_include/) + +add_executable(tests ${SRCS} ${HEADERS}) +target_link_libraries(tests core video_core audio_core common) +target_link_libraries(tests ${PLATFORM_LIBRARIES}) + +add_test(NAME tests COMMAND $<TARGET_FILE:tests>) diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp new file mode 100644 index 000000000..73978676f --- /dev/null +++ b/src/tests/tests.cpp @@ -0,0 +1,9 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#define CATCH_CONFIG_MAIN +#include <catch.hpp> + +// Catch provides the main function since we've given it the +// CATCH_CONFIG_MAIN preprocessor directive. diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index ad0da796e..bf4664f9e 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -199,9 +199,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { // Processes information about internal vertex attributes to figure out how a vertex is loaded. // Later, these can be compiled and cached. - VertexLoader loader; const u32 base_address = regs.vertex_attributes.GetPhysicalBaseAddress(); - loader.Setup(regs); + VertexLoader loader(regs); // Load vertices bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed)); 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/pica.h b/src/video_core/pica.h index 86c0a0096..544ea037f 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -787,23 +787,21 @@ struct Regs { LightColor diffuse; // material.diffuse * light.diffuse LightColor ambient; // material.ambient * light.ambient - struct { - // Encoded as 16-bit floating point - union { - BitField< 0, 16, u32> x; - BitField<16, 16, u32> y; - }; - union { - BitField< 0, 16, u32> z; - }; + // Encoded as 16-bit floating point + union { + BitField< 0, 16, u32> x; + BitField<16, 16, u32> y; + }; + union { + BitField< 0, 16, u32> z; + }; - INSERT_PADDING_WORDS(0x3); + INSERT_PADDING_WORDS(0x3); - union { - BitField<0, 1, u32> directional; - BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 - }; - }; + union { + BitField<0, 1, u32> directional; + BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 + } config; BitField<0, 20, u32> dist_atten_bias; BitField<0, 20, u32> dist_atten_scale; @@ -824,7 +822,7 @@ struct Regs { BitField<27, 1, u32> clamp_highlights; BitField<28, 2, LightingBumpMode> bump_mode; BitField<30, 1, u32> disable_bump_renorm; - }; + } config0; union { BitField<16, 1, u32> disable_lut_d0; @@ -845,13 +843,13 @@ struct Regs { BitField<29, 1, u32> disable_dist_atten_light_5; BitField<30, 1, u32> disable_dist_atten_light_6; BitField<31, 1, u32> disable_dist_atten_light_7; - }; + } config1; bool IsDistAttenDisabled(unsigned index) const { - const unsigned disable[] = { disable_dist_atten_light_0, disable_dist_atten_light_1, - disable_dist_atten_light_2, disable_dist_atten_light_3, - disable_dist_atten_light_4, disable_dist_atten_light_5, - disable_dist_atten_light_6, disable_dist_atten_light_7 }; + const unsigned disable[] = { config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1, + config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3, + config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5, + config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7 }; return disable[index] != 0; } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c09bc8857..931c34a37 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -380,6 +380,17 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncCombinerColor(); break; + // Fragment lighting switches + case PICA_REG_INDEX(lighting.disable): + case PICA_REG_INDEX(lighting.num_lights): + case PICA_REG_INDEX(lighting.config0): + case PICA_REG_INDEX(lighting.config1): + case PICA_REG_INDEX(lighting.abs_lut_input): + case PICA_REG_INDEX(lighting.lut_input): + case PICA_REG_INDEX(lighting.lut_scale): + case PICA_REG_INDEX(lighting.light_enable): + break; + // Fragment lighting specular 0 color case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_0, 0x140 + 0 * 0x10): SyncLightSpecular0(0); @@ -518,6 +529,70 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncLightPosition(7); break; + // Fragment lighting light source config + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].config, 0x149 + 0 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].config, 0x149 + 1 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].config, 0x149 + 2 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].config, 0x149 + 3 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].config, 0x149 + 4 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].config, 0x149 + 5 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].config, 0x149 + 6 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].config, 0x149 + 7 * 0x10): + shader_dirty = true; + break; + + // Fragment lighting distance attenuation bias + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].dist_atten_bias, 0x014A + 0 * 0x10): + SyncLightDistanceAttenuationBias(0); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].dist_atten_bias, 0x014A + 1 * 0x10): + SyncLightDistanceAttenuationBias(1); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].dist_atten_bias, 0x014A + 2 * 0x10): + SyncLightDistanceAttenuationBias(2); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].dist_atten_bias, 0x014A + 3 * 0x10): + SyncLightDistanceAttenuationBias(3); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].dist_atten_bias, 0x014A + 4 * 0x10): + SyncLightDistanceAttenuationBias(4); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].dist_atten_bias, 0x014A + 5 * 0x10): + SyncLightDistanceAttenuationBias(5); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].dist_atten_bias, 0x014A + 6 * 0x10): + SyncLightDistanceAttenuationBias(6); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].dist_atten_bias, 0x014A + 7 * 0x10): + SyncLightDistanceAttenuationBias(7); + break; + + // Fragment lighting distance attenuation scale + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].dist_atten_scale, 0x014B + 0 * 0x10): + SyncLightDistanceAttenuationScale(0); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].dist_atten_scale, 0x014B + 1 * 0x10): + SyncLightDistanceAttenuationScale(1); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].dist_atten_scale, 0x014B + 2 * 0x10): + SyncLightDistanceAttenuationScale(2); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].dist_atten_scale, 0x014B + 3 * 0x10): + SyncLightDistanceAttenuationScale(3); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].dist_atten_scale, 0x014B + 4 * 0x10): + SyncLightDistanceAttenuationScale(4); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].dist_atten_scale, 0x014B + 5 * 0x10): + SyncLightDistanceAttenuationScale(5); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].dist_atten_scale, 0x014B + 6 * 0x10): + SyncLightDistanceAttenuationScale(6); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].dist_atten_scale, 0x014B + 7 * 0x10): + SyncLightDistanceAttenuationScale(7); + break; + // Fragment lighting global ambient color (emission + ambient * ambient) case PICA_REG_INDEX_WORKAROUND(lighting.global_ambient, 0x1c0): SyncGlobalAmbient(); @@ -896,6 +971,8 @@ void RasterizerOpenGL::SetShader() { SyncLightDiffuse(light_index); SyncLightAmbient(light_index); SyncLightPosition(light_index); + SyncLightDistanceAttenuationBias(light_index); + SyncLightDistanceAttenuationScale(light_index); } } } @@ -947,6 +1024,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); @@ -1103,3 +1182,21 @@ void RasterizerOpenGL::SyncLightPosition(int light_index) { uniform_block_data.dirty = true; } } + +void RasterizerOpenGL::SyncLightDistanceAttenuationBias(int light_index) { + GLfloat dist_atten_bias = Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_bias).ToFloat32(); + + if (dist_atten_bias != uniform_block_data.data.light_src[light_index].dist_atten_bias) { + uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias; + uniform_block_data.dirty = true; + } +} + +void RasterizerOpenGL::SyncLightDistanceAttenuationScale(int light_index) { + GLfloat dist_atten_scale = Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_scale).ToFloat32(); + + if (dist_atten_scale != uniform_block_data.data.light_src[light_index].dist_atten_scale) { + uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale; + uniform_block_data.dirty = true; + } +} diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d70369400..bb7f20161 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -89,49 +89,47 @@ union PicaShaderConfig { unsigned num = regs.lighting.light_enable.GetNum(light_index); const auto& light = regs.lighting.light[num]; state.lighting.light[light_index].num = num; - state.lighting.light[light_index].directional = light.directional != 0; - state.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0; + state.lighting.light[light_index].directional = light.config.directional != 0; + state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; state.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num); - state.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32(); - state.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32(); } - state.lighting.lut_d0.enable = regs.lighting.disable_lut_d0 == 0; + state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0; state.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0; state.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value(); state.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0); - state.lighting.lut_d1.enable = regs.lighting.disable_lut_d1 == 0; + state.lighting.lut_d1.enable = regs.lighting.config1.disable_lut_d1 == 0; state.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0; state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value(); state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); - state.lighting.lut_fr.enable = regs.lighting.disable_lut_fr == 0; + state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0; state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0; state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value(); state.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr); - state.lighting.lut_rr.enable = regs.lighting.disable_lut_rr == 0; + state.lighting.lut_rr.enable = regs.lighting.config1.disable_lut_rr == 0; state.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0; state.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value(); state.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr); - state.lighting.lut_rg.enable = regs.lighting.disable_lut_rg == 0; + state.lighting.lut_rg.enable = regs.lighting.config1.disable_lut_rg == 0; state.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0; state.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value(); state.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg); - state.lighting.lut_rb.enable = regs.lighting.disable_lut_rb == 0; + state.lighting.lut_rb.enable = regs.lighting.config1.disable_lut_rb == 0; state.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0; state.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value(); state.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb); - state.lighting.config = regs.lighting.config; - state.lighting.fresnel_selector = regs.lighting.fresnel_selector; - state.lighting.bump_mode = regs.lighting.bump_mode; - state.lighting.bump_selector = regs.lighting.bump_selector; - state.lighting.bump_renorm = regs.lighting.disable_bump_renorm == 0; - state.lighting.clamp_highlights = regs.lighting.clamp_highlights != 0; + state.lighting.config = regs.lighting.config0.config; + state.lighting.fresnel_selector = regs.lighting.config0.fresnel_selector; + state.lighting.bump_mode = regs.lighting.config0.bump_mode; + state.lighting.bump_selector = regs.lighting.config0.bump_selector; + state.lighting.bump_renorm = regs.lighting.config0.disable_bump_renorm == 0; + state.lighting.clamp_highlights = regs.lighting.config0.clamp_highlights != 0; return res; } @@ -184,8 +182,6 @@ union PicaShaderConfig { bool directional; bool two_sided_diffuse; bool dist_atten_enable; - GLfloat dist_atten_scale; - GLfloat dist_atten_bias; } light[8]; bool enable; @@ -316,6 +312,8 @@ private: alignas(16) GLvec3 diffuse; alignas(16) GLvec3 ambient; alignas(16) GLvec3 position; + GLfloat dist_atten_bias; + GLfloat dist_atten_scale; }; /// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned @@ -330,7 +328,7 @@ private: LightSrc light_src[8]; }; - static_assert(sizeof(UniformData) == 0x310, "The size of the UniformData structure has changed, update the structure in the shader"); + static_assert(sizeof(UniformData) == 0x390, "The size of the UniformData structure has changed, update the structure in the shader"); static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); /// Sets the OpenGL shader in accordance with the current PICA register state @@ -402,6 +400,12 @@ private: /// Syncs the specified light's position to match the PICA register void SyncLightPosition(int light_index); + /// Syncs the specified light's distance attenuation bias to match the PICA register + void SyncLightDistanceAttenuationBias(int light_index); + + /// Syncs the specified light's distance attenuation scale to match the PICA register + void SyncLightDistanceAttenuationScale(int light_index); + OpenGLState state; RasterizerCacheOpenGL res_cache; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 71d60e69c..8332e722d 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -439,9 +439,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { // If enabled, compute distance attenuation value std::string dist_atten = "1.0"; if (light_config.dist_atten_enable) { - std::string scale = std::to_string(light_config.dist_atten_scale); - std::string bias = std::to_string(light_config.dist_atten_bias); - std::string index = "(" + scale + " * length(-view - " + light_src + ".position) + " + bias + ")"; + std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + light_src + ".position) + " + light_src + ".dist_atten_bias)"; index = "((clamp(" + index + ", 0.0, FLOAT_255)))"; const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + light_config.num); dist_atten = GetLutValue((Regs::LightingSampler)lut_num, index); @@ -549,6 +547,8 @@ struct LightSrc { vec3 diffuse; vec3 ambient; vec3 position; + float dist_atten_bias; + float dist_atten_scale; }; layout (std140) uniform shader_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; }; diff --git a/src/video_core/vertex_loader.cpp b/src/video_core/vertex_loader.cpp index 83896814f..e40f0f1ee 100644 --- a/src/video_core/vertex_loader.cpp +++ b/src/video_core/vertex_loader.cpp @@ -2,8 +2,8 @@ #include <boost/range/algorithm/fill.hpp> -#include "common/assert.h" #include "common/alignment.h" +#include "common/assert.h" #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -21,6 +21,8 @@ namespace Pica { void VertexLoader::Setup(const Pica::Regs& regs) { + ASSERT_MSG(!is_setup, "VertexLoader is not intended to be setup more than once."); + const auto& attribute_config = regs.vertex_attributes; num_total_attributes = attribute_config.GetNumTotalAttributes(); @@ -60,9 +62,13 @@ void VertexLoader::Setup(const Pica::Regs& regs) { } } } + + is_setup = true; } void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::InputVertex& input, DebugUtils::MemoryAccessTracker& memory_accesses) { + ASSERT_MSG(is_setup, "A VertexLoader needs to be setup before loading vertices."); + for (int i = 0; i < num_total_attributes; ++i) { if (vertex_attribute_elements[i] != 0) { // Load per-vertex data from the loader arrays diff --git a/src/video_core/vertex_loader.h b/src/video_core/vertex_loader.h index becf5a403..ac162c254 100644 --- a/src/video_core/vertex_loader.h +++ b/src/video_core/vertex_loader.h @@ -1,7 +1,8 @@ #pragma once -#include "common/common_types.h" +#include <array> +#include "common/common_types.h" #include "video_core/pica.h" namespace Pica { @@ -11,23 +12,29 @@ class MemoryAccessTracker; } namespace Shader { -class InputVertex; +struct InputVertex; } class VertexLoader { public: + VertexLoader() = default; + explicit VertexLoader(const Pica::Regs& regs) { + Setup(regs); + } + void Setup(const Pica::Regs& regs); void LoadVertex(u32 base_address, int index, int vertex, Shader::InputVertex& input, DebugUtils::MemoryAccessTracker& memory_accesses); int GetNumTotalAttributes() const { return num_total_attributes; } private: - u32 vertex_attribute_sources[16]; - u32 vertex_attribute_strides[16] = {}; - Regs::VertexAttributeFormat vertex_attribute_formats[16] = {}; - u32 vertex_attribute_elements[16] = {}; - bool vertex_attribute_is_default[16]; - int num_total_attributes; + std::array<u32, 16> vertex_attribute_sources; + std::array<u32, 16> vertex_attribute_strides{}; + std::array<Regs::VertexAttributeFormat, 16> vertex_attribute_formats; + std::array<u32, 16> vertex_attribute_elements{}; + std::array<bool, 16> vertex_attribute_is_default; + int num_total_attributes = 0; + bool is_setup = false; }; } // namespace Pica |