summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/audio_core/CMakeLists.txt16
-rw-r--r--src/audio_core/audio_core.cpp53
-rw-r--r--src/audio_core/audio_core.h26
-rw-r--r--src/audio_core/hle/dsp.cpp42
-rw-r--r--src/audio_core/hle/dsp.h502
-rw-r--r--src/audio_core/hle/pipe.cpp55
-rw-r--r--src/audio_core/hle/pipe.h38
-rw-r--r--src/audio_core/sink.h34
-rw-r--r--src/citra/CMakeLists.txt21
-rw-r--r--src/citra/citra.cpp4
-rw-r--r--src/citra/config.cpp60
-rw-r--r--src/citra/config.h10
-rw-r--r--src/citra/default_ini.h2
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp168
-rw-r--r--src/citra/emu_window/emu_window_glfw.h54
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp167
-rw-r--r--src/citra/emu_window/emu_window_sdl2.h64
-rw-r--r--src/citra_qt/CMakeLists.txt27
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp1
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp1
-rw-r--r--src/citra_qt/debugger/graphics_tracing.cpp2
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp2
-rw-r--r--src/citra_qt/main.cpp18
-rw-r--r--src/citra_qt/main.h8
-rw-r--r--src/common/bit_field.h44
-rw-r--r--src/common/emu_window.cpp4
-rw-r--r--src/common/file_util.cpp2
-rw-r--r--src/common/logging/backend.cpp3
-rw-r--r--src/common/logging/log.h3
-rw-r--r--src/common/x64/emitter.cpp4
-rw-r--r--src/core/file_sys/disk_archive.cpp6
-rw-r--r--src/core/hle/kernel/memory.cpp5
-rw-r--r--src/core/hle/kernel/process.cpp2
-rw-r--r--src/core/hle/result.h8
-rw-r--r--src/core/hle/service/cfg/cfg.cpp26
-rw-r--r--src/core/hle/service/dsp_dsp.cpp135
-rw-r--r--src/core/hle/service/dsp_dsp.h12
-rw-r--r--src/core/hle/service/gsp_gpu.cpp6
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/ir/ir.cpp72
-rw-r--r--src/core/hle/service/ir/ir.h47
-rw-r--r--src/core/hle/service/ir/ir_user.cpp41
-rw-r--r--src/core/hle/service/ptm/ptm.cpp4
-rw-r--r--src/core/hle/service/soc_u.cpp12
-rw-r--r--src/core/hle/svc.cpp1
-rw-r--r--src/core/hw/gpu.cpp22
-rw-r--r--src/core/system.cpp7
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/clipper.cpp1
-rw-r--r--src/video_core/command_processor.cpp76
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp11
-rw-r--r--src/video_core/debug_utils/debug_utils.h3
-rw-r--r--src/video_core/pica.cpp1
-rw-r--r--src/video_core/pica.h66
-rw-r--r--src/video_core/pica_state.h60
-rw-r--r--src/video_core/primitive_assembly.cpp12
-rw-r--r--src/video_core/primitive_assembly.h10
-rw-r--r--src/video_core/rasterizer.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp4
-rw-r--r--src/video_core/shader/shader.cpp3
-rw-r--r--src/video_core/shader/shader.h18
-rw-r--r--src/video_core/shader/shader_interpreter.cpp1
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp2
66 files changed, 1611 insertions, 507 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cb09f3cd1..de4fe716a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,7 +4,8 @@ include_directories(.)
add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(video_core)
-if (ENABLE_GLFW)
+add_subdirectory(audio_core)
+if (ENABLE_SDL2)
add_subdirectory(citra)
endif()
if (ENABLE_QT)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
new file mode 100644
index 000000000..b0d1c7eb6
--- /dev/null
+++ b/src/audio_core/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SRCS
+ audio_core.cpp
+ hle/dsp.cpp
+ hle/pipe.cpp
+ )
+
+set(HEADERS
+ audio_core.h
+ hle/dsp.h
+ hle/pipe.h
+ sink.h
+ )
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(audio_core STATIC ${SRCS} ${HEADERS}) \ No newline at end of file
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp
new file mode 100644
index 000000000..894f46990
--- /dev/null
+++ b/src/audio_core/audio_core.cpp
@@ -0,0 +1,53 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/audio_core.h"
+#include "audio_core/hle/dsp.h"
+
+#include "core/core_timing.h"
+#include "core/hle/kernel/vm_manager.h"
+#include "core/hle/service/dsp_dsp.h"
+
+namespace AudioCore {
+
+// Audio Ticks occur about every 5 miliseconds.
+static int tick_event; ///< CoreTiming event
+static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles
+
+static void AudioTickCallback(u64 /*userdata*/, int cycles_late) {
+ if (DSP::HLE::Tick()) {
+ // HACK: We're not signaling the interrups when they should be, but just firing them all off together.
+ // It should be only (interrupt_id = 2, channel_id = 2) that's signalled here.
+ // TODO(merry): Understand when the other interrupts are fired.
+ DSP_DSP::SignalAllInterrupts();
+ }
+
+ // Reschedule recurrent event
+ CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event);
+}
+
+/// Initialise Audio
+void Init() {
+ DSP::HLE::Init();
+
+ tick_event = CoreTiming::RegisterEvent("AudioCore::tick_event", AudioTickCallback);
+ CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event);
+}
+
+/// Add DSP address spaces to Process's address space.
+void AddAddressSpace(Kernel::VMManager& address_space) {
+ auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_region0), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom();
+ address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite);
+
+ auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_region1), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom();
+ address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite);
+}
+
+/// Shutdown Audio
+void Shutdown() {
+ CoreTiming::UnscheduleEvent(tick_event, 0);
+ DSP::HLE::Shutdown();
+}
+
+} //namespace
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h
new file mode 100644
index 000000000..64c330914
--- /dev/null
+++ b/src/audio_core/audio_core.h
@@ -0,0 +1,26 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Kernel {
+class VMManager;
+}
+
+namespace AudioCore {
+
+constexpr int num_sources = 24;
+constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
+constexpr int native_sample_rate = 32728; ///< 32kHz
+
+/// Initialise Audio Core
+void Init();
+
+/// Add DSP address spaces to a Process.
+void AddAddressSpace(Kernel::VMManager& vm_manager);
+
+/// Shutdown Audio Core
+void Shutdown();
+
+} // namespace
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp
new file mode 100644
index 000000000..c89356edc
--- /dev/null
+++ b/src/audio_core/hle/dsp.cpp
@@ -0,0 +1,42 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/pipe.h"
+
+namespace DSP {
+namespace HLE {
+
+SharedMemory g_region0;
+SharedMemory g_region1;
+
+void Init() {
+ DSP::HLE::ResetPipes();
+}
+
+void Shutdown() {
+}
+
+bool Tick() {
+ return true;
+}
+
+SharedMemory& CurrentRegion() {
+ // The region with the higher frame counter is chosen unless there is wraparound.
+
+ if (g_region0.frame_counter == 0xFFFFu && g_region1.frame_counter != 0xFFFEu) {
+ // Wraparound has occured.
+ return g_region1;
+ }
+
+ if (g_region1.frame_counter == 0xFFFFu && g_region0.frame_counter != 0xFFFEu) {
+ // Wraparound has occured.
+ return g_region0;
+ }
+
+ return (g_region0.frame_counter > g_region1.frame_counter) ? g_region0 : g_region1;
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
new file mode 100644
index 000000000..14c4000c6
--- /dev/null
+++ b/src/audio_core/hle/dsp.h
@@ -0,0 +1,502 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <type_traits>
+
+#include "audio_core/audio_core.h"
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace DSP {
+namespace HLE {
+
+// The application-accessible region of DSP memory consists of two parts.
+// Both are marked as IO and have Read/Write permissions.
+//
+// First Region: 0x1FF50000 (Size: 0x8000)
+// Second Region: 0x1FF70000 (Size: 0x8000)
+//
+// The DSP reads from each region alternately based on the frame counter for each region much like a
+// 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;
+extern SharedMemory g_region0;
+
+constexpr VAddr region1_base = 0x1FF70000;
+extern SharedMemory g_region1;
+
+/**
+ * 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
+ * layout of the ARM11. Hence from the ARM11's point of view the memory space appears to be
+ * middle-endian.
+ *
+ * Unusually this does not appear to be an issue for floating point numbers. The DSP makes the more
+ * sensible choice of keeping that little-endian. There are also some exceptions such as the
+ * IntermediateMixSamples structure, which is little-endian.
+ *
+ * This struct implements the conversion to and from this middle-endianness.
+ */
+struct u32_dsp {
+ u32_dsp() = default;
+ operator u32() const {
+ return Convert(storage);
+ }
+ void operator=(u32 new_value) {
+ storage = Convert(new_value);
+ }
+private:
+ static constexpr u32 Convert(u32 value) {
+ return (value << 16) | (value >> 16);
+ }
+ u32_le storage;
+};
+#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
+static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivially copyable");
+#endif
+
+// There are 15 structures in each memory region. A table of them in the order they appear in memory
+// is presented below
+//
+// Pipe 2 # First Region DSP Address Purpose Control
+// 5 0x8400 DSP Status DSP
+// 9 0x8410 DSP Debug Info DSP
+// 6 0x8540 Final Mix Samples DSP
+// 2 0x8680 Source Status [24] DSP
+// 8 0x8710 Compressor Table Application
+// 4 0x9430 DSP Configuration Application
+// 7 0x9492 Intermediate Mix Samples DSP + App
+// 1 0x9E92 Source Configuration [24] Application
+// 3 0xA792 Source ADPCM Coefficients [24] Application
+// 10 0xA912 Surround Sound Related
+// 11 0xAA12 Surround Sound Related
+// 12 0xAAD2 Surround Sound Related
+// 13 0xAC52 Surround Sound Related
+// 14 0xAC5C Surround Sound Related
+// 0 0xBFFF Frame Counter Application
+//
+// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are
+// not fixed in stone. The addresses above are only an examplar; they're what this implementation
+// does and provides to applications.
+//
+// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the
+// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the
+// second region via:
+// second_region_dsp_addr = first_region_dsp_addr | 0x10000
+//
+// Applications maintain most of its own audio state, the memory region is used mainly for
+// communication and not storage of state.
+//
+// In the documentation below, filter and effect transfer functions are specified in the z domain.
+// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
+// frequency domain, just like how the s domain is the analog frequency domain.)
+
+#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words))
+
+// GCC versions < 5.0 do not implement std::is_trivially_copyable.
+// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
+#if (__GNUC__ >= 5) || defined(__clang__)
+ #define ASSERT_DSP_STRUCT(name, size) \
+ static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
+ static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \
+ static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
+#else
+ #define ASSERT_DSP_STRUCT(name, size) \
+ static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
+ static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
+#endif
+
+struct SourceConfiguration {
+ struct Configuration {
+ /// These dirty flags are set by the application when it updates the fields in this struct.
+ /// The DSP clears these each audio frame.
+ union {
+ u32_le dirty_raw;
+
+ BitField<2, 1, u32_le> adpcm_coefficients_dirty;
+ BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
+
+ BitField<16, 1, u32_le> enable_dirty;
+ BitField<17, 1, u32_le> interpolation_dirty;
+ BitField<18, 1, u32_le> rate_multiplier_dirty;
+ BitField<19, 1, u32_le> buffer_queue_dirty;
+ BitField<20, 1, u32_le> loop_related_dirty;
+ BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated.
+ BitField<22, 1, u32_le> filters_enabled_dirty;
+ BitField<23, 1, u32_le> simple_filter_dirty;
+ BitField<24, 1, u32_le> biquad_filter_dirty;
+ BitField<25, 1, u32_le> gain_0_dirty;
+ BitField<26, 1, u32_le> gain_1_dirty;
+ BitField<27, 1, u32_le> gain_2_dirty;
+ BitField<28, 1, u32_le> sync_dirty;
+ BitField<29, 1, u32_le> reset_flag;
+
+ BitField<31, 1, u32_le> embedded_buffer_dirty;
+ };
+
+ // Gain control
+
+ /**
+ * Gain is between 0.0-1.0. This determines how much will this source appear on
+ * each of the 12 channels that feed into the intermediate mixers.
+ * Each of the three intermediate mixers is fed two left and two right channels.
+ */
+ float_le gain[3][4];
+
+ // Interpolation
+
+ /// Multiplier for sample rate. Resampling occurs with the selected interpolation method.
+ float_le rate_multiplier;
+
+ enum class InterpolationMode : u8 {
+ None = 0,
+ Linear = 1,
+ Polyphase = 2
+ };
+
+ InterpolationMode interpolation_mode;
+ INSERT_PADDING_BYTES(1); ///< Interpolation related
+
+ // Filters
+
+ /**
+ * This is the simplest normalized first-order digital recursive filter.
+ * The transfer function of this filter is:
+ * H(z) = b0 / (1 + a1 z^-1)
+ * Values are signed fixed point with 15 fractional bits.
+ */
+ struct SimpleFilter {
+ s16_le b0;
+ s16_le a1;
+ };
+
+ /**
+ * This is a normalised biquad filter (second-order).
+ * The transfer function of this filter is:
+ * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
+ * Nintendo chose to negate the feedbackward coefficients. This differs from standard notation
+ * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
+ * Values are signed fixed point with 14 fractional bits.
+ */
+ struct BiquadFilter {
+ s16_le b0;
+ s16_le b1;
+ s16_le b2;
+ s16_le a1;
+ s16_le a2;
+ };
+
+ union {
+ u16_le filters_enabled;
+ BitField<0, 1, u16_le> simple_filter_enabled;
+ BitField<1, 1, u16_le> biquad_filter_enabled;
+ };
+
+ SimpleFilter simple_filter;
+ BiquadFilter biquad_filter;
+
+ // Buffer Queue
+
+ /// A buffer of audio data from the application, along with metadata about it.
+ struct Buffer {
+ /// Physical memory address of the start of the buffer
+ u32_dsp physical_address;
+
+ /// This is length in terms of samples.
+ /// Note that in different buffer formats a sample takes up different number of bytes.
+ u32_dsp length;
+
+ /// ADPCM Predictor (4 bits) and Scale (4 bits)
+ union {
+ u16_le adpcm_ps;
+ BitField<0, 4, u16_le> adpcm_scale;
+ BitField<4, 4, u16_le> adpcm_predictor;
+ };
+
+ /// ADPCM Historical Samples (y[n-1] and y[n-2])
+ u16_le adpcm_yn[2];
+
+ /// This is non-zero when the ADPCM values above are to be updated.
+ u8 adpcm_dirty;
+
+ /// Is a looping buffer.
+ u8 is_looping;
+
+ /// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished.
+ /// This allows the emulated application to tell what buffer is currently playing
+ u16_le buffer_id;
+
+ INSERT_PADDING_DSPWORDS(1);
+ };
+
+ u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
+ Buffer buffers[4]; ///< Queued Buffers
+
+ // Playback controls
+
+ u32_dsp loop_related;
+ u8 enable;
+ INSERT_PADDING_BYTES(1);
+ u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
+ u32_dsp play_position; ///< Position. (Units: number of samples)
+ INSERT_PADDING_DSPWORDS(2);
+
+ // Embedded Buffer
+ // This buffer is often the first buffer to be used when initiating audio playback,
+ // after which the buffer queue is used.
+
+ u32_dsp physical_address;
+
+ /// This is length in terms of samples.
+ /// Note a sample takes up different number of bytes in different buffer formats.
+ u32_dsp length;
+
+ enum class MonoOrStereo : u16_le {
+ Mono = 1,
+ Stereo = 2
+ };
+
+ enum class Format : u16_le {
+ PCM8 = 0,
+ PCM16 = 1,
+ ADPCM = 2
+ };
+
+ union {
+ u16_le flags1_raw;
+ BitField<0, 2, MonoOrStereo> mono_or_stereo;
+ BitField<2, 2, Format> format;
+ BitField<5, 1, u16_le> fade_in;
+ };
+
+ /// ADPCM Predictor (4 bit) and Scale (4 bit)
+ union {
+ u16_le adpcm_ps;
+ BitField<0, 4, u16_le> adpcm_scale;
+ BitField<4, 4, u16_le> adpcm_predictor;
+ };
+
+ /// ADPCM Historical Samples (y[n-1] and y[n-2])
+ u16_le adpcm_yn[2];
+
+ union {
+ u16_le flags2_raw;
+ BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
+ BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
+ };
+
+ /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer).
+ u16_le buffer_id;
+ };
+
+ Configuration config[AudioCore::num_sources];
+};
+ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192);
+ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
+
+struct SourceStatus {
+ struct Status {
+ u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
+ u8 previous_buffer_id_dirty; ///< Non-zero when previous_buffer_id changes
+ u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
+ u32_dsp buffer_position; ///< Number of samples into the current buffer
+ u16_le previous_buffer_id; ///< Updated when a buffer finishes playing
+ INSERT_PADDING_DSPWORDS(1);
+ };
+
+ Status status[AudioCore::num_sources];
+};
+ASSERT_DSP_STRUCT(SourceStatus::Status, 12);
+
+struct DspConfiguration {
+ /// These dirty flags are set by the application when it updates the fields in this struct.
+ /// The DSP clears these each audio frame.
+ union {
+ u32_le dirty_raw;
+
+ BitField<8, 1, u32_le> mixer1_enabled_dirty;
+ BitField<9, 1, u32_le> mixer2_enabled_dirty;
+ BitField<10, 1, u32_le> delay_effect_0_dirty;
+ BitField<11, 1, u32_le> delay_effect_1_dirty;
+ BitField<12, 1, u32_le> reverb_effect_0_dirty;
+ BitField<13, 1, u32_le> reverb_effect_1_dirty;
+
+ BitField<16, 1, u32_le> volume_0_dirty;
+
+ BitField<24, 1, u32_le> volume_1_dirty;
+ BitField<25, 1, u32_le> volume_2_dirty;
+ BitField<26, 1, u32_le> output_format_dirty;
+ BitField<27, 1, u32_le> limiter_enabled_dirty;
+ BitField<28, 1, u32_le> headphones_connected_dirty;
+ };
+
+ /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer
+ float_le volume[3];
+
+ INSERT_PADDING_DSPWORDS(3);
+
+ enum class OutputFormat : u16_le {
+ Mono = 0,
+ Stereo = 1,
+ Surround = 2
+ };
+
+ OutputFormat output_format;
+
+ u16_le limiter_enabled; ///< Not sure of the exact gain equation for the limiter.
+ u16_le headphones_connected; ///< Application updates the DSP on headphone status.
+ INSERT_PADDING_DSPWORDS(4); ///< TODO: Surround sound related
+ INSERT_PADDING_DSPWORDS(2); ///< TODO: Intermediate mixer 1/2 related
+ u16_le mixer1_enabled;
+ u16_le mixer2_enabled;
+
+ /**
+ * This is delay with feedback.
+ * Transfer function:
+ * H(z) = a z^-N / (1 - b z^-1 + a g z^-N)
+ * where
+ * N = frame_count * samples_per_frame
+ * g, a and b are fixed point with 7 fractional bits
+ */
+ struct DelayEffect {
+ /// These dirty flags are set by the application when it updates the fields in this struct.
+ /// The DSP clears these each audio frame.
+ union {
+ u16_le dirty_raw;
+ BitField<0, 1, u16_le> enable_dirty;
+ BitField<1, 1, u16_le> work_buffer_address_dirty;
+ BitField<2, 1, u16_le> other_dirty; ///< Set when anything else has been changed
+ };
+
+ u16_le enable;
+ INSERT_PADDING_DSPWORDS(1);
+ u16_le outputs;
+ u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer.
+ u16_le frame_count; ///< Frames to delay by
+
+ // Coefficients
+ s16_le g; ///< Fixed point with 7 fractional bits
+ s16_le a; ///< Fixed point with 7 fractional bits
+ s16_le b; ///< Fixed point with 7 fractional bits
+ };
+
+ DelayEffect delay_effect[2];
+
+ struct ReverbEffect {
+ INSERT_PADDING_DSPWORDS(26); ///< TODO
+ };
+
+ ReverbEffect reverb_effect[2];
+
+ INSERT_PADDING_DSPWORDS(4);
+};
+ASSERT_DSP_STRUCT(DspConfiguration, 196);
+ASSERT_DSP_STRUCT(DspConfiguration::DelayEffect, 20);
+ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
+
+struct AdpcmCoefficients {
+ /// Coefficients are signed fixed point with 11 fractional bits.
+ /// Each source has 16 coefficients associated with it.
+ s16_le coeff[AudioCore::num_sources][16];
+};
+ASSERT_DSP_STRUCT(AdpcmCoefficients, 768);
+
+struct DspStatus {
+ u16_le unknown;
+ u16_le dropped_frames;
+ INSERT_PADDING_DSPWORDS(0xE);
+};
+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 * AudioCore::samples_per_frame];
+};
+ASSERT_DSP_STRUCT(FinalMixSamples, 640);
+
+/// DSP writes output of intermediate mixers 1 and 2 here.
+/// Writes to this region by the application edits the output of the intermediate mixers.
+/// This seems to be intended to allow the application to do custom effects on the ARM11.
+/// Values that exceed s16 range will be clipped by the DSP after further processing.
+struct IntermediateMixSamples {
+ struct Samples {
+ s32_le pcm32[4][AudioCore::samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
+ };
+
+ Samples mix1;
+ Samples mix2;
+};
+ASSERT_DSP_STRUCT(IntermediateMixSamples, 5120);
+
+/// Compressor table
+struct Compressor {
+ INSERT_PADDING_DSPWORDS(0xD20); ///< TODO
+};
+
+/// There is no easy way to implement this in a HLE implementation.
+struct DspDebug {
+ INSERT_PADDING_DSPWORDS(0x130);
+};
+ASSERT_DSP_STRUCT(DspDebug, 0x260);
+
+struct SharedMemory {
+ /// Padding
+ INSERT_PADDING_DSPWORDS(0x400);
+
+ DspStatus dsp_status;
+
+ DspDebug dsp_debug;
+
+ FinalMixSamples final_samples;
+
+ SourceStatus source_statuses;
+
+ Compressor compressor;
+
+ DspConfiguration dsp_configuration;
+
+ IntermediateMixSamples intermediate_mix_samples;
+
+ SourceConfiguration source_configurations;
+
+ AdpcmCoefficients adpcm_coefficients;
+
+ /// Unknown 10-14 (Surround sound related)
+ INSERT_PADDING_DSPWORDS(0x16ED);
+
+ u16_le frame_counter;
+};
+ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
+
+#undef INSERT_PADDING_DSPWORDS
+#undef ASSERT_DSP_STRUCT
+
+/// Initialize DSP hardware
+void Init();
+
+/// Shutdown DSP hardware
+void Shutdown();
+
+/**
+ * Perform processing and updates state of current shared memory buffer.
+ * This function is called every audio tick before triggering the audio interrupt.
+ * @return Whether an audio interrupt should be triggered this frame.
+ */
+bool Tick();
+
+/// Returns a mutable reference to the current region. Current region is selected based on the frame counter.
+SharedMemory& CurrentRegion();
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp
new file mode 100644
index 000000000..6542c760c
--- /dev/null
+++ b/src/audio_core/hle/pipe.cpp
@@ -0,0 +1,55 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <vector>
+
+#include "audio_core/hle/pipe.h"
+
+#include "common/common_types.h"
+#include "common/logging/log.h"
+
+namespace DSP {
+namespace HLE {
+
+static size_t pipe2position = 0;
+
+void ResetPipes() {
+ pipe2position = 0;
+}
+
+std::vector<u8> PipeRead(u32 pipe_number, u32 length) {
+ if (pipe_number != 2) {
+ LOG_WARNING(Audio_DSP, "pipe_number = %u (!= 2), unimplemented", pipe_number);
+ return {}; // We currently don't handle anything other than the audio pipe.
+ }
+
+ // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
+ // TODO: Our implementation will actually use a slightly different response than this one.
+ // TODO: Use offsetof on DSP structures instead for a proper response.
+ static const std::array<u8, 32> canned_response {{
+ 0x0F, 0x00, 0xFF, 0xBF, 0x8E, 0x9E, 0x80, 0x86, 0x8E, 0xA7, 0x30, 0x94, 0x00, 0x84, 0x40, 0x85,
+ 0x8E, 0x94, 0x10, 0x87, 0x10, 0x84, 0x0E, 0xA9, 0x0E, 0xAA, 0xCE, 0xAA, 0x4E, 0xAC, 0x58, 0xAC
+ }};
+
+ // TODO: Move this into dsp::DSP service since it happens on the service side.
+ // Hardware observation: No data is returned if requested length reads beyond the end of the data in-pipe.
+ if (pipe2position + length > canned_response.size()) {
+ return {};
+ }
+
+ std::vector<u8> ret;
+ for (size_t i = 0; i < length; i++, pipe2position++) {
+ ret.emplace_back(canned_response[pipe2position]);
+ }
+
+ return ret;
+}
+
+void PipeWrite(u32 pipe_number, const std::vector<u8>& buffer) {
+ // TODO: proper pipe behaviour
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h
new file mode 100644
index 000000000..ff6536950
--- /dev/null
+++ b/src/audio_core/hle/pipe.h
@@ -0,0 +1,38 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace DSP {
+namespace HLE {
+
+/// Reset the pipes by setting pipe positions back to the beginning.
+void ResetPipes();
+
+/**
+ * Read a DSP pipe.
+ * Pipe IDs:
+ * pipe_number = 0: Debug
+ * pipe_number = 1: P-DMA
+ * pipe_number = 2: Audio
+ * pipe_number = 3: Binary
+ * @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.
+ */
+std::vector<u8> PipeRead(u32 pipe_number, u32 length);
+
+/**
+ * Write to a DSP pipe.
+ * @param pipe_number The Pipe ID
+ * @param buffer The data to write to the pipe.
+ */
+void PipeWrite(u32 pipe_number, const std::vector<u8>& buffer);
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
new file mode 100644
index 000000000..cad21a85e
--- /dev/null
+++ b/src/audio_core/sink.h
@@ -0,0 +1,34 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace AudioCore {
+
+/**
+ * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output.
+ * Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs.
+ */
+class Sink {
+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;
+
+ /**
+ * Feed stereo samples to sink.
+ * @param samples Samples in interleaved stereo PCM16 format. Size of vector must be multiple of two.
+ */
+ virtual void EnqueueSamples(const std::vector<s16>& samples) = 0;
+
+ /// Samples enqueued that have not been played yet.
+ virtual std::size_t SamplesInQueue() const = 0;
+};
+
+} // namespace
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index e7f8a17f9..fa615deb9 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -1,11 +1,11 @@
set(SRCS
- emu_window/emu_window_glfw.cpp
+ emu_window/emu_window_sdl2.cpp
citra.cpp
config.cpp
citra.rc
)
set(HEADERS
- emu_window/emu_window_glfw.h
+ emu_window/emu_window_sdl2.h
config.h
default_ini.h
resource.h
@@ -13,12 +13,11 @@ set(HEADERS
create_directory_groups(${SRCS} ${HEADERS})
-include_directories(${GLFW_INCLUDE_DIRS})
-link_directories(${GLFW_LIBRARY_DIRS})
+include_directories(${SDL2_INCLUDE_DIR})
add_executable(citra ${SRCS} ${HEADERS})
-target_link_libraries(citra core video_core common)
-target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih glad)
+target_link_libraries(citra core video_core audio_core common)
+target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad)
if (MSVC)
target_link_libraries(citra getopt)
endif()
@@ -27,3 +26,13 @@ target_link_libraries(citra ${PLATFORM_LIBRARIES})
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
endif()
+
+if (MSVC)
+ include(WindowsCopyFiles)
+
+ set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
+
+ windows_copy_files(citra ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll)
+
+ unset(DLL_DEST)
+endif()
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index c96fc1374..415b98a05 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -27,7 +27,7 @@
#include "core/loader/loader.h"
#include "citra/config.h"
-#include "citra/emu_window/emu_window_glfw.h"
+#include "citra/emu_window/emu_window_sdl2.h"
#include "video_core/video_core.h"
@@ -76,7 +76,7 @@ int main(int argc, char **argv) {
GDBStub::ToggleServer(Settings::values.use_gdbstub);
GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
- EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
+ EmuWindow_SDL2* emu_window = new EmuWindow_SDL2;
VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer;
VideoCore::g_shader_jit_enabled = Settings::values.use_shader_jit;
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 2f13c29a2..9034b188e 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -2,14 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
#include <inih/cpp/INIReader.h>
+#include <SDL.h>
+
#include "citra/default_ini.h"
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "common/make_unique.h"
#include "core/settings.h"
@@ -17,21 +18,22 @@
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- glfw_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "glfw-config.ini";
- glfw_config = new INIReader(glfw_config_loc);
+ sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini";
+ sdl2_config = Common::make_unique<INIReader>(sdl2_config_loc);
Reload();
}
-bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) {
- if (config->ParseError() < 0) {
+bool Config::LoadINI(const std::string& default_contents, bool retry) {
+ const char* location = this->sdl2_config_loc.c_str();
+ if (sdl2_config->ParseError() < 0) {
if (retry) {
LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location);
FileUtil::CreateFullPath(location);
FileUtil::WriteStringToFile(true, default_contents, location);
- *config = INIReader(location); // Reopen file
+ sdl2_config = Common::make_unique<INIReader>(location); // Reopen file
- return LoadINI(config, location, default_contents, false);
+ return LoadINI(default_contents, false);
}
LOG_ERROR(Config, "Failed.");
return false;
@@ -41,51 +43,47 @@ bool Config::LoadINI(INIReader* config, const char* location, const std::string&
}
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
- GLFW_KEY_A, GLFW_KEY_S, GLFW_KEY_Z, GLFW_KEY_X,
- GLFW_KEY_Q, GLFW_KEY_W, GLFW_KEY_1, GLFW_KEY_2,
- GLFW_KEY_M, GLFW_KEY_N, GLFW_KEY_B,
- GLFW_KEY_T, GLFW_KEY_G, GLFW_KEY_F, GLFW_KEY_H,
- GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_KEY_LEFT, GLFW_KEY_RIGHT,
- GLFW_KEY_I, GLFW_KEY_K, GLFW_KEY_J, GLFW_KEY_L
+ SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X,
+ SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2,
+ SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B,
+ SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H,
+ SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,
+ SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L
};
void Config::ReadValues() {
// Controls
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
- glfw_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
+ sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
}
// Core
- Settings::values.frame_skip = glfw_config->GetInteger("Core", "frame_skip", 0);
+ Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
// Renderer
- Settings::values.use_hw_renderer = glfw_config->GetBoolean("Renderer", "use_hw_renderer", false);
- Settings::values.use_shader_jit = glfw_config->GetBoolean("Renderer", "use_shader_jit", true);
+ Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", false);
+ Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
- Settings::values.bg_red = (float)glfw_config->GetReal("Renderer", "bg_red", 1.0);
- Settings::values.bg_green = (float)glfw_config->GetReal("Renderer", "bg_green", 1.0);
- Settings::values.bg_blue = (float)glfw_config->GetReal("Renderer", "bg_blue", 1.0);
+ Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0);
+ Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0);
+ Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0);
// Data Storage
- Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true);
+ Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
// System Region
- Settings::values.region_value = glfw_config->GetInteger("System Region", "region_value", 1);
+ Settings::values.region_value = sdl2_config->GetInteger("System Region", "region_value", 1);
// Miscellaneous
- Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info");
+ Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info");
// Debugging
- Settings::values.use_gdbstub = glfw_config->GetBoolean("Debugging", "use_gdbstub", false);
- Settings::values.gdbstub_port = glfw_config->GetInteger("Debugging", "gdbstub_port", 24689);
+ Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
+ Settings::values.gdbstub_port = sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689);
}
void Config::Reload() {
- LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file);
+ LoadINI(DefaultINI::sdl2_config_file);
ReadValues();
}
-
-Config::~Config() {
- delete glfw_config;
-}
diff --git a/src/citra/config.h b/src/citra/config.h
index c326ec669..52a478146 100644
--- a/src/citra/config.h
+++ b/src/citra/config.h
@@ -4,19 +4,19 @@
#pragma once
+#include <memory>
#include <string>
-class INIReader;
+#include <inih/cpp/INIReader.h>
class Config {
- INIReader* glfw_config;
- std::string glfw_config_loc;
+ std::unique_ptr<INIReader> sdl2_config;
+ std::string sdl2_config_loc;
- bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true);
+ bool LoadINI(const std::string& default_contents="", bool retry=true);
void ReadValues();
public:
Config();
- ~Config();
void Reload();
};
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 5ba40a8ed..c9b490a00 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -6,7 +6,7 @@
namespace DefaultINI {
-const char* glfw_config_file = R"(
+const char* sdl2_config_file = R"(
[Controls]
pad_start =
pad_select =
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
deleted file mode 100644
index 9453b1f48..000000000
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <cstdlib>
-#include <string>
-
-// Let’s use our own GL header, instead of one from GLFW.
-#include <glad/glad.h>
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
-
-#include "common/assert.h"
-#include "common/key_map.h"
-#include "common/logging/log.h"
-#include "common/scm_rev.h"
-#include "common/string_util.h"
-
-#include "video_core/video_core.h"
-
-#include "core/settings.h"
-#include "core/hle/service/hid/hid.h"
-
-#include "citra/emu_window/emu_window_glfw.h"
-
-EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) {
- return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win));
-}
-
-void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action, int mods) {
- if (button == GLFW_MOUSE_BUTTON_LEFT) {
- auto emu_window = GetEmuWindow(win);
- auto layout = emu_window->GetFramebufferLayout();
- double x, y;
- glfwGetCursorPos(win, &x, &y);
-
- if (action == GLFW_PRESS)
- emu_window->TouchPressed(static_cast<unsigned>(x), static_cast<unsigned>(y));
- else if (action == GLFW_RELEASE)
- emu_window->TouchReleased();
- }
-}
-
-void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) {
- GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(std::max(x, 0.0)), static_cast<unsigned>(std::max(y, 0.0)));
-}
-
-/// Called by GLFW when a key event occurs
-void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) {
- auto emu_window = GetEmuWindow(win);
- int keyboard_id = emu_window->keyboard_id;
-
- if (action == GLFW_PRESS) {
- emu_window->KeyPressed({key, keyboard_id});
- } else if (action == GLFW_RELEASE) {
- emu_window->KeyReleased({key, keyboard_id});
- }
-}
-
-/// Whether the window is still open, and a close request hasn't yet been sent
-const bool EmuWindow_GLFW::IsOpen() {
- return glfwWindowShouldClose(m_render_window) == 0;
-}
-
-void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) {
- GetEmuWindow(win)->NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
-}
-
-void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) {
- // NOTE: GLFW provides no proper way to set a minimal window size.
- // Hence, we just ignore the corresponding EmuWindow hint.
- OnFramebufferResizeEvent(win, width, height);
-}
-
-/// EmuWindow_GLFW constructor
-EmuWindow_GLFW::EmuWindow_GLFW() {
- keyboard_id = KeyMap::NewDeviceId();
-
- ReloadSetKeymaps();
-
- glfwSetErrorCallback([](int error, const char *desc){
- LOG_ERROR(Frontend, "GLFW 0x%08x: %s", error, desc);
- });
-
- // Initialize the window
- if(glfwInit() != GL_TRUE) {
- LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting...");
- exit(1);
- }
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
- glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-
- std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
- m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
- (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
- window_title.c_str(), nullptr, nullptr);
-
- if (m_render_window == nullptr) {
- LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting...");
- exit(1);
- }
-
- glfwSetWindowUserPointer(m_render_window, this);
-
- // Notify base interface about window state
- int width, height;
- glfwGetFramebufferSize(m_render_window, &width, &height);
- OnFramebufferResizeEvent(m_render_window, width, height);
-
- glfwGetWindowSize(m_render_window, &width, &height);
- OnClientAreaResizeEvent(m_render_window, width, height);
-
- // Setup callbacks
- glfwSetKeyCallback(m_render_window, OnKeyEvent);
- glfwSetMouseButtonCallback(m_render_window, OnMouseButtonEvent);
- glfwSetCursorPosCallback(m_render_window, OnCursorPosEvent);
- glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent);
- glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent);
-
- DoneCurrent();
-}
-
-/// EmuWindow_GLFW destructor
-EmuWindow_GLFW::~EmuWindow_GLFW() {
- glfwTerminate();
-}
-
-/// Swap buffers to display the next frame
-void EmuWindow_GLFW::SwapBuffers() {
- glfwSwapBuffers(m_render_window);
-}
-
-/// Polls window events
-void EmuWindow_GLFW::PollEvents() {
- glfwPollEvents();
-}
-
-/// Makes the GLFW OpenGL context current for the caller thread
-void EmuWindow_GLFW::MakeCurrent() {
- glfwMakeContextCurrent(m_render_window);
-}
-
-/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
-void EmuWindow_GLFW::DoneCurrent() {
- glfwMakeContextCurrent(nullptr);
-}
-
-void EmuWindow_GLFW::ReloadSetKeymaps() {
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]);
- }
-}
-
-void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
- std::pair<int,int> current_size;
- glfwGetWindowSize(m_render_window, &current_size.first, &current_size.second);
-
- DEBUG_ASSERT((int)minimal_size.first > 0 && (int)minimal_size.second > 0);
- int new_width = std::max(current_size.first, (int)minimal_size.first);
- int new_height = std::max(current_size.second, (int)minimal_size.second);
-
- if (current_size != std::make_pair(new_width, new_height))
- glfwSetWindowSize(m_render_window, new_width, new_height);
-}
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h
deleted file mode 100644
index 7ccd5e6aa..000000000
--- a/src/citra/emu_window/emu_window_glfw.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <utility>
-
-#include "common/emu_window.h"
-
-struct GLFWwindow;
-
-class EmuWindow_GLFW : public EmuWindow {
-public:
- EmuWindow_GLFW();
- ~EmuWindow_GLFW();
-
- /// Swap buffers to display the next frame
- void SwapBuffers() override;
-
- /// Polls window events
- void PollEvents() override;
-
- /// Makes the graphics context current for the caller thread
- void MakeCurrent() override;
-
- /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
- void DoneCurrent() override;
-
- static void OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods);
-
- static void OnMouseButtonEvent(GLFWwindow* window, int button, int action, int mods);
-
- static void OnCursorPosEvent(GLFWwindow* window, double x, double y);
-
- /// Whether the window is still open, and a close request hasn't yet been sent
- const bool IsOpen();
-
- static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height);
-
- static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height);
-
- void ReloadSetKeymaps() override;
-
-private:
- void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
-
- static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win);
-
- GLFWwindow* m_render_window; ///< Internal GLFW render window
-
- /// Device id of keyboard for use with KeyMap
- int keyboard_id;
-};
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
new file mode 100644
index 000000000..1fed82e78
--- /dev/null
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -0,0 +1,167 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+
+#define SDL_MAIN_HANDLED
+#include <SDL.h>
+
+#include "common/key_map.h"
+#include "common/logging/log.h"
+#include "common/scm_rev.h"
+#include "common/string_util.h"
+
+#include "core/settings.h"
+#include "core/hle/service/hid/hid.h"
+
+#include "citra/emu_window/emu_window_sdl2.h"
+
+#include "video_core/video_core.h"
+
+void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
+ TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
+}
+
+void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
+ if (button != SDL_BUTTON_LEFT)
+ return;
+
+ if (state == SDL_PRESSED) {
+ TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
+ } else {
+ TouchReleased();
+ }
+}
+
+void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
+ if (state == SDL_PRESSED) {
+ KeyPressed({ key, keyboard_id });
+ } else if (state == SDL_RELEASED) {
+ KeyReleased({ key, keyboard_id });
+ }
+}
+
+bool EmuWindow_SDL2::IsOpen() const {
+ return is_open;
+}
+
+void EmuWindow_SDL2::OnResize() {
+ int width, height;
+
+ SDL_GetWindowSize(render_window, &width, &height);
+
+ NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
+}
+
+EmuWindow_SDL2::EmuWindow_SDL2() {
+ keyboard_id = KeyMap::NewDeviceId();
+
+ ReloadSetKeymaps();
+
+ SDL_SetMainReady();
+
+ // Initialize the window
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
+ exit(1);
+ }
+
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
+ render_window = SDL_CreateWindow(window_title.c_str(),
+ SDL_WINDOWPOS_UNDEFINED, // x position
+ SDL_WINDOWPOS_UNDEFINED, // y position
+ VideoCore::kScreenTopWidth,
+ VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
+ SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
+
+ if (render_window == nullptr) {
+ LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting...");
+ exit(1);
+ }
+
+ gl_context = SDL_GL_CreateContext(render_window);
+
+ if (gl_context == nullptr) {
+ LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting...");
+ exit(1);
+ }
+
+ OnResize();
+ OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
+ SDL_PumpEvents();
+
+ DoneCurrent();
+}
+
+EmuWindow_SDL2::~EmuWindow_SDL2() {
+ SDL_GL_DeleteContext(gl_context);
+ SDL_Quit();
+}
+
+void EmuWindow_SDL2::SwapBuffers() {
+ SDL_GL_SwapWindow(render_window);
+}
+
+void EmuWindow_SDL2::PollEvents() {
+ SDL_Event event;
+
+ // SDL_PollEvent returns 0 when there are no more events in the event queue
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_WINDOWEVENT:
+ switch (event.window.event) {
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ case SDL_WINDOWEVENT_RESIZED:
+ case SDL_WINDOWEVENT_MAXIMIZED:
+ case SDL_WINDOWEVENT_RESTORED:
+ case SDL_WINDOWEVENT_MINIMIZED:
+ OnResize();
+ break;
+ case SDL_WINDOWEVENT_CLOSE:
+ is_open = false;
+ break;
+ }
+ break;
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
+ break;
+ case SDL_MOUSEMOTION:
+ OnMouseMotion(event.motion.x, event.motion.y);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
+ break;
+ case SDL_QUIT:
+ is_open = false;
+ break;
+ }
+ }
+}
+
+void EmuWindow_SDL2::MakeCurrent() {
+ SDL_GL_MakeCurrent(render_window, gl_context);
+}
+
+void EmuWindow_SDL2::DoneCurrent() {
+ SDL_GL_MakeCurrent(render_window, nullptr);
+}
+
+void EmuWindow_SDL2::ReloadSetKeymaps() {
+ for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
+ KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, Service::HID::pad_mapping[i]);
+ }
+}
+
+void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) {
+ SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
+}
diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h
new file mode 100644
index 000000000..77279f022
--- /dev/null
+++ b/src/citra/emu_window/emu_window_sdl2.h
@@ -0,0 +1,64 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <utility>
+
+#include "common/emu_window.h"
+
+struct SDL_Window;
+
+class EmuWindow_SDL2 : public EmuWindow {
+public:
+ EmuWindow_SDL2();
+ ~EmuWindow_SDL2();
+
+ /// Swap buffers to display the next frame
+ void SwapBuffers() override;
+
+ /// Polls window events
+ void PollEvents() override;
+
+ /// Makes the graphics context current for the caller thread
+ void MakeCurrent() override;
+
+ /// Releases the GL context from the caller thread
+ void DoneCurrent() override;
+
+ /// Whether the window is still open, and a close request hasn't yet been sent
+ bool IsOpen() const;
+
+ /// Load keymap from configuration
+ void ReloadSetKeymaps() override;
+
+private:
+ /// Called by PollEvents when a key is pressed or released.
+ void OnKeyEvent(int key, u8 state);
+
+ /// Called by PollEvents when the mouse moves.
+ void OnMouseMotion(s32 x, s32 y);
+
+ /// Called by PollEvents when a mouse button is pressed or released
+ void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
+
+ /// Called by PollEvents when any event that may cause the window to be resized occurs
+ void OnResize();
+
+ /// Called when a configuration change affects the minimal size of the window
+ void OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) override;
+
+ /// Is the window still open?
+ bool is_open = true;
+
+ /// Internal SDL2 render window
+ SDL_Window* render_window;
+
+ using SDL_GLContext = void *;
+ /// The OpenGL context associated with the window
+ SDL_GLContext gl_context;
+
+ /// Device id of keyboard for use with KeyMap
+ int keyboard_id;
+};
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index bbf6ae001..9b3eb2cd6 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -79,7 +79,7 @@ if (APPLE)
else()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
endif()
-target_link_libraries(citra-qt core video_core common qhexedit)
+target_link_libraries(citra-qt core video_core audio_core common qhexedit)
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
@@ -88,9 +88,14 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
endif()
if (Qt5_FOUND AND MSVC)
+ include(WindowsCopyFiles)
+
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
- set(Qt5_DLLS
+ set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
+ set(PLATFORMS ${DLL_DEST}platforms/)
+
+ windows_copy_files(citra-qt ${Qt5_DLL_DIR} ${DLL_DEST}
icudt*.dll
icuin*.dll
icuuc*.dll
@@ -99,24 +104,8 @@ if (Qt5_FOUND AND MSVC)
Qt5OpenGL$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
)
- set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
- set(PLATFORMS ${DLL_DEST}platforms/)
+ windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
- # windows commandline expects the / to be \ so switch them
- string(REPLACE "/" "\\\\" Qt5_DLL_DIR ${Qt5_DLL_DIR})
- string(REPLACE "/" "\\\\" Qt5_PLATFORMS_DIR ${Qt5_PLATFORMS_DIR})
- string(REPLACE "/" "\\\\" DLL_DEST ${DLL_DEST})
- string(REPLACE "/" "\\\\" PLATFORMS ${PLATFORMS})
-
- # /NJH /NJS /NDL /NFL /NC /NS /NP - Silence any output
- # cmake adds an extra check for command success which doesn't work too well with robocopy
- # so trick it into thinking the command was successful with the || cmd /c "exit /b 0"
- add_custom_command(TARGET citra-qt POST_BUILD
- COMMAND robocopy ${Qt5_DLL_DIR} ${DLL_DEST} ${Qt5_DLLS} /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
- COMMAND if not exist ${PLATFORMS} mkdir ${PLATFORMS} 2> nul
- COMMAND robocopy ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.* /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
- )
- unset(Qt5_DLLS)
unset(Qt5_DLL_DIR)
unset(Qt5_PLATFORMS_DIR)
unset(DLL_DEST)
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index ab97c8d2d..5186d2b44 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -21,6 +21,7 @@
#include "common/vector_math.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/debug_utils/debug_utils.h"
QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
index 80b32eaff..c30e75933 100644
--- a/src/citra_qt/debugger/graphics_framebuffer.cpp
+++ b/src/citra_qt/debugger/graphics_framebuffer.cpp
@@ -18,6 +18,7 @@
#include "core/hw/gpu.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/utils.h"
GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
diff --git a/src/citra_qt/debugger/graphics_tracing.cpp b/src/citra_qt/debugger/graphics_tracing.cpp
index b0bc782df..e06498744 100644
--- a/src/citra_qt/debugger/graphics_tracing.cpp
+++ b/src/citra_qt/debugger/graphics_tracing.cpp
@@ -22,7 +22,7 @@
#include "nihstro/float24.h"
#include "video_core/pica.h"
-
+#include "video_core/pica_state.h"
GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent)
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
index a5a5fe6b0..a11c61667 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -19,6 +19,8 @@
#include "citra_qt/debugger/graphics_vertex_shader.h"
#include "citra_qt/util/util.h"
+#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/shader/shader.h"
using nihstro::OpCode;
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 144f11117..da9ea6c91 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -171,6 +171,8 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
}
UpdateRecentFiles();
+ confirm_before_closing = settings.value("confirmClose", true).toBool();
+
// Setup connections
connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)));
connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()));
@@ -497,7 +499,22 @@ void GMainWindow::OnConfigure() {
//GControllerConfigDialog* dialog = new GControllerConfigDialog(controller_ports, this);
}
+bool GMainWindow::ConfirmClose() {
+ if (emu_thread == nullptr || !confirm_before_closing)
+ return true;
+
+ auto answer = QMessageBox::question(this, tr("Citra"),
+ tr("Are you sure you want to close Citra?"),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ return answer != QMessageBox::No;
+}
+
void GMainWindow::closeEvent(QCloseEvent* event) {
+ if (!ConfirmClose()) {
+ event->ignore();
+ return;
+ }
+
// Save window layout
QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Citra team", "Citra");
@@ -512,6 +529,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked());
settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked());
settings.setValue("firstStart", false);
+ settings.setValue("confirmClose", confirm_before_closing);
game_list->SaveInterfaceLayout(settings);
SaveHotkeys(settings);
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index f6d429cd9..8c195f816 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -82,6 +82,13 @@ private:
*/
void UpdateRecentFiles();
+ /**
+ * If the emulation is running,
+ * asks the user if he really want to close the emulator
+ *
+ * @return true if the user confirmed
+ */
+ bool ConfirmClose();
void closeEvent(QCloseEvent* event) override;
private slots:
@@ -122,6 +129,7 @@ private:
GPUCommandListWidget* graphicsCommandsWidget;
QAction* actions_recent_files[max_recent_files_item];
+ bool confirm_before_closing;
};
#endif // _CITRA_QT_MAIN_HXX_
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 66689f398..371eb17a1 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -115,29 +115,24 @@ template<std::size_t position, std::size_t bits, typename T>
struct BitField
{
private:
- // This constructor might be considered ambiguous:
- // Would it initialize the storage or just the bitfield?
- // Hence, delete it. Use the assignment operator to set bitfield values!
- BitField(T val) = delete;
+ // We hide the copy assigment operator here, because the default copy
+ // assignment would copy the full storage value, rather than just the bits
+ // relevant to this particular bit field.
+ // We don't delete it because we want BitField to be trivially copyable.
+ BitField& operator=(const BitField&) = default;
public:
+ // This constructor and assignment operator might be considered ambiguous:
+ // Would they initialize the storage or just the bitfield?
+ // Hence, delete them. Use the Assign method to set bitfield values!
+ BitField(T val) = delete;
+ BitField& operator=(T val) = delete;
+
// Force default constructor to be created
// so that we can use this within unions
BitField() = default;
- // We explicitly delete the copy assigment operator here, because the
- // default copy assignment would copy the full storage value, rather than
- // just the bits relevant to this particular bit field.
- BitField& operator=(const BitField&) = delete;
-
- FORCE_INLINE BitField& operator=(T val)
- {
- Assign(val);
- return *this;
- }
-
- FORCE_INLINE operator T() const
- {
+ FORCE_INLINE operator T() const {
return Value();
}
@@ -145,8 +140,7 @@ public:
storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask());
}
- FORCE_INLINE T Value() const
- {
+ FORCE_INLINE T Value() const {
if (std::numeric_limits<T>::is_signed)
{
std::size_t shift = 8 * sizeof(T)-bits;
@@ -159,8 +153,7 @@ public:
}
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
- FORCE_INLINE bool ToBool() const
- {
+ FORCE_INLINE bool ToBool() const {
return Value() != 0;
}
@@ -176,8 +169,7 @@ private:
// Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
- FORCE_INLINE StorageType GetMask() const
- {
+ FORCE_INLINE StorageType GetMask() const {
return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position;
}
@@ -189,6 +181,10 @@ private:
static_assert(position < 8 * sizeof(T), "Invalid position");
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
static_assert(bits > 0, "Invalid number of bits");
- static_assert(std::is_standard_layout<T>::value, "Invalid base type");
+ static_assert(std::is_pod<T>::value, "Invalid base type");
};
#pragma pack()
+
+#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
+static_assert(std::is_trivially_copyable<BitField<0, 1, u32>>::value, "BitField must be trivially copyable");
+#endif
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp
index b69b05cb9..b2807354a 100644
--- a/src/common/emu_window.cpp
+++ b/src/common/emu_window.cpp
@@ -55,14 +55,14 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
(framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
touch_pressed = true;
- pad_state.touch = 1;
+ pad_state.touch.Assign(1);
}
void EmuWindow::TouchReleased() {
touch_pressed = false;
touch_x = 0;
touch_y = 0;
- pad_state.touch = 0;
+ pad_state.touch.Assign(0);
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 052c0ecd6..c3061479a 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -457,7 +457,7 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
if (virtual_name == "." || virtual_name == "..")
continue;
- unsigned ret_entries;
+ unsigned ret_entries = 0;
if (!callback(&ret_entries, directory, virtual_name)) {
callback_error = true;
break;
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index d186ba8f8..54291429a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -49,6 +49,7 @@ namespace Log {
SUB(Service, DSP) \
SUB(Service, HID) \
SUB(Service, SOC) \
+ SUB(Service, IR) \
SUB(Service, Y2R) \
CLS(HW) \
SUB(HW, Memory) \
@@ -58,6 +59,8 @@ namespace Log {
CLS(Render) \
SUB(Render, Software) \
SUB(Render, OpenGL) \
+ CLS(Audio) \
+ SUB(Audio, DSP) \
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 2d9323a7b..4b01805ae 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -64,6 +64,7 @@ enum class Class : ClassType {
Service_DSP, ///< The DSP (DSP control) service
Service_HID, ///< The HID (Human interface device) service
Service_SOC, ///< The SOC (Socket) service
+ Service_IR, ///< The IR service
Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
@@ -73,6 +74,8 @@ 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_DSP, ///< The HLE implementation of the DSP
Loader, ///< ROM loader
Count ///< Total number of logging classes
diff --git a/src/common/x64/emitter.cpp b/src/common/x64/emitter.cpp
index 939df210e..1dcf2416c 100644
--- a/src/common/x64/emitter.cpp
+++ b/src/common/x64/emitter.cpp
@@ -225,14 +225,14 @@ void OpArg::WriteVex(XEmitter* emit, X64Reg regOp1, X64Reg regOp2, int L, int pp
// do we need any VEX fields that only appear in the three-byte form?
if (X == 1 && B == 1 && W == 0 && mmmmm == 1)
{
- u8 RvvvvLpp = (R << 7) | (vvvv << 3) | (L << 1) | pp;
+ u8 RvvvvLpp = (R << 7) | (vvvv << 3) | (L << 2) | pp;
emit->Write8(0xC5);
emit->Write8(RvvvvLpp);
}
else
{
u8 RXBmmmmm = (R << 7) | (X << 6) | (B << 5) | mmmmm;
- u8 WvvvvLpp = (W << 7) | (vvvv << 3) | (L << 1) | pp;
+ u8 WvvvvLpp = (W << 7) | (vvvv << 3) | (L << 2) | pp;
emit->Write8(0xC4);
emit->Write8(RXBmmmmm);
emit->Write8(WvvvvLpp);
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index 0ba502200..a51416774 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -139,7 +139,7 @@ bool DiskFile::Close() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
-DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) {
+DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) : directory() {
// TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
// the root directory we set while opening the archive.
// For example, opening /../../usr/bin can give the emulated program your installed programs.
@@ -149,7 +149,9 @@ DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) {
bool DiskDirectory::Open() {
if (!FileUtil::IsDirectory(path))
return false;
- FileUtil::ScanDirectoryTree(path, directory);
+ unsigned size = FileUtil::ScanDirectoryTree(path, directory);
+ directory.size = size;
+ directory.isDirectory = true;
children_iterator = directory.children.begin();
return true;
}
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 0cfb43fc7..862643448 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -7,6 +7,8 @@
#include <utility>
#include <vector>
+#include "audio_core/audio_core.h"
+
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -107,7 +109,6 @@ struct MemoryArea {
static MemoryArea memory_areas[] = {
{SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory
{VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
- {DSP_RAM_VADDR, DSP_RAM_SIZE, "DSP RAM"}, // DSP memory
{TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory
};
@@ -133,6 +134,8 @@ void InitLegacyAddressSpace(Kernel::VMManager& address_space) {
auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR,
(u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom();
address_space.Reprotect(shared_page_vma, VMAPermission::Read);
+
+ AudioCore::AddAddressSpace(address_space);
}
} // namespace
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index d148efde2..16eb972fb 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -35,7 +35,7 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) {
process->codeset = std::move(code_set);
process->flags.raw = 0;
- process->flags.memory_region = MemoryRegion::APPLICATION;
+ process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
Memory::InitLegacyAddressSpace(process->vm_manager);
return process;
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index ea3abb5f6..0fce5988b 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -193,10 +193,10 @@ union ResultCode {
explicit ResultCode(u32 raw) : raw(raw) {}
ResultCode(ErrorDescription description_, ErrorModule module_,
ErrorSummary summary_, ErrorLevel level_) : raw(0) {
- description = description_;
- module = module_;
- summary = summary_;
- level = level_;
+ description.Assign(description_);
+ module.Assign(module_);
+ summary.Assign(summary_);
+ level.Assign(level_);
}
ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; }
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 633fe19eb..4c82a58e4 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -4,9 +4,10 @@
#include <algorithm>
+#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "common/file_util.h"
+#include "common/swap.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/file_backend.h"
@@ -293,8 +294,8 @@ ResultCode DeleteConfigNANDSaveFile() {
ResultCode UpdateConfigNANDSavegame() {
FileSys::Mode mode = {};
- mode.write_flag = 1;
- mode.create_flag = 1;
+ mode.write_flag.Assign(1);
+ mode.create_flag.Assign(1);
FileSys::Path path("config");
@@ -334,6 +335,18 @@ ResultCode FormatConfig() {
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);
+ 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);
@@ -344,9 +357,10 @@ ResultCode FormatConfig() {
res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
if (!res.IsSuccess()) return res;
- char16_t country_name_buffer[16][0x40] = {};
+ u16_le country_name_buffer[16][0x40] = {};
+ std::u16string region_name = Common::UTF8ToUTF16("Gensokyo");
for (size_t i = 0; i < 16; ++i) {
- Common::UTF8ToUTF16("Gensokyo").copy(country_name_buffer[i], 0x40);
+ std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]);
}
// 0x000B0001 - Localized names for the profile Country
res = CreateConfigInfoBlk(0x000B0001, sizeof(country_name_buffer), 0xE, country_name_buffer);
@@ -405,7 +419,7 @@ void Init() {
FileSys::Path config_path("config");
FileSys::Mode open_mode = {};
- open_mode.read_flag = 1;
+ open_mode.read_flag.Assign(1);
auto config_result = Service::FS::OpenFileFromArchive(*archive_result, config_path, open_mode);
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index f9f931f6d..15d3274ec 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "audio_core/hle/pipe.h"
+
#include "common/logging/log.h"
#include "core/hle/kernel/event.h"
@@ -14,17 +16,30 @@ namespace DSP_DSP {
static u32 read_pipe_count;
static Kernel::SharedPtr<Kernel::Event> semaphore_event;
-static Kernel::SharedPtr<Kernel::Event> interrupt_event;
-void SignalInterrupt() {
- // TODO(bunnei): This is just a stub, it does not do anything other than signal to the emulated
- // application that a DSP interrupt occurred, without specifying which one. Since we do not
- // emulate the DSP yet (and how it works is largely unknown), this is a work around to get games
- // that check the DSP interrupt signal event to run. We should figure out the different types of
- // DSP interrupts, and trigger them at the appropriate times.
+struct PairHash {
+ template <typename T, typename U>
+ std::size_t operator()(const std::pair<T, U> &x) const {
+ // TODO(yuriks): Replace with better hash combining function.
+ return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
+ }
+};
+
+/// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents
+static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events;
+
+// DSP Interrupts:
+// Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting
+// for an interrupt event. Immediately after this interrupt event, userland normally updates the
+// state in the next region and increments the relevant frame counter by two.
+void SignalAllInterrupts() {
+ // HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case.
+ for (auto& interrupt_event : interrupt_events)
+ interrupt_event.second->Signal();
+}
- if (interrupt_event != 0)
- interrupt_event->Signal();
+void SignalInterrupt(u32 interrupt, u32 channel) {
+ interrupt_events[std::make_pair(interrupt, channel)]->Signal();
}
/**
@@ -43,7 +58,7 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) {
cmd_buff[1] = 0; // No error
cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000);
- LOG_WARNING(Service_DSP, "(STUBBED) called with address 0x%08X", addr);
+ LOG_TRACE(Service_DSP, "addr=0x%08X", addr);
}
/**
@@ -121,8 +136,8 @@ static void FlushDataCache(Service::Interface* self) {
/**
* DSP_DSP::RegisterInterruptEvents service function
* Inputs:
- * 1 : Parameter 0 (purpose unknown)
- * 2 : Parameter 1 (purpose unknown)
+ * 1 : Interrupt Number
+ * 2 : Channel Number
* 4 : Interrupt event handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
@@ -130,22 +145,24 @@ static void FlushDataCache(Service::Interface* self) {
static void RegisterInterruptEvents(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 param0 = cmd_buff[1];
- u32 param1 = cmd_buff[2];
+ u32 interrupt = cmd_buff[1];
+ u32 channel = cmd_buff[2];
u32 event_handle = cmd_buff[4];
- auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
- if (evt != nullptr) {
- interrupt_event = evt;
- cmd_buff[1] = 0; // No error
+ if (event_handle) {
+ auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
+ if (evt) {
+ interrupt_events[std::make_pair(interrupt, channel)] = evt;
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ LOG_WARNING(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
+ } else {
+ cmd_buff[1] = -1;
+ LOG_ERROR(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
+ }
} else {
- LOG_ERROR(Service_DSP, "called with invalid handle=%08X", cmd_buff[4]);
-
- // TODO(yuriks): An error should be returned from SendSyncRequest, not in the cmdbuf
- cmd_buff[1] = -1;
+ interrupt_events.erase(std::make_pair(interrupt, channel));
+ LOG_WARNING(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
}
-
- LOG_WARNING(Service_DSP, "(STUBBED) called param0=%u, param1=%u, event_handle=0x%08X", param0, param1, event_handle);
}
/**
@@ -158,8 +175,6 @@ static void RegisterInterruptEvents(Service::Interface* self) {
static void SetSemaphore(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- SignalInterrupt();
-
cmd_buff[1] = 0; // No error
LOG_WARNING(Service_DSP, "(STUBBED) called");
@@ -168,9 +183,9 @@ static void SetSemaphore(Service::Interface* self) {
/**
* DSP_DSP::WriteProcessPipe service function
* Inputs:
- * 1 : Number
+ * 1 : Channel
* 2 : Size
- * 3 : (size <<14) | 0x402
+ * 3 : (size << 14) | 0x402
* 4 : Buffer
* Outputs:
* 0 : Return header
@@ -179,21 +194,42 @@ static void SetSemaphore(Service::Interface* self) {
static void WriteProcessPipe(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 number = cmd_buff[1];
+ u32 channel = cmd_buff[1];
u32 size = cmd_buff[2];
- u32 new_size = cmd_buff[3];
u32 buffer = cmd_buff[4];
+ if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) {
+ LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). channel=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], channel, size, buffer);
+ cmd_buff[1] = -1; // TODO
+ return;
+ }
+
+ if (!Memory::GetPointer(buffer)) {
+ LOG_ERROR(Service_DSP, "Invalid Buffer: channel=%u, size=0x%X, buffer=0x%08X", channel, size, buffer);
+ cmd_buff[1] = -1; // TODO
+ return;
+ }
+
+ std::vector<u8> message(size);
+
+ for (size_t i = 0; i < size; i++) {
+ message[i] = Memory::Read8(buffer + i);
+ }
+
+ DSP::HLE::PipeWrite(channel, message);
+
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_WARNING(Service_DSP, "(STUBBED) called number=%u, size=0x%X, new_size=0x%X, buffer=0x%08X",
- number, size, new_size, buffer);
+ LOG_TRACE(Service_DSP, "channel=%u, size=0x%X, buffer=0x%08X", channel, size, buffer);
}
/**
* DSP_DSP::ReadPipeIfPossible service function
+ * A pipe is a means of communication between the ARM11 and DSP that occurs on
+ * hardware by writing to/reading from the DSP registers at 0x10203000.
+ * Pipes are used for initialisation. See also DSP::HLE::PipeRead.
* Inputs:
- * 1 : Unknown
+ * 1 : Pipe Number
* 2 : Unknown
* 3 : Size in bytes of read (observed only lower half word used)
* 0x41 : Virtual address to read from DSP pipe to in memory
@@ -204,35 +240,25 @@ static void WriteProcessPipe(Service::Interface* self) {
static void ReadPipeIfPossible(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 unk1 = cmd_buff[1];
+ u32 pipe = cmd_buff[1];
u32 unk2 = cmd_buff[2];
u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
VAddr addr = cmd_buff[0x41];
- // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
- // TODO: Remove this hack :)
- static const std::array<u16, 16> canned_read_pipe = {{
- 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540,
- 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58
- }};
+ if (!Memory::GetPointer(addr)) {
+ LOG_ERROR(Service_DSP, "Invalid addr: pipe=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X", pipe, unk2, size, addr);
+ cmd_buff[1] = -1; // TODO
+ return;
+ }
- u32 initial_size = read_pipe_count;
+ std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
- for (unsigned offset = 0; offset < size; offset += sizeof(u16)) {
- if (read_pipe_count < canned_read_pipe.size()) {
- Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
- read_pipe_count++;
- } else {
- LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
- break;
- }
- }
+ Memory::WriteBlock(addr, response.data(), response.size());
cmd_buff[1] = 0; // No error
- cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
+ cmd_buff[2] = (u32)response.size();
- LOG_WARNING(Service_DSP, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X",
- unk1, unk2, size, addr);
+ LOG_TRACE(Service_DSP, "pipe=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X", pipe, unk2, size, addr);
}
/**
@@ -311,7 +337,6 @@ const Interface::FunctionInfo FunctionTable[] = {
Interface::Interface() {
semaphore_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
- interrupt_event = nullptr;
read_pipe_count = 0;
Register(FunctionTable);
@@ -319,7 +344,7 @@ Interface::Interface() {
Interface::~Interface() {
semaphore_event = nullptr;
- interrupt_event = nullptr;
+ interrupt_events.clear();
}
} // namespace
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index b6f611db5..32b89e9bb 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -23,7 +23,15 @@ public:
}
};
-/// Signals that a DSP interrupt has occurred to userland code
-void SignalInterrupt();
+/// Signal all audio related interrupts.
+void SignalAllInterrupts();
+
+/**
+ * Signal a specific audio related interrupt based on interrupt id and channel id.
+ * @param interrupt_id The interrupt id
+ * @param channel_id The channel id
+ * The significance of various values of interrupt_id and channel_id is not yet known.
+ */
+void SignalInterrupt(u32 interrupt_id, u32 channel_id);
} // namespace
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 98b11c798..5838b6d71 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -347,7 +347,7 @@ void SignalInterrupt(InterruptId interrupt_id) {
FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
if (info->is_dirty) {
SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
- info->is_dirty = false;
+ info->is_dirty.Assign(false);
}
}
}
@@ -499,7 +499,7 @@ static void SetLcdForceBlack(Service::Interface* self) {
// Since data is already zeroed, there is no need to explicitly set
// the color to black (all zero).
- data.is_enabled = enable_black;
+ data.is_enabled.Assign(enable_black);
LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_top), data.raw); // Top LCD
LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_bottom), data.raw); // Bottom LCD
@@ -521,7 +521,7 @@ static void TriggerCmdReqQueue(Service::Interface* self) {
ExecuteCommand(command_buffer->commands[i], thread_id);
// Indicates that command has completed
- command_buffer->number_commands = command_buffer->number_commands - 1;
+ command_buffer->number_commands.Assign(command_buffer->number_commands - 1);
}
}
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 0bed0ce36..11d7e69a1 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -105,7 +105,7 @@ void Update() {
bool pressed = false;
std::tie(touch_entry->x, touch_entry->y, pressed) = VideoCore::g_emu_window->GetTouchState();
- touch_entry->valid = pressed ? 1 : 0;
+ touch_entry->valid.Assign(pressed ? 1 : 0);
// TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
// supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being
diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp
index 0f8eed33a..c2121cb2e 100644
--- a/src/core/hle/service/ir/ir.cpp
+++ b/src/core/hle/service/ir/ir.cpp
@@ -2,20 +2,22 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/shared_memory.h"
+
#include "core/hle/service/service.h"
#include "core/hle/service/ir/ir.h"
#include "core/hle/service/ir/ir_rst.h"
#include "core/hle/service/ir/ir_u.h"
#include "core/hle/service/ir/ir_user.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-
namespace Service {
namespace IR {
static Kernel::SharedPtr<Kernel::Event> handle_event;
+static Kernel::SharedPtr<Kernel::Event> conn_status_event;
static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
+static Kernel::SharedPtr<Kernel::SharedMemory> transfer_shared_memory;
void GetHandles(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
@@ -26,6 +28,64 @@ void GetHandles(Service::Interface* self) {
cmd_buff[4] = Kernel::g_handle_table.Create(Service::IR::handle_event).MoveFrom();
}
+void InitializeIrNopShared(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 transfer_buff_size = cmd_buff[1];
+ u32 recv_buff_size = cmd_buff[2];
+ u32 unk1 = cmd_buff[3];
+ u32 send_buff_size = cmd_buff[4];
+ u32 unk2 = cmd_buff[5];
+ u8 baud_rate = cmd_buff[6] & 0xFF;
+ Handle handle = cmd_buff[8];
+
+ if(Kernel::g_handle_table.IsValid(handle)) {
+ transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle);
+ transfer_shared_memory->name = "IR:TransferSharedMemory";
+ }
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_IR, "(STUBBED) called, transfer_buff_size=%d, recv_buff_size=%d, "
+ "unk1=%d, send_buff_size=%d, unk2=%d, baud_rate=%u, handle=0x%08X",
+ transfer_buff_size, recv_buff_size, unk1, send_buff_size, unk2, baud_rate, handle);
+}
+
+void RequireConnection(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ conn_status_event->Signal();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_IR, "(STUBBED) called");
+}
+
+void Disconnect(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_IR, "(STUBBED) called");
+}
+
+void GetConnectionStatusEvent(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom();
+
+ LOG_WARNING(Service_IR, "(STUBBED) called");
+}
+
+void FinalizeIrNop(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_IR, "(STUBBED) called");
+}
+
void Init() {
using namespace Kernel;
@@ -35,15 +95,19 @@ void Init() {
using Kernel::MemoryPermission;
shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory");
+ Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory");
+ transfer_shared_memory = nullptr;
// Create event handle(s)
handle_event = Event::Create(RESETTYPE_ONESHOT, "IR:HandleEvent");
+ conn_status_event = Event::Create(RESETTYPE_ONESHOT, "IR:ConnectionStatusEvent");
}
void Shutdown() {
+ transfer_shared_memory = nullptr;
shared_memory = nullptr;
handle_event = nullptr;
+ conn_status_event = nullptr;
}
} // namespace IR
diff --git a/src/core/hle/service/ir/ir.h b/src/core/hle/service/ir/ir.h
index 3e107a8fe..72d44ce60 100644
--- a/src/core/hle/service/ir/ir.h
+++ b/src/core/hle/service/ir/ir.h
@@ -20,6 +20,53 @@ namespace IR {
*/
void GetHandles(Interface* self);
+/**
+ * IR::InitializeIrNopShared service function
+ * Inputs:
+ * 1 : Size of transfer buffer
+ * 2 : Recv buffer size
+ * 3 : unknown
+ * 4 : Send buffer size
+ * 5 : unknown
+ * 6 : BaudRate (u8)
+ * 7 : 0
+ * 8 : Handle of transfer shared memory
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void InitializeIrNopShared(Interface* self);
+
+/**
+ * IR::FinalizeIrNop service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void FinalizeIrNop(Interface* self);
+
+/**
+ * IR::GetConnectionStatusEvent service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Connection Status Event handle
+ */
+void GetConnectionStatusEvent(Interface* self);
+
+/**
+ * IR::Disconnect service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void Disconnect(Interface* self);
+
+/**
+ * IR::RequireConnection service function
+ * Inputs:
+ * 1 : unknown (u8), looks like always 1
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void RequireConnection(Interface* self);
+
/// Initialize IR service
void Init();
diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp
index 0a98e5801..06a601029 100644
--- a/src/core/hle/service/ir/ir_user.cpp
+++ b/src/core/hle/service/ir/ir_user.cpp
@@ -2,26 +2,39 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/service/ir/ir.h"
#include "core/hle/service/ir/ir_user.h"
namespace Service {
namespace IR {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010182, nullptr, "InitializeIrNop"},
- {0x00020000, nullptr, "FinalizeIrNop"},
- {0x00030000, nullptr, "ClearReceiveBuffer"},
- {0x00040000, nullptr, "ClearSendBuffer"},
- {0x00060040, nullptr, "RequireConnection"},
- {0x00090000, nullptr, "Disconnect"},
- {0x000A0000, nullptr, "GetReceiveEvent"},
- {0x000B0000, nullptr, "GetSendEvent"},
- {0x000C0000, nullptr, "GetConnectionStatusEvent"},
- {0x000D0042, nullptr, "SendIrNop"},
- {0x000E0042, nullptr, "SendIrNopLarge"},
- {0x00180182, nullptr, "InitializeIrNopShared"},
- {0x00190040, nullptr, "ReleaseReceivedData"},
- {0x001A0040, nullptr, "SetOwnMachineId"},
+ {0x00010182, nullptr, "InitializeIrNop"},
+ {0x00020000, FinalizeIrNop, "FinalizeIrNop"},
+ {0x00030000, nullptr, "ClearReceiveBuffer"},
+ {0x00040000, nullptr, "ClearSendBuffer"},
+ {0x000500C0, nullptr, "WaitConnection"},
+ {0x00060040, RequireConnection, "RequireConnection"},
+ {0x000702C0, nullptr, "AutoConnection"},
+ {0x00080000, nullptr, "AnyConnection"},
+ {0x00090000, Disconnect, "Disconnect"},
+ {0x000A0000, nullptr, "GetReceiveEvent"},
+ {0x000B0000, nullptr, "GetSendEvent"},
+ {0x000C0000, GetConnectionStatusEvent, "GetConnectionStatusEvent"},
+ {0x000D0042, nullptr, "SendIrNop"},
+ {0x000E0042, nullptr, "SendIrNopLarge"},
+ {0x000F0040, nullptr, "ReceiveIrnop"},
+ {0x00100042, nullptr, "ReceiveIrnopLarge"},
+ {0x00110040, nullptr, "GetLatestReceiveErrorResult"},
+ {0x00120040, nullptr, "GetLatestSendErrorResult"},
+ {0x00130000, nullptr, "GetConnectionStatus"},
+ {0x00140000, nullptr, "GetTryingToConnectStatus"},
+ {0x00150000, nullptr, "GetReceiveSizeFreeAndUsed"},
+ {0x00160000, nullptr, "GetSendSizeFreeAndUsed"},
+ {0x00170000, nullptr, "GetConnectionRole"},
+ {0x00180182, InitializeIrNopShared, "InitializeIrNopShared"},
+ {0x00190040, nullptr, "ReleaseReceivedData"},
+ {0x001A0040, nullptr, "SetOwnMachineId"},
};
IR_User_Interface::IR_User_Interface() {
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 22c1093ff..6bdee4d9e 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -110,8 +110,8 @@ void Init() {
FileSys::Path gamecoin_path("gamecoin.dat");
FileSys::Mode open_mode = {};
- open_mode.write_flag = 1;
- open_mode.create_flag = 1;
+ open_mode.write_flag.Assign(1);
+ open_mode.create_flag.Assign(1);
// Open the file and write the default gamecoin information
auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);
if (gamecoin_result.Succeeded()) {
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 822b093f4..e603bf794 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -178,17 +178,17 @@ struct CTRPollFD {
static Events TranslateTo3DS(u32 input_event) {
Events ev = {};
if (input_event & POLLIN)
- ev.pollin = 1;
+ ev.pollin.Assign(1);
if (input_event & POLLPRI)
- ev.pollpri = 1;
+ ev.pollpri.Assign(1);
if (input_event & POLLHUP)
- ev.pollhup = 1;
+ ev.pollhup.Assign(1);
if (input_event & POLLERR)
- ev.pollerr = 1;
+ ev.pollerr.Assign(1);
if (input_event & POLLOUT)
- ev.pollout = 1;
+ ev.pollout.Assign(1);
if (input_event & POLLNVAL)
- ev.pollnval = 1;
+ ev.pollnval.Assign(1);
return ev;
}
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index ba21e06d5..7a39b101d 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -486,6 +486,7 @@ static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point
}
switch (processor_id) {
+ case THREADPROCESSORID_ALL:
case THREADPROCESSORID_DEFAULT:
case THREADPROCESSORID_0:
case THREADPROCESSORID_1:
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 4bd3a632d..5312baa83 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -17,7 +17,6 @@
#include "core/core_timing.h"
#include "core/hle/service/gsp_gpu.h"
-#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/hid/hid.h"
#include "core/hw/hw.h"
@@ -146,8 +145,8 @@ inline void Write(u32 addr, const T data) {
// Reset "trigger" flag and set the "finish" flag
// NOTE: This was confirmed to happen on hardware even if "address_start" is zero.
- config.trigger = 0;
- config.finished = 1;
+ config.trigger.Assign(0);
+ config.finished.Assign(1);
}
break;
}
@@ -414,11 +413,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
- // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
- // until we can emulate DSP interrupts, this is probably the only reasonable place to do
- // this. Certain games expect this to be periodically signaled.
- DSP_DSP::SignalInterrupt();
-
// Check for user input updates
Service::HID::Update();
@@ -444,16 +438,16 @@ void Init() {
framebuffer_sub.address_left1 = 0x1848F000;
framebuffer_sub.address_left2 = 0x184C7800;
- framebuffer_top.width = 240;
- framebuffer_top.height = 400;
+ framebuffer_top.width.Assign(240);
+ framebuffer_top.height.Assign(400);
framebuffer_top.stride = 3 * 240;
- framebuffer_top.color_format = Regs::PixelFormat::RGB8;
+ framebuffer_top.color_format.Assign(Regs::PixelFormat::RGB8);
framebuffer_top.active_fb = 0;
- framebuffer_sub.width = 240;
- framebuffer_sub.height = 320;
+ framebuffer_sub.width.Assign(240);
+ framebuffer_sub.height.Assign(320);
framebuffer_sub.stride = 3 * 240;
- framebuffer_sub.color_format = Regs::PixelFormat::RGB8;
+ framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8);
framebuffer_sub.active_fb = 0;
last_skip_frame = false;
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 7e9c56538..b62ebf69e 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -2,9 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "audio_core/audio_core.h"
+
#include "core/core.h"
#include "core/core_timing.h"
#include "core/system.h"
+#include "core/gdbstub/gdbstub.h"
#include "core/hw/hw.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
@@ -12,8 +15,6 @@
#include "video_core/video_core.h"
-#include "core/gdbstub/gdbstub.h"
-
namespace System {
void Init(EmuWindow* emu_window) {
@@ -24,11 +25,13 @@ void Init(EmuWindow* emu_window) {
Kernel::Init();
HLE::Init();
VideoCore::Init(emu_window);
+ AudioCore::Init();
GDBStub::Init();
}
void Shutdown() {
GDBStub::Shutdown();
+ AudioCore::Shutdown();
VideoCore::Shutdown();
HLE::Shutdown();
Kernel::Shutdown();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 4b5d298f3..76cfd4f7d 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -33,6 +33,7 @@ set(HEADERS
command_processor.h
gpu_debugger.h
pica.h
+ pica_state.h
pica_types.h
primitive_assembly.h
rasterizer.h
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp
index a385589d2..3d503486e 100644
--- a/src/video_core/clipper.cpp
+++ b/src/video_core/clipper.cpp
@@ -6,6 +6,7 @@
#include "video_core/clipper.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/rasterizer.h"
#include "video_core/shader/shader_interpreter.h"
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 5dfedfe31..2f92be661 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -15,6 +15,7 @@
#include "video_core/clipper.h"
#include "video_core/command_processor.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/primitive_assembly.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -73,6 +74,14 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
break;
+ case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
+ if (regs.vs_default_attributes_setup.index == 15) {
+ // Reset immediate primitive state
+ g_state.immediate.primitive_assembler.Reconfigure(regs.triangle_topology);
+ g_state.immediate.attribute_id = 0;
+ }
+ break;
+
// Load default vertex input attributes
case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233):
case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234):
@@ -108,11 +117,48 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
attribute.w.ToFloat32());
// TODO: Verify that this actually modifies the register!
- setup.index = setup.index + 1;
+ if (setup.index < 15) {
+ setup.index++;
+ } else {
+ // Put each attribute into an immediate input buffer.
+ // When all specified immediate attributes are present, the Vertex Shader is invoked and everything is
+ // sent to the primitive assembler.
+
+ auto& immediate_input = g_state.immediate.input;
+ auto& immediate_attribute_id = g_state.immediate.attribute_id;
+ const auto& attribute_config = regs.vertex_attributes;
+
+ immediate_input.attr[immediate_attribute_id++] = attribute;
+
+ if (immediate_attribute_id >= attribute_config.GetNumTotalAttributes()) {
+ immediate_attribute_id = 0;
+
+ Shader::UnitState<false> shader_unit;
+ Shader::Setup(shader_unit);
+
+ // Send to vertex shader
+ Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, attribute_config.GetNumTotalAttributes());
+
+ // Send to renderer
+ using Pica::Shader::OutputVertex;
+ auto AddTriangle = [](const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2) {
+ VideoCore::g_renderer->rasterizer->AddTriangle(v0, v1, v2);
+ };
+
+ g_state.immediate.primitive_assembler.SubmitVertex(output, AddTriangle);
+ }
+ }
}
break;
}
+ case PICA_REG_INDEX(gpu_mode):
+ if (regs.gpu_mode == Regs::GPUMode::Configuring && regs.vs_default_attributes_setup.index == 15) {
+ // Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
+ VideoCore::g_renderer->rasterizer->DrawTriangles();
+ }
+ break;
+
case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c):
case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d):
{
@@ -157,15 +203,25 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// TODO: What happens if a loader overwrites a previous one's data?
for (unsigned component = 0; component < loader_config.component_count; ++component) {
- if (component >= 12)
+ if (component >= 12) {
LOG_ERROR(HW_GPU, "Overflow in the vertex attribute loader %u trying to load component %u", loader, component);
+ continue;
+ }
+
u32 attribute_index = loader_config.GetComponent(component);
- vertex_attribute_sources[attribute_index] = load_address;
- vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
- vertex_attribute_formats[attribute_index] = attribute_config.GetFormat(attribute_index);
- vertex_attribute_elements[attribute_index] = attribute_config.GetNumElements(attribute_index);
- vertex_attribute_element_size[attribute_index] = attribute_config.GetElementSizeInBytes(attribute_index);
- load_address += attribute_config.GetStride(attribute_index);
+ if (attribute_index < 12) {
+ vertex_attribute_sources[attribute_index] = load_address;
+ vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
+ vertex_attribute_formats[attribute_index] = attribute_config.GetFormat(attribute_index);
+ vertex_attribute_elements[attribute_index] = attribute_config.GetNumElements(attribute_index);
+ vertex_attribute_element_size[attribute_index] = attribute_config.GetElementSizeInBytes(attribute_index);
+ load_address += attribute_config.GetStride(attribute_index);
+ } else if (attribute_index < 16) {
+ // Attribute ids 12, 13, 14 and 15 signify 4, 8, 12 and 16-byte paddings, respectively
+ load_address += (attribute_index - 11) * 4;
+ } else {
+ UNREACHABLE(); // This is truly unreachable due to the number of bits for each component
+ }
}
}
@@ -429,7 +485,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
uniform.w.ToFloat32());
// TODO: Verify that this actually modifies the register!
- uniform_setup.index = uniform_setup.index + 1;
+ uniform_setup.index.Assign(uniform_setup.index + 1);
}
break;
}
@@ -478,7 +534,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
ASSERT_MSG(lut_config.index < 256, "lut_config.index exceeded maximum value of 255!");
g_state.lighting.luts[lut_config.type][lut_config.index].raw = value;
- lut_config.index = lut_config.index + 1;
+ lut_config.index.Assign(lut_config.index + 1);
break;
}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 4f66dbd65..6e21caa78 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -28,6 +28,7 @@
#include "core/settings.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/renderer_base.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
@@ -113,7 +114,7 @@ void GeometryDumper::Dump() {
}
-void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const State::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes)
+void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes)
{
struct StuffToWrite {
u8* pointer;
@@ -201,11 +202,11 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
if (it == output_info_table.end()) {
output_info_table.emplace_back();
- output_info_table.back().type = type;
- output_info_table.back().component_mask = component_mask;
- output_info_table.back().id = i;
+ output_info_table.back().type.Assign(type);
+ output_info_table.back().component_mask.Assign(component_mask);
+ output_info_table.back().id.Assign(i);
} else {
- it->component_mask = it->component_mask | component_mask;
+ it->component_mask.Assign(it->component_mask | component_mask);
}
} catch (const std::out_of_range& ) {
DEBUG_ASSERT_MSG(false, "Unknown output attribute mapping");
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index 85762f5b4..795160a32 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -17,6 +17,7 @@
#include "core/tracer/recorder.h"
#include "video_core/pica.h"
+#include "video_core/shader/shader.h"
namespace Pica {
@@ -182,7 +183,7 @@ private:
};
void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
- const State::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes);
+ const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes);
// Utility class to log Pica commands.
diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp
index d7b31384a..32ad72674 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -6,6 +6,7 @@
#include <unordered_map>
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/shader/shader.h"
namespace Pica {
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 9077b1725..2e0c33201 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -1089,7 +1089,16 @@ struct Regs {
}
} command_buffer;
- INSERT_PADDING_WORDS(0x20);
+ INSERT_PADDING_WORDS(0x07);
+
+ enum class GPUMode : u32 {
+ Drawing = 0,
+ Configuring = 1
+ };
+
+ GPUMode gpu_mode;
+
+ INSERT_PADDING_WORDS(0x18);
enum class TriangleTopology : u32 {
List = 0,
@@ -1278,6 +1287,7 @@ ASSERT_REG_POSITION(trigger_draw, 0x22e);
ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f);
ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232);
ASSERT_REG_POSITION(command_buffer, 0x238);
+ASSERT_REG_POSITION(gpu_mode, 0x245);
ASSERT_REG_POSITION(triangle_topology, 0x25e);
ASSERT_REG_POSITION(restart_primitive, 0x25f);
ASSERT_REG_POSITION(gs, 0x280);
@@ -1292,64 +1302,10 @@ static_assert(sizeof(Regs::ShaderConfig) == 0x30 * sizeof(u32), "ShaderConfig st
static_assert(sizeof(Regs) <= 0x300 * sizeof(u32), "Register set structure larger than it should be");
static_assert(sizeof(Regs) >= 0x300 * sizeof(u32), "Register set structure smaller than it should be");
-/// Struct used to describe current Pica state
-struct State {
- /// Pica registers
- Regs regs;
-
- /// 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.
- Math::Vec4<float24> MEMORY_ALIGNED16(f[96]);
-
- std::array<bool, 16> b;
- std::array<Math::Vec4<u8>, 4> i;
- } uniforms;
-
- Math::Vec4<float24> default_attributes[16];
-
- std::array<u32, 1024> program_code;
- std::array<u32, 1024> swizzle_data;
- };
-
- ShaderSetup vs;
- ShaderSetup gs;
-
- struct {
- union LutEntry {
- // Used for raw access
- u32 raw;
-
- // LUT value, encoded as 12-bit fixed point, with 12 fraction bits
- BitField< 0, 12, u32> value;
-
- // Used by HW for efficient interpolation, Citra does not use these
- BitField<12, 12, u32> difference;
-
- float ToFloat() {
- return static_cast<float>(value) / 4095.f;
- }
- };
-
- std::array<std::array<LutEntry, 256>, 24> luts;
- } lighting;
-
- /// Current Pica command list
- struct {
- const u32* head_ptr;
- const u32* current_ptr;
- u32 length;
- } cmd_list;
-};
-
/// Initialize Pica state
void Init();
/// Shutdown Pica state
void Shutdown();
-extern State g_state; ///< Current Pica state
-
} // namespace
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
new file mode 100644
index 000000000..c7616bc55
--- /dev/null
+++ b/src/video_core/pica_state.h
@@ -0,0 +1,60 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "video_core/pica.h"
+#include "video_core/primitive_assembly.h"
+#include "video_core/shader/shader.h"
+
+namespace Pica {
+
+/// Struct used to describe current Pica state
+struct State {
+ /// Pica registers
+ Regs regs;
+
+ Shader::ShaderSetup vs;
+ Shader::ShaderSetup gs;
+
+ struct {
+ union LutEntry {
+ // Used for raw access
+ u32 raw;
+
+ // LUT value, encoded as 12-bit fixed point, with 12 fraction bits
+ BitField< 0, 12, u32> value;
+
+ // Used by HW for efficient interpolation, Citra does not use these
+ BitField<12, 12, u32> difference;
+
+ float ToFloat() {
+ return static_cast<float>(value) / 4095.f;
+ }
+ };
+
+ std::array<std::array<LutEntry, 256>, 24> luts;
+ } lighting;
+
+ /// Current Pica command list
+ struct {
+ const u32* head_ptr;
+ const u32* current_ptr;
+ u32 length;
+ } cmd_list;
+
+ /// Struct used to describe immediate mode rendering state
+ struct ImmediateModeState {
+ Shader::InputVertex input;
+ // This is constructed with a dummy triangle topology
+ PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
+ int attribute_id = 0;
+
+ ImmediateModeState() : primitive_assembler(Regs::TriangleTopology::List) {}
+ } immediate;
+};
+
+extern State g_state; ///< Current Pica state
+
+} // namespace
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp
index d5a0a96a4..0061690f1 100644
--- a/src/video_core/primitive_assembly.cpp
+++ b/src/video_core/primitive_assembly.cpp
@@ -53,6 +53,18 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl
}
}
+template<typename VertexType>
+void PrimitiveAssembler<VertexType>::Reset() {
+ buffer_index = 0;
+ strip_ready = false;
+}
+
+template<typename VertexType>
+void PrimitiveAssembler<VertexType>::Reconfigure(Regs::TriangleTopology topology) {
+ Reset();
+ this->topology = topology;
+}
+
// explicitly instantiate use cases
template
struct PrimitiveAssembler<Shader::OutputVertex>;
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h
index 52d0ec8ff..cc6e5fde5 100644
--- a/src/video_core/primitive_assembly.h
+++ b/src/video_core/primitive_assembly.h
@@ -30,6 +30,16 @@ struct PrimitiveAssembler {
*/
void SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler);
+ /**
+ * Resets the internal state of the PrimitiveAssembler.
+ */
+ void Reset();
+
+ /**
+ * Reconfigures the PrimitiveAssembler to use a different triangle topology.
+ */
+ void Reconfigure(Regs::TriangleTopology topology);
+
private:
Regs::TriangleTopology topology;
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index ecfdbc9e8..dd1604a38 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -15,6 +15,7 @@
#include "core/hw/gpu.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/rasterizer.h"
#include "video_core/utils.h"
#include "video_core/debug_utils/debug_utils.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index b7d19bf94..0f864b617 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -19,6 +19,7 @@
#include "core/hw/gpu.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/utils.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index fef5f5331..fc85aa3ff 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -14,6 +14,7 @@
#include "common/hash.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index ab4b6c7b1..08e4d0b54 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -48,6 +48,10 @@ OpenGLState::OpenGLState() {
texture_unit.sampler = 0;
}
+ for (auto& lut : lighting_lut) {
+ lut.texture_1d = 0;
+ }
+
draw.framebuffer = 0;
draw.vertex_array = 0;
draw.vertex_buffer = 0;
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index 44c234ed8..5e8930476 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -14,6 +14,7 @@
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/video_core.h"
#include "shader.h"
@@ -145,7 +146,7 @@ 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 State::ShaderSetup& setup) {
+DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup) {
UnitState<true> state;
state.program_counter = config.main_offset;
diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h
index f068cd93f..1be4e3734 100644
--- a/src/video_core/shader/shader.h
+++ b/src/video_core/shader/shader.h
@@ -77,6 +77,22 @@ 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.
+ Math::Vec4<float24> MEMORY_ALIGNED16(f[96]);
+
+ std::array<bool, 16> b;
+ std::array<Math::Vec4<u8>, 4> i;
+ } uniforms;
+
+ Math::Vec4<float24> default_attributes[16];
+
+ std::array<u32, 1024> program_code;
+ std::array<u32, 1024> swizzle_data;
+};
// Helper structure used to keep track of data useful for inspection of shader emulation
template<bool full_debugging>
@@ -347,7 +363,7 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
* @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 State::ShaderSetup& setup);
+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 aeced71b0..79fcc56b9 100644
--- a/src/video_core/shader/shader_interpreter.cpp
+++ b/src/video_core/shader/shader_interpreter.cpp
@@ -7,6 +7,7 @@
#include <nihstro/shader_bytecode.h>
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/shader/shader.h"
#include "video_core/shader/shader_interpreter.h"
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp
index 4249675a5..5083d7e54 100644
--- a/src/video_core/shader/shader_jit_x64.cpp
+++ b/src/video_core/shader/shader_jit_x64.cpp
@@ -11,6 +11,8 @@
#include "shader.h"
#include "shader_jit_x64.h"
+#include "video_core/pica_state.h"
+
namespace Pica {
namespace Shader {