summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/CMakeLists.txt15
-rw-r--r--src/audio_core/hle/dsp.cpp75
-rw-r--r--src/audio_core/hle/dsp.h8
-rw-r--r--src/audio_core/hle/mixers.cpp201
-rw-r--r--src/audio_core/hle/mixers.h63
-rw-r--r--src/audio_core/hle/pipe.cpp9
-rw-r--r--src/audio_core/hle/pipe.h12
-rw-r--r--src/audio_core/hle/source.cpp4
-rw-r--r--src/audio_core/sdl2_sink.cpp126
-rw-r--r--src/audio_core/sdl2_sink.h30
-rw-r--r--src/audio_core/sink.h2
-rw-r--r--src/audio_core/sink_details.cpp7
-rw-r--r--src/audio_core/time_stretch.cpp144
-rw-r--r--src/audio_core/time_stretch.h57
-rw-r--r--src/citra/citra.cpp8
-rw-r--r--src/citra/config.cpp7
-rw-r--r--src/citra/default_ini.h2
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp7
-rw-r--r--src/citra_qt/CMakeLists.txt8
-rw-r--r--src/citra_qt/config.cpp6
-rw-r--r--src/citra_qt/config/controller_config.cpp95
-rw-r--r--src/citra_qt/config/controller_config.h56
-rw-r--r--src/citra_qt/config/controller_config.ui308
-rw-r--r--src/citra_qt/config/controller_config_util.cpp125
-rw-r--r--src/citra_qt/config/controller_config_util.h82
-rw-r--r--src/citra_qt/configure.ui11
-rw-r--r--src/citra_qt/configure_audio.cpp44
-rw-r--r--src/citra_qt/configure_audio.h27
-rw-r--r--src/citra_qt/configure_audio.ui48
-rw-r--r--src/citra_qt/configure_dialog.cpp1
-rw-r--r--src/citra_qt/debugger/callstack.cpp5
-rw-r--r--src/citra_qt/debugger/graphics_tracing.cpp2
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp4
-rw-r--r--src/citra_qt/debugger/profiler.cpp16
-rw-r--r--src/citra_qt/game_list.cpp35
-rw-r--r--src/citra_qt/game_list_p.h55
-rw-r--r--src/citra_qt/main.cpp21
-rw-r--r--src/citra_qt/util/util.cpp2
-rw-r--r--src/common/common_funcs.h30
-rw-r--r--src/common/file_util.cpp48
-rw-r--r--src/common/file_util.h12
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h3
-rw-r--r--src/common/swap.h68
-rw-r--r--src/core/CMakeLists.txt16
-rw-r--r--src/core/arm/arm_interface.h1
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_dec.cpp4
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp12
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp_helper.h20
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpdouble.cpp142
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp142
-rw-r--r--src/core/file_sys/archive_backend.cpp12
-rw-r--r--src/core/gdbstub/gdbstub.cpp26
-rw-r--r--src/core/hle/applets/applet.h1
-rw-r--r--src/core/hle/applets/mii_selector.cpp41
-rw-r--r--src/core/hle/applets/mii_selector.h10
-rw-r--r--src/core/hle/applets/swkbd.cpp37
-rw-r--r--src/core/hle/applets/swkbd.h7
-rw-r--r--src/core/hle/function_wrappers.h3
-rw-r--r--src/core/hle/kernel/memory.cpp5
-rw-r--r--src/core/hle/kernel/process.cpp2
-rw-r--r--src/core/hle/kernel/process.h9
-rw-r--r--src/core/hle/kernel/shared_memory.cpp177
-rw-r--r--src/core/hle/kernel/shared_memory.h48
-rw-r--r--src/core/hle/kernel/thread.cpp88
-rw-r--r--src/core/hle/kernel/thread.h4
-rw-r--r--src/core/hle/kernel/vm_manager.cpp1
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/act_a.cpp26
-rw-r--r--src/core/hle/service/act_a.h (renamed from src/core/hle/service/dlp_srvr.h)6
-rw-r--r--src/core/hle/service/act_u.cpp3
-rw-r--r--src/core/hle/service/apt/apt.cpp134
-rw-r--r--src/core/hle/service/apt/apt.h65
-rw-r--r--src/core/hle/service/apt/apt_a.cpp6
-rw-r--r--src/core/hle/service/apt/apt_s.cpp8
-rw-r--r--src/core/hle/service/apt/apt_u.cpp8
-rw-r--r--src/core/hle/service/apt/bcfnt/bcfnt.cpp71
-rw-r--r--src/core/hle/service/apt/bcfnt/bcfnt.h87
-rw-r--r--src/core/hle/service/cfg/cfg.cpp58
-rw-r--r--src/core/hle/service/csnd_snd.cpp13
-rw-r--r--src/core/hle/service/dlp/dlp.cpp24
-rw-r--r--src/core/hle/service/dlp/dlp.h15
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.cpp20
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.h22
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.cpp20
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.h22
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp (renamed from src/core/hle/service/dlp_srvr.cpp)22
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.h22
-rw-r--r--src/core/hle/service/dsp_dsp.cpp25
-rw-r--r--src/core/hle/service/frd/frd.cpp10
-rw-r--r--src/core/hle/service/fs/archive.cpp22
-rw-r--r--src/core/hle/service/gsp_gpu.cpp118
-rw-r--r--src/core/hle/service/hid/hid.cpp5
-rw-r--r--src/core/hle/service/ir/ir.cpp5
-rw-r--r--src/core/hle/service/ptm/ptm.cpp16
-rw-r--r--src/core/hle/service/ptm/ptm.h8
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.cpp4
-rw-r--r--src/core/hle/service/service.cpp7
-rw-r--r--src/core/hle/service/soc_u.cpp100
-rw-r--r--src/core/hle/service/ssl_c.cpp7
-rw-r--r--src/core/hle/svc.cpp62
-rw-r--r--src/core/hw/gpu.cpp4
-rw-r--r--src/core/loader/3dsx.cpp9
-rw-r--r--src/core/loader/3dsx.h8
-rw-r--r--src/core/loader/elf.h8
-rw-r--r--src/core/loader/loader.cpp59
-rw-r--r--src/core/loader/loader.h69
-rw-r--r--src/core/loader/ncch.cpp12
-rw-r--r--src/core/loader/ncch.h8
-rw-r--r--src/core/loader/smdh.cpp54
-rw-r--r--src/core/loader/smdh.h82
-rw-r--r--src/core/memory.cpp236
-rw-r--r--src/core/memory.h14
-rw-r--r--src/core/memory_setup.h2
-rw-r--r--src/core/mmio.h6
-rw-r--r--src/core/settings.h3
-rw-r--r--src/core/tracer/recorder.cpp24
-rw-r--r--src/tests/CMakeLists.txt16
-rw-r--r--src/tests/tests.cpp9
-rw-r--r--src/video_core/clipper.cpp4
-rw-r--r--src/video_core/command_processor.cpp30
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp208
-rw-r--r--src/video_core/debug_utils/debug_utils.h6
-rw-r--r--src/video_core/pica.cpp2
-rw-r--r--src/video_core/pica.h70
-rw-r--r--src/video_core/pica_state.h4
-rw-r--r--src/video_core/rasterizer.cpp55
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp140
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h235
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp183
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_state.h2
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h20
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp10
-rw-r--r--src/video_core/shader/shader.cpp132
-rw-r--r--src/video_core/shader/shader.h129
-rw-r--r--src/video_core/shader/shader_interpreter.cpp78
-rw-r--r--src/video_core/shader/shader_interpreter.h2
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp32
-rw-r--r--src/video_core/shader/shader_jit_x64.h6
-rw-r--r--src/video_core/vertex_loader.cpp10
-rw-r--r--src/video_core/vertex_loader.h23
146 files changed, 3742 insertions, 2016 deletions
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 4cd7aba67..a72a907ef 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -3,10 +3,12 @@ set(SRCS
codec.cpp
hle/dsp.cpp
hle/filter.cpp
+ hle/mixers.cpp
hle/pipe.cpp
hle/source.cpp
interpolate.cpp
sink_details.cpp
+ time_stretch.cpp
)
set(HEADERS
@@ -15,17 +17,30 @@ set(HEADERS
hle/common.h
hle/dsp.h
hle/filter.h
+ hle/mixers.h
hle/pipe.h
hle/source.h
interpolate.h
null_sink.h
sink.h
sink_details.h
+ time_stretch.h
)
include_directories(../../externals/soundtouch/include)
+if(SDL2_FOUND)
+ set(SRCS ${SRCS} sdl2_sink.cpp)
+ set(HEADERS ${HEADERS} sdl2_sink.h)
+ include_directories(${SDL2_INCLUDE_DIR})
+endif()
+
create_directory_groups(${SRCS} ${HEADERS})
add_library(audio_core STATIC ${SRCS} ${HEADERS})
target_link_libraries(audio_core SoundTouch)
+
+if(SDL2_FOUND)
+ target_link_libraries(audio_core ${SDL2_LIBRARY})
+ set_property(TARGET audio_core APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2)
+endif()
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp
index 0cdbdb06a..0640e1eff 100644
--- a/src/audio_core/hle/dsp.cpp
+++ b/src/audio_core/hle/dsp.cpp
@@ -6,13 +6,17 @@
#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"
+#include "audio_core/time_stretch.h"
namespace DSP {
namespace HLE {
+// Region management
+
std::array<SharedMemory, 2> g_regions;
static size_t CurrentRegionIndex() {
@@ -40,43 +44,96 @@ 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();
+
for (auto& source : sources) {
source.Reset();
}
+
+ mixers.Reset();
+
+ time_stretcher.Reset();
+ if (sink) {
+ time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
+ }
}
void Shutdown() {
+ time_stretcher.Flush();
+ while (true) {
+ std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue());
+ if (residual_audio.empty())
+ break;
+ sink->EnqueueSamples(residual_audio);
+ }
}
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;
}
void SetSink(std::unique_ptr<AudioCore::Sink> sink_) {
sink = std::move(sink_);
+ time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
}
} // namespace HLE
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
index 4459a5668..9275cd7de 100644
--- a/src/audio_core/hle/dsp.h
+++ b/src/audio_core/hle/dsp.h
@@ -33,13 +33,9 @@ namespace HLE {
// double-buffer. The frame counter is located as the very last u16 of each region and is incremented
// each audio tick.
-struct SharedMemory;
-
constexpr VAddr region0_base = 0x1FF50000;
constexpr VAddr region1_base = 0x1FF70000;
-extern std::array<SharedMemory, 2> g_regions;
-
/**
* The DSP is native 16-bit. The DSP also appears to be big-endian. When reading 32-bit numbers from
* its memory regions, the higher and lower 16-bit halves are swapped compared to the little-endian
@@ -432,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);
@@ -507,6 +503,8 @@ struct SharedMemory {
};
ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
+extern std::array<SharedMemory, 2> g_regions;
+
// Structures must have an offset that is a multiple of two.
static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
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/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp
index 03280780f..44dff1345 100644
--- a/src/audio_core/hle/pipe.cpp
+++ b/src/audio_core/hle/pipe.cpp
@@ -36,12 +36,17 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
return {};
}
+ if (length > UINT16_MAX) { // Can only read at most UINT16_MAX from the pipe
+ LOG_ERROR(Audio_DSP, "length of %u greater than max of %u", length, UINT16_MAX);
+ return {};
+ }
+
std::vector<u8>& data = pipe_data[pipe_index];
if (length > data.size()) {
LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain",
pipe_index, length, data.size());
- length = data.size();
+ length = static_cast<u32>(data.size());
}
if (length == 0)
@@ -94,7 +99,7 @@ static void AudioPipeWriteStructAddresses() {
};
// Begin with a u16 denoting the number of structs.
- WriteU16(DspPipe::Audio, struct_addresses.size());
+ WriteU16(DspPipe::Audio, static_cast<u16>(struct_addresses.size()));
// Then write the struct addresses.
for (u16 addr : struct_addresses) {
WriteU16(DspPipe::Audio, addr);
diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h
index 64d97f8ba..b714c0496 100644
--- a/src/audio_core/hle/pipe.h
+++ b/src/audio_core/hle/pipe.h
@@ -24,10 +24,14 @@ enum class DspPipe {
constexpr size_t NUM_DSP_PIPE = 8;
/**
- * Read a DSP pipe.
- * @param pipe_number The Pipe ID
- * @param length How much data to request.
- * @return The data read from the pipe. The size of this vector can be less than the length requested.
+ * Reads `length` bytes from the DSP pipe identified with `pipe_number`.
+ * @note Can read up to the maximum value of a u16 in bytes (65,535).
+ * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty vector will be returned.
+ * @note IF `length` is set to 0, an empty vector will be returned.
+ * @note IF `length` is greater than the amount of data available, this function will only read the available amount.
+ * @param pipe_number a `DspPipe`
+ * @param length the number of bytes to read. The max is 65,535 (max of u16).
+ * @returns a vector of bytes from the specified pipe. On error, will be empty.
*/
std::vector<u8> PipeRead(DspPipe pipe_number, u32 length);
diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp
index daaf6e3f3..30552fe26 100644
--- a/src/audio_core/hle/source.cpp
+++ b/src/audio_core/hle/source.cpp
@@ -126,13 +126,13 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
if (config.simple_filter_dirty) {
config.simple_filter_dirty.Assign(0);
state.filters.Configure(config.simple_filter);
- LOG_TRACE(Audio_DSP, "source_id=%zu simple filter update");
+ LOG_TRACE(Audio_DSP, "source_id=%zu simple filter update", source_id);
}
if (config.biquad_filter_dirty) {
config.biquad_filter_dirty.Assign(0);
state.filters.Configure(config.biquad_filter);
- LOG_TRACE(Audio_DSP, "source_id=%zu biquad filter update");
+ LOG_TRACE(Audio_DSP, "source_id=%zu biquad filter update", source_id);
}
if (config.interpolation_dirty) {
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
new file mode 100644
index 000000000..dc75c04ee
--- /dev/null
+++ b/src/audio_core/sdl2_sink.cpp
@@ -0,0 +1,126 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <list>
+#include <vector>
+
+#include <SDL.h>
+
+#include "audio_core/audio_core.h"
+#include "audio_core/sdl2_sink.h"
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include <numeric>
+
+namespace AudioCore {
+
+struct SDL2Sink::Impl {
+ unsigned int sample_rate = 0;
+
+ SDL_AudioDeviceID audio_device_id = 0;
+
+ std::list<std::vector<s16>> queue;
+
+ static void Callback(void* impl_, u8* buffer, int buffer_size_in_bytes);
+};
+
+SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
+ if (SDL_Init(SDL_INIT_AUDIO) < 0) {
+ LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed");
+ impl->audio_device_id = 0;
+ return;
+ }
+
+ SDL_AudioSpec desired_audiospec;
+ SDL_zero(desired_audiospec);
+ desired_audiospec.format = AUDIO_S16;
+ desired_audiospec.channels = 2;
+ desired_audiospec.freq = native_sample_rate;
+ desired_audiospec.samples = 1024;
+ desired_audiospec.userdata = impl.get();
+ desired_audiospec.callback = &Impl::Callback;
+
+ SDL_AudioSpec obtained_audiospec;
+ SDL_zero(obtained_audiospec);
+
+ impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0);
+ if (impl->audio_device_id <= 0) {
+ LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed");
+ return;
+ }
+
+ impl->sample_rate = obtained_audiospec.freq;
+
+ // SDL2 audio devices start out paused, unpause it:
+ SDL_PauseAudioDevice(impl->audio_device_id, 0);
+}
+
+SDL2Sink::~SDL2Sink() {
+ if (impl->audio_device_id <= 0)
+ return;
+
+ SDL_CloseAudioDevice(impl->audio_device_id);
+}
+
+unsigned int SDL2Sink::GetNativeSampleRate() const {
+ if (impl->audio_device_id <= 0)
+ return native_sample_rate;
+
+ return impl->sample_rate;
+}
+
+void SDL2Sink::EnqueueSamples(const std::vector<s16>& samples) {
+ if (impl->audio_device_id <= 0)
+ return;
+
+ ASSERT_MSG(samples.size() % 2 == 0, "Samples must be in interleaved stereo PCM16 format (size must be a multiple of two)");
+
+ SDL_LockAudioDevice(impl->audio_device_id);
+ impl->queue.emplace_back(samples);
+ SDL_UnlockAudioDevice(impl->audio_device_id);
+}
+
+size_t SDL2Sink::SamplesInQueue() const {
+ if (impl->audio_device_id <= 0)
+ return 0;
+
+ SDL_LockAudioDevice(impl->audio_device_id);
+
+ size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0),
+ [](size_t sum, const auto& buffer) {
+ // Division by two because each stereo sample is made of two s16.
+ return sum + buffer.size() / 2;
+ });
+
+ SDL_UnlockAudioDevice(impl->audio_device_id);
+
+ return total_size;
+}
+
+void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
+ Impl* impl = reinterpret_cast<Impl*>(impl_);
+
+ size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments.
+
+ while (remaining_size > 0 && !impl->queue.empty()) {
+ if (impl->queue.front().size() <= remaining_size) {
+ memcpy(buffer, impl->queue.front().data(), impl->queue.front().size() * sizeof(s16));
+ buffer += impl->queue.front().size() * sizeof(s16);
+ remaining_size -= impl->queue.front().size();
+ impl->queue.pop_front();
+ } else {
+ memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16));
+ buffer += remaining_size * sizeof(s16);
+ impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size);
+ remaining_size = 0;
+ }
+ }
+
+ if (remaining_size > 0) {
+ memset(buffer, 0, remaining_size * sizeof(s16));
+ }
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h
new file mode 100644
index 000000000..0f296b673
--- /dev/null
+++ b/src/audio_core/sdl2_sink.h
@@ -0,0 +1,30 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <memory>
+
+#include "audio_core/sink.h"
+
+namespace AudioCore {
+
+class SDL2Sink final : public Sink {
+public:
+ SDL2Sink();
+ ~SDL2Sink() override;
+
+ unsigned int GetNativeSampleRate() const override;
+
+ void EnqueueSamples(const std::vector<s16>& samples) override;
+
+ size_t SamplesInQueue() const override;
+
+private:
+ struct Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+} // namespace AudioCore
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
index cad21a85e..1c881c3d2 100644
--- a/src/audio_core/sink.h
+++ b/src/audio_core/sink.h
@@ -19,7 +19,7 @@ public:
virtual ~Sink() = default;
/// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec)
- virtual unsigned GetNativeSampleRate() const = 0;
+ virtual unsigned int GetNativeSampleRate() const = 0;
/**
* Feed stereo samples to sink.
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
index d2cc74103..ba5e83d17 100644
--- a/src/audio_core/sink_details.cpp
+++ b/src/audio_core/sink_details.cpp
@@ -8,10 +8,17 @@
#include "audio_core/null_sink.h"
#include "audio_core/sink_details.h"
+#ifdef HAVE_SDL2
+#include "audio_core/sdl2_sink.h"
+#endif
+
namespace AudioCore {
// g_sink_details is ordered in terms of desirability, with the best choice at the top.
const std::vector<SinkDetails> g_sink_details = {
+#ifdef HAVE_SDL2
+ { "sdl2", []() { return std::make_unique<SDL2Sink>(); } },
+#endif
{ "null", []() { return std::make_unique<NullSink>(); } },
};
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
new file mode 100644
index 000000000..ea38f40d0
--- /dev/null
+++ b/src/audio_core/time_stretch.cpp
@@ -0,0 +1,144 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <chrono>
+#include <cmath>
+#include <vector>
+
+#include <SoundTouch.h>
+
+#include "audio_core/audio_core.h"
+#include "audio_core/time_stretch.h"
+
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "common/math_util.h"
+
+using steady_clock = std::chrono::steady_clock;
+
+namespace AudioCore {
+
+constexpr double MIN_RATIO = 0.1;
+constexpr double MAX_RATIO = 100.0;
+
+static double ClampRatio(double ratio) {
+ return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO);
+}
+
+constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds
+constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds
+constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples
+
+constexpr double SMOOTHING_FACTOR = 0.007;
+
+struct TimeStretcher::Impl {
+ soundtouch::SoundTouch soundtouch;
+
+ steady_clock::time_point frame_timer = steady_clock::now();
+ size_t samples_queued = 0;
+
+ double smoothed_ratio = 1.0;
+
+ double sample_rate = static_cast<double>(native_sample_rate);
+};
+
+std::vector<s16> TimeStretcher::Process(size_t samples_in_queue) {
+ // This is a very simple algorithm without any fancy control theory. It works and is stable.
+
+ double ratio = CalculateCurrentRatio();
+ ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue);
+ impl->smoothed_ratio = (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio;
+ impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio);
+
+ // SoundTouch's tempo definition the inverse of our ratio definition.
+ impl->soundtouch.setTempo(1.0 / impl->smoothed_ratio);
+
+ std::vector<s16> samples = GetSamples();
+ if (samples_in_queue >= DROP_FRAMES_SAMPLE_DELAY) {
+ samples.clear();
+ LOG_DEBUG(Audio, "Dropping frames!");
+ }
+ return samples;
+}
+
+TimeStretcher::TimeStretcher() : impl(std::make_unique<Impl>()) {
+ impl->soundtouch.setPitch(1.0);
+ impl->soundtouch.setChannels(2);
+ impl->soundtouch.setSampleRate(native_sample_rate);
+ Reset();
+}
+
+TimeStretcher::~TimeStretcher() {
+ impl->soundtouch.clear();
+}
+
+void TimeStretcher::SetOutputSampleRate(unsigned int sample_rate) {
+ impl->sample_rate = static_cast<double>(sample_rate);
+ impl->soundtouch.setRate(static_cast<double>(native_sample_rate) / impl->sample_rate);
+}
+
+void TimeStretcher::AddSamples(const s16* buffer, size_t num_samples) {
+ impl->soundtouch.putSamples(buffer, static_cast<uint>(num_samples));
+ impl->samples_queued += num_samples;
+}
+
+void TimeStretcher::Flush() {
+ impl->soundtouch.flush();
+}
+
+void TimeStretcher::Reset() {
+ impl->soundtouch.setTempo(1.0);
+ impl->soundtouch.clear();
+ impl->smoothed_ratio = 1.0;
+ impl->frame_timer = steady_clock::now();
+ impl->samples_queued = 0;
+ SetOutputSampleRate(native_sample_rate);
+}
+
+double TimeStretcher::CalculateCurrentRatio() {
+ const steady_clock::time_point now = steady_clock::now();
+ const std::chrono::duration<double> duration = now - impl->frame_timer;
+
+ const double expected_time = static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate);
+ const double actual_time = duration.count();
+
+ double ratio;
+ if (expected_time != 0) {
+ ratio = ClampRatio(actual_time / expected_time);
+ } else {
+ ratio = impl->smoothed_ratio;
+ }
+
+ impl->frame_timer = now;
+ impl->samples_queued = 0;
+
+ return ratio;
+}
+
+double TimeStretcher::CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const {
+ const size_t min_sample_delay = static_cast<size_t>(MIN_DELAY_TIME * impl->sample_rate);
+ const size_t max_sample_delay = static_cast<size_t>(MAX_DELAY_TIME * impl->sample_rate);
+
+ if (sample_delay < min_sample_delay) {
+ // Make the ratio bigger.
+ ratio = ratio > 1.0 ? ratio * ratio : sqrt(ratio);
+ } else if (sample_delay > max_sample_delay) {
+ // Make the ratio smaller.
+ ratio = ratio > 1.0 ? sqrt(ratio) : ratio * ratio;
+ }
+
+ return ClampRatio(ratio);
+}
+
+std::vector<s16> TimeStretcher::GetSamples() {
+ uint available = impl->soundtouch.numSamples();
+
+ std::vector<s16> output(static_cast<size_t>(available) * 2);
+
+ impl->soundtouch.receiveSamples(output.data(), available);
+
+ return output;
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
new file mode 100644
index 000000000..1fde3f72a
--- /dev/null
+++ b/src/audio_core/time_stretch.h
@@ -0,0 +1,57 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace AudioCore {
+
+class TimeStretcher final {
+public:
+ TimeStretcher();
+ ~TimeStretcher();
+
+ /**
+ * Set sample rate for the samples that Process returns.
+ * @param sample_rate The sample rate.
+ */
+ void SetOutputSampleRate(unsigned int sample_rate);
+
+ /**
+ * Add samples to be processed.
+ * @param sample_buffer Buffer of samples in interleaved stereo PCM16 format.
+ * @param num_sample Number of samples.
+ */
+ void AddSamples(const s16* sample_buffer, size_t num_samples);
+
+ /// Flush audio remaining in internal buffers.
+ void Flush();
+
+ /// Resets internal state and clears buffers.
+ void Reset();
+
+ /**
+ * Does audio stretching and produces the time-stretched samples.
+ * Timer calculations use sample_delay to determine how much of a margin we have.
+ * @param sample_delay How many samples are buffered downstream of this module and haven't been played yet.
+ * @return Samples to play in interleaved stereo PCM16 format.
+ */
+ std::vector<s16> Process(size_t sample_delay);
+
+private:
+ struct Impl;
+ std::unique_ptr<Impl> impl;
+
+ /// INTERNAL: ratio = wallclock time / emulated time
+ double CalculateCurrentRatio();
+ /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate direction.
+ double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const;
+ /// INTERNAL: Gets the time-stretched samples from SoundTouch.
+ std::vector<s16> GetSamples();
+};
+
+} // namespace AudioCore
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 0d17c80bf..c64de8e22 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -77,15 +77,16 @@ 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");
// Debugging
Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
- Settings::values.gdbstub_port = sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689);
+ Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
}
void Config::Reload() {
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 0e6171736..49126356f 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -58,7 +58,7 @@ bg_green =
[Audio]
# Which audio output engine to use.
-# auto (default): Auto-select, null: No audio output
+# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
output_engine =
[Data Storage]
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index 924189f4c..12cdd9d95 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -9,6 +9,8 @@
#define SDL_MAIN_HANDLED
#include <SDL.h>
+#include <glad/glad.h>
+
#include "common/key_map.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
@@ -98,6 +100,11 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
exit(1);
}
+ if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
+ LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting...");
+ exit(1);
+ }
+
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 2c7e80106..43a766053 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -2,8 +2,6 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(SRCS
- config/controller_config.cpp
- config/controller_config_util.cpp
config.cpp
debugger/callstack.cpp
debugger/disassembler.cpp
@@ -20,6 +18,7 @@ set(SRCS
util/spinbox.cpp
util/util.cpp
bootmanager.cpp
+ configure_audio.cpp
configure_debug.cpp
configure_dialog.cpp
configure_general.cpp
@@ -32,8 +31,6 @@ set(SRCS
)
set(HEADERS
- config/controller_config.h
- config/controller_config_util.h
config.h
debugger/callstack.h
debugger/disassembler.h
@@ -51,6 +48,7 @@ set(HEADERS
util/spinbox.h
util/util.h
bootmanager.h
+ configure_audio.h
configure_debug.h
configure_dialog.h
configure_general.h
@@ -63,12 +61,12 @@ set(HEADERS
)
set(UIS
- config/controller_config.ui
debugger/callstack.ui
debugger/disassembler.ui
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/config/controller_config.cpp b/src/citra_qt/config/controller_config.cpp
deleted file mode 100644
index 512879f1b..000000000
--- a/src/citra_qt/config/controller_config.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <QDialogButtonBox>
-
-#include "controller_config.h"
-#include "controller_config_util.h"
-
-/* TODO(bunnei): ImplementMe
-
-using common::Config;
-
-GControllerConfig::GControllerConfig(common::Config::ControllerPort* initial_config, QWidget* parent) : QWidget(parent)
-{
- ui.setupUi(this);
- ((QGridLayout*)ui.mainStickTab->layout())->addWidget(new GStickConfig(Config::ANALOG_LEFT, Config::ANALOG_RIGHT, Config::ANALOG_UP, Config::ANALOG_DOWN, this, this), 1, 1);
- ((QGridLayout*)ui.cStickTab->layout())->addWidget(new GStickConfig(Config::C_LEFT, Config::C_RIGHT, Config::C_UP, Config::C_DOWN, this, this), 1, 1);
- ((QGridLayout*)ui.dPadTab->layout())->addWidget(new GStickConfig(Config::DPAD_LEFT, Config::DPAD_RIGHT, Config::DPAD_UP, Config::DPAD_DOWN, this, this), 1, 1);
-
- // TODO: Arrange these more compactly?
- QVBoxLayout* layout = (QVBoxLayout*)ui.buttonsTab->layout();
- layout->addWidget(new GButtonConfigGroup("A Button", Config::BUTTON_A, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("B Button", Config::BUTTON_B, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("X Button", Config::BUTTON_X, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Y Button", Config::BUTTON_Y, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Z Button", Config::BUTTON_Z, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("L Trigger", Config::TRIGGER_L, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("R Trigger", Config::TRIGGER_R, this, ui.buttonsTab));
- layout->addWidget(new GButtonConfigGroup("Start Button", Config::BUTTON_START, this, ui.buttonsTab));
-
- memcpy(config, initial_config, sizeof(config));
-
- emit ActivePortChanged(config[0]);
-}
-
-void GControllerConfig::OnKeyConfigChanged(common::Config::Control id, int key, const QString& name)
-{
- if (InputSourceJoypad())
- {
- config[GetActiveController()].pads.key_code[id] = key;
- }
- else
- {
- config[GetActiveController()].keys.key_code[id] = key;
- }
- emit ActivePortChanged(config[GetActiveController()]);
-}
-
-int GControllerConfig::GetActiveController()
-{
- return ui.activeControllerCB->currentIndex();
-}
-
-bool GControllerConfig::InputSourceJoypad()
-{
- return ui.inputSourceCB->currentIndex() == 1;
-}
-
-GControllerConfigDialog::GControllerConfigDialog(common::Config::ControllerPort* controller_ports, QWidget* parent) : QDialog(parent), config_ptr(controller_ports)
-{
- setWindowTitle(tr("Input configuration"));
-
- QVBoxLayout* layout = new QVBoxLayout(this);
- config_widget = new GControllerConfig(controller_ports, this);
- layout->addWidget(config_widget);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- layout->addWidget(buttons);
-
- connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
- connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
-
- connect(this, SIGNAL(accepted()), this, SLOT(EnableChanges()));
-
- layout->setSizeConstraint(QLayout::SetFixedSize);
- setLayout(layout);
- setModal(true);
- show();
-}
-
-void GControllerConfigDialog::EnableChanges()
-{
- for (unsigned int i = 0; i < 4; ++i)
- {
- memcpy(&config_ptr[i], &config_widget->GetControllerConfig(i), sizeof(common::Config::ControllerPort));
-
- if (common::g_config) {
- // Apply changes if running a game
- memcpy(&common::g_config->controller_ports(i), &config_widget->GetControllerConfig(i), sizeof(common::Config::ControllerPort));
- }
- }
-}
-
-*/
diff --git a/src/citra_qt/config/controller_config.h b/src/citra_qt/config/controller_config.h
deleted file mode 100644
index 451593de1..000000000
--- a/src/citra_qt/config/controller_config.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#ifndef _CONTROLLER_CONFIG_HXX_
-#define _CONTROLLER_CONFIG_HXX_
-
-#include <QDialog>
-
-//#include "ui_controller_config.h"
-
-/* TODO(bunnei): ImplementMe
-
-#include "config.h"
-
-class GControllerConfig : public QWidget
-{
- Q_OBJECT
-
-public:
- GControllerConfig(common::Config::ControllerPort* initial_config, QWidget* parent = NULL);
-
- const common::Config::ControllerPort& GetControllerConfig(int index) const { return config[index]; }
-
-signals:
- void ActivePortChanged(const common::Config::ControllerPort&);
-
-public slots:
- void OnKeyConfigChanged(common::Config::Control id, int key, const QString& name);
-
-private:
- int GetActiveController();
- bool InputSourceJoypad();
-
- Ui::ControllerConfig ui;
- common::Config::ControllerPort config[4];
-};
-
-class GControllerConfigDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- GControllerConfigDialog(common::Config::ControllerPort* controller_ports, QWidget* parent = NULL);
-
-public slots:
- void EnableChanges();
-
-private:
- GControllerConfig* config_widget;
- common::Config::ControllerPort* config_ptr;
-};
-
-*/
-
-#endif // _CONTROLLER_CONFIG_HXX_
diff --git a/src/citra_qt/config/controller_config.ui b/src/citra_qt/config/controller_config.ui
deleted file mode 100644
index 9f650047b..000000000
--- a/src/citra_qt/config/controller_config.ui
+++ /dev/null
@@ -1,308 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>ControllerConfig</class>
- <widget class="QWidget" name="ControllerConfig">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>503</width>
- <height>293</height>
- </rect>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="windowTitle">
- <string>Controller Configuration</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="sizeConstraint">
- <enum>QLayout::SetFixedSize</enum>
- </property>
- <item>
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="1">
- <widget class="QComboBox" name="activeControllerCB">
- <item>
- <property name="text">
- <string>Controller 1</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 2</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 3</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Controller 4</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="0" column="2">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="2">
- <widget class="QCheckBox" name="checkBox">
- <property name="text">
- <string>Enabled</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="inputSourceCB">
- <item>
- <property name="text">
- <string>Keyboard</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Joypad</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Active Controller:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Input Source:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QTabWidget" name="tabWidget">
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="mainStickTab">
- <attribute name="title">
- <string>Main Stick</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="2" column="2">
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="2">
- <spacer name="verticalSpacer_3">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="4">
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="cStickTab">
- <attribute name="title">
- <string>C-Stick</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="1" column="0">
- <spacer name="horizontalSpacer_6">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <spacer name="verticalSpacer_5">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="1">
- <spacer name="verticalSpacer_4">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="2">
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="dPadTab">
- <attribute name="title">
- <string>D-Pad</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_5">
- <item row="1" column="2">
- <spacer name="horizontalSpacer_7">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <spacer name="verticalSpacer_7">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="1">
- <spacer name="verticalSpacer_6">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer_8">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="buttonsTab">
- <attribute name="title">
- <string>Buttons</string>
- </attribute>
- <layout class="QVBoxLayout" name="verticalLayout_2"/>
- </widget>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
- <slots>
- <signal>ControlsChanged()</signal>
- <signal>MainStickCleared()</signal>
- <signal>CStickCleared()</signal>
- <signal>DPadCleared()</signal>
- <signal>ButtonsCleared()</signal>
- <slot>OnControlsChanged()</slot>
- <slot>OnMainStickCleared()</slot>
- <slot>OnCStickCleared()</slot>
- <slot>OnDPadCleared()</slot>
- <slot>OnButtonsCleared()</slot>
- </slots>
-</ui>
diff --git a/src/citra_qt/config/controller_config_util.cpp b/src/citra_qt/config/controller_config_util.cpp
deleted file mode 100644
index d68b119df..000000000
--- a/src/citra_qt/config/controller_config_util.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <QPushButton>
-#include <QStyle>
-#include <QGridLayout>
-#include <QKeyEvent>
-#include <QHBoxLayout>
-#include <QLabel>
-
-#include "controller_config_util.h"
-
-/* TODO(bunnei): ImplementMe
-GStickConfig::GStickConfig(common::Config::Control leftid, common::Config::Control rightid, common::Config::Control upid, common::Config::Control downid, QObject* change_receiver, QWidget* parent) : QWidget(parent)
-{
- left = new GKeyConfigButton(leftid, style()->standardIcon(QStyle::SP_ArrowLeft), QString(), change_receiver, this);
- right = new GKeyConfigButton(rightid, style()->standardIcon(QStyle::SP_ArrowRight), QString(), change_receiver, this);
- up = new GKeyConfigButton(upid, style()->standardIcon(QStyle::SP_ArrowUp), QString(), change_receiver, this);
- down = new GKeyConfigButton(downid, style()->standardIcon(QStyle::SP_ArrowDown), QString(), change_receiver, this);
- clear = new QPushButton(tr("Clear"), this);
-
- QGridLayout* layout = new QGridLayout(this);
- layout->addWidget(left, 1, 0);
- layout->addWidget(right, 1, 2);
- layout->addWidget(up, 0, 1);
- layout->addWidget(down, 2, 1);
- layout->addWidget(clear, 1, 1);
-
- setLayout(layout);
-}
-
-GKeyConfigButton::GKeyConfigButton(common::Config::Control id, const QIcon& icon, const QString& text, QObject* change_receiver, QWidget* parent) : QPushButton(icon, text, parent), id(id), inputGrabbed(false)
-{
- connect(this, SIGNAL(clicked()), this, SLOT(OnClicked()));
- connect(this, SIGNAL(KeyAssigned(common::Config::Control, int, const QString&)), change_receiver, SLOT(OnKeyConfigChanged(common::Config::Control, int, const QString&)));
- connect(change_receiver, SIGNAL(ActivePortChanged(const common::Config::ControllerPort&)), this, SLOT(OnActivePortChanged(const common::Config::ControllerPort&)));
-}
-
-GKeyConfigButton::GKeyConfigButton(common::Config::Control id, const QString& text, QObject* change_receiver, QWidget* parent) : QPushButton(text, parent), id(id), inputGrabbed(false)
-{
- connect(this, SIGNAL(clicked()), this, SLOT(OnClicked()));
- connect(this, SIGNAL(KeyAssigned(common::Config::Control, int, const QString&)), change_receiver, SLOT(OnKeyConfigChanged(common::Config::Control, int, const QString&)));
- connect(change_receiver, SIGNAL(ActivePortChanged(const common::Config::ControllerPort&)), this, SLOT(OnActivePortChanged(const common::Config::ControllerPort&)));
-}
-
-void GKeyConfigButton::OnActivePortChanged(const common::Config::ControllerPort& config)
-{
- // TODO: Doesn't use joypad struct if that's the input source...
- QString text = QKeySequence(config.keys.key_code[id]).toString(); // has a nicer format
- if (config.keys.key_code[id] == Qt::Key_Shift) text = tr("Shift");
- else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control");
- else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt");
- else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta");
- setText(text);
-}
-
-void GKeyConfigButton::OnClicked()
-{
- grabKeyboard();
- grabMouse();
- inputGrabbed = true;
-
- old_text = text();
- setText(tr("Input..."));
-}
-
-void GKeyConfigButton::keyPressEvent(QKeyEvent* event)
-{
- if (inputGrabbed)
- {
- releaseKeyboard();
- releaseMouse();
- setText(QString());
-
- // TODO: Doesn't capture "return" key
- // TODO: This doesn't quite work well, yet... find a better way
- QString text = QKeySequence(event->key()).toString(); // has a nicer format than event->text()
- int key = event->key();
- if (event->modifiers() == Qt::ShiftModifier) { text = tr("Shift"); key = Qt::Key_Shift; }
- else if (event->modifiers() == Qt::ControlModifier) { text = tr("Ctrl"); key = Qt::Key_Control; }
- else if (event->modifiers() == Qt::AltModifier) { text = tr("Alt"); key = Qt::Key_Alt; }
- else if (event->modifiers() == Qt::MetaModifier) { text = tr("Meta"); key = Qt::Key_Meta; }
-
- setText(old_text);
- emit KeyAssigned(id, key, text);
-
- inputGrabbed = false;
-
- // TODO: Keys like "return" cause another keyPressEvent to be generated after this one...
- }
-
- QPushButton::keyPressEvent(event); // TODO: Necessary?
-}
-
-void GKeyConfigButton::mousePressEvent(QMouseEvent* event)
-{
- // Abort key assignment
- if (inputGrabbed)
- {
- releaseKeyboard();
- releaseMouse();
- setText(old_text);
- inputGrabbed = false;
- }
-
- QAbstractButton::mousePressEvent(event);
-}
-
-GButtonConfigGroup::GButtonConfigGroup(const QString& name, common::Config::Control id, QObject* change_receiver, QWidget* parent) : QWidget(parent), id(id)
-{
- QHBoxLayout* layout = new QHBoxLayout(this);
-
- QPushButton* clear_button = new QPushButton(tr("Clear"));
-
- layout->addWidget(new QLabel(name, this));
- layout->addWidget(config_button = new GKeyConfigButton(id, QString(), change_receiver, this));
- layout->addWidget(clear_button);
-
- // TODO: connect config_button, clear_button
-
- setLayout(layout);
-}
-
-*/
diff --git a/src/citra_qt/config/controller_config_util.h b/src/citra_qt/config/controller_config_util.h
deleted file mode 100644
index 15e025b57..000000000
--- a/src/citra_qt/config/controller_config_util.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#ifndef _CONTROLLER_CONFIG_UTIL_HXX_
-#define _CONTROLLER_CONFIG_UTIL_HXX_
-
-#include <QWidget>
-#include <QPushButton>
-
-/* TODO(bunnei): ImplementMe
-
-#include "config.h"
-
-class GStickConfig : public QWidget
-{
- Q_OBJECT
-
-public:
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GStickConfig(common::Config::Control leftid, common::Config::Control rightid, common::Config::Control upid, common::Config::Control downid, QObject* change_receiver, QWidget* parent = NULL);
-
-signals:
- void LeftChanged();
- void RightChanged();
- void UpChanged();
- void DownChanged();
-
-private:
- QPushButton* left;
- QPushButton* right;
- QPushButton* up;
- QPushButton* down;
-
- QPushButton* clear;
-};
-
-class GKeyConfigButton : public QPushButton
-{
- Q_OBJECT
-
-public:
- // TODO: change_receiver also needs to have an ActivePortChanged(const common::Config::ControllerPort&) signal
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GKeyConfigButton(common::Config::Control id, const QIcon& icon, const QString& text, QObject* change_receiver, QWidget* parent);
- GKeyConfigButton(common::Config::Control id, const QString& text, QObject* change_receiver, QWidget* parent);
-
-signals:
- void KeyAssigned(common::Config::Control id, int key, const QString& text);
-
-private slots:
- void OnActivePortChanged(const common::Config::ControllerPort& config);
-
- void OnClicked();
-
- void keyPressEvent(QKeyEvent* event); // TODO: bGrabbed?
- void mousePressEvent(QMouseEvent* event);
-
-private:
- common::Config::Control id;
- bool inputGrabbed;
-
- QString old_text;
-};
-
-class GButtonConfigGroup : public QWidget
-{
- Q_OBJECT
-
-public:
- // change_receiver needs to have a OnKeyConfigChanged(common::Config::Control, int, const QString&) slot!
- GButtonConfigGroup(const QString& name, common::Config::Control id, QObject* change_receiver, QWidget* parent = NULL);
-
-private:
- GKeyConfigButton* config_button;
-
- common::Config::Control id;
-};
-
-*/
-
-#endif // _CONTROLLER_CONFIG_HXX_
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/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index 793944639..1a3077495 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -37,10 +37,13 @@ void CallstackWidget::OnDebugModeEntered()
int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
{
+ if (!Memory::IsValidVirtualAddress(addr))
+ break;
+
const u32 ret_addr = Memory::Read32(addr);
const u32 call_addr = ret_addr - 4; //get call address???
- if (Memory::GetPointer(call_addr) == nullptr)
+ if (!Memory::IsValidVirtualAddress(call_addr))
break;
/* TODO (mattvail) clean me, move to debugger interface */
diff --git a/src/citra_qt/debugger/graphics_tracing.cpp b/src/citra_qt/debugger/graphics_tracing.cpp
index 1402f8e79..9c80f7ec9 100644
--- a/src/citra_qt/debugger/graphics_tracing.cpp
+++ b/src/citra_qt/debugger/graphics_tracing.cpp
@@ -74,7 +74,7 @@ void GraphicsTracingWidget::StartRecording() {
std::array<u32, 4 * 16> default_attributes;
for (unsigned i = 0; i < 16; ++i) {
for (unsigned comp = 0; comp < 3; ++comp) {
- default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs.default_attributes[i][comp].ToFloat32());
+ default_attributes[4 * i + comp] = nihstro::to_float24(Pica::g_state.vs_default_attributes[i][comp].ToFloat32());
}
}
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
index 6e8d7ef42..391666d35 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -501,7 +501,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
info.labels.insert({ entry_point, "main" });
// Generate debug information
- debug_data = Pica::Shader::ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup);
+ debug_data = Pica::g_state.vs.ProduceDebugInfo(input_vertex, num_attributes, shader_config, shader_setup);
// Reload widget state
for (int attr = 0; attr < num_attributes; ++attr) {
@@ -515,7 +515,7 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
}
// Initialize debug info text for current cycle count
- cycle_index->setMaximum(debug_data.records.size() - 1);
+ cycle_index->setMaximum(static_cast<int>(debug_data.records.size() - 1));
OnCycleIndexChanged(cycle_index->value());
model->endResetModel();
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..15484fae3 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -118,46 +118,33 @@ void GameList::LoadInterfaceLayout()
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
}
-void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool deep_scan)
+void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion)
{
const auto callback = [&](unsigned* num_entries_out,
const std::string& directory,
- const std::string& virtual_name) -> bool {
+ const std::string& virtual_name,
+ unsigned int recursion) -> bool {
std::string physical_name = directory + DIR_SEP + virtual_name;
if (stop_processing)
return false; // Breaks the callback loop.
- 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());
+ if (!FileUtil::IsDirectory(physical_name)) {
+ 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)),
});
+ } else if (recursion > 0) {
+ AddFstEntriesToGameList(physical_name, recursion - 1);
}
return true;
@@ -169,7 +156,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
void GameListWorker::run()
{
stop_processing = false;
- AddFstEntriesToGameList(dir_path.toStdString(), deep_scan);
+ AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0);
emit Finished();
}
diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h
index 284f5da81..353b2d1e2 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 {
@@ -212,5 +181,5 @@ private:
bool deep_scan;
std::atomic_bool stop_processing;
- void AddFstEntriesToGameList(const std::string& dir_path, bool deep_scan);
+ void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index dfc7c0752..0ed1ffa5a 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -6,6 +6,9 @@
#include <memory>
#include <thread>
+#include <glad/glad.h>
+
+#define QT_NO_OPENGL
#include <QDesktopWidget>
#include <QtGui>
#include <QFileDialog>
@@ -240,6 +243,14 @@ bool GMainWindow::InitializeSystem() {
if (emu_thread != nullptr)
ShutdownGame();
+ render_window->MakeCurrent();
+ if (!gladLoadGL()) {
+ QMessageBox::critical(this, tr("Error while starting Citra!"),
+ tr("Failed to initialize the video core!\n\n"
+ "Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver."));
+ return false;
+ }
+
// Initialize the core emulation
System::Result system_result = System::Init(render_window);
if (System::Result::Success != system_result) {
@@ -261,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/citra_qt/util/util.cpp b/src/citra_qt/util/util.cpp
index 8734a8efd..2f9beb5cc 100644
--- a/src/citra_qt/util/util.cpp
+++ b/src/citra_qt/util/util.cpp
@@ -19,7 +19,7 @@ QString ReadableByteSize(qulonglong size) {
static const std::array<const char*, 6> units = { "B", "KiB", "MiB", "GiB", "TiB", "PiB" };
if (size == 0)
return "0";
- int digit_groups = std::min<int>((int)(std::log10(size) / std::log10(1024)), units.size());
+ int digit_groups = std::min<int>(static_cast<int>(std::log10(size) / std::log10(1024)), static_cast<int>(units.size()));
return QString("%L1 %2").arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
.arg(units[digit_groups]);
}
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index ab3515683..4633897ce 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -72,18 +72,24 @@ inline u64 _rotr64(u64 x, unsigned int shift){
}
#else // _MSC_VER
- #if (_MSC_VER < 1900)
- // Function Cross-Compatibility
- #define snprintf _snprintf
- #endif
-
- // Locale Cross-Compatibility
- #define locale_t _locale_t
-
- extern "C" {
- __declspec(dllimport) void __stdcall DebugBreak(void);
- }
- #define Crash() {DebugBreak();}
+
+#if (_MSC_VER < 1900)
+ // Function Cross-Compatibility
+ #define snprintf _snprintf
+#endif
+
+// Locale Cross-Compatibility
+#define locale_t _locale_t
+
+extern "C" {
+ __declspec(dllimport) void __stdcall DebugBreak(void);
+}
+#define Crash() {DebugBreak();}
+
+// cstdlib provides these on MSVC
+#define rotr _rotr
+#define rotl _rotl
+
#endif // _MSC_VER ndef
// Generic function to get last error message.
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 6e2867658..17af7c385 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -434,7 +434,7 @@ bool CreateEmptyFile(const std::string &filename)
}
-bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback)
+bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback, unsigned int recursion)
{
LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
@@ -472,7 +472,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
continue;
unsigned ret_entries = 0;
- if (!callback(&ret_entries, directory, virtual_name)) {
+ if (!callback(&ret_entries, directory, virtual_name, recursion)) {
callback_error = true;
break;
}
@@ -486,30 +486,34 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
closedir(dirp);
#endif
- if (!callback_error) {
- // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
- if (num_entries_out != nullptr)
- *num_entries_out = found_entries;
- return true;
- } else {
+ if (callback_error)
return false;
- }
+
+ // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
+ if (num_entries_out != nullptr)
+ *num_entries_out = found_entries;
+ return true;
}
-unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
+unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion)
{
const auto callback = [&parent_entry](unsigned* num_entries_out,
const std::string& directory,
- const std::string& virtual_name) -> bool {
+ const std::string& virtual_name,
+ unsigned int recursion) -> bool {
FSTEntry entry;
entry.virtualName = virtual_name;
entry.physicalName = directory + DIR_SEP + virtual_name;
if (IsDirectory(entry.physicalName)) {
entry.isDirectory = true;
- // is a directory, lets go inside
- entry.size = ScanDirectoryTree(entry.physicalName, entry);
- *num_entries_out += (int)entry.size;
+ // is a directory, lets go inside if we didn't recurse to often
+ if (recursion > 0) {
+ entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1);
+ *num_entries_out += (int)entry.size;
+ } else {
+ entry.size = 0;
+ }
} else { // is a file
entry.isDirectory = false;
entry.size = GetSize(entry.physicalName);
@@ -522,23 +526,27 @@ unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
};
unsigned num_entries;
- return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0;
+ return ForeachDirectoryEntry(&num_entries, directory, callback, recursion) ? num_entries : 0;
}
-bool DeleteDirRecursively(const std::string &directory)
+bool DeleteDirRecursively(const std::string &directory, unsigned int recursion)
{
const static auto callback = [](unsigned* num_entries_out,
const std::string& directory,
- const std::string& virtual_name) -> bool {
+ const std::string& virtual_name,
+ unsigned int recursion) -> bool {
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
- if (IsDirectory(new_path))
- return DeleteDirRecursively(new_path);
+ if (IsDirectory(new_path)) {
+ if (recursion == 0)
+ return false;
+ return DeleteDirRecursively(new_path, recursion - 1);
+ }
return Delete(new_path);
};
- if (!ForeachDirectoryEntry(nullptr, directory, callback))
+ if (!ForeachDirectoryEntry(nullptr, directory, callback, recursion))
return false;
// Delete the outermost directory
diff --git a/src/common/file_util.h b/src/common/file_util.h
index c6a8694ce..32ae2dc57 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -105,11 +105,13 @@ bool CreateEmptyFile(const std::string &filename);
* @param num_entries_out to be assigned by the callable with the number of iterated directory entries, never null
* @param directory the path to the enclosing directory
* @param virtual_name the entry name, without any preceding directory info
+ * @param recursion Number of children directory to read before giving up
* @return whether handling the entry succeeded
*/
using DirectoryEntryCallable = std::function<bool(unsigned* num_entries_out,
const std::string& directory,
- const std::string& virtual_name)>;
+ const std::string& virtual_name,
+ unsigned int recursion)>;
/**
* Scans a directory, calling the callback for each file/directory contained within.
@@ -117,20 +119,22 @@ using DirectoryEntryCallable = std::function<bool(unsigned* num_entries_out,
* @param num_entries_out assigned by the function with the number of iterated directory entries, can be null
* @param directory the directory to scan
* @param callback The callback which will be called for each entry
+ * @param recursion Number of children directories to read before giving up
* @return whether scanning the directory succeeded
*/
-bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback);
+bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback, unsigned int recursion = 0);
/**
* Scans the directory tree, storing the results.
* @param directory the parent directory to start scanning from
* @param parent_entry FSTEntry where the filesystem tree results will be stored.
+ * @param recursion Number of children directories to read before giving up.
* @return the total number of files/directories found
*/
-unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry);
+unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry, unsigned int recursion = 0);
// deletes the given directory and anything under it. Returns true on success.
-bool DeleteDirRecursively(const std::string &directory);
+bool DeleteDirRecursively(const std::string &directory, unsigned int recursion = 256);
// Returns the current directory
std::string GetCurrentDir();
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 3d39f94d5..d7008fc66 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -65,6 +65,7 @@ namespace Log {
SUB(Render, OpenGL) \
CLS(Audio) \
SUB(Audio, DSP) \
+ SUB(Audio, Sink) \
CLS(Loader)
// GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 521362317..c6910b1c7 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -78,8 +78,9 @@ enum class Class : ClassType {
Render, ///< Emulator video output and hardware acceleration
Render_Software, ///< Software renderer backend
Render_OpenGL, ///< OpenGL backend
- Audio, ///< Emulator audio output
+ Audio, ///< Audio emulation
Audio_DSP, ///< The HLE implementation of the DSP
+ Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
Count ///< Total number of logging classes
diff --git a/src/common/swap.h b/src/common/swap.h
index a7c37bc44..1749bd7a4 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -25,6 +25,8 @@
#include <sys/endian.h>
#endif
+#include <cstring>
+
#include "common/common_types.h"
// GCC 4.6+
@@ -58,9 +60,6 @@
namespace Common {
-inline u8 swap8(u8 _data) {return _data;}
-inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];}
-
#ifdef _MSC_VER
inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
@@ -92,52 +91,29 @@ inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 3
#endif
inline float swapf(float f) {
- union {
- float f;
- unsigned int u32;
- } dat1, dat2;
-
- dat1.f = f;
- dat2.u32 = swap32(dat1.u32);
+ static_assert(sizeof(u32) == sizeof(float),
+ "float must be the same size as uint32_t.");
- return dat2.f;
-}
-
-inline double swapd(double f) {
- union {
- double f;
- unsigned long long u64;
- } dat1, dat2;
+ u32 value;
+ std::memcpy(&value, &f, sizeof(u32));
- dat1.f = f;
- dat2.u64 = swap64(dat1.u64);
+ value = swap32(value);
+ std::memcpy(&f, &value, sizeof(u32));
- return dat2.f;
+ return f;
}
-inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);}
-inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);}
-inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);}
-
-template <int count>
-void swap(u8*);
+inline double swapd(double f) {
+ static_assert(sizeof(u64) == sizeof(double),
+ "double must be the same size as uint64_t.");
-template <>
-inline void swap<1>(u8* data) { }
+ u64 value;
+ std::memcpy(&value, &f, sizeof(u64));
-template <>
-inline void swap<2>(u8* data) {
- *reinterpret_cast<u16*>(data) = swap16(data);
-}
-
-template <>
-inline void swap<4>(u8* data) {
- *reinterpret_cast<u32*>(data) = swap32(data);
-}
+ value = swap64(value);
+ std::memcpy(&f, &value, sizeof(u64));
-template <>
-inline void swap<8>(u8* data) {
- *reinterpret_cast<u64*>(data) = swap64(data);
+ return f;
}
} // Namespace Common
@@ -534,35 +510,35 @@ bool operator==(const S &p, const swap_struct_t<T, F> v) {
template <typename T>
struct swap_64_t {
static T swap(T x) {
- return (T)Common::swap64(*(u64 *)&x);
+ return static_cast<T>(Common::swap64(x));
}
};
template <typename T>
struct swap_32_t {
static T swap(T x) {
- return (T)Common::swap32(*(u32 *)&x);
+ return static_cast<T>(Common::swap32(x));
}
};
template <typename T>
struct swap_16_t {
static T swap(T x) {
- return (T)Common::swap16(*(u16 *)&x);
+ return static_cast<T>(Common::swap16(x));
}
};
template <typename T>
struct swap_float_t {
static T swap(T x) {
- return (T)Common::swapf(*(float *)&x);
+ return static_cast<T>(Common::swapf(x));
}
};
template <typename T>
struct swap_double_t {
static T swap(T x) {
- return (T)Common::swapd(*(double *)&x);
+ return static_cast<T>(Common::swapd(x));
}
};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a8d891689..e9b04098b 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
@@ -52,6 +53,7 @@ set(SRCS
hle/service/apt/apt_a.cpp
hle/service/apt/apt_s.cpp
hle/service/apt/apt_u.cpp
+ hle/service/apt/bcfnt/bcfnt.cpp
hle/service/boss/boss.cpp
hle/service/boss/boss_p.cpp
hle/service/boss/boss_u.cpp
@@ -68,7 +70,10 @@ set(SRCS
hle/service/cfg/cfg_s.cpp
hle/service/cfg/cfg_u.cpp
hle/service/csnd_snd.cpp
- hle/service/dlp_srvr.cpp
+ hle/service/dlp/dlp.cpp
+ hle/service/dlp/dlp_clnt.cpp
+ hle/service/dlp/dlp_fkcl.cpp
+ hle/service/dlp/dlp_srvr.cpp
hle/service/dsp_dsp.cpp
hle/service/err_f.cpp
hle/service/frd/frd.cpp
@@ -119,6 +124,7 @@ set(SRCS
loader/elf.cpp
loader/loader.cpp
loader/ncch.cpp
+ loader/smdh.cpp
tracer/recorder.cpp
memory.cpp
settings.cpp
@@ -175,6 +181,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
@@ -185,6 +192,7 @@ set(HEADERS
hle/service/apt/apt_a.h
hle/service/apt/apt_s.h
hle/service/apt/apt_u.h
+ hle/service/apt/bcfnt/bcfnt.h
hle/service/boss/boss.h
hle/service/boss/boss_p.h
hle/service/boss/boss_u.h
@@ -201,7 +209,10 @@ set(HEADERS
hle/service/cfg/cfg_s.h
hle/service/cfg/cfg_u.h
hle/service/csnd_snd.h
- hle/service/dlp_srvr.h
+ hle/service/dlp/dlp.h
+ hle/service/dlp/dlp_clnt.h
+ hle/service/dlp/dlp_fkcl.h
+ hle/service/dlp/dlp_srvr.h
hle/service/dsp_dsp.h
hle/service/err_f.h
hle/service/frd/frd.h
@@ -252,6 +263,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.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index a3581132c..13492a08b 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -93,7 +93,7 @@ void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 e
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
- context.cpsr = 0x1F | ((entry_point & 1) << 5); // Usermode and THUMB mode
+ context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode
}
void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
diff --git a/src/core/arm/dyncom/arm_dyncom_dec.cpp b/src/core/arm/dyncom/arm_dyncom_dec.cpp
index 8cd6755cb..247d379e3 100644
--- a/src/core/arm/dyncom/arm_dyncom_dec.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_dec.cpp
@@ -422,6 +422,10 @@ ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx) {
n = arm_instruction[i].attribute_value;
base = 0;
+ // 3DS has no VFP3 support
+ if (arm_instruction[i].version == ARMVFP3)
+ continue;
+
while (n) {
if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) {
// clrex
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 45914d479..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?
@@ -560,7 +568,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
if (vdm.exponent >= 1023 + 32) {
d = vdm.sign ? 0 : 0xffffffff;
exceptions = FPSCR_IOC;
- } else if (vdm.exponent >= 1023 - 1) {
+ } else if (vdm.exponent >= 1023) {
int shift = 1023 + 63 - vdm.exponent;
u64 rem, incr = 0;
@@ -595,12 +603,20 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
} else {
d = 0;
if (vdm.exponent | vdm.significand) {
- exceptions |= FPSCR_IXC;
- if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ if (vdm.exponent >= 1022) {
+ d = vdm.sign ? 0 : 1;
+ exceptions |= vdm.sign ? FPSCR_IOC : FPSCR_IXC;
+ } else {
+ exceptions |= FPSCR_IXC;
+ }
+ } else if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) {
d = 1;
- else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
- d = 0;
- exceptions |= FPSCR_IOC;
+ exceptions |= FPSCR_IXC;
+ } else if (rmode == FPSCR_ROUND_MINUSINF) {
+ exceptions |= vdm.sign ? FPSCR_IOC : FPSCR_IXC;
+ } else {
+ exceptions |= FPSCR_IXC;
}
}
}
@@ -615,7 +631,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
{
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- return vfp_double_ftoui(state, sd, unused, dm, FPSCR_ROUND_TOZERO);
+ return vfp_double_ftoui(state, sd, unused, dm, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
}
static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
@@ -626,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);
/*
@@ -639,12 +655,12 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
if (tm & VFP_NAN) {
d = 0;
exceptions |= FPSCR_IOC;
- } else if (vdm.exponent >= 1023 + 32) {
+ } else if (vdm.exponent >= 1023 + 31) {
d = 0x7fffffff;
if (vdm.sign)
d = ~d;
exceptions |= FPSCR_IOC;
- } else if (vdm.exponent >= 1023 - 1) {
+ } else if (vdm.exponent >= 1023) {
int shift = 1023 + 63 - vdm.exponent; /* 58 */
u64 rem, incr = 0;
@@ -675,10 +691,17 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
d = 0;
if (vdm.exponent | vdm.significand) {
exceptions |= FPSCR_IXC;
- if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ if (vdm.exponent >= 1022) {
+ d = vdm.sign ? 0xffffffff : 1;
+ } else {
+ d = 0;
+ }
+ } else if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) {
d = 1;
- else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign)
- d = -1;
+ } else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
+ d = 0xffffffff;
+ }
}
}
@@ -692,7 +715,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
- return vfp_double_ftosi(state, dd, unused, dm, FPSCR_ROUND_TOZERO);
+ return vfp_double_ftosi(state, dd, unused, dm, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
}
static struct op fops_ext[] = {
@@ -892,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);
@@ -915,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;
}
/*
@@ -964,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;
}
/*
@@ -985,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;
}
/*
@@ -1008,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;
}
/*
@@ -1030,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);
@@ -1046,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;
}
/*
@@ -1061,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;
@@ -1131,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:
@@ -1149,7 +1180,7 @@ zero:
goto pack;
divzero:
- exceptions = FPSCR_DZC;
+ exceptions |= FPSCR_DZC;
infinity:
vdd.exponent = 2047;
vdd.significand = 0;
@@ -1157,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 e47ad2760..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;
@@ -592,7 +600,11 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
* 2^0 <= m < 2^32-2^8
*/
d = (vsm.significand << 1) >> shift;
- rem = vsm.significand << (33 - shift);
+ if (shift > 0) {
+ rem = (vsm.significand << 1) << (32 - shift);
+ } else {
+ rem = 0;
+ }
if (rmode == FPSCR_ROUND_NEAREST) {
incr = 0x80000000;
@@ -619,12 +631,20 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
} else {
d = 0;
if (vsm.exponent | vsm.significand) {
- exceptions |= FPSCR_IXC;
- if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ if (vsm.exponent >= 126) {
+ d = vsm.sign ? 0 : 1;
+ exceptions |= vsm.sign ? FPSCR_IOC : FPSCR_IXC;
+ } else {
+ exceptions |= FPSCR_IXC;
+ }
+ } else if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) {
d = 1;
- else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
- d = 0;
- exceptions |= FPSCR_IOC;
+ exceptions |= FPSCR_IXC;
+ } else if (rmode == FPSCR_ROUND_MINUSINF) {
+ exceptions |= vsm.sign ? FPSCR_IOC : FPSCR_IXC;
+ } else {
+ exceptions |= FPSCR_IXC;
}
}
}
@@ -638,7 +658,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
static u32 vfp_single_ftouiz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
{
- return vfp_single_ftoui(state, sd, unused, m, FPSCR_ROUND_TOZERO);
+ return vfp_single_ftoui(state, sd, unused, m, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
}
static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
@@ -648,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);
/*
@@ -661,7 +681,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
if (tm & VFP_NAN) {
d = 0;
exceptions |= FPSCR_IOC;
- } else if (vsm.exponent >= 127 + 32) {
+ } else if (vsm.exponent >= 127 + 31) {
/*
* m >= 2^31-2^7: invalid
*/
@@ -675,7 +695,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
/* 2^0 <= m <= 2^31-2^7 */
d = (vsm.significand << 1) >> shift;
- rem = vsm.significand << (33 - shift);
+ rem = (vsm.significand << 1) << (32 - shift);
if (rmode == FPSCR_ROUND_NEAREST) {
incr = 0x80000000;
@@ -701,10 +721,14 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
d = 0;
if (vsm.exponent | vsm.significand) {
exceptions |= FPSCR_IXC;
- if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ if (vsm.exponent >= 126)
+ d = vsm.sign ? 0xffffffff : 1;
+ } else if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) {
d = 1;
- else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign)
- d = -1;
+ } else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
+ d = 0xffffffff;
+ }
}
}
@@ -717,7 +741,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
static u32 vfp_single_ftosiz(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr)
{
- return vfp_single_ftosi(state, sd, unused, m, FPSCR_ROUND_TOZERO);
+ return vfp_single_ftosi(state, sd, unused, m, (fpscr & ~FPSCR_RMODE_MASK) | FPSCR_ROUND_TOZERO);
}
static struct op fops_ext[] = {
@@ -774,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 {
/*
@@ -921,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);
@@ -950,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;
}
/*
@@ -962,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;
}
/*
@@ -1000,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;
}
/*
@@ -1023,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;
}
/*
@@ -1047,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);
@@ -1055,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;
}
/*
@@ -1095,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;
@@ -1162,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:
@@ -1180,7 +1213,7 @@ zero:
goto pack;
divzero:
- exceptions = FPSCR_DZC;
+ exceptions |= FPSCR_DZC;
infinity:
vsd.exponent = 255;
vsd.significand = 0;
@@ -1188,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/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp
index 97adf0e12..cc0aa7022 100644
--- a/src/core/file_sys/archive_backend.cpp
+++ b/src/core/file_sys/archive_backend.cpp
@@ -19,22 +19,22 @@ Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) {
switch (type) {
case Binary:
{
- u8* data = Memory::GetPointer(pointer);
- binary = std::vector<u8>(data, data + size);
+ binary.resize(size);
+ Memory::ReadBlock(pointer, binary.data(), binary.size());
break;
}
case Char:
{
- const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
- string = std::string(data, size - 1); // Data is always null-terminated.
+ string.resize(size - 1); // Data is always null-terminated.
+ Memory::ReadBlock(pointer, &string[0], string.size());
break;
}
case Wchar:
{
- const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer));
- u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated.
+ u16str.resize(size / 2 - 1); // Data is always null-terminated.
+ Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t));
break;
}
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index ae0c116ef..820b19e1a 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -374,7 +374,7 @@ static void SendReply(const char* reply) {
memset(command_buffer, 0, sizeof(command_buffer));
- command_length = strlen(reply);
+ command_length = static_cast<u32>(strlen(reply));
if (command_length + 4 > sizeof(command_buffer)) {
LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply");
return;
@@ -437,7 +437,7 @@ static void HandleSetThread() {
*
* @param signal Signal to be sent to client.
*/
-void SendSignal(u32 signal) {
+static void SendSignal(u32 signal) {
if (gdbserver_socket == -1) {
return;
}
@@ -515,7 +515,7 @@ static bool IsDataAvailable() {
return false;
}
- return FD_ISSET(gdbserver_socket, &fd_socket);
+ return FD_ISSET(gdbserver_socket, &fd_socket) != 0;
}
/// Send requested register to gdb client.
@@ -633,10 +633,10 @@ static void ReadMemory() {
auto start_offset = command_buffer+1;
auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
- PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+ PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
start_offset = addr_pos+1;
- u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+ u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len);
@@ -658,11 +658,11 @@ static void ReadMemory() {
static void WriteMemory() {
auto start_offset = command_buffer+1;
auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
- PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+ PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
start_offset = addr_pos+1;
auto len_pos = std::find(start_offset, command_buffer+command_length, ':');
- u32 len = HexToInt(start_offset, len_pos - start_offset);
+ u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset));
u8* dst = Memory::GetPointer(addr);
if (!dst) {
@@ -713,7 +713,7 @@ static void Continue() {
* @param addr Address of breakpoint.
* @param len Length of breakpoint.
*/
-bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
+static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
Breakpoint breakpoint;
@@ -752,10 +752,10 @@ static void AddBreakpoint() {
auto start_offset = command_buffer+3;
auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
- PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+ PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
start_offset = addr_pos+1;
- u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+ u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints
@@ -800,10 +800,10 @@ static void RemoveBreakpoint() {
auto start_offset = command_buffer+3;
auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
- PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+ PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
start_offset = addr_pos+1;
- u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+ u32 len = HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints
@@ -907,7 +907,7 @@ void ToggleServer(bool status) {
}
}
-void Init(u16 port) {
+static void Init(u16 port) {
if (!g_server_enabled) {
// Set the halt loop to false in case the user enabled the gdbstub mid-execution.
// This way the CPU can still execute normally.
diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h
index af442f81d..754c6f7db 100644
--- a/src/core/hle/applets/applet.h
+++ b/src/core/hle/applets/applet.h
@@ -65,6 +65,7 @@ protected:
virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0;
Service::APT::AppletId id; ///< Id of this Applet
+ std::shared_ptr<std::vector<u8>> heap_memory; ///< Heap memory for this Applet
};
/// Returns whether a library applet is currently running
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 5191c821d..77f01d208 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -21,13 +21,6 @@
namespace HLE {
namespace Applets {
-MiiSelector::MiiSelector(Service::APT::AppletId id) : Applet(id), started(false) {
- // Create the SharedMemory that will hold the framebuffer data
- // TODO(Subv): What size should we use here?
- using Kernel::MemoryPermission;
- framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "MiiSelector Memory");
-}
-
ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
@@ -36,11 +29,25 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
return ResultCode(-1);
}
+ // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
+ // Create the SharedMemory that will hold the framebuffer data
+ Service::APT::CaptureBufferInfo capture_info;
+ ASSERT(sizeof(capture_info) == parameter.buffer.size());
+
+ memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
+
+ using Kernel::MemoryPermission;
+ // Allocate a heap block of the required size for this applet.
+ heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
+ // Create a SharedMemory that directly points to this heap block.
+ framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(),
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "MiiSelector Memory");
+
+ // Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
- // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
- result.data = nullptr;
- result.buffer_size = 0;
+ result.buffer.clear();
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id);
result.object = framebuffer_memory;
@@ -55,15 +62,17 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
// TODO(Subv): Set the expected fields in the response buffer before resending it to the application.
// TODO(Subv): Reverse the parameter format for the Mii Selector
- if(parameter.buffer_size >= sizeof(u32)) {
- // TODO: defaults return no error, but garbage in other unknown fields
- memset(parameter.data, 0, sizeof(u32));
- }
+ memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
+
+ // TODO(Subv): Find more about this structure, result code 0 is enough to let most games continue.
+ MiiResult result;
+ memset(&result, 0, sizeof(result));
+ result.result_code = 0;
// Let the application know that we're closing
Service::APT::MessageParameter message;
- message.buffer_size = parameter.buffer_size;
- message.data = parameter.data;
+ message.buffer.resize(sizeof(MiiResult));
+ std::memcpy(message.buffer.data(), &result, message.buffer.size());
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id);
diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h
index c02dded4a..24e8e721d 100644
--- a/src/core/hle/applets/mii_selector.h
+++ b/src/core/hle/applets/mii_selector.h
@@ -24,7 +24,7 @@ struct MiiConfig {
u8 unk_004;
INSERT_PADDING_BYTES(3);
u16 unk_008;
- INSERT_PADDING_BYTES(0x8C - 0xA);
+ INSERT_PADDING_BYTES(0x82);
u8 unk_08C;
INSERT_PADDING_BYTES(3);
u16 unk_090;
@@ -62,19 +62,21 @@ ASSERT_REG_POSITION(unk_6C, 0x6C);
class MiiSelector final : public Applet {
public:
- MiiSelector(Service::APT::AppletId id);
+ MiiSelector(Service::APT::AppletId id) : Applet(id), started(false) { }
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
void Update() override;
bool IsRunning() const override { return started; }
- /// TODO(Subv): Find out what this is actually used for.
- /// It is believed that the application stores the current screen image here.
+ /// This SharedMemory will be created when we receive the LibAppJustStarted message.
+ /// It holds the framebuffer info retrieved by the application with GSPGPU::ImportDisplayCaptureInfo
Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
/// Whether this applet is currently running instead of the host application or not.
bool started;
+
+ MiiConfig config;
};
}
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index 1db6b5a17..d87bf3d57 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -24,13 +24,6 @@
namespace HLE {
namespace Applets {
-SoftwareKeyboard::SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) {
- // Create the SharedMemory that will hold the framebuffer data
- // TODO(Subv): What size should we use here?
- using Kernel::MemoryPermission;
- framebuffer_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, "SoftwareKeyboard Memory");
-}
-
ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) {
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
@@ -39,11 +32,25 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
return ResultCode(-1);
}
+ // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
+ // Create the SharedMemory that will hold the framebuffer data
+ Service::APT::CaptureBufferInfo capture_info;
+ ASSERT(sizeof(capture_info) == parameter.buffer.size());
+
+ memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
+
+ using Kernel::MemoryPermission;
+ // Allocate a heap block of the required size for this applet.
+ heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
+ // Create a SharedMemory that directly points to this heap block.
+ framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(),
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "SoftwareKeyboard Memory");
+
+ // Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
- // The buffer passed in parameter contains the data returned by GSPGPU::ImportDisplayCaptureInfo
result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
- result.data = nullptr;
- result.buffer_size = 0;
+ result.buffer.clear();
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id);
result.object = framebuffer_memory;
@@ -53,9 +60,9 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
}
ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) {
- ASSERT_MSG(parameter.buffer_size == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong");
+ ASSERT_MSG(parameter.buffer.size() == sizeof(config), "The size of the parameter (SoftwareKeyboardConfig) is wrong");
- memcpy(&config, parameter.data, parameter.buffer_size);
+ memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
text_memory = boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object);
// TODO(Subv): Verify if this is the correct behavior
@@ -91,7 +98,7 @@ void SoftwareKeyboard::DrawScreenKeyboard() {
auto info = bottom_screen->framebuffer_info[bottom_screen->index];
// TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer
- memset(Memory::GetPointer(info.address_left), 0, info.stride * 320);
+ Memory::ZeroBlock(info.address_left, info.stride * 320);
GSP_GPU::SetBufferSwap(1, info);
}
@@ -99,8 +106,8 @@ void SoftwareKeyboard::DrawScreenKeyboard() {
void SoftwareKeyboard::Finalize() {
// Let the application know that we're closing
Service::APT::MessageParameter message;
- message.buffer_size = sizeof(SoftwareKeyboardConfig);
- message.data = reinterpret_cast<u8*>(&config);
+ message.buffer.resize(sizeof(SoftwareKeyboardConfig));
+ std::memcpy(message.buffer.data(), &config, message.buffer.size());
message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id);
diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h
index cb95b8d90..cf26a8fb7 100644
--- a/src/core/hle/applets/swkbd.h
+++ b/src/core/hle/applets/swkbd.h
@@ -53,8 +53,7 @@ static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config
class SoftwareKeyboard final : public Applet {
public:
- SoftwareKeyboard(Service::APT::AppletId id);
- ~SoftwareKeyboard() {}
+ SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) { }
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
@@ -72,8 +71,8 @@ public:
*/
void Finalize();
- /// TODO(Subv): Find out what this is actually used for.
- /// It is believed that the application stores the current screen image here.
+ /// This SharedMemory will be created when we receive the LibAppJustStarted message.
+ /// It holds the framebuffer info retrieved by the application with GSPGPU::ImportDisplayCaptureInfo
Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
/// SharedMemory where the output text will be stored
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 4d718b681..bf7f875b6 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -170,7 +170,8 @@ template<ResultCode func(s64*, u32, s32)> void Wrap() {
template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() {
u32 param_1 = 0;
- u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw;
+ // The last parameter is passed in R0 instead of R4
+ u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(0)).raw;
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 862643448..17ae87aef 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -55,6 +55,9 @@ void MemoryInit(u32 mem_type) {
memory_regions[i].size = memory_region_sizes[mem_type][i];
memory_regions[i].used = 0;
memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
+ // Reserve enough space for this region of FCRAM.
+ // We do not want this block of memory to be relocated when allocating from it.
+ memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size);
base += memory_regions[i].size;
}
@@ -107,9 +110,7 @@ struct MemoryArea {
// We don't declare the IO regions in here since its handled by other means.
static MemoryArea memory_areas[] = {
- {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory
{VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
- {TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory
};
}
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 0546f6e16..69302cc82 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -209,7 +209,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
return ERR_INVALID_ADDRESS;
}
- // Expansion of the linear heap is only allowed if you do an allocation immediatelly at its
+ // Expansion of the linear heap is only allowed if you do an allocation immediately at its
// end. It's possible to free gaps in the middle of the heap and then reallocate them later,
// but expansions are only allowed at the end.
if (target == heap_end) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 6d2ca96a2..d781ef32c 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -107,6 +107,8 @@ public:
ProcessFlags flags;
/// Kernel compatibility version for this process
u16 kernel_version = 0;
+ /// The default CPU for this process, threads are scheduled on this cpu by default.
+ u8 ideal_processor = 0;
/// The id of this process
u32 process_id = next_process_id++;
@@ -140,8 +142,11 @@ public:
MemoryRegionInfo* memory_region = nullptr;
- /// Bitmask of the used TLS slots
- std::bitset<300> used_tls_slots;
+ /// The Thread Local Storage area is allocated as processes create threads,
+ /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
+ /// holds the TLS for a specific thread. This vector contains which parts are in use for each page as a bitmask.
+ /// This vector will grow as more pages are allocated for new threads.
+ std::vector<std::bitset<8>> tls_slots;
VAddr GetLinearHeapAreaAddress() const;
VAddr GetLinearHeapBase() const;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index d90f0f00f..6a22c8986 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "core/memory.h"
+#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
namespace Kernel {
@@ -14,93 +15,157 @@ namespace Kernel {
SharedMemory::SharedMemory() {}
SharedMemory::~SharedMemory() {}
-SharedPtr<SharedMemory> SharedMemory::Create(u32 size, MemoryPermission permissions,
- MemoryPermission other_permissions, std::string name) {
+SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
+ MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
+ shared_memory->owner_process = owner_process;
shared_memory->name = std::move(name);
- shared_memory->base_address = 0x0;
- shared_memory->fixed_address = 0x0;
shared_memory->size = size;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
+ if (address == 0) {
+ // We need to allocate a block from the Linear Heap ourselves.
+ // We'll manually allocate some memory from the linear heap in the specified region.
+ MemoryRegionInfo* memory_region = GetMemoryRegion(region);
+ auto& linheap_memory = memory_region->linear_heap_memory;
+
+ ASSERT_MSG(linheap_memory->size() + size <= memory_region->size, "Not enough space in region to allocate shared memory!");
+
+ shared_memory->backing_block = linheap_memory;
+ shared_memory->backing_block_offset = linheap_memory->size();
+ // Allocate some memory from the end of the linear heap for this region.
+ linheap_memory->insert(linheap_memory->end(), size, 0);
+ memory_region->used += size;
+
+ shared_memory->linear_heap_phys_address = Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset;
+
+ // Increase the amount of used linear heap memory for the owner process.
+ if (shared_memory->owner_process != nullptr) {
+ shared_memory->owner_process->linear_heap_used += size;
+ }
+
+ // Refresh the address mappings for the current process.
+ if (Kernel::g_current_process != nullptr) {
+ Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
+ }
+ } else {
+ // TODO(Subv): What happens if an application tries to create multiple memory blocks pointing to the same address?
+ auto& vm_manager = shared_memory->owner_process->vm_manager;
+ // The memory is already available and mapped in the owner process.
+ auto vma = vm_manager.FindVMA(address)->second;
+ // Copy it over to our own storage
+ shared_memory->backing_block = std::make_shared<std::vector<u8>>(vma.backing_block->data() + vma.offset,
+ vma.backing_block->data() + vma.offset + size);
+ shared_memory->backing_block_offset = 0;
+ // Unmap the existing pages
+ vm_manager.UnmapRange(address, size);
+ // Map our own block into the address space
+ vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, MemoryState::Shared);
+ // Reprotect the block with the new permissions
+ vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions));
+ }
+
+ shared_memory->base_address = address;
return shared_memory;
}
-ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions,
- MemoryPermission other_permissions) {
+SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
+ MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
+ SharedPtr<SharedMemory> shared_memory(new SharedMemory);
- if (base_address != 0) {
- LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: already mapped at 0x%08X!",
- GetObjectId(), address, name.c_str(), base_address);
- // TODO: Verify error code with hardware
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
- }
+ shared_memory->owner_process = nullptr;
+ shared_memory->name = std::move(name);
+ shared_memory->size = size;
+ shared_memory->permissions = permissions;
+ shared_memory->other_permissions = other_permissions;
+ shared_memory->backing_block = heap_block;
+ shared_memory->backing_block_offset = offset;
+ shared_memory->base_address = Memory::HEAP_VADDR + offset;
- // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't
- // match what was specified when the memory block was created.
+ return shared_memory;
+}
- // TODO(Subv): Return E0E01BEE when address should be 0.
- // Note: Find out when that's the case.
+ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
+ MemoryPermission other_permissions) {
- if (fixed_address != 0) {
- if (address != 0 && address != fixed_address) {
- LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: fixed_addres is 0x%08X!",
- GetObjectId(), address, name.c_str(), fixed_address);
- // TODO: Verify error code with hardware
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
- }
+ MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions;
- // HACK(yuriks): This is only here to support the APT shared font mapping right now.
- // Later, this should actually map the memory block onto the address space.
- return RESULT_SUCCESS;
+ // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
+ if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
- if (address < Memory::SHARED_MEMORY_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
- LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s outside of shared mem bounds!",
- GetObjectId(), address, name.c_str());
- // TODO: Verify error code with hardware
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ // Error out if the requested permissions don't match what the creator process allows.
+ if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
+ GetObjectId(), address, name.c_str());
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
- // TODO: Test permissions
+ // Heap-backed memory blocks can not be mapped with other_permissions = DontCare
+ if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
+ GetObjectId(), address, name.c_str());
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }
- // HACK: Since there's no way to write to the memory block without mapping it onto the game
- // process yet, at least initialize memory the first time it's mapped.
- if (address != this->base_address) {
- std::memset(Memory::GetPointer(address), 0, size);
+ // Error out if the provided permissions are not compatible with what the creator process needs.
+ if (other_permissions != MemoryPermission::DontCare &&
+ static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
+ GetObjectId(), address, name.c_str());
+ return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
}
- this->base_address = address;
+ // TODO(Subv): Check for the Shared Device Mem flag in the creator process.
+ /*if (was_created_with_shared_device_mem && address != 0) {
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }*/
- return RESULT_SUCCESS;
-}
+ // TODO(Subv): The same process that created a SharedMemory object
+ // can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
-ResultCode SharedMemory::Unmap(VAddr address) {
- if (base_address == 0) {
- // TODO(Subv): Verify what actually happens when you want to unmap a memory block that
- // was originally mapped with address = 0
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ if (address != 0) {
+ if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
+ LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address",
+ GetObjectId(), address, name.c_str());
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }
}
- if (base_address != address)
- return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage);
+ VAddr target_address = address;
- base_address = 0;
+ if (base_address == 0 && target_address == 0) {
+ // Calculate the address at which to map the memory block.
+ target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address);
+ }
+
+ // Map the memory block into the target process
+ auto result = target_process->vm_manager.MapMemoryBlock(target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
+ if (result.Failed()) {
+ LOG_ERROR(Kernel, "cannot map id=%u, target_address=0x%08X name=%s, error mapping to virtual memory",
+ GetObjectId(), target_address, name.c_str());
+ return result.Code();
+ }
- return RESULT_SUCCESS;
+ return target_process->vm_manager.ReprotectRange(target_address, size, ConvertPermissions(permissions));
}
-u8* SharedMemory::GetPointer(u32 offset) {
- if (base_address != 0)
- return Memory::GetPointer(base_address + offset);
+ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
+ // TODO(Subv): Verify what happens if the application tries to unmap an address that is not mapped to a SharedMemory.
+ return target_process->vm_manager.UnmapRange(address, size);
+}
+
+VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
+ u32 masked_permissions = static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute);
+ return static_cast<VMAPermission>(masked_permissions);
+};
- LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId());
- return nullptr;
+u8* SharedMemory::GetPointer(u32 offset) {
+ return backing_block->data() + backing_block_offset + offset;
}
} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index b51049ad0..0c404a9f8 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -9,6 +9,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -29,14 +30,29 @@ enum class MemoryPermission : u32 {
class SharedMemory final : public Object {
public:
/**
- * Creates a shared memory object
+ * Creates a shared memory object.
+ * @param owner_process Process that created this shared memory object.
* @param size Size of the memory block. Must be page-aligned.
* @param permissions Permission restrictions applied to the process which created the block.
* @param other_permissions Permission restrictions applied to other processes mapping the block.
+ * @param address The address from which to map the Shared Memory.
+ * @param region If the address is 0, the shared memory will be allocated in this region of the linear heap.
* @param name Optional object name, used for debugging purposes.
*/
- static SharedPtr<SharedMemory> Create(u32 size, MemoryPermission permissions,
- MemoryPermission other_permissions, std::string name = "Unknown");
+ static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u32 size, MemoryPermission permissions,
+ MemoryPermission other_permissions, VAddr address = 0, MemoryRegion region = MemoryRegion::BASE, std::string name = "Unknown");
+
+ /**
+ * Creates a shared memory object from a block of memory managed by an HLE applet.
+ * @param heap_block Heap block of the HLE applet.
+ * @param offset The offset into the heap block that the SharedMemory will map.
+ * @param size Size of the memory block. Must be page-aligned.
+ * @param permissions Permission restrictions applied to the process which created the block.
+ * @param other_permissions Permission restrictions applied to other processes mapping the block.
+ * @param name Optional object name, used for debugging purposes.
+ */
+ static SharedPtr<SharedMemory> CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
+ MemoryPermission permissions, MemoryPermission other_permissions, std::string name = "Unknown Applet");
std::string GetTypeName() const override { return "SharedMemory"; }
std::string GetName() const override { return name; }
@@ -45,19 +61,27 @@ public:
HandleType GetHandleType() const override { return HANDLE_TYPE; }
/**
- * Maps a shared memory block to an address in system memory
+ * Converts the specified MemoryPermission into the equivalent VMAPermission.
+ * @param permission The MemoryPermission to convert.
+ */
+ static VMAPermission ConvertPermissions(MemoryPermission permission);
+
+ /**
+ * Maps a shared memory block to an address in the target process' address space
+ * @param target_process Process on which to map the memory block.
* @param address Address in system memory to map shared memory block to
* @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field)
*/
- ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
+ ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
/**
* Unmaps a shared memory block from the specified address in system memory
+ * @param target_process Process from which to umap the memory block.
* @param address Address in system memory where the shared memory block is mapped
* @return Result code of the unmap operation
*/
- ResultCode Unmap(VAddr address);
+ ResultCode Unmap(Process* target_process, VAddr address);
/**
* Gets a pointer to the shared memory block
@@ -66,10 +90,16 @@ public:
*/
u8* GetPointer(u32 offset = 0);
- /// Address of shared memory block in the process.
+ /// Process that created this shared memory block.
+ SharedPtr<Process> owner_process;
+ /// Address of shared memory block in the owner process if specified.
VAddr base_address;
- /// Fixed address to allow mapping to. Used for blocks created from the linear heap.
- VAddr fixed_address;
+ /// Physical address of the shared memory block in the linear heap if no address was specified during creation.
+ PAddr linear_heap_phys_address;
+ /// Backing memory for this shared memory block.
+ std::shared_ptr<std::vector<u8>> backing_block;
+ /// Offset into the backing block for this shared memory.
+ u32 backing_block_offset;
/// Size of the memory block. Page-aligned.
u32 size;
/// Permission restrictions applied to the process which created the block.
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 6dc95d0f1..3f6bec5fa 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -117,9 +117,10 @@ void Thread::Stop() {
}
wait_objects.clear();
- Kernel::g_current_process->used_tls_slots[tls_index] = false;
- g_current_process->misc_memory_used -= Memory::TLS_ENTRY_SIZE;
- g_current_process->memory_region->used -= Memory::TLS_ENTRY_SIZE;
+ // Mark the TLS slot in the thread's page as free.
+ u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
+ u32 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
+ Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
HLE::Reschedule(__func__);
}
@@ -366,6 +367,31 @@ static void DebugThreadQueue() {
}
}
+/**
+ * Finds a free location for the TLS section of a thread.
+ * @param tls_slots The TLS page array of the thread's owner process.
+ * Returns a tuple of (page, slot, alloc_needed) where:
+ * page: The index of the first allocated TLS page that has free slots.
+ * slot: The index of the first free slot in the indicated page.
+ * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
+ */
+std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& tls_slots) {
+ // Iterate over all the allocated pages, and try to find one where not all slots are used.
+ for (unsigned page = 0; page < tls_slots.size(); ++page) {
+ const auto& page_tls_slots = tls_slots[page];
+ if (!page_tls_slots.all()) {
+ // We found a page with at least one free slot, find which slot it is
+ for (unsigned slot = 0; slot < page_tls_slots.size(); ++slot) {
+ if (!page_tls_slots.test(slot)) {
+ return std::make_tuple(page, slot, false);
+ }
+ }
+ }
+ }
+
+ return std::make_tuple(0, 0, true);
+}
+
ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
u32 arg, s32 processor_id, VAddr stack_top) {
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
@@ -377,7 +403,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
priority = new_priority;
}
- if (!Memory::GetPointer(entry_point)) {
+ if (!Memory::IsValidVirtualAddress(entry_point)) {
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
// TODO: Verify error
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
@@ -403,22 +429,50 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->name = std::move(name);
thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom();
thread->owner_process = g_current_process;
- thread->tls_index = -1;
thread->waitsynch_waited = false;
// Find the next available TLS index, and mark it as used
- auto& used_tls_slots = Kernel::g_current_process->used_tls_slots;
- for (unsigned int i = 0; i < used_tls_slots.size(); ++i) {
- if (used_tls_slots[i] == false) {
- thread->tls_index = i;
- used_tls_slots[i] = true;
- break;
+ auto& tls_slots = Kernel::g_current_process->tls_slots;
+ bool needs_allocation = true;
+ u32 available_page; // Which allocated page has free space
+ u32 available_slot; // Which slot within the page is free
+
+ std::tie(available_page, available_slot, needs_allocation) = GetFreeThreadLocalSlot(tls_slots);
+
+ if (needs_allocation) {
+ // There are no already-allocated pages with free slots, lets allocate a new one.
+ // TLS pages are allocated from the BASE region in the linear heap.
+ MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE);
+ auto& linheap_memory = memory_region->linear_heap_memory;
+
+ if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
+ LOG_ERROR(Kernel_SVC, "Not enough space in region to allocate a new TLS page for thread");
+ return ResultCode(ErrorDescription::OutOfMemory, ErrorModule::Kernel, ErrorSummary::OutOfResource, ErrorLevel::Permanent);
}
+
+ u32 offset = linheap_memory->size();
+
+ // Allocate some memory from the end of the linear heap for this region.
+ linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
+ memory_region->used += Memory::PAGE_SIZE;
+ Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE;
+
+ tls_slots.emplace_back(0); // The page is completely available at the start
+ available_page = tls_slots.size() - 1;
+ available_slot = 0; // Use the first slot in the new page
+
+ auto& vm_manager = Kernel::g_current_process->vm_manager;
+ vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
+
+ // Map the page to the current process' address space.
+ // TODO(Subv): Find the correct MemoryState for this region.
+ vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
+ linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::Private);
}
- ASSERT_MSG(thread->tls_index != -1, "Out of TLS space");
- g_current_process->misc_memory_used += Memory::TLS_ENTRY_SIZE;
- g_current_process->memory_region->used += Memory::TLS_ENTRY_SIZE;
+ // Mark the slot as used
+ tls_slots[available_page].set(available_slot);
+ thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE;
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
@@ -472,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());
@@ -509,10 +565,6 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
context.cpu_registers[1] = output;
}
-VAddr Thread::GetTLSAddress() const {
- return Memory::TLS_AREA_VADDR + tls_index * Memory::TLS_ENTRY_SIZE;
-}
-
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 97ba57fc5..deab5d5a6 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -127,7 +127,7 @@ public:
* Returns the Thread Local Storage address of the current thread
* @returns VAddr of the thread's TLS
*/
- VAddr GetTLSAddress() const;
+ VAddr GetTLSAddress() const { return tls_address; }
Core::ThreadContext context;
@@ -144,7 +144,7 @@ public:
s32 processor_id;
- s32 tls_index; ///< Index of the Thread Local Storage of the thread
+ VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
bool waitsynch_waited; ///< Set to true if the last svcWaitSynch call caused the thread to wait
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/result.h b/src/core/hle/result.h
index 3fc1ab4ee..57dedcb22 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -17,6 +17,7 @@
/// Detailed description of the error. This listing is likely incomplete.
enum class ErrorDescription : u32 {
Success = 0,
+ WrongPermission = 46,
OS_InvalidBufferDescriptor = 48,
WrongAddress = 53,
FS_NotFound = 120,
@@ -25,6 +26,7 @@ enum class ErrorDescription : u32 {
FS_NotAFile = 250,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage
+ GPU_FirstInitialization = 519,
FS_InvalidPath = 702,
InvalidSection = 1000,
TooLarge = 1001,
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/dlp_srvr.h b/src/core/hle/service/act_a.h
index d65d00814..765cae644 100644
--- a/src/core/hle/service/dlp_srvr.h
+++ b/src/core/hle/service/act_a.h
@@ -7,16 +7,16 @@
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace DLP_SRVR
+// Namespace ACT_A
-namespace DLP_SRVR {
+namespace ACT_A {
class Interface : public Service::Interface {
public:
Interface();
std::string GetPortName() const override {
- return "dlp:SRVR";
+ return "act:a";
}
};
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 6d72e8188..1e54a53dd 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -12,7 +12,9 @@
#include "core/hle/service/apt/apt_a.h"
#include "core/hle/service/apt/apt_s.h"
#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"
@@ -22,25 +24,19 @@
namespace Service {
namespace APT {
-// Address used for shared font (as observed on HW)
-// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
-// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
-// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
-// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
-// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
-static const VAddr SHARED_FONT_VADDR = 0x18000000;
-
/// Handle to shared memory region designated to for shared system font
static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
+static bool shared_font_relocated = false;
static Kernel::SharedPtr<Kernel::Mutex> lock;
static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
-static std::shared_ptr<std::vector<u8>> shared_font;
-
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;
@@ -74,23 +70,25 @@ void Initialize(Service::Interface* self) {
void GetSharedFont(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- if (shared_font != nullptr) {
- // TODO(yuriks): This is a hack to keep this working right now even with our completely
- // broken shared memory system.
- shared_font_mem->fixed_address = SHARED_FONT_VADDR;
- Kernel::g_current_process->vm_manager.MapMemoryBlock(shared_font_mem->fixed_address,
- shared_font, 0, shared_font_mem->size, Kernel::MemoryState::Shared);
-
- cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = SHARED_FONT_VADDR;
- cmd_buff[3] = IPC::MoveHandleDesc();
- cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
- } else {
- cmd_buff[0] = IPC::MakeHeader(0x44, 1, 0);
- cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
- LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
+ // The shared font has to be relocated to the new address before being passed to the application.
+ VAddr target_address = Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address);
+ // The shared font dumped by 3dsutils (https://github.com/citra-emu/3dsutils) uses this address as base,
+ // so we relocate it from there to our real address.
+ // TODO(Subv): This address is wrong if the shared font is dumped from a n3DS,
+ // we need a way to automatically calculate the original address of the font from the file.
+ static const VAddr SHARED_FONT_VADDR = 0x18000000;
+ if (!shared_font_relocated) {
+ BCFNT::RelocateSharedFont(shared_font_mem, SHARED_FONT_VADDR, target_address);
+ shared_font_relocated = true;
}
+ cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ // Since the SharedMemory interface doesn't provide the address at which the memory was allocated,
+ // the real APT service calculates this address by scanning the entire address space (using svcQueryMemory)
+ // and searches for an allocation of the same size as the Shared Font.
+ cmd_buff[2] = target_address;
+ cmd_buff[3] = IPC::MoveHandleDesc();
+ cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
}
void NotifyToWait(Service::Interface* self) {
@@ -182,12 +180,12 @@ void SendParameter(Service::Interface* self) {
}
MessageParameter param;
- param.buffer_size = buffer_size;
param.destination_id = dst_app_id;
param.sender_id = src_app_id;
param.object = Kernel::g_handle_table.GetGeneric(handle);
param.signal = signal_type;
- param.data = Memory::GetPointer(buffer);
+ param.buffer.resize(buffer_size);
+ Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
cmd_buff[1] = dest_applet->ReceiveParameter(param).raw;
@@ -205,16 +203,15 @@ void ReceiveParameter(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = next_parameter.sender_id;
cmd_buff[3] = next_parameter.signal; // Signal type
- cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size
+ cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size
cmd_buff[5] = 0x10;
cmd_buff[6] = 0;
if (next_parameter.object != nullptr)
cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
- cmd_buff[7] = (next_parameter.buffer_size << 14) | 2;
+ cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
cmd_buff[8] = buffer;
- if (next_parameter.data)
- memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size));
+ Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size());
LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
@@ -228,16 +225,15 @@ void GlanceParameter(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = next_parameter.sender_id;
cmd_buff[3] = next_parameter.signal; // Signal type
- cmd_buff[4] = next_parameter.buffer_size; // Parameter buffer size
+ cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size
cmd_buff[5] = 0x10;
cmd_buff[6] = 0;
if (next_parameter.object != nullptr)
cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
- cmd_buff[7] = (next_parameter.buffer_size << 14) | 2;
+ cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
cmd_buff[8] = buffer;
- if (next_parameter.data)
- memcpy(Memory::GetPointer(buffer), next_parameter.data, std::min(buffer_size, next_parameter.buffer_size));
+ Memory::WriteBlock(buffer, next_parameter.buffer.data(), std::min(static_cast<size_t>(buffer_size), next_parameter.buffer.size()));
LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
@@ -264,6 +260,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,"
@@ -371,14 +371,36 @@ void StartLibraryApplet(Service::Interface* self) {
return;
}
+ size_t buffer_size = cmd_buff[2];
+ VAddr buffer_addr = cmd_buff[6];
+
AppletStartupParameter parameter;
- parameter.buffer_size = cmd_buff[2];
parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]);
- parameter.data = Memory::GetPointer(cmd_buff[6]);
+ parameter.buffer.resize(buffer_size);
+ Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
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]);
@@ -414,6 +436,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);
@@ -433,14 +478,12 @@ void Init() {
FileUtil::IOFile file(filepath, "rb");
if (file.IsOpen()) {
- // Read shared font data
- shared_font = std::make_shared<std::vector<u8>>((size_t)file.GetSize());
- file.ReadBytes(shared_font->data(), shared_font->size());
-
// Create shared font memory object
using Kernel::MemoryPermission;
- shared_font_mem = Kernel::SharedMemory::Create(3 * 1024 * 1024, // 3MB
- MemoryPermission::ReadWrite, MemoryPermission::Read, "APT_U:shared_font_mem");
+ shared_font_mem = Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB
+ MemoryPermission::ReadWrite, MemoryPermission::Read, 0, Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
+ // Read shared font data
+ file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
} else {
LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
shared_font_mem = nullptr;
@@ -449,6 +492,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");
@@ -459,8 +503,8 @@ void Init() {
}
void Shutdown() {
- shared_font = nullptr;
shared_font_mem = nullptr;
+ shared_font_relocated = false;
lock = nullptr;
notification_event = nullptr;
parameter_event = nullptr;
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 668b4a66f..76b3a3807 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -5,6 +5,7 @@
#pragma once
#include "common/common_types.h"
+#include "common/swap.h"
#include "core/hle/kernel/kernel.h"
@@ -19,18 +20,30 @@ struct MessageParameter {
u32 sender_id = 0;
u32 destination_id = 0;
u32 signal = 0;
- u32 buffer_size = 0;
Kernel::SharedPtr<Kernel::Object> object = nullptr;
- u8* data = nullptr;
+ std::vector<u8> buffer;
};
/// Holds information about the parameters used in StartLibraryApplet
struct AppletStartupParameter {
- u32 buffer_size = 0;
Kernel::SharedPtr<Kernel::Object> object = nullptr;
- u8* data = nullptr;
+ std::vector<u8> buffer;
};
+/// Used by the application to pass information about the current framebuffer to applets.
+struct CaptureBufferInfo {
+ u32_le size;
+ u8 is_3d;
+ INSERT_PADDING_BYTES(0x3); // Padding for alignment
+ u32_le top_screen_left_offset;
+ u32_le top_screen_right_offset;
+ u32_le top_screen_format;
+ u32_le bottom_screen_left_offset;
+ u32_le bottom_screen_right_offset;
+ u32_le bottom_screen_format;
+};
+static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
+
/// Signals used by APT functions
enum class SignalType : u32 {
None = 0x0,
@@ -361,6 +374,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/apt/bcfnt/bcfnt.cpp b/src/core/hle/service/apt/bcfnt/bcfnt.cpp
new file mode 100644
index 000000000..b0d39d4a5
--- /dev/null
+++ b/src/core/hle/service/apt/bcfnt/bcfnt.cpp
@@ -0,0 +1,71 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/apt/bcfnt/bcfnt.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace APT {
+namespace BCFNT {
+
+void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address) {
+ static const u32 SharedFontStartOffset = 0x80;
+ u8* data = shared_font->GetPointer(SharedFontStartOffset);
+
+ CFNT cfnt;
+ memcpy(&cfnt, data, sizeof(cfnt));
+
+ // Advance past the header
+ data = shared_font->GetPointer(SharedFontStartOffset + cfnt.header_size);
+
+ for (unsigned block = 0; block < cfnt.num_blocks; ++block) {
+
+ u32 section_size = 0;
+ if (memcmp(data, "FINF", 4) == 0) {
+ BCFNT::FINF finf;
+ memcpy(&finf, data, sizeof(finf));
+ section_size = finf.section_size;
+
+ // Relocate the offsets in the FINF section
+ finf.cmap_offset += new_address - previous_address;
+ finf.cwdh_offset += new_address - previous_address;
+ finf.tglp_offset += new_address - previous_address;
+
+ memcpy(data, &finf, sizeof(finf));
+ } else if (memcmp(data, "CMAP", 4) == 0) {
+ BCFNT::CMAP cmap;
+ memcpy(&cmap, data, sizeof(cmap));
+ section_size = cmap.section_size;
+
+ // Relocate the offsets in the CMAP section
+ cmap.next_cmap_offset += new_address - previous_address;
+
+ memcpy(data, &cmap, sizeof(cmap));
+ } else if (memcmp(data, "CWDH", 4) == 0) {
+ BCFNT::CWDH cwdh;
+ memcpy(&cwdh, data, sizeof(cwdh));
+ section_size = cwdh.section_size;
+
+ // Relocate the offsets in the CWDH section
+ cwdh.next_cwdh_offset += new_address - previous_address;
+
+ memcpy(data, &cwdh, sizeof(cwdh));
+ } else if (memcmp(data, "TGLP", 4) == 0) {
+ BCFNT::TGLP tglp;
+ memcpy(&tglp, data, sizeof(tglp));
+ section_size = tglp.section_size;
+
+ // Relocate the offsets in the TGLP section
+ tglp.sheet_data_offset += new_address - previous_address;
+
+ memcpy(data, &tglp, sizeof(tglp));
+ }
+
+ data += section_size;
+ }
+}
+
+} // namespace BCFNT
+} // namespace APT
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.h b/src/core/hle/service/apt/bcfnt/bcfnt.h
new file mode 100644
index 000000000..388c6bea0
--- /dev/null
+++ b/src/core/hle/service/apt/bcfnt/bcfnt.h
@@ -0,0 +1,87 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/swap.h"
+
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace APT {
+namespace BCFNT { ///< BCFNT Shared Font file structures
+
+struct CFNT {
+ u8 magic[4];
+ u16_le endianness;
+ u16_le header_size;
+ u32_le version;
+ u32_le file_size;
+ u32_le num_blocks;
+};
+
+struct FINF {
+ u8 magic[4];
+ u32_le section_size;
+ u8 font_type;
+ u8 line_feed;
+ u16_le alter_char_index;
+ u8 default_width[3];
+ u8 encoding;
+ u32_le tglp_offset;
+ u32_le cwdh_offset;
+ u32_le cmap_offset;
+ u8 height;
+ u8 width;
+ u8 ascent;
+ u8 reserved;
+};
+
+struct TGLP {
+ u8 magic[4];
+ u32_le section_size;
+ u8 cell_width;
+ u8 cell_height;
+ u8 baseline_position;
+ u8 max_character_width;
+ u32_le sheet_size;
+ u16_le num_sheets;
+ u16_le sheet_image_format;
+ u16_le num_columns;
+ u16_le num_rows;
+ u16_le sheet_width;
+ u16_le sheet_height;
+ u32_le sheet_data_offset;
+};
+
+struct CMAP {
+ u8 magic[4];
+ u32_le section_size;
+ u16_le code_begin;
+ u16_le code_end;
+ u16_le mapping_method;
+ u16_le reserved;
+ u32_le next_cmap_offset;
+};
+
+struct CWDH {
+ u8 magic[4];
+ u32_le section_size;
+ u16_le start_index;
+ u16_le end_index;
+ u32_le next_cwdh_offset;
+};
+
+/**
+ * Relocates the internal addresses of the BCFNT Shared Font to the new base.
+ * @param shared_font SharedMemory object that contains the Shared Font
+ * @param previous_address Previous address at which the offsets in the structure were based.
+ * @param new_address New base for the offsets in the structure.
+ */
+void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr previous_address, VAddr new_address);
+
+} // namespace BCFNT
+} // namespace APT
+} // namespace Service
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index b9322c55d..e067db645 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -47,6 +47,12 @@ struct UsernameBlock {
};
static_assert(sizeof(UsernameBlock) == 0x1C, "UsernameBlock must be exactly 0x1C bytes");
+struct BirthdayBlock {
+ u8 month; ///< The month of the birthday
+ u8 day; ///< The day of the birthday
+};
+static_assert(sizeof(BirthdayBlock) == 2, "BirthdayBlock must be exactly 2 bytes");
+
struct ConsoleModelInfo {
u8 model; ///< The console model (3DS, 2DS, etc)
u8 unknown[3]; ///< Unknown data
@@ -65,9 +71,8 @@ static const u64 CFG_SAVE_ID = 0x00010017;
static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
-static const char CONSOLE_USERNAME[0x14] = "CITRA";
-/// This will be initialized in Init, and will be used when creating the block
-static UsernameBlock CONSOLE_USERNAME_BLOCK;
+static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
+static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
/// TODO(Subv): Find out what this actually is
static const u8 SOUND_OUTPUT_MODE = 2;
static const u8 UNITED_STATES_COUNTRY_ID = 49;
@@ -191,28 +196,32 @@ void GetConfigInfoBlk2(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 size = cmd_buff[1];
u32 block_id = cmd_buff[2];
- u8* data_pointer = Memory::GetPointer(cmd_buff[4]);
+ VAddr data_pointer = cmd_buff[4];
- if (data_pointer == nullptr) {
+ if (!Memory::IsValidVirtualAddress(data_pointer)) {
cmd_buff[1] = -1; // TODO(Subv): Find the right error code
return;
}
- cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw;
+ std::vector<u8> data(size);
+ cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data.data()).raw;
+ Memory::WriteBlock(data_pointer, data.data(), data.size());
}
void GetConfigInfoBlk8(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 size = cmd_buff[1];
u32 block_id = cmd_buff[2];
- u8* data_pointer = Memory::GetPointer(cmd_buff[4]);
+ VAddr data_pointer = cmd_buff[4];
- if (data_pointer == nullptr) {
+ if (!Memory::IsValidVirtualAddress(data_pointer)) {
cmd_buff[1] = -1; // TODO(Subv): Find the right error code
return;
}
- cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw;
+ std::vector<u8> data(size);
+ cmd_buff[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data.data()).raw;
+ Memory::WriteBlock(data_pointer, data.data(), data.size());
}
void UpdateConfigNANDSavegame(Service::Interface* self) {
@@ -329,32 +338,22 @@ ResultCode FormatConfig() {
res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data());
if (!res.IsSuccess()) return res;
+
res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
if (!res.IsSuccess()) return res;
+
res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
- if (!res.IsSuccess()) return res;
- // 0x000A0000 - Profile username
- struct {
- u16_le username[10];
- u8 unused[4];
- u32_le wordfilter_version; // Unused by Citra
- } profile_username = {};
-
- std::u16string username_string = Common::UTF8ToUTF16("Citra");
- std::copy(username_string.cbegin(), username_string.cend(), profile_username.username);
- res = CreateConfigInfoBlk(0x000A0000, sizeof(profile_username), 0xE, &profile_username);
+ res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
if (!res.IsSuccess()) return res;
- // 0x000A0001 - Profile birthday
- const u8 profile_birthday[2] = {3, 25}; // March 25th, 2014
- res = CreateConfigInfoBlk(0x000A0001, sizeof(profile_birthday), 0xE, profile_birthday);
+ res = CreateConfigInfoBlk(0x000A0001, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY);
if (!res.IsSuccess()) return res;
res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
if (!res.IsSuccess()) return res;
+
res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
if (!res.IsSuccess()) return res;
@@ -435,17 +434,6 @@ void Init() {
return;
}
- // Initialize the Username block
- // TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals
- memset(&CONSOLE_USERNAME_BLOCK, 0, sizeof(CONSOLE_USERNAME_BLOCK));
- CONSOLE_USERNAME_BLOCK.ng_word = 0;
- CONSOLE_USERNAME_BLOCK.zero = 0;
-
- // Copy string to buffer and pad with zeros at the end
- auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14);
- std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size,
- std::end(CONSOLE_USERNAME_BLOCK.username), 0);
-
FormatConfig();
}
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 6318bf2a7..d2bb8941c 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cstring>
+#include "common/alignment.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h"
@@ -41,14 +42,16 @@ static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr;
void Initialize(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- shared_memory = Kernel::SharedMemory::Create(cmd_buff[1],
- Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem");
+ u32 size = Common::AlignUp(cmd_buff[1], Memory::PAGE_SIZE);
+ using Kernel::MemoryPermission;
+ shared_memory = Kernel::SharedMemory::Create(nullptr, size,
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ 0, Kernel::MemoryRegion::BASE, "CSND:SharedMemory");
mutex = Kernel::Mutex::Create(false);
- cmd_buff[1] = 0;
- cmd_buff[2] = 0x4000000;
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = IPC::MoveHandleDesc(2);
cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom();
cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom();
}
diff --git a/src/core/hle/service/dlp/dlp.cpp b/src/core/hle/service/dlp/dlp.cpp
new file mode 100644
index 000000000..7c8db794b
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp.cpp
@@ -0,0 +1,24 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/service.h"
+#include "core/hle/service/dlp/dlp.h"
+#include "core/hle/service/dlp/dlp_clnt.h"
+#include "core/hle/service/dlp/dlp_fkcl.h"
+#include "core/hle/service/dlp/dlp_srvr.h"
+
+namespace Service {
+namespace DLP {
+
+void Init() {
+ AddService(new DLP_CLNT_Interface);
+ AddService(new DLP_FKCL_Interface);
+ AddService(new DLP_SRVR_Interface);
+}
+
+void Shutdown() {
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp.h b/src/core/hle/service/dlp/dlp.h
new file mode 100644
index 000000000..ec2fe46e8
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp.h
@@ -0,0 +1,15 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+namespace Service {
+namespace DLP {
+
+/// Initializes the DLP services.
+void Init();
+
+/// Shuts down the DLP services.
+void Shutdown();
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp
new file mode 100644
index 000000000..0b31d47df
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_clnt.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/dlp/dlp_clnt.h"
+
+namespace Service {
+namespace DLP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C3, nullptr, "Initialize"},
+ {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
+};
+
+DLP_CLNT_Interface::DLP_CLNT_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_clnt.h b/src/core/hle/service/dlp/dlp_clnt.h
new file mode 100644
index 000000000..067f11e37
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_clnt.h
@@ -0,0 +1,22 @@
+// 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 Service {
+namespace DLP {
+
+class DLP_CLNT_Interface final : public Interface {
+public:
+ DLP_CLNT_Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:CLNT";
+ }
+};
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp
new file mode 100644
index 000000000..a845260e5
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_fkcl.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/dlp/dlp_fkcl.h"
+
+namespace Service {
+namespace DLP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010083, nullptr, "Initialize"},
+ {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
+};
+
+DLP_FKCL_Interface::DLP_FKCL_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_fkcl.h b/src/core/hle/service/dlp/dlp_fkcl.h
new file mode 100644
index 000000000..e4837a167
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_fkcl.h
@@ -0,0 +1,22 @@
+// 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 Service {
+namespace DLP {
+
+class DLP_FKCL_Interface final : public Interface {
+public:
+ DLP_FKCL_Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:FKCL";
+ }
+};
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 1f30188da..da9b30f56 100644
--- a/src/core/hle/service/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -2,16 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
-#include "core/hle/service/dlp_srvr.h"
+#include "core/hle/result.h"
+#include "core/hle/service/dlp/dlp_srvr.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace DLP_SRVR
+namespace Service {
+namespace DLP {
-namespace DLP_SRVR {
-
-static void unk_0x000E0040(Service::Interface* self) {
+static void unk_0x000E0040(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
@@ -23,14 +22,13 @@ static void unk_0x000E0040(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010183, nullptr, "Initialize"},
{0x00020000, nullptr, "Finalize"},
+ {0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
{0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+DLP_SRVR_Interface::DLP_SRVR_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_srvr.h b/src/core/hle/service/dlp/dlp_srvr.h
new file mode 100644
index 000000000..19fe17840
--- /dev/null
+++ b/src/core/hle/service/dlp/dlp_srvr.h
@@ -0,0 +1,22 @@
+// 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 Service {
+namespace DLP {
+
+class DLP_SRVR_Interface final : public Interface {
+public:
+ DLP_SRVR_Interface();
+
+ std::string GetPortName() const override {
+ return "dlp:SRVR";
+ }
+};
+
+} // namespace DLP
+} // namespace Service
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 995bee3f9..c8aadd9db 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -140,12 +140,15 @@ static void LoadComponent(Service::Interface* self) {
// TODO(bunnei): Implement real DSP firmware loading
- ASSERT(Memory::GetPointer(buffer) != nullptr);
- ASSERT(size > 0x37C);
+ ASSERT(Memory::IsValidVirtualAddress(buffer));
+
+ std::vector<u8> component_data(size);
+ Memory::ReadBlock(buffer, component_data.data(), component_data.size());
- LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer), size));
+ LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(component_data.data(), component_data.size()));
// Some versions of the firmware have the location of DSP structures listed here.
- LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer) + 0x340, 60));
+ ASSERT(size > 0x37C);
+ LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, Common::ComputeHash64(component_data.data() + 0x340, 60));
LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X",
size, prog_mask, data_mask, buffer);
@@ -285,10 +288,10 @@ static void WriteProcessPipe(Service::Interface* self) {
return;
}
- ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer);
+ ASSERT_MSG(Memory::IsValidVirtualAddress(buffer), "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
std::vector<u8> message(size);
- for (size_t i = 0; i < size; i++) {
+ for (u32 i = 0; i < size; i++) {
message[i] = Memory::Read8(buffer + i);
}
@@ -324,7 +327,7 @@ static void ReadPipeIfPossible(Service::Interface* self) {
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
- ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr);
+ ASSERT_MSG(Memory::IsValidVirtualAddress(addr), "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -364,7 +367,7 @@ static void ReadPipe(Service::Interface* self) {
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
- ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr);
+ ASSERT_MSG(Memory::IsValidVirtualAddress(addr), "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
@@ -403,7 +406,7 @@ static void GetPipeReadableSize(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe);
+ cmd_buff[2] = static_cast<u32>(DSP::HLE::GetPipeReadableSize(pipe));
LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, cmd_buff[2]);
}
@@ -440,9 +443,9 @@ static void GetHeadphoneStatus(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 0; // Not using headphones?
+ cmd_buff[2] = 0; // Not using headphones
- LOG_WARNING(Service_DSP, "(STUBBED) called");
+ LOG_DEBUG(Service_DSP, "called");
}
/**
diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp
index 15d604bb6..29d144365 100644
--- a/src/core/hle/service/frd/frd.cpp
+++ b/src/core/hle/service/frd/frd.cpp
@@ -23,7 +23,7 @@ void GetMyPresence(Service::Interface* self) {
ASSERT(shifted_out_size == ((sizeof(MyPresence) << 14) | 2));
- Memory::WriteBlock(my_presence_addr, reinterpret_cast<const u8*>(&my_presence), sizeof(MyPresence));
+ Memory::WriteBlock(my_presence_addr, &my_presence, sizeof(MyPresence));
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -39,8 +39,7 @@ void GetFriendKeyList(Service::Interface* self) {
FriendKey zero_key = {};
for (u32 i = 0; i < frd_count; ++i) {
- Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey),
- reinterpret_cast<const u8*>(&zero_key), sizeof(FriendKey));
+ Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey), &zero_key, sizeof(FriendKey));
}
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -58,8 +57,7 @@ void GetFriendProfile(Service::Interface* self) {
Profile zero_profile = {};
for (u32 i = 0; i < count; ++i) {
- Memory::WriteBlock(profiles_addr + i * sizeof(Profile),
- reinterpret_cast<const u8*>(&zero_profile), sizeof(Profile));
+ Memory::WriteBlock(profiles_addr + i * sizeof(Profile), &zero_profile, sizeof(Profile));
}
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@@ -88,7 +86,7 @@ void GetMyFriendKey(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- Memory::WriteBlock(cmd_buff[2], reinterpret_cast<const u8*>(&my_friend_key), sizeof(FriendKey));
+ Memory::WriteBlock(cmd_buff[2], &my_friend_key, sizeof(FriendKey));
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index cc51ede0c..81b9abe4c 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -108,13 +108,14 @@ ResultVal<bool> File::SyncRequest() {
offset, length, backend->GetSize());
}
- ResultVal<size_t> read = backend->Read(offset, length, Memory::GetPointer(address));
+ std::vector<u8> data(length);
+ ResultVal<size_t> read = backend->Read(offset, data.size(), data.data());
if (read.Failed()) {
cmd_buff[1] = read.Code().raw;
return read.Code();
}
+ Memory::WriteBlock(address, data.data(), *read);
cmd_buff[2] = static_cast<u32>(*read);
- Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length);
break;
}
@@ -128,7 +129,9 @@ ResultVal<bool> File::SyncRequest() {
LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
- ResultVal<size_t> written = backend->Write(offset, length, flush != 0, Memory::GetPointer(address));
+ std::vector<u8> data(length);
+ Memory::ReadBlock(address, data.data(), data.size());
+ ResultVal<size_t> written = backend->Write(offset, data.size(), flush != 0, data.data());
if (written.Failed()) {
cmd_buff[1] = written.Code().raw;
return written.Code();
@@ -216,12 +219,14 @@ ResultVal<bool> Directory::SyncRequest() {
{
u32 count = cmd_buff[1];
u32 address = cmd_buff[3];
- auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
+ std::vector<FileSys::Entry> entries(count);
LOG_TRACE(Service_FS, "Read %s %s: count=%d",
GetTypeName().c_str(), GetName().c_str(), count);
// Number of entries actually read
- cmd_buff[2] = backend->Read(count, entries);
+ u32 read = backend->Read(entries.size(), entries.data());
+ cmd_buff[2] = read;
+ Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry));
break;
}
@@ -456,11 +461,12 @@ ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon
if (result.IsError())
return result;
- u8* smdh_icon = Memory::GetPointer(icon_buffer);
- if (!smdh_icon)
+ if (!Memory::IsValidVirtualAddress(icon_buffer))
return ResultCode(-1); // TODO(Subv): Find the right error code
- ext_savedata->WriteIcon(path, smdh_icon, icon_size);
+ std::vector<u8> smdh_icon(icon_size);
+ Memory::ReadBlock(icon_buffer, smdh_icon.data(), smdh_icon.size());
+ ext_savedata->WriteIcon(path, smdh_icon.data(), smdh_icon.size());
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index b4c146e08..ec565f46d 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -44,7 +44,7 @@ Kernel::SharedPtr<Kernel::SharedMemory> g_shared_memory;
u32 g_thread_id = 0;
static bool gpu_right_acquired = false;
-
+static bool first_initialization = true;
/// Gets a pointer to a thread command buffer in GSP shared memory
static inline u8* GetCommandBuffer(u32 thread_id) {
return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer)));
@@ -66,14 +66,26 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
}
/**
+ * Writes a single GSP GPU hardware registers with a single u32 value
+ * (For internal use.)
+ *
+ * @param base_address The address of the register in question
+ * @param data Data to be written
+ */
+static void WriteSingleHWReg(u32 base_address, u32 data) {
+ DEBUG_ASSERT_MSG((base_address & 3) == 0 && base_address < 0x420000, "Write address out of range or misaligned");
+ HW::Write<u32>(base_address + REGS_BEGIN, data);
+}
+
+/**
* Writes sequential GSP GPU hardware registers using an array of source data
*
* @param base_address The address of the first register in the sequence
* @param size_in_bytes The number of registers to update (size of data)
- * @param data A pointer to the source data
+ * @param data_vaddr A pointer to the source data
* @return RESULT_SUCCESS if the parameters are valid, error code otherwise
*/
-static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
+static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_vaddr) {
// This magic number is verified to be done by the gsp module
const u32 max_size_in_bytes = 0x80;
@@ -87,10 +99,10 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* da
return ERR_GSP_REGS_MISALIGNED;
} else {
while (size_in_bytes > 0) {
- HW::Write<u32>(base_address + REGS_BEGIN, *data);
+ WriteSingleHWReg(base_address, Memory::Read32(data_vaddr));
size_in_bytes -= 4;
- ++data;
+ data_vaddr += 4;
base_address += 4;
}
return RESULT_SUCCESS;
@@ -112,7 +124,7 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* da
* @param masks A pointer to the masks
* @return RESULT_SUCCESS if the parameters are valid, error code otherwise
*/
-static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) {
+static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr data_vaddr, VAddr masks_vaddr) {
// This magic number is verified to be done by the gsp module
const u32 max_size_in_bytes = 0x80;
@@ -131,14 +143,17 @@ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const
u32 reg_value;
HW::Read<u32>(reg_value, reg_address);
+ u32 data = Memory::Read32(data_vaddr);
+ u32 mask = Memory::Read32(masks_vaddr);
+
// Update the current value of the register only for set mask bits
- reg_value = (reg_value & ~*masks) | (*data | *masks);
+ reg_value = (reg_value & ~mask) | (data | mask);
- HW::Write<u32>(reg_address, reg_value);
+ WriteSingleHWReg(base_address, reg_value);
size_in_bytes -= 4;
- ++data;
- ++masks;
+ data_vaddr += 4;
+ masks_vaddr += 4;
base_address += 4;
}
return RESULT_SUCCESS;
@@ -164,8 +179,7 @@ static void WriteHWRegs(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
-
- u32* src = (u32*)Memory::GetPointer(cmd_buff[4]);
+ VAddr src = cmd_buff[4];
cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw;
}
@@ -186,8 +200,8 @@ static void WriteHWRegsWithMask(Service::Interface* self) {
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
- u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]);
- u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]);
+ VAddr src_data = cmd_buff[4];
+ VAddr mask_data = cmd_buff[6];
cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw;
}
@@ -210,13 +224,16 @@ static void ReadHWRegs(Service::Interface* self) {
return;
}
- u32* dst = (u32*)Memory::GetPointer(cmd_buff[0x41]);
+ VAddr dst_vaddr = cmd_buff[0x41];
while (size > 0) {
- HW::Read<u32>(*dst, reg_addr + REGS_BEGIN);
+ u32 value;
+ HW::Read<u32>(value, reg_addr + REGS_BEGIN);
+
+ Memory::Write32(dst_vaddr, value);
size -= 4;
- ++dst;
+ dst_vaddr += 4;
reg_addr += 4;
}
}
@@ -226,22 +243,22 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left);
PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right);
if (info.active_fb == 0) {
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)),
- 4, &phys_address_left);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)),
- 4, &phys_address_right);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)),
+ phys_address_left);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)),
+ phys_address_right);
} else {
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)),
- 4, &phys_address_left);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)),
- 4, &phys_address_right);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)),
+ phys_address_left);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)),
+ phys_address_right);
}
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)),
- 4, &info.stride);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)),
- 4, &info.format);
- WriteHWRegs(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)),
- 4, &info.shown_fb);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)),
+ info.stride);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)),
+ info.format);
+ WriteSingleHWReg(base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)),
+ info.shown_fb);
if (Pica::g_debug_context)
Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr);
@@ -330,23 +347,25 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
u32 flags = cmd_buff[1];
g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]);
+ // TODO(mailwl): return right error code instead assert
ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!");
g_interrupt_event->name = "GSP_GPU::interrupt_event";
- using Kernel::MemoryPermission;
- g_shared_memory = Kernel::SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
- MemoryPermission::ReadWrite, "GSPSharedMem");
-
- Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom();
-
- // This specific code is required for a successful initialization, rather than 0
- cmd_buff[1] = ResultCode((ErrorDescription)519, ErrorModule::GX,
- ErrorSummary::Success, ErrorLevel::Success).raw;
+ if (first_initialization) {
+ // This specific code is required for a successful initialization, rather than 0
+ first_initialization = false;
+ cmd_buff[1] = ResultCode(ErrorDescription::GPU_FirstInitialization, ErrorModule::GX,
+ ErrorSummary::Success, ErrorLevel::Success).raw;
+ } else {
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ }
cmd_buff[2] = g_thread_id++; // Thread ID
- cmd_buff[4] = shmem_handle; // GSP shared memory
+ cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); // GSP shared memory
g_interrupt_event->Signal(); // TODO(bunnei): Is this correct?
+
+ LOG_WARNING(Service_GSP, "called, flags=0x%08X", flags);
}
/**
@@ -357,12 +376,12 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
static void UnregisterInterruptRelayQueue(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- g_shared_memory = nullptr;
+ g_thread_id = 0;
g_interrupt_event = nullptr;
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_GSP, "called");
+ LOG_WARNING(Service_GSP, "(STUBBED) called");
}
/**
@@ -431,9 +450,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
command.dma_request.size);
- memcpy(Memory::GetPointer(command.dma_request.dest_address),
- Memory::GetPointer(command.dma_request.source_address),
- command.dma_request.size);
+ // TODO(Subv): These memory accesses should not go through the application's memory mapping.
+ // They should go through the GSP module's memory mapping.
+ Memory::CopyBlock(command.dma_request.dest_address, command.dma_request.source_address, command.dma_request.size);
SignalInterrupt(InterruptId::DMA);
break;
}
@@ -700,10 +719,15 @@ Interface::Interface() {
Register(FunctionTable);
g_interrupt_event = nullptr;
- g_shared_memory = nullptr;
+
+ using Kernel::MemoryPermission;
+ g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000,
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ 0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory");
g_thread_id = 0;
gpu_right_acquired = false;
+ first_initialization = true;
}
Interface::~Interface() {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1053d0f40..d216cecb4 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -280,8 +280,9 @@ void Init() {
AddService(new HID_SPVR_Interface);
using Kernel::MemoryPermission;
- shared_mem = SharedMemory::Create(0x1000, MemoryPermission::ReadWrite,
- MemoryPermission::Read, "HID:SharedMem");
+ shared_mem = SharedMemory::Create(nullptr, 0x1000,
+ MemoryPermission::ReadWrite, MemoryPermission::Read,
+ 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
next_pad_index = 0;
next_touch_index = 0;
diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp
index 505c441c6..079a87e48 100644
--- a/src/core/hle/service/ir/ir.cpp
+++ b/src/core/hle/service/ir/ir.cpp
@@ -94,8 +94,9 @@ void Init() {
AddService(new IR_User_Interface);
using Kernel::MemoryPermission;
- shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory");
+ shared_memory = SharedMemory::Create(nullptr, 0x1000,
+ Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::ReadWrite,
+ 0, Kernel::MemoryRegion::BASE, "IR:SharedMemory");
transfer_shared_memory = nullptr;
// Create event handle(s)
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..395880843 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,9 +7,9 @@
#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"
#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/err_f.h"
#include "core/hle/service/gsp_gpu.h"
@@ -30,6 +30,7 @@
#include "core/hle/service/boss/boss.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cecd/cecd.h"
+#include "core/hle/service/dlp/dlp.h"
#include "core/hle/service/frd/frd.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/cfg/cfg.h"
@@ -110,6 +111,7 @@ void Init() {
Service::CAM::Init();
Service::CECD::Init();
Service::CFG::Init();
+ Service::DLP::Init();
Service::FRD::Init();
Service::HID::Init();
Service::IR::Init();
@@ -119,9 +121,9 @@ 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);
AddService(new DSP_DSP::Interface);
AddService(new GSP_GPU::Interface);
AddService(new GSP_LCD::Interface);
@@ -148,6 +150,7 @@ void Shutdown() {
Service::IR::Shutdown();
Service::HID::Shutdown();
Service::FRD::Shutdown();
+ Service::DLP::Shutdown();
Service::CFG::Shutdown();
Service::CECD::Shutdown();
Service::CAM::Shutdown();
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index d3e5d4bca..9b285567b 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -373,14 +373,18 @@ static void Bind(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
- CTRSockAddr* ctr_sock_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
- if (ctr_sock_addr == nullptr) {
+ // Virtual address of the sock_addr structure
+ VAddr sock_addr_addr = cmd_buffer[6];
+ if (!Memory::IsValidVirtualAddress(sock_addr_addr)) {
cmd_buffer[1] = -1; // TODO(Subv): Correct code
return;
}
- sockaddr sock_addr = CTRSockAddr::ToPlatform(*ctr_sock_addr);
+ CTRSockAddr ctr_sock_addr;
+ Memory::ReadBlock(sock_addr_addr, reinterpret_cast<u8*>(&ctr_sock_addr), sizeof(CTRSockAddr));
+
+ sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
@@ -496,7 +500,7 @@ static void Accept(Service::Interface* self) {
result = TranslateError(GET_ERRNO);
} else {
CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
- Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len);
+ Memory::WriteBlock(cmd_buffer[0x104 >> 2], &ctr_addr, sizeof(ctr_addr));
}
cmd_buffer[0] = IPC::MakeHeader(4, 2, 2);
@@ -547,20 +551,31 @@ static void SendTo(Service::Interface* self) {
u32 flags = cmd_buffer[3];
u32 addr_len = cmd_buffer[4];
- u8* input_buff = Memory::GetPointer(cmd_buffer[8]);
- CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[10]));
+ VAddr input_buff_address = cmd_buffer[8];
+ if (!Memory::IsValidVirtualAddress(input_buff_address)) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
- if (ctr_dest_addr == nullptr) {
+ // Memory address of the dest_addr structure
+ VAddr dest_addr_addr = cmd_buffer[10];
+ if (!Memory::IsValidVirtualAddress(dest_addr_addr)) {
cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
return;
}
+ std::vector<u8> input_buff(len);
+ Memory::ReadBlock(input_buff_address, input_buff.data(), input_buff.size());
+
+ CTRSockAddr ctr_dest_addr;
+ Memory::ReadBlock(dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
+
int ret = -1;
if (addr_len > 0) {
- sockaddr dest_addr = CTRSockAddr::ToPlatform(*ctr_dest_addr);
- ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, &dest_addr, sizeof(dest_addr));
+ sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
+ ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags, &dest_addr, sizeof(dest_addr));
} else {
- ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, nullptr, 0);
+ ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags, nullptr, 0);
}
int result = 0;
@@ -591,14 +606,24 @@ static void RecvFrom(Service::Interface* self) {
std::memcpy(&buffer_parameters, &cmd_buffer[64], sizeof(buffer_parameters));
- u8* output_buff = Memory::GetPointer(buffer_parameters.output_buffer_addr);
+ if (!Memory::IsValidVirtualAddress(buffer_parameters.output_buffer_addr)) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ if (!Memory::IsValidVirtualAddress(buffer_parameters.output_src_address_buffer)) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ std::vector<u8> output_buff(len);
sockaddr src_addr;
socklen_t src_addr_len = sizeof(src_addr);
- int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
+ int ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags, &src_addr, &src_addr_len);
if (ret >= 0 && buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) {
- CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer));
- *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
+ CTRSockAddr ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
+ Memory::WriteBlock(buffer_parameters.output_src_address_buffer, &ctr_src_addr, sizeof(ctr_src_addr));
}
int result = 0;
@@ -606,6 +631,9 @@ static void RecvFrom(Service::Interface* self) {
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
total_received = 0;
+ } else {
+ // Write only the data we received to avoid overwriting parts of the buffer with zeros
+ Memory::WriteBlock(buffer_parameters.output_buffer_addr, output_buff.data(), total_received);
}
cmd_buffer[1] = result;
@@ -617,18 +645,28 @@ static void Poll(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 nfds = cmd_buffer[1];
int timeout = cmd_buffer[2];
- CTRPollFD* input_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[6]));
- CTRPollFD* output_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+
+ VAddr input_fds_addr = cmd_buffer[6];
+ VAddr output_fds_addr = cmd_buffer[0x104 >> 2];
+ if (!Memory::IsValidVirtualAddress(input_fds_addr) || !Memory::IsValidVirtualAddress(output_fds_addr)) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find correct error code.
+ return;
+ }
+
+ std::vector<CTRPollFD> ctr_fds(nfds);
+ Memory::ReadBlock(input_fds_addr, ctr_fds.data(), nfds * sizeof(CTRPollFD));
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
// so we have to copy the data
std::vector<pollfd> platform_pollfd(nfds);
- std::transform(input_fds, input_fds + nfds, platform_pollfd.begin(), CTRPollFD::ToPlatform);
+ std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform);
const int ret = ::poll(platform_pollfd.data(), nfds, timeout);
// Now update the output pollfd structure
- std::transform(platform_pollfd.begin(), platform_pollfd.end(), output_fds, CTRPollFD::FromPlatform);
+ std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(), CTRPollFD::FromPlatform);
+
+ Memory::WriteBlock(output_fds_addr, ctr_fds.data(), nfds * sizeof(CTRPollFD));
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
@@ -643,14 +681,16 @@ static void GetSockName(Service::Interface* self) {
u32 socket_handle = cmd_buffer[1];
socklen_t ctr_len = cmd_buffer[2];
- CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+ // Memory address of the ctr_dest_addr structure
+ VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2];
sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr);
int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len);
- if (ctr_dest_addr != nullptr) {
- *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ if (ctr_dest_addr_addr != 0 && Memory::IsValidVirtualAddress(ctr_dest_addr_addr)) {
+ CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ Memory::WriteBlock(ctr_dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
} else {
cmd_buffer[1] = -1; // TODO(Subv): Verify error
return;
@@ -682,14 +722,16 @@ static void GetPeerName(Service::Interface* self) {
u32 socket_handle = cmd_buffer[1];
socklen_t len = cmd_buffer[2];
- CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+ // Memory address of the ctr_dest_addr structure
+ VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2];
sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr);
int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len);
- if (ctr_dest_addr != nullptr) {
- *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ if (ctr_dest_addr_addr != 0 && Memory::IsValidVirtualAddress(ctr_dest_addr_addr)) {
+ CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
+ Memory::WriteBlock(ctr_dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
} else {
cmd_buffer[1] = -1;
return;
@@ -711,13 +753,17 @@ static void Connect(Service::Interface* self) {
u32 socket_handle = cmd_buffer[1];
socklen_t len = cmd_buffer[2];
- CTRSockAddr* ctr_input_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
- if (ctr_input_addr == nullptr) {
+ // Memory address of the ctr_input_addr structure
+ VAddr ctr_input_addr_addr = cmd_buffer[6];
+ if (!Memory::IsValidVirtualAddress(ctr_input_addr_addr)) {
cmd_buffer[1] = -1; // TODO(Subv): Verify error
return;
}
- sockaddr input_addr = CTRSockAddr::ToPlatform(*ctr_input_addr);
+ CTRSockAddr ctr_input_addr;
+ Memory::ReadBlock(ctr_input_addr_addr, &ctr_input_addr, sizeof(ctr_input_addr));
+
+ sockaddr input_addr = CTRSockAddr::ToPlatform(ctr_input_addr);
int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
int result = 0;
if (ret != 0)
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
index 14a4e98ec..a8aff1abf 100644
--- a/src/core/hle/service/ssl_c.cpp
+++ b/src/core/hle/service/ssl_c.cpp
@@ -31,7 +31,6 @@ static void GenerateRandomData(Service::Interface* self) {
u32 size = cmd_buff[1];
VAddr address = cmd_buff[3];
- u8* output_buff = Memory::GetPointer(address);
// Fill the output buffer with random data.
u32 data = 0;
@@ -44,13 +43,13 @@ static void GenerateRandomData(Service::Interface* self) {
if (size > 4) {
// Use up the entire 4 bytes of the random data for as long as possible
- *(u32*)(output_buff + i) = data;
+ Memory::Write32(address + i, data);
i += 4;
} else if (size == 2) {
- *(u16*)(output_buff + i) = (u16)(data & 0xffff);
+ Memory::Write16(address + i, static_cast<u16>(data & 0xffff));
i += 2;
} else {
- *(u8*)(output_buff + i) = (u8)(data & 0xff);
+ Memory::Write8(address + i, static_cast<u8>(data & 0xff));
i++;
}
}
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index fb2aecbf2..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"
@@ -99,6 +100,7 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add
switch (operation & MEMOP_OPERATION_MASK) {
case MEMOP_FREE:
{
+ // TODO(Subv): What happens if an application tries to FREE a block of memory that has a SharedMemory pointing to it?
if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) {
ResultCode result = process.HeapFree(addr0, size);
if (result.IsError()) return result;
@@ -160,8 +162,6 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions);
- // TODO(Subv): The same process that created a SharedMemory object can not map it in its own address space
-
SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
if (shared_memory == nullptr)
return ERR_INVALID_HANDLE;
@@ -176,7 +176,7 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
case MemoryPermission::WriteExecute:
case MemoryPermission::ReadWriteExecute:
case MemoryPermission::DontCare:
- return shared_memory->Map(addr, permissions_type,
+ return shared_memory->Map(Kernel::g_current_process.get(), addr, permissions_type,
static_cast<MemoryPermission>(other_permissions));
default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
@@ -196,7 +196,7 @@ static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) {
if (shared_memory == nullptr)
return ERR_INVALID_HANDLE;
- return shared_memory->Unmap(addr);
+ return shared_memory->Unmap(Kernel::g_current_process.get(), addr);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -327,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...
@@ -496,8 +496,16 @@ static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point
break;
}
+ if (processor_id == THREADPROCESSORID_1 || processor_id == THREADPROCESSORID_ALL ||
+ (processor_id == THREADPROCESSORID_DEFAULT && Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1)) {
+ LOG_WARNING(Kernel_SVC, "Newly created thread is allowed to be run in the SysCore, unimplemented.");
+ }
+
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, "
@@ -785,18 +793,44 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
if (size % Memory::PAGE_SIZE != 0)
return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
- // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap
-
- // TODO(Subv): Implement this function properly
+ SharedPtr<SharedMemory> shared_memory = nullptr;
using Kernel::MemoryPermission;
- SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size,
- (MemoryPermission)my_permission, (MemoryPermission)other_permission);
- // Map the SharedMemory to the specified address
- shared_memory->base_address = addr;
+ auto VerifyPermissions = [](MemoryPermission permission) {
+ // SharedMemory blocks can not be created with Execute permissions
+ switch (permission) {
+ case MemoryPermission::None:
+ case MemoryPermission::Read:
+ case MemoryPermission::Write:
+ case MemoryPermission::ReadWrite:
+ case MemoryPermission::DontCare:
+ return true;
+ default:
+ return false;
+ }
+ };
+
+ if (!VerifyPermissions(static_cast<MemoryPermission>(my_permission)) ||
+ !VerifyPermissions(static_cast<MemoryPermission>(other_permission)))
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+
+ if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) {
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }
+
+ // When trying to create a memory block with address = 0,
+ // if the process has the Shared Device Memory flag in the exheader,
+ // then we have to allocate from the same region as the caller process instead of the BASE region.
+ Kernel::MemoryRegion region = Kernel::MemoryRegion::BASE;
+ if (addr == 0 && Kernel::g_current_process->flags.shared_device_mem)
+ region = Kernel::g_current_process->flags.memory_region;
+
+ shared_memory = SharedMemory::Create(Kernel::g_current_process, size,
+ static_cast<MemoryPermission>(my_permission), static_cast<MemoryPermission>(other_permission), addr, region);
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory)));
- LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr);
+ LOG_WARNING(Kernel_SVC, "called addr=0x%08X", addr);
return RESULT_SUCCESS;
}
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 2fe856293..a4dfb7e43 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -188,10 +188,10 @@ inline void Write(u32 addr, const T data) {
u32 output_gap = config.texture_copy.output_gap * 16;
size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap);
- Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size);
+ Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), static_cast<u32>(contiguous_input_size));
size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap);
- Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size);
+ Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), static_cast<u32>(contiguous_output_size));
u32 remaining_size = config.texture_copy.size;
u32 remaining_input = input_width;
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 48a11ef81..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"
@@ -178,11 +179,11 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
const auto& table = reloc_table[current_inprogress];
LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)", current_segment_reloc_table,
- (u32)table.skip, (u32)table.patch);
+ static_cast<u32>(table.skip), static_cast<u32>(table.patch));
pos += table.skip;
s32 num_patches = table.patch;
while (0 < num_patches && pos < end_pos) {
- u32 in_addr = (u8*)pos - program_image.data();
+ u32 in_addr = static_cast<u32>(reinterpret_cast<u8*>(pos) - program_image.data());
u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)",
base_addr + in_addr, addr, current_segment_reloc_table, *pos);
@@ -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;
}
@@ -284,7 +287,7 @@ ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& ro
// Check if the 3DSX has a RomFS...
if (hdr.fs_offset != 0) {
u32 romfs_offset = hdr.fs_offset;
- u32 romfs_size = file.GetSize() - hdr.fs_offset;
+ u32 romfs_size = static_cast<u32>(file.GetSize()) - hdr.fs_offset;
LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
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 0d4c1d351..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,43 +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
- if (ResultStatus::Success == app_loader->Load()) {
- Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*app_loader), Service::FS::ArchiveIdCode::RomFS);
- return ResultStatus::Success;
- }
- break;
- }
-
- // 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 d362a4419..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"
@@ -156,6 +158,9 @@ ResultStatus AppLoader_NCCH::LoadExec() {
Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(
static_cast<Kernel::ResourceLimitCategory>(exheader_header.arm11_system_local_caps.resource_limit_category));
+ // Set the default CPU core for this process
+ Kernel::g_current_process->ideal_processor = exheader_header.arm11_system_local_caps.ideal_processor;
+
// Copy data while converting endianess
std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps;
std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps));
@@ -300,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.cpp b/src/core/memory.cpp
index ee9b69f81..8c9e5d46d 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -246,6 +246,26 @@ void Write(const VAddr vaddr, const T data) {
}
}
+bool IsValidVirtualAddress(const VAddr vaddr) {
+ const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
+ if (page_pointer)
+ return true;
+
+ if (current_page_table->attributes[vaddr >> PAGE_BITS] != PageType::Special)
+ return false;
+
+ MMIORegionPointer mmio_region = GetMMIOHandler(vaddr);
+ if (mmio_region) {
+ return mmio_region->IsValidAddress(vaddr);
+ }
+
+ return false;
+}
+
+bool IsValidPhysicalAddress(const PAddr paddr) {
+ return IsValidVirtualAddress(PhysicalToVirtualAddress(paddr));
+}
+
u8* GetPointer(const VAddr vaddr) {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer) {
@@ -261,6 +281,7 @@ u8* GetPointer(const VAddr vaddr) {
}
u8* GetPhysicalPointer(PAddr address) {
+ // TODO(Subv): This call should not go through the application's memory mapping.
return GetPointer(PhysicalToVirtualAddress(address));
}
@@ -343,6 +364,59 @@ u64 Read64(const VAddr addr) {
return Read<u64_le>(addr);
}
+void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
+ size_t remaining_size = size;
+ size_t page_index = src_addr >> PAGE_BITS;
+ size_t page_offset = src_addr & PAGE_MASK;
+
+ while (remaining_size > 0) {
+ const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
+ const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
+
+ switch (current_page_table->attributes[page_index]) {
+ case PageType::Unmapped: {
+ LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, src_addr, size);
+ std::memset(dest_buffer, 0, copy_amount);
+ break;
+ }
+ case PageType::Memory: {
+ DEBUG_ASSERT(current_page_table->pointers[page_index]);
+
+ const u8* src_ptr = current_page_table->pointers[page_index] + page_offset;
+ std::memcpy(dest_buffer, src_ptr, copy_amount);
+ break;
+ }
+ case PageType::Special: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedMemory: {
+ RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ std::memcpy(dest_buffer, GetPointerFromVMA(current_vaddr), copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedSpecial: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ page_index++;
+ page_offset = 0;
+ dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
+ remaining_size -= copy_amount;
+ }
+}
+
void Write8(const VAddr addr, const u8 data) {
Write<u8>(addr, data);
}
@@ -359,9 +433,165 @@ void Write64(const VAddr addr, const u64 data) {
Write<u64_le>(addr, data);
}
-void WriteBlock(const VAddr addr, const u8* data, const size_t size) {
- for (u32 offset = 0; offset < size; offset++) {
- Write8(addr + offset, data[offset]);
+void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) {
+ size_t remaining_size = size;
+ size_t page_index = dest_addr >> PAGE_BITS;
+ size_t page_offset = dest_addr & PAGE_MASK;
+
+ while (remaining_size > 0) {
+ const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
+ const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
+
+ switch (current_page_table->attributes[page_index]) {
+ case PageType::Unmapped: {
+ LOG_ERROR(HW_Memory, "unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, dest_addr, size);
+ break;
+ }
+ case PageType::Memory: {
+ DEBUG_ASSERT(current_page_table->pointers[page_index]);
+
+ u8* dest_ptr = current_page_table->pointers[page_index] + page_offset;
+ std::memcpy(dest_ptr, src_buffer, copy_amount);
+ break;
+ }
+ case PageType::Special: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedMemory: {
+ RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ std::memcpy(GetPointerFromVMA(current_vaddr), src_buffer, copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedSpecial: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ page_index++;
+ page_offset = 0;
+ src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
+ remaining_size -= copy_amount;
+ }
+}
+
+void ZeroBlock(const VAddr dest_addr, const size_t size) {
+ size_t remaining_size = size;
+ size_t page_index = dest_addr >> PAGE_BITS;
+ size_t page_offset = dest_addr & PAGE_MASK;
+
+ static const std::array<u8, PAGE_SIZE> zeros = {};
+
+ while (remaining_size > 0) {
+ const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
+ const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
+
+ switch (current_page_table->attributes[page_index]) {
+ case PageType::Unmapped: {
+ LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, dest_addr, size);
+ break;
+ }
+ case PageType::Memory: {
+ DEBUG_ASSERT(current_page_table->pointers[page_index]);
+
+ u8* dest_ptr = current_page_table->pointers[page_index] + page_offset;
+ std::memset(dest_ptr, 0, copy_amount);
+ break;
+ }
+ case PageType::Special: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedMemory: {
+ RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedSpecial: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ page_index++;
+ page_offset = 0;
+ remaining_size -= copy_amount;
+ }
+}
+
+void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
+ size_t remaining_size = size;
+ size_t page_index = src_addr >> PAGE_BITS;
+ size_t page_offset = src_addr & PAGE_MASK;
+
+ while (remaining_size > 0) {
+ const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
+ const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset;
+
+ switch (current_page_table->attributes[page_index]) {
+ case PageType::Unmapped: {
+ LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, src_addr, size);
+ ZeroBlock(dest_addr, copy_amount);
+ break;
+ }
+ case PageType::Memory: {
+ DEBUG_ASSERT(current_page_table->pointers[page_index]);
+ const u8* src_ptr = current_page_table->pointers[page_index] + page_offset;
+ WriteBlock(dest_addr, src_ptr, copy_amount);
+ break;
+ }
+ case PageType::Special: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ std::vector<u8> buffer(copy_amount);
+ GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
+ WriteBlock(dest_addr, buffer.data(), buffer.size());
+ break;
+ }
+ case PageType::RasterizerCachedMemory: {
+ RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount);
+ break;
+ }
+ case PageType::RasterizerCachedSpecial: {
+ DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
+
+ RasterizerFlushRegion(VirtualToPhysicalAddress(current_vaddr), copy_amount);
+
+ std::vector<u8> buffer(copy_amount);
+ GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
+ WriteBlock(dest_addr, buffer.data(), buffer.size());
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ page_index++;
+ page_offset = 0;
+ dest_addr += copy_amount;
+ src_addr += copy_amount;
+ remaining_size -= copy_amount;
}
}
diff --git a/src/core/memory.h b/src/core/memory.h
index 9caa3c3f5..ae5588dee 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -100,15 +100,9 @@ enum : VAddr {
SHARED_PAGE_SIZE = 0x00001000,
SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
- // TODO(yuriks): The size of this area is dynamic, the kernel grows
- // it as more and more threads are created. For now we'll just use a
- // hardcoded value.
/// Area where TLS (Thread-Local Storage) buffers are allocated.
TLS_AREA_VADDR = 0x1FF82000,
TLS_ENTRY_SIZE = 0x200,
- TLS_AREA_SIZE = 300 * TLS_ENTRY_SIZE + 0x800, // Space for up to 300 threads + round to page size
- TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
-
/// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS.
NEW_LINEAR_HEAP_VADDR = 0x30000000,
@@ -116,6 +110,9 @@ enum : VAddr {
NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
};
+bool IsValidVirtualAddress(const VAddr addr);
+bool IsValidPhysicalAddress(const PAddr addr);
+
u8 Read8(VAddr addr);
u16 Read16(VAddr addr);
u32 Read32(VAddr addr);
@@ -126,7 +123,10 @@ void Write16(VAddr addr, u16 data);
void Write32(VAddr addr, u32 data);
void Write64(VAddr addr, u64 data);
-void WriteBlock(VAddr addr, const u8* data, size_t size);
+void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size);
+void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
+void ZeroBlock(const VAddr dest_addr, const size_t size);
+void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
u8* GetPointer(VAddr virtual_address);
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/mmio.h b/src/core/mmio.h
index 06b555e98..d76f005d8 100644
--- a/src/core/mmio.h
+++ b/src/core/mmio.h
@@ -18,15 +18,21 @@ class MMIORegion {
public:
virtual ~MMIORegion() = default;
+ virtual bool IsValidAddress(VAddr addr) = 0;
+
virtual u8 Read8(VAddr addr) = 0;
virtual u16 Read16(VAddr addr) = 0;
virtual u32 Read32(VAddr addr) = 0;
virtual u64 Read64(VAddr addr) = 0;
+ virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) = 0;
+
virtual void Write8(VAddr addr, u8 data) = 0;
virtual void Write16(VAddr addr, u16 data) = 0;
virtual void Write32(VAddr addr, u32 data) = 0;
virtual void Write64(VAddr addr, u64 data) = 0;
+
+ virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) = 0;
};
using MMIORegionPointer = std::shared_ptr<MMIORegion>;
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/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp
index c6dc35c83..7abaacf70 100644
--- a/src/core/tracer/recorder.cpp
+++ b/src/core/tracer/recorder.cpp
@@ -26,17 +26,17 @@ void Recorder::Finish(const std::string& filename) {
// Calculate file offsets
auto& initial = header.initial_state_offsets;
- initial.gpu_registers_size = initial_state.gpu_registers.size();
- initial.lcd_registers_size = initial_state.lcd_registers.size();
- initial.pica_registers_size = initial_state.pica_registers.size();
- initial.default_attributes_size = initial_state.default_attributes.size();
- initial.vs_program_binary_size = initial_state.vs_program_binary.size();
- initial.vs_swizzle_data_size = initial_state.vs_swizzle_data.size();
- initial.vs_float_uniforms_size = initial_state.vs_float_uniforms.size();
- initial.gs_program_binary_size = initial_state.gs_program_binary.size();
- initial.gs_swizzle_data_size = initial_state.gs_swizzle_data.size();
- initial.gs_float_uniforms_size = initial_state.gs_float_uniforms.size();
- header.stream_size = stream.size();
+ initial.gpu_registers_size = static_cast<u32>(initial_state.gpu_registers.size());
+ initial.lcd_registers_size = static_cast<u32>(initial_state.lcd_registers.size());
+ initial.pica_registers_size = static_cast<u32>(initial_state.pica_registers.size());
+ initial.default_attributes_size = static_cast<u32>(initial_state.default_attributes.size());
+ initial.vs_program_binary_size = static_cast<u32>(initial_state.vs_program_binary.size());
+ initial.vs_swizzle_data_size = static_cast<u32>(initial_state.vs_swizzle_data.size());
+ initial.vs_float_uniforms_size = static_cast<u32>(initial_state.vs_float_uniforms.size());
+ initial.gs_program_binary_size = static_cast<u32>(initial_state.gs_program_binary.size());
+ initial.gs_swizzle_data_size = static_cast<u32>(initial_state.gs_swizzle_data.size());
+ initial.gs_float_uniforms_size = static_cast<u32>(initial_state.gs_float_uniforms.size());
+ header.stream_size = static_cast<u32>(stream.size());
initial.gpu_registers = sizeof(header);
initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32);
@@ -68,7 +68,7 @@ void Recorder::Finish(const std::string& filename) {
DEBUG_ASSERT(stream_element.extra_data.size() == 0);
break;
}
- header.stream_offset += stream_element.extra_data.size();
+ header.stream_offset += static_cast<u32>(stream_element.extra_data.size());
}
try {
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/clipper.cpp b/src/video_core/clipper.cpp
index 2bc747102..db99ce666 100644
--- a/src/video_core/clipper.cpp
+++ b/src/video_core/clipper.cpp
@@ -75,8 +75,6 @@ static void InitScreenCoordinates(OutputVertex& vtx)
viewport.halfsize_y = float24::FromRaw(regs.viewport_size_y);
viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.viewport_corner.x));
viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.viewport_corner.y));
- viewport.zscale = float24::FromRaw(regs.viewport_depth_range);
- viewport.offset_z = float24::FromRaw(regs.viewport_depth_far_plane);
float24 inv_w = float24::FromFloat32(1.f) / vtx.pos.w;
vtx.color *= inv_w;
@@ -89,7 +87,7 @@ static void InitScreenCoordinates(OutputVertex& vtx)
vtx.screenpos[0] = (vtx.pos.x * inv_w + float24::FromFloat32(1.0)) * viewport.halfsize_x + viewport.offset_x;
vtx.screenpos[1] = (vtx.pos.y * inv_w + float24::FromFloat32(1.0)) * viewport.halfsize_y + viewport.offset_y;
- vtx.screenpos[2] = viewport.offset_z + vtx.pos.z * inv_w * viewport.zscale;
+ vtx.screenpos[2] = vtx.pos.z * inv_w;
}
void ProcessTriangle(const OutputVertex &v0, const OutputVertex &v1, const OutputVertex &v2) {
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index dd1379503..19e03adf4 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -128,7 +128,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// TODO: Verify that this actually modifies the register!
if (setup.index < 15) {
- g_state.vs.default_attributes[setup.index] = attribute;
+ g_state.vs_default_attributes[setup.index] = attribute;
setup.index++;
} else {
// Put each attribute into an immediate input buffer.
@@ -144,12 +144,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
immediate_attribute_id = 0;
Shader::UnitState<false> shader_unit;
- Shader::Setup();
+ g_state.vs.Setup();
// Send to vertex shader
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, static_cast<void*>(&immediate_input));
- Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
+ g_state.vs.Run(shader_unit, immediate_input, regs.vs.num_input_attributes+1);
+ Shader::OutputVertex output_vertex = shader_unit.output_registers.ToVertex(regs.vs);
// Send to renderer
using Pica::Shader::OutputVertex;
@@ -157,7 +158,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
};
- g_state.primitive_assembler.SubmitVertex(output, AddTriangle);
+ g_state.primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
}
}
}
@@ -199,9 +200,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));
@@ -231,13 +231,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// The size has been tuned for optimal balance between hit-rate and the cost of lookup
const size_t VERTEX_CACHE_SIZE = 32;
std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
- std::array<Shader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache;
+ std::array<Shader::OutputRegisters, VERTEX_CACHE_SIZE> vertex_cache;
unsigned int vertex_cache_pos = 0;
vertex_cache_ids.fill(-1);
Shader::UnitState<false> shader_unit;
- Shader::Setup();
+ g_state.vs.Setup();
for (unsigned int index = 0; index < regs.num_vertices; ++index)
{
@@ -249,7 +249,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
ASSERT(vertex != -1);
bool vertex_cache_hit = false;
- Shader::OutputVertex output;
+ Shader::OutputRegisters output_registers;
if (is_indexed) {
if (g_debug_context && Pica::g_debug_context->recorder) {
@@ -259,7 +259,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
if (vertex == vertex_cache_ids[i]) {
- output = vertex_cache[i];
+ output_registers = vertex_cache[i];
vertex_cache_hit = true;
break;
}
@@ -274,15 +274,19 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// Send to vertex shader
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, (void*)&input);
- output = Shader::Run(shader_unit, input, loader.GetNumTotalAttributes());
+ g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes());
+ output_registers = shader_unit.output_registers;
if (is_indexed) {
- vertex_cache[vertex_cache_pos] = output;
+ vertex_cache[vertex_cache_pos] = output_registers;
vertex_cache_ids[vertex_cache_pos] = vertex;
vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
}
}
+ // Retreive vertex from register data
+ Shader::OutputVertex output_vertex = output_registers.ToVertex(regs.vs);
+
// Send to renderer
using Pica::Shader::OutputVertex;
auto AddTriangle = [](
@@ -290,7 +294,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
};
- primitive_assembler.SubmitVertex(output, AddTriangle);
+ primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
}
for (auto& range : memory_accesses.ranges) {
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index fb20f81dd..871368323 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -208,11 +208,12 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
// TODO: Reduce the amount of binary code written to relevant portions
dvlp.binary_offset = write_offset - dvlp_offset;
- dvlp.binary_size_words = setup.program_code.size();
- QueueForWriting(reinterpret_cast<const u8*>(setup.program_code.data()), setup.program_code.size() * sizeof(u32));
+ dvlp.binary_size_words = static_cast<uint32_t>(setup.program_code.size());
+ QueueForWriting(reinterpret_cast<const u8*>(setup.program_code.data()),
+ static_cast<u32>(setup.program_code.size()) * sizeof(u32));
dvlp.swizzle_info_offset = write_offset - dvlp_offset;
- dvlp.swizzle_info_num_entries = setup.swizzle_data.size();
+ dvlp.swizzle_info_num_entries = static_cast<uint32_t>(setup.swizzle_data.size());
u32 dummy = 0;
for (unsigned int i = 0; i < setup.swizzle_data.size(); ++i) {
QueueForWriting(reinterpret_cast<const u8*>(&setup.swizzle_data[i]), sizeof(setup.swizzle_data[i]));
@@ -264,7 +265,7 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
constant_table.emplace_back(constant);
}
dvle.constant_table_offset = write_offset - dvlb.dvle_offset;
- dvle.constant_table_size = constant_table.size();
+ dvle.constant_table_size = static_cast<uint32_t>(constant_table.size());
for (const auto& constant : constant_table) {
QueueForWriting(reinterpret_cast<const u8*>(&constant), sizeof(constant));
}
@@ -695,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.cpp b/src/video_core/pica.cpp
index be82cf4b5..ec78f9593 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -500,7 +500,7 @@ void Init() {
}
void Shutdown() {
- Shader::Shutdown();
+ Shader::ClearCache();
}
template <typename T>
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 5891fb72a..544ea037f 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -70,7 +70,7 @@ struct Regs {
INSERT_PADDING_WORDS(0x9);
BitField<0, 24, u32> viewport_depth_range; // float24
- BitField<0, 24, u32> viewport_depth_far_plane; // float24
+ BitField<0, 24, u32> viewport_depth_near_plane; // float24
BitField<0, 3, u32> vs_output_total;
@@ -122,9 +122,31 @@ struct Regs {
BitField<16, 10, s32> y;
} viewport_corner;
- INSERT_PADDING_WORDS(0x17);
+ INSERT_PADDING_WORDS(0x1);
+
+ //TODO: early depth
+ INSERT_PADDING_WORDS(0x1);
+
+ INSERT_PADDING_WORDS(0x2);
+
+ enum DepthBuffering : u32 {
+ WBuffering = 0,
+ ZBuffering = 1,
+ };
+ BitField< 0, 1, DepthBuffering> depthmap_enable;
+
+ INSERT_PADDING_WORDS(0x12);
struct TextureConfig {
+ enum TextureType : u32 {
+ Texture2D = 0,
+ TextureCube = 1,
+ Shadow2D = 2,
+ Projection2D = 3,
+ ShadowCube = 4,
+ Disabled = 5,
+ };
+
enum WrapMode : u32 {
ClampToEdge = 0,
ClampToBorder = 1,
@@ -155,6 +177,7 @@ struct Regs {
BitField< 2, 1, TextureFilter> min_filter;
BitField< 8, 2, WrapMode> wrap_t;
BitField<12, 2, WrapMode> wrap_s;
+ BitField<28, 2, TextureType> type; ///< @note Only valid for texture 0 according to 3DBrew.
};
INSERT_PADDING_WORDS(0x1);
@@ -764,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;
@@ -801,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;
@@ -822,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;
}
@@ -1279,10 +1300,11 @@ ASSERT_REG_POSITION(cull_mode, 0x40);
ASSERT_REG_POSITION(viewport_size_x, 0x41);
ASSERT_REG_POSITION(viewport_size_y, 0x43);
ASSERT_REG_POSITION(viewport_depth_range, 0x4d);
-ASSERT_REG_POSITION(viewport_depth_far_plane, 0x4e);
+ASSERT_REG_POSITION(viewport_depth_near_plane, 0x4e);
ASSERT_REG_POSITION(vs_output_attributes[0], 0x50);
ASSERT_REG_POSITION(vs_output_attributes[1], 0x51);
ASSERT_REG_POSITION(viewport_corner, 0x68);
+ASSERT_REG_POSITION(depthmap_enable, 0x6D);
ASSERT_REG_POSITION(texture0_enable, 0x80);
ASSERT_REG_POSITION(texture0, 0x81);
ASSERT_REG_POSITION(texture0_format, 0x8e);
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index bbecad850..495174c25 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -25,6 +25,8 @@ struct State {
Shader::ShaderSetup vs;
Shader::ShaderSetup gs;
+ std::array<Math::Vec4<float24>, 16> vs_default_attributes;
+
struct {
union LutEntry {
// Used for raw access
@@ -56,7 +58,7 @@ struct State {
// Used to buffer partial vertices for immediate-mode rendering.
Shader::InputVertex input_vertex;
// Index of the next attribute to be loaded into `input_vertex`.
- int current_attribute = 0;
+ u32 current_attribute = 0;
} immediate;
// This is constructed with a dummy triangle topology
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index df67b9081..65168f05a 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -442,8 +442,33 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
DEBUG_ASSERT(0 != texture.config.address);
- int s = (int)(uv[i].u() * float24::FromFloat32(static_cast<float>(texture.config.width))).ToFloat32();
- int t = (int)(uv[i].v() * float24::FromFloat32(static_cast<float>(texture.config.height))).ToFloat32();
+ float24 u = uv[i].u();
+ float24 v = uv[i].v();
+
+ // Only unit 0 respects the texturing type (according to 3DBrew)
+ // TODO: Refactor so cubemaps and shadowmaps can be handled
+ if (i == 0) {
+ switch(texture.config.type) {
+ case Regs::TextureConfig::Texture2D:
+ break;
+ case Regs::TextureConfig::Projection2D: {
+ auto tc0_w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w);
+ u /= tc0_w;
+ v /= tc0_w;
+ break;
+ }
+ default:
+ // TODO: Change to LOG_ERROR when more types are handled.
+ LOG_DEBUG(HW_GPU, "Unhandled texture type %x", (int)texture.config.type);
+ UNIMPLEMENTED();
+ break;
+ }
+ }
+
+ int s = (int)(u * float24::FromFloat32(static_cast<float>(texture.config.width))).ToFloat32();
+ int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height))).ToFloat32();
+
+
static auto GetWrappedTexCoord = [](Regs::TextureConfig::WrapMode mode, int val, unsigned size) {
switch (mode) {
case Regs::TextureConfig::ClampToEdge:
@@ -862,10 +887,30 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
}
}
+ // interpolated_z = z / w
+ float interpolated_z_over_w = (v0.screenpos[2].ToFloat32() * w0 +
+ v1.screenpos[2].ToFloat32() * w1 +
+ v2.screenpos[2].ToFloat32() * w2) / wsum;
+
+ // Not fully accurate. About 3 bits in precision are missing.
+ // Z-Buffer (z / w * scale + offset)
+ float depth_scale = float24::FromRaw(regs.viewport_depth_range).ToFloat32();
+ float depth_offset = float24::FromRaw(regs.viewport_depth_near_plane).ToFloat32();
+ float depth = interpolated_z_over_w * depth_scale + depth_offset;
+
+ // Potentially switch to W-Buffer
+ if (regs.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) {
+
+ // W-Buffer (z * scale + w * offset = (z / w * scale + offset) * w)
+ depth *= interpolated_w_inverse.ToFloat32() * wsum;
+ }
+
+ // Clamp the result
+ depth = MathUtil::Clamp(depth, 0.0f, 1.0f);
+
+ // Convert float to integer
unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format);
- u32 z = (u32)((v0.screenpos[2].ToFloat32() * w0 +
- v1.screenpos[2].ToFloat32() * w1 +
- v2.screenpos[2].ToFloat32() * w2) * ((1 << num_bits) - 1) / wsum);
+ u32 z = (u32)(depth * ((1 << num_bits) - 1));
if (output_merger.depth_test_enable) {
u32 ref_z = GetDepth(x >> 4, y >> 4);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 519d81aeb..931c34a37 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -76,6 +76,9 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1);
glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2);
+ glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD0_W, 1, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0_w));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD0_W);
+
glVertexAttribPointer(GLShader::ATTRIBUTE_NORMQUAT, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, normquat));
glEnableVertexAttribArray(GLShader::ATTRIBUTE_NORMQUAT);
@@ -93,7 +96,7 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
state.Apply();
for (size_t i = 0; i < lighting_luts.size(); ++i) {
- glActiveTexture(GL_TEXTURE3 + i);
+ glActiveTexture(static_cast<GLenum>(GL_TEXTURE3 + i));
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -101,7 +104,6 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
// Sync fixed function OpenGL state
SyncCullMode();
- SyncDepthModifiers();
SyncBlendEnabled();
SyncBlendFuncs();
SyncBlendColor();
@@ -256,8 +258,15 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
// Depth modifiers
case PICA_REG_INDEX(viewport_depth_range):
- case PICA_REG_INDEX(viewport_depth_far_plane):
- SyncDepthModifiers();
+ SyncDepthScale();
+ break;
+ case PICA_REG_INDEX(viewport_depth_near_plane):
+ SyncDepthOffset();
+ break;
+
+ // Depth buffering
+ case PICA_REG_INDEX(depthmap_enable):
+ shader_dirty = true;
break;
// Blending
@@ -314,6 +323,11 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
SyncLogicOp();
break;
+ // Texture 0 type
+ case PICA_REG_INDEX(texture0.type):
+ shader_dirty = true;
+ break;
+
// TEV stages
case PICA_REG_INDEX(tev_stage0.color_source1):
case PICA_REG_INDEX(tev_stage0.color_modifier1):
@@ -366,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);
@@ -504,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();
@@ -867,6 +956,8 @@ void RasterizerOpenGL::SetShader() {
glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
// Update uniforms
+ SyncDepthScale();
+ SyncDepthOffset();
SyncAlphaTest();
SyncCombinerColor();
auto& tev_stages = Pica::g_state.regs.GetTevStages();
@@ -880,6 +971,8 @@ void RasterizerOpenGL::SetShader() {
SyncLightDiffuse(light_index);
SyncLightAmbient(light_index);
SyncLightPosition(light_index);
+ SyncLightDistanceAttenuationBias(light_index);
+ SyncLightDistanceAttenuationScale(light_index);
}
}
}
@@ -909,13 +1002,20 @@ void RasterizerOpenGL::SyncCullMode() {
}
}
-void RasterizerOpenGL::SyncDepthModifiers() {
- float depth_scale = -Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32();
- float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_far_plane).ToFloat32() / 2.0f;
+void RasterizerOpenGL::SyncDepthScale() {
+ float depth_scale = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32();
+ if (depth_scale != uniform_block_data.data.depth_scale) {
+ uniform_block_data.data.depth_scale = depth_scale;
+ uniform_block_data.dirty = true;
+ }
+}
- // TODO: Implement scale modifier
- uniform_block_data.data.depth_offset = depth_offset;
- uniform_block_data.dirty = true;
+void RasterizerOpenGL::SyncDepthOffset() {
+ float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_near_plane).ToFloat32();
+ if (depth_offset != uniform_block_data.data.depth_offset) {
+ uniform_block_data.data.depth_offset = depth_offset;
+ uniform_block_data.dirty = true;
+ }
}
void RasterizerOpenGL::SyncBlendEnabled() {
@@ -924,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);
@@ -1080,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 82fa61742..bb7f20161 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -39,140 +39,181 @@ struct ScreenInfo;
* directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
* Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
* two separate shaders sharing the same key.
+ *
+ * We use a union because "implicitly-defined copy/move constructor for a union X copies the object representation of X."
+ * and "implicitly-defined copy assignment operator for a union X copies the object representation (3.9) of X."
+ * = Bytewise copy instead of memberwise copy.
+ * This is important because the padding bytes are included in the hash and comparison between objects.
*/
-struct PicaShaderConfig {
+union PicaShaderConfig {
+
/// Construct a PicaShaderConfig with the current Pica register configuration.
static PicaShaderConfig CurrentConfig() {
PicaShaderConfig res;
+
+ auto& state = res.state;
+ std::memset(&state, 0, sizeof(PicaShaderConfig::State));
+
const auto& regs = Pica::g_state.regs;
- res.alpha_test_func = regs.output_merger.alpha_test.enable ?
+ state.depthmap_enable = regs.depthmap_enable;
+
+ state.alpha_test_func = regs.output_merger.alpha_test.enable ?
regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always;
+ state.texture0_type = regs.texture0.type;
+
// Copy relevant tev stages fields.
// We don't sync const_color here because of the high variance, it is a
// shader uniform instead.
const auto& tev_stages = regs.GetTevStages();
- DEBUG_ASSERT(res.tev_stages.size() == tev_stages.size());
+ DEBUG_ASSERT(state.tev_stages.size() == tev_stages.size());
for (size_t i = 0; i < tev_stages.size(); i++) {
const auto& tev_stage = tev_stages[i];
- res.tev_stages[i].sources_raw = tev_stage.sources_raw;
- res.tev_stages[i].modifiers_raw = tev_stage.modifiers_raw;
- res.tev_stages[i].ops_raw = tev_stage.ops_raw;
- res.tev_stages[i].scales_raw = tev_stage.scales_raw;
+ state.tev_stages[i].sources_raw = tev_stage.sources_raw;
+ state.tev_stages[i].modifiers_raw = tev_stage.modifiers_raw;
+ state.tev_stages[i].ops_raw = tev_stage.ops_raw;
+ state.tev_stages[i].scales_raw = tev_stage.scales_raw;
}
- res.combiner_buffer_input =
+ state.combiner_buffer_input =
regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
regs.tev_combiner_buffer_input.update_mask_a.Value() << 4;
// Fragment lighting
- res.lighting.enable = !regs.lighting.disable;
- res.lighting.src_num = regs.lighting.num_lights + 1;
+ state.lighting.enable = !regs.lighting.disable;
+ state.lighting.src_num = regs.lighting.num_lights + 1;
- for (unsigned light_index = 0; light_index < res.lighting.src_num; ++light_index) {
+ for (unsigned light_index = 0; light_index < state.lighting.src_num; ++light_index) {
unsigned num = regs.lighting.light_enable.GetNum(light_index);
const auto& light = regs.lighting.light[num];
- res.lighting.light[light_index].num = num;
- res.lighting.light[light_index].directional = light.directional != 0;
- res.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0;
- res.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num);
- res.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32();
- res.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32();
+ state.lighting.light[light_index].num = num;
+ 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);
}
- res.lighting.lut_d0.enable = regs.lighting.disable_lut_d0 == 0;
- res.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0;
- res.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value();
- res.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
-
- res.lighting.lut_d1.enable = regs.lighting.disable_lut_d1 == 0;
- res.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0;
- res.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();
- res.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
-
- res.lighting.lut_fr.enable = regs.lighting.disable_lut_fr == 0;
- res.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;
- res.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value();
- res.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
-
- res.lighting.lut_rr.enable = regs.lighting.disable_lut_rr == 0;
- res.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0;
- res.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value();
- res.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
-
- res.lighting.lut_rg.enable = regs.lighting.disable_lut_rg == 0;
- res.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0;
- res.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value();
- res.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
-
- res.lighting.lut_rb.enable = regs.lighting.disable_lut_rb == 0;
- res.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0;
- res.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value();
- res.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
-
- res.lighting.config = regs.lighting.config;
- res.lighting.fresnel_selector = regs.lighting.fresnel_selector;
- res.lighting.bump_mode = regs.lighting.bump_mode;
- res.lighting.bump_selector = regs.lighting.bump_selector;
- res.lighting.bump_renorm = regs.lighting.disable_bump_renorm == 0;
- res.lighting.clamp_highlights = regs.lighting.clamp_highlights != 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.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.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.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.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.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.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;
}
bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
- return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index));
+ return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index));
}
bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
- return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index));
+ return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index));
}
bool operator ==(const PicaShaderConfig& o) const {
- return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0;
+ return std::memcmp(&state, &o.state, sizeof(PicaShaderConfig::State)) == 0;
+ };
+
+ // NOTE: MSVC15 (Update 2) doesn't think `delete`'d constructors and operators are TC.
+ // This makes BitField not TC when used in a union or struct so we have to resort
+ // to this ugly hack.
+ // Once that bug is fixed we can use Pica::Regs::TevStageConfig here.
+ // Doesn't include const_color because we don't sync it, see comment in CurrentConfig()
+ struct TevStageConfigRaw {
+ u32 sources_raw;
+ u32 modifiers_raw;
+ u32 ops_raw;
+ u32 scales_raw;
+ explicit operator Pica::Regs::TevStageConfig() const noexcept {
+ Pica::Regs::TevStageConfig stage;
+ stage.sources_raw = sources_raw;
+ stage.modifiers_raw = modifiers_raw;
+ stage.ops_raw = ops_raw;
+ stage.const_color = 0;
+ stage.scales_raw = scales_raw;
+ return stage;
+ }
};
- Pica::Regs::CompareFunc alpha_test_func = Pica::Regs::CompareFunc::Never;
- std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
- u8 combiner_buffer_input = 0;
+ struct State {
- struct {
- struct {
- unsigned num = 0;
- bool directional = false;
- bool two_sided_diffuse = false;
- bool dist_atten_enable = false;
- GLfloat dist_atten_scale = 0.0f;
- GLfloat dist_atten_bias = 0.0f;
- } light[8];
-
- bool enable = false;
- unsigned src_num = 0;
- Pica::Regs::LightingBumpMode bump_mode = Pica::Regs::LightingBumpMode::None;
- unsigned bump_selector = 0;
- bool bump_renorm = false;
- bool clamp_highlights = false;
-
- Pica::Regs::LightingConfig config = Pica::Regs::LightingConfig::Config0;
- Pica::Regs::LightingFresnelSelector fresnel_selector = Pica::Regs::LightingFresnelSelector::None;
+ Pica::Regs::CompareFunc alpha_test_func;
+ Pica::Regs::TextureConfig::TextureType texture0_type;
+ std::array<TevStageConfigRaw, 6> tev_stages;
+ u8 combiner_buffer_input;
+
+ Pica::Regs::DepthBuffering depthmap_enable;
struct {
- bool enable = false;
- bool abs_input = false;
- Pica::Regs::LightingLutInput type = Pica::Regs::LightingLutInput::NH;
- float scale = 1.0f;
- } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb;
- } lighting;
+ struct {
+ unsigned num;
+ bool directional;
+ bool two_sided_diffuse;
+ bool dist_atten_enable;
+ } light[8];
+
+ bool enable;
+ unsigned src_num;
+ Pica::Regs::LightingBumpMode bump_mode;
+ unsigned bump_selector;
+ bool bump_renorm;
+ bool clamp_highlights;
+
+ Pica::Regs::LightingConfig config;
+ Pica::Regs::LightingFresnelSelector fresnel_selector;
+
+ struct {
+ bool enable;
+ bool abs_input;
+ Pica::Regs::LightingLutInput type;
+ float scale;
+ } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb;
+ } lighting;
+
+ } state;
};
+#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
+static_assert(std::is_trivially_copyable<PicaShaderConfig::State>::value, "PicaShaderConfig::State must be trivially copyable");
+#endif
namespace std {
template <>
struct hash<PicaShaderConfig> {
size_t operator()(const PicaShaderConfig& k) const {
- return Common::ComputeHash64(&k, sizeof(PicaShaderConfig));
+ return Common::ComputeHash64(&k.state, sizeof(PicaShaderConfig::State));
}
};
@@ -239,6 +280,7 @@ private:
tex_coord1[1] = v.tc1.y.ToFloat32();
tex_coord2[0] = v.tc2.x.ToFloat32();
tex_coord2[1] = v.tc2.y.ToFloat32();
+ tex_coord0_w = v.tc0_w.ToFloat32();
normquat[0] = v.quat.x.ToFloat32();
normquat[1] = v.quat.y.ToFloat32();
normquat[2] = v.quat.z.ToFloat32();
@@ -259,6 +301,7 @@ private:
GLfloat tex_coord0[2];
GLfloat tex_coord1[2];
GLfloat tex_coord2[2];
+ GLfloat tex_coord0_w;
GLfloat normquat[4];
GLfloat view[3];
};
@@ -269,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
@@ -277,12 +322,13 @@ private:
GLvec4 const_color[6];
GLvec4 tev_combiner_buffer_color;
GLint alphatest_ref;
+ GLfloat depth_scale;
GLfloat depth_offset;
alignas(16) GLvec3 lighting_global_ambient;
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
@@ -291,8 +337,11 @@ private:
/// Syncs the cull mode to match the PICA register
void SyncCullMode();
- /// Syncs the depth scale and offset to match the PICA registers
- void SyncDepthModifiers();
+ /// Syncs the depth scale to match the PICA register
+ void SyncDepthScale();
+
+ /// Syncs the depth offset to match the PICA register
+ void SyncDepthOffset();
/// Syncs the blend enabled status to match the PICA register
void SyncBlendEnabled();
@@ -351,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;
@@ -365,7 +420,7 @@ private:
UniformData data;
bool lut_dirty[6];
bool dirty;
- } uniform_block_data;
+ } uniform_block_data = {};
std::array<SamplerInfo, 3> texture_samplers;
OGLVertexArray vertex_array;
@@ -374,5 +429,5 @@ private:
OGLFramebuffer framebuffer;
std::array<OGLTexture, 6> lighting_luts;
- std::array<std::array<GLvec4, 256>, 6> lighting_lut_data;
+ std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{};
};
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 9011caa39..8332e722d 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -32,8 +32,9 @@ static bool IsPassThroughTevStage(const TevStageConfig& stage) {
}
/// Writes the specified TEV stage source component(s)
-static void AppendSource(std::string& out, TevStageConfig::Source source,
+static void AppendSource(std::string& out, const PicaShaderConfig& config, TevStageConfig::Source source,
const std::string& index_name) {
+ const auto& state = config.state;
using Source = TevStageConfig::Source;
switch (source) {
case Source::PrimaryColor:
@@ -46,7 +47,20 @@ static void AppendSource(std::string& out, TevStageConfig::Source source,
out += "secondary_fragment_color";
break;
case Source::Texture0:
- out += "texture(tex[0], texcoord[0])";
+ // Only unit 0 respects the texturing type (according to 3DBrew)
+ switch(state.texture0_type) {
+ case Pica::Regs::TextureConfig::Texture2D:
+ out += "texture(tex[0], texcoord[0])";
+ break;
+ case Pica::Regs::TextureConfig::Projection2D:
+ out += "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))";
+ break;
+ default:
+ out += "texture(tex[0], texcoord[0])";
+ LOG_CRITICAL(HW_GPU, "Unhandled texture type %x", static_cast<int>(state.texture0_type));
+ UNIMPLEMENTED();
+ break;
+ }
break;
case Source::Texture1:
out += "texture(tex[1], texcoord[1])";
@@ -71,53 +85,53 @@ static void AppendSource(std::string& out, TevStageConfig::Source source,
}
/// Writes the color components to use for the specified TEV stage color modifier
-static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier modifier,
+static void AppendColorModifier(std::string& out, const PicaShaderConfig& config, TevStageConfig::ColorModifier modifier,
TevStageConfig::Source source, const std::string& index_name) {
using ColorModifier = TevStageConfig::ColorModifier;
switch (modifier) {
case ColorModifier::SourceColor:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".rgb";
break;
case ColorModifier::OneMinusSourceColor:
out += "vec3(1.0) - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".rgb";
break;
case ColorModifier::SourceAlpha:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".aaa";
break;
case ColorModifier::OneMinusSourceAlpha:
out += "vec3(1.0) - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".aaa";
break;
case ColorModifier::SourceRed:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".rrr";
break;
case ColorModifier::OneMinusSourceRed:
out += "vec3(1.0) - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".rrr";
break;
case ColorModifier::SourceGreen:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".ggg";
break;
case ColorModifier::OneMinusSourceGreen:
out += "vec3(1.0) - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".ggg";
break;
case ColorModifier::SourceBlue:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".bbb";
break;
case ColorModifier::OneMinusSourceBlue:
out += "vec3(1.0) - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".bbb";
break;
default:
@@ -128,44 +142,44 @@ static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier
}
/// Writes the alpha component to use for the specified TEV stage alpha modifier
-static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier modifier,
+static void AppendAlphaModifier(std::string& out, const PicaShaderConfig& config, TevStageConfig::AlphaModifier modifier,
TevStageConfig::Source source, const std::string& index_name) {
using AlphaModifier = TevStageConfig::AlphaModifier;
switch (modifier) {
case AlphaModifier::SourceAlpha:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".a";
break;
case AlphaModifier::OneMinusSourceAlpha:
out += "1.0 - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".a";
break;
case AlphaModifier::SourceRed:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".r";
break;
case AlphaModifier::OneMinusSourceRed:
out += "1.0 - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".r";
break;
case AlphaModifier::SourceGreen:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".g";
break;
case AlphaModifier::OneMinusSourceGreen:
out += "1.0 - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".g";
break;
case AlphaModifier::SourceBlue:
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".b";
break;
case AlphaModifier::OneMinusSourceBlue:
out += "1.0 - ";
- AppendSource(out, source, index_name);
+ AppendSource(out, config, source, index_name);
out += ".b";
break;
default:
@@ -287,16 +301,16 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) {
/// Writes the code to emulate the specified TEV stage
static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
- auto& stage = config.tev_stages[index];
+ const auto stage = static_cast<const Pica::Regs::TevStageConfig>(config.state.tev_stages[index]);
if (!IsPassThroughTevStage(stage)) {
std::string index_name = std::to_string(index);
out += "vec3 color_results_" + index_name + "[3] = vec3[3](";
- AppendColorModifier(out, stage.color_modifier1, stage.color_source1, index_name);
+ AppendColorModifier(out, config, stage.color_modifier1, stage.color_source1, index_name);
out += ", ";
- AppendColorModifier(out, stage.color_modifier2, stage.color_source2, index_name);
+ AppendColorModifier(out, config, stage.color_modifier2, stage.color_source2, index_name);
out += ", ";
- AppendColorModifier(out, stage.color_modifier3, stage.color_source3, index_name);
+ AppendColorModifier(out, config, stage.color_modifier3, stage.color_source3, index_name);
out += ");\n";
out += "vec3 color_output_" + index_name + " = ";
@@ -304,11 +318,11 @@ static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsi
out += ";\n";
out += "float alpha_results_" + index_name + "[3] = float[3](";
- AppendAlphaModifier(out, stage.alpha_modifier1, stage.alpha_source1, index_name);
+ AppendAlphaModifier(out, config, stage.alpha_modifier1, stage.alpha_source1, index_name);
out += ", ";
- AppendAlphaModifier(out, stage.alpha_modifier2, stage.alpha_source2, index_name);
+ AppendAlphaModifier(out, config, stage.alpha_modifier2, stage.alpha_source2, index_name);
out += ", ";
- AppendAlphaModifier(out, stage.alpha_modifier3, stage.alpha_source3, index_name);
+ AppendAlphaModifier(out, config, stage.alpha_modifier3, stage.alpha_source3, index_name);
out += ");\n";
out += "float alpha_output_" + index_name + " = ";
@@ -331,6 +345,8 @@ static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsi
/// Writes the code to emulate fragment lighting
static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
+ const auto& lighting = config.state.lighting;
+
// Define lighting globals
out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
"vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
@@ -338,17 +354,17 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
"vec3 refl_value = vec3(0.0);\n";
// Compute fragment normals
- if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) {
+ if (lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) {
// Bump mapping is enabled using a normal map, read perturbation vector from the selected texture
- std::string bump_selector = std::to_string(config.lighting.bump_selector);
+ std::string bump_selector = std::to_string(lighting.bump_selector);
out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], texcoord[" + bump_selector + "]).rgb - 1.0;\n";
// Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher precision result
- if (config.lighting.bump_renorm) {
+ if (lighting.bump_renorm) {
std::string val = "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))";
out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n";
}
- } else if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) {
+ } else if (lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) {
// Bump mapping is enabled using a tangent map
LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)");
UNIMPLEMENTED();
@@ -361,7 +377,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n";
// Gets the index into the specified lookup table for specular lighting
- auto GetLutIndex = [config](unsigned light_num, Regs::LightingLutInput input, bool abs) {
+ auto GetLutIndex = [&lighting](unsigned light_num, Regs::LightingLutInput input, bool abs) {
const std::string half_angle = "normalize(normalize(view) + light_vector)";
std::string index;
switch (input) {
@@ -389,7 +405,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
if (abs) {
// LUT index is in the range of (0.0, 1.0)
- index = config.lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)";
+ index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)";
return "(FLOAT_255 * clamp(" + index + ", 0.0, 1.0))";
} else {
// LUT index is in the range of (-1.0, 1.0)
@@ -407,8 +423,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
};
// Write the code to emulate each enabled light
- for (unsigned light_index = 0; light_index < config.lighting.src_num; ++light_index) {
- const auto& light_config = config.lighting.light[light_index];
+ for (unsigned light_index = 0; light_index < lighting.src_num; ++light_index) {
+ const auto& light_config = lighting.light[light_index];
std::string light_src = "light_src[" + std::to_string(light_config.num) + "]";
// Compute light vector (directional or positional)
@@ -423,48 +439,46 @@ 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);
}
// If enabled, clamp specular component if lighting result is negative
- std::string clamp_highlights = config.lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0";
+ std::string clamp_highlights = lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0";
// Specular 0 component
std::string d0_lut_value = "1.0";
- if (config.lighting.lut_d0.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution0)) {
+ if (lighting.lut_d0.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::Distribution0)) {
// Lookup specular "distribution 0" LUT value
- std::string index = GetLutIndex(light_config.num, config.lighting.lut_d0.type, config.lighting.lut_d0.abs_input);
- d0_lut_value = "(" + std::to_string(config.lighting.lut_d0.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution0, index) + ")";
+ std::string index = GetLutIndex(light_config.num, lighting.lut_d0.type, lighting.lut_d0.abs_input);
+ d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution0, index) + ")";
}
std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)";
// If enabled, lookup ReflectRed value, otherwise, 1.0 is used
- if (config.lighting.lut_rr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectRed)) {
- std::string index = GetLutIndex(light_config.num, config.lighting.lut_rr.type, config.lighting.lut_rr.abs_input);
- std::string value = "(" + std::to_string(config.lighting.lut_rr.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")";
+ if (lighting.lut_rr.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::ReflectRed)) {
+ std::string index = GetLutIndex(light_config.num, lighting.lut_rr.type, lighting.lut_rr.abs_input);
+ std::string value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")";
out += "refl_value.r = " + value + ";\n";
} else {
out += "refl_value.r = 1.0;\n";
}
// If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used
- if (config.lighting.lut_rg.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectGreen)) {
- std::string index = GetLutIndex(light_config.num, config.lighting.lut_rg.type, config.lighting.lut_rg.abs_input);
- std::string value = "(" + std::to_string(config.lighting.lut_rg.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")";
+ if (lighting.lut_rg.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::ReflectGreen)) {
+ std::string index = GetLutIndex(light_config.num, lighting.lut_rg.type, lighting.lut_rg.abs_input);
+ std::string value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")";
out += "refl_value.g = " + value + ";\n";
} else {
out += "refl_value.g = refl_value.r;\n";
}
// If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used
- if (config.lighting.lut_rb.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectBlue)) {
- std::string index = GetLutIndex(light_config.num, config.lighting.lut_rb.type, config.lighting.lut_rb.abs_input);
- std::string value = "(" + std::to_string(config.lighting.lut_rb.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")";
+ if (lighting.lut_rb.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::ReflectBlue)) {
+ std::string index = GetLutIndex(light_config.num, lighting.lut_rb.type, lighting.lut_rb.abs_input);
+ std::string value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")";
out += "refl_value.b = " + value + ";\n";
} else {
out += "refl_value.b = refl_value.r;\n";
@@ -472,27 +486,27 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
// Specular 1 component
std::string d1_lut_value = "1.0";
- if (config.lighting.lut_d1.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution1)) {
+ if (lighting.lut_d1.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::Distribution1)) {
// Lookup specular "distribution 1" LUT value
- std::string index = GetLutIndex(light_config.num, config.lighting.lut_d1.type, config.lighting.lut_d1.abs_input);
- d1_lut_value = "(" + std::to_string(config.lighting.lut_d1.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution1, index) + ")";
+ std::string index = GetLutIndex(light_config.num, lighting.lut_d1.type, lighting.lut_d1.abs_input);
+ d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution1, index) + ")";
}
std::string specular_1 = "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)";
// Fresnel
- if (config.lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Fresnel)) {
+ if (lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::Fresnel)) {
// Lookup fresnel LUT value
- std::string index = GetLutIndex(light_config.num, config.lighting.lut_fr.type, config.lighting.lut_fr.abs_input);
- std::string value = "(" + std::to_string(config.lighting.lut_fr.scale) + " * " + GetLutValue(Regs::LightingSampler::Fresnel, index) + ")";
+ std::string index = GetLutIndex(light_config.num, lighting.lut_fr.type, lighting.lut_fr.abs_input);
+ std::string value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + GetLutValue(Regs::LightingSampler::Fresnel, index) + ")";
// Enabled for difffuse lighting alpha component
- if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha ||
- config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
+ if (lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha ||
+ lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
out += "diffuse_sum.a *= " + value + ";\n";
// Enabled for the specular lighting alpha component
- if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha ||
- config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
+ if (lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha ||
+ lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
out += "specular_sum.a *= " + value + ";\n";
}
@@ -510,6 +524,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
}
std::string GenerateFragmentShader(const PicaShaderConfig& config) {
+ const auto& state = config.state;
+
std::string out = R"(
#version 330 core
#define NUM_TEV_STAGES 6
@@ -519,6 +535,7 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) {
in vec4 primary_color;
in vec2 texcoord[3];
+in float texcoord0_w;
in vec4 normquat;
in vec3 view;
@@ -530,12 +547,15 @@ struct LightSrc {
vec3 diffuse;
vec3 ambient;
vec3 position;
+ float dist_atten_bias;
+ float dist_atten_scale;
};
layout (std140) uniform shader_data {
vec4 const_color[NUM_TEV_STAGES];
vec4 tev_combiner_buffer_color;
int alphatest_ref;
+ float depth_scale;
float depth_offset;
vec3 lighting_global_ambient;
LightSrc light_src[NUM_LIGHTS];
@@ -555,29 +575,37 @@ vec4 secondary_fragment_color = vec4(0.0);
)";
// Do not do any sort of processing if it's obvious we're not going to pass the alpha test
- if (config.alpha_test_func == Regs::CompareFunc::Never) {
+ if (state.alpha_test_func == Regs::CompareFunc::Never) {
out += "discard; }";
return out;
}
- if (config.lighting.enable)
+ if (state.lighting.enable)
WriteLighting(out, config);
out += "vec4 combiner_buffer = vec4(0.0);\n";
out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n";
out += "vec4 last_tex_env_out = vec4(0.0);\n";
- for (size_t index = 0; index < config.tev_stages.size(); ++index)
+ for (size_t index = 0; index < state.tev_stages.size(); ++index)
WriteTevStage(out, config, (unsigned)index);
- if (config.alpha_test_func != Regs::CompareFunc::Always) {
+ if (state.alpha_test_func != Regs::CompareFunc::Always) {
out += "if (";
- AppendAlphaTestCondition(out, config.alpha_test_func);
+ AppendAlphaTestCondition(out, state.alpha_test_func);
out += ") discard;\n";
}
out += "color = last_tex_env_out;\n";
- out += "gl_FragDepth = gl_FragCoord.z + depth_offset;\n}";
+
+ out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n";
+ out += "float depth = z_over_w * depth_scale + depth_offset;\n";
+ if (state.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) {
+ out += "depth /= gl_FragCoord.w;\n";
+ }
+ out += "gl_FragDepth = depth;\n";
+
+ out += "}";
return out;
}
@@ -585,17 +613,19 @@ vec4 secondary_fragment_color = vec4(0.0);
std::string GenerateVertexShader() {
std::string out = "#version 330 core\n";
- out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n";
- out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n";
- out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n";
- out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n";
- out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n";
- out += "layout(location = " + std::to_string((int)ATTRIBUTE_NORMQUAT) + ") in vec4 vert_normquat;\n";
- out += "layout(location = " + std::to_string((int)ATTRIBUTE_VIEW) + ") in vec3 vert_view;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0_W) + ") in float vert_texcoord0_w;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_NORMQUAT) + ") in vec4 vert_normquat;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_VIEW) + ") in vec3 vert_view;\n";
out += R"(
out vec4 primary_color;
out vec2 texcoord[3];
+out float texcoord0_w;
out vec4 normquat;
out vec3 view;
@@ -604,6 +634,7 @@ void main() {
texcoord[0] = vert_texcoord0;
texcoord[1] = vert_texcoord1;
texcoord[2] = vert_texcoord2;
+ texcoord0_w = vert_texcoord0_w;
normquat = vert_normquat;
view = vert_view;
gl_Position = vec4(vert_position.x, vert_position.y, -vert_position.z, vert_position.w);
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 3eb07d57a..bef3249cf 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -6,7 +6,7 @@
#include <string>
-struct PicaShaderConfig;
+union PicaShaderConfig;
namespace GLShader {
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 097242f6f..f59912f79 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -14,6 +14,7 @@ enum Attributes {
ATTRIBUTE_TEXCOORD0,
ATTRIBUTE_TEXCOORD1,
ATTRIBUTE_TEXCOORD2,
+ ATTRIBUTE_TEXCOORD0_W,
ATTRIBUTE_NORMQUAT,
ATTRIBUTE_VIEW,
};
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/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 0e9a0be8b..8410e0a64 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -192,7 +192,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
// only allows rows to have a memory alignement of 4.
ASSERT(pixel_stride % 4 == 0);
- if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, pixel_stride, screen_info)) {
+ if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, static_cast<u32>(pixel_stride), screen_info)) {
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture.resource.handle;
screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
@@ -450,7 +450,7 @@ static const char* GetType(GLenum type) {
#undef RET
}
-static void DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
+static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
const GLchar* message, const void* user_param) {
Log::Level level;
switch (severity) {
@@ -473,12 +473,6 @@ static void DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity,
bool RendererOpenGL::Init() {
render_window->MakeCurrent();
- // TODO: Make frontends initialize this, so they can use gladLoadGLLoader with their own loaders
- if (!gladLoadGL()) {
- LOG_CRITICAL(Render_OpenGL, "Failed to initialize GL functions! Exiting...");
- exit(-1);
- }
-
if (GLAD_GL_KHR_debug) {
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(DebugHandler, nullptr);
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index 65dcc9156..f565e2c91 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -30,65 +30,7 @@ namespace Pica {
namespace Shader {
-#ifdef ARCHITECTURE_x86_64
-static std::unordered_map<u64, std::unique_ptr<JitShader>> shader_map;
-static const JitShader* jit_shader;
-#endif // ARCHITECTURE_x86_64
-
-void Setup() {
-#ifdef ARCHITECTURE_x86_64
- if (VideoCore::g_shader_jit_enabled) {
- u64 cache_key = (Common::ComputeHash64(&g_state.vs.program_code, sizeof(g_state.vs.program_code)) ^
- Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data)));
-
- auto iter = shader_map.find(cache_key);
- if (iter != shader_map.end()) {
- jit_shader = iter->second.get();
- } else {
- auto shader = std::make_unique<JitShader>();
- shader->Compile();
- jit_shader = shader.get();
- shader_map[cache_key] = std::move(shader);
- }
- }
-#endif // ARCHITECTURE_x86_64
-}
-
-void Shutdown() {
-#ifdef ARCHITECTURE_x86_64
- shader_map.clear();
-#endif // ARCHITECTURE_x86_64
-}
-
-MICROPROFILE_DEFINE(GPU_VertexShader, "GPU", "Vertex Shader", MP_RGB(50, 50, 240));
-
-OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attributes) {
- auto& config = g_state.regs.vs;
-
- MICROPROFILE_SCOPE(GPU_VertexShader);
-
- state.program_counter = config.main_offset;
- state.debug.max_offset = 0;
- state.debug.max_opdesc_id = 0;
-
- // Setup input register table
- const auto& attribute_register_map = config.input_register_map;
-
- for (unsigned i = 0; i < num_attributes; i++)
- state.registers.input[attribute_register_map.GetRegisterForAttribute(i)] = input.attr[i];
-
- state.conditional_code[0] = false;
- state.conditional_code[1] = false;
-
-#ifdef ARCHITECTURE_x86_64
- if (VideoCore::g_shader_jit_enabled)
- jit_shader->Run(&state.registers, g_state.regs.vs.main_offset);
- else
- RunInterpreter(state);
-#else
- RunInterpreter(state);
-#endif // ARCHITECTURE_x86_64
-
+OutputVertex OutputRegisters::ToVertex(const Regs::ShaderConfig& config) {
// Setup output data
OutputVertex ret;
// TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
@@ -99,10 +41,10 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
if (index >= g_state.regs.vs_output_total)
break;
- if ((g_state.regs.vs.output_mask & (1 << i)) == 0)
+ if ((config.output_mask & (1 << i)) == 0)
continue;
- const auto& output_register_map = g_state.regs.vs_output_attributes[index]; // TODO: Don't hardcode VS here
+ const auto& output_register_map = g_state.regs.vs_output_attributes[index];
u32 semantics[4] = {
output_register_map.map_x, output_register_map.map_y,
@@ -112,7 +54,7 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
for (unsigned comp = 0; comp < 4; ++comp) {
float24* out = ((float24*)&ret) + semantics[comp];
if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
- *out = state.registers.output[i][comp];
+ *out = value[i][comp];
} else {
// Zero output so that attributes which aren't output won't have denormals in them,
// which would slow us down later.
@@ -140,10 +82,70 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
return ret;
}
-DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup) {
+#ifdef ARCHITECTURE_x86_64
+static std::unordered_map<u64, std::unique_ptr<JitShader>> shader_map;
+static const JitShader* jit_shader;
+#endif // ARCHITECTURE_x86_64
+
+void ClearCache() {
+#ifdef ARCHITECTURE_x86_64
+ shader_map.clear();
+#endif // ARCHITECTURE_x86_64
+}
+
+void ShaderSetup::Setup() {
+#ifdef ARCHITECTURE_x86_64
+ if (VideoCore::g_shader_jit_enabled) {
+ u64 cache_key = (Common::ComputeHash64(&g_state.vs.program_code, sizeof(g_state.vs.program_code)) ^
+ Common::ComputeHash64(&g_state.vs.swizzle_data, sizeof(g_state.vs.swizzle_data)));
+
+ auto iter = shader_map.find(cache_key);
+ if (iter != shader_map.end()) {
+ jit_shader = iter->second.get();
+ } else {
+ auto shader = std::make_unique<JitShader>();
+ shader->Compile();
+ jit_shader = shader.get();
+ shader_map[cache_key] = std::move(shader);
+ }
+ }
+#endif // ARCHITECTURE_x86_64
+}
+
+MICROPROFILE_DEFINE(GPU_Shader, "GPU", "Shader", MP_RGB(50, 50, 240));
+
+void 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);
+
+ state.debug.max_offset = 0;
+ state.debug.max_opdesc_id = 0;
+
+ // Setup input register table
+ const auto& attribute_register_map = config.input_register_map;
+
+ for (unsigned i = 0; i < num_attributes; i++)
+ state.registers.input[attribute_register_map.GetRegisterForAttribute(i)] = input.attr[i];
+
+ state.conditional_code[0] = false;
+ state.conditional_code[1] = false;
+
+#ifdef ARCHITECTURE_x86_64
+ if (VideoCore::g_shader_jit_enabled)
+ jit_shader->Run(setup, state, config.main_offset);
+ else
+ RunInterpreter(setup, state, config.main_offset);
+#else
+ RunInterpreter(setup, state, config.main_offset);
+#endif // ARCHITECTURE_x86_64
+
+}
+
+DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup) {
UnitState<true> state;
- state.program_counter = config.main_offset;
state.debug.max_offset = 0;
state.debug.max_opdesc_id = 0;
@@ -158,7 +160,7 @@ DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, c
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 56b83bfeb..fee16df62 100644
--- a/src/video_core/shader/shader.h
+++ b/src/video_core/shader/shader.h
@@ -43,7 +43,8 @@ struct OutputVertex {
Math::Vec4<float24> color;
Math::Vec2<float24> tc0;
Math::Vec2<float24> tc1;
- INSERT_PADDING_WORDS(2);
+ float24 tc0_w;
+ INSERT_PADDING_WORDS(1);
Math::Vec3<float24> view;
INSERT_PADDING_WORDS(1);
Math::Vec2<float24> tc2;
@@ -83,22 +84,14 @@ struct OutputVertex {
static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD");
static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size");
-/// Vertex shader memory
-struct ShaderSetup {
- struct {
- // The float uniforms are accessed by the shader JIT using SSE instructions, and are
- // therefore required to be 16-byte aligned.
- alignas(16) Math::Vec4<float24> f[96];
-
- std::array<bool, 16> b;
- std::array<Math::Vec4<u8>, 4> i;
- } uniforms;
+struct OutputRegisters {
+ OutputRegisters() = default;
- Math::Vec4<float24> default_attributes[16];
+ alignas(16) Math::Vec4<float24> value[16];
- std::array<u32, 1024> program_code;
- std::array<u32, 1024> swizzle_data;
+ OutputVertex ToVertex(const Regs::ShaderConfig& config);
};
+static_assert(std::is_pod<OutputRegisters>::value, "Structure is not POD");
// Helper structure used to keep track of data useful for inspection of shader emulation
template<bool full_debugging>
@@ -283,43 +276,27 @@ struct UnitState {
// The registers are accessed by the shader JIT using SSE instructions, and are therefore
// required to be 16-byte aligned.
alignas(16) Math::Vec4<float24> input[16];
- alignas(16) Math::Vec4<float24> output[16];
alignas(16) Math::Vec4<float24> temporary[16];
} registers;
static_assert(std::is_pod<Registers>::value, "Structure is not POD");
- u32 program_counter;
+ OutputRegisters output_registers;
+
bool conditional_code[2];
// Two Address registers and one loop counter
// TODO: How many bits do these actually have?
s32 address_registers[3];
- enum {
- INVALID_ADDRESS = 0xFFFFFFFF
- };
-
- struct CallStackElement {
- u32 final_address; // Address upon which we jump to return_address
- u32 return_address; // Where to jump when leaving scope
- u8 repeat_counter; // How often to repeat until this call stack element is removed
- u8 loop_increment; // Which value to add to the loop counter after an iteration
- // TODO: Should this be a signed value? Does it even matter?
- u32 loop_address; // The address where we'll return to after each loop iteration
- };
-
- // TODO: Is there a maximal size for this?
- boost::container::static_vector<CallStackElement, 16> call_stack;
-
DebugData<Debug> debug;
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();
@@ -330,10 +307,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, output_registers.value) + 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();
@@ -342,33 +319,65 @@ struct UnitState {
}
};
-/**
- * Performs any shader unit setup that only needs to happen once per shader (as opposed to once per
- * vertex, which would happen within the `Run` function).
- */
-void Setup();
+/// Clears the shader cache
+void ClearCache();
-/// Performs any cleanup when the emulator is shutdown
-void Shutdown();
+struct ShaderSetup {
-/**
- * Runs the currently setup shader
- * @param state Shader unit state, must be setup per shader and per shader unit
- * @param input Input vertex into the shader
- * @param num_attributes The number of vertex shader attributes
- * @return The output vertex, after having been processed by the vertex shader
- */
-OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attributes);
+ struct {
+ // The float uniforms are accessed by the shader JIT using SSE instructions, and are
+ // therefore required to be 16-byte aligned.
+ alignas(16) Math::Vec4<float24> f[96];
-/**
- * Produce debug information based on the given shader and input vertex
- * @param input Input vertex into the shader
- * @param num_attributes The number of vertex shader attributes
- * @param config Configuration object for the shader pipeline
- * @param setup Setup object for the shader pipeline
- * @return Debug information for this shader with regards to the given vertex
- */
-DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup);
+ std::array<bool, 16> b;
+ 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;
+
+ /**
+ * Performs any shader unit setup that only needs to happen once per shader (as opposed to once per
+ * vertex, which would happen within the `Run` function).
+ */
+ void Setup();
+
+ /**
+ * Runs the currently setup shader
+ * @param state Shader unit state, must be setup per shader and per shader unit
+ * @param input Input vertex into the shader
+ * @param num_attributes The number of vertex shader attributes
+ */
+ void Run(UnitState<false>& state, const InputVertex& input, int num_attributes);
+
+ /**
+ * Produce debug information based on the given shader and input vertex
+ * @param input Input vertex into the shader
+ * @param num_attributes The number of vertex shader attributes
+ * @param config Configuration object for the shader pipeline
+ * @param setup Setup object for the shader pipeline
+ * @return Debug information for this shader with regards to the given vertex
+ */
+ DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup);
+
+};
} // namespace Shader
diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp
index 7710f7fbc..b1eadc071 100644
--- a/src/video_core/shader/shader_interpreter.cpp
+++ b/src/video_core/shader/shader_interpreter.cpp
@@ -29,8 +29,24 @@ namespace Pica {
namespace Shader {
+constexpr u32 INVALID_ADDRESS = 0xFFFFFFFF;
+
+struct CallStackElement {
+ u32 final_address; // Address upon which we jump to return_address
+ u32 return_address; // Where to jump when leaving scope
+ u8 repeat_counter; // How often to repeat until this call stack element is removed
+ u8 loop_increment; // Which value to add to the loop counter after an iteration
+ // TODO: Should this be a signed value? Does it even matter?
+ u32 loop_address; // The address where we'll return to after each loop iteration
+};
+
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 = offset;
+
const auto& uniforms = g_state.vs.uniforms;
const auto& swizzle_data = g_state.vs.swizzle_data;
const auto& program_code = g_state.vs.program_code;
@@ -41,16 +57,16 @@ void RunInterpreter(UnitState<Debug>& state) {
unsigned iteration = 0;
bool exit_loop = false;
while (!exit_loop) {
- if (!state.call_stack.empty()) {
- auto& top = state.call_stack.back();
- if (state.program_counter == top.final_address) {
+ if (!call_stack.empty()) {
+ auto& top = call_stack.back();
+ if (program_counter == top.final_address) {
state.address_registers[2] += top.loop_increment;
if (top.repeat_counter-- == 0) {
- state.program_counter = top.return_address;
- state.call_stack.pop_back();
+ program_counter = top.return_address;
+ call_stack.pop_back();
} else {
- state.program_counter = top.loop_address;
+ program_counter = top.loop_address;
}
// TODO: Is "trying again" accurate to hardware?
@@ -58,20 +74,20 @@ void RunInterpreter(UnitState<Debug>& state) {
}
}
- const Instruction instr = { program_code[state.program_counter] };
+ const Instruction instr = { program_code[program_counter] };
const SwizzlePattern swizzle = { swizzle_data[instr.common.operand_desc_id] };
- static auto call = [](UnitState<Debug>& state, u32 offset, u32 num_instructions,
+ static auto call = [&program_counter, &call_stack](UnitState<Debug>& state, u32 offset, u32 num_instructions,
u32 return_offset, u8 repeat_count, u8 loop_increment) {
- state.program_counter = offset - 1; // -1 to make sure when incrementing the PC we end up at the correct offset
- ASSERT(state.call_stack.size() < state.call_stack.capacity());
- state.call_stack.push_back({ offset + num_instructions, return_offset, repeat_count, loop_increment, offset });
+ program_counter = offset - 1; // -1 to make sure when incrementing the PC we end up at the correct offset
+ ASSERT(call_stack.size() < call_stack.capacity());
+ call_stack.push_back({ offset + num_instructions, return_offset, repeat_count, loop_increment, offset });
};
- Record<DebugDataRecord::CUR_INSTR>(state.debug, iteration, state.program_counter);
+ Record<DebugDataRecord::CUR_INSTR>(state.debug, iteration, program_counter);
if (iteration > 0)
- Record<DebugDataRecord::NEXT_INSTR>(state.debug, iteration - 1, state.program_counter);
+ Record<DebugDataRecord::NEXT_INSTR>(state.debug, iteration - 1, program_counter);
- state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + state.program_counter);
+ state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + program_counter);
auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* {
switch (source_reg.GetRegisterType()) {
@@ -128,7 +144,7 @@ void RunInterpreter(UnitState<Debug>& state) {
src2[3] = src2[3] * float24::FromFloat32(-1);
}
- float24* dest = (instr.common.dest.Value() < 0x10) ? &state.registers.output[instr.common.dest.Value().GetIndex()][0]
+ float24* dest = (instr.common.dest.Value() < 0x10) ? &state.output_registers.value[instr.common.dest.Value().GetIndex()][0]
: (instr.common.dest.Value() < 0x20) ? &state.registers.temporary[instr.common.dest.Value().GetIndex()][0]
: dummy_vec4_float24;
@@ -467,7 +483,7 @@ void RunInterpreter(UnitState<Debug>& state) {
src3[3] = src3[3] * float24::FromFloat32(-1);
}
- float24* dest = (instr.mad.dest.Value() < 0x10) ? &state.registers.output[instr.mad.dest.Value().GetIndex()][0]
+ float24* dest = (instr.mad.dest.Value() < 0x10) ? &state.output_registers.value[instr.mad.dest.Value().GetIndex()][0]
: (instr.mad.dest.Value() < 0x20) ? &state.registers.temporary[instr.mad.dest.Value().GetIndex()][0]
: dummy_vec4_float24;
@@ -519,7 +535,7 @@ void RunInterpreter(UnitState<Debug>& state) {
case OpCode::Id::JMPC:
Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, state.conditional_code);
if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
- state.program_counter = instr.flow_control.dest_offset - 1;
+ program_counter = instr.flow_control.dest_offset - 1;
}
break;
@@ -527,7 +543,7 @@ void RunInterpreter(UnitState<Debug>& state) {
Record<DebugDataRecord::COND_BOOL_IN>(state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
if (uniforms.b[instr.flow_control.bool_uniform_id] == !(instr.flow_control.num_instructions & 1)) {
- state.program_counter = instr.flow_control.dest_offset - 1;
+ program_counter = instr.flow_control.dest_offset - 1;
}
break;
@@ -535,7 +551,7 @@ void RunInterpreter(UnitState<Debug>& state) {
call(state,
instr.flow_control.dest_offset,
instr.flow_control.num_instructions,
- state.program_counter + 1, 0, 0);
+ program_counter + 1, 0, 0);
break;
case OpCode::Id::CALLU:
@@ -544,7 +560,7 @@ void RunInterpreter(UnitState<Debug>& state) {
call(state,
instr.flow_control.dest_offset,
instr.flow_control.num_instructions,
- state.program_counter + 1, 0, 0);
+ program_counter + 1, 0, 0);
}
break;
@@ -554,7 +570,7 @@ void RunInterpreter(UnitState<Debug>& state) {
call(state,
instr.flow_control.dest_offset,
instr.flow_control.num_instructions,
- state.program_counter + 1, 0, 0);
+ program_counter + 1, 0, 0);
}
break;
@@ -565,8 +581,8 @@ void RunInterpreter(UnitState<Debug>& state) {
Record<DebugDataRecord::COND_BOOL_IN>(state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
if (uniforms.b[instr.flow_control.bool_uniform_id]) {
call(state,
- state.program_counter + 1,
- instr.flow_control.dest_offset - state.program_counter - 1,
+ program_counter + 1,
+ instr.flow_control.dest_offset - program_counter - 1,
instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0);
} else {
call(state,
@@ -584,8 +600,8 @@ void RunInterpreter(UnitState<Debug>& state) {
Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, state.conditional_code);
if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
call(state,
- state.program_counter + 1,
- instr.flow_control.dest_offset - state.program_counter - 1,
+ program_counter + 1,
+ instr.flow_control.dest_offset - program_counter - 1,
instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0);
} else {
call(state,
@@ -607,8 +623,8 @@ void RunInterpreter(UnitState<Debug>& state) {
Record<DebugDataRecord::LOOP_INT_IN>(state.debug, iteration, loop_param);
call(state,
- state.program_counter + 1,
- instr.flow_control.dest_offset - state.program_counter + 1,
+ program_counter + 1,
+ instr.flow_control.dest_offset - program_counter + 1,
instr.flow_control.dest_offset + 1,
loop_param.x,
loop_param.z);
@@ -625,14 +641,14 @@ void RunInterpreter(UnitState<Debug>& state) {
}
}
- ++state.program_counter;
+ ++program_counter;
++iteration;
}
}
// 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 21ae52949..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
@@ -124,7 +130,7 @@ void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::I
input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(), input.attr[i][2].ToFloat32(), input.attr[i][3].ToFloat32());
} else if (vertex_attribute_is_default[i]) {
// Load the default attribute if we're configured to do so
- input.attr[i] = g_state.vs.default_attributes[i];
+ input.attr[i] = g_state.vs_default_attributes[i];
LOG_TRACE(HW_GPU, "Loaded default attribute %x for vertex %x (index %x): (%f, %f, %f, %f)",
i, vertex, index,
input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(),
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