diff options
Diffstat (limited to 'src')
184 files changed, 5736 insertions, 1780 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..376436c29 --- /dev/null +++ b/src/audio_core/hle/dsp.h @@ -0,0 +1,539 @@ +// 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: +// +// # 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 +// +// #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe. +// See also: DSP::HLE::PipeRead. +// +// 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; + + struct { + INSERT_PADDING_DSPWORDS(0x100); + } unknown10; + + struct { + INSERT_PADDING_DSPWORDS(0xC0); + } unknown11; + + struct { + INSERT_PADDING_DSPWORDS(0x180); + } unknown12; + + struct { + INSERT_PADDING_DSPWORDS(0xA); + } unknown13; + + struct { + INSERT_PADDING_DSPWORDS(0x13A3); + } unknown14; + + u16_le frame_counter; +}; +ASSERT_DSP_STRUCT(SharedMemory, 0x8000); + +// Structures must have an offset that is a multiple of two. +static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); +static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); + +#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..9381883b4 --- /dev/null +++ b/src/audio_core/hle/pipe.cpp @@ -0,0 +1,159 @@ +// 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/dsp.h" +#include "audio_core/hle/pipe.h" + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" + +namespace DSP { +namespace HLE { + +static DspState dsp_state = DspState::Off; + +static std::array<std::vector<u8>, static_cast<size_t>(DspPipe::DspPipe_MAX)> pipe_data; + +void ResetPipes() { + for (auto& data : pipe_data) { + data.clear(); + } + dsp_state = DspState::Off; +} + +std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) { + if (pipe_number >= DspPipe::DspPipe_MAX) { + LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number); + return {}; + } + + std::vector<u8>& data = pipe_data[static_cast<size_t>(pipe_number)]; + + if (length > data.size()) { + LOG_WARNING(Audio_DSP, "pipe_number = %u is out of data, application requested read of %u but %zu remain", + pipe_number, length, data.size()); + length = data.size(); + } + + if (length == 0) + return {}; + + std::vector<u8> ret(data.begin(), data.begin() + length); + data.erase(data.begin(), data.begin() + length); + return ret; +} + +size_t GetPipeReadableSize(DspPipe pipe_number) { + if (pipe_number >= DspPipe::DspPipe_MAX) { + LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number); + return 0; + } + + return pipe_data[static_cast<size_t>(pipe_number)].size(); +} + +static void WriteU16(DspPipe pipe_number, u16 value) { + std::vector<u8>& data = pipe_data[static_cast<size_t>(pipe_number)]; + // Little endian + data.emplace_back(value & 0xFF); + data.emplace_back(value >> 8); +} + +static void AudioPipeWriteStructAddresses() { + // These struct addresses are DSP dram addresses. + // See also: DSP_DSP::ConvertProcessAddressFromDspDram + static const std::array<u16, 15> struct_addresses = { + 0x8000 + offsetof(SharedMemory, frame_counter) / 2, + 0x8000 + offsetof(SharedMemory, source_configurations) / 2, + 0x8000 + offsetof(SharedMemory, source_statuses) / 2, + 0x8000 + offsetof(SharedMemory, adpcm_coefficients) / 2, + 0x8000 + offsetof(SharedMemory, dsp_configuration) / 2, + 0x8000 + offsetof(SharedMemory, dsp_status) / 2, + 0x8000 + offsetof(SharedMemory, final_samples) / 2, + 0x8000 + offsetof(SharedMemory, intermediate_mix_samples) / 2, + 0x8000 + offsetof(SharedMemory, compressor) / 2, + 0x8000 + offsetof(SharedMemory, dsp_debug) / 2, + 0x8000 + offsetof(SharedMemory, unknown10) / 2, + 0x8000 + offsetof(SharedMemory, unknown11) / 2, + 0x8000 + offsetof(SharedMemory, unknown12) / 2, + 0x8000 + offsetof(SharedMemory, unknown13) / 2, + 0x8000 + offsetof(SharedMemory, unknown14) / 2 + }; + + // Begin with a u16 denoting the number of structs. + WriteU16(DspPipe::Audio, struct_addresses.size()); + // Then write the struct addresses. + for (u16 addr : struct_addresses) { + WriteU16(DspPipe::Audio, addr); + } +} + +void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { + switch (pipe_number) { + case DspPipe::Audio: { + if (buffer.size() != 4) { + LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", buffer.size()); + return; + } + + enum class StateChange { + Initalize = 0, + Shutdown = 1, + Wakeup = 2, + Sleep = 3 + }; + + // The difference between Initialize and Wakeup is that Input state is maintained + // when sleeping but isn't when turning it off and on again. (TODO: Implement this.) + // Waking up from sleep garbles some of the structs in the memory region. (TODO: + // Implement this.) Applications store away the state of these structs before + // sleeping and reset it back after wakeup on behalf of the DSP. + + switch (static_cast<StateChange>(buffer[0])) { + case StateChange::Initalize: + LOG_INFO(Audio_DSP, "Application has requested initialization of DSP hardware"); + ResetPipes(); + AudioPipeWriteStructAddresses(); + dsp_state = DspState::On; + break; + case StateChange::Shutdown: + LOG_INFO(Audio_DSP, "Application has requested shutdown of DSP hardware"); + dsp_state = DspState::Off; + break; + case StateChange::Wakeup: + LOG_INFO(Audio_DSP, "Application has requested wakeup of DSP hardware"); + ResetPipes(); + AudioPipeWriteStructAddresses(); + dsp_state = DspState::On; + break; + case StateChange::Sleep: + LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware"); + UNIMPLEMENTED(); + dsp_state = DspState::Sleeping; + break; + default: + LOG_ERROR(Audio_DSP, "Application has requested unknown state transition of DSP hardware %hhu", buffer[0]); + dsp_state = DspState::Off; + break; + } + + return; + } + default: + LOG_CRITICAL(Audio_DSP, "pipe_number = %u unimplemented", pipe_number); + UNIMPLEMENTED(); + return; + } +} + +DspState GetDspState() { + return dsp_state; +} + +} // 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..382d35e87 --- /dev/null +++ b/src/audio_core/hle/pipe.h @@ -0,0 +1,57 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstddef> +#include <vector> + +#include "common/common_types.h" + +namespace DSP { +namespace HLE { + +/// Reset the pipes by setting pipe positions back to the beginning. +void ResetPipes(); + +enum class DspPipe { + Debug = 0, + Dma = 1, + Audio = 2, + Binary = 3, + DspPipe_MAX +}; + +/** + * Read a DSP pipe. + * @param pipe_number The Pipe ID + * @param length How much data to request. + * @return The data read from the pipe. The size of this vector can be less than the length requested. + */ +std::vector<u8> PipeRead(DspPipe pipe_number, u32 length); + +/** + * How much data is left in pipe + * @param pipe_number The Pipe ID + * @return The amount of data remaning in the pipe. This is the maximum length PipeRead will return. + */ +size_t GetPipeReadableSize(DspPipe pipe_number); + +/** + * Write to a DSP pipe. + * @param pipe_number The Pipe ID + * @param buffer The data to write to the pipe. + */ +void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer); + +enum class DspState { + Off, + On, + Sleeping +}; +/// Get the state of the DSP +DspState GetDspState(); + +} // 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, ¤t_size.first, ¤t_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/callstack.cpp b/src/citra_qt/debugger/callstack.cpp index d45eed179..793944639 100644 --- a/src/citra_qt/debugger/callstack.cpp +++ b/src/citra_qt/debugger/callstack.cpp @@ -29,18 +29,16 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent) void CallstackWidget::OnDebugModeEntered() { - ARM_Interface* app_core = Core::g_app_core; - - u32 sp = app_core->GetReg(13); //stack pointer - u32 ret_addr, call_addr, func_addr; + // Stack pointer + const u32 sp = Core::g_app_core->GetReg(13); Clear(); int counter = 0; for (u32 addr = 0x10000000; addr >= sp; addr -= 4) { - ret_addr = Memory::Read32(addr); - call_addr = ret_addr - 4; //get call address??? + const u32 ret_addr = Memory::Read32(addr); + const u32 call_addr = ret_addr - 4; //get call address??? if (Memory::GetPointer(call_addr) == nullptr) break; @@ -60,7 +58,7 @@ void CallstackWidget::OnDebugModeEntered() // Pre-compute the left-shift and the prefetch offset i_offset <<= 2; i_offset += 8; - func_addr = call_addr + i_offset; + const u32 func_addr = call_addr + i_offset; callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); 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..4b676f1b1 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; @@ -496,8 +498,8 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d // Reload widget state for (int attr = 0; attr < num_attributes; ++attr) { unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr); - input_data_mapping[source_attr]->setText(QString("-> v%1").arg(attr)); - input_data_container[source_attr]->setVisible(true); + input_data_mapping[attr]->setText(QString("-> v%1").arg(source_attr)); + input_data_container[attr]->setVisible(true); } // Only show input attributes which are used as input to the shader for (unsigned int attr = num_attributes; attr < 16; ++attr) { diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp index 6100d67c5..1bd0bfebc 100644 --- a/src/citra_qt/debugger/registers.cpp +++ b/src/citra_qt/debugger/registers.cpp @@ -59,16 +59,14 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) { } void RegistersWidget::OnDebugModeEntered() { - ARM_Interface* app_core = Core::g_app_core; - - if (app_core == nullptr) + if (!Core::g_app_core) return; for (int i = 0; i < core_registers->childCount(); ++i) - core_registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0'))); + core_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0'))); for (int i = 0; i < vfp_registers->childCount(); ++i) - vfp_registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetVFPReg(i), 8, 16, QLatin1Char('0'))); + vfp_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0'))); UpdateCPSRValues(); UpdateVFPSystemRegisterValues(); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index d6c27f0df..57adbc136 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <clocale> #include <thread> #include <QDesktopWidget> @@ -171,6 +172,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())); @@ -208,7 +211,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr) show(); - game_list->PopulateAsync(settings.value("gameListRootDir").toString(), settings.value("gameListDeepScan").toBool()); + game_list->PopulateAsync(settings.value("gameListRootDir", ".").toString(), settings.value("gameListDeepScan", false).toBool()); QStringList args = QApplication::arguments(); if (args.length() >= 2) { @@ -246,22 +249,73 @@ void GMainWindow::OnDisplayTitleBars(bool show) } } -void GMainWindow::BootGame(const std::string& filename) { - LOG_INFO(Frontend, "Citra starting..."); - +bool GMainWindow::InitializeSystem() { // Shutdown previous session if the emu thread is still active... if (emu_thread != nullptr) ShutdownGame(); // Initialize the core emulation - System::Init(render_window); + System::Result system_result = System::Init(render_window); + if (System::Result::Success != system_result) { + switch (system_result) { + case System::Result::ErrorInitVideoCore: + QMessageBox::critical(this, tr("Error while starting Citra!"), + tr("Failed to initialize the video core!\n\n" + "Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver.")); + break; + + default: + QMessageBox::critical(this, tr("Error while starting Citra!"), + tr("Unknown error (please check the log)!")); + break; + } + return false; + } + return true; +} - // Load the game - if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { +bool GMainWindow::LoadROM(const std::string& filename) { + Loader::ResultStatus result = Loader::LoadFile(filename); + if (Loader::ResultStatus::Success != result) { LOG_CRITICAL(Frontend, "Failed to load ROM!"); System::Shutdown(); - return; + + switch (result) { + case Loader::ResultStatus::ErrorEncrypted: { + // Build the MessageBox ourselves to have clickable link + QMessageBox popup_error; + popup_error.setTextFormat(Qt::RichText); + popup_error.setWindowTitle(tr("Error while loading ROM!")); + popup_error.setText(tr("The game that you are trying to load must be decrypted before being used with Citra.<br/><br/>" + "For more information on dumping and decrypting games, please see: <a href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://citra-emu.org/wiki/Dumping-Game-Cartridges</a>")); + popup_error.setIcon(QMessageBox::Critical); + popup_error.exec(); + break; + } + case Loader::ResultStatus::ErrorInvalidFormat: + QMessageBox::critical(this, tr("Error while loading ROM!"), + tr("The ROM format is not supported.")); + break; + case Loader::ResultStatus::Error: + + default: + QMessageBox::critical(this, tr("Error while loading ROM!"), + tr("Unknown error!")); + break; + } + return false; } + return true; +} + +void GMainWindow::BootGame(const std::string& filename) { + LOG_INFO(Frontend, "Citra starting..."); + + if (!InitializeSystem()) + return; + + if (!LoadROM(filename)) + return; // Create and start the emulation thread emu_thread = Common::make_unique<EmuThread>(render_window); @@ -497,7 +551,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 +581,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); @@ -545,6 +615,9 @@ int main(int argc, char* argv[]) { QApplication::setAttribute(Qt::AA_X11InitThreads); QApplication app(argc, argv); + // Qt changes the locale and causes issues in float conversion using std::to_string() when generating shaders + setlocale(LC_ALL, "C"); + GMainWindow main_window; // After settings have been loaded by GMainWindow, apply the filter log_filter.ParseFilterString(Settings::values.log_filter); diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index f6d429cd9..945aea0cd 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -59,6 +59,8 @@ signals: void EmulationStopping(); private: + bool InitializeSystem(); + bool LoadROM(const std::string& filename); void BootGame(const std::string& filename); void ShutdownGame(); @@ -82,6 +84,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 +131,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/color.h b/src/common/color.h index eb199e308..908879ea6 100644 --- a/src/common/color.h +++ b/src/common/color.h @@ -11,42 +11,42 @@ namespace Color { /// Convert a 1-bit color component to 8 bit -inline u8 Convert1To8(u8 value) { +constexpr u8 Convert1To8(u8 value) { return value * 255; } /// Convert a 4-bit color component to 8 bit -inline u8 Convert4To8(u8 value) { +constexpr u8 Convert4To8(u8 value) { return (value << 4) | value; } /// Convert a 5-bit color component to 8 bit -inline u8 Convert5To8(u8 value) { +constexpr u8 Convert5To8(u8 value) { return (value << 3) | (value >> 2); } /// Convert a 6-bit color component to 8 bit -inline u8 Convert6To8(u8 value) { +constexpr u8 Convert6To8(u8 value) { return (value << 2) | (value >> 4); } /// Convert a 8-bit color component to 1 bit -inline u8 Convert8To1(u8 value) { +constexpr u8 Convert8To1(u8 value) { return value >> 7; } /// Convert a 8-bit color component to 4 bit -inline u8 Convert8To4(u8 value) { +constexpr u8 Convert8To4(u8 value) { return value >> 4; } /// Convert a 8-bit color component to 5 bit -inline u8 Convert8To5(u8 value) { +constexpr u8 Convert8To5(u8 value) { return value >> 3; } /// Convert a 8-bit color component to 6 bit -inline u8 Convert8To6(u8 value) { +constexpr u8 Convert8To6(u8 value) { return value >> 2; } diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index ed20c3629..aa6aff7b9 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -18,19 +18,11 @@ #define INSERT_PADDING_BYTES(num_bytes) u8 CONCAT2(pad, __LINE__)[(num_bytes)] #define INSERT_PADDING_WORDS(num_words) u32 CONCAT2(pad, __LINE__)[(num_words)] +// Inlining #ifdef _WIN32 - // Alignment #define FORCE_INLINE __forceinline - #define MEMORY_ALIGNED16(x) __declspec(align(16)) x - #define MEMORY_ALIGNED32(x) __declspec(align(32)) x - #define MEMORY_ALIGNED64(x) __declspec(align(64)) x - #define MEMORY_ALIGNED128(x) __declspec(align(128)) x #else #define FORCE_INLINE inline __attribute__((always_inline)) - #define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x - #define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x - #define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x - #define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x #endif #ifndef _MSC_VER 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 4c7113390..c3061479a 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -427,6 +427,9 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo // How many files + directories we found unsigned found_entries = 0; + // Save the status of callback function + bool callback_error = false; + #ifdef _WIN32 // Find the first file in the directory. WIN32_FIND_DATA ffd; @@ -454,9 +457,11 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo if (virtual_name == "." || virtual_name == "..") continue; - unsigned ret_entries; - if (!callback(&ret_entries, directory, virtual_name)) + unsigned ret_entries = 0; + if (!callback(&ret_entries, directory, virtual_name)) { + callback_error = true; break; + } found_entries += ret_entries; #ifdef _WIN32 @@ -467,9 +472,14 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo closedir(dirp); #endif - // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it - if (num_entries_out != nullptr) - *num_entries_out = found_entries; + if (!callback_error) { + // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it + if (num_entries_out != nullptr) + *num_entries_out = found_entries; + return true; + } else { + return false; + } } unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry) diff --git a/src/common/key_map.h b/src/common/key_map.h index 0ecec714f..68f7e2f99 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h @@ -4,6 +4,7 @@ #pragma once +#include <tuple> #include "core/hle/service/hid/hid.h" namespace KeyMap { @@ -15,15 +16,14 @@ struct HostDeviceKey { int key_code; int device_id; ///< Uniquely identifies a host device - bool operator < (const HostDeviceKey &other) const { - if (device_id == other.device_id) { - return key_code < other.key_code; - } - return device_id < other.device_id; + bool operator<(const HostDeviceKey &other) const { + return std::tie(key_code, device_id) < + std::tie(other.key_code, other.device_id); } - bool operator == (const HostDeviceKey &other) const { - return device_id == other.device_id && key_code == other.key_code; + bool operator==(const HostDeviceKey &other) const { + return std::tie(key_code, device_id) == + std::tie(other.key_code, other.device_id); } }; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 21a9ae8d0..54291429a 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -44,10 +44,12 @@ namespace Log { SUB(Service, LDR) \ SUB(Service, NIM) \ SUB(Service, NWM) \ + SUB(Service, CAM) \ SUB(Service, CFG) \ SUB(Service, DSP) \ SUB(Service, HID) \ SUB(Service, SOC) \ + SUB(Service, IR) \ SUB(Service, Y2R) \ CLS(HW) \ SUB(HW, Memory) \ @@ -57,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/backend.h b/src/common/logging/backend.h index c1f4d08e4..795d42ebd 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -27,25 +27,9 @@ struct Entry { std::string message; Entry() = default; + Entry(Entry&& o) = default; - // TODO(yuriks) Use defaulted move constructors once MSVC supports them -#define MOVE(member) member(std::move(o.member)) - Entry(Entry&& o) - : MOVE(timestamp), MOVE(log_class), MOVE(log_level), - MOVE(location), MOVE(message) - {} -#undef MOVE - - Entry& operator=(const Entry&& o) { -#define MOVE(member) member = std::move(o.member) - MOVE(timestamp); - MOVE(log_class); - MOVE(log_level); - MOVE(location); - MOVE(message); -#undef MOVE - return *this; - } + Entry& operator=(Entry&& o) = default; }; /** diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 43f0c59e4..4b01805ae 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -59,10 +59,12 @@ enum class Class : ClassType { Service_LDR, ///< The LDR (3ds dll loader) service Service_NIM, ///< The NIM (Network interface manager) service Service_NWM, ///< The NWM (Network wlan manager) service + Service_CAM, ///< The CAM (Camera) service Service_CFG, ///< The CFG (Configuration) service 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 @@ -72,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/common/x64/emitter.h b/src/common/x64/emitter.h index 2dd0dc94e..7c6548fb5 100644 --- a/src/common/x64/emitter.h +++ b/src/common/x64/emitter.h @@ -157,45 +157,37 @@ class XEmitter; // RIP addressing does not benefit from micro op fusion on Core arch struct OpArg { - OpArg() {} // dummy op arg, used for storage - OpArg(u64 _offset, int _scale, X64Reg rmReg = RAX, X64Reg scaledReg = RAX) + friend class XEmitter; + + constexpr OpArg() = default; // dummy op arg, used for storage + constexpr OpArg(u64 offset_, int scale_, X64Reg rmReg = RAX, X64Reg scaledReg = RAX) + : scale(static_cast<u8>(scale_)) + , offsetOrBaseReg(static_cast<u16>(rmReg)) + , indexReg(static_cast<u16>(scaledReg)) + , offset(offset_) { - operandReg = 0; - scale = (u8)_scale; - offsetOrBaseReg = (u16)rmReg; - indexReg = (u16)scaledReg; - //if scale == 0 never mind offsetting - offset = _offset; } - bool operator==(const OpArg &b) const + + constexpr bool operator==(const OpArg &b) const { - return operandReg == b.operandReg && scale == b.scale && offsetOrBaseReg == b.offsetOrBaseReg && - indexReg == b.indexReg && offset == b.offset; + return operandReg == b.operandReg && + scale == b.scale && + offsetOrBaseReg == b.offsetOrBaseReg && + indexReg == b.indexReg && + offset == b.offset; } + void WriteRex(XEmitter *emit, int opBits, int bits, int customOp = -1) const; void WriteVex(XEmitter* emit, X64Reg regOp1, X64Reg regOp2, int L, int pp, int mmmmm, int W = 0) const; void WriteRest(XEmitter *emit, int extraBytes=0, X64Reg operandReg=INVALID_REG, bool warn_64bit_offset = true) const; - void WriteFloatModRM(XEmitter *emit, FloatOp op); void WriteSingleByteOp(XEmitter *emit, u8 op, X64Reg operandReg, int bits); - // This one is public - must be written to - u64 offset; // use RIP-relative as much as possible - 64-bit immediates are not available. - u16 operandReg; - void WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &operand, int bits) const; - bool IsImm() const {return scale == SCALE_IMM8 || scale == SCALE_IMM16 || scale == SCALE_IMM32 || scale == SCALE_IMM64;} - bool IsSimpleReg() const {return scale == SCALE_NONE;} - bool IsSimpleReg(X64Reg reg) const - { - if (!IsSimpleReg()) - return false; - return GetSimpleReg() == reg; - } - bool CanDoOpWith(const OpArg &other) const + constexpr bool IsImm() const { return scale == SCALE_IMM8 || scale == SCALE_IMM16 || scale == SCALE_IMM32 || scale == SCALE_IMM64; } + constexpr bool IsSimpleReg() const { return scale == SCALE_NONE; } + constexpr bool IsSimpleReg(X64Reg reg) const { - if (IsSimpleReg()) return true; - if (!IsSimpleReg() && !other.IsSimpleReg() && !other.IsImm()) return false; - return true; + return IsSimpleReg() && GetSimpleReg() == reg; } int GetImmBits() const @@ -220,16 +212,15 @@ struct OpArg } } - X64Reg GetSimpleReg() const + constexpr X64Reg GetSimpleReg() const { - if (scale == SCALE_NONE) - return (X64Reg)offsetOrBaseReg; - else - return INVALID_REG; + return scale == SCALE_NONE + ? static_cast<X64Reg>(offsetOrBaseReg) + : INVALID_REG; } - u32 GetImmValue() const { - return (u32)offset; + constexpr u32 GetImmValue() const { + return static_cast<u32>(offset); } // For loops. @@ -238,56 +229,60 @@ struct OpArg } private: - u8 scale; - u16 offsetOrBaseReg; - u16 indexReg; + u8 scale = 0; + u16 offsetOrBaseReg = 0; + u16 indexReg = 0; + u64 offset = 0; // use RIP-relative as much as possible - 64-bit immediates are not available. + u16 operandReg = 0; }; -inline OpArg M(const void *ptr) {return OpArg((u64)ptr, (int)SCALE_RIP);} template <typename T> -inline OpArg M(const T *ptr) {return OpArg((u64)(const void *)ptr, (int)SCALE_RIP);} -inline OpArg R(X64Reg value) {return OpArg(0, SCALE_NONE, value);} -inline OpArg MatR(X64Reg value) {return OpArg(0, SCALE_ATREG, value);} +inline OpArg M(const T *ptr) { return OpArg(reinterpret_cast<u64>(ptr), static_cast<int>(SCALE_RIP)); } +constexpr OpArg R(X64Reg value) { return OpArg(0, SCALE_NONE, value); } +constexpr OpArg MatR(X64Reg value) { return OpArg(0, SCALE_ATREG, value); } -inline OpArg MDisp(X64Reg value, int offset) +constexpr OpArg MDisp(X64Reg value, int offset) { - return OpArg((u32)offset, SCALE_ATREG, value); + return OpArg(static_cast<u32>(offset), SCALE_ATREG, value); } -inline OpArg MComplex(X64Reg base, X64Reg scaled, int scale, int offset) +constexpr OpArg MComplex(X64Reg base, X64Reg scaled, int scale, int offset) { return OpArg(offset, scale, base, scaled); } -inline OpArg MScaled(X64Reg scaled, int scale, int offset) +constexpr OpArg MScaled(X64Reg scaled, int scale, int offset) { - if (scale == SCALE_1) - return OpArg(offset, SCALE_ATREG, scaled); - else - return OpArg(offset, scale | 0x20, RAX, scaled); + return scale == SCALE_1 + ? OpArg(offset, SCALE_ATREG, scaled) + : OpArg(offset, scale | 0x20, RAX, scaled); } -inline OpArg MRegSum(X64Reg base, X64Reg offset) +constexpr OpArg MRegSum(X64Reg base, X64Reg offset) { return MComplex(base, offset, 1, 0); } -inline OpArg Imm8 (u8 imm) {return OpArg(imm, SCALE_IMM8);} -inline OpArg Imm16(u16 imm) {return OpArg(imm, SCALE_IMM16);} //rarely used -inline OpArg Imm32(u32 imm) {return OpArg(imm, SCALE_IMM32);} -inline OpArg Imm64(u64 imm) {return OpArg(imm, SCALE_IMM64);} -inline OpArg UImmAuto(u32 imm) { +constexpr OpArg Imm8 (u8 imm) { return OpArg(imm, SCALE_IMM8); } +constexpr OpArg Imm16(u16 imm) { return OpArg(imm, SCALE_IMM16); } //rarely used +constexpr OpArg Imm32(u32 imm) { return OpArg(imm, SCALE_IMM32); } +constexpr OpArg Imm64(u64 imm) { return OpArg(imm, SCALE_IMM64); } +constexpr OpArg UImmAuto(u32 imm) { return OpArg(imm, imm >= 128 ? SCALE_IMM32 : SCALE_IMM8); } -inline OpArg SImmAuto(s32 imm) { +constexpr OpArg SImmAuto(s32 imm) { return OpArg(imm, (imm >= 128 || imm < -128) ? SCALE_IMM32 : SCALE_IMM8); } +template <typename T> +OpArg ImmPtr(const T* imm) +{ #ifdef _ARCH_64 -inline OpArg ImmPtr(const void* imm) {return Imm64((u64)imm);} + return Imm64(reinterpret_cast<u64>(imm)); #else -inline OpArg ImmPtr(const void* imm) {return Imm32((u32)imm);} + return Imm32(reinterpret_cast<u32>(imm)); #endif +} inline u32 PtrOffset(const void* ptr, const void* base) { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d21f198e5..35b61dada 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -252,6 +252,7 @@ set(HEADERS tracer/citrace.h memory.h memory_setup.h + mmio.h settings.h system.h ) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 5cffe513c..533067d4f 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -14,10 +14,6 @@ namespace Core { /// Generic ARM11 CPU interface class ARM_Interface : NonCopyable { public: - ARM_Interface() { - num_instructions = 0; - } - virtual ~ARM_Interface() { } @@ -146,11 +142,11 @@ public: virtual void PrepareReschedule() = 0; /// Getter for num_instructions - u64 GetNumInstructions() { + u64 GetNumInstructions() const { return num_instructions; } - s64 down_count; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop + s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop protected: @@ -162,6 +158,5 @@ protected: private: - u64 num_instructions; ///< Number of instructions executed - + u64 num_instructions = 0; ///< Number of instructions executed }; diff --git a/src/core/arm/disassembler/arm_disasm.cpp b/src/core/arm/disassembler/arm_disasm.cpp index 76408e9fa..5ad1f1c29 100644 --- a/src/core/arm/disassembler/arm_disasm.cpp +++ b/src/core/arm/disassembler/arm_disasm.cpp @@ -738,23 +738,23 @@ std::string ARM_Disasm::DisassembleMemHalf(u32 insn) if (is_immed) { if (is_pre) { if (offset == 0) { - return Common::StringFromFormat("%s%sh\tr%d, [r%d]", opname, cond_to_str(cond), rd, rn); + return Common::StringFromFormat("%s%s%s\tr%d, [r%d]", opname, cond_to_str(cond), width, rd, rn); } else { - return Common::StringFromFormat("%s%sh\tr%d, [r%d, #%s%u]%s", - opname, cond_to_str(cond), rd, rn, minus, offset, bang); + return Common::StringFromFormat("%s%s%s\tr%d, [r%d, #%s%u]%s", + opname, cond_to_str(cond), width, rd, rn, minus, offset, bang); } } else { - return Common::StringFromFormat("%s%sh\tr%d, [r%d], #%s%u", - opname, cond_to_str(cond), rd, rn, minus, offset); + return Common::StringFromFormat("%s%s%s\tr%d, [r%d], #%s%u", + opname, cond_to_str(cond), width, rd, rn, minus, offset); } } if (is_pre) { - return Common::StringFromFormat("%s%sh\tr%d, [r%d, %sr%d]%s", - opname, cond_to_str(cond), rd, rn, minus, rm, bang); + return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d]%s", + opname, cond_to_str(cond), width, rd, rn, minus, rm, bang); } else { - return Common::StringFromFormat("%s%sh\tr%d, [r%d], %sr%d", - opname, cond_to_str(cond), rd, rn, minus, rm); + return Common::StringFromFormat("%s%s%s\tr%d, [r%d], %sr%d", + opname, cond_to_str(cond), width, rd, rn, minus, rm); } } diff --git a/src/core/arm/dyncom/arm_dyncom_dec.cpp b/src/core/arm/dyncom/arm_dyncom_dec.cpp index ee4288314..8cd6755cb 100644 --- a/src/core/arm/dyncom/arm_dyncom_dec.cpp +++ b/src/core/arm/dyncom/arm_dyncom_dec.cpp @@ -6,10 +6,9 @@ #include "core/arm/skyeye_common/armsupp.h" const InstructionSetEncodingItem arm_instruction[] = { - { "vmla", 4, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 4, 4, 0 }}, - { "vmls", 7, ARMVFP2, { 28, 31, 0xF, 25, 27, 0x1, 23, 23, 1, 11, 11, 0, 8, 9, 0x2, 6, 6, 1, 4, 4, 0 }}, - { "vnmla", 4, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 4, 4, 0 }}, - { "vnmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }}, + { "vmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }}, + { "vmls", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }}, + { "vnmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }}, { "vnmls", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }}, { "vnmul", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }}, { "vmul", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }}, @@ -211,7 +210,6 @@ const InstructionSetEncodingItem arm_exclusion_code[] = { { "vmla", 0, ARMVFP2, { 0 }}, { "vmls", 0, ARMVFP2, { 0 }}, { "vnmla", 0, ARMVFP2, { 0 }}, - { "vnmla", 0, ARMVFP2, { 0 }}, { "vnmls", 0, ARMVFP2, { 0 }}, { "vnmul", 0, ARMVFP2, { 0 }}, { "vmul", 0, ARMVFP2, { 0 }}, diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 96c88c83a..5f8826034 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -51,7 +51,7 @@ enum { typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper); -static bool CondPassed(ARMul_State* cpu, unsigned int cond) { +static bool CondPassed(const ARMul_State* cpu, unsigned int cond) { const bool n_flag = cpu->NFlag != 0; const bool z_flag = cpu->ZFlag != 0; const bool c_flag = cpu->CFlag != 0; @@ -1623,9 +1623,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrb)(unsigned int inst, int index) inst_cream->inst = inst; inst_cream->get_addr = get_calc_addr_op(inst); - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index) @@ -1646,9 +1643,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index) DEBUG_MSG; } - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrd)(unsigned int inst, int index) @@ -1703,9 +1697,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrh)(unsigned int inst, int index) inst_cream->inst = inst; inst_cream->get_addr = get_calc_addr_op(inst); - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(unsigned int inst, int index) @@ -1720,9 +1711,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(unsigned int inst, int index) inst_cream->inst = inst; inst_cream->get_addr = get_calc_addr_op(inst); - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index) @@ -1737,9 +1725,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index) inst_cream->inst = inst; inst_cream->get_addr = get_calc_addr_op(inst); - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrt)(unsigned int inst, int index) @@ -2597,9 +2582,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(str)(unsigned int inst, int index) inst_cream->inst = inst; inst_cream->get_addr = get_calc_addr_op(inst); - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb)(unsigned int inst, int index) @@ -2645,9 +2627,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strb)(unsigned int inst, int index) inst_cream->inst = inst; inst_cream->get_addr = get_calc_addr_op(inst); - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index) @@ -2669,9 +2648,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index) DEBUG_MSG; } - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(unsigned int inst, int index){ @@ -2685,9 +2661,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(unsigned int inst, int index){ inst_cream->inst = inst; inst_cream->get_addr = get_calc_addr_op(inst); - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(strex)(unsigned int inst, int index) @@ -2729,9 +2702,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strh)(unsigned int inst, int index) inst_cream->inst = inst; inst_cream->get_addr = get_calc_addr_op(inst); - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index) @@ -2757,9 +2727,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index) DEBUG_MSG; } - if (BITS(inst, 12, 15) == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(sub)(unsigned int inst, int index) @@ -2808,9 +2775,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(swp)(unsigned int inst, int index) inst_cream->Rd = BITS(inst, 12, 15); inst_cream->Rm = BITS(inst, 0, 3); - if (inst_cream->Rd == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(unsigned int inst, int index){ @@ -2825,9 +2789,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(unsigned int inst, int index){ inst_cream->Rd = BITS(inst, 12, 15); inst_cream->Rm = BITS(inst, 0, 3); - if (inst_cream->Rd == 15) { - inst_base->br = INDIRECT_BRANCH; - } return inst_base; } static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(unsigned int inst, int index){ @@ -2915,9 +2876,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index) inst_cream->shifter_operand = BITS(inst, 0, 11); inst_cream->shtop_func = get_shtop(inst); - if (inst_cream->Rd == 15) - inst_base->br = INDIRECT_BRANCH; - return inst_base; } @@ -3244,7 +3202,6 @@ const transop_fp_t arm_instruction_trans[] = { INTERPRETER_TRANSLATE(vmla), INTERPRETER_TRANSLATE(vmls), INTERPRETER_TRANSLATE(vnmla), - INTERPRETER_TRANSLATE(vnmla), INTERPRETER_TRANSLATE(vnmls), INTERPRETER_TRANSLATE(vnmul), INTERPRETER_TRANSLATE(vmul), @@ -3636,209 +3593,208 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { case 0: goto VMLA_INST; \ case 1: goto VMLS_INST; \ case 2: goto VNMLA_INST; \ - case 3: goto VNMLA_INST; \ - case 4: goto VNMLS_INST; \ - case 5: goto VNMUL_INST; \ - case 6: goto VMUL_INST; \ - case 7: goto VADD_INST; \ - case 8: goto VSUB_INST; \ - case 9: goto VDIV_INST; \ - case 10: goto VMOVI_INST; \ - case 11: goto VMOVR_INST; \ - case 12: goto VABS_INST; \ - case 13: goto VNEG_INST; \ - case 14: goto VSQRT_INST; \ - case 15: goto VCMP_INST; \ - case 16: goto VCMP2_INST; \ - case 17: goto VCVTBDS_INST; \ - case 18: goto VCVTBFF_INST; \ - case 19: goto VCVTBFI_INST; \ - case 20: goto VMOVBRS_INST; \ - case 21: goto VMSR_INST; \ - case 22: goto VMOVBRC_INST; \ - case 23: goto VMRS_INST; \ - case 24: goto VMOVBCR_INST; \ - case 25: goto VMOVBRRSS_INST; \ - case 26: goto VMOVBRRD_INST; \ - case 27: goto VSTR_INST; \ - case 28: goto VPUSH_INST; \ - case 29: goto VSTM_INST; \ - case 30: goto VPOP_INST; \ - case 31: goto VLDR_INST; \ - case 32: goto VLDM_INST ; \ - case 33: goto SRS_INST; \ - case 34: goto RFE_INST; \ - case 35: goto BKPT_INST; \ - case 36: goto BLX_INST; \ - case 37: goto CPS_INST; \ - case 38: goto PLD_INST; \ - case 39: goto SETEND_INST; \ - case 40: goto CLREX_INST; \ - case 41: goto REV16_INST; \ - case 42: goto USAD8_INST; \ - case 43: goto SXTB_INST; \ - case 44: goto UXTB_INST; \ - case 45: goto SXTH_INST; \ - case 46: goto SXTB16_INST; \ - case 47: goto UXTH_INST; \ - case 48: goto UXTB16_INST; \ - case 49: goto CPY_INST; \ - case 50: goto UXTAB_INST; \ - case 51: goto SSUB8_INST; \ - case 52: goto SHSUB8_INST; \ - case 53: goto SSUBADDX_INST; \ - case 54: goto STREX_INST; \ - case 55: goto STREXB_INST; \ - case 56: goto SWP_INST; \ - case 57: goto SWPB_INST; \ - case 58: goto SSUB16_INST; \ - case 59: goto SSAT16_INST; \ - case 60: goto SHSUBADDX_INST; \ - case 61: goto QSUBADDX_INST; \ - case 62: goto SHADDSUBX_INST; \ - case 63: goto SHADD8_INST; \ - case 64: goto SHADD16_INST; \ - case 65: goto SEL_INST; \ - case 66: goto SADDSUBX_INST; \ - case 67: goto SADD8_INST; \ - case 68: goto SADD16_INST; \ - case 69: goto SHSUB16_INST; \ - case 70: goto UMAAL_INST; \ - case 71: goto UXTAB16_INST; \ - case 72: goto USUBADDX_INST; \ - case 73: goto USUB8_INST; \ - case 74: goto USUB16_INST; \ - case 75: goto USAT16_INST; \ - case 76: goto USADA8_INST; \ - case 77: goto UQSUBADDX_INST; \ - case 78: goto UQSUB8_INST; \ - case 79: goto UQSUB16_INST; \ - case 80: goto UQADDSUBX_INST; \ - case 81: goto UQADD8_INST; \ - case 82: goto UQADD16_INST; \ - case 83: goto SXTAB_INST; \ - case 84: goto UHSUBADDX_INST; \ - case 85: goto UHSUB8_INST; \ - case 86: goto UHSUB16_INST; \ - case 87: goto UHADDSUBX_INST; \ - case 88: goto UHADD8_INST; \ - case 89: goto UHADD16_INST; \ - case 90: goto UADDSUBX_INST; \ - case 91: goto UADD8_INST; \ - case 92: goto UADD16_INST; \ - case 93: goto SXTAH_INST; \ - case 94: goto SXTAB16_INST; \ - case 95: goto QADD8_INST; \ - case 96: goto BXJ_INST; \ - case 97: goto CLZ_INST; \ - case 98: goto UXTAH_INST; \ - case 99: goto BX_INST; \ - case 100: goto REV_INST; \ - case 101: goto BLX_INST; \ - case 102: goto REVSH_INST; \ - case 103: goto QADD_INST; \ - case 104: goto QADD16_INST; \ - case 105: goto QADDSUBX_INST; \ - case 106: goto LDREX_INST; \ - case 107: goto QDADD_INST; \ - case 108: goto QDSUB_INST; \ - case 109: goto QSUB_INST; \ - case 110: goto LDREXB_INST; \ - case 111: goto QSUB8_INST; \ - case 112: goto QSUB16_INST; \ - case 113: goto SMUAD_INST; \ - case 114: goto SMMUL_INST; \ - case 115: goto SMUSD_INST; \ - case 116: goto SMLSD_INST; \ - case 117: goto SMLSLD_INST; \ - case 118: goto SMMLA_INST; \ - case 119: goto SMMLS_INST; \ - case 120: goto SMLALD_INST; \ - case 121: goto SMLAD_INST; \ - case 122: goto SMLAW_INST; \ - case 123: goto SMULW_INST; \ - case 124: goto PKHTB_INST; \ - case 125: goto PKHBT_INST; \ - case 126: goto SMUL_INST; \ - case 127: goto SMLALXY_INST; \ - case 128: goto SMLA_INST; \ - case 129: goto MCRR_INST; \ - case 130: goto MRRC_INST; \ - case 131: goto CMP_INST; \ - case 132: goto TST_INST; \ - case 133: goto TEQ_INST; \ - case 134: goto CMN_INST; \ - case 135: goto SMULL_INST; \ - case 136: goto UMULL_INST; \ - case 137: goto UMLAL_INST; \ - case 138: goto SMLAL_INST; \ - case 139: goto MUL_INST; \ - case 140: goto MLA_INST; \ - case 141: goto SSAT_INST; \ - case 142: goto USAT_INST; \ - case 143: goto MRS_INST; \ - case 144: goto MSR_INST; \ - case 145: goto AND_INST; \ - case 146: goto BIC_INST; \ - case 147: goto LDM_INST; \ - case 148: goto EOR_INST; \ - case 149: goto ADD_INST; \ - case 150: goto RSB_INST; \ - case 151: goto RSC_INST; \ - case 152: goto SBC_INST; \ - case 153: goto ADC_INST; \ - case 154: goto SUB_INST; \ - case 155: goto ORR_INST; \ - case 156: goto MVN_INST; \ - case 157: goto MOV_INST; \ - case 158: goto STM_INST; \ - case 159: goto LDM_INST; \ - case 160: goto LDRSH_INST; \ - case 161: goto STM_INST; \ - case 162: goto LDM_INST; \ - case 163: goto LDRSB_INST; \ - case 164: goto STRD_INST; \ - case 165: goto LDRH_INST; \ - case 166: goto STRH_INST; \ - case 167: goto LDRD_INST; \ - case 168: goto STRT_INST; \ - case 169: goto STRBT_INST; \ - case 170: goto LDRBT_INST; \ - case 171: goto LDRT_INST; \ - case 172: goto MRC_INST; \ - case 173: goto MCR_INST; \ + case 3: goto VNMLS_INST; \ + case 4: goto VNMUL_INST; \ + case 5: goto VMUL_INST; \ + case 6: goto VADD_INST; \ + case 7: goto VSUB_INST; \ + case 8: goto VDIV_INST; \ + case 9: goto VMOVI_INST; \ + case 10: goto VMOVR_INST; \ + case 11: goto VABS_INST; \ + case 12: goto VNEG_INST; \ + case 13: goto VSQRT_INST; \ + case 14: goto VCMP_INST; \ + case 15: goto VCMP2_INST; \ + case 16: goto VCVTBDS_INST; \ + case 17: goto VCVTBFF_INST; \ + case 18: goto VCVTBFI_INST; \ + case 19: goto VMOVBRS_INST; \ + case 20: goto VMSR_INST; \ + case 21: goto VMOVBRC_INST; \ + case 22: goto VMRS_INST; \ + case 23: goto VMOVBCR_INST; \ + case 24: goto VMOVBRRSS_INST; \ + case 25: goto VMOVBRRD_INST; \ + case 26: goto VSTR_INST; \ + case 27: goto VPUSH_INST; \ + case 28: goto VSTM_INST; \ + case 29: goto VPOP_INST; \ + case 30: goto VLDR_INST; \ + case 31: goto VLDM_INST ; \ + case 32: goto SRS_INST; \ + case 33: goto RFE_INST; \ + case 34: goto BKPT_INST; \ + case 35: goto BLX_INST; \ + case 36: goto CPS_INST; \ + case 37: goto PLD_INST; \ + case 38: goto SETEND_INST; \ + case 39: goto CLREX_INST; \ + case 40: goto REV16_INST; \ + case 41: goto USAD8_INST; \ + case 42: goto SXTB_INST; \ + case 43: goto UXTB_INST; \ + case 44: goto SXTH_INST; \ + case 45: goto SXTB16_INST; \ + case 46: goto UXTH_INST; \ + case 47: goto UXTB16_INST; \ + case 48: goto CPY_INST; \ + case 49: goto UXTAB_INST; \ + case 50: goto SSUB8_INST; \ + case 51: goto SHSUB8_INST; \ + case 52: goto SSUBADDX_INST; \ + case 53: goto STREX_INST; \ + case 54: goto STREXB_INST; \ + case 55: goto SWP_INST; \ + case 56: goto SWPB_INST; \ + case 57: goto SSUB16_INST; \ + case 58: goto SSAT16_INST; \ + case 59: goto SHSUBADDX_INST; \ + case 60: goto QSUBADDX_INST; \ + case 61: goto SHADDSUBX_INST; \ + case 62: goto SHADD8_INST; \ + case 63: goto SHADD16_INST; \ + case 64: goto SEL_INST; \ + case 65: goto SADDSUBX_INST; \ + case 66: goto SADD8_INST; \ + case 67: goto SADD16_INST; \ + case 68: goto SHSUB16_INST; \ + case 69: goto UMAAL_INST; \ + case 70: goto UXTAB16_INST; \ + case 71: goto USUBADDX_INST; \ + case 72: goto USUB8_INST; \ + case 73: goto USUB16_INST; \ + case 74: goto USAT16_INST; \ + case 75: goto USADA8_INST; \ + case 76: goto UQSUBADDX_INST; \ + case 77: goto UQSUB8_INST; \ + case 78: goto UQSUB16_INST; \ + case 79: goto UQADDSUBX_INST; \ + case 80: goto UQADD8_INST; \ + case 81: goto UQADD16_INST; \ + case 82: goto SXTAB_INST; \ + case 83: goto UHSUBADDX_INST; \ + case 84: goto UHSUB8_INST; \ + case 85: goto UHSUB16_INST; \ + case 86: goto UHADDSUBX_INST; \ + case 87: goto UHADD8_INST; \ + case 88: goto UHADD16_INST; \ + case 89: goto UADDSUBX_INST; \ + case 90: goto UADD8_INST; \ + case 91: goto UADD16_INST; \ + case 92: goto SXTAH_INST; \ + case 93: goto SXTAB16_INST; \ + case 94: goto QADD8_INST; \ + case 95: goto BXJ_INST; \ + case 96: goto CLZ_INST; \ + case 97: goto UXTAH_INST; \ + case 98: goto BX_INST; \ + case 99: goto REV_INST; \ + case 100: goto BLX_INST; \ + case 101: goto REVSH_INST; \ + case 102: goto QADD_INST; \ + case 103: goto QADD16_INST; \ + case 104: goto QADDSUBX_INST; \ + case 105: goto LDREX_INST; \ + case 106: goto QDADD_INST; \ + case 107: goto QDSUB_INST; \ + case 108: goto QSUB_INST; \ + case 109: goto LDREXB_INST; \ + case 110: goto QSUB8_INST; \ + case 111: goto QSUB16_INST; \ + case 112: goto SMUAD_INST; \ + case 113: goto SMMUL_INST; \ + case 114: goto SMUSD_INST; \ + case 115: goto SMLSD_INST; \ + case 116: goto SMLSLD_INST; \ + case 117: goto SMMLA_INST; \ + case 118: goto SMMLS_INST; \ + case 119: goto SMLALD_INST; \ + case 120: goto SMLAD_INST; \ + case 121: goto SMLAW_INST; \ + case 122: goto SMULW_INST; \ + case 123: goto PKHTB_INST; \ + case 124: goto PKHBT_INST; \ + case 125: goto SMUL_INST; \ + case 126: goto SMLALXY_INST; \ + case 127: goto SMLA_INST; \ + case 128: goto MCRR_INST; \ + case 129: goto MRRC_INST; \ + case 130: goto CMP_INST; \ + case 131: goto TST_INST; \ + case 132: goto TEQ_INST; \ + case 133: goto CMN_INST; \ + case 134: goto SMULL_INST; \ + case 135: goto UMULL_INST; \ + case 136: goto UMLAL_INST; \ + case 137: goto SMLAL_INST; \ + case 138: goto MUL_INST; \ + case 139: goto MLA_INST; \ + case 140: goto SSAT_INST; \ + case 141: goto USAT_INST; \ + case 142: goto MRS_INST; \ + case 143: goto MSR_INST; \ + case 144: goto AND_INST; \ + case 145: goto BIC_INST; \ + case 146: goto LDM_INST; \ + case 147: goto EOR_INST; \ + case 148: goto ADD_INST; \ + case 149: goto RSB_INST; \ + case 150: goto RSC_INST; \ + case 151: goto SBC_INST; \ + case 152: goto ADC_INST; \ + case 153: goto SUB_INST; \ + case 154: goto ORR_INST; \ + case 155: goto MVN_INST; \ + case 156: goto MOV_INST; \ + case 157: goto STM_INST; \ + case 158: goto LDM_INST; \ + case 159: goto LDRSH_INST; \ + case 160: goto STM_INST; \ + case 161: goto LDM_INST; \ + case 162: goto LDRSB_INST; \ + case 163: goto STRD_INST; \ + case 164: goto LDRH_INST; \ + case 165: goto STRH_INST; \ + case 166: goto LDRD_INST; \ + case 167: goto STRT_INST; \ + case 168: goto STRBT_INST; \ + case 169: goto LDRBT_INST; \ + case 170: goto LDRT_INST; \ + case 171: goto MRC_INST; \ + case 172: goto MCR_INST; \ + case 173: goto MSR_INST; \ case 174: goto MSR_INST; \ case 175: goto MSR_INST; \ case 176: goto MSR_INST; \ case 177: goto MSR_INST; \ - case 178: goto MSR_INST; \ - case 179: goto LDRB_INST; \ - case 180: goto STRB_INST; \ - case 181: goto LDR_INST; \ - case 182: goto LDRCOND_INST ; \ - case 183: goto STR_INST; \ - case 184: goto CDP_INST; \ - case 185: goto STC_INST; \ - case 186: goto LDC_INST; \ - case 187: goto LDREXD_INST; \ - case 188: goto STREXD_INST; \ - case 189: goto LDREXH_INST; \ - case 190: goto STREXH_INST; \ - case 191: goto NOP_INST; \ - case 192: goto YIELD_INST; \ - case 193: goto WFE_INST; \ - case 194: goto WFI_INST; \ - case 195: goto SEV_INST; \ - case 196: goto SWI_INST; \ - case 197: goto BBL_INST; \ - case 198: goto B_2_THUMB ; \ - case 199: goto B_COND_THUMB ; \ - case 200: goto BL_1_THUMB ; \ - case 201: goto BL_2_THUMB ; \ - case 202: goto BLX_1_THUMB ; \ - case 203: goto DISPATCH; \ - case 204: goto INIT_INST_LENGTH; \ - case 205: goto END; \ + case 178: goto LDRB_INST; \ + case 179: goto STRB_INST; \ + case 180: goto LDR_INST; \ + case 181: goto LDRCOND_INST ; \ + case 182: goto STR_INST; \ + case 183: goto CDP_INST; \ + case 184: goto STC_INST; \ + case 185: goto LDC_INST; \ + case 186: goto LDREXD_INST; \ + case 187: goto STREXD_INST; \ + case 188: goto LDREXH_INST; \ + case 189: goto STREXH_INST; \ + case 190: goto NOP_INST; \ + case 191: goto YIELD_INST; \ + case 192: goto WFE_INST; \ + case 193: goto WFI_INST; \ + case 194: goto SEV_INST; \ + case 195: goto SWI_INST; \ + case 196: goto BBL_INST; \ + case 197: goto B_2_THUMB ; \ + case 198: goto B_COND_THUMB ; \ + case 199: goto BL_1_THUMB ; \ + case 200: goto BL_2_THUMB ; \ + case 201: goto BLX_1_THUMB ; \ + case 202: goto DISPATCH; \ + case 203: goto INIT_INST_LENGTH; \ + case 204: goto END; \ } #endif @@ -3865,7 +3821,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { // to a clunky switch statement. #if defined __GNUC__ || defined __clang__ void *InstLabel[] = { - &&VMLA_INST, &&VMLS_INST, &&VNMLA_INST, &&VNMLA_INST, &&VNMLS_INST, &&VNMUL_INST, &&VMUL_INST, &&VADD_INST, &&VSUB_INST, + &&VMLA_INST, &&VMLS_INST, &&VNMLA_INST, &&VNMLS_INST, &&VNMUL_INST, &&VMUL_INST, &&VADD_INST, &&VSUB_INST, &&VDIV_INST, &&VMOVI_INST, &&VMOVR_INST, &&VABS_INST, &&VNEG_INST, &&VSQRT_INST, &&VCMP_INST, &&VCMP2_INST, &&VCVTBDS_INST, &&VCVTBFF_INST, &&VCVTBFI_INST, &&VMOVBRS_INST, &&VMSR_INST, &&VMOVBRC_INST, &&VMRS_INST, &&VMOVBCR_INST, &&VMOVBRRSS_INST, &&VMOVBRRD_INST, &&VSTR_INST, &&VPUSH_INST, &&VSTM_INST, &&VPOP_INST, &&VLDR_INST, &&VLDM_INST, @@ -4477,11 +4433,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { inst_cream->get_addr(cpu, inst_cream->inst, addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr); - - if (BITS(inst_cream->inst, 12, 15) == 15) { - INC_PC(sizeof(ldst_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); @@ -4494,12 +4445,14 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr); + const u32 dest_index = BITS(inst_cream->inst, 12, 15); + const u32 previous_mode = cpu->Mode; - if (BITS(inst_cream->inst, 12, 15) == 15) { - INC_PC(sizeof(ldst_inst)); - goto DISPATCH; - } + cpu->ChangePrivilegeMode(USER32MODE); + const u8 value = cpu->ReadMemory8(addr); + cpu->ChangePrivilegeMode(previous_mode); + + cpu->Reg[dest_index] = value; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); @@ -4535,10 +4488,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->SetExclusiveMemoryAddress(read_addr); RD = cpu->ReadMemory32(read_addr); - if (inst_cream->Rd == 15) { - INC_PC(sizeof(generic_arm_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); @@ -4554,10 +4503,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->SetExclusiveMemoryAddress(read_addr); RD = cpu->ReadMemory8(read_addr); - if (inst_cream->Rd == 15) { - INC_PC(sizeof(generic_arm_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); @@ -4573,10 +4518,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { cpu->SetExclusiveMemoryAddress(read_addr); RD = cpu->ReadMemory16(read_addr); - if (inst_cream->Rd == 15) { - INC_PC(sizeof(generic_arm_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); @@ -4593,11 +4534,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { RD = cpu->ReadMemory32(read_addr); RD2 = cpu->ReadMemory32(read_addr + 4); - - if (inst_cream->Rd == 15) { - INC_PC(sizeof(generic_arm_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(generic_arm_inst)); @@ -4611,10 +4547,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { inst_cream->get_addr(cpu, inst_cream->inst, addr); cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory16(addr); - if (BITS(inst_cream->inst, 12, 15) == 15) { - INC_PC(sizeof(ldst_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); @@ -4631,10 +4563,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { value |= 0xffffff00; } cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; - if (BITS(inst_cream->inst, 12, 15) == 15) { - INC_PC(sizeof(ldst_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); @@ -4652,10 +4580,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { value |= 0xffff0000; } cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; - if (BITS(inst_cream->inst, 12, 15) == 15) { - INC_PC(sizeof(ldst_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); @@ -4668,13 +4592,14 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - unsigned int value = cpu->ReadMemory32(addr); - cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; + const u32 dest_index = BITS(inst_cream->inst, 12, 15); + const u32 previous_mode = cpu->Mode; - if (BITS(inst_cream->inst, 12, 15) == 15) { - INC_PC(sizeof(ldst_inst)); - goto DISPATCH; - } + cpu->ChangePrivilegeMode(USER32MODE); + const u32 value = cpu->ReadMemory32(addr); + cpu->ChangePrivilegeMode(previous_mode); + + cpu->Reg[dest_index] = value; } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); @@ -4731,10 +4656,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { UPDATE_NFLAG(RD); UPDATE_ZFLAG(RD); } - if (inst_cream->Rd == 15) { - INC_PC(sizeof(mla_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mla_inst)); @@ -4773,18 +4694,15 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { mrc_inst* inst_cream = (mrc_inst*)inst_base->component; - unsigned int inst = inst_cream->inst; - if (inst_cream->Rd == 15) { - DEBUG_MSG; - } - if (inst_cream->inst == 0xeef04a10) { - // Undefined instruction fmrx - RD = 0x20000000; - CITRA_IGNORE_EXIT(-1); - goto END; - } else { - if (inst_cream->cp_num == 15) - RD = cpu->ReadCP15Register(CRn, OPCODE_1, CRm, OPCODE_2); + if (inst_cream->cp_num == 15) { + const uint32_t value = cpu->ReadCP15Register(CRn, OPCODE_1, CRm, OPCODE_2); + + if (inst_cream->Rd == 15) { + cpu->Cpsr = (cpu->Cpsr & ~0xF0000000) | (value & 0xF0000000); + LOAD_NZCVT; + } else { + RD = value; + } } } cpu->Reg[15] += cpu->GetInstructionSize(); @@ -4883,10 +4801,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { UPDATE_NFLAG(RD); UPDATE_ZFLAG(RD); } - if (inst_cream->Rd == 15) { - INC_PC(sizeof(mul_inst)); - goto DISPATCH; - } } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(mul_inst)); @@ -6061,8 +5975,13 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; + + const u32 previous_mode = cpu->Mode; + const u32 value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff; + + cpu->ChangePrivilegeMode(USER32MODE); cpu->WriteMemory8(addr, value); + cpu->ChangePrivilegeMode(previous_mode); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); @@ -6196,8 +6115,16 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { ldst_inst* inst_cream = (ldst_inst*)inst_base->component; inst_cream->get_addr(cpu, inst_cream->inst, addr); - unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)]; + const u32 previous_mode = cpu->Mode; + const u32 rt_index = BITS(inst_cream->inst, 12, 15); + + u32 value = cpu->Reg[rt_index]; + if (rt_index == 15) + value += 2 * cpu->GetInstructionSize(); + + cpu->ChangePrivilegeMode(USER32MODE); cpu->WriteMemory32(addr, value); + cpu->ChangePrivilegeMode(previous_mode); } cpu->Reg[15] += cpu->GetInstructionSize(); INC_PC(sizeof(ldst_inst)); diff --git a/src/core/arm/dyncom/arm_dyncom_run.h b/src/core/arm/dyncom/arm_dyncom_run.h index 13bef17fc..8eb694fee 100644 --- a/src/core/arm/dyncom/arm_dyncom_run.h +++ b/src/core/arm/dyncom/arm_dyncom_run.h @@ -30,7 +30,7 @@ * @return If the PC is being read, then the word-aligned PC value is returned. * If the PC is not being read, then the value stored in the register is returned. */ -static inline u32 CHECK_READ_REG15_WA(ARMul_State* cpu, int Rn) { +inline u32 CHECK_READ_REG15_WA(const ARMul_State* cpu, int Rn) { return (Rn == 15) ? ((cpu->Reg[15] & ~0x3) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn]; } @@ -43,6 +43,6 @@ static inline u32 CHECK_READ_REG15_WA(ARMul_State* cpu, int Rn) { * @return If the PC is being read, then the incremented PC value is returned. * If the PC is not being read, then the values stored in the register is returned. */ -static inline u32 CHECK_READ_REG15(ARMul_State* cpu, int Rn) { +inline u32 CHECK_READ_REG15(const ARMul_State* cpu, int Rn) { return (Rn == 15) ? ((cpu->Reg[15] & ~0x1) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn]; } diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.h b/src/core/arm/dyncom/arm_dyncom_thumb.h index 447974363..c1be3c735 100644 --- a/src/core/arm/dyncom/arm_dyncom_thumb.h +++ b/src/core/arm/dyncom/arm_dyncom_thumb.h @@ -38,7 +38,7 @@ enum class ThumbDecodeStatus { // Translates a Thumb mode instruction into its ARM equivalent. ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u32* inst_size); -static inline u32 GetThumbInstruction(u32 instr, u32 address) { +inline u32 GetThumbInstruction(u32 instr, u32 address) { // Normally you would need to handle instruction endianness, // however, it is fixed to little-endian on the MPCore, so // there's no need to check for this beforehand. diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h index 91a8d4d57..210972917 100644 --- a/src/core/arm/skyeye_common/vfp/vfp_helper.h +++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h @@ -85,7 +85,7 @@ enum : u32 { #define vfp_single(inst) (((inst) & 0x0000f00) == 0xa00) -static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift) +inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift) { if (shift) { if (shift < 32) @@ -96,7 +96,7 @@ static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift) return val; } -static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift) +inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift) { if (shift) { if (shift < 64) @@ -107,7 +107,7 @@ static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift) return val; } -static inline u32 vfp_hi64to32jamming(u64 val) +inline u32 vfp_hi64to32jamming(u64 val) { u32 v; u32 highval = val >> 32; @@ -121,7 +121,7 @@ static inline u32 vfp_hi64to32jamming(u64 val) return v; } -static inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml) +inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml) { *resl = nl + ml; *resh = nh + mh; @@ -129,7 +129,7 @@ static inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml) *resh += 1; } -static inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml) +inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml) { *resl = nl - ml; *resh = nh - mh; @@ -137,7 +137,7 @@ static inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml) *resh -= 1; } -static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m) +inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m) { u32 nh, nl, mh, ml; u64 rh, rma, rmb, rl; @@ -164,20 +164,20 @@ static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m) *resh = rh; } -static inline void shift64left(u64* resh, u64* resl, u64 n) +inline void shift64left(u64* resh, u64* resl, u64 n) { *resh = n >> 63; *resl = n << 1; } -static inline u64 vfp_hi64multiply64(u64 n, u64 m) +inline u64 vfp_hi64multiply64(u64 n, u64 m) { u64 rh, rl; mul64to128(&rh, &rl, n, m); return rh | (rl != 0); } -static inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m) +inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m) { u64 mh, ml, remh, reml, termh, terml, z; @@ -249,7 +249,7 @@ enum : u32 { VFP_SNAN = (VFP_NAN|VFP_NAN_SIGNAL) }; -static inline int vfp_single_type(vfp_single* s) +inline int vfp_single_type(const vfp_single* s) { int type = VFP_NUMBER; if (s->exponent == 255) { @@ -271,7 +271,7 @@ static inline int vfp_single_type(vfp_single* s) // Unpack a single-precision float. Note that this returns the magnitude // of the single-precision float mantissa with the 1. if necessary, // aligned to bit 30. -static inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr) +inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr) { s->sign = vfp_single_packed_sign(val) >> 16, s->exponent = vfp_single_packed_exponent(val); @@ -293,7 +293,7 @@ static inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr) // Re-pack a single-precision float. This assumes that the float is // already normalised such that the MSB is bit 30, _not_ bit 31. -static inline s32 vfp_single_pack(vfp_single* s) +inline s32 vfp_single_pack(const vfp_single* s) { u32 val = (s->sign << 16) + (s->exponent << VFP_SINGLE_MANTISSA_BITS) + @@ -335,7 +335,7 @@ struct vfp_double { #define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1)) #define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1)) -static inline int vfp_double_type(vfp_double* s) +inline int vfp_double_type(const vfp_double* s) { int type = VFP_NUMBER; if (s->exponent == 2047) { @@ -357,7 +357,7 @@ static inline int vfp_double_type(vfp_double* s) // Unpack a double-precision float. Note that this returns the magnitude // of the double-precision float mantissa with the 1. if necessary, // aligned to bit 62. -static inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr) +inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr) { s->sign = vfp_double_packed_sign(val) >> 48; s->exponent = vfp_double_packed_exponent(val); @@ -379,7 +379,7 @@ static inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr) // Re-pack a double-precision float. This assumes that the float is // already normalised such that the MSB is bit 30, _not_ bit 31. -static inline s64 vfp_double_pack(vfp_double* s) +inline s64 vfp_double_pack(const vfp_double* s) { u64 val = ((u64)s->sign << 48) + ((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) + @@ -415,7 +415,7 @@ struct op { u32 flags; }; -static inline u32 fls(u32 x) +inline u32 fls(u32 x) { int r = 32; diff --git a/src/core/core.cpp b/src/core/core.cpp index 219b03af4..84d6c392e 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> + +#include "common/make_unique.h" #include "common/logging/log.h" #include "core/core.h" @@ -17,8 +20,8 @@ namespace Core { -ARM_Interface* g_app_core = nullptr; ///< ARM11 application core -ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core +std::unique_ptr<ARM_Interface> g_app_core; ///< ARM11 application core +std::unique_ptr<ARM_Interface> g_sys_core; ///< ARM11 system (OS) core /// Run the core CPU loop void RunLoop(int tight_loop) { @@ -70,17 +73,16 @@ void Stop() { } /// Initialize the core -int Init() { - g_sys_core = new ARM_DynCom(USER32MODE); - g_app_core = new ARM_DynCom(USER32MODE); +void Init() { + g_sys_core = Common::make_unique<ARM_DynCom>(USER32MODE); + g_app_core = Common::make_unique<ARM_DynCom>(USER32MODE); LOG_DEBUG(Core, "Initialized OK"); - return 0; } void Shutdown() { - delete g_app_core; - delete g_sys_core; + g_app_core.reset(); + g_sys_core.reset(); LOG_DEBUG(Core, "Shutdown OK"); } diff --git a/src/core/core.h b/src/core/core.h index 491230a74..ad26dca3f 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -4,6 +4,7 @@ #pragma once +#include <memory> #include "common/common_types.h" class ARM_Interface; @@ -23,8 +24,8 @@ struct ThreadContext { u32 fpexc; }; -extern ARM_Interface* g_app_core; ///< ARM11 application core -extern ARM_Interface* g_sys_core; ///< ARM11 system (OS) core +extern std::unique_ptr<ARM_Interface> g_app_core; ///< ARM11 application core +extern std::unique_ptr<ARM_Interface> g_sys_core; ///< ARM11 system (OS) core //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -51,7 +52,7 @@ void Halt(const char *msg); void Stop(); /// Initialize the core -int Init(); +void Init(); /// Shutdown the core void Shutdown(); diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp index 3f81447df..97adf0e12 100644 --- a/src/core/file_sys/archive_backend.cpp +++ b/src/core/file_sys/archive_backend.cpp @@ -43,7 +43,7 @@ Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) { } } -const std::string Path::DebugStr() const { +std::string Path::DebugStr() const { switch (GetType()) { case Invalid: default: @@ -66,7 +66,7 @@ const std::string Path::DebugStr() const { } } -const std::string Path::AsString() const { +std::string Path::AsString() const { switch (GetType()) { case Char: return string; @@ -83,7 +83,7 @@ const std::string Path::AsString() const { } } -const std::u16string Path::AsU16Str() const { +std::u16string Path::AsU16Str() const { switch (GetType()) { case Char: return Common::UTF8ToUTF16(string); @@ -99,7 +99,7 @@ const std::u16string Path::AsU16Str() const { } } -const std::vector<u8> Path::AsBinary() const { +std::vector<u8> Path::AsBinary() const { switch (GetType()) { case Binary: return binary; diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h index e7a59a1ed..601e95d8c 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/archive_backend.h @@ -49,11 +49,11 @@ public: * Gets the string representation of the path for debugging * @return String representation of the path for debugging */ - const std::string DebugStr() const; + std::string DebugStr() const; - const std::string AsString() const; - const std::u16string AsU16Str() const; - const std::vector<u8> AsBinary() const; + std::string AsString() const; + std::u16string AsU16Str() const; + std::vector<u8> AsBinary() const; private: LowPathType type; 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/function_wrappers.h b/src/core/hle/function_wrappers.h index 3501e45db..882a51df1 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -188,6 +188,10 @@ template<ResultCode func(s64*, Handle, u32)> void Wrap() { FuncReturn(retval); } +template<ResultCode func(Handle, u32)> void Wrap() { + FuncReturn(func(PARAM(0), PARAM(1)).raw); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u32 diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 195286422..5c3c47acf 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -45,30 +45,32 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: - if ((s32)Memory::Read32(address) <= value) { + if ((s32)Memory::Read32(address) < value) { Kernel::WaitCurrentThread_ArbitrateAddress(address); } break; case ArbitrationType::WaitIfLessThanWithTimeout: - if ((s32)Memory::Read32(address) <= value) { + if ((s32)Memory::Read32(address) < value) { Kernel::WaitCurrentThread_ArbitrateAddress(address); GetCurrentThread()->WakeAfterDelay(nanoseconds); } break; case ArbitrationType::DecrementAndWaitIfLessThan: { - s32 memory_value = Memory::Read32(address) - 1; - Memory::Write32(address, memory_value); - if (memory_value <= value) { + s32 memory_value = Memory::Read32(address); + if (memory_value < value) { + // Only change the memory value if the thread should wait + Memory::Write32(address, (s32)memory_value - 1); Kernel::WaitCurrentThread_ArbitrateAddress(address); } break; } case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: { - s32 memory_value = Memory::Read32(address) - 1; - Memory::Write32(address, memory_value); - if (memory_value <= value) { + s32 memory_value = Memory::Read32(address); + if (memory_value < value) { + // Only change the memory value if the thread should wait + Memory::Write32(address, (s32)memory_value - 1); Kernel::WaitCurrentThread_ArbitrateAddress(address); GetCurrentThread()->WakeAfterDelay(nanoseconds); } @@ -82,6 +84,13 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, HLE::Reschedule(__func__); + // The calls that use a timeout seem to always return a Timeout error even if they did not put the thread to sleep + if (type == ArbitrationType::WaitIfLessThanWithTimeout || + type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) { + + return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info); + } return RESULT_SUCCESS; } 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..24b266eae 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; @@ -130,9 +130,11 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); } +VAddr Process::GetLinearHeapAreaAddress() const { + return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; +} VAddr Process::GetLinearHeapBase() const { - return (kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR) - + memory_region->base; + return GetLinearHeapAreaAddress() + memory_region->base; } VAddr Process::GetLinearHeapLimit() const { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 60e17f251..6d2ca96a2 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -143,6 +143,7 @@ public: /// Bitmask of the used TLS slots std::bitset<300> used_tls_slots; + VAddr GetLinearHeapAreaAddress() const; VAddr GetLinearHeapBase() const; VAddr GetLinearHeapLimit() const; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 1f477664b..d90f0f00f 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -39,6 +39,12 @@ ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } + // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't + // match what was specified when the memory block was created. + + // TODO(Subv): Return E0E01BEE when address should be 0. + // Note: Find out when that's the case. + if (fixed_address != 0) { if (address != 0 && address != fixed_address) { LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: fixed_addres is 0x%08X!", @@ -74,6 +80,21 @@ ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, return RESULT_SUCCESS; } +ResultCode SharedMemory::Unmap(VAddr address) { + if (base_address == 0) { + // TODO(Subv): Verify what actually happens when you want to unmap a memory block that + // was originally mapped with address = 0 + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + } + + if (base_address != address) + return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage); + + base_address = 0; + + return RESULT_SUCCESS; +} + u8* SharedMemory::GetPointer(u32 offset) { if (base_address != 0) return Memory::GetPointer(base_address + offset); diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 35b550d12..b51049ad0 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -53,6 +53,13 @@ public: ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions); /** + * Unmaps a shared memory block from the specified address in system memory + * @param address Address in system memory where the shared memory block is mapped + * @return Result code of the unmap operation + */ + ResultCode Unmap(VAddr address); + + /** * Gets a pointer to the shared memory block * @param offset Offset from the start of the shared memory block to get pointer * @return Pointer to the shared memory block from the specified offset diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index c08fc1c7a..bf32f653d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -300,7 +300,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { thread->waitsynch_waited = false; - if (thread->status == THREADSTATUS_WAIT_SYNCH) { + if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) { thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, ErrorSummary::StatusChanged, ErrorLevel::Info)); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 08b3ea8c0..ce6bbd719 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -42,6 +42,9 @@ bool Timer::ShouldWait() { void Timer::Acquire() { ASSERT_MSG( !ShouldWait(), "object unavailable!"); + + if (reset_type == RESETTYPE_ONESHOT) + signaled = false; } void Timer::Set(s64 initial, s64 interval) { @@ -84,9 +87,6 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { // Resume all waiting threads timer->WakeupAllWaitingThreads(); - if (timer->reset_type == RESETTYPE_ONESHOT) - timer->signaled = false; - if (timer->interval_delay != 0) { // Reschedule the timer with the interval delay u64 interval_microseconds = timer->interval_delay / 1000; diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 2610acf76..1e289f38a 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -8,6 +8,7 @@ #include "core/hle/kernel/vm_manager.h" #include "core/memory_setup.h" +#include "core/mmio.h" namespace Kernel { @@ -104,7 +105,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8 * m return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); } -ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state) { +ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler) { // This is the appropriately sized VMA that will turn into our allocation. CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); VirtualMemoryArea& final_vma = vma_handle->second; @@ -114,6 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3 final_vma.permissions = VMAPermission::ReadWrite; final_vma.meminfo_state = state; final_vma.paddr = paddr; + final_vma.mmio_handler = mmio_handler; UpdatePageTableForVMA(final_vma); return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); @@ -330,8 +332,7 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); break; case VMAType::MMIO: - // TODO(yuriks): Add support for MMIO handlers. - Memory::MapIoRegion(vma.base, vma.size); + Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); break; } } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 4e95f1f0c..91d40655b 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "core/hle/result.h" +#include "core/mmio.h" namespace Kernel { @@ -92,6 +93,7 @@ struct VirtualMemoryArea { // Settings for type = MMIO /// Physical address of the register area this VMA maps to. PAddr paddr = 0; + Memory::MMIORegionPointer mmio_handler = nullptr; /// Tests if this area can be merged to the right with `next`. bool CanBeMergedWith(const VirtualMemoryArea& next) const; @@ -168,8 +170,9 @@ public: * @param paddr The physical address where the registers are present. * @param size Size of the mapping. * @param state MemoryState tag to attach to the VMA. + * @param mmio_handler The handler that will implement read and write for this MMIO region. */ - ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state); + ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler); /// Unmaps a range of addresses, splitting VMAs as necessary. ResultCode UnmapRange(VAddr target, u32 size); diff --git a/src/core/hle/result.h b/src/core/hle/result.h index cb2d681e0..69613fbbb 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -18,6 +18,7 @@ /// Detailed description of the error. This listing is likely incomplete. enum class ErrorDescription : u32 { Success = 0, + WrongAddress = 53, FS_NotFound = 100, FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive InvalidSection = 1000, @@ -192,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; } @@ -268,7 +269,6 @@ public: : result_code(error_code) { ASSERT(error_code.IsError()); - UpdateDebugPtr(); } /** @@ -286,40 +286,37 @@ public: : result_code(o.result_code) { if (!o.empty()) { - new (&storage) T(*o.GetPointer()); + new (&object) T(o.object); } - UpdateDebugPtr(); } ResultVal(ResultVal&& o) : result_code(o.result_code) { if (!o.empty()) { - new (&storage) T(std::move(*o.GetPointer())); + new (&object) T(std::move(o.object)); } - UpdateDebugPtr(); } ~ResultVal() { if (!empty()) { - GetPointer()->~T(); + object.~T(); } } ResultVal& operator=(const ResultVal& o) { if (!empty()) { if (!o.empty()) { - *GetPointer() = *o.GetPointer(); + object = o.object; } else { - GetPointer()->~T(); + object.~T(); } } else { if (!o.empty()) { - new (&storage) T(*o.GetPointer()); + new (&object) T(o.object); } } result_code = o.result_code; - UpdateDebugPtr(); return *this; } @@ -332,11 +329,10 @@ public: void emplace(ResultCode success_code, Args&&... args) { ASSERT(success_code.IsSuccess()); if (!empty()) { - GetPointer()->~T(); + object.~T(); } - new (&storage) T(std::forward<Args>(args)...); + new (&object) T(std::forward<Args>(args)...); result_code = success_code; - UpdateDebugPtr(); } /// Returns true if the `ResultVal` contains an error code and no value. @@ -349,15 +345,15 @@ public: ResultCode Code() const { return result_code; } - const T& operator* () const { return *GetPointer(); } - T& operator* () { return *GetPointer(); } - const T* operator->() const { return GetPointer(); } - T* operator->() { return GetPointer(); } + const T& operator* () const { return object; } + T& operator* () { return object; } + const T* operator->() const { return &object; } + T* operator->() { return &object; } /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing. template <typename U> T ValueOr(U&& value) const { - return !empty() ? *GetPointer() : std::move(value); + return !empty() ? object : std::move(value); } /// Asserts that the result succeeded and returns a reference to it. @@ -371,31 +367,10 @@ public: } private: - typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType; - - StorageType storage; + // A union is used to allocate the storage for the value, while allowing us to construct and + // destruct it at will. + union { T object; }; ResultCode result_code; -#ifdef _DEBUG - // The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the - // need to cast `storage` to a pointer or pay attention to `result_code`. - const T* debug_ptr; -#endif - - void UpdateDebugPtr() { -#ifdef _DEBUG - debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage)); -#endif - } - - const T* GetPointer() const { - ASSERT(!empty()); - return static_cast<const T*>(static_cast<const void*>(&storage)); - } - - T* GetPointer() { - ASSERT(!empty()); - return static_cast<T*>(static_cast<void*>(&storage)); - } }; /** diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index f8aab6bc7..d67325506 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include "common/logging/log.h" -#include "core/hle/hle.h" #include "core/hle/service/ac_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -23,12 +22,27 @@ static void GetWifiStatus(Service::Interface* self) { // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; // Connection type set to none LOG_WARNING(Service_AC, "(STUBBED) called"); } +/** + * AC_U::IsConnected service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : bool, is connected + */ +static void IsConnected(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = false; // Not connected to ac:u service + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010000, nullptr, "CreateDefaultConfig"}, {0x00040006, nullptr, "ConnectAsync"}, @@ -45,7 +59,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x002D0082, nullptr, "SetRequestEulaVersion"}, {0x00300004, nullptr, "RegisterDisconnectEvent"}, {0x003C0042, nullptr, "GetAPSSIDList"}, - {0x003E0042, nullptr, "IsConnected"}, + {0x003E0042, IsConnected, "IsConnected"}, {0x00400042, nullptr, "SetClientVersion"}, }; diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp index 57f49c91f..b23d17fba 100644 --- a/src/core/hle/service/act_u.cpp +++ b/src/core/hle/service/act_u.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/act_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -10,14 +9,15 @@ namespace ACT_U { -// Empty arrays are illegal -- commented out until an entry is added. -//const Interface::FunctionInfo FunctionTable[] = { }; +const Interface::FunctionInfo FunctionTable[] = { + {0x000600C2, nullptr, "GetAccountDataBlock"}, +}; //////////////////////////////////////////////////////////////////////////////////////////////////// // Interface class Interface::Interface() { - //Register(FunctionTable); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 7ae4859a7..06be9940e 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -10,10 +10,6 @@ #include "core/hle/service/am/am_net.h" #include "core/hle/service/am/am_sys.h" -#include "core/hle/hle.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" - namespace Service { namespace AM { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 0b78f5393..15e63bc7b 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -4,10 +4,10 @@ #pragma once -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" - namespace Service { + +class Interface; + namespace AM { /** diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp index f40a87cb4..16c76a1eb 100644 --- a/src/core/hle/service/am/am_app.cpp +++ b/src/core/hle/service/am/am_app.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/am_app.h" diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp index aa391f3b2..065e04118 100644 --- a/src/core/hle/service/am/am_net.cpp +++ b/src/core/hle/service/am/am_net.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/am_net.h" @@ -10,6 +9,36 @@ namespace Service { namespace AM { const Interface::FunctionInfo FunctionTable[] = { + {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, + {0x00020082, GetTitleIDList, "GetTitleIDList"}, + {0x00030084, nullptr, "ListTitles"}, + {0x000400C0, nullptr, "DeleteApplicationTitle"}, + {0x000500C0, nullptr, "GetTitleProductCode"}, + {0x00080000, nullptr, "TitleIDListGetTotal3"}, + {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000A0000, nullptr, "GetDeviceID"}, + {0x000D0084, nullptr, "ListTitles2"}, + {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x00180080, nullptr, "InitializeTitleDatabase"}, + {0x00190040, nullptr, "ReloadDBS"}, + {0x001A00C0, nullptr, "GetDSiWareExportSize"}, + {0x001B0144, nullptr, "ExportDSiWare"}, + {0x001C0084, nullptr, "ImportDSiWare"}, + {0x00230080, nullptr, "TitleIDListGetTotal2"}, + {0x002400C2, nullptr, "GetTitleIDList2"}, + {0x04010080, nullptr, "InstallFIRM"}, + {0x04020040, nullptr, "StartInstallCIADB0"}, + {0x04030000, nullptr, "StartInstallCIADB1"}, + {0x04040002, nullptr, "AbortCIAInstall"}, + {0x04050002, nullptr, "CloseCIAFinalizeInstall"}, + {0x04060002, nullptr, "CloseCIA"}, + {0x040700C2, nullptr, "FinalizeTitlesInstall"}, + {0x04080042, nullptr, "GetCiaFileInfo"}, + {0x040E00C2, nullptr, "InstallTitlesFinish"}, + {0x040F0000, nullptr, "InstallNATIVEFIRM"}, + {0x041000C0, nullptr, "DeleteTitle"}, + {0x04120000, nullptr, "Initialize"}, + {0x041700C0, nullptr, "MigrateAGBtoSAV"}, {0x08010000, nullptr, "OpenTicket"}, {0x08020002, nullptr, "TicketAbortInstall"}, {0x08030002, nullptr, "TicketFinalizeInstall"}, diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp index 864fc14df..e38812297 100644 --- a/src/core/hle/service/am/am_sys.cpp +++ b/src/core/hle/service/am/am_sys.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/am_sys.h" @@ -12,6 +11,21 @@ namespace AM { const Interface::FunctionInfo FunctionTable[] = { {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, {0x00020082, GetTitleIDList, "GetTitleIDList"}, + {0x00030084, nullptr, "ListTitles"}, + {0x000400C0, nullptr, "DeleteApplicationTitle"}, + {0x000500C0, nullptr, "GetTitleProductCode"}, + {0x00080000, nullptr, "TitleIDListGetTotal3"}, + {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000A0000, nullptr, "GetDeviceID"}, + {0x000D0084, nullptr, "ListTitles2"}, + {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x00180080, nullptr, "InitializeTitleDatabase"}, + {0x00190040, nullptr, "ReloadDBS"}, + {0x001A00C0, nullptr, "GetDSiWareExportSize"}, + {0x001B0144, nullptr, "ExportDSiWare"}, + {0x001C0084, nullptr, "ImportDSiWare"}, + {0x00230080, nullptr, "TitleIDListGetTotal2"}, + {0x002400C2, nullptr, "GetTitleIDList2"} }; AM_SYS_Interface::AM_SYS_Interface() { diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp index 6bf84b36b..c0392b754 100644 --- a/src/core/hle/service/am/am_u.cpp +++ b/src/core/hle/service/am/am_u.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/am_u.h" @@ -12,6 +11,34 @@ namespace AM { const Interface::FunctionInfo FunctionTable[] = { {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"}, {0x00020082, GetTitleIDList, "GetTitleIDList"}, + {0x00030084, nullptr, "ListTitles"}, + {0x000400C0, nullptr, "DeleteApplicationTitle"}, + {0x000500C0, nullptr, "GetTitleProductCode"}, + {0x00080000, nullptr, "TitleIDListGetTotal3"}, + {0x00090082, nullptr, "GetTitleIDList3"}, + {0x000A0000, nullptr, "GetDeviceID"}, + {0x000D0084, nullptr, "ListTitles2"}, + {0x00140040, nullptr, "FinishInstallToMedia"}, + {0x00180080, nullptr, "InitializeTitleDatabase"}, + {0x00190040, nullptr, "ReloadDBS"}, + {0x001A00C0, nullptr, "GetDSiWareExportSize"}, + {0x001B0144, nullptr, "ExportDSiWare"}, + {0x001C0084, nullptr, "ImportDSiWare"}, + {0x00230080, nullptr, "TitleIDListGetTotal2"}, + {0x002400C2, nullptr, "GetTitleIDList2"}, + {0x04010080, nullptr, "InstallFIRM"}, + {0x04020040, nullptr, "StartInstallCIADB0"}, + {0x04030000, nullptr, "StartInstallCIADB1"}, + {0x04040002, nullptr, "AbortCIAInstall"}, + {0x04050002, nullptr, "CloseCIAFinalizeInstall"}, + {0x04060002, nullptr, "CloseCIA"}, + {0x040700C2, nullptr, "FinalizeTitlesInstall"}, + {0x04080042, nullptr, "GetCiaFileInfo"}, + {0x040E00C2, nullptr, "InstallTitlesFinish"}, + {0x040F0000, nullptr, "InstallNATIVEFIRM"}, + {0x041000C0, nullptr, "DeleteTitle"}, + {0x04120000, nullptr, "Initialize"}, + {0x041700C0, nullptr, "MigrateAGBtoSAV"} }; AM_U_Interface::AM_U_Interface() { diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index feb579778..98c72fc32 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -14,12 +14,10 @@ #include "core/hle/service/apt/apt_u.h" #include "core/hle/service/fs/archive.h" -#include "core/hle/hle.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/shared_memory.h" -#include "core/hle/kernel/thread.h" namespace Service { namespace APT { diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 22800c56f..0c6a77305 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/apt/apt.h" #include "core/hle/service/apt/apt_a.h" diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 3ac6ff94f..7f6e81a63 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - -#include "core/hle/hle.h" #include "core/hle/service/apt/apt.h" #include "core/hle/service/apt/apt_s.h" @@ -91,6 +89,12 @@ const Interface::FunctionInfo FunctionTable[] = { {0x004E0000, nullptr, "HardwareResetAsync"}, {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, + {0x00510080, nullptr, "GetStartupArgument"}, + {0x00520104, nullptr, "Wrap1"}, + {0x00530104, nullptr, "Unwrap1"}, + {0x00580002, nullptr, "GetProgramID"}, + {0x01010000, nullptr, "CheckNew3DSApp"}, + {0x01020000, nullptr, "CheckNew3DS"} }; APT_S_Interface::APT_S_Interface() { diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index 2e9d1f4b4..b13b51549 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -2,9 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - -#include "common/file_util.h" - #include "core/hle/service/apt/apt.h" #include "core/hle/service/apt/apt_u.h" @@ -92,6 +89,12 @@ const Interface::FunctionInfo FunctionTable[] = { {0x004E0000, nullptr, "HardwareResetAsync"}, {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, + {0x00510080, nullptr, "GetStartupArgument"}, + {0x00520104, nullptr, "Wrap1"}, + {0x00530104, nullptr, "Unwrap1"}, + {0x00580002, nullptr, "GetProgramID"}, + {0x01010000, nullptr, "CheckNew3DSApp"}, + {0x01020000, nullptr, "CheckNew3DS"} }; APT_U_Interface::APT_U_Interface() { diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp index d38140f19..419ec976e 100644 --- a/src/core/hle/service/boss/boss.cpp +++ b/src/core/hle/service/boss/boss.cpp @@ -7,10 +7,6 @@ #include "core/hle/service/boss/boss_p.h" #include "core/hle/service/boss/boss_u.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" -#include "core/hle/hle.h" - namespace Service { namespace BOSS { diff --git a/src/core/hle/service/boss/boss.h b/src/core/hle/service/boss/boss.h index a6942ada6..d3b5d7101 100644 --- a/src/core/hle/service/boss/boss.h +++ b/src/core/hle/service/boss/boss.h @@ -4,9 +4,6 @@ #pragma once -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" - namespace Service { namespace BOSS { diff --git a/src/core/hle/service/boss/boss_p.cpp b/src/core/hle/service/boss/boss_p.cpp index 089f5f186..c498abe4e 100644 --- a/src/core/hle/service/boss/boss_p.cpp +++ b/src/core/hle/service/boss/boss_p.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/boss/boss.h" #include "core/hle/service/boss/boss_p.h" namespace Service { diff --git a/src/core/hle/service/boss/boss_u.cpp b/src/core/hle/service/boss/boss_u.cpp index ed978b963..d59babe71 100644 --- a/src/core/hle/service/boss/boss_u.cpp +++ b/src/core/hle/service/boss/boss_u.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/boss/boss.h" #include "core/hle/service/boss/boss_u.h" namespace Service { @@ -11,6 +9,9 @@ namespace BOSS { const Interface::FunctionInfo FunctionTable[] = { {0x00020100, nullptr, "GetStorageInfo"}, + {0x000C0082, nullptr, "UnregisterTask"}, + {0x001E0042, nullptr, "CancelTask"}, + {0x00330042, nullptr, "StartBgImmediate"}, }; BOSS_U_Interface::BOSS_U_Interface() { diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 4f34b699b..4d714037f 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -4,20 +4,287 @@ #include "common/logging/log.h" -#include "core/hle/service/service.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_c.h" #include "core/hle/service/cam/cam_q.h" #include "core/hle/service/cam/cam_s.h" #include "core/hle/service/cam/cam_u.h" - -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" -#include "core/hle/hle.h" +#include "core/hle/service/service.h" namespace Service { namespace CAM { +static const u32 TRANSFER_BYTES = 5 * 1024; + +static Kernel::SharedPtr<Kernel::Event> completion_event_cam1; +static Kernel::SharedPtr<Kernel::Event> completion_event_cam2; +static Kernel::SharedPtr<Kernel::Event> interrupt_error_event; +static Kernel::SharedPtr<Kernel::Event> vsync_interrupt_error_event; + +void StartCapture(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 port = cmd_buff[1] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); +} + +void StopCapture(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 port = cmd_buff[1] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); +} + +void GetVsyncInterruptEvent(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 port = cmd_buff[1] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = IPC::MoveHandleDesc(); + cmd_buff[3] = Kernel::g_handle_table.Create(vsync_interrupt_error_event).MoveFrom(); + + LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); +} + +void GetBufferErrorInterruptEvent(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 port = cmd_buff[1] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x6, 1, 2); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = IPC::MoveHandleDesc(); + cmd_buff[3] = Kernel::g_handle_table.Create(interrupt_error_event).MoveFrom(); + + LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); +} + +void SetReceiving(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + VAddr dest = cmd_buff[1]; + u8 port = cmd_buff[2] & 0xFF; + u32 image_size = cmd_buff[3]; + u16 trans_unit = cmd_buff[4] & 0xFFFF; + + Kernel::Event* completion_event = (Port)port == Port::Cam2 ? + completion_event_cam2.get() : completion_event_cam1.get(); + + completion_event->Signal(); + + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = IPC::MoveHandleDesc(); + cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); + + LOG_WARNING(Service_CAM, "(STUBBED) called, addr=0x%X, port=%d, image_size=%d, trans_unit=%d", + dest, port, image_size, trans_unit); +} + +void SetTransferLines(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 port = cmd_buff[1] & 0xFF; + u16 transfer_lines = cmd_buff[2] & 0xFFFF; + u16 width = cmd_buff[3] & 0xFFFF; + u16 height = cmd_buff[4] & 0xFFFF; + + cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, lines=%d, width=%d, height=%d", + port, transfer_lines, width, height); +} + +void GetMaxLines(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u16 width = cmd_buff[1] & 0xFFFF; + u16 height = cmd_buff[2] & 0xFFFF; + + cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = TRANSFER_BYTES / (2 * width); + + LOG_WARNING(Service_CAM, "(STUBBED) called, width=%d, height=%d, lines = %d", + width, height, cmd_buff[2]); +} + +void GetTransferBytes(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 port = cmd_buff[1] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = TRANSFER_BYTES; + + LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); +} + +void SetTrimming(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 port = cmd_buff[1] & 0xFF; + bool trim = (cmd_buff[2] & 0xFF) != 0; + + cmd_buff[0] = IPC::MakeHeader(0xE, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, trim=%d", port, trim); +} + +void SetTrimmingParamsCenter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 port = cmd_buff[1] & 0xFF; + s16 trimW = cmd_buff[2] & 0xFFFF; + s16 trimH = cmd_buff[3] & 0xFFFF; + s16 camW = cmd_buff[4] & 0xFFFF; + s16 camH = cmd_buff[5] & 0xFFFF; + + cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, trimW=%d, trimH=%d, camW=%d, camH=%d", + port, trimW, trimH, camW, camH); +} + +void Activate(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 cam_select = cmd_buff[1] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d", + cam_select); +} + +void FlipImage(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 cam_select = cmd_buff[1] & 0xFF; + u8 flip = cmd_buff[2] & 0xFF; + u8 context = cmd_buff[3] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x1D, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, flip=%d, context=%d", + cam_select, flip, context); +} + +void SetSize(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 cam_select = cmd_buff[1] & 0xFF; + u8 size = cmd_buff[2] & 0xFF; + u8 context = cmd_buff[3] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x1F, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, size=%d, context=%d", + cam_select, size, context); +} + +void SetFrameRate(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 cam_select = cmd_buff[1] & 0xFF; + u8 frame_rate = cmd_buff[2] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, frame_rate=%d", + cam_select, frame_rate); +} + +void GetStereoCameraCalibrationData(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + // Default values taken from yuriks' 3DS. Valid data is required here or games using the + // calibration get stuck in an infinite CPU loop. + StereoCameraCalibrationData data = {}; + data.isValidRotationXY = 0; + data.scale = 1.001776f; + data.rotationZ = 0.008322907f; + data.translationX = -87.70484f; + data.translationY = -7.640977f; + data.rotationX = 0.0f; + data.rotationY = 0.0f; + data.angleOfViewRight = 64.66875f; + data.angleOfViewLeft = 64.76067f; + data.distanceToChart = 250.0f; + data.distanceCameras = 35.0f; + data.imageWidth = 640; + data.imageHeight = 480; + + cmd_buff[0] = IPC::MakeHeader(0x2B, 17, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + memcpy(&cmd_buff[2], &data, sizeof(data)); + + LOG_TRACE(Service_CAM, "called"); +} + +void GetSuitableY2rStandardCoefficient(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x36, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0; + + LOG_WARNING(Service_CAM, "(STUBBED) called"); +} + +void PlayShutterSound(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u8 sound_id = cmd_buff[1] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x38, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called, sound_id=%d", sound_id); +} + +void DriverInitialize(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + completion_event_cam1->Clear(); + completion_event_cam2->Clear(); + interrupt_error_event->Clear(); + vsync_interrupt_error_event->Clear(); + + cmd_buff[0] = IPC::MakeHeader(0x39, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called"); +} + +void DriverFinalize(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x3A, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called"); +} + void Init() { using namespace Kernel; @@ -25,9 +292,18 @@ void Init() { AddService(new CAM_Q_Interface); AddService(new CAM_S_Interface); AddService(new CAM_U_Interface); + + completion_event_cam1 = Kernel::Event::Create(RESETTYPE_ONESHOT, "CAM_U::completion_event_cam1"); + completion_event_cam2 = Kernel::Event::Create(RESETTYPE_ONESHOT, "CAM_U::completion_event_cam2"); + interrupt_error_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "CAM_U::interrupt_error_event"); + vsync_interrupt_error_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "CAM_U::vsync_interrupt_error_event"); } void Shutdown() { + completion_event_cam1 = nullptr; + completion_event_cam2 = nullptr; + interrupt_error_event = nullptr; + vsync_interrupt_error_event = nullptr; } } // namespace CAM diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index edd524841..2f4923728 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -4,12 +4,429 @@ #pragma once +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + #include "core/hle/kernel/kernel.h" #include "core/hle/service/service.h" namespace Service { namespace CAM { +enum class Port : u8 { + None = 0, + Cam1 = 1, + Cam2 = 2, + Both = Cam1 | Cam2 +}; + +enum class CameraSelect : u8 { + None = 0, + Out1 = 1, + In1 = 2, + Out2 = 4, + In1Out1 = Out1 | In1, + Out1Out2 = Out1 | Out2, + In1Out2 = In1 | Out2, + All = Out1 | In1 | Out2 +}; + +enum class Effect : u8 { + None = 0, + Mono = 1, + Sepia = 2, + Negative = 3, + Negafilm = 4, + Sepia01 = 5 +}; + +enum class Context : u8 { + None = 0, + A = 1, + B = 2, + Both = A | B +}; + +enum class Flip : u8 { + None = 0, + Horizontal = 1, + Vertical = 2, + Reverse = 3 +}; + +enum class Size : u8 { + VGA = 0, + QVGA = 1, + QQVGA = 2, + CIF = 3, + QCIF = 4, + DS_LCD = 5, + DS_LCDx4 = 6, + CTR_TOP_LCD = 7, + CTR_BOTTOM_LCD = QVGA +}; + +enum class FrameRate : u8 { + Rate_15 = 0, + Rate_15_To_5 = 1, + Rate_15_To_2 = 2, + Rate_10 = 3, + Rate_8_5 = 4, + Rate_5 = 5, + Rate_20 = 6, + Rate_20_To_5 = 7, + Rate_30 = 8, + Rate_30_To_5 = 9, + Rate_15_To_10 = 10, + Rate_20_To_10 = 11, + Rate_30_To_10 = 12 +}; + +enum class ShutterSoundType : u8 { + Normal = 0, + Movie = 1, + MovieEnd = 2 +}; + +enum class WhiteBalance : u8 { + BalanceAuto = 0, + Balance3200K = 1, + Balance4150K = 2, + Balance5200K = 3, + Balance6000K = 4, + Balance7000K = 5, + BalanceMax = 6, + BalanceNormal = BalanceAuto, + BalanceTungsten = Balance3200K, + BalanceWhiteFluorescentLight = Balance4150K, + BalanceDaylight = Balance5200K, + BalanceCloudy = Balance6000K, + BalanceHorizon = Balance6000K, + BalanceShade = Balance7000K +}; + +enum class PhotoMode : u8 { + Normal = 0, + Portrait = 1, + Landscape = 2, + Nightview = 3, + Letter0 = 4 +}; + +enum class LensCorrection : u8 { + Off = 0, + On70 = 1, + On90 = 2, + Dark = Off, + Normal = On70, + Bright = On90 +}; + +enum class Contrast : u8 { + Pattern01 = 1, + Pattern02 = 2, + Pattern03 = 3, + Pattern04 = 4, + Pattern05 = 5, + Pattern06 = 6, + Pattern07 = 7, + Pattern08 = 8, + Pattern09 = 9, + Pattern10 = 10, + Pattern11 = 11, + Low = Pattern05, + Normal = Pattern06, + High = Pattern07 +}; + +enum class OutputFormat : u8 { + YUV422 = 0, + RGB565 = 1 +}; + +/// Stereo camera calibration data. +struct StereoCameraCalibrationData { + u8 isValidRotationXY; ///< Bool indicating whether the X and Y rotation data is valid. + INSERT_PADDING_BYTES(3); + float_le scale; ///< Scale to match the left camera image with the right. + float_le rotationZ; ///< Z axis rotation to match the left camera image with the right. + float_le translationX; ///< X axis translation to match the left camera image with the right. + float_le translationY; ///< Y axis translation to match the left camera image with the right. + float_le rotationX; ///< X axis rotation to match the left camera image with the right. + float_le rotationY; ///< Y axis rotation to match the left camera image with the right. + float_le angleOfViewRight; ///< Right camera angle of view. + float_le angleOfViewLeft; ///< Left camera angle of view. + float_le distanceToChart; ///< Distance between cameras and measurement chart. + float_le distanceCameras; ///< Distance between left and right cameras. + s16_le imageWidth; ///< Image width. + s16_le imageHeight; ///< Image height. + INSERT_PADDING_BYTES(16); +}; +static_assert(sizeof(StereoCameraCalibrationData) == 64, "StereoCameraCalibrationData structure size is wrong"); + +struct PackageParameterCameraSelect { + CameraSelect camera; + s8 exposure; + WhiteBalance white_balance; + s8 sharpness; + bool auto_exposure; + bool auto_white_balance; + FrameRate frame_rate; + PhotoMode photo_mode; + Contrast contrast; + LensCorrection lens_correction; + bool noise_filter; + u8 padding; + s16 auto_exposure_window_x; + s16 auto_exposure_window_y; + s16 auto_exposure_window_width; + s16 auto_exposure_window_height; + s16 auto_white_balance_window_x; + s16 auto_white_balance_window_y; + s16 auto_white_balance_window_width; + s16 auto_white_balance_window_height; +}; + +static_assert(sizeof(PackageParameterCameraSelect) == 28, "PackageParameterCameraSelect structure size is wrong"); + +/** + * Unknown + * Inputs: + * 0: 0x00010040 + * 1: u8 Camera port (`Port` enum) + * Outputs: + * 0: 0x00010040 + * 1: ResultCode + */ +void StartCapture(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x00020040 + * 1: u8 Camera port (`Port` enum) + * Outputs: + * 0: 0x00020040 + * 1: ResultCode + */ +void StopCapture(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x00050040 + * 1: u8 Camera port (`Port` enum) + * Outputs: + * 0: 0x00050042 + * 1: ResultCode + * 2: Descriptor: Handle + * 3: Event handle + */ +void GetVsyncInterruptEvent(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x00060040 + * 1: u8 Camera port (`Port` enum) + * Outputs: + * 0: 0x00060042 + * 1: ResultCode + * 2: Descriptor: Handle + * 3: Event handle + */ +void GetBufferErrorInterruptEvent(Service::Interface* self); + +/** + * Sets the target buffer to receive a frame of image data and starts the transfer. Each camera + * port has its own event to signal the end of the transfer. + * + * Inputs: + * 0: 0x00070102 + * 1: Destination address in calling process + * 2: u8 Camera port (`Port` enum) + * 3: Image size (in bytes?) + * 4: u16 Transfer unit size (in bytes?) + * 5: Descriptor: Handle + * 6: Handle to destination process + * Outputs: + * 0: 0x00070042 + * 1: ResultCode + * 2: Descriptor: Handle + * 3: Handle to event signalled when transfer finishes + */ +void SetReceiving(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x00090100 + * 1: u8 Camera port (`Port` enum) + * 2: u16 Number of lines to transfer + * 3: u16 Width + * 4: u16 Height + * Outputs: + * 0: 0x00090040 + * 1: ResultCode + */ +void SetTransferLines(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x000A0080 + * 1: u16 Width + * 2: u16 Height + * Outputs: + * 0: 0x000A0080 + * 1: ResultCode + * 2: Maximum number of lines that fit in the buffer(?) + */ +void GetMaxLines(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x000C0040 + * 1: u8 Camera port (`Port` enum) + * Outputs: + * 0: 0x000C0080 + * 1: ResultCode + * 2: Total number of bytes for each frame with current settings(?) + */ +void GetTransferBytes(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x000E0080 + * 1: u8 Camera port (`Port` enum) + * 2: u8 bool Enable trimming if true + * Outputs: + * 0: 0x000E0040 + * 1: ResultCode + */ +void SetTrimming(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x00120140 + * 1: u8 Camera port (`Port` enum) + * 2: s16 Trim width(?) + * 3: s16 Trim height(?) + * 4: s16 Camera width(?) + * 5: s16 Camera height(?) + * Outputs: + * 0: 0x00120040 + * 1: ResultCode + */ +void SetTrimmingParamsCenter(Service::Interface* self); + +/** + * Selects up to two physical cameras to enable. + * Inputs: + * 0: 0x00130040 + * 1: u8 Cameras to activate (`CameraSelect` enum) + * Outputs: + * 0: 0x00130040 + * 1: ResultCode + */ +void Activate(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x001D00C0 + * 1: u8 Camera select (`CameraSelect` enum) + * 2: u8 Type of flipping to perform (`Flip` enum) + * 3: u8 Context (`Context` enum) + * Outputs: + * 0: 0x001D0040 + * 1: ResultCode + */ +void FlipImage(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x001F00C0 + * 1: u8 Camera select (`CameraSelect` enum) + * 2: u8 Camera frame resolution (`Size` enum) + * 3: u8 Context id (`Context` enum) + * Outputs: + * 0: 0x001F0040 + * 1: ResultCode + */ +void SetSize(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x00200080 + * 1: u8 Camera select (`CameraSelect` enum) + * 2: u8 Camera framerate (`FrameRate` enum) + * Outputs: + * 0: 0x00200040 + * 1: ResultCode + */ +void SetFrameRate(Service::Interface* self); + +/** + * Returns calibration data relating the outside cameras to eachother, for use in AR applications. + * + * Inputs: + * 0: 0x002B0000 + * Outputs: + * 0: 0x002B0440 + * 1: ResultCode + * 2-17: `StereoCameraCalibrationData` structure with calibration values + */ +void GetStereoCameraCalibrationData(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x00360000 + * Outputs: + * 0: 0x00360080 + * 1: ResultCode + * 2: ? + */ +void GetSuitableY2rStandardCoefficient(Service::Interface* self); + +/** + * Unknown + * Inputs: + * 0: 0x00380040 + * 1: u8 Sound ID + * Outputs: + * 0: 0x00380040 + * 1: ResultCode + */ +void PlayShutterSound(Service::Interface* self); + +/** + * Initializes the camera driver. Must be called before using other functions. + * Inputs: + * 0: 0x00390000 + * Outputs: + * 0: 0x00390040 + * 1: ResultCode + */ +void DriverInitialize(Service::Interface* self); + +/** + * Shuts down the camera driver. + * Inputs: + * 0: 0x003A0000 + * Outputs: + * 0: 0x003A0040 + * 1: ResultCode + */ +void DriverFinalize(Service::Interface* self); + /// Initialize CAM service(s) void Init(); diff --git a/src/core/hle/service/cam/cam_c.cpp b/src/core/hle/service/cam/cam_c.cpp index d35adcb9f..8fa7abc85 100644 --- a/src/core/hle/service/cam/cam_c.cpp +++ b/src/core/hle/service/cam/cam_c.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_c.h" namespace Service { diff --git a/src/core/hle/service/cam/cam_q.cpp b/src/core/hle/service/cam/cam_q.cpp index c2760a102..d3ba91e9d 100644 --- a/src/core/hle/service/cam/cam_q.cpp +++ b/src/core/hle/service/cam/cam_q.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_q.h" namespace Service { diff --git a/src/core/hle/service/cam/cam_s.cpp b/src/core/hle/service/cam/cam_s.cpp index aefbf7df4..2a13984d8 100644 --- a/src/core/hle/service/cam/cam_s.cpp +++ b/src/core/hle/service/cam/cam_s.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_s.h" namespace Service { diff --git a/src/core/hle/service/cam/cam_u.cpp b/src/core/hle/service/cam/cam_u.cpp index 55083e0c7..a1070ebb2 100644 --- a/src/core/hle/service/cam/cam_u.cpp +++ b/src/core/hle/service/cam/cam_u.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_u.h" @@ -10,25 +9,25 @@ namespace Service { namespace CAM { const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, nullptr, "StartCapture"}, - {0x00020040, nullptr, "StopCapture"}, + {0x00010040, StartCapture, "StartCapture"}, + {0x00020040, StopCapture, "StopCapture"}, {0x00030040, nullptr, "IsBusy"}, {0x00040040, nullptr, "ClearBuffer"}, - {0x00050040, nullptr, "GetVsyncInterruptEvent"}, - {0x00060040, nullptr, "GetBufferErrorInterruptEvent"}, - {0x00070102, nullptr, "SetReceiving"}, + {0x00050040, GetVsyncInterruptEvent, "GetVsyncInterruptEvent"}, + {0x00060040, GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"}, + {0x00070102, SetReceiving, "SetReceiving"}, {0x00080040, nullptr, "IsFinishedReceiving"}, - {0x00090100, nullptr, "SetTransferLines"}, - {0x000A0080, nullptr, "GetMaxLines"}, + {0x00090100, SetTransferLines, "SetTransferLines"}, + {0x000A0080, GetMaxLines, "GetMaxLines"}, {0x000B0100, nullptr, "SetTransferBytes"}, - {0x000C0040, nullptr, "GetTransferBytes"}, + {0x000C0040, GetTransferBytes, "GetTransferBytes"}, {0x000D0080, nullptr, "GetMaxBytes"}, - {0x000E0080, nullptr, "SetTrimming"}, + {0x000E0080, SetTrimming, "SetTrimming"}, {0x000F0040, nullptr, "IsTrimming"}, {0x00100140, nullptr, "SetTrimmingParams"}, {0x00110040, nullptr, "GetTrimmingParams"}, - {0x00120140, nullptr, "SetTrimmingParamsCenter"}, - {0x00130040, nullptr, "Activate"}, + {0x00120140, SetTrimmingParamsCenter, "SetTrimmingParamsCenter"}, + {0x00130040, Activate, "Activate"}, {0x00140080, nullptr, "SwitchContext"}, {0x00150080, nullptr, "SetExposure"}, {0x00160080, nullptr, "SetWhiteBalance"}, @@ -38,10 +37,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001A0040, nullptr, "IsAutoExposure"}, {0x001B0080, nullptr, "SetAutoWhiteBalance"}, {0x001C0040, nullptr, "IsAutoWhiteBalance"}, - {0x001D00C0, nullptr, "FlipImage"}, + {0x001D00C0, FlipImage, "FlipImage"}, {0x001E0200, nullptr, "SetDetailSize"}, - {0x001F00C0, nullptr, "SetSize"}, - {0x00200080, nullptr, "SetFrameRate"}, + {0x001F00C0, SetSize, "SetSize"}, + {0x00200080, SetFrameRate, "SetFrameRate"}, {0x00210080, nullptr, "SetPhotoMode"}, {0x002200C0, nullptr, "SetEffect"}, {0x00230080, nullptr, "SetContrast"}, @@ -52,17 +51,22 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00280080, nullptr, "SetNoiseFilter"}, {0x00290080, nullptr, "SynchronizeVsyncTiming"}, {0x002A0080, nullptr, "GetLatestVsyncTiming"}, - {0x002B0000, nullptr, "GetStereoCameraCalibrationData"}, + {0x002B0000, GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"}, {0x002C0400, nullptr, "SetStereoCameraCalibrationData"}, + {0x002D00C0, nullptr, "WriteRegisterI2c"}, + {0x002E00C0, nullptr, "WriteMcuVariableI2c"}, + {0x002F0080, nullptr, "ReadRegisterI2cExclusive"}, + {0x00300080, nullptr, "ReadMcuVariableI2cExclusive"}, {0x00310180, nullptr, "SetImageQualityCalibrationData"}, {0x00320000, nullptr, "GetImageQualityCalibrationData"}, {0x003302C0, nullptr, "SetPackageParameterWithoutContext"}, {0x00340140, nullptr, "SetPackageParameterWithContext"}, {0x003501C0, nullptr, "SetPackageParameterWithContextDetail"}, - {0x00360000, nullptr, "GetSuitableY2rStandardCoefficient"}, - {0x00380040, nullptr, "PlayShutterSound"}, - {0x00390000, nullptr, "DriverInitialize"}, - {0x003A0000, nullptr, "DriverFinalize"}, + {0x00360000, GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"}, + {0x00370202, nullptr, "PlayShutterSoundWithWave"}, + {0x00380040, PlayShutterSound, "PlayShutterSound"}, + {0x00390000, DriverInitialize, "DriverInitialize"}, + {0x003A0000, DriverFinalize, "DriverFinalize"}, {0x003B0000, nullptr, "GetActivatedCamera"}, {0x003C0000, nullptr, "GetSleepCamera"}, {0x003D0040, nullptr, "SetSleepCamera"}, diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index db0e52b79..6d79ce9b4 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -9,10 +9,6 @@ #include "core/hle/service/cecd/cecd_s.h" #include "core/hle/service/cecd/cecd_u.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" -#include "core/hle/hle.h" - namespace Service { namespace CECD { diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h index 32fd2045d..9e158521b 100644 --- a/src/core/hle/service/cecd/cecd.h +++ b/src/core/hle/service/cecd/cecd.h @@ -4,9 +4,6 @@ #pragma once -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" - namespace Service { namespace CECD { diff --git a/src/core/hle/service/cecd/cecd_s.cpp b/src/core/hle/service/cecd/cecd_s.cpp index 72d7e8d44..bfd821c07 100644 --- a/src/core/hle/service/cecd/cecd_s.cpp +++ b/src/core/hle/service/cecd/cecd_s.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_s.h" namespace Service { diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp index 0a23bafbc..9b720a738 100644 --- a/src/core/hle/service/cecd/cecd_u.cpp +++ b/src/core/hle/service/cecd/cecd_u.cpp @@ -2,18 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_u.h" namespace Service { namespace CECD { -// Empty arrays are illegal -- commented out until an entry is added. -//const Interface::FunctionInfo FunctionTable[] = { }; +static const Interface::FunctionInfo FunctionTable[] = { + { 0x00120104, nullptr, "ReadSavedData" }, +}; CECD_U_Interface::CECD_U_Interface() { - //Register(FunctionTable); + Register(FunctionTable); } } // namespace CECD diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 56986a49e..4c82a58e4 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -4,13 +4,15 @@ #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" #include "core/settings.h" +#include "core/hle/result.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_i.h" #include "core/hle/service/cfg/cfg_s.h" @@ -292,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"); @@ -333,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); @@ -343,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); @@ -404,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/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index fc2a16a04..606ab99cf 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -5,10 +5,15 @@ #pragma once #include <array> -#include "core/hle/result.h" -#include "core/hle/service/service.h" + +#include "common/common_types.h" + +union ResultCode; namespace Service { + +class Interface; + namespace CFG { enum SystemModel { diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 5aeadc084..0559a07b2 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_i.h" diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index a329514a6..b03d290e5 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_s.h" diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index 18939750b..89ae96c9e 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_u.h" diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 669659510..6318bf2a7 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -22,9 +22,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00060000, nullptr, "ReleaseSoundChannels"}, {0x00070000, nullptr, "AcquireCaptureDevice"}, {0x00080040, nullptr, "ReleaseCaptureDevice"}, - {0x00090082, nullptr, "FlushDCache"}, - {0x000A0082, nullptr, "StoreDCache"}, - {0x000B0082, nullptr, "InvalidateDCache"}, + {0x00090082, nullptr, "FlushDataCache"}, + {0x000A0082, nullptr, "StoreDataCache"}, + {0x000B0082, nullptr, "InvalidateDataCache"}, + {0x000C0000, nullptr, "Reset"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index ce5619069..3ba24d466 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -2,9 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> + +#include "audio_core/hle/pipe.h" + +#include "common/hash.h" #include "common/logging/log.h" -#include "core/hle/hle.h" #include "core/hle/kernel/event.h" #include "core/hle/service/dsp_dsp.h" @@ -15,17 +19,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(); } /** @@ -41,18 +58,18 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) { u32 addr = cmd_buff[1]; - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); - LOG_WARNING(Service_DSP, "(STUBBED) called with address 0x%08X", addr); + LOG_DEBUG(Service_DSP, "addr=0x%08X", addr); } /** * DSP_DSP::LoadComponent service function * Inputs: * 1 : Size - * 2 : Unknown (observed only half word used) - * 3 : Unknown (observed only half word used) + * 2 : Program mask (observed only half word used) + * 3 : Data mask (observed only half word used) * 4 : (size << 4) | 0xA * 5 : Buffer address * Outputs: @@ -63,18 +80,28 @@ static void LoadComponent(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 size = cmd_buff[1]; - u32 unk1 = cmd_buff[2]; - u32 unk2 = cmd_buff[3]; - u32 new_size = cmd_buff[4]; + u32 prog_mask = cmd_buff[2]; + u32 data_mask = cmd_buff[3]; + u32 desc = cmd_buff[4]; u32 buffer = cmd_buff[5]; - cmd_buff[1] = 0; // No error + cmd_buff[0] = IPC::MakeHeader(0x11, 2, 2); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware + cmd_buff[3] = desc; + cmd_buff[4] = buffer; // TODO(bunnei): Implement real DSP firmware loading - LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, unk1=0x%08X, unk2=0x%08X, new_size=0x%X, buffer=0x%08X", - size, unk1, unk2, new_size, buffer); + ASSERT(Memory::GetPointer(buffer) != nullptr); + ASSERT(size > 0x37C); + + LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer), size)); + // Some versions of the firmware have the location of DSP structures listed here. + LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer) + 0x340, 60)); + + LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", + size, prog_mask, data_mask, buffer); } /** @@ -115,15 +142,14 @@ static void FlushDataCache(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_DEBUG(Service_DSP, "(STUBBED) called address=0x%08X, size=0x%X, process=0x%08X", - address, size, process); + LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, process); } /** * 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 @@ -131,37 +157,37 @@ 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_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); + } else { + LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); + ASSERT(false); // This should really be handled at a IPC translation layer. + } } 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_INFO(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); } /** - * DSP_DSP::WriteReg0x10 service function + * DSP_DSP::SetSemaphore service function * Inputs: * 1 : Unknown (observed only half word used) * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ -static void WriteReg0x10(Service::Interface* self) { +static void SetSemaphore(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - SignalInterrupt(); - - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_DSP, "(STUBBED) called"); } @@ -169,9 +195,9 @@ static void WriteReg0x10(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 @@ -180,24 +206,36 @@ static void WriteReg0x10(Service::Interface* self) { static void WriteProcessPipe(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 number = cmd_buff[1]; - u32 size = cmd_buff[2]; - u32 new_size = cmd_buff[3]; - u32 buffer = cmd_buff[4]; + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 size = cmd_buff[2]; + u32 buffer = cmd_buff[4]; + + ASSERT_MSG(IPC::StaticBufferDesc(size, 1) == cmd_buff[3], "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer); + ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + + std::vector<u8> message(size); + + for (size_t i = 0; i < size; i++) { + message[i] = Memory::Read8(buffer + i); + } + + DSP::HLE::PipeWrite(pipe, 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_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, 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 + * 0x41 : Virtual address of memory buffer to write pipe contents to * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Number of bytes read from pipe @@ -205,35 +243,82 @@ static void WriteProcessPipe(Service::Interface* self) { static void ReadPipeIfPossible(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 unk1 = cmd_buff[1]; - u32 unk2 = cmd_buff[2]; - u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 unknown = 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 - }}; + ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); - u32 initial_size = read_pipe_count; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { + 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[2] = static_cast<u32>(response.size()); + } else { + cmd_buff[2] = 0; // Return no data } - cmd_buff[1] = 0; // No error - cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16); + LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); +} - LOG_WARNING(Service_DSP, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X", - unk1, unk2, size, addr); +/** + * DSP_DSP::ReadPipe service function + * Inputs: + * 1 : Pipe Number + * 2 : Unknown + * 3 : Size in bytes of read (observed only lower half word used) + * 0x41 : Virtual address of memory buffer to write pipe contents to + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Number of bytes read from pipe + */ +static void ReadPipe(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 unknown = cmd_buff[2]; + u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size + VAddr addr = cmd_buff[0x41]; + + ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); + + if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { + std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); + + Memory::WriteBlock(addr, response.data(), response.size()); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = static_cast<u32>(response.size()); + } else { + // No more data is in pipe. Hardware hangs in this case; this should never happen. + UNREACHABLE(); + } + + LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); +} + +/** + * DSP_DSP::GetPipeReadableSize service function + * Inputs: + * 1 : Pipe Number + * 2 : Unknown + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Number of bytes readable from pipe + */ +static void GetPipeReadableSize(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 unknown = cmd_buff[2]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe); + + LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, cmd_buff[2]); } /** @@ -268,20 +353,86 @@ static void GetHeadphoneStatus(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; // Not using headphones? - LOG_DEBUG(Service_DSP, "(STUBBED) called"); + LOG_WARNING(Service_DSP, "(STUBBED) called"); +} + +/** + * DSP_DSP::RecvData service function + * This function reads a value out of a DSP register. + * Inputs: + * 1 : Register Number + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Value in the register + * Notes: + * This function has only been observed being called with a register number of 0. + */ +static void RecvData(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 register_number = cmd_buff[1]; + + ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); + + // Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept. + + cmd_buff[1] = RESULT_SUCCESS.raw; + switch (DSP::HLE::GetDspState()) { + case DSP::HLE::DspState::On: + cmd_buff[2] = 0; + break; + case DSP::HLE::DspState::Off: + case DSP::HLE::DspState::Sleeping: + cmd_buff[2] = 1; + break; + default: + UNREACHABLE(); + break; + } + + LOG_DEBUG(Service_DSP, "register_number=%u", register_number); +} + +/** + * DSP_DSP::RecvDataIsReady service function + * This function checks whether a DSP register is ready to be read. + * Inputs: + * 1 : Register Number + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : non-zero == ready + * Note: + * This function has only been observed being called with a register number of 0. + */ +static void RecvDataIsReady(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 register_number = cmd_buff[1]; + + ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; // Ready to read + + LOG_DEBUG(Service_DSP, "register_number=%u", register_number); } const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, nullptr, "RecvData"}, - {0x00020040, nullptr, "RecvDataIsReady"}, + {0x00010040, RecvData, "RecvData"}, + {0x00020040, RecvDataIsReady, "RecvDataIsReady"}, {0x00030080, nullptr, "SendData"}, {0x00040040, nullptr, "SendDataIsEmpty"}, - {0x00070040, WriteReg0x10, "WriteReg0x10"}, + {0x000500C2, nullptr, "SendFifoEx"}, + {0x000600C0, nullptr, "RecvFifoEx"}, + {0x00070040, SetSemaphore, "SetSemaphore"}, {0x00080000, nullptr, "GetSemaphore"}, {0x00090040, nullptr, "ClearSemaphore"}, + {0x000A0040, nullptr, "MaskSemaphore"}, {0x000B0000, nullptr, "CheckSemaphoreRequest"}, {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"}, {0x000D0082, WriteProcessPipe, "WriteProcessPipe"}, + {0x000E00C0, ReadPipe, "ReadPipe"}, + {0x000F0080, GetPipeReadableSize, "GetPipeReadableSize"}, {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"}, {0x001100C2, LoadComponent, "LoadComponent"}, {0x00120000, nullptr, "UnloadComponent"}, @@ -295,7 +446,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, {0x001C0082, nullptr, "SetIirFilterEQ"}, + {0x001D00C0, nullptr, "ReadMultiEx_SPI2"}, + {0x001E00C2, nullptr, "WriteMultiEx_SPI2"}, {0x001F0000, GetHeadphoneStatus, "GetHeadphoneStatus"}, + {0x00200040, nullptr, "ForceHeadphoneOut"}, {0x00210000, nullptr, "GetIsDspOccupied"}, }; @@ -304,7 +458,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); @@ -312,7 +465,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/err_f.cpp b/src/core/hle/service/err_f.cpp index e8c06c1cf..5f9cf6e94 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -2,9 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/bit_field.h" +#include "common/common_types.h" #include "common/logging/log.h" -#include "core/hle/hle.h" #include "core/hle/service/err_f.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp index 2911ab402..c13ffd9d2 100644 --- a/src/core/hle/service/frd/frd.cpp +++ b/src/core/hle/service/frd/frd.cpp @@ -7,10 +7,6 @@ #include "core/hle/service/frd/frd_a.h" #include "core/hle/service/frd/frd_u.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" -#include "core/hle/hle.h" - namespace Service { namespace FRD { diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h index 41f7a2f6b..f9f88b444 100644 --- a/src/core/hle/service/frd/frd.h +++ b/src/core/hle/service/frd/frd.h @@ -4,9 +4,6 @@ #pragma once -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" - namespace Service { namespace FRD { diff --git a/src/core/hle/service/frd/frd_a.cpp b/src/core/hle/service/frd/frd_a.cpp index 1c438a337..818d610f3 100644 --- a/src/core/hle/service/frd/frd_a.cpp +++ b/src/core/hle/service/frd/frd_a.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/frd/frd.h" #include "core/hle/service/frd/frd_a.h" namespace Service { diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index 3a5897d06..2c6885377 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/frd/frd.h" #include "core/hle/service/frd/frd_u.h" namespace Service { @@ -11,25 +9,58 @@ namespace FRD { const Interface::FunctionInfo FunctionTable[] = { {0x00010000, nullptr, "HasLoggedIn"}, + {0x00020000, nullptr, "IsOnline"}, {0x00030000, nullptr, "Login"}, {0x00040000, nullptr, "Logout"}, - {0x00050000, nullptr, "GetFriendKey"}, + {0x00050000, nullptr, "GetMyFriendKey"}, + {0x00060000, nullptr, "GetMyPreference"}, + {0x00070000, nullptr, "GetMyProfile"}, {0x00080000, nullptr, "GetMyPresence"}, {0x00090000, nullptr, "GetMyScreenName"}, - {0x00100040, nullptr, "GetPassword"}, + {0x000A0000, nullptr, "GetMyMii"}, + {0x000B0000, nullptr, "GetMyLocalAccountId"}, + {0x000C0000, nullptr, "GetMyPlayingGame"}, + {0x000D0000, nullptr, "GetMyFavoriteGame"}, + {0x000E0000, nullptr, "GetMyNcPrincipalId"}, + {0x000F0000, nullptr, "GetMyComment"}, + {0x00100040, nullptr, "GetMyPassword"}, {0x00110080, nullptr, "GetFriendKeyList"}, + {0x00120042, nullptr, "GetFriendPresence"}, + {0x00130142, nullptr, "GetFriendScreenName"}, + {0x00140044, nullptr, "GetFriendMii"}, + {0x00150042, nullptr, "GetFriendProfile"}, + {0x00160042, nullptr, "GetFriendRelationship"}, + {0x00170042, nullptr, "GetFriendAttributeFlags"}, + {0x00180044, nullptr, "GetFriendPlayingGame"}, {0x00190042, nullptr, "GetFriendFavoriteGame"}, {0x001A00C4, nullptr, "GetFriendInfo"}, - {0x001B0080, nullptr, "IsOnFriendList"}, - {0x001C0042, nullptr, "DecodeLocalFriendCode"}, - {0x001D0002, nullptr, "SetCurrentlyPlayingText"}, + {0x001B0080, nullptr, "IsIncludedInFriendList"}, + {0x001C0042, nullptr, "UnscrambleLocalFriendCode"}, + {0x001D0002, nullptr, "UpdateGameModeDescription"}, + {0x001E02C2, nullptr, "UpdateGameMode"}, + {0x001F0042, nullptr, "SendInvitation"}, + {0x00200002, nullptr, "AttachToEventNotification"}, + {0x00210040, nullptr, "SetNotificationMask"}, + {0x00220040, nullptr, "GetEventNotification"}, {0x00230000, nullptr, "GetLastResponseResult"}, + {0x00240040, nullptr, "PrincipalIdToFriendCode"}, + {0x00250080, nullptr, "FriendCodeToPrincipalId"}, + {0x00260080, nullptr, "IsValidFriendCode"}, {0x00270040, nullptr, "ResultToErrorCode"}, {0x00280244, nullptr, "RequestGameAuthentication"}, {0x00290000, nullptr, "GetGameAuthenticationData"}, {0x002A0204, nullptr, "RequestServiceLocator"}, {0x002B0000, nullptr, "GetServiceLocatorData"}, + {0x002C0002, nullptr, "DetectNatProperties"}, + {0x002D0000, nullptr, "GetNatProperties"}, + {0x002E0000, nullptr, "GetServerTimeInterval"}, + {0x002F0040, nullptr, "AllowHalfAwake"}, + {0x00300000, nullptr, "GetServerTypes"}, + {0x00310082, nullptr, "GetFriendComment"}, {0x00320042, nullptr, "SetClientSdkVersion"}, + {0x00330000, nullptr, "GetMyApproachContext"}, + {0x00340046, nullptr, "AddFriendWithApproach"}, + {0x00350082, nullptr, "DecryptApproachContext"}, }; FRD_U_Interface::FRD_U_Interface() { diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index b3fa89302..632620a56 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -708,96 +708,114 @@ static void GetPriority(Service::Interface* self) { } const Interface::FunctionInfo FunctionTable[] = { - {0x000100C6, nullptr, "Dummy1"}, - {0x040100C4, nullptr, "Control"}, - {0x08010002, Initialize, "Initialize"}, - {0x080201C2, OpenFile, "OpenFile"}, - {0x08030204, OpenFileDirectly, "OpenFileDirectly"}, - {0x08040142, DeleteFile, "DeleteFile"}, - {0x08050244, RenameFile, "RenameFile"}, - {0x08060142, DeleteDirectory, "DeleteDirectory"}, - {0x08070142, nullptr, "DeleteDirectoryRecursively"}, - {0x08080202, CreateFile, "CreateFile"}, - {0x08090182, CreateDirectory, "CreateDirectory"}, - {0x080A0244, RenameDirectory, "RenameDirectory"}, - {0x080B0102, OpenDirectory, "OpenDirectory"}, - {0x080C00C2, OpenArchive, "OpenArchive"}, - {0x080D0144, nullptr, "ControlArchive"}, - {0x080E0080, CloseArchive, "CloseArchive"}, - {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"}, - {0x08100200, nullptr, "CreateSystemSaveData"}, - {0x08110040, nullptr, "DeleteSystemSaveData"}, - {0x08120080, GetFreeBytes, "GetFreeBytes"}, - {0x08130000, nullptr, "GetCardType"}, - {0x08140000, nullptr, "GetSdmcArchiveResource"}, - {0x08150000, nullptr, "GetNandArchiveResource"}, - {0x08160000, nullptr, "GetSdmcFatfsErro"}, - {0x08170000, IsSdmcDetected, "IsSdmcDetected"}, - {0x08180000, IsSdmcWriteable, "IsSdmcWritable"}, - {0x08190042, nullptr, "GetSdmcCid"}, - {0x081A0042, nullptr, "GetNandCid"}, - {0x081B0000, nullptr, "GetSdmcSpeedInfo"}, - {0x081C0000, nullptr, "GetNandSpeedInfo"}, - {0x081D0042, nullptr, "GetSdmcLog"}, - {0x081E0042, nullptr, "GetNandLog"}, - {0x081F0000, nullptr, "ClearSdmcLog"}, - {0x08200000, nullptr, "ClearNandLog"}, - {0x08210000, CardSlotIsInserted, "CardSlotIsInserted"}, - {0x08220000, nullptr, "CardSlotPowerOn"}, - {0x08230000, nullptr, "CardSlotPowerOff"}, - {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"}, - {0x08250040, nullptr, "CardNorDirectCommand"}, - {0x08260080, nullptr, "CardNorDirectCommandWithAddress"}, - {0x08270082, nullptr, "CardNorDirectRead"}, - {0x082800C2, nullptr, "CardNorDirectReadWithAddress"}, - {0x08290082, nullptr, "CardNorDirectWrite"}, - {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"}, - {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"}, - {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"}, - {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"}, - {0x082E0040, nullptr, "GetProductInfo"}, - {0x082F0040, nullptr, "GetProgramLaunchInfo"}, - {0x08300182, nullptr, "CreateExtSaveData"}, - {0x08310180, nullptr, "CreateSharedExtSaveData"}, - {0x08320102, nullptr, "ReadExtSaveDataIcon"}, - {0x08330082, nullptr, "EnumerateExtSaveData"}, - {0x08340082, nullptr, "EnumerateSharedExtSaveData"}, - {0x08350080, nullptr, "DeleteExtSaveData"}, - {0x08360080, nullptr, "DeleteSharedExtSaveData"}, - {0x08370040, nullptr, "SetCardSpiBaudRate"}, - {0x08380040, nullptr, "SetCardSpiBusMode"}, - {0x08390000, nullptr, "SendInitializeInfoTo9"}, - {0x083A0100, nullptr, "GetSpecialContentIndex"}, - {0x083B00C2, nullptr, "GetLegacyRomHeader"}, - {0x083C00C2, nullptr, "GetLegacyBannerData"}, - {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"}, - {0x083E00C2, nullptr, "QueryTotalQuotaSize"}, - {0x083F00C0, nullptr, "GetExtDataBlockSize"}, - {0x08400040, nullptr, "AbnegateAccessRight"}, - {0x08410000, nullptr, "DeleteSdmcRoot"}, - {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"}, - {0x08430000, nullptr, "InitializeCtrFileSystem"}, - {0x08440000, nullptr, "CreateSeed"}, - {0x084500C2, nullptr, "GetFormatInfo"}, - {0x08460102, nullptr, "GetLegacyRomHeader2"}, - {0x08470180, nullptr, "FormatCtrCardUserSaveData"}, - {0x08480042, nullptr, "GetSdmcCtrRootPath"}, - {0x08490040, nullptr, "GetArchiveResource"}, - {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, - {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, - {0x084C0242, FormatSaveData, "FormatSaveData"}, - {0x084D0102, nullptr, "GetLegacySubBannerData"}, - {0x084E0342, nullptr, "UpdateSha256Context"}, - {0x084F0102, nullptr, "ReadSpecialFile"}, - {0x08500040, nullptr, "GetSpecialFileSize"}, - {0x08510242, CreateExtSaveData, "CreateExtSaveData"}, - {0x08520100, DeleteExtSaveData, "DeleteExtSaveData"}, - {0x08560240, CreateSystemSaveData, "CreateSystemSaveData"}, - {0x08570080, DeleteSystemSaveData, "DeleteSystemSaveData"}, - {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"}, + {0x000100C6, nullptr, "Dummy1"}, + {0x040100C4, nullptr, "Control"}, + {0x08010002, Initialize, "Initialize"}, + {0x080201C2, OpenFile, "OpenFile"}, + {0x08030204, OpenFileDirectly, "OpenFileDirectly"}, + {0x08040142, DeleteFile, "DeleteFile"}, + {0x08050244, RenameFile, "RenameFile"}, + {0x08060142, DeleteDirectory, "DeleteDirectory"}, + {0x08070142, nullptr, "DeleteDirectoryRecursively"}, + {0x08080202, CreateFile, "CreateFile"}, + {0x08090182, CreateDirectory, "CreateDirectory"}, + {0x080A0244, RenameDirectory, "RenameDirectory"}, + {0x080B0102, OpenDirectory, "OpenDirectory"}, + {0x080C00C2, OpenArchive, "OpenArchive"}, + {0x080D0144, nullptr, "ControlArchive"}, + {0x080E0080, CloseArchive, "CloseArchive"}, + {0x080F0180, FormatThisUserSaveData, "FormatThisUserSaveData"}, + {0x08100200, nullptr, "CreateSystemSaveData"}, + {0x08110040, nullptr, "DeleteSystemSaveData"}, + {0x08120080, GetFreeBytes, "GetFreeBytes"}, + {0x08130000, nullptr, "GetCardType"}, + {0x08140000, nullptr, "GetSdmcArchiveResource"}, + {0x08150000, nullptr, "GetNandArchiveResource"}, + {0x08160000, nullptr, "GetSdmcFatfsError"}, + {0x08170000, IsSdmcDetected, "IsSdmcDetected"}, + {0x08180000, IsSdmcWriteable, "IsSdmcWritable"}, + {0x08190042, nullptr, "GetSdmcCid"}, + {0x081A0042, nullptr, "GetNandCid"}, + {0x081B0000, nullptr, "GetSdmcSpeedInfo"}, + {0x081C0000, nullptr, "GetNandSpeedInfo"}, + {0x081D0042, nullptr, "GetSdmcLog"}, + {0x081E0042, nullptr, "GetNandLog"}, + {0x081F0000, nullptr, "ClearSdmcLog"}, + {0x08200000, nullptr, "ClearNandLog"}, + {0x08210000, CardSlotIsInserted, "CardSlotIsInserted"}, + {0x08220000, nullptr, "CardSlotPowerOn"}, + {0x08230000, nullptr, "CardSlotPowerOff"}, + {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"}, + {0x08250040, nullptr, "CardNorDirectCommand"}, + {0x08260080, nullptr, "CardNorDirectCommandWithAddress"}, + {0x08270082, nullptr, "CardNorDirectRead"}, + {0x082800C2, nullptr, "CardNorDirectReadWithAddress"}, + {0x08290082, nullptr, "CardNorDirectWrite"}, + {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"}, + {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"}, + {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"}, + {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"}, + {0x082E0040, nullptr, "GetProductInfo"}, + {0x082F0040, nullptr, "GetProgramLaunchInfo"}, + {0x08300182, nullptr, "CreateExtSaveData"}, + {0x08310180, nullptr, "CreateSharedExtSaveData"}, + {0x08320102, nullptr, "ReadExtSaveDataIcon"}, + {0x08330082, nullptr, "EnumerateExtSaveData"}, + {0x08340082, nullptr, "EnumerateSharedExtSaveData"}, + {0x08350080, nullptr, "DeleteExtSaveData"}, + {0x08360080, nullptr, "DeleteSharedExtSaveData"}, + {0x08370040, nullptr, "SetCardSpiBaudRate"}, + {0x08380040, nullptr, "SetCardSpiBusMode"}, + {0x08390000, nullptr, "SendInitializeInfoTo9"}, + {0x083A0100, nullptr, "GetSpecialContentIndex"}, + {0x083B00C2, nullptr, "GetLegacyRomHeader"}, + {0x083C00C2, nullptr, "GetLegacyBannerData"}, + {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"}, + {0x083E00C2, nullptr, "QueryTotalQuotaSize"}, + {0x083F00C0, nullptr, "GetExtDataBlockSize"}, + {0x08400040, nullptr, "AbnegateAccessRight"}, + {0x08410000, nullptr, "DeleteSdmcRoot"}, + {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"}, + {0x08430000, nullptr, "InitializeCtrFileSystem"}, + {0x08440000, nullptr, "CreateSeed"}, + {0x084500C2, nullptr, "GetFormatInfo"}, + {0x08460102, nullptr, "GetLegacyRomHeader2"}, + {0x08470180, nullptr, "FormatCtrCardUserSaveData"}, + {0x08480042, nullptr, "GetSdmcCtrRootPath"}, + {0x08490040, nullptr, "GetArchiveResource"}, + {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, + {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, + {0x084C0242, FormatSaveData, "FormatSaveData"}, + {0x084D0102, nullptr, "GetLegacySubBannerData"}, + {0x084E0342, nullptr, "UpdateSha256Context"}, + {0x084F0102, nullptr, "ReadSpecialFile"}, + {0x08500040, nullptr, "GetSpecialFileSize"}, + {0x08510242, CreateExtSaveData, "CreateExtSaveData"}, + {0x08520100, DeleteExtSaveData, "DeleteExtSaveData"}, + {0x08530142, nullptr, "ReadExtSaveDataIcon"}, + {0x085400C0, nullptr, "GetExtDataBlockSize"}, + {0x08550102, nullptr, "EnumerateExtSaveData"}, + {0x08560240, CreateSystemSaveData, "CreateSystemSaveData"}, + {0x08570080, DeleteSystemSaveData, "DeleteSystemSaveData"}, + {0x08580000, nullptr, "StartDeviceMoveAsSource"}, + {0x08590200, nullptr, "StartDeviceMoveAsDestination"}, + {0x085A00C0, nullptr, "SetArchivePriority"}, + {0x085B0080, nullptr, "GetArchivePriority"}, + {0x085C00C0, nullptr, "SetCtrCardLatencyParameter"}, + {0x085D01C0, nullptr, "SetFsCompatibilityInfo"}, + {0x085E0040, nullptr, "ResetCardCompatibilityParameter"}, + {0x085F0040, nullptr, "SwitchCleanupInvalidSaveData"}, + {0x08600042, nullptr, "EnumerateSystemSaveData"}, {0x08610042, InitializeWithSdkVersion, "InitializeWithSdkVersion"}, - {0x08620040, SetPriority, "SetPriority"}, - {0x08630000, GetPriority, "GetPriority"}, + {0x08620040, SetPriority, "SetPriority"}, + {0x08630000, GetPriority, "GetPriority"}, + {0x08640000, nullptr, "GetNandInfo"}, + {0x08650140, nullptr, "SetSaveDataSecureValue"}, + {0x086600C0, nullptr, "GetSaveDataSecureValue"}, + {0x086700C4, nullptr, "ControlSecureSave"}, + {0x08680000, nullptr, "GetMediaType"}, + {0x08690000, nullptr, "GetNandEraseCount"}, + {0x086A0082, nullptr, "ReadNandReport"} }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 481da0c9f..2ace2cade 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -275,7 +275,7 @@ static void FlushDataCache(Service::Interface* self) { u32 size = cmd_buff[2]; u32 process = cmd_buff[4]; - VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(address), size); + VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size); // TODO(purpasmart96): Verify return header on HW @@ -320,7 +320,7 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { * @todo This probably does not belong in the GSP module, instead move to video_core */ void SignalInterrupt(InterruptId interrupt_id) { - if (0 == g_interrupt_event) { + if (nullptr == g_interrupt_event) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); return; } @@ -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); } } } @@ -365,7 +365,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { // GX request DMA - typically used for copying memory from GSP heap to VRAM case CommandId::REQUEST_DMA: - VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), + VideoCore::g_renderer->Rasterizer()->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), command.dma_request.size); memcpy(Memory::GetPointer(command.dma_request.dest_address), @@ -373,7 +373,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { command.dma_request.size); SignalInterrupt(InterruptId::DMA); - VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), + VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), command.dma_request.size); break; @@ -467,7 +467,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { if (region.size == 0) break; - VideoCore::g_renderer->hw_rasterizer->NotifyFlush( + VideoCore::g_renderer->Rasterizer()->InvalidateRegion( Memory::VirtualToPhysicalAddress(region.address), region.size); } break; @@ -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/gsp_lcd.cpp b/src/core/hle/service/gsp_lcd.cpp index 9e36732b4..c700c21c5 100644 --- a/src/core/hle/service/gsp_lcd.cpp +++ b/src/core/hle/service/gsp_lcd.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/bit_field.h" - #include "core/hle/service/gsp_lcd.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -11,14 +9,19 @@ namespace GSP_LCD { -/*const Interface::FunctionInfo FunctionTable[] = { -};*/ +const Interface::FunctionInfo FunctionTable[] = { + {0x000F0000, nullptr, "PowerOnAllBacklights"}, + {0x00100000, nullptr, "PowerOffAllBacklights"}, + {0x00110040, nullptr, "PowerOnBacklight"}, + {0x00120040, nullptr, "PowerOffBacklight"}, + {0x00130040, nullptr, "SetLedForceOff"} +}; //////////////////////////////////////////////////////////////////////////////////////////////////// // Interface class Interface::Interface() { - //Register(FunctionTable); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 2e1e5c3e9..11d7e69a1 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -13,7 +13,6 @@ #include "core/core_timing.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" -#include "core/hle/hle.h" #include "video_core/video_core.h" @@ -106,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/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp index 532931ae0..c50f597eb 100644 --- a/src/core/hle/service/hid/hid_spvr.cpp +++ b/src/core/hle/service/hid/hid_spvr.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_spvr.h" diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp index fbfb9e885..bbdde2abb 100644 --- a/src/core/hle/service/hid/hid_user.cpp +++ b/src/core/hle/service/hid/hid_user.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_user.h" @@ -11,6 +10,8 @@ namespace HID { const Interface::FunctionInfo FunctionTable[] = { {0x000A0000, GetIPCHandles, "GetIPCHandles"}, + {0x000B0000, nullptr, "StartAnalogStickCalibration"}, + {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, {0x00110000, EnableAccelerometer, "EnableAccelerometer"}, {0x00120000, DisableAccelerometer, "DisableAccelerometer"}, {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"}, diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp index 0a3aba0a0..0855ab227 100644 --- a/src/core/hle/service/http_c.cpp +++ b/src/core/hle/service/http_c.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/http_c.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -47,10 +46,20 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00220040, nullptr, "GetResponseStatusCode"}, {0x002300C0, nullptr, "GetResponseStatusCodeTimeout"}, {0x00240082, nullptr, "AddTrustedRootCA"}, + {0x00250080, nullptr, "AddDefaultCert"}, + {0x00260080, nullptr, "SelectRootCertChain"}, + {0x002700C4, nullptr, "SetClientCert"}, + {0x002B0080, nullptr, "SetSSLOpt"}, + {0x002C0080, nullptr, "SetSSLClearOpt"}, + {0x002D0000, nullptr, "CreateRootCertChain"}, + {0x002E0040, nullptr, "DestroyRootCertChain"}, + {0x002F0082, nullptr, "RootCertChainAddCert"}, + {0x00300080, nullptr, "RootCertChainAddDefaultCert"}, {0x00350186, nullptr, "SetDefaultProxy"}, {0x00360000, nullptr, "ClearDNSCache"}, {0x00370080, nullptr, "SetKeepAlive"}, - {0x003800C0, nullptr, "Finalize"}, + {0x003800C0, nullptr, "SetPostDataTypeSize"}, + {0x00390000, nullptr, "Finalize"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp index adfbb258d..c2121cb2e 100644 --- a/src/core/hle/service/ir/ir.cpp +++ b/src/core/hle/service/ir/ir.cpp @@ -2,21 +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/hle.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(); @@ -27,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; @@ -36,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 c16d963e7..72d44ce60 100644 --- a/src/core/hle/service/ir/ir.h +++ b/src/core/hle/service/ir/ir.h @@ -4,10 +4,10 @@ #pragma once -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" - namespace Service { + +class Interface; + namespace IR { /** @@ -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_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 96ae63420..c0300f109 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_rst.h" diff --git a/src/core/hle/service/ir/ir_u.cpp b/src/core/hle/service/ir/ir_u.cpp index 1b1e3078b..96f76cb83 100644 --- a/src/core/hle/service/ir/ir_u.cpp +++ b/src/core/hle/service/ir/ir_u.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_u.h" namespace Service { diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index 8e3ff140f..06a601029 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_user.h" @@ -10,20 +9,32 @@ 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/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp index f84ce4d72..ecec2ce32 100644 --- a/src/core/hle/service/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro.cpp @@ -2,9 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/common_types.h" #include "common/logging/log.h" -#include "core/hle/hle.h" #include "core/hle/service/ldr_ro.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 25e70d321..f792bc9cd 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/mic_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -18,14 +17,14 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00050000, nullptr, "StopSampling"}, {0x00060000, nullptr, "IsSampling"}, {0x00070000, nullptr, "GetEventHandle"}, - {0x00080040, nullptr, "SetControl"}, - {0x00090000, nullptr, "GetControl"}, - {0x000A0040, nullptr, "SetBias"}, - {0x000B0000, nullptr, "GetBias"}, + {0x00080040, nullptr, "SetGain"}, + {0x00090000, nullptr, "GetGain"}, + {0x000A0040, nullptr, "SetPower"}, + {0x000B0000, nullptr, "GetPower"}, {0x000C0042, nullptr, "size"}, {0x000D0040, nullptr, "SetClamp"}, {0x000E0000, nullptr, "GetClamp"}, - {0x000F0040, nullptr, "unknown_input1"}, + {0x000F0040, nullptr, "SetAllowShellClosed"}, {0x00100040, nullptr, "unknown_input2"}, }; diff --git a/src/core/hle/service/ndm_u.cpp b/src/core/hle/service/ndm_u.cpp index df3c97193..8fdf1ef90 100644 --- a/src/core/hle/service/ndm_u.cpp +++ b/src/core/hle/service/ndm_u.cpp @@ -2,8 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "ndm_u.h" +#include "core/hle/service/ndm_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace NDM_U @@ -14,10 +13,26 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00010042, nullptr, "EnterExclusiveState"}, {0x00020002, nullptr, "LeaveExclusiveState"}, {0x00030000, nullptr, "QueryExclusiveMode"}, + {0x00040002, nullptr, "LockState"}, + {0x00050002, nullptr, "UnlockState"}, {0x00060040, nullptr, "SuspendDaemons"}, + {0x00070040, nullptr, "ResumeDaemons"}, {0x00080040, nullptr, "DisableWifiUsage"}, {0x00090000, nullptr, "EnableWifiUsage"}, + {0x000A0000, nullptr, "GetCurrentState"}, + {0x000B0000, nullptr, "GetTargetState"}, + {0x000C0000, nullptr, "<Stubbed>"}, + {0x000D0040, nullptr, "QueryStatus"}, + {0x000E0040, nullptr, "GetDaemonDisableCount"}, + {0x000F0000, nullptr, "GetSchedulerDisableCount"}, + {0x00100040, nullptr, "SetScanInterval"}, + {0x00110000, nullptr, "GetScanInterval"}, + {0x00120040, nullptr, "SetRetryInterval"}, + {0x00130000, nullptr, "GetRetryInterval"}, {0x00140040, nullptr, "OverrideDefaultDaemons"}, + {0x00150000, nullptr, "ResetDefaultDaemons"}, + {0x00160000, nullptr, "GetDefaultDaemons"}, + {0x00170000, nullptr, "ClearHalfAwakeMacFilter"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/news/news.cpp b/src/core/hle/service/news/news.cpp index 63cbd3850..b3f500694 100644 --- a/src/core/hle/service/news/news.cpp +++ b/src/core/hle/service/news/news.cpp @@ -9,10 +9,6 @@ #include "core/hle/service/news/news_s.h" #include "core/hle/service/news/news_u.h" -#include "core/hle/hle.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" - namespace Service { namespace NEWS { diff --git a/src/core/hle/service/news/news.h b/src/core/hle/service/news/news.h index b31ade255..46a3ffcb5 100644 --- a/src/core/hle/service/news/news.h +++ b/src/core/hle/service/news/news.h @@ -4,9 +4,6 @@ #pragma once -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" - namespace Service { namespace NEWS { diff --git a/src/core/hle/service/news/news_s.cpp b/src/core/hle/service/news/news_s.cpp index 2f8c37d9e..39b5a50f8 100644 --- a/src/core/hle/service/news/news_s.cpp +++ b/src/core/hle/service/news/news_s.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/news/news.h" #include "core/hle/service/news/news_s.h" @@ -11,6 +10,18 @@ namespace NEWS { const Interface::FunctionInfo FunctionTable[] = { {0x000100C6, nullptr, "AddNotification"}, + {0x00050000, nullptr, "GetTotalNotifications"}, + {0x00060042, nullptr, "SetNewsDBHeader"}, + {0x00070082, nullptr, "SetNotificationHeader"}, + {0x00080082, nullptr, "SetNotificationMessage"}, + {0x00090082, nullptr, "SetNotificationImage"}, + {0x000A0042, nullptr, "GetNewsDBHeader"}, + {0x000B0082, nullptr, "GetNotificationHeader"}, + {0x000C0082, nullptr, "GetNotificationMessage"}, + {0x000D0082, nullptr, "GetNotificationImage"}, + {0x000E0040, nullptr, "SetInfoLEDPattern"}, + {0x00120082, nullptr, "GetNotificationHeaderOther"}, + {0x00130000, nullptr, "WriteNewsDBSavedata"}, }; NEWS_S_Interface::NEWS_S_Interface() { diff --git a/src/core/hle/service/news/news_u.cpp b/src/core/hle/service/news/news_u.cpp index 81f45a244..6b75cc24e 100644 --- a/src/core/hle/service/news/news_u.cpp +++ b/src/core/hle/service/news/news_u.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/news/news.h" #include "core/hle/service/news/news_u.h" namespace Service { diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 73b0ee52a..ed42464ce 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/service/service.h" @@ -10,10 +11,6 @@ #include "core/hle/service/nim/nim_s.h" #include "core/hle/service/nim/nim_u.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" -#include "core/hle/hle.h" - namespace Service { namespace NIM { diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h index f7635c747..c3106f18b 100644 --- a/src/core/hle/service/nim/nim.h +++ b/src/core/hle/service/nim/nim.h @@ -4,10 +4,10 @@ #pragma once -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" - namespace Service { + +class Interface; + namespace NIM { /** diff --git a/src/core/hle/service/nim/nim_aoc.cpp b/src/core/hle/service/nim/nim_aoc.cpp index e6b1b6145..4a4818d57 100644 --- a/src/core/hle/service/nim/nim_aoc.cpp +++ b/src/core/hle/service/nim/nim_aoc.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/nim/nim.h" #include "core/hle/service/nim/nim_aoc.h" namespace Service { diff --git a/src/core/hle/service/nim/nim_s.cpp b/src/core/hle/service/nim/nim_s.cpp index 5d8bc059f..dcaa0255a 100644 --- a/src/core/hle/service/nim/nim_s.cpp +++ b/src/core/hle/service/nim/nim_s.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" -#include "core/hle/service/nim/nim.h" #include "core/hle/service/nim/nim_s.h" namespace Service { @@ -11,6 +9,9 @@ namespace NIM { const Interface::FunctionInfo FunctionTable[] = { {0x000A0000, nullptr, "CheckSysupdateAvailableSOAP"}, + {0x0016020A, nullptr, "ListTitles"}, + {0x002D0042, nullptr, "DownloadTickets"}, + {0x00420240, nullptr, "StartDownload"}, }; NIM_S_Interface::NIM_S_Interface() { diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp index 066570a85..eae45ebc0 100644 --- a/src/core/hle/service/nim/nim_u.cpp +++ b/src/core/hle/service/nim/nim_u.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/nim/nim_u.h" diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp index 6b3ef6ece..072918d62 100644 --- a/src/core/hle/service/ns_s.cpp +++ b/src/core/hle/service/ns_s.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - -#include "core/hle/hle.h" #include "core/hle/service/ns_s.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -12,7 +10,21 @@ namespace NS_S { const Interface::FunctionInfo FunctionTable[] = { + {0x000100C0, nullptr, "LaunchFIRM"}, {0x000200C0, nullptr, "LaunchTitle"}, + {0x00030000, nullptr, "TerminateApplication"}, + {0x00040040, nullptr, "TerminateProcess"}, + {0x000500C0, nullptr, "LaunchApplicationFIRM"}, + {0x00060042, nullptr, "SetFIRMParams4A0"}, + {0x00070042, nullptr, "CardUpdateInitialize"}, + {0x00080000, nullptr, "CardUpdateShutdown"}, + {0x000D0140, nullptr, "SetTWLBannerHMAC"}, + {0x000E0000, nullptr, "ShutdownAsync"}, + {0x00100180, nullptr, "RebootSystem"}, + {0x00110100, nullptr, "TerminateTitle"}, + {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, + {0x00150140, nullptr, "LaunchApplication"}, + {0x00160000, nullptr, "RebootSystemClean"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp index 18b22956f..dc80984b2 100644 --- a/src/core/hle/service/nwm_uds.cpp +++ b/src/core/hle/service/nwm_uds.cpp @@ -2,9 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/common_types.h" #include "common/logging/log.h" -#include "core/hle/hle.h" #include "core/hle/kernel/event.h" #include "core/hle/service/nwm_uds.h" @@ -106,14 +106,32 @@ static void Initialize(Service::Interface* self) { } const Interface::FunctionInfo FunctionTable[] = { + {0x00020000, nullptr, "Scrap"}, {0x00030000, Shutdown, "Shutdown"}, + {0x00040402, nullptr, "CreateNetwork"}, + {0x00050040, nullptr, "EjectClient"}, + {0x00060000, nullptr, "EjectSpectator"}, + {0x00070080, nullptr, "UpdateNetworkAttribute"}, + {0x00080000, nullptr, "DestroyNetwork"}, + {0x000A0000, nullptr, "DisconnectNetwork"}, + {0x000B0000, nullptr, "GetConnectionStatus"}, + {0x000D0040, nullptr, "GetNodeInformation"}, {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, {0x00100042, nullptr, "SetBeaconAdditionalData"}, + {0x00110040, nullptr, "GetApplicationData"}, + {0x00120100, nullptr, "Bind"}, + {0x00130040, nullptr, "Unbind"}, {0x001400C0, nullptr, "RecvBroadcastDataFrame"}, + {0x00150080, nullptr, "SetMaxSendDelay"}, + {0x00170182, nullptr, "SendTo"}, + {0x001A0000, nullptr, "GetChannel"}, {0x001B0302, Initialize, "Initialize"}, {0x001D0044, nullptr, "BeginHostingNetwork"}, {0x001E0084, nullptr, "ConnectToNetwork"}, {0x001F0006, nullptr, "DecryptBeaconData"}, + {0x00200040, nullptr, "Flush"}, + {0x00210080, nullptr, "SetProbeResponseParam"}, + {0x00220402, nullptr, "ScanOnConnection"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp index 7420a62f4..05d01bc48 100644 --- a/src/core/hle/service/pm_app.cpp +++ b/src/core/hle/service/pm_app.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/pm_app.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,6 +18,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00070042, nullptr, "GetFIRMLaunchParams"}, {0x00080100, nullptr, "GetTitleExheaderFlags"}, {0x00090042, nullptr, "SetFIRMLaunchParams"}, + {0x000A0140, nullptr, "SetResourceLimit"}, + {0x000B0140, nullptr, "GetResourceLimitMax"}, + {0x000C0080, nullptr, "UnregisterProcess"}, + {0x000D0240, nullptr, "LaunchTitleUpdate"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// 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/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h index f2e76441f..4cf7383d1 100644 --- a/src/core/hle/service/ptm/ptm.h +++ b/src/core/hle/service/ptm/ptm.h @@ -4,11 +4,12 @@ #pragma once -#include <array> -#include "core/hle/service/service.h" -#include "core/hle/result.h" +#include "common/common_types.h" namespace Service { + +class Interface; + namespace PTM { /// Charge levels used by PTM functions diff --git a/src/core/hle/service/ptm/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp index 7bb990193..ca5dd0403 100644 --- a/src/core/hle/service/ptm/ptm_play.cpp +++ b/src/core/hle/service/ptm/ptm_play.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/ptm/ptm_play.h" namespace Service { diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp index 655658f3b..fe76dd108 100644 --- a/src/core/hle/service/ptm/ptm_sysm.cpp +++ b/src/core/hle/service/ptm/ptm_sysm.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/hle.h" #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/ptm/ptm_sysm.h" @@ -39,7 +38,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x08110000, nullptr, "GetShellStatus"}, {0x08120000, nullptr, "IsShutdownByBatteryEmpty"}, {0x08130000, nullptr, "FormatSavedata"}, - {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"} + {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"}, + {0x08180040, nullptr, "ConfigureNew3DSCPU"}, }; PTM_Sysm_Interface::PTM_Sysm_Interface() { diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp index 09dc38c3e..17e764866 100644 --- a/src/core/hle/service/ptm/ptm_u.cpp +++ b/src/core/hle/service/ptm/ptm_u.cpp @@ -2,9 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/logging/log.h" - -#include "core/hle/hle.h" #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/ptm/ptm_u.h" diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 633b66fe2..b52e52d4a 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; } @@ -552,13 +552,23 @@ static void RecvFrom(Service::Interface* self) { u32 flags = cmd_buffer[3]; socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]); - u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]); + struct + { + u32 output_buffer_descriptor; + u32 output_buffer_addr; + u32 address_buffer_descriptor; + u32 output_src_address_buffer; + } buffer_parameters; + + std::memcpy(&buffer_parameters, &cmd_buffer[64], sizeof(buffer_parameters)); + + u8* output_buff = Memory::GetPointer(buffer_parameters.output_buffer_addr); sockaddr src_addr; socklen_t src_addr_len = sizeof(src_addr); int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len); - if (cmd_buffer[0x1A0 >> 2] != 0) { - CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2])); + if (buffer_parameters.output_src_address_buffer != 0) { + CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer)); *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr); } @@ -732,7 +742,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000C0082, Shutdown, "Shutdown"}, {0x000D0082, nullptr, "GetHostByName"}, {0x000E00C2, nullptr, "GetHostByAddr"}, - {0x000F0106, nullptr, "unknown_resolve_ip"}, + {0x000F0106, nullptr, "GetAddrInfo"}, + {0x00100102, nullptr, "GetNameInfo"}, {0x00110102, nullptr, "GetSockOpt"}, {0x00120104, nullptr, "SetSockOpt"}, {0x001300C2, Fcntl, "Fcntl"}, @@ -749,6 +760,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001E0040, nullptr, "ICMPClose"}, {0x001F0040, nullptr, "GetResolverInfo"}, {0x00210002, nullptr, "CloseSockets"}, + {0x00230040, nullptr, "AddGlobalSocket"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 3b8c7c0e4..41fc3437b 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -2,9 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/common_types.h" #include "common/logging/log.h" -#include "core/hle/hle.h" #include "core/hle/service/srv.h" #include "core/hle/kernel/event.h" diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp index cabd18c80..14a4e98ec 100644 --- a/src/core/hle/service/ssl_c.cpp +++ b/src/core/hle/service/ssl_c.cpp @@ -4,7 +4,7 @@ #include <random> -#include "core/hle/hle.h" +#include "common/common_types.h" #include "core/hle/service/ssl_c.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -62,10 +62,24 @@ static void GenerateRandomData(Service::Interface* self) { const Interface::FunctionInfo FunctionTable[] = { {0x00010002, Initialize, "Initialize"}, {0x000200C2, nullptr, "CreateContext"}, + {0x00030000, nullptr, "CreateRootCertChain"}, + {0x00040040, nullptr, "DestroyRootCertChain"}, {0x00050082, nullptr, "AddTrustedRootCA"}, + {0x00060080, nullptr, "RootCertChainAddDefaultCert"}, + {0x00070080, nullptr, "RootCertChainRemoveCert"}, + {0x000E0040, nullptr, "OpenDefaultClientCertContext"}, + {0x000F0040, nullptr, "CloseClientCertContext"}, {0x00110042, GenerateRandomData, "GenerateRandomData"}, + {0x00120042, nullptr, "InitializeConnectionSession"}, + {0x00130040, nullptr, "StartConnection"}, + {0x00140040, nullptr, "StartConnectionGetOut"}, {0x00150082, nullptr, "Read"}, {0x00170082, nullptr, "Write"}, + {0x00180080, nullptr, "ContextSetRootCertChain"}, + {0x00190080, nullptr, "ContextSetClientCert"}, + {0x001B0080, nullptr, "ContextClearOpt"}, + {0x001E0040, nullptr, "DestroyContext"}, + {0x001F0082, nullptr, "ContextInitSharedmem"} }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 6b1b71fe4..a495441a4 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -4,15 +4,15 @@ #include <cstring> +#include "common/common_types.h" #include "common/logging/log.h" -#include "core/hle/hle.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/service/y2r_u.h" #include "core/hw/y2r.h" #include "video_core/renderer_base.h" -#include "video_core/utils.h" #include "video_core/video_core.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -267,7 +267,7 @@ static void StartConversion(Service::Interface* self) { // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( u32 total_output_size = conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap); - VideoCore::g_renderer->hw_rasterizer->NotifyFlush( + VideoCore::g_renderer->Rasterizer()->InvalidateRegion( Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); LOG_DEBUG(Service_Y2R, "called"); @@ -375,21 +375,41 @@ static void DriverFinalize(Service::Interface* self) { const Interface::FunctionInfo FunctionTable[] = { {0x00010040, SetInputFormat, "SetInputFormat"}, + {0x00020000, nullptr, "GetInputFormat"}, {0x00030040, SetOutputFormat, "SetOutputFormat"}, + {0x00040000, nullptr, "GetOutputFormat"}, {0x00050040, SetRotation, "SetRotation"}, + {0x00060000, nullptr, "GetRotation"}, {0x00070040, SetBlockAlignment, "SetBlockAlignment"}, + {0x00080000, nullptr, "GetBlockAlignment"}, + {0x00090040, nullptr, "SetSpacialDithering"}, + {0x000A0000, nullptr, "GetSpacialDithering"}, + {0x000B0040, nullptr, "SetTemporalDithering"}, + {0x000C0000, nullptr, "GetTemporalDithering"}, {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"}, {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, {0x00100102, SetSendingY, "SetSendingY"}, {0x00110102, SetSendingU, "SetSendingU"}, {0x00120102, SetSendingV, "SetSendingV"}, {0x00130102, SetSendingYUYV, "SetSendingYUYV"}, + {0x00140000, nullptr, "IsFinishedSendingYuv"}, + {0x00150000, nullptr, "IsFinishedSendingY"}, + {0x00160000, nullptr, "IsFinishedSendingU"}, + {0x00170000, nullptr, "IsFinishedSendingV"}, {0x00180102, SetReceiving, "SetReceiving"}, + {0x00190000, nullptr, "IsFinishedReceiving"}, {0x001A0040, SetInputLineWidth, "SetInputLineWidth"}, + {0x001B0000, nullptr, "GetInputLineWidth"}, {0x001C0040, SetInputLines, "SetInputLines"}, + {0x001D0000, nullptr, "GetInputLines"}, {0x001E0100, SetCoefficient, "SetCoefficient"}, + {0x001F0000, nullptr, "GetCoefficient"}, {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"}, + {0x00210040, nullptr, "GetStandardCoefficientParams"}, {0x00220040, SetAlpha, "SetAlpha"}, + {0x00230000, nullptr, "GetAlpha"}, + {0x00240200, nullptr, "SetDitheringWeightParams"}, + {0x00250000, nullptr, "GetDitheringWeightParams"}, {0x00260000, StartConversion, "StartConversion"}, {0x00270000, StopConversion, "StopConversion"}, {0x00280000, IsBusyConversion, "IsBusyConversion"}, @@ -397,6 +417,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x002A0000, PingProcess, "PingProcess"}, {0x002B0000, DriverInitialize, "DriverInitialize"}, {0x002C0000, DriverFinalize, "DriverFinalize"}, + {0x002D0000, nullptr, "GetPackageParameter"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 7f63ff505..7a39b101d 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -161,6 +161,8 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", handle, addr, permissions, other_permissions); + // TODO(Subv): The same process that created a SharedMemory object can not map it in its own address space + SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); if (shared_memory == nullptr) return ERR_INVALID_HANDLE; @@ -175,13 +177,27 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o case MemoryPermission::WriteExecute: case MemoryPermission::ReadWriteExecute: case MemoryPermission::DontCare: - shared_memory->Map(addr, permissions_type, + return shared_memory->Map(addr, permissions_type, static_cast<MemoryPermission>(other_permissions)); - break; default: LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); } - return RESULT_SUCCESS; + + return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); +} + +static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) { + using Kernel::SharedMemory; + + LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X", handle, addr); + + // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap + + SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); + if (shared_memory == nullptr) + return ERR_INVALID_HANDLE; + + return shared_memory->Unmap(addr); } /// Connect to an OS service given the port name, returns the handle to the port to out @@ -470,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: @@ -765,7 +782,13 @@ static s64 GetSystemTick() { static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission, u32 other_permission) { using Kernel::SharedMemory; - // TODO(Subv): Implement this function + + if (size % Memory::PAGE_SIZE != 0) + return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + + // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap + + // TODO(Subv): Implement this function properly using Kernel::MemoryPermission; SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size, @@ -781,7 +804,7 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { using Kernel::MemoryRegion; - LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u param=%d", process_handle, type, param); + LOG_TRACE(Kernel_SVC, "called type=%u param=%d", type, param); switch ((SystemInfoType)type) { case SystemInfoType::REGION_MEMORY_USAGE: @@ -807,7 +830,7 @@ static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { } break; case SystemInfoType::KERNEL_ALLOCATED_PAGES: - LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=2 param=%d", type, param); + LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=2 param=%d", param); *out = 0; break; case SystemInfoType::KERNEL_SPAWNED_PIDS: @@ -912,7 +935,7 @@ static const FunctionDef SVC_Table[] = { {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"}, {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"}, {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, - {0x20, nullptr, "UnmapMemoryBlock"}, + {0x20, HLE::Wrap<UnmapMemoryBlock>, "UnmapMemoryBlock"}, {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, {0x22, HLE::Wrap<ArbitrateAddress>, "ArbitrateAddress"}, {0x23, HLE::Wrap<CloseHandle>, "CloseHandle"}, diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index bc7bde903..7e2f9cdfa 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" @@ -26,7 +25,7 @@ #include "core/tracer/recorder.h" #include "video_core/command_processor.h" -#include "video_core/hwrasterizer_base.h" +#include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" #include "video_core/utils.h" #include "video_core/video_core.h" @@ -141,13 +140,13 @@ inline void Write(u32 addr, const T data) { GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); } - VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); + VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); } // 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; } @@ -172,7 +171,7 @@ inline void Write(u32 addr, const T data) { u32 output_gap = config.texture_copy.output_gap * 16; size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); - VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), contiguous_input_size); + VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size); u32 remaining_size = config.texture_copy.size; u32 remaining_input = input_width; @@ -205,7 +204,7 @@ inline void Write(u32 addr, const T data) { config.flags); size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap); - VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), contiguous_output_size); + VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size); GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); break; @@ -232,7 +231,7 @@ inline void Write(u32 addr, const T data) { u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); - VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), input_size); + VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), input_size); for (u32 y = 0; y < output_height; ++y) { for (u32 x = 0; x < output_width; ++x) { @@ -339,7 +338,7 @@ inline void Write(u32 addr, const T data) { g_regs.display_transfer_config.trigger = 0; GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); - VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size); + VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), output_size); } 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/loader/elf.cpp b/src/core/loader/elf.cpp index 5d7264f12..69df94324 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -250,7 +250,7 @@ const char *ElfReader::GetSectionName(int section) const { return nullptr; int name_offset = sections[section].sh_name; - const char* ptr = (char*)GetSectionDataPtr(header->e_shstrndx); + const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx)); if (ptr) return ptr + name_offset; @@ -347,10 +347,10 @@ bool ElfReader::LoadSymbols() { SectionID sec = GetSectionByName(".symtab"); if (sec != -1) { int stringSection = sections[sec].sh_link; - const char *stringBase = (const char *)GetSectionDataPtr(stringSection); + const char *stringBase = reinterpret_cast<const char*>(GetSectionDataPtr(stringSection)); //We have a symbol table! - Elf32_Sym* symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); + const Elf32_Sym* symtab = reinterpret_cast<const Elf32_Sym*>(GetSectionDataPtr(sec)); unsigned int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); for (unsigned sym = 0; sym < numSymbols; sym++) { int size = symtab[sym].st_size; diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 6b88169e1..b1907cd55 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -137,11 +137,11 @@ ResultStatus LoadFile(const std::string& filename) { AppLoader_NCCH app_loader(std::move(file), filename); // Load application and RomFS - if (ResultStatus::Success == app_loader.Load()) { + ResultStatus result = app_loader.Load(); + if (ResultStatus::Success == result) { Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); - return ResultStatus::Success; } - break; + return result; } // CIA file format... diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 68b3f546e..93f21bec2 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -255,7 +255,8 @@ ResultStatus AppLoader_NCCH::Load() { priority = exheader_header.arm11_system_local_caps.priority; resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category; - LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name); + LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name); + LOG_INFO(Loader, "Program ID: %016X" , ncch_header.program_id); LOG_DEBUG(Loader, "Code compressed: %s" , is_compressed ? "yes" : "no"); LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); LOG_DEBUG(Loader, "Code size: 0x%08X", code_size); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index fc79c3ee9..7de5bd15d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -13,6 +13,7 @@ #include "core/hle/kernel/process.h" #include "core/memory.h" #include "core/memory_setup.h" +#include "core/mmio.h" namespace Memory { @@ -25,6 +26,12 @@ enum class PageType { Special, }; +struct SpecialRegion { + VAddr base; + u32 size; + MMIORegionPointer handler; +}; + /** * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and @@ -41,8 +48,13 @@ struct PageTable { std::array<u8*, NUM_ENTRIES> pointers; /** + * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of type `Special`. + */ + std::vector<SpecialRegion> special_regions; + + /** * Array of fine grained page attributes. If it is set to any value other than `Memory`, then - * the corresponding entry in `pointer` MUST be set to null. + * the corresponding entry in `pointers` MUST be set to null. */ std::array<PageType, NUM_ENTRIES> attributes; }; @@ -80,10 +92,12 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target) { MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); } -void MapIoRegion(VAddr base, u32 size) { +void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); + + current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); } void UnmapRegion(VAddr base, u32 size) { @@ -92,6 +106,22 @@ void UnmapRegion(VAddr base, u32 size) { MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); } +/** + * This function should only be called for virtual addreses with attribute `PageType::Special`. + */ +static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { + for (const auto& region : current_page_table->special_regions) { + if (vaddr >= region.base && vaddr < (region.base + region.size)) { + return region.handler; + } + } + ASSERT_MSG(false, "Mapped IO page without a handler @ %08X", vaddr); + return nullptr; // Should never happen +} + +template<typename T> +T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr); + template <typename T> T Read(const VAddr vaddr) { const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; @@ -108,14 +138,17 @@ T Read(const VAddr vaddr) { return 0; case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); + break; case PageType::Special: - LOG_ERROR(HW_Memory, "I/O reads aren't implemented yet @ %08X", vaddr); - return 0; + return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr); default: UNREACHABLE(); } } +template<typename T> +void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data); + template <typename T> void Write(const VAddr vaddr, const T data) { u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; @@ -131,9 +164,10 @@ void Write(const VAddr vaddr, const T data) { return; case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); + break; case PageType::Special: - LOG_ERROR(HW_Memory, "I/O writes aren't implemented yet @ %08X", vaddr); - return; + WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data); + break; default: UNREACHABLE(); } @@ -191,6 +225,46 @@ void WriteBlock(const VAddr addr, const u8* data, const size_t size) { } } +template<> +u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) { + return mmio_handler->Read8(addr); +} + +template<> +u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) { + return mmio_handler->Read16(addr); +} + +template<> +u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) { + return mmio_handler->Read32(addr); +} + +template<> +u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) { + return mmio_handler->Read64(addr); +} + +template<> +void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) { + mmio_handler->Write8(addr, data); +} + +template<> +void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) { + mmio_handler->Write16(addr, data); +} + +template<> +void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) { + mmio_handler->Write32(addr, data); +} + +template<> +void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) { + mmio_handler->Write64(addr, data); +} + PAddr VirtualToPhysicalAddress(const VAddr addr) { if (addr == 0) { return 0; @@ -217,7 +291,7 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) { } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { return addr - VRAM_PADDR + VRAM_VADDR; } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { - return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapBase(); + return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapAreaAddress(); } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h index 84ff30120..05f70a1fe 100644 --- a/src/core/memory_setup.h +++ b/src/core/memory_setup.h @@ -23,10 +23,11 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target); /** * Maps a region of the emulated process address space as a IO region. - * @note Currently this can only be used to mark a region as being IO, since actual memory-mapped - * IO isn't yet supported. + * @param base The address to start mapping at. Must be page-aligned. + * @param size The amount of bytes to map. Must be page-aligned. + * @param mmio_handler The handler that backs the mapping. */ -void MapIoRegion(VAddr base, u32 size); +void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler); void UnmapRegion(VAddr base, u32 size); diff --git a/src/core/mmio.h b/src/core/mmio.h new file mode 100644 index 000000000..06b555e98 --- /dev/null +++ b/src/core/mmio.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 <memory> + +#include "common/common_types.h" + +namespace Memory { + +/** + * Represents a device with memory mapped IO. + * A device may be mapped to multiple regions of memory. + */ +class MMIORegion { +public: + virtual ~MMIORegion() = default; + + virtual u8 Read8(VAddr addr) = 0; + virtual u16 Read16(VAddr addr) = 0; + virtual u32 Read32(VAddr addr) = 0; + virtual u64 Read64(VAddr addr) = 0; + + virtual void Write8(VAddr addr, u8 data) = 0; + virtual void Write16(VAddr addr, u16 data) = 0; + virtual void Write32(VAddr addr, u32 data) = 0; + virtual void Write64(VAddr addr, u64 data) = 0; +}; + +using MMIORegionPointer = std::shared_ptr<MMIORegion>; + +}; diff --git a/src/core/system.cpp b/src/core/system.cpp index 7e9c56538..4a4757af3 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,23 +15,27 @@ #include "video_core/video_core.h" -#include "core/gdbstub/gdbstub.h" - namespace System { -void Init(EmuWindow* emu_window) { +Result Init(EmuWindow* emu_window) { Core::Init(); CoreTiming::Init(); Memory::Init(); HW::Init(); Kernel::Init(); HLE::Init(); - VideoCore::Init(emu_window); + if (!VideoCore::Init(emu_window)) { + return Result::ErrorInitVideoCore; + } + AudioCore::Init(); GDBStub::Init(); + + return Result::Success; } void Shutdown() { GDBStub::Shutdown(); + AudioCore::Shutdown(); VideoCore::Shutdown(); HLE::Shutdown(); Kernel::Shutdown(); diff --git a/src/core/system.h b/src/core/system.h index 59a75ca12..a4a627ea9 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -8,7 +8,14 @@ class EmuWindow; namespace System { -void Init(EmuWindow* emu_window); +enum class Result { + Success, ///< Everything is fine + Error, ///< Something went wrong (no module specified) + ErrorInitCore, ///< Something went wrong during core init + ErrorInitVideoCore, ///< Something went wrong during video core init +}; + +Result Init(EmuWindow* emu_window); void Shutdown(); } diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 2a924f4ad..76cfd4f7d 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -11,8 +11,10 @@ set(SRCS pica.cpp primitive_assembly.cpp rasterizer.cpp + renderer_base.cpp shader/shader.cpp shader/shader_interpreter.cpp + swrasterizer.cpp utils.cpp video_core.cpp ) @@ -30,13 +32,16 @@ set(HEADERS clipper.h command_processor.h gpu_debugger.h - hwrasterizer_base.h pica.h + pica_state.h + pica_types.h primitive_assembly.h rasterizer.h + rasterizer_interface.h renderer_base.h shader/shader.h shader/shader_interpreter.h + swrasterizer.h utils.h video_core.h ) diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp index ed99c4f13..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" @@ -59,15 +60,17 @@ static void InitScreenCoordinates(OutputVertex& vtx) } viewport; const auto& regs = g_state.regs; - viewport.halfsize_x = float24::FromRawFloat24(regs.viewport_size_x); - viewport.halfsize_y = float24::FromRawFloat24(regs.viewport_size_y); + viewport.halfsize_x = float24::FromRaw(regs.viewport_size_x); + viewport.halfsize_y = float24::FromRaw(regs.viewport_size_y); viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.viewport_corner.x)); viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.viewport_corner.y)); - viewport.zscale = float24::FromRawFloat24(regs.viewport_depth_range); - viewport.offset_z = float24::FromRawFloat24(regs.viewport_depth_far_plane); + viewport.zscale = float24::FromRaw(regs.viewport_depth_range); + viewport.offset_z = float24::FromRaw(regs.viewport_depth_far_plane); float24 inv_w = float24::FromFloat32(1.f) / vtx.pos.w; vtx.color *= inv_w; + vtx.view *= inv_w; + vtx.quat *= inv_w; vtx.tc0 *= inv_w; vtx.tc1 *= inv_w; vtx.tc2 *= inv_w; @@ -78,7 +81,7 @@ static void InitScreenCoordinates(OutputVertex& vtx) vtx.screenpos[2] = viewport.offset_z + vtx.pos.z * inv_w * viewport.zscale; } -void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { +void ProcessTriangle(const OutputVertex &v0, const OutputVertex &v1, const OutputVertex &v2) { using boost::container::static_vector; // Clipping a planar n-gon against a plane will remove at least 1 vertex and introduces 2 at diff --git a/src/video_core/clipper.h b/src/video_core/clipper.h index 6ed01e877..f85d8d4c9 100644 --- a/src/video_core/clipper.h +++ b/src/video_core/clipper.h @@ -14,7 +14,7 @@ namespace Clipper { using Shader::OutputVertex; -void ProcessTriangle(OutputVertex& v0, OutputVertex& v1, OutputVertex& v2); +void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2); } // namespace diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index bd1b09a4b..2274dfa66 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): @@ -98,21 +107,58 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index]; // NOTE: The destination component order indeed is "backwards" - attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8); - attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF)); - attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF)); - attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF); + attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8); + attribute.z = float24::FromRaw(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF)); + attribute.y = float24::FromRaw(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF)); + attribute.x = float24::FromRaw(default_attr_write_buffer[2] & 0xFFFFFF); LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index, attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(), 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 + } } } @@ -336,19 +392,14 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { } } - if (Settings::values.use_hw_renderer) { - // Send to hardware renderer - static auto AddHWTriangle = [](const Pica::Shader::OutputVertex& v0, - const Pica::Shader::OutputVertex& v1, - const Pica::Shader::OutputVertex& v2) { - VideoCore::g_renderer->hw_rasterizer->AddTriangle(v0, v1, v2); - }; + // 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); + }; - primitive_assembler.SubmitVertex(output, AddHWTriangle); - } else { - // Send to triangle clipper - primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle); - } + primitive_assembler.SubmitVertex(output, AddTriangle); } for (auto& range : memory_accesses.ranges) { @@ -356,9 +407,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { range.second, range.first); } - if (Settings::values.use_hw_renderer) { - VideoCore::g_renderer->hw_rasterizer->DrawTriangles(); - } + VideoCore::g_renderer->Rasterizer()->DrawTriangles(); #if PICA_DUMP_GEOMETRY geometry_dumper.Dump(); @@ -425,10 +474,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { uniform[3 - i] = float24::FromFloat32(*(float*)(&uniform_write_buffer[i])); } else { // TODO: Untested - uniform.w = float24::FromRawFloat24(uniform_write_buffer[0] >> 8); - uniform.z = float24::FromRawFloat24(((uniform_write_buffer[0] & 0xFF)<<16) | ((uniform_write_buffer[1] >> 16) & 0xFFFF)); - uniform.y = float24::FromRawFloat24(((uniform_write_buffer[1] & 0xFFFF)<<8) | ((uniform_write_buffer[2] >> 24) & 0xFF)); - uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF); + uniform.w = float24::FromRaw(uniform_write_buffer[0] >> 8); + uniform.z = float24::FromRaw(((uniform_write_buffer[0] & 0xFF) << 16) | ((uniform_write_buffer[1] >> 16) & 0xFFFF)); + uniform.y = float24::FromRaw(((uniform_write_buffer[1] & 0xFFFF) << 8) | ((uniform_write_buffer[2] >> 24) & 0xFF)); + uniform.x = float24::FromRaw(uniform_write_buffer[2] & 0xFFFFFF); } LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index, @@ -436,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; } @@ -471,11 +520,29 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { break; } + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[0], 0x1c8): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[1], 0x1c9): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[2], 0x1ca): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[3], 0x1cb): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[4], 0x1cc): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[5], 0x1cd): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf): + { + auto& lut_config = regs.lighting.lut_config; + + 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.Assign(lut_config.index + 1); + break; + } + default: break; } - VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id); + VideoCore::g_renderer->Rasterizer()->NotifyPicaRegisterChanged(id); if (g_debug_context) g_debug_context->OnEvent(DebugContext::Event::PicaCommandProcessed, reinterpret_cast<void*>(&id)); @@ -493,9 +560,8 @@ void ProcessCommandList(const u32* list, u32 size) { u32 value = *g_state.cmd_list.current_ptr++; const CommandHeader header = { *g_state.cmd_list.current_ptr++ }; - u32 cmd = header.cmd_id; - WritePicaReg(cmd, value, header.parameter_mask); + WritePicaReg(header.cmd_id, value, header.parameter_mask); for (unsigned i = 0; i < header.extra_data_length; ++i) { u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0); diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index f1cfa9361..271e81ca1 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" @@ -46,10 +47,8 @@ void DebugContext::OnEvent(Event event, void* data) { { std::unique_lock<std::mutex> lock(breakpoint_mutex); - if (Settings::values.use_hw_renderer) { - // Commit the hardware renderer's framebuffer so it will show on debug widgets - VideoCore::g_renderer->hw_rasterizer->CommitFramebuffer(); - } + // Commit the hardware renderer's framebuffer so it will show on debug widgets + VideoCore::g_renderer->Rasterizer()->FlushFramebuffer(); // TODO: Should stop the CPU thread here once we multithread emulation. @@ -115,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; @@ -203,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 8c528989e..32ad72674 100644 --- a/src/video_core/pica.cpp +++ b/src/video_core/pica.cpp @@ -6,72 +6,481 @@ #include <unordered_map> #include "video_core/pica.h" +#include "video_core/pica_state.h" #include "video_core/shader/shader.h" namespace Pica { State g_state; +static const std::pair<u16, const char*> register_names[] = { + { 0x010, "GPUREG_FINALIZE" }, + + { 0x040, "GPUREG_FACECULLING_CONFIG" }, + { 0x041, "GPUREG_VIEWPORT_WIDTH" }, + { 0x042, "GPUREG_VIEWPORT_INVW" }, + { 0x043, "GPUREG_VIEWPORT_HEIGHT" }, + { 0x044, "GPUREG_VIEWPORT_INVH" }, + + { 0x047, "GPUREG_FRAGOP_CLIP" }, + { 0x048, "GPUREG_FRAGOP_CLIP_DATA0" }, + { 0x049, "GPUREG_FRAGOP_CLIP_DATA1" }, + { 0x04A, "GPUREG_FRAGOP_CLIP_DATA2" }, + { 0x04B, "GPUREG_FRAGOP_CLIP_DATA3" }, + + { 0x04D, "GPUREG_DEPTHMAP_SCALE" }, + { 0x04E, "GPUREG_DEPTHMAP_OFFSET" }, + { 0x04F, "GPUREG_SH_OUTMAP_TOTAL" }, + { 0x050, "GPUREG_SH_OUTMAP_O0" }, + { 0x051, "GPUREG_SH_OUTMAP_O1" }, + { 0x052, "GPUREG_SH_OUTMAP_O2" }, + { 0x053, "GPUREG_SH_OUTMAP_O3" }, + { 0x054, "GPUREG_SH_OUTMAP_O4" }, + { 0x055, "GPUREG_SH_OUTMAP_O5" }, + { 0x056, "GPUREG_SH_OUTMAP_O6" }, + + { 0x061, "GPUREG_EARLYDEPTH_FUNC" }, + { 0x062, "GPUREG_EARLYDEPTH_TEST1" }, + { 0x063, "GPUREG_EARLYDEPTH_CLEAR" }, + { 0x064, "GPUREG_SH_OUTATTR_MODE" }, + { 0x065, "GPUREG_SCISSORTEST_MODE" }, + { 0x066, "GPUREG_SCISSORTEST_POS" }, + { 0x067, "GPUREG_SCISSORTEST_DIM" }, + { 0x068, "GPUREG_VIEWPORT_XY" }, + + { 0x06A, "GPUREG_EARLYDEPTH_DATA" }, + + { 0x06D, "GPUREG_DEPTHMAP_ENABLE" }, + { 0x06E, "GPUREG_RENDERBUF_DIM" }, + { 0x06F, "GPUREG_SH_OUTATTR_CLOCK" }, + + { 0x080, "GPUREG_TEXUNIT_CONFIG" }, + { 0x081, "GPUREG_TEXUNIT0_BORDER_COLOR" }, + { 0x082, "GPUREG_TEXUNIT0_DIM" }, + { 0x083, "GPUREG_TEXUNIT0_PARAM" }, + { 0x084, "GPUREG_TEXUNIT0_LOD" }, + { 0x085, "GPUREG_TEXUNIT0_ADDR1" }, + { 0x086, "GPUREG_TEXUNIT0_ADDR2" }, + { 0x087, "GPUREG_TEXUNIT0_ADDR3" }, + { 0x088, "GPUREG_TEXUNIT0_ADDR4" }, + { 0x089, "GPUREG_TEXUNIT0_ADDR5" }, + { 0x08A, "GPUREG_TEXUNIT0_ADDR6" }, + { 0x08B, "GPUREG_TEXUNIT0_SHADOW" }, + + { 0x08E, "GPUREG_TEXUNIT0_TYPE" }, + { 0x08F, "GPUREG_LIGHTING_ENABLE0" }, + + { 0x091, "GPUREG_TEXUNIT1_BORDER_COLOR" }, + { 0x092, "GPUREG_TEXUNIT1_DIM" }, + { 0x093, "GPUREG_TEXUNIT1_PARAM" }, + { 0x094, "GPUREG_TEXUNIT1_LOD" }, + { 0x095, "GPUREG_TEXUNIT1_ADDR" }, + { 0x096, "GPUREG_TEXUNIT1_TYPE" }, + + { 0x099, "GPUREG_TEXUNIT2_BORDER_COLOR" }, + { 0x09A, "GPUREG_TEXUNIT2_DIM" }, + { 0x09B, "GPUREG_TEXUNIT2_PARAM" }, + { 0x09C, "GPUREG_TEXUNIT2_LOD" }, + { 0x09D, "GPUREG_TEXUNIT2_ADDR" }, + { 0x09E, "GPUREG_TEXUNIT2_TYPE" }, + + { 0x0A8, "GPUREG_TEXUNIT3_PROCTEX0" }, + { 0x0A9, "GPUREG_TEXUNIT3_PROCTEX1" }, + { 0x0AA, "GPUREG_TEXUNIT3_PROCTEX2" }, + { 0x0AB, "GPUREG_TEXUNIT3_PROCTEX3" }, + { 0x0AC, "GPUREG_TEXUNIT3_PROCTEX4" }, + { 0x0AD, "GPUREG_TEXUNIT3_PROCTEX5" }, + + { 0x0AF, "GPUREG_PROCTEX_LUT" }, + { 0x0B0, "GPUREG_PROCTEX_LUT_DATA0" }, + { 0x0B1, "GPUREG_PROCTEX_LUT_DATA1" }, + { 0x0B2, "GPUREG_PROCTEX_LUT_DATA2" }, + { 0x0B3, "GPUREG_PROCTEX_LUT_DATA3" }, + { 0x0B4, "GPUREG_PROCTEX_LUT_DATA4" }, + { 0x0B5, "GPUREG_PROCTEX_LUT_DATA5" }, + { 0x0B6, "GPUREG_PROCTEX_LUT_DATA6" }, + { 0x0B7, "GPUREG_PROCTEX_LUT_DATA7" }, + + { 0x0C0, "GPUREG_TEXENV0_SOURCE" }, + { 0x0C1, "GPUREG_TEXENV0_OPERAND" }, + { 0x0C2, "GPUREG_TEXENV0_COMBINER" }, + { 0x0C3, "GPUREG_TEXENV0_COLOR" }, + { 0x0C4, "GPUREG_TEXENV0_SCALE" }, + + { 0x0C8, "GPUREG_TEXENV1_SOURCE" }, + { 0x0C9, "GPUREG_TEXENV1_OPERAND" }, + { 0x0CA, "GPUREG_TEXENV1_COMBINER" }, + { 0x0CB, "GPUREG_TEXENV1_COLOR" }, + { 0x0CC, "GPUREG_TEXENV1_SCALE" }, + + { 0x0D0, "GPUREG_TEXENV2_SOURCE" }, + { 0x0D1, "GPUREG_TEXENV2_OPERAND" }, + { 0x0D2, "GPUREG_TEXENV2_COMBINER" }, + { 0x0D3, "GPUREG_TEXENV2_COLOR" }, + { 0x0D4, "GPUREG_TEXENV2_SCALE" }, + + { 0x0D8, "GPUREG_TEXENV3_SOURCE" }, + { 0x0D9, "GPUREG_TEXENV3_OPERAND" }, + { 0x0DA, "GPUREG_TEXENV3_COMBINER" }, + { 0x0DB, "GPUREG_TEXENV3_COLOR" }, + { 0x0DC, "GPUREG_TEXENV3_SCALE" }, + + { 0x0E0, "GPUREG_TEXENV_UPDATE_BUFFER" }, + { 0x0E1, "GPUREG_FOG_COLOR" }, + + { 0x0E4, "GPUREG_GAS_ATTENUATION" }, + { 0x0E5, "GPUREG_GAS_ACCMAX" }, + { 0x0E6, "GPUREG_FOG_LUT_INDEX" }, + + { 0x0E8, "GPUREG_FOG_LUT_DATA0" }, + { 0x0E9, "GPUREG_FOG_LUT_DATA1" }, + { 0x0EA, "GPUREG_FOG_LUT_DATA2" }, + { 0x0EB, "GPUREG_FOG_LUT_DATA3" }, + { 0x0EC, "GPUREG_FOG_LUT_DATA4" }, + { 0x0ED, "GPUREG_FOG_LUT_DATA5" }, + { 0x0EE, "GPUREG_FOG_LUT_DATA6" }, + { 0x0EF, "GPUREG_FOG_LUT_DATA7" }, + { 0x0F0, "GPUREG_TEXENV4_SOURCE" }, + { 0x0F1, "GPUREG_TEXENV4_OPERAND" }, + { 0x0F2, "GPUREG_TEXENV4_COMBINER" }, + { 0x0F3, "GPUREG_TEXENV4_COLOR" }, + { 0x0F4, "GPUREG_TEXENV4_SCALE" }, + + { 0x0F8, "GPUREG_TEXENV5_SOURCE" }, + { 0x0F9, "GPUREG_TEXENV5_OPERAND" }, + { 0x0FA, "GPUREG_TEXENV5_COMBINER" }, + { 0x0FB, "GPUREG_TEXENV5_COLOR" }, + { 0x0FC, "GPUREG_TEXENV5_SCALE" }, + { 0x0FD, "GPUREG_TEXENV_BUFFER_COLOR" }, + + { 0x100, "GPUREG_COLOR_OPERATION" }, + { 0x101, "GPUREG_BLEND_FUNC" }, + { 0x102, "GPUREG_LOGIC_OP" }, + { 0x103, "GPUREG_BLEND_COLOR" }, + { 0x104, "GPUREG_FRAGOP_ALPHA_TEST" }, + { 0x105, "GPUREG_STENCIL_TEST" }, + { 0x106, "GPUREG_STENCIL_OP" }, + { 0x107, "GPUREG_DEPTH_COLOR_MASK" }, + + { 0x110, "GPUREG_FRAMEBUFFER_INVALIDATE" }, + { 0x111, "GPUREG_FRAMEBUFFER_FLUSH" }, + { 0x112, "GPUREG_COLORBUFFER_READ" }, + { 0x113, "GPUREG_COLORBUFFER_WRITE" }, + { 0x114, "GPUREG_DEPTHBUFFER_READ" }, + { 0x115, "GPUREG_DEPTHBUFFER_WRITE" }, + { 0x116, "GPUREG_DEPTHBUFFER_FORMAT" }, + { 0x117, "GPUREG_COLORBUFFER_FORMAT" }, + { 0x118, "GPUREG_EARLYDEPTH_TEST2" }, + + { 0x11B, "GPUREG_FRAMEBUFFER_BLOCK32" }, + { 0x11C, "GPUREG_DEPTHBUFFER_LOC" }, + { 0x11D, "GPUREG_COLORBUFFER_LOC" }, + { 0x11E, "GPUREG_FRAMEBUFFER_DIM" }, + + { 0x120, "GPUREG_GAS_LIGHT_XY" }, + { 0x121, "GPUREG_GAS_LIGHT_Z" }, + { 0x122, "GPUREG_GAS_LIGHT_Z_COLOR" }, + { 0x123, "GPUREG_GAS_LUT_INDEX" }, + { 0x124, "GPUREG_GAS_LUT_DATA" }, + + { 0x126, "GPUREG_GAS_DELTAZ_DEPTH" }, + + { 0x130, "GPUREG_FRAGOP_SHADOW" }, + + { 0x140, "GPUREG_LIGHT0_SPECULAR0" }, + { 0x141, "GPUREG_LIGHT0_SPECULAR1" }, + { 0x142, "GPUREG_LIGHT0_DIFFUSE" }, + { 0x143, "GPUREG_LIGHT0_AMBIENT" }, + { 0x144, "GPUREG_LIGHT0_XY" }, + { 0x145, "GPUREG_LIGHT0_Z" }, + { 0x146, "GPUREG_LIGHT0_SPOTDIR_XY" }, + { 0x147, "GPUREG_LIGHT0_SPOTDIR_Z" }, + + { 0x149, "GPUREG_LIGHT0_CONFIG" }, + { 0x14A, "GPUREG_LIGHT0_ATTENUATION_BIAS" }, + { 0x14B, "GPUREG_LIGHT0_ATTENUATION_SCALE" }, + + { 0x150, "GPUREG_LIGHT1_SPECULAR0" }, + { 0x151, "GPUREG_LIGHT1_SPECULAR1" }, + { 0x152, "GPUREG_LIGHT1_DIFFUSE" }, + { 0x153, "GPUREG_LIGHT1_AMBIENT" }, + { 0x154, "GPUREG_LIGHT1_XY" }, + { 0x155, "GPUREG_LIGHT1_Z" }, + { 0x156, "GPUREG_LIGHT1_SPOTDIR_XY" }, + { 0x157, "GPUREG_LIGHT1_SPOTDIR_Z" }, + + { 0x159, "GPUREG_LIGHT1_CONFIG" }, + { 0x15A, "GPUREG_LIGHT1_ATTENUATION_BIAS" }, + { 0x15B, "GPUREG_LIGHT1_ATTENUATION_SCALE" }, + + { 0x160, "GPUREG_LIGHT2_SPECULAR0" }, + { 0x161, "GPUREG_LIGHT2_SPECULAR1" }, + { 0x162, "GPUREG_LIGHT2_DIFFUSE" }, + { 0x163, "GPUREG_LIGHT2_AMBIENT" }, + { 0x164, "GPUREG_LIGHT2_XY" }, + { 0x165, "GPUREG_LIGHT2_Z" }, + { 0x166, "GPUREG_LIGHT2_SPOTDIR_XY" }, + { 0x167, "GPUREG_LIGHT2_SPOTDIR_Z" }, + + { 0x169, "GPUREG_LIGHT2_CONFIG" }, + { 0x16A, "GPUREG_LIGHT2_ATTENUATION_BIAS" }, + { 0x16B, "GPUREG_LIGHT2_ATTENUATION_SCALE" }, + + { 0x170, "GPUREG_LIGHT3_SPECULAR0" }, + { 0x171, "GPUREG_LIGHT3_SPECULAR1" }, + { 0x172, "GPUREG_LIGHT3_DIFFUSE" }, + { 0x173, "GPUREG_LIGHT3_AMBIENT" }, + { 0x174, "GPUREG_LIGHT3_XY" }, + { 0x175, "GPUREG_LIGHT3_Z" }, + { 0x176, "GPUREG_LIGHT3_SPOTDIR_XY" }, + { 0x177, "GPUREG_LIGHT3_SPOTDIR_Z" }, + + { 0x179, "GPUREG_LIGHT3_CONFIG" }, + { 0x17A, "GPUREG_LIGHT3_ATTENUATION_BIAS" }, + { 0x17B, "GPUREG_LIGHT3_ATTENUATION_SCALE" }, + + { 0x180, "GPUREG_LIGHT4_SPECULAR0" }, + { 0x181, "GPUREG_LIGHT4_SPECULAR1" }, + { 0x182, "GPUREG_LIGHT4_DIFFUSE" }, + { 0x183, "GPUREG_LIGHT4_AMBIENT" }, + { 0x184, "GPUREG_LIGHT4_XY" }, + { 0x185, "GPUREG_LIGHT4_Z" }, + { 0x186, "GPUREG_LIGHT4_SPOTDIR_XY" }, + { 0x187, "GPUREG_LIGHT4_SPOTDIR_Z" }, + + { 0x189, "GPUREG_LIGHT4_CONFIG" }, + { 0x18A, "GPUREG_LIGHT4_ATTENUATION_BIAS" }, + { 0x18B, "GPUREG_LIGHT4_ATTENUATION_SCALE" }, + + { 0x190, "GPUREG_LIGHT5_SPECULAR0" }, + { 0x191, "GPUREG_LIGHT5_SPECULAR1" }, + { 0x192, "GPUREG_LIGHT5_DIFFUSE" }, + { 0x193, "GPUREG_LIGHT5_AMBIENT" }, + { 0x194, "GPUREG_LIGHT5_XY" }, + { 0x195, "GPUREG_LIGHT5_Z" }, + { 0x196, "GPUREG_LIGHT5_SPOTDIR_XY" }, + { 0x197, "GPUREG_LIGHT5_SPOTDIR_Z" }, + + { 0x199, "GPUREG_LIGHT5_CONFIG" }, + { 0x19A, "GPUREG_LIGHT5_ATTENUATION_BIAS" }, + { 0x19B, "GPUREG_LIGHT5_ATTENUATION_SCALE" }, + + { 0x1A0, "GPUREG_LIGHT6_SPECULAR0" }, + { 0x1A1, "GPUREG_LIGHT6_SPECULAR1" }, + { 0x1A2, "GPUREG_LIGHT6_DIFFUSE" }, + { 0x1A3, "GPUREG_LIGHT6_AMBIENT" }, + { 0x1A4, "GPUREG_LIGHT6_XY" }, + { 0x1A5, "GPUREG_LIGHT6_Z" }, + { 0x1A6, "GPUREG_LIGHT6_SPOTDIR_XY" }, + { 0x1A7, "GPUREG_LIGHT6_SPOTDIR_Z" }, + + { 0x1A9, "GPUREG_LIGHT6_CONFIG" }, + { 0x1AA, "GPUREG_LIGHT6_ATTENUATION_BIAS" }, + { 0x1AB, "GPUREG_LIGHT6_ATTENUATION_SCALE" }, + + { 0x1B0, "GPUREG_LIGHT7_SPECULAR0" }, + { 0x1B1, "GPUREG_LIGHT7_SPECULAR1" }, + { 0x1B2, "GPUREG_LIGHT7_DIFFUSE" }, + { 0x1B3, "GPUREG_LIGHT7_AMBIENT" }, + { 0x1B4, "GPUREG_LIGHT7_XY" }, + { 0x1B5, "GPUREG_LIGHT7_Z" }, + { 0x1B6, "GPUREG_LIGHT7_SPOTDIR_XY" }, + { 0x1B7, "GPUREG_LIGHT7_SPOTDIR_Z" }, + + { 0x1B9, "GPUREG_LIGHT7_CONFIG" }, + { 0x1BA, "GPUREG_LIGHT7_ATTENUATION_BIAS" }, + { 0x1BB, "GPUREG_LIGHT7_ATTENUATION_SCALE" }, + + { 0x1C0, "GPUREG_LIGHTING_AMBIENT" }, + + { 0x1C2, "GPUREG_LIGHTING_NUM_LIGHTS" }, + { 0x1C3, "GPUREG_LIGHTING_CONFIG0" }, + { 0x1C4, "GPUREG_LIGHTING_CONFIG1" }, + { 0x1C5, "GPUREG_LIGHTING_LUT_INDEX" }, + { 0x1C6, "GPUREG_LIGHTING_ENABLE1" }, + + { 0x1C8, "GPUREG_LIGHTING_LUT_DATA0" }, + { 0x1C9, "GPUREG_LIGHTING_LUT_DATA1" }, + { 0x1CA, "GPUREG_LIGHTING_LUT_DATA2" }, + { 0x1CB, "GPUREG_LIGHTING_LUT_DATA3" }, + { 0x1CC, "GPUREG_LIGHTING_LUT_DATA4" }, + { 0x1CD, "GPUREG_LIGHTING_LUT_DATA5" }, + { 0x1CE, "GPUREG_LIGHTING_LUT_DATA6" }, + { 0x1CF, "GPUREG_LIGHTING_LUT_DATA7" }, + { 0x1D0, "GPUREG_LIGHTING_LUTINPUT_ABS" }, + { 0x1D1, "GPUREG_LIGHTING_LUTINPUT_SELECT" }, + { 0x1D2, "GPUREG_LIGHTING_LUTINPUT_SCALE" }, + + { 0x1D9, "GPUREG_LIGHTING_LIGHT_PERMUTATION" }, + + { 0x200, "GPUREG_ATTRIBBUFFERS_LOC" }, + { 0x201, "GPUREG_ATTRIBBUFFERS_FORMAT_LOW" }, + { 0x202, "GPUREG_ATTRIBBUFFERS_FORMAT_HIGH" }, + { 0x203, "GPUREG_ATTRIBBUFFER0_OFFSET" }, + { 0x204, "GPUREG_ATTRIBBUFFER0_CONFIG1" }, + { 0x205, "GPUREG_ATTRIBBUFFER0_CONFIG2" }, + { 0x206, "GPUREG_ATTRIBBUFFER1_OFFSET" }, + { 0x207, "GPUREG_ATTRIBBUFFER1_CONFIG1" }, + { 0x208, "GPUREG_ATTRIBBUFFER1_CONFIG2" }, + { 0x209, "GPUREG_ATTRIBBUFFER2_OFFSET" }, + { 0x20A, "GPUREG_ATTRIBBUFFER2_CONFIG1" }, + { 0x20B, "GPUREG_ATTRIBBUFFER2_CONFIG2" }, + { 0x20C, "GPUREG_ATTRIBBUFFER3_OFFSET" }, + { 0x20D, "GPUREG_ATTRIBBUFFER3_CONFIG1" }, + { 0x20E, "GPUREG_ATTRIBBUFFER3_CONFIG2" }, + { 0x20F, "GPUREG_ATTRIBBUFFER4_OFFSET" }, + { 0x210, "GPUREG_ATTRIBBUFFER4_CONFIG1" }, + { 0x211, "GPUREG_ATTRIBBUFFER4_CONFIG2" }, + { 0x212, "GPUREG_ATTRIBBUFFER5_OFFSET" }, + { 0x213, "GPUREG_ATTRIBBUFFER5_CONFIG1" }, + { 0x214, "GPUREG_ATTRIBBUFFER5_CONFIG2" }, + { 0x215, "GPUREG_ATTRIBBUFFER6_OFFSET" }, + { 0x216, "GPUREG_ATTRIBBUFFER6_CONFIG1" }, + { 0x217, "GPUREG_ATTRIBBUFFER6_CONFIG2" }, + { 0x218, "GPUREG_ATTRIBBUFFER7_OFFSET" }, + { 0x219, "GPUREG_ATTRIBBUFFER7_CONFIG1" }, + { 0x21A, "GPUREG_ATTRIBBUFFER7_CONFIG2" }, + { 0x21B, "GPUREG_ATTRIBBUFFER8_OFFSET" }, + { 0x21C, "GPUREG_ATTRIBBUFFER8_CONFIG1" }, + { 0x21D, "GPUREG_ATTRIBBUFFER8_CONFIG2" }, + { 0x21E, "GPUREG_ATTRIBBUFFER9_OFFSET" }, + { 0x21F, "GPUREG_ATTRIBBUFFER9_CONFIG1" }, + { 0x220, "GPUREG_ATTRIBBUFFER9_CONFIG2" }, + { 0x221, "GPUREG_ATTRIBBUFFER10_OFFSET" }, + { 0x222, "GPUREG_ATTRIBBUFFER10_CONFIG1" }, + { 0x223, "GPUREG_ATTRIBBUFFER10_CONFIG2" }, + { 0x224, "GPUREG_ATTRIBBUFFER11_OFFSET" }, + { 0x225, "GPUREG_ATTRIBBUFFER11_CONFIG1" }, + { 0x226, "GPUREG_ATTRIBBUFFER11_CONFIG2" }, + { 0x227, "GPUREG_INDEXBUFFER_CONFIG" }, + { 0x228, "GPUREG_NUMVERTICES" }, + { 0x229, "GPUREG_GEOSTAGE_CONFIG" }, + { 0x22A, "GPUREG_VERTEX_OFFSET" }, + + { 0x22D, "GPUREG_POST_VERTEX_CACHE_NUM" }, + { 0x22E, "GPUREG_DRAWARRAYS" }, + { 0x22F, "GPUREG_DRAWELEMENTS" }, + + { 0x231, "GPUREG_VTX_FUNC" }, + { 0x232, "GPUREG_FIXEDATTRIB_INDEX" }, + { 0x233, "GPUREG_FIXEDATTRIB_DATA0" }, + { 0x234, "GPUREG_FIXEDATTRIB_DATA1" }, + { 0x235, "GPUREG_FIXEDATTRIB_DATA2" }, + + { 0x238, "GPUREG_CMDBUF_SIZE0" }, + { 0x239, "GPUREG_CMDBUF_SIZE1" }, + { 0x23A, "GPUREG_CMDBUF_ADDR0" }, + { 0x23B, "GPUREG_CMDBUF_ADDR1" }, + { 0x23C, "GPUREG_CMDBUF_JUMP0" }, + { 0x23D, "GPUREG_CMDBUF_JUMP1" }, + + { 0x242, "GPUREG_VSH_NUM_ATTR" }, + + { 0x244, "GPUREG_VSH_COM_MODE" }, + { 0x245, "GPUREG_START_DRAW_FUNC0" }, + + { 0x24A, "GPUREG_VSH_OUTMAP_TOTAL1" }, + + { 0x251, "GPUREG_VSH_OUTMAP_TOTAL2" }, + { 0x252, "GPUREG_GSH_MISC0" }, + { 0x253, "GPUREG_GEOSTAGE_CONFIG2" }, + { 0x254, "GPUREG_GSH_MISC1" }, + + { 0x25E, "GPUREG_PRIMITIVE_CONFIG" }, + { 0x25F, "GPUREG_RESTART_PRIMITIVE" }, + + { 0x280, "GPUREG_GSH_BOOLUNIFORM" }, + { 0x281, "GPUREG_GSH_INTUNIFORM_I0" }, + { 0x282, "GPUREG_GSH_INTUNIFORM_I1" }, + { 0x283, "GPUREG_GSH_INTUNIFORM_I2" }, + { 0x284, "GPUREG_GSH_INTUNIFORM_I3" }, + + { 0x289, "GPUREG_GSH_INPUTBUFFER_CONFIG" }, + { 0x28A, "GPUREG_GSH_ENTRYPOINT" }, + { 0x28B, "GPUREG_GSH_ATTRIBUTES_PERMUTATION_LOW" }, + { 0x28C, "GPUREG_GSH_ATTRIBUTES_PERMUTATION_HIGH" }, + { 0x28D, "GPUREG_GSH_OUTMAP_MASK" }, + + { 0x28F, "GPUREG_GSH_CODETRANSFER_END" }, + { 0x290, "GPUREG_GSH_FLOATUNIFORM_INDEX" }, + { 0x291, "GPUREG_GSH_FLOATUNIFORM_DATA0" }, + { 0x292, "GPUREG_GSH_FLOATUNIFORM_DATA1" }, + { 0x293, "GPUREG_GSH_FLOATUNIFORM_DATA2" }, + { 0x294, "GPUREG_GSH_FLOATUNIFORM_DATA3" }, + { 0x295, "GPUREG_GSH_FLOATUNIFORM_DATA4" }, + { 0x296, "GPUREG_GSH_FLOATUNIFORM_DATA5" }, + { 0x297, "GPUREG_GSH_FLOATUNIFORM_DATA6" }, + { 0x298, "GPUREG_GSH_FLOATUNIFORM_DATA7" }, + + { 0x29B, "GPUREG_GSH_CODETRANSFER_INDEX" }, + { 0x29C, "GPUREG_GSH_CODETRANSFER_DATA0" }, + { 0x29D, "GPUREG_GSH_CODETRANSFER_DATA1" }, + { 0x29E, "GPUREG_GSH_CODETRANSFER_DATA2" }, + { 0x29F, "GPUREG_GSH_CODETRANSFER_DATA3" }, + { 0x2A0, "GPUREG_GSH_CODETRANSFER_DATA4" }, + { 0x2A1, "GPUREG_GSH_CODETRANSFER_DATA5" }, + { 0x2A2, "GPUREG_GSH_CODETRANSFER_DATA6" }, + { 0x2A3, "GPUREG_GSH_CODETRANSFER_DATA7" }, + + { 0x2A5, "GPUREG_GSH_OPDESCS_INDEX" }, + { 0x2A6, "GPUREG_GSH_OPDESCS_DATA0" }, + { 0x2A7, "GPUREG_GSH_OPDESCS_DATA1" }, + { 0x2A8, "GPUREG_GSH_OPDESCS_DATA2" }, + { 0x2A9, "GPUREG_GSH_OPDESCS_DATA3" }, + { 0x2AA, "GPUREG_GSH_OPDESCS_DATA4" }, + { 0x2AB, "GPUREG_GSH_OPDESCS_DATA5" }, + { 0x2AC, "GPUREG_GSH_OPDESCS_DATA6" }, + { 0x2AD, "GPUREG_GSH_OPDESCS_DATA7" }, + + { 0x2B0, "GPUREG_VSH_BOOLUNIFORM" }, + { 0x2B1, "GPUREG_VSH_INTUNIFORM_I0" }, + { 0x2B2, "GPUREG_VSH_INTUNIFORM_I1" }, + { 0x2B3, "GPUREG_VSH_INTUNIFORM_I2" }, + { 0x2B4, "GPUREG_VSH_INTUNIFORM_I3" }, + + { 0x2B9, "GPUREG_VSH_INPUTBUFFER_CONFIG" }, + { 0x2BA, "GPUREG_VSH_ENTRYPOINT" }, + { 0x2BB, "GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW" }, + { 0x2BC, "GPUREG_VSH_ATTRIBUTES_PERMUTATION_HIGH" }, + { 0x2BD, "GPUREG_VSH_OUTMAP_MASK" }, + + { 0x2BF, "GPUREG_VSH_CODETRANSFER_END" }, + { 0x2C0, "GPUREG_VSH_FLOATUNIFORM_INDEX" }, + { 0x2C1, "GPUREG_VSH_FLOATUNIFORM_DATA0" }, + { 0x2C2, "GPUREG_VSH_FLOATUNIFORM_DATA1" }, + { 0x2C3, "GPUREG_VSH_FLOATUNIFORM_DATA2" }, + { 0x2C4, "GPUREG_VSH_FLOATUNIFORM_DATA3" }, + { 0x2C5, "GPUREG_VSH_FLOATUNIFORM_DATA4" }, + { 0x2C6, "GPUREG_VSH_FLOATUNIFORM_DATA5" }, + { 0x2C7, "GPUREG_VSH_FLOATUNIFORM_DATA6" }, + { 0x2C8, "GPUREG_VSH_FLOATUNIFORM_DATA7" }, + + { 0x2CB, "GPUREG_VSH_CODETRANSFER_INDEX" }, + { 0x2CC, "GPUREG_VSH_CODETRANSFER_DATA0" }, + { 0x2CD, "GPUREG_VSH_CODETRANSFER_DATA1" }, + { 0x2CE, "GPUREG_VSH_CODETRANSFER_DATA2" }, + { 0x2CF, "GPUREG_VSH_CODETRANSFER_DATA3" }, + { 0x2D0, "GPUREG_VSH_CODETRANSFER_DATA4" }, + { 0x2D1, "GPUREG_VSH_CODETRANSFER_DATA5" }, + { 0x2D2, "GPUREG_VSH_CODETRANSFER_DATA6" }, + { 0x2D3, "GPUREG_VSH_CODETRANSFER_DATA7" }, + + { 0x2D5, "GPUREG_VSH_OPDESCS_INDEX" }, + { 0x2D6, "GPUREG_VSH_OPDESCS_DATA0" }, + { 0x2D7, "GPUREG_VSH_OPDESCS_DATA1" }, + { 0x2D8, "GPUREG_VSH_OPDESCS_DATA2" }, + { 0x2D9, "GPUREG_VSH_OPDESCS_DATA3" }, + { 0x2DA, "GPUREG_VSH_OPDESCS_DATA4" }, + { 0x2DB, "GPUREG_VSH_OPDESCS_DATA5" }, + { 0x2DC, "GPUREG_VSH_OPDESCS_DATA6" }, + { 0x2DD, "GPUREG_VSH_OPDESCS_DATA7" }, +}; + std::string Regs::GetCommandName(int index) { - static std::unordered_map<u32, std::string> map; + static std::unordered_map<u32, const char*> map; if (map.empty()) { - #define ADD_FIELD(name) \ - map.insert({static_cast<u32>(PICA_REG_INDEX(name)), #name}); \ - /* TODO: change to Regs::name when VS2015 and other compilers support it */ \ - for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(Regs().name) / 4; ++i) \ - map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ - - ADD_FIELD(trigger_irq); - ADD_FIELD(cull_mode); - ADD_FIELD(viewport_size_x); - ADD_FIELD(viewport_size_y); - ADD_FIELD(viewport_depth_range); - ADD_FIELD(viewport_depth_far_plane); - ADD_FIELD(viewport_corner); - ADD_FIELD(texture0_enable); - ADD_FIELD(texture0); - ADD_FIELD(texture0_format); - ADD_FIELD(texture1); - ADD_FIELD(texture1_format); - ADD_FIELD(texture2); - ADD_FIELD(texture2_format); - ADD_FIELD(tev_stage0); - ADD_FIELD(tev_stage1); - ADD_FIELD(tev_stage2); - ADD_FIELD(tev_stage3); - ADD_FIELD(tev_combiner_buffer_input); - ADD_FIELD(tev_stage4); - ADD_FIELD(tev_stage5); - ADD_FIELD(tev_combiner_buffer_color); - ADD_FIELD(output_merger); - ADD_FIELD(framebuffer); - ADD_FIELD(vertex_attributes); - ADD_FIELD(index_array); - ADD_FIELD(num_vertices); - ADD_FIELD(vertex_offset); - ADD_FIELD(trigger_draw); - ADD_FIELD(trigger_draw_indexed); - ADD_FIELD(vs_default_attributes_setup); - ADD_FIELD(command_buffer); - ADD_FIELD(triangle_topology); - ADD_FIELD(restart_primitive); - ADD_FIELD(gs.bool_uniforms); - ADD_FIELD(gs.int_uniforms); - ADD_FIELD(gs.main_offset); - ADD_FIELD(gs.input_register_map); - ADD_FIELD(gs.uniform_setup); - ADD_FIELD(gs.program); - ADD_FIELD(gs.swizzle_patterns); - ADD_FIELD(vs.bool_uniforms); - ADD_FIELD(vs.int_uniforms); - ADD_FIELD(vs.main_offset); - ADD_FIELD(vs.input_register_map); - ADD_FIELD(vs.uniform_setup); - ADD_FIELD(vs.program); - ADD_FIELD(vs.swizzle_patterns); - -#undef ADD_FIELD + map.insert(begin(register_names), end(register_names)); } // Return empty string if no match is found diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 2f1b2dec4..2e0c33201 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -16,6 +16,8 @@ #include "common/vector_math.h" #include "common/logging/log.h" +#include "pica_types.h" + namespace Pica { // Returns index corresponding to the Regs member labeled by field_name @@ -239,7 +241,8 @@ struct Regs { TextureConfig texture0; INSERT_PADDING_WORDS(0x8); BitField<0, 4, TextureFormat> texture0_format; - INSERT_PADDING_WORDS(0x2); + BitField<0, 1, u32> fragment_lighting_enable; + INSERT_PADDING_WORDS(0x1); TextureConfig texture1; BitField<0, 4, TextureFormat> texture1_format; INSERT_PADDING_WORDS(0x2); @@ -641,7 +644,268 @@ struct Regs { } } - INSERT_PADDING_WORDS(0xe0); + INSERT_PADDING_WORDS(0x20); + + enum class LightingSampler { + Distribution0 = 0, + Distribution1 = 1, + Fresnel = 3, + ReflectBlue = 4, + ReflectGreen = 5, + ReflectRed = 6, + SpotlightAttenuation = 8, + DistanceAttenuation = 16, + }; + + /** + * Pica fragment lighting supports using different LUTs for each lighting component: + * Reflectance R, G, and B channels, distribution function for specular components 0 and 1, + * fresnel factor, and spotlight attenuation. Furthermore, which LUTs are used for each channel + * (or whether a channel is enabled at all) is specified by various pre-defined lighting + * configurations. With configurations that require more LUTs, more cycles are required on HW to + * perform lighting computations. + */ + enum class LightingConfig { + Config0 = 0, ///< Reflect Red, Distribution 0, Spotlight + Config1 = 1, ///< Reflect Red, Fresnel, Spotlight + Config2 = 2, ///< Reflect Red, Distribution 0/1 + Config3 = 3, ///< Distribution 0/1, Fresnel + Config4 = 4, ///< Reflect Red/Green/Blue, Distribution 0/1, Spotlight + Config5 = 5, ///< Reflect Red/Green/Blue, Distribution 0, Fresnel, Spotlight + Config6 = 6, ///< Reflect Red, Distribution 0/1, Fresnel, Spotlight + Config7 = 8, ///< Reflect Red/Green/Blue, Distribution 0/1, Fresnel, Spotlight + ///< NOTE: '8' is intentional, '7' does not appear to be a valid configuration + }; + + /// Selects which lighting components are affected by fresnel + enum class LightingFresnelSelector { + None = 0, ///< Fresnel is disabled + PrimaryAlpha = 1, ///< Primary (diffuse) lighting alpha is affected by fresnel + SecondaryAlpha = 2, ///< Secondary (specular) lighting alpha is affected by fresnel + Both = PrimaryAlpha | SecondaryAlpha, ///< Both primary and secondary lighting alphas are affected by fresnel + }; + + /// Factor used to scale the output of a lighting LUT + enum class LightingScale { + Scale1 = 0, ///< Scale is 1x + Scale2 = 1, ///< Scale is 2x + Scale4 = 2, ///< Scale is 4x + Scale8 = 3, ///< Scale is 8x + Scale1_4 = 6, ///< Scale is 0.25x + Scale1_2 = 7, ///< Scale is 0.5x + }; + + enum class LightingLutInput { + NH = 0, // Cosine of the angle between the normal and half-angle vectors + VH = 1, // Cosine of the angle between the view and half-angle vectors + NV = 2, // Cosine of the angle between the normal and the view vector + LN = 3, // Cosine of the angle between the light and the normal vectors + }; + + enum class LightingBumpMode : u32 { + None = 0, + NormalMap = 1, + TangentMap = 2, + }; + + union LightColor { + BitField< 0, 10, u32> b; + BitField<10, 10, u32> g; + BitField<20, 10, u32> r; + + Math::Vec3f ToVec3f() const { + // These fields are 10 bits wide, however 255 corresponds to 1.0f for each color component + return Math::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f); + } + }; + + /// Returns true if the specified lighting sampler is supported by the current Pica lighting configuration + static bool IsLightingSamplerSupported(LightingConfig config, LightingSampler sampler) { + switch (sampler) { + case LightingSampler::Distribution0: + return (config != LightingConfig::Config1); + + case LightingSampler::Distribution1: + return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) && (config != LightingConfig::Config5); + + case LightingSampler::Fresnel: + return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) && (config != LightingConfig::Config4); + + case LightingSampler::ReflectRed: + return (config != LightingConfig::Config3); + + case LightingSampler::ReflectGreen: + case LightingSampler::ReflectBlue: + return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) || (config == LightingConfig::Config7); + } + return false; + } + + struct { + struct LightSrc { + LightColor specular_0; // material.specular_0 * light.specular_0 + LightColor specular_1; // material.specular_1 * light.specular_1 + LightColor diffuse; // material.diffuse * light.diffuse + LightColor ambient; // material.ambient * light.ambient + + struct { + // Encoded as 16-bit floating point + union { + BitField< 0, 16, u32> x; + BitField<16, 16, u32> y; + }; + union { + BitField< 0, 16, u32> z; + }; + + INSERT_PADDING_WORDS(0x3); + + union { + BitField<0, 1, u32> directional; + BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0 + }; + }; + + BitField<0, 20, u32> dist_atten_bias; + BitField<0, 20, u32> dist_atten_scale; + + INSERT_PADDING_WORDS(0x4); + }; + static_assert(sizeof(LightSrc) == 0x10 * sizeof(u32), "LightSrc structure must be 0x10 words"); + + LightSrc light[8]; + LightColor global_ambient; // Emission + (material.ambient * lighting.ambient) + INSERT_PADDING_WORDS(0x1); + BitField<0, 3, u32> num_lights; // Number of enabled lights - 1 + + union { + BitField< 2, 2, LightingFresnelSelector> fresnel_selector; + BitField< 4, 4, LightingConfig> config; + BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2 + BitField<27, 1, u32> clamp_highlights; + BitField<28, 2, LightingBumpMode> bump_mode; + BitField<30, 1, u32> disable_bump_renorm; + }; + + union { + BitField<16, 1, u32> disable_lut_d0; + BitField<17, 1, u32> disable_lut_d1; + BitField<19, 1, u32> disable_lut_fr; + BitField<20, 1, u32> disable_lut_rr; + BitField<21, 1, u32> disable_lut_rg; + BitField<22, 1, u32> disable_lut_rb; + + // Each bit specifies whether distance attenuation should be applied for the + // corresponding light + + BitField<24, 1, u32> disable_dist_atten_light_0; + BitField<25, 1, u32> disable_dist_atten_light_1; + BitField<26, 1, u32> disable_dist_atten_light_2; + BitField<27, 1, u32> disable_dist_atten_light_3; + BitField<28, 1, u32> disable_dist_atten_light_4; + BitField<29, 1, u32> disable_dist_atten_light_5; + BitField<30, 1, u32> disable_dist_atten_light_6; + BitField<31, 1, u32> disable_dist_atten_light_7; + }; + + bool IsDistAttenDisabled(unsigned index) const { + const unsigned disable[] = { disable_dist_atten_light_0, disable_dist_atten_light_1, + disable_dist_atten_light_2, disable_dist_atten_light_3, + disable_dist_atten_light_4, disable_dist_atten_light_5, + disable_dist_atten_light_6, disable_dist_atten_light_7 }; + return disable[index] != 0; + } + + union { + BitField<0, 8, u32> index; ///< Index at which to set data in the LUT + BitField<8, 5, u32> type; ///< Type of LUT for which to set data + } lut_config; + + BitField<0, 1, u32> disable; + INSERT_PADDING_WORDS(0x1); + + // When data is written to any of these registers, it gets written to the lookup table of + // the selected type at the selected index, specified above in the `lut_config` register. + // With each write, `lut_config.index` is incremented. It does not matter which of these + // registers is written to, the behavior will be the same. + u32 lut_data[8]; + + // These are used to specify if absolute (abs) value should be used for each LUT index. When + // abs mode is disabled, LUT indexes are in the range of (-1.0, 1.0). Otherwise, they are in + // the range of (0.0, 1.0). + union { + BitField< 1, 1, u32> disable_d0; + BitField< 5, 1, u32> disable_d1; + BitField< 9, 1, u32> disable_sp; + BitField<13, 1, u32> disable_fr; + BitField<17, 1, u32> disable_rb; + BitField<21, 1, u32> disable_rg; + BitField<25, 1, u32> disable_rr; + } abs_lut_input; + + union { + BitField< 0, 3, LightingLutInput> d0; + BitField< 4, 3, LightingLutInput> d1; + BitField< 8, 3, LightingLutInput> sp; + BitField<12, 3, LightingLutInput> fr; + BitField<16, 3, LightingLutInput> rb; + BitField<20, 3, LightingLutInput> rg; + BitField<24, 3, LightingLutInput> rr; + } lut_input; + + union { + BitField< 0, 3, LightingScale> d0; + BitField< 4, 3, LightingScale> d1; + BitField< 8, 3, LightingScale> sp; + BitField<12, 3, LightingScale> fr; + BitField<16, 3, LightingScale> rb; + BitField<20, 3, LightingScale> rg; + BitField<24, 3, LightingScale> rr; + + static float GetScale(LightingScale scale) { + switch (scale) { + case LightingScale::Scale1: + return 1.0f; + case LightingScale::Scale2: + return 2.0f; + case LightingScale::Scale4: + return 4.0f; + case LightingScale::Scale8: + return 8.0f; + case LightingScale::Scale1_4: + return 0.25f; + case LightingScale::Scale1_2: + return 0.5f; + } + return 0.0f; + } + } lut_scale; + + INSERT_PADDING_WORDS(0x6); + + union { + // There are 8 light enable "slots", corresponding to the total number of lights + // supported by Pica. For N enabled lights (specified by register 0x1c2, or 'src_num' + // above), the first N slots below will be set to integers within the range of 0-7, + // corresponding to the actual light that is enabled for each slot. + + BitField< 0, 3, u32> slot_0; + BitField< 4, 3, u32> slot_1; + BitField< 8, 3, u32> slot_2; + BitField<12, 3, u32> slot_3; + BitField<16, 3, u32> slot_4; + BitField<20, 3, u32> slot_5; + BitField<24, 3, u32> slot_6; + BitField<28, 3, u32> slot_7; + + unsigned GetNum(unsigned index) const { + const unsigned enable_slots[] = { slot_0, slot_1, slot_2, slot_3, slot_4, slot_5, slot_6, slot_7 }; + return enable_slots[index]; + } + } light_enable; + } lighting; + + INSERT_PADDING_WORDS(0x26); enum class VertexAttributeFormat : u64 { BYTE = 0, @@ -825,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, @@ -990,6 +1263,7 @@ ASSERT_REG_POSITION(viewport_corner, 0x68); ASSERT_REG_POSITION(texture0_enable, 0x80); ASSERT_REG_POSITION(texture0, 0x81); ASSERT_REG_POSITION(texture0_format, 0x8e); +ASSERT_REG_POSITION(fragment_lighting_enable, 0x8f); ASSERT_REG_POSITION(texture1, 0x91); ASSERT_REG_POSITION(texture1_format, 0x96); ASSERT_REG_POSITION(texture2, 0x99); @@ -1004,6 +1278,7 @@ ASSERT_REG_POSITION(tev_stage5, 0xf8); ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd); ASSERT_REG_POSITION(output_merger, 0x100); ASSERT_REG_POSITION(framebuffer, 0x110); +ASSERT_REG_POSITION(lighting, 0x140); ASSERT_REG_POSITION(vertex_attributes, 0x200); ASSERT_REG_POSITION(index_array, 0x227); ASSERT_REG_POSITION(num_vertices, 0x228); @@ -1012,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); @@ -1026,157 +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 float24 { - static float24 FromFloat32(float val) { - float24 ret; - ret.value = val; - return ret; - } - - // 16 bit mantissa, 7 bit exponent, 1 bit sign - // TODO: No idea if this works as intended - static float24 FromRawFloat24(u32 hex) { - float24 ret; - if ((hex & 0xFFFFFF) == 0) { - ret.value = 0; - } else { - u32 mantissa = hex & 0xFFFF; - u32 exponent = (hex >> 16) & 0x7F; - u32 sign = hex >> 23; - ret.value = std::pow(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * std::pow(2.0f, -16.f)); - if (sign) - ret.value = -ret.value; - } - return ret; - } - - static float24 Zero() { - return FromFloat32(0.f); - } - - // Not recommended for anything but logging - float ToFloat32() const { - return value; - } - - float24 operator * (const float24& flt) const { - if ((this->value == 0.f && !std::isnan(flt.value)) || - (flt.value == 0.f && !std::isnan(this->value))) - // PICA gives 0 instead of NaN when multiplying by inf - return Zero(); - return float24::FromFloat32(ToFloat32() * flt.ToFloat32()); - } - - float24 operator / (const float24& flt) const { - return float24::FromFloat32(ToFloat32() / flt.ToFloat32()); - } - - float24 operator + (const float24& flt) const { - return float24::FromFloat32(ToFloat32() + flt.ToFloat32()); - } - - float24 operator - (const float24& flt) const { - return float24::FromFloat32(ToFloat32() - flt.ToFloat32()); - } - - float24& operator *= (const float24& flt) { - if ((this->value == 0.f && !std::isnan(flt.value)) || - (flt.value == 0.f && !std::isnan(this->value))) - // PICA gives 0 instead of NaN when multiplying by inf - *this = Zero(); - else value *= flt.ToFloat32(); - return *this; - } - - float24& operator /= (const float24& flt) { - value /= flt.ToFloat32(); - return *this; - } - - float24& operator += (const float24& flt) { - value += flt.ToFloat32(); - return *this; - } - - float24& operator -= (const float24& flt) { - value -= flt.ToFloat32(); - return *this; - } - - float24 operator - () const { - return float24::FromFloat32(-ToFloat32()); - } - - bool operator < (const float24& flt) const { - return ToFloat32() < flt.ToFloat32(); - } - - bool operator > (const float24& flt) const { - return ToFloat32() > flt.ToFloat32(); - } - - bool operator >= (const float24& flt) const { - return ToFloat32() >= flt.ToFloat32(); - } - - bool operator <= (const float24& flt) const { - return ToFloat32() <= flt.ToFloat32(); - } - - bool operator == (const float24& flt) const { - return ToFloat32() == flt.ToFloat32(); - } - - bool operator != (const float24& flt) const { - return ToFloat32() != flt.ToFloat32(); - } - -private: - // Stored as a regular float, merely for convenience - // TODO: Perform proper arithmetic on this! - float value; -}; -static_assert(sizeof(float24) == sizeof(float), "Shader JIT assumes float24 is implemented as a 32-bit float"); - -/// 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; - - /// 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/pica_types.h b/src/video_core/pica_types.h new file mode 100644 index 000000000..ecf45654b --- /dev/null +++ b/src/video_core/pica_types.h @@ -0,0 +1,146 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstring> + +#include "common/common_types.h" + +namespace Pica { + +/** + * Template class for converting arbitrary Pica float types to IEEE 754 32-bit single-precision + * floating point. + * + * When decoding, format is as follows: + * - The first `M` bits are the mantissa + * - The next `E` bits are the exponent + * - The last bit is the sign bit + * + * @todo Verify on HW if this conversion is sufficiently accurate. + */ +template<unsigned M, unsigned E> +struct Float { +public: + static Float<M, E> FromFloat32(float val) { + Float<M, E> ret; + ret.value = val; + return ret; + } + + static Float<M, E> FromRaw(u32 hex) { + Float<M, E> res; + + const int width = M + E + 1; + const int bias = 128 - (1 << (E - 1)); + const int exponent = (hex >> M) & ((1 << E) - 1); + const unsigned mantissa = hex & ((1 << M) - 1); + + if (hex & ((1 << (width - 1)) - 1)) + hex = ((hex >> (E + M)) << 31) | (mantissa << (23 - M)) | ((exponent + bias) << 23); + else + hex = ((hex >> (E + M)) << 31); + + std::memcpy(&res.value, &hex, sizeof(float)); + + return res; + } + + static Float<M, E> Zero() { + return FromFloat32(0.f); + } + + // Not recommended for anything but logging + float ToFloat32() const { + return value; + } + + Float<M, E> operator * (const Float<M, E>& flt) const { + if ((this->value == 0.f && !std::isnan(flt.value)) || + (flt.value == 0.f && !std::isnan(this->value))) + // PICA gives 0 instead of NaN when multiplying by inf + return Zero(); + return Float<M, E>::FromFloat32(ToFloat32() * flt.ToFloat32()); + } + + Float<M, E> operator / (const Float<M, E>& flt) const { + return Float<M, E>::FromFloat32(ToFloat32() / flt.ToFloat32()); + } + + Float<M, E> operator + (const Float<M, E>& flt) const { + return Float<M, E>::FromFloat32(ToFloat32() + flt.ToFloat32()); + } + + Float<M, E> operator - (const Float<M, E>& flt) const { + return Float<M, E>::FromFloat32(ToFloat32() - flt.ToFloat32()); + } + + Float<M, E>& operator *= (const Float<M, E>& flt) { + if ((this->value == 0.f && !std::isnan(flt.value)) || + (flt.value == 0.f && !std::isnan(this->value))) + // PICA gives 0 instead of NaN when multiplying by inf + *this = Zero(); + else value *= flt.ToFloat32(); + return *this; + } + + Float<M, E>& operator /= (const Float<M, E>& flt) { + value /= flt.ToFloat32(); + return *this; + } + + Float<M, E>& operator += (const Float<M, E>& flt) { + value += flt.ToFloat32(); + return *this; + } + + Float<M, E>& operator -= (const Float<M, E>& flt) { + value -= flt.ToFloat32(); + return *this; + } + + Float<M, E> operator - () const { + return Float<M, E>::FromFloat32(-ToFloat32()); + } + + bool operator < (const Float<M, E>& flt) const { + return ToFloat32() < flt.ToFloat32(); + } + + bool operator > (const Float<M, E>& flt) const { + return ToFloat32() > flt.ToFloat32(); + } + + bool operator >= (const Float<M, E>& flt) const { + return ToFloat32() >= flt.ToFloat32(); + } + + bool operator <= (const Float<M, E>& flt) const { + return ToFloat32() <= flt.ToFloat32(); + } + + bool operator == (const Float<M, E>& flt) const { + return ToFloat32() == flt.ToFloat32(); + } + + bool operator != (const Float<M, E>& flt) const { + return ToFloat32() != flt.ToFloat32(); + } + +private: + static const unsigned MASK = (1 << (M + E + 1)) - 1; + static const unsigned MANTISSA_MASK = (1 << M) - 1; + static const unsigned EXPONENT_MASK = (1 << E) - 1; + + // Stored as a regular float, merely for convenience + // TODO: Perform proper arithmetic on this! + float value; +}; + +using float24 = Float<16, 7>; +using float20 = Float<12, 7>; +using float16 = Float<10, 5>; + +} // namespace Pica diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp index 44a8dbfe9..0061690f1 100644 --- a/src/video_core/primitive_assembly.cpp +++ b/src/video_core/primitive_assembly.cpp @@ -39,13 +39,12 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl buffer[buffer_index] = vtx; - if (topology == Regs::TriangleTopology::Strip) { - strip_ready |= (buffer_index == 1); + strip_ready |= (buffer_index == 1); + + if (topology == Regs::TriangleTopology::Strip) buffer_index = !buffer_index; - } else if (topology == Regs::TriangleTopology::Fan) { + else if (topology == Regs::TriangleTopology::Fan) buffer_index = 1; - strip_ready = true; - } break; default: @@ -54,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..fd02aa652 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" @@ -857,12 +858,12 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, } } - // TODO: Does depth indeed only get written even if depth testing is enabled? + unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format); + u32 z = (u32)((v0.screenpos[2].ToFloat32() * w0 + + v1.screenpos[2].ToFloat32() * w1 + + v2.screenpos[2].ToFloat32() * w2) * ((1 << num_bits) - 1) / wsum); + if (output_merger.depth_test_enable) { - unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format); - u32 z = (u32)((v0.screenpos[2].ToFloat32() * w0 + - v1.screenpos[2].ToFloat32() * w1 + - v2.screenpos[2].ToFloat32() * w2) * ((1 << num_bits) - 1) / wsum); u32 ref_z = GetDepth(x >> 4, y >> 4); bool pass = false; @@ -906,11 +907,11 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, UpdateStencil(stencil_test.action_depth_fail); continue; } - - if (output_merger.depth_write_enable) - SetDepth(x >> 4, y >> 4, z); } + if (output_merger.depth_write_enable) + SetDepth(x >> 4, y >> 4, z); + // The stencil depth_pass action is executed even if depth testing is disabled if (stencil_action_enable) UpdateStencil(stencil_test.action_depth_pass); diff --git a/src/video_core/hwrasterizer_base.h b/src/video_core/rasterizer_interface.h index 54b8892fb..008c5827b 100644 --- a/src/video_core/hwrasterizer_base.h +++ b/src/video_core/rasterizer_interface.h @@ -12,10 +12,11 @@ struct OutputVertex; } } -class HWRasterizer { +namespace VideoCore { + +class RasterizerInterface { public: - virtual ~HWRasterizer() { - } + virtual ~RasterizerInterface() {} /// Initialize API-specific GPU objects virtual void InitObjects() = 0; @@ -32,14 +33,16 @@ public: virtual void DrawTriangles() = 0; /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer - virtual void CommitFramebuffer() = 0; + virtual void FlushFramebuffer() = 0; /// Notify rasterizer that the specified PICA register has been changed virtual void NotifyPicaRegisterChanged(u32 id) = 0; - /// Notify rasterizer that the specified 3DS memory region will be read from after this notification - virtual void NotifyPreRead(PAddr addr, u32 size) = 0; + /// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory. + virtual void FlushRegion(PAddr addr, u32 size) = 0; - /// Notify rasterizer that a 3DS memory region has been changed - virtual void NotifyFlush(PAddr addr, u32 size) = 0; + /// Notify rasterizer that any caches of the specified region should be discraded and reloaded from 3DS memory. + virtual void InvalidateRegion(PAddr addr, u32 size) = 0; }; + +} diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp new file mode 100644 index 000000000..6467ff723 --- /dev/null +++ b/src/video_core/renderer_base.cpp @@ -0,0 +1,29 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/make_unique.h" + +#include "core/settings.h" + +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" +#include "video_core/swrasterizer.h" +#include "video_core/renderer_opengl/gl_rasterizer.h" + +void RendererBase::RefreshRasterizerSetting() { + bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled; + if (rasterizer == nullptr || opengl_rasterizer_active != hw_renderer_enabled) { + opengl_rasterizer_active = hw_renderer_enabled; + + if (hw_renderer_enabled) { + rasterizer = Common::make_unique<RasterizerOpenGL>(); + } else { + rasterizer = Common::make_unique<VideoCore::SWRasterizer>(); + } + rasterizer->InitObjects(); + rasterizer->Reset(); + } +} diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 6587bcf27..f68091cc8 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -8,7 +8,7 @@ #include "common/common_types.h" -#include "video_core/hwrasterizer_base.h" +#include "video_core/rasterizer_interface.h" class EmuWindow; @@ -22,9 +22,6 @@ public: kFramebuffer_Texture }; - RendererBase() : m_current_fps(0), m_current_frame(0) { - } - virtual ~RendererBase() { } @@ -38,7 +35,7 @@ public: virtual void SetWindow(EmuWindow* window) = 0; /// Initialize the renderer - virtual void Init() = 0; + virtual bool Init() = 0; /// Shutdown the renderer virtual void ShutDown() = 0; @@ -46,18 +43,25 @@ public: // Getter/setter functions: // ------------------------ - f32 GetCurrentframe() const { + f32 GetCurrentFPS() const { return m_current_fps; } - int current_frame() const { + int GetCurrentFrame() const { return m_current_frame; } - std::unique_ptr<HWRasterizer> hw_rasterizer; + VideoCore::RasterizerInterface* Rasterizer() const { + return rasterizer.get(); + } + + void RefreshRasterizerSetting(); protected: - f32 m_current_fps; ///< Current framerate, should be set by the renderer - int m_current_frame; ///< Current frame, should be set by the renderer + std::unique_ptr<VideoCore::RasterizerInterface> rasterizer; + f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer + int m_current_frame = 0; ///< Current frame, should be set by the renderer +private: + bool opengl_rasterizer_active = false; }; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 822739088..b3dc6aa19 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" @@ -36,7 +37,7 @@ static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { stage.GetAlphaMultiplier() == 1); } -RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr(0) { } +RasterizerOpenGL::RasterizerOpenGL() : cached_fb_color_addr(0), cached_fb_depth_addr(0) { } RasterizerOpenGL::~RasterizerOpenGL() { } void RasterizerOpenGL::InitObjects() { @@ -75,6 +76,12 @@ void RasterizerOpenGL::InitObjects() { glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1); glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2); + glVertexAttribPointer(GLShader::ATTRIBUTE_NORMQUAT, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, normquat)); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_NORMQUAT); + + glVertexAttribPointer(GLShader::ATTRIBUTE_VIEW, 3, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, view)); + glEnableVertexAttribArray(GLShader::ATTRIBUTE_VIEW); + SetShader(); // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation @@ -120,12 +127,26 @@ void RasterizerOpenGL::InitObjects() { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0); + for (size_t i = 0; i < lighting_lut.size(); ++i) { + lighting_lut[i].Create(); + state.lighting_lut[i].texture_1d = lighting_lut[i].handle; + + glActiveTexture(GL_TEXTURE3 + i); + glBindTexture(GL_TEXTURE_1D, state.lighting_lut[i].texture_1d); + + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + state.Apply(); + ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER)); } void RasterizerOpenGL::Reset() { SyncCullMode(); + SyncDepthModifiers(); SyncBlendEnabled(); SyncBlendFuncs(); SyncBlendColor(); @@ -135,15 +156,37 @@ void RasterizerOpenGL::Reset() { SetShader(); - res_cache.FullFlush(); + res_cache.InvalidateAll(); +} + +/** + * This is a helper function to resolve an issue with opposite quaternions being interpolated by + * OpenGL. See below for a detailed description of this issue (yuriks): + * + * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you + * interpolate two quaternions that are opposite, instead of going from one rotation to another + * using the shortest path, you'll go around the longest path. You can test if two quaternions are + * opposite by checking if Dot(Q1, W2) < 0. In that case, you can flip either of them, therefore + * making Dot(-Q1, W2) positive. + * + * NOTE: This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This + * should be correct for nearly all cases, however a more correct implementation (but less trivial + * and perhaps unnecessary) would be to handle this per-fragment, by interpolating the quaternions + * manually using two Lerps, and doing this correction before each Lerp. + */ +static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) { + Math::Vec4f a{ qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32() }; + Math::Vec4f b{ qb.x.ToFloat32(), qb.y.ToFloat32(), qb.z.ToFloat32(), qb.w.ToFloat32() }; + + return (Math::Dot(a, b) < 0.f); } void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, const Pica::Shader::OutputVertex& v2) { - vertex_batch.emplace_back(v0); - vertex_batch.emplace_back(v1); - vertex_batch.emplace_back(v2); + vertex_batch.emplace_back(v0, false); + vertex_batch.emplace_back(v1, AreQuaternionsOpposite(v0.quat, v1.quat)); + vertex_batch.emplace_back(v2, AreQuaternionsOpposite(v0.quat, v2.quat)); } void RasterizerOpenGL::DrawTriangles() { @@ -155,6 +198,13 @@ void RasterizerOpenGL::DrawTriangles() { state.draw.shader_dirty = false; } + for (unsigned index = 0; index < lighting_lut.size(); index++) { + if (uniform_block_data.lut_dirty[index]) { + SyncLightingLUT(index); + uniform_block_data.lut_dirty[index] = false; + } + } + if (uniform_block_data.dirty) { glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW); uniform_block_data.dirty = false; @@ -168,19 +218,17 @@ void RasterizerOpenGL::DrawTriangles() { // Flush the resource cache at the current depth and color framebuffer addresses for render-to-texture const auto& regs = Pica::g_state.regs; - PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress(); - u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format) - * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight(); + u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format) + * fb_color_texture.width * fb_color_texture.height; - PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress(); - u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format) - * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight(); + u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format) + * fb_depth_texture.width * fb_depth_texture.height; - res_cache.NotifyFlush(cur_fb_color_addr, cur_fb_color_size, true); - res_cache.NotifyFlush(cur_fb_depth_addr, cur_fb_depth_size, true); + res_cache.InvalidateInRange(cached_fb_color_addr, cached_fb_color_size, true); + res_cache.InvalidateInRange(cached_fb_depth_addr, cached_fb_depth_size, true); } -void RasterizerOpenGL::CommitFramebuffer() { +void RasterizerOpenGL::FlushFramebuffer() { CommitColorBuffer(); CommitDepthBuffer(); } @@ -188,15 +236,18 @@ void RasterizerOpenGL::CommitFramebuffer() { void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { const auto& regs = Pica::g_state.regs; - if (!Settings::values.use_hw_renderer) - return; - switch(id) { // Culling case PICA_REG_INDEX(cull_mode): SyncCullMode(); break; + // Depth modifiers + case PICA_REG_INDEX(viewport_depth_range): + case PICA_REG_INDEX(viewport_depth_far_plane): + SyncDepthModifiers(); + break; + // Blending case PICA_REG_INDEX(output_merger.alphablend_enable): SyncBlendEnabled(); @@ -281,54 +332,203 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { case PICA_REG_INDEX(tev_combiner_buffer_color): SyncCombinerColor(); break; + + // Fragment lighting specular 0 color + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_0, 0x140 + 0 * 0x10): + SyncLightSpecular0(0); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_0, 0x140 + 1 * 0x10): + SyncLightSpecular0(1); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_0, 0x140 + 2 * 0x10): + SyncLightSpecular0(2); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_0, 0x140 + 3 * 0x10): + SyncLightSpecular0(3); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_0, 0x140 + 4 * 0x10): + SyncLightSpecular0(4); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_0, 0x140 + 5 * 0x10): + SyncLightSpecular0(5); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_0, 0x140 + 6 * 0x10): + SyncLightSpecular0(6); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_0, 0x140 + 7 * 0x10): + SyncLightSpecular0(7); + break; + + // Fragment lighting specular 1 color + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_1, 0x141 + 0 * 0x10): + SyncLightSpecular1(0); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_1, 0x141 + 1 * 0x10): + SyncLightSpecular1(1); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_1, 0x141 + 2 * 0x10): + SyncLightSpecular1(2); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_1, 0x141 + 3 * 0x10): + SyncLightSpecular1(3); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_1, 0x141 + 4 * 0x10): + SyncLightSpecular1(4); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_1, 0x141 + 5 * 0x10): + SyncLightSpecular1(5); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_1, 0x141 + 6 * 0x10): + SyncLightSpecular1(6); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_1, 0x141 + 7 * 0x10): + SyncLightSpecular1(7); + break; + + // Fragment lighting diffuse color + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].diffuse, 0x142 + 0 * 0x10): + SyncLightDiffuse(0); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].diffuse, 0x142 + 1 * 0x10): + SyncLightDiffuse(1); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].diffuse, 0x142 + 2 * 0x10): + SyncLightDiffuse(2); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].diffuse, 0x142 + 3 * 0x10): + SyncLightDiffuse(3); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].diffuse, 0x142 + 4 * 0x10): + SyncLightDiffuse(4); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].diffuse, 0x142 + 5 * 0x10): + SyncLightDiffuse(5); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].diffuse, 0x142 + 6 * 0x10): + SyncLightDiffuse(6); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].diffuse, 0x142 + 7 * 0x10): + SyncLightDiffuse(7); + break; + + // Fragment lighting ambient color + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].ambient, 0x143 + 0 * 0x10): + SyncLightAmbient(0); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].ambient, 0x143 + 1 * 0x10): + SyncLightAmbient(1); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].ambient, 0x143 + 2 * 0x10): + SyncLightAmbient(2); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].ambient, 0x143 + 3 * 0x10): + SyncLightAmbient(3); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].ambient, 0x143 + 4 * 0x10): + SyncLightAmbient(4); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].ambient, 0x143 + 5 * 0x10): + SyncLightAmbient(5); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].ambient, 0x143 + 6 * 0x10): + SyncLightAmbient(6); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].ambient, 0x143 + 7 * 0x10): + SyncLightAmbient(7); + break; + + // Fragment lighting position + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].x, 0x144 + 0 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[0].z, 0x145 + 0 * 0x10): + SyncLightPosition(0); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].x, 0x144 + 1 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[1].z, 0x145 + 1 * 0x10): + SyncLightPosition(1); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].x, 0x144 + 2 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[2].z, 0x145 + 2 * 0x10): + SyncLightPosition(2); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].x, 0x144 + 3 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[3].z, 0x145 + 3 * 0x10): + SyncLightPosition(3); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].x, 0x144 + 4 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[4].z, 0x145 + 4 * 0x10): + SyncLightPosition(4); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].x, 0x144 + 5 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[5].z, 0x145 + 5 * 0x10): + SyncLightPosition(5); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].x, 0x144 + 6 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[6].z, 0x145 + 6 * 0x10): + SyncLightPosition(6); + break; + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].x, 0x144 + 7 * 0x10): + case PICA_REG_INDEX_WORKAROUND(lighting.light[7].z, 0x145 + 7 * 0x10): + SyncLightPosition(7); + break; + + // Fragment lighting global ambient color (emission + ambient * ambient) + case PICA_REG_INDEX_WORKAROUND(lighting.global_ambient, 0x1c0): + SyncGlobalAmbient(); + break; + + // Fragment lighting lookup tables + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[0], 0x1c8): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[1], 0x1c9): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[2], 0x1ca): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[3], 0x1cb): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[4], 0x1cc): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[5], 0x1cd): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce): + case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf): + { + auto& lut_config = regs.lighting.lut_config; + uniform_block_data.lut_dirty[lut_config.type / 4] = true; + break; + } + } } -void RasterizerOpenGL::NotifyPreRead(PAddr addr, u32 size) { +void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { const auto& regs = Pica::g_state.regs; - if (!Settings::values.use_hw_renderer) - return; + u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format) + * fb_color_texture.width * fb_color_texture.height; - PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress(); - u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format) - * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight(); - - PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress(); - u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format) - * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight(); + u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format) + * fb_depth_texture.width * fb_depth_texture.height; // If source memory region overlaps 3DS framebuffers, commit them before the copy happens - if (MathUtil::IntervalsIntersect(addr, size, cur_fb_color_addr, cur_fb_color_size)) + if (MathUtil::IntervalsIntersect(addr, size, cached_fb_color_addr, cached_fb_color_size)) CommitColorBuffer(); - if (MathUtil::IntervalsIntersect(addr, size, cur_fb_depth_addr, cur_fb_depth_size)) + if (MathUtil::IntervalsIntersect(addr, size, cached_fb_depth_addr, cached_fb_depth_size)) CommitDepthBuffer(); } -void RasterizerOpenGL::NotifyFlush(PAddr addr, u32 size) { +void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) { const auto& regs = Pica::g_state.regs; - if (!Settings::values.use_hw_renderer) - return; - - PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress(); - u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format) - * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight(); + u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format) + * fb_color_texture.width * fb_color_texture.height; - PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress(); - u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format) - * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight(); + u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format) + * fb_depth_texture.width * fb_depth_texture.height; // If modified memory region overlaps 3DS framebuffers, reload their contents into OpenGL - if (MathUtil::IntervalsIntersect(addr, size, cur_fb_color_addr, cur_fb_color_size)) + if (MathUtil::IntervalsIntersect(addr, size, cached_fb_color_addr, cached_fb_color_size)) ReloadColorBuffer(); - if (MathUtil::IntervalsIntersect(addr, size, cur_fb_depth_addr, cur_fb_depth_size)) + if (MathUtil::IntervalsIntersect(addr, size, cached_fb_depth_addr, cached_fb_depth_size)) ReloadDepthBuffer(); // Notify cache of flush in case the region touches a cached resource - res_cache.NotifyFlush(addr, size); + res_cache.InvalidateInRange(addr, size); } void RasterizerOpenGL::SamplerInfo::Create() { @@ -499,27 +699,48 @@ void RasterizerOpenGL::SetShader() { uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]"); if (uniform_tex != -1) { glUniform1i(uniform_tex, 2); } + // Set the texture samplers to correspond to different lookup table texture units + GLuint uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[0]"); + if (uniform_lut != -1) { glUniform1i(uniform_lut, 3); } + uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[1]"); + if (uniform_lut != -1) { glUniform1i(uniform_lut, 4); } + uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[2]"); + if (uniform_lut != -1) { glUniform1i(uniform_lut, 5); } + uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[3]"); + if (uniform_lut != -1) { glUniform1i(uniform_lut, 6); } + uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[4]"); + if (uniform_lut != -1) { glUniform1i(uniform_lut, 7); } + uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]"); + if (uniform_lut != -1) { glUniform1i(uniform_lut, 8); } + current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data"); glUniformBlockBinding(current_shader->shader.handle, block_index, 0); - } - // Update uniforms - SyncAlphaTest(); - SyncCombinerColor(); - auto& tev_stages = Pica::g_state.regs.GetTevStages(); - for (int index = 0; index < tev_stages.size(); ++index) - SyncTevConstColor(index, tev_stages[index]); + // Update uniforms + SyncAlphaTest(); + SyncCombinerColor(); + auto& tev_stages = Pica::g_state.regs.GetTevStages(); + for (int index = 0; index < tev_stages.size(); ++index) + SyncTevConstColor(index, tev_stages[index]); + + SyncGlobalAmbient(); + for (int light_index = 0; light_index < 8; light_index++) { + SyncLightDiffuse(light_index); + SyncLightAmbient(light_index); + SyncLightPosition(light_index); + } + } } void RasterizerOpenGL::SyncFramebuffer() { const auto& regs = Pica::g_state.regs; - PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress(); + PAddr new_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress(); Pica::Regs::ColorFormat new_fb_color_format = regs.framebuffer.color_format; - PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress(); + PAddr new_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress(); Pica::Regs::DepthFormat new_fb_depth_format = regs.framebuffer.depth_format; bool fb_size_changed = fb_color_texture.width != static_cast<GLsizei>(regs.framebuffer.GetWidth()) || @@ -531,10 +752,10 @@ void RasterizerOpenGL::SyncFramebuffer() { bool depth_fb_prop_changed = fb_depth_texture.format != new_fb_depth_format || fb_size_changed; - bool color_fb_modified = last_fb_color_addr != cur_fb_color_addr || + bool color_fb_modified = cached_fb_color_addr != new_fb_color_addr || color_fb_prop_changed; - bool depth_fb_modified = last_fb_depth_addr != cur_fb_depth_addr || + bool depth_fb_modified = cached_fb_depth_addr != new_fb_depth_addr || depth_fb_prop_changed; // Commit if framebuffer modified in any way @@ -574,13 +795,13 @@ void RasterizerOpenGL::SyncFramebuffer() { // Load buffer data again if fb modified in any way if (color_fb_modified) { - last_fb_color_addr = cur_fb_color_addr; + cached_fb_color_addr = new_fb_color_addr; ReloadColorBuffer(); } if (depth_fb_modified) { - last_fb_depth_addr = cur_fb_depth_addr; + cached_fb_depth_addr = new_fb_depth_addr; ReloadDepthBuffer(); } @@ -596,12 +817,12 @@ void RasterizerOpenGL::SyncCullMode() { case Pica::Regs::CullMode::KeepClockWise: state.cull.enabled = true; - state.cull.mode = GL_BACK; + state.cull.front_face = GL_CW; break; case Pica::Regs::CullMode::KeepCounterClockWise: state.cull.enabled = true; - state.cull.mode = GL_FRONT; + state.cull.front_face = GL_CCW; break; default: @@ -611,6 +832,15 @@ void RasterizerOpenGL::SyncCullMode() { } } +void RasterizerOpenGL::SyncDepthModifiers() { + float depth_scale = -Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32(); + float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_far_plane).ToFloat32() / 2.0f; + + // TODO: Implement scale modifier + uniform_block_data.data.depth_offset = depth_offset; + uniform_block_data.dirty = true; +} + void RasterizerOpenGL::SyncBlendEnabled() { state.blend.enabled = (Pica::g_state.regs.output_merger.alphablend_enable == 1); } @@ -657,8 +887,10 @@ void RasterizerOpenGL::SyncStencilTest() { void RasterizerOpenGL::SyncDepthTest() { const auto& regs = Pica::g_state.regs; - state.depth.test_enabled = (regs.output_merger.depth_test_enable == 1); - state.depth.test_func = PicaToGL::CompareFunc(regs.output_merger.depth_test_func); + state.depth.test_enabled = regs.output_merger.depth_test_enable == 1 || + regs.output_merger.depth_write_enable == 1; + state.depth.test_func = regs.output_merger.depth_test_enable == 1 ? + PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS; state.color_mask.red_enabled = regs.output_merger.red_enable; state.color_mask.green_enabled = regs.output_merger.green_enable; state.color_mask.blue_enabled = regs.output_merger.blue_enable; @@ -682,19 +914,87 @@ void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevS } } +void RasterizerOpenGL::SyncGlobalAmbient() { + auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.global_ambient); + if (color != uniform_block_data.data.lighting_global_ambient) { + uniform_block_data.data.lighting_global_ambient = color; + uniform_block_data.dirty = true; + } +} + +void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) { + std::array<GLvec4, 256> new_data; + + for (unsigned offset = 0; offset < new_data.size(); ++offset) { + new_data[offset][0] = Pica::g_state.lighting.luts[(lut_index * 4) + 0][offset].ToFloat(); + new_data[offset][1] = Pica::g_state.lighting.luts[(lut_index * 4) + 1][offset].ToFloat(); + new_data[offset][2] = Pica::g_state.lighting.luts[(lut_index * 4) + 2][offset].ToFloat(); + new_data[offset][3] = Pica::g_state.lighting.luts[(lut_index * 4) + 3][offset].ToFloat(); + } + + if (new_data != lighting_lut_data[lut_index]) { + lighting_lut_data[lut_index] = new_data; + glActiveTexture(GL_TEXTURE3 + lut_index); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, lighting_lut_data[lut_index].data()); + } +} + +void RasterizerOpenGL::SyncLightSpecular0(int light_index) { + auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_0); + if (color != uniform_block_data.data.light_src[light_index].specular_0) { + uniform_block_data.data.light_src[light_index].specular_0 = color; + uniform_block_data.dirty = true; + } +} + +void RasterizerOpenGL::SyncLightSpecular1(int light_index) { + auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_1); + if (color != uniform_block_data.data.light_src[light_index].specular_1) { + uniform_block_data.data.light_src[light_index].specular_1 = color; + uniform_block_data.dirty = true; + } +} + +void RasterizerOpenGL::SyncLightDiffuse(int light_index) { + auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].diffuse); + if (color != uniform_block_data.data.light_src[light_index].diffuse) { + uniform_block_data.data.light_src[light_index].diffuse = color; + uniform_block_data.dirty = true; + } +} + +void RasterizerOpenGL::SyncLightAmbient(int light_index) { + auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].ambient); + if (color != uniform_block_data.data.light_src[light_index].ambient) { + uniform_block_data.data.light_src[light_index].ambient = color; + uniform_block_data.dirty = true; + } +} + +void RasterizerOpenGL::SyncLightPosition(int light_index) { + GLvec3 position = { + Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].x).ToFloat32(), + Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].y).ToFloat32(), + Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32() }; + + if (position != uniform_block_data.data.light_src[light_index].position) { + uniform_block_data.data.light_src[light_index].position = position; + uniform_block_data.dirty = true; + } +} + void RasterizerOpenGL::SyncDrawState() { const auto& regs = Pica::g_state.regs; // Sync the viewport - GLsizei viewport_width = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_x).ToFloat32() * 2; - GLsizei viewport_height = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_y).ToFloat32() * 2; + GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2; + GLsizei viewport_height = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_y).ToFloat32() * 2; // OpenGL uses different y coordinates, so negate corner offset and flip origin // TODO: Ensure viewport_corner.x should not be negated or origin flipped // TODO: Use floating-point viewports for accuracy if supported - glViewport((GLsizei)static_cast<float>(regs.viewport_corner.x), - -(GLsizei)static_cast<float>(regs.viewport_corner.y) - + regs.framebuffer.GetHeight() - viewport_height, + glViewport((GLsizei)regs.viewport_corner.x, + (GLsizei)regs.viewport_corner.y, viewport_width, viewport_height); // Sync bound texture(s), upload if not cached @@ -717,7 +1017,7 @@ void RasterizerOpenGL::SyncDrawState() { MICROPROFILE_DEFINE(OpenGL_FramebufferReload, "OpenGL", "FB Reload", MP_RGB(70, 70, 200)); void RasterizerOpenGL::ReloadColorBuffer() { - u8* color_buffer = Memory::GetPhysicalPointer(Pica::g_state.regs.framebuffer.GetColorBufferPhysicalAddress()); + u8* color_buffer = Memory::GetPhysicalPointer(cached_fb_color_addr); if (color_buffer == nullptr) return; @@ -733,7 +1033,7 @@ void RasterizerOpenGL::ReloadColorBuffer() { for (int x = 0; x < fb_color_texture.width; ++x) { const u32 coarse_y = y & ~7; u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + y * fb_color_texture.width) * bytes_per_pixel; + u32 gl_pixel_index = (x + (fb_color_texture.height - 1 - y) * fb_color_texture.width) * bytes_per_pixel; u8* pixel = color_buffer + dst_offset; memcpy(&temp_fb_color_buffer[gl_pixel_index], pixel, bytes_per_pixel); @@ -752,13 +1052,11 @@ void RasterizerOpenGL::ReloadColorBuffer() { } void RasterizerOpenGL::ReloadDepthBuffer() { - PAddr depth_buffer_addr = Pica::g_state.regs.framebuffer.GetDepthBufferPhysicalAddress(); - - if (depth_buffer_addr == 0) + if (cached_fb_depth_addr == 0) return; // TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil - u8* depth_buffer = Memory::GetPhysicalPointer(depth_buffer_addr); + u8* depth_buffer = Memory::GetPhysicalPointer(cached_fb_depth_addr); if (depth_buffer == nullptr) return; @@ -779,7 +1077,7 @@ void RasterizerOpenGL::ReloadDepthBuffer() { for (int x = 0; x < fb_depth_texture.width; ++x) { const u32 coarse_y = y & ~7; u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + y * fb_depth_texture.width); + u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width); u8* pixel = depth_buffer + dst_offset; u32 depth_stencil = *(u32*)pixel; @@ -791,7 +1089,7 @@ void RasterizerOpenGL::ReloadDepthBuffer() { for (int x = 0; x < fb_depth_texture.width; ++x) { const u32 coarse_y = y & ~7; u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp; + u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width) * gl_bpp; u8* pixel = depth_buffer + dst_offset; memcpy(&temp_fb_depth_data[gl_pixel_index], pixel, bytes_per_pixel); @@ -821,8 +1119,8 @@ Common::Profiling::TimingCategory buffer_commit_category("Framebuffer Commit"); MICROPROFILE_DEFINE(OpenGL_FramebufferCommit, "OpenGL", "FB Commit", MP_RGB(70, 70, 200)); void RasterizerOpenGL::CommitColorBuffer() { - if (last_fb_color_addr != 0) { - u8* color_buffer = Memory::GetPhysicalPointer(last_fb_color_addr); + if (cached_fb_color_addr != 0) { + u8* color_buffer = Memory::GetPhysicalPointer(cached_fb_color_addr); if (color_buffer != nullptr) { Common::Profiling::ScopeTimer timer(buffer_commit_category); @@ -846,7 +1144,7 @@ void RasterizerOpenGL::CommitColorBuffer() { for (int x = 0; x < fb_color_texture.width; ++x) { const u32 coarse_y = y & ~7; u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; - u32 gl_pixel_index = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel; + u32 gl_pixel_index = x * bytes_per_pixel + (fb_color_texture.height - 1 - y) * fb_color_texture.width * bytes_per_pixel; u8* pixel = color_buffer + dst_offset; memcpy(pixel, &temp_gl_color_buffer[gl_pixel_index], bytes_per_pixel); @@ -857,9 +1155,9 @@ void RasterizerOpenGL::CommitColorBuffer() { } void RasterizerOpenGL::CommitDepthBuffer() { - if (last_fb_depth_addr != 0) { + if (cached_fb_depth_addr != 0) { // TODO: Output seems correct visually, but doesn't quite match sw renderer output. One of them is wrong. - u8* depth_buffer = Memory::GetPhysicalPointer(last_fb_depth_addr); + u8* depth_buffer = Memory::GetPhysicalPointer(cached_fb_depth_addr); if (depth_buffer != nullptr) { Common::Profiling::ScopeTimer timer(buffer_commit_category); @@ -888,7 +1186,7 @@ void RasterizerOpenGL::CommitDepthBuffer() { for (int x = 0; x < fb_depth_texture.width; ++x) { const u32 coarse_y = y & ~7; u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + y * fb_depth_texture.width); + u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width); u8* pixel = depth_buffer + dst_offset; u32 depth_stencil = ((u32*)temp_gl_depth_data)[gl_pixel_index]; @@ -900,7 +1198,7 @@ void RasterizerOpenGL::CommitDepthBuffer() { for (int x = 0; x < fb_depth_texture.width; ++x) { const u32 coarse_y = y & ~7; u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp; + u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width) * gl_bpp; u8* pixel = depth_buffer + dst_offset; memcpy(pixel, &temp_gl_depth_data[gl_pixel_index], bytes_per_pixel); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 5ba898189..fc85aa3ff 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -14,9 +14,11 @@ #include "common/hash.h" #include "video_core/pica.h" -#include "video_core/hwrasterizer_base.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" +#include "video_core/renderer_opengl/pica_to_gl.h" #include "video_core/shader/shader_interpreter.h" /** @@ -71,6 +73,59 @@ struct PicaShaderConfig { regs.tev_combiner_buffer_input.update_mask_rgb.Value() | regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; + // Fragment lighting + + res.lighting.enable = !regs.lighting.disable; + res.lighting.src_num = regs.lighting.num_lights + 1; + + for (unsigned light_index = 0; light_index < res.lighting.src_num; ++light_index) { + unsigned num = regs.lighting.light_enable.GetNum(light_index); + const auto& light = regs.lighting.light[num]; + res.lighting.light[light_index].num = num; + res.lighting.light[light_index].directional = light.directional != 0; + res.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0; + res.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num); + res.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32(); + res.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32(); + } + + res.lighting.lut_d0.enable = regs.lighting.disable_lut_d0 == 0; + res.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0; + res.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value(); + res.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0); + + res.lighting.lut_d1.enable = regs.lighting.disable_lut_d1 == 0; + res.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0; + res.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value(); + res.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); + + res.lighting.lut_fr.enable = regs.lighting.disable_lut_fr == 0; + res.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0; + res.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value(); + res.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr); + + res.lighting.lut_rr.enable = regs.lighting.disable_lut_rr == 0; + res.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0; + res.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value(); + res.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr); + + res.lighting.lut_rg.enable = regs.lighting.disable_lut_rg == 0; + res.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0; + res.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value(); + res.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg); + + res.lighting.lut_rb.enable = regs.lighting.disable_lut_rb == 0; + res.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0; + res.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value(); + res.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb); + + res.lighting.config = regs.lighting.config; + res.lighting.fresnel_selector = regs.lighting.fresnel_selector; + res.lighting.bump_mode = regs.lighting.bump_mode; + res.lighting.bump_selector = regs.lighting.bump_selector; + res.lighting.bump_renorm = regs.lighting.disable_bump_renorm == 0; + res.lighting.clamp_highlights = regs.lighting.clamp_highlights != 0; + return res; } @@ -86,9 +141,37 @@ struct PicaShaderConfig { return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0; }; - Pica::Regs::CompareFunc alpha_test_func; + Pica::Regs::CompareFunc alpha_test_func = Pica::Regs::CompareFunc::Never; std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {}; - u8 combiner_buffer_input; + u8 combiner_buffer_input = 0; + + struct { + struct { + unsigned num = 0; + bool directional = false; + bool two_sided_diffuse = false; + bool dist_atten_enable = false; + GLfloat dist_atten_scale = 0.0f; + GLfloat dist_atten_bias = 0.0f; + } light[8]; + + bool enable = false; + unsigned src_num = 0; + Pica::Regs::LightingBumpMode bump_mode = Pica::Regs::LightingBumpMode::None; + unsigned bump_selector = 0; + bool bump_renorm = false; + bool clamp_highlights = false; + + Pica::Regs::LightingConfig config = Pica::Regs::LightingConfig::Config0; + Pica::Regs::LightingFresnelSelector fresnel_selector = Pica::Regs::LightingFresnelSelector::None; + + struct { + bool enable = false; + bool abs_input = false; + Pica::Regs::LightingLutInput type = Pica::Regs::LightingLutInput::NH; + float scale = 1.0f; + } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb; + } lighting; }; namespace std { @@ -102,37 +185,22 @@ struct hash<PicaShaderConfig> { } // namespace std -class RasterizerOpenGL : public HWRasterizer { +class RasterizerOpenGL : public VideoCore::RasterizerInterface { public: RasterizerOpenGL(); ~RasterizerOpenGL() override; - /// Initialize API-specific GPU objects void InitObjects() override; - - /// Reset the rasterizer, such as flushing all caches and updating all state void Reset() override; - - /// Queues the primitive formed by the given vertices for rendering void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, const Pica::Shader::OutputVertex& v2) override; - - /// Draw the current batch of triangles void DrawTriangles() override; - - /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer - void CommitFramebuffer() override; - - /// Notify rasterizer that the specified PICA register has been changed + void FlushFramebuffer() override; void NotifyPicaRegisterChanged(u32 id) override; - - /// Notify rasterizer that the specified 3DS memory region will be read from after this notification - void NotifyPreRead(PAddr addr, u32 size) override; - - /// Notify rasterizer that a 3DS memory region has been changed - void NotifyFlush(PAddr addr, u32 size) override; + void FlushRegion(PAddr addr, u32 size) override; + void InvalidateRegion(PAddr addr, u32 size) override; /// OpenGL shader generated for a given Pica register state struct PicaShader { @@ -182,7 +250,7 @@ private: /// Structure that the hardware rendered vertices are composed of struct HardwareVertex { - HardwareVertex(const Pica::Shader::OutputVertex& v) { + HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) { position[0] = v.pos.x.ToFloat32(); position[1] = v.pos.y.ToFloat32(); position[2] = v.pos.z.ToFloat32(); @@ -197,6 +265,19 @@ private: tex_coord1[1] = v.tc1.y.ToFloat32(); tex_coord2[0] = v.tc2.x.ToFloat32(); tex_coord2[1] = v.tc2.y.ToFloat32(); + normquat[0] = v.quat.x.ToFloat32(); + normquat[1] = v.quat.y.ToFloat32(); + normquat[2] = v.quat.z.ToFloat32(); + normquat[3] = v.quat.w.ToFloat32(); + view[0] = v.view.x.ToFloat32(); + view[1] = v.view.y.ToFloat32(); + view[2] = v.view.z.ToFloat32(); + + if (flip_quaternion) { + for (float& x : normquat) { + x = -x; + } + } } GLfloat position[4]; @@ -204,19 +285,31 @@ private: GLfloat tex_coord0[2]; GLfloat tex_coord1[2]; GLfloat tex_coord2[2]; + GLfloat normquat[4]; + GLfloat view[3]; + }; + + struct LightSrc { + alignas(16) GLvec3 specular_0; + alignas(16) GLvec3 specular_1; + alignas(16) GLvec3 diffuse; + alignas(16) GLvec3 ambient; + alignas(16) GLvec3 position; }; /// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned struct UniformData { // A vec4 color for each of the six tev stages - std::array<GLfloat, 4> const_color[6]; - std::array<GLfloat, 4> tev_combiner_buffer_color; + GLvec4 const_color[6]; + GLvec4 tev_combiner_buffer_color; GLint alphatest_ref; - INSERT_PADDING_BYTES(12); + GLfloat depth_offset; + alignas(16) GLvec3 lighting_global_ambient; + LightSrc light_src[8]; }; - static_assert(sizeof(UniformData) == 0x80, "The size of the UniformData structure has changed, update the structure in the shader"); - static_assert(sizeof(UniformData) < 16000, "UniformData structure must be less than 16kb as per the OpenGL spec"); + static_assert(sizeof(UniformData) == 0x310, "The size of the UniformData structure has changed, update the structure in the shader"); + static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); /// Reconfigure the OpenGL color texture to use the given format and dimensions void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height); @@ -233,6 +326,9 @@ private: /// Syncs the cull mode to match the PICA register void SyncCullMode(); + /// Syncs the depth scale and offset to match the PICA registers + void SyncDepthModifiers(); + /// Syncs the blend enabled status to match the PICA register void SyncBlendEnabled(); @@ -260,6 +356,27 @@ private: /// Syncs the TEV combiner color buffer to match the PICA register void SyncCombinerColor(); + /// Syncs the lighting global ambient color to match the PICA register + void SyncGlobalAmbient(); + + /// Syncs the lighting lookup tables + void SyncLightingLUT(unsigned index); + + /// Syncs the specified light's diffuse color to match the PICA register + void SyncLightDiffuse(int light_index); + + /// Syncs the specified light's ambient color to match the PICA register + void SyncLightAmbient(int light_index); + + /// Syncs the specified light's position to match the PICA register + void SyncLightPosition(int light_index); + + /// Syncs the specified light's specular 0 color to match the PICA register + void SyncLightSpecular0(int light_index); + + /// Syncs the specified light's specular 1 color to match the PICA register + void SyncLightSpecular1(int light_index); + /// Syncs the remaining OpenGL drawing state to match the current PICA state void SyncDrawState(); @@ -289,8 +406,8 @@ private: OpenGLState state; - PAddr last_fb_color_addr; - PAddr last_fb_depth_addr; + PAddr cached_fb_color_addr; + PAddr cached_fb_depth_addr; // Hardware rasterizer std::array<SamplerInfo, 3> texture_samplers; @@ -302,6 +419,7 @@ private: struct { UniformData data; + bool lut_dirty[6]; bool dirty; } uniform_block_data; @@ -309,4 +427,7 @@ private: OGLBuffer vertex_buffer; OGLBuffer uniform_buffer; OGLFramebuffer framebuffer; + + std::array<OGLTexture, 6> lighting_lut; + std::array<std::array<GLvec4, 256>, 6> lighting_lut_data; }; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 10d4ab0b6..a9ad46fe0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -15,7 +15,7 @@ #include "video_core/renderer_opengl/pica_to_gl.h" RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { - FullFlush(); + InvalidateAll(); } MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); @@ -58,8 +58,7 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text } } -void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash) { - // Flush any texture that falls in the flushed region +void RasterizerCacheOpenGL::InvalidateInRange(PAddr addr, u32 size, bool ignore_hash) { // TODO: Optimize by also inserting upper bound (addr + size) of each texture into the same map and also narrow using lower_bound auto cache_upper_bound = texture_cache.upper_bound(addr + size); @@ -77,6 +76,6 @@ void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash) } } -void RasterizerCacheOpenGL::FullFlush() { +void RasterizerCacheOpenGL::InvalidateAll() { texture_cache.clear(); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 98a48ffbe..b69651427 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -23,11 +23,11 @@ public: LoadAndBindTexture(state, texture_unit, Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format)); } - /// Flush any cached resource that touches the flushed region - void NotifyFlush(PAddr addr, u32 size, bool ignore_hash = false); + /// Invalidate any cached resource intersecting the specified region. + void InvalidateInRange(PAddr addr, u32 size, bool ignore_hash = false); - /// Flush all cached OpenGL resources tracked by this cache manager - void FullFlush(); + /// Invalidate all cached OpenGL resources tracked by this cache manager + void InvalidateAll(); private: struct CachedTexture { diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 498c506e7..ee4b54ab9 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -32,12 +32,10 @@ static void AppendSource(std::string& out, TevStageConfig::Source source, out += "primary_color"; break; case Source::PrimaryFragmentColor: - // HACK: Until we implement fragment lighting, use primary_color - out += "primary_color"; + out += "primary_fragment_color"; break; case Source::SecondaryFragmentColor: - // HACK: Until we implement fragment lighting, use zero - out += "vec4(0.0)"; + out += "secondary_fragment_color"; break; case Source::Texture0: out += "texture(tex[0], texcoord[0])"; @@ -320,25 +318,229 @@ static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsi out += "next_combiner_buffer.a = last_tex_env_out.a;\n"; } +/// Writes the code to emulate fragment lighting +static void WriteLighting(std::string& out, const PicaShaderConfig& config) { + // Define lighting globals + out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" + "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" + "vec3 light_vector = vec3(0.0);\n" + "vec3 refl_value = vec3(0.0);\n"; + + // Compute fragment normals + if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) { + // Bump mapping is enabled using a normal map, read perturbation vector from the selected texture + std::string bump_selector = std::to_string(config.lighting.bump_selector); + out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], texcoord[" + bump_selector + "]).rgb - 1.0;\n"; + + // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher precision result + if (config.lighting.bump_renorm) { + std::string val = "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; + out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; + } + } else if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) { + // Bump mapping is enabled using a tangent map + LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)"); + UNIMPLEMENTED(); + } else { + // No bump mapping - surface local normal is just a unit normal + out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; + } + + // Rotate the surface-local normal by the interpolated normal quaternion to convert it to eyespace + out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n"; + + // Gets the index into the specified lookup table for specular lighting + auto GetLutIndex = [config](unsigned light_num, Regs::LightingLutInput input, bool abs) { + const std::string half_angle = "normalize(normalize(view) + light_vector)"; + std::string index; + switch (input) { + case Regs::LightingLutInput::NH: + index = "dot(normal, " + half_angle + ")"; + break; + + case Regs::LightingLutInput::VH: + index = std::string("dot(normalize(view), " + half_angle + ")"); + break; + + case Regs::LightingLutInput::NV: + index = std::string("dot(normal, normalize(view))"); + break; + + case Regs::LightingLutInput::LN: + index = std::string("dot(light_vector, normal)"); + break; + + default: + LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); + UNIMPLEMENTED(); + break; + } + + if (abs) { + // LUT index is in the range of (0.0, 1.0) + index = config.lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)"; + return "(FLOAT_255 * clamp(" + index + ", 0.0, 1.0))"; + } else { + // LUT index is in the range of (-1.0, 1.0) + index = "clamp(" + index + ", -1.0, 1.0)"; + return "(FLOAT_255 * ((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0)"; + } + + return std::string(); + }; + + // Gets the lighting lookup table value given the specified sampler and index + auto GetLutValue = [](Regs::LightingSampler sampler, std::string lut_index) { + return std::string("texture(lut[" + std::to_string((unsigned)sampler / 4) + "], " + + lut_index + ")[" + std::to_string((unsigned)sampler & 3) + "]"); + }; + + // Write the code to emulate each enabled light + for (unsigned light_index = 0; light_index < config.lighting.src_num; ++light_index) { + const auto& light_config = config.lighting.light[light_index]; + std::string light_src = "light_src[" + std::to_string(light_config.num) + "]"; + + // Compute light vector (directional or positional) + if (light_config.directional) + out += "light_vector = normalize(" + light_src + ".position);\n"; + else + out += "light_vector = normalize(" + light_src + ".position + view);\n"; + + // Compute dot product of light_vector and normal, adjust if lighting is one-sided or two-sided + std::string dot_product = light_config.two_sided_diffuse ? "abs(dot(light_vector, normal))" : "max(dot(light_vector, normal), 0.0)"; + + // If enabled, compute distance attenuation value + std::string dist_atten = "1.0"; + if (light_config.dist_atten_enable) { + std::string scale = std::to_string(light_config.dist_atten_scale); + std::string bias = std::to_string(light_config.dist_atten_bias); + std::string index = "(" + scale + " * length(-view - " + light_src + ".position) + " + bias + ")"; + index = "((clamp(" + index + ", 0.0, FLOAT_255)))"; + const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + light_config.num); + dist_atten = GetLutValue((Regs::LightingSampler)lut_num, index); + } + + // If enabled, clamp specular component if lighting result is negative + std::string clamp_highlights = config.lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; + + // Specular 0 component + std::string d0_lut_value = "1.0"; + if (config.lighting.lut_d0.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution0)) { + // Lookup specular "distribution 0" LUT value + std::string index = GetLutIndex(light_config.num, config.lighting.lut_d0.type, config.lighting.lut_d0.abs_input); + d0_lut_value = "(" + std::to_string(config.lighting.lut_d0.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution0, index) + ")"; + } + std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; + + // If enabled, lookup ReflectRed value, otherwise, 1.0 is used + if (config.lighting.lut_rr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectRed)) { + std::string index = GetLutIndex(light_config.num, config.lighting.lut_rr.type, config.lighting.lut_rr.abs_input); + std::string value = "(" + std::to_string(config.lighting.lut_rr.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")"; + out += "refl_value.r = " + value + ";\n"; + } else { + out += "refl_value.r = 1.0;\n"; + } + + // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used + if (config.lighting.lut_rg.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectGreen)) { + std::string index = GetLutIndex(light_config.num, config.lighting.lut_rg.type, config.lighting.lut_rg.abs_input); + std::string value = "(" + std::to_string(config.lighting.lut_rg.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")"; + out += "refl_value.g = " + value + ";\n"; + } else { + out += "refl_value.g = refl_value.r;\n"; + } + + // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used + if (config.lighting.lut_rb.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectBlue)) { + std::string index = GetLutIndex(light_config.num, config.lighting.lut_rb.type, config.lighting.lut_rb.abs_input); + std::string value = "(" + std::to_string(config.lighting.lut_rb.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")"; + out += "refl_value.b = " + value + ";\n"; + } else { + out += "refl_value.b = refl_value.r;\n"; + } + + // Specular 1 component + std::string d1_lut_value = "1.0"; + if (config.lighting.lut_d1.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution1)) { + // Lookup specular "distribution 1" LUT value + std::string index = GetLutIndex(light_config.num, config.lighting.lut_d1.type, config.lighting.lut_d1.abs_input); + d1_lut_value = "(" + std::to_string(config.lighting.lut_d1.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution1, index) + ")"; + } + std::string specular_1 = "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; + + // Fresnel + if (config.lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Fresnel)) { + // Lookup fresnel LUT value + std::string index = GetLutIndex(light_config.num, config.lighting.lut_fr.type, config.lighting.lut_fr.abs_input); + std::string value = "(" + std::to_string(config.lighting.lut_fr.scale) + " * " + GetLutValue(Regs::LightingSampler::Fresnel, index) + ")"; + + // Enabled for difffuse lighting alpha component + if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha || + config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both) + out += "diffuse_sum.a *= " + value + ";\n"; + + // Enabled for the specular lighting alpha component + if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha || + config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both) + out += "specular_sum.a *= " + value + ";\n"; + } + + // Compute primary fragment color (diffuse lighting) function + out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * " + dot_product + ") + " + light_src + ".ambient) * " + dist_atten + ";\n"; + + // Compute secondary fragment color (specular lighting) function + out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + ") * " + clamp_highlights + " * " + dist_atten + ";\n"; + } + + // Sum final lighting result + out += "diffuse_sum.rgb += lighting_global_ambient;\n"; + out += "primary_fragment_color = clamp(diffuse_sum, vec4(0.0), vec4(1.0));\n"; + out += "secondary_fragment_color = clamp(specular_sum, vec4(0.0), vec4(1.0));\n"; +} + std::string GenerateFragmentShader(const PicaShaderConfig& config) { std::string out = R"( #version 330 core #define NUM_TEV_STAGES 6 +#define NUM_LIGHTS 8 +#define LIGHTING_LUT_SIZE 256 +#define FLOAT_255 (255.0 / 256.0) in vec4 primary_color; in vec2 texcoord[3]; +in vec4 normquat; +in vec3 view; out vec4 color; +struct LightSrc { + vec3 specular_0; + vec3 specular_1; + vec3 diffuse; + vec3 ambient; + vec3 position; +}; + layout (std140) uniform shader_data { vec4 const_color[NUM_TEV_STAGES]; vec4 tev_combiner_buffer_color; int alphatest_ref; + float depth_offset; + vec3 lighting_global_ambient; + LightSrc light_src[NUM_LIGHTS]; }; uniform sampler2D tex[3]; +uniform sampler1D lut[6]; + +// Rotate the vector v by the quaternion q +vec3 quaternion_rotate(vec4 q, vec3 v) { + return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); +} void main() { +vec4 primary_fragment_color = vec4(0.0); +vec4 secondary_fragment_color = vec4(0.0); )"; // Do not do any sort of processing if it's obvious we're not going to pass the alpha test @@ -347,6 +549,9 @@ void main() { return out; } + if (config.lighting.enable) + WriteLighting(out, config); + out += "vec4 combiner_buffer = vec4(0.0);\n"; out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n"; out += "vec4 last_tex_env_out = vec4(0.0);\n"; @@ -360,29 +565,37 @@ void main() { out += ") discard;\n"; } - out += "color = last_tex_env_out;\n}"; + out += "color = last_tex_env_out;\n"; + out += "gl_FragDepth = gl_FragCoord.z + depth_offset;\n}"; return out; } std::string GenerateVertexShader() { std::string out = "#version 330 core\n"; + out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n"; out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n"; out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n"; out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n"; out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n"; + out += "layout(location = " + std::to_string((int)ATTRIBUTE_NORMQUAT) + ") in vec4 vert_normquat;\n"; + out += "layout(location = " + std::to_string((int)ATTRIBUTE_VIEW) + ") in vec3 vert_view;\n"; out += R"( out vec4 primary_color; out vec2 texcoord[3]; +out vec4 normquat; +out vec3 view; void main() { primary_color = vert_color; texcoord[0] = vert_texcoord0; texcoord[1] = vert_texcoord1; texcoord[2] = vert_texcoord2; - gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); + normquat = vert_normquat; + view = vert_view; + gl_Position = vec4(vert_position.x, vert_position.y, -vert_position.z, vert_position.w); } )"; diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 046aae14f..097242f6f 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -14,6 +14,8 @@ enum Attributes { ATTRIBUTE_TEXCOORD0, ATTRIBUTE_TEXCOORD1, ATTRIBUTE_TEXCOORD2, + ATTRIBUTE_NORMQUAT, + ATTRIBUTE_VIEW, }; /** diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index c44497fc3..08e4d0b54 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -11,6 +11,7 @@ OpenGLState::OpenGLState() { // These all match default OpenGL values cull.enabled = false; cull.mode = GL_BACK; + cull.front_face = GL_CCW; depth.test_enabled = false; depth.test_func = GL_LESS; @@ -47,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; @@ -67,6 +72,10 @@ void OpenGLState::Apply() { glCullFace(cull.mode); } + if (cull.front_face != cur_state.cull.front_face) { + glFrontFace(cull.front_face); + } + // Depth test if (depth.test_enabled != cur_state.depth.test_enabled) { if (depth.test_enabled) { @@ -165,6 +174,14 @@ void OpenGLState::Apply() { } } + // Lighting LUTs + for (unsigned i = 0; i < ARRAY_SIZE(lighting_lut); ++i) { + if (lighting_lut[i].texture_1d != cur_state.lighting_lut[i].texture_1d) { + glActiveTexture(GL_TEXTURE3 + i); + glBindTexture(GL_TEXTURE_1D, lighting_lut[i].texture_1d); + } + } + // Framebuffer if (draw.framebuffer != cur_state.draw.framebuffer) { glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer); diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 84b3d49bc..e848058d7 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -11,6 +11,7 @@ public: struct { bool enabled; // GL_CULL_FACE GLenum mode; // GL_CULL_FACE_MODE + GLenum front_face; // GL_FRONT_FACE } cull; struct { @@ -61,6 +62,10 @@ public: } texture_units[3]; struct { + GLuint texture_1d; // GL_TEXTURE_BINDING_1D + } lighting_lut[6]; + + struct { GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 04c1d1a34..3d6c4e9e5 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -10,6 +10,9 @@ #include "video_core/pica.h" +using GLvec3 = std::array<GLfloat, 3>; +using GLvec4 = std::array<GLfloat, 4>; + namespace PicaToGL { inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) { @@ -175,7 +178,7 @@ inline GLenum StencilOp(Pica::Regs::StencilAction action) { return stencil_op_table[(unsigned)action]; } -inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) { +inline GLvec4 ColorRGBA8(const u32 color) { return { { (color >> 0 & 0xFF) / 255.0f, (color >> 8 & 0xFF) / 255.0f, (color >> 16 & 0xFF) / 255.0f, @@ -183,4 +186,11 @@ inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) { } }; } +inline std::array<GLfloat, 3> LightColor(const Pica::Regs::LightColor& color) { + return { { color.r / 255.0f, + color.g / 255.0f, + color.b / 255.0f + } }; +} + } // namespace diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 1420229cc..11c4d0daf 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -81,8 +81,8 @@ struct ScreenRectVertex { * The projection part of the matrix is trivial, hence these operations are represented * by a 3x2 matrix. */ -static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const float height) { - std::array<GLfloat, 3*2> matrix; +static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, const float height) { + std::array<GLfloat, 3 * 2> matrix; matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; @@ -93,7 +93,6 @@ static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const /// RendererOpenGL constructor RendererOpenGL::RendererOpenGL() { - hw_rasterizer.reset(new RasterizerOpenGL()); resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth); resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight; } @@ -157,15 +156,7 @@ void RendererOpenGL::SwapBuffers() { profiler.BeginFrame(); - bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled; - if (Settings::values.use_hw_renderer != hw_renderer_enabled) { - // TODO: Save new setting value to config file for next startup - Settings::values.use_hw_renderer = hw_renderer_enabled; - - if (Settings::values.use_hw_renderer) { - hw_rasterizer->Reset(); - } - } + RefreshRasterizerSetting(); if (Pica::g_debug_context && Pica::g_debug_context->recorder) { Pica::g_debug_context->recorder->FrameFinished(); @@ -286,8 +277,6 @@ void RendererOpenGL::InitOpenGLObjects() { state.texture_units[0].texture_2d = 0; state.Apply(); - - hw_rasterizer->InitObjects(); } void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, @@ -405,8 +394,58 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { render_window = window; } +static const char* GetSource(GLenum source) { +#define RET(s) case GL_DEBUG_SOURCE_##s: return #s + switch (source) { + RET(API); + RET(WINDOW_SYSTEM); + RET(SHADER_COMPILER); + RET(THIRD_PARTY); + RET(APPLICATION); + RET(OTHER); + default: + UNREACHABLE(); + } +#undef RET +} + +static const char* GetType(GLenum type) { +#define RET(t) case GL_DEBUG_TYPE_##t: return #t + switch (type) { + RET(ERROR); + RET(DEPRECATED_BEHAVIOR); + RET(UNDEFINED_BEHAVIOR); + RET(PORTABILITY); + RET(PERFORMANCE); + RET(OTHER); + RET(MARKER); + default: + UNREACHABLE(); + } +#undef RET +} + +static void DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, + const GLchar* message, const void* user_param) { + Log::Level level; + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: + level = Log::Level::Error; + break; + case GL_DEBUG_SEVERITY_MEDIUM: + level = Log::Level::Warning; + break; + case GL_DEBUG_SEVERITY_NOTIFICATION: + case GL_DEBUG_SEVERITY_LOW: + level = Log::Level::Debug; + break; + } + LOG_GENERIC(Log::Class::Render_OpenGL, level, "%s %s %d: %s", + GetSource(source), GetType(type), id, message); +} + /// Initialize the renderer -void RendererOpenGL::Init() { +bool RendererOpenGL::Init() { render_window->MakeCurrent(); // TODO: Make frontends initialize this, so they can use gladLoadGLLoader with their own loaders @@ -415,10 +454,23 @@ void RendererOpenGL::Init() { exit(-1); } + if (GLAD_GL_KHR_debug) { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(DebugHandler, nullptr); + } + LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION)); LOG_INFO(Render_OpenGL, "GL_VENDOR: %s", glGetString(GL_VENDOR)); LOG_INFO(Render_OpenGL, "GL_RENDERER: %s", glGetString(GL_RENDERER)); + if (!GLAD_GL_VERSION_3_3) { + return false; + } + InitOpenGLObjects(); + + RefreshRasterizerSetting(); + + return true; } /// Shutdown the renderer diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index b42df7654..fe4d142a5 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -31,7 +31,7 @@ public: void SetWindow(EmuWindow* window) override; /// Initialize the renderer - void Init() override; + bool Init() override; /// Shutdown the renderer void ShutDown() override; diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index 59f54236b..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" @@ -134,16 +135,18 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f)); } - LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), quat (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)", + LOG_TRACE(Render_Software, "Output vertex: pos(%.2f, %.2f, %.2f, %.2f), quat(%.2f, %.2f, %.2f, %.2f), " + "col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f), view(%.2f, %.2f, %.2f)", ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(), ret.quat.x.ToFloat32(), ret.quat.y.ToFloat32(), ret.quat.z.ToFloat32(), ret.quat.w.ToFloat32(), ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(), - ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32()); + ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32(), + ret.view.x.ToFloat32(), ret.view.y.ToFloat32(), ret.view.z.ToFloat32()); 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 1c6fa592c..7af8f1fa1 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h @@ -37,17 +37,19 @@ struct OutputVertex { Math::Vec4<float24> color; Math::Vec2<float24> tc0; Math::Vec2<float24> tc1; - float24 pad[6]; + INSERT_PADDING_WORDS(2); + Math::Vec3<float24> view; + INSERT_PADDING_WORDS(1); Math::Vec2<float24> tc2; // Padding for optimal alignment - float24 pad2[4]; + INSERT_PADDING_WORDS(4); // Attributes used to store intermediate results // position after perspective divide Math::Vec3<float24> screenpos; - float24 pad3; + INSERT_PADDING_WORDS(1); // Linear interpolation // factor: 0=this, 1=vtx @@ -75,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. + alignas(16) Math::Vec4<float24> 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> @@ -258,9 +276,9 @@ struct UnitState { struct Registers { // The registers are accessed by the shader JIT using SSE instructions, and are therefore // required to be 16-byte aligned. - Math::Vec4<float24> MEMORY_ALIGNED16(input[16]); - Math::Vec4<float24> MEMORY_ALIGNED16(output[16]); - Math::Vec4<float24> MEMORY_ALIGNED16(temporary[16]); + alignas(16) Math::Vec4<float24> input[16]; + alignas(16) Math::Vec4<float24> output[16]; + alignas(16) Math::Vec4<float24> temporary[16]; } registers; static_assert(std::is_pod<Registers>::value, "Structure is not POD"); @@ -345,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 7b0c20b74..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" @@ -515,7 +516,8 @@ void RunInterpreter(UnitState<Debug>& state) { case OpCode::Id::JMPU: Record<DebugDataRecord::COND_BOOL_IN>(state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); - if (uniforms.b[instr.flow_control.bool_uniform_id]) { + + if (uniforms.b[instr.flow_control.bool_uniform_id] == !(instr.flow_control.num_instructions & 1)) { state.program_counter = instr.flow_control.dest_offset - 1; } break; diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index 00415e402..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 { @@ -653,7 +655,7 @@ void JitCompiler::Compile_IF(Instruction instr) { FixupBranch b = J_CC(CC_Z, true); // Compile the code that corresponds to the condition evaluating as true - Compile_Block(instr.flow_control.dest_offset - 1); + Compile_Block(instr.flow_control.dest_offset); // If there isn't an "ELSE" condition, we are done here if (instr.flow_control.num_instructions == 0) { @@ -667,7 +669,7 @@ void JitCompiler::Compile_IF(Instruction instr) { // This code corresponds to the "ELSE" condition // Comple the code that corresponds to the condition evaluating as false - Compile_Block(instr.flow_control.dest_offset + instr.flow_control.num_instructions - 1); + Compile_Block(instr.flow_control.dest_offset + instr.flow_control.num_instructions); SetJumpTarget(b2); } @@ -691,7 +693,7 @@ void JitCompiler::Compile_LOOP(Instruction instr) { auto loop_start = GetCodePtr(); - Compile_Block(instr.flow_control.dest_offset); + Compile_Block(instr.flow_control.dest_offset + 1); ADD(32, R(LOOPCOUNT_REG), R(LOOPINC)); // Increment LOOPCOUNT_REG by Z-component SUB(32, R(LOOPCOUNT), Imm8(1)); // Increment loop count by 1 @@ -710,19 +712,21 @@ void JitCompiler::Compile_JMP(Instruction instr) { else UNREACHABLE(); - FixupBranch b = J_CC(CC_NZ, true); + bool inverted_condition = (instr.opcode.Value() == OpCode::Id::JMPU) && + (instr.flow_control.num_instructions & 1); + FixupBranch b = J_CC(inverted_condition ? CC_Z : CC_NZ, true); Compile_Block(instr.flow_control.dest_offset); SetJumpTarget(b); } -void JitCompiler::Compile_Block(unsigned stop) { +void JitCompiler::Compile_Block(unsigned end) { // Save current offset pointer unsigned* prev_offset_ptr = offset_ptr; unsigned offset = *prev_offset_ptr; - while (offset <= stop) + while (offset < end) Compile_NextInstr(&offset); // Restore current offset pointer diff --git a/src/video_core/shader/shader_jit_x64.h b/src/video_core/shader/shader_jit_x64.h index 3afbceccf..5ad2d9606 100644 --- a/src/video_core/shader/shader_jit_x64.h +++ b/src/video_core/shader/shader_jit_x64.h @@ -61,7 +61,7 @@ public: void Compile_MAD(Instruction instr); private: - void Compile_Block(unsigned stop); + void Compile_Block(unsigned end); void Compile_NextInstr(unsigned* offset); void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, Gen::X64Reg dest); diff --git a/src/video_core/swrasterizer.cpp b/src/video_core/swrasterizer.cpp new file mode 100644 index 000000000..03df15b01 --- /dev/null +++ b/src/video_core/swrasterizer.cpp @@ -0,0 +1,16 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/clipper.h" +#include "video_core/swrasterizer.h" + +namespace VideoCore { + +void SWRasterizer::AddTriangle(const Pica::Shader::OutputVertex& v0, + const Pica::Shader::OutputVertex& v1, + const Pica::Shader::OutputVertex& v2) { + Pica::Clipper::ProcessTriangle(v0, v1, v2); +} + +} diff --git a/src/video_core/swrasterizer.h b/src/video_core/swrasterizer.h new file mode 100644 index 000000000..9a9a76d7a --- /dev/null +++ b/src/video_core/swrasterizer.h @@ -0,0 +1,26 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#include "video_core/rasterizer_interface.h" + +namespace VideoCore { + +class SWRasterizer : public RasterizerInterface { + void InitObjects() override {} + void Reset() override {} + void AddTriangle(const Pica::Shader::OutputVertex& v0, + const Pica::Shader::OutputVertex& v1, + const Pica::Shader::OutputVertex& v2) override; + void DrawTriangles() override {} + void FlushFramebuffer() override {} + void NotifyPicaRegisterChanged(u32 id) override {} + void FlushRegion(PAddr addr, u32 size) override {} + void InvalidateRegion(PAddr addr, u32 size) override {} +}; + +} diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index eaddda668..ee5e50df1 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -2,7 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> + #include "common/emu_window.h" +#include "common/make_unique.h" #include "common/logging/log.h" #include "core/core.h" @@ -18,29 +21,33 @@ namespace VideoCore { -EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window -RendererBase* g_renderer = nullptr; ///< Renderer plugin +EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window +std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin std::atomic<bool> g_hw_renderer_enabled; std::atomic<bool> g_shader_jit_enabled; /// Initialize the video core -void Init(EmuWindow* emu_window) { +bool Init(EmuWindow* emu_window) { Pica::Init(); g_emu_window = emu_window; - g_renderer = new RendererOpenGL(); + g_renderer = Common::make_unique<RendererOpenGL>(); g_renderer->SetWindow(g_emu_window); - g_renderer->Init(); - - LOG_DEBUG(Render, "initialized OK"); + if (g_renderer->Init()) { + LOG_DEBUG(Render, "initialized OK"); + } else { + LOG_ERROR(Render, "initialization failed !"); + return false; + } + return true; } /// Shutdown the video core void Shutdown() { Pica::Shutdown(); - delete g_renderer; + g_renderer.reset(); LOG_DEBUG(Render, "shutdown OK"); } diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 2867bf03e..bca67fb8c 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -5,6 +5,7 @@ #pragma once #include <atomic> +#include <memory> class EmuWindow; class RendererBase; @@ -29,8 +30,8 @@ static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height // Video core renderer // --------------------- -extern RendererBase* g_renderer; ///< Renderer plugin -extern EmuWindow* g_emu_window; ///< Emu window +extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin +extern EmuWindow* g_emu_window; ///< Emu window // TODO: Wrap these in a user settings struct along with any other graphics settings (often set from qt ui) extern std::atomic<bool> g_hw_renderer_enabled; @@ -40,7 +41,7 @@ extern std::atomic<bool> g_shader_jit_enabled; void Start(); /// Initialize the video core -void Init(EmuWindow* emu_window); +bool Init(EmuWindow* emu_window); /// Shutdown the video core void Shutdown(); |