diff options
41 files changed, 702 insertions, 485 deletions
diff --git a/.gitmodules b/.gitmodules index 598e4c64d..059512902 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -7,3 +7,6 @@ | |||
| 7 | [submodule "nihstro"] | 7 | [submodule "nihstro"] |
| 8 | path = externals/nihstro | 8 | path = externals/nihstro |
| 9 | url = https://github.com/neobrain/nihstro.git | 9 | url = https://github.com/neobrain/nihstro.git |
| 10 | [submodule "soundtouch"] | ||
| 11 | path = externals/soundtouch | ||
| 12 | url = https://github.com/citra-emu/soundtouch.git | ||
diff --git a/.travis-deps.sh b/.travis-deps.sh index c7bb7e785..4a79feb70 100755 --- a/.travis-deps.sh +++ b/.travis-deps.sh | |||
| @@ -9,7 +9,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then | |||
| 9 | export CXX=g++-5 | 9 | export CXX=g++-5 |
| 10 | mkdir -p $HOME/.local | 10 | mkdir -p $HOME/.local |
| 11 | 11 | ||
| 12 | curl -L http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ | 12 | curl -L http://www.cmake.org/files/v3.1/cmake-3.1.0-Linux-i386.tar.gz \ |
| 13 | | tar -xz -C $HOME/.local --strip-components=1 | 13 | | tar -xz -C $HOME/.local --strip-components=1 |
| 14 | 14 | ||
| 15 | ( | 15 | ( |
| @@ -20,6 +20,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then | |||
| 20 | ) | 20 | ) |
| 21 | elif [ "$TRAVIS_OS_NAME" = "osx" ]; then | 21 | elif [ "$TRAVIS_OS_NAME" = "osx" ]; then |
| 22 | brew update > /dev/null # silence the very verbose output | 22 | brew update > /dev/null # silence the very verbose output |
| 23 | brew install qt5 sdl2 dylibbundler | 23 | brew unlink cmake |
| 24 | brew install cmake31 qt5 sdl2 dylibbundler | ||
| 24 | gem install xcpretty | 25 | gem install xcpretty |
| 25 | fi | 26 | fi |
diff --git a/CMakeLists.txt b/CMakeLists.txt index ddde19760..d628ecc50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | # CMake 2.8.11 required for Qt5 settings to be applied automatically on | 1 | # CMake 3.1 required for Qt5 settings to be applied automatically on |
| 2 | # dependent libraries. | 2 | # dependent libraries and IMPORTED targets. |
| 3 | cmake_minimum_required(VERSION 2.8.11) | 3 | cmake_minimum_required(VERSION 3.1) |
| 4 | 4 | ||
| 5 | function(download_bundled_external remote_path lib_name prefix_var) | 5 | function(download_bundled_external remote_path lib_name prefix_var) |
| 6 | set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") | 6 | set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") |
| @@ -135,6 +135,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules") | |||
| 135 | find_package(OpenGL REQUIRED) | 135 | find_package(OpenGL REQUIRED) |
| 136 | include_directories(${OPENGL_INCLUDE_DIR}) | 136 | include_directories(${OPENGL_INCLUDE_DIR}) |
| 137 | 137 | ||
| 138 | # Prefer the -pthread flag on Linux. | ||
| 139 | set (THREADS_PREFER_PTHREAD_FLAG ON) | ||
| 138 | find_package(Threads REQUIRED) | 140 | find_package(Threads REQUIRED) |
| 139 | 141 | ||
| 140 | if (ENABLE_SDL2) | 142 | if (ENABLE_SDL2) |
| @@ -247,6 +249,9 @@ if(ENABLE_QT) | |||
| 247 | include_directories(externals/qhexedit) | 249 | include_directories(externals/qhexedit) |
| 248 | add_subdirectory(externals/qhexedit) | 250 | add_subdirectory(externals/qhexedit) |
| 249 | endif() | 251 | endif() |
| 252 | |||
| 253 | add_subdirectory(externals/soundtouch) | ||
| 254 | |||
| 250 | add_subdirectory(src) | 255 | add_subdirectory(src) |
| 251 | 256 | ||
| 252 | # Install freedesktop.org metadata files, following those specifications: | 257 | # Install freedesktop.org metadata files, following those specifications: |
diff --git a/externals/soundtouch b/externals/soundtouch new file mode 160000 | |||
| Subproject 5274ec4dec498bd88ccbcd28862a0f78a3b95ef | |||
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 869da5e83..c08ce69e0 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -16,6 +16,9 @@ set(HEADERS | |||
| 16 | sink.h | 16 | sink.h |
| 17 | ) | 17 | ) |
| 18 | 18 | ||
| 19 | include_directories(../../externals/soundtouch/include) | ||
| 20 | |||
| 19 | create_directory_groups(${SRCS} ${HEADERS}) | 21 | create_directory_groups(${SRCS} ${HEADERS}) |
| 20 | 22 | ||
| 21 | add_library(audio_core STATIC ${SRCS} ${HEADERS}) \ No newline at end of file | 23 | add_library(audio_core STATIC ${SRCS} ${HEADERS}) |
| 24 | target_link_libraries(audio_core SoundTouch) | ||
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 894f46990..b512b0f9b 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "audio_core/audio_core.h" | 5 | #include "audio_core/audio_core.h" |
| 6 | #include "audio_core/hle/dsp.h" | 6 | #include "audio_core/hle/dsp.h" |
| 7 | #include "audio_core/hle/pipe.h" | ||
| 7 | 8 | ||
| 8 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 9 | #include "core/hle/kernel/vm_manager.h" | 10 | #include "core/hle/kernel/vm_manager.h" |
| @@ -17,10 +18,10 @@ static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles | |||
| 17 | 18 | ||
| 18 | static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { | 19 | static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { |
| 19 | if (DSP::HLE::Tick()) { | 20 | if (DSP::HLE::Tick()) { |
| 20 | // HACK: We're not signaling the interrups when they should be, but just firing them all off together. | 21 | // TODO(merry): Signal all the other interrupts as appropriate. |
| 21 | // It should be only (interrupt_id = 2, channel_id = 2) that's signalled here. | 22 | DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Audio); |
| 22 | // TODO(merry): Understand when the other interrupts are fired. | 23 | // HACK(merry): Added to prevent regressions. Will remove soon. |
| 23 | DSP_DSP::SignalAllInterrupts(); | 24 | DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Binary); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | // Reschedule recurrent event | 27 | // Reschedule recurrent event |
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index 64c330914..b349895ea 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h | |||
| @@ -10,8 +10,6 @@ class VMManager; | |||
| 10 | 10 | ||
| 11 | namespace AudioCore { | 11 | namespace AudioCore { |
| 12 | 12 | ||
| 13 | constexpr int num_sources = 24; | ||
| 14 | constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate | ||
| 15 | constexpr int native_sample_rate = 32728; ///< 32kHz | 13 | constexpr int native_sample_rate = 32728; ///< 32kHz |
| 16 | 14 | ||
| 17 | /// Initialise Audio Core | 15 | /// Initialise Audio Core |
diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h index 37d441eb2..7910f42ae 100644 --- a/src/audio_core/hle/common.h +++ b/src/audio_core/hle/common.h | |||
| @@ -7,18 +7,19 @@ | |||
| 7 | #include <algorithm> | 7 | #include <algorithm> |
| 8 | #include <array> | 8 | #include <array> |
| 9 | 9 | ||
| 10 | #include "audio_core/audio_core.h" | ||
| 11 | |||
| 12 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 13 | 11 | ||
| 14 | namespace DSP { | 12 | namespace DSP { |
| 15 | namespace HLE { | 13 | namespace HLE { |
| 16 | 14 | ||
| 15 | constexpr int num_sources = 24; | ||
| 16 | constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate | ||
| 17 | |||
| 17 | /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. | 18 | /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. |
| 18 | using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>; | 19 | using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; |
| 19 | 20 | ||
| 20 | /// The DSP is quadraphonic internally. | 21 | /// The DSP is quadraphonic internally. |
| 21 | using QuadFrame32 = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>; | 22 | using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; |
| 22 | 23 | ||
| 23 | /** | 24 | /** |
| 24 | * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. | 25 | * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. |
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index c15ef0b7a..c76350bdd 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <type_traits> | 8 | #include <type_traits> |
| 9 | 9 | ||
| 10 | #include "audio_core/audio_core.h" | 10 | #include "audio_core/hle/common.h" |
| 11 | 11 | ||
| 12 | #include "common/bit_field.h" | 12 | #include "common/bit_field.h" |
| 13 | #include "common/common_funcs.h" | 13 | #include "common/common_funcs.h" |
| @@ -305,7 +305,7 @@ struct SourceConfiguration { | |||
| 305 | u16_le buffer_id; | 305 | u16_le buffer_id; |
| 306 | }; | 306 | }; |
| 307 | 307 | ||
| 308 | Configuration config[AudioCore::num_sources]; | 308 | Configuration config[num_sources]; |
| 309 | }; | 309 | }; |
| 310 | ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192); | 310 | ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192); |
| 311 | ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20); | 311 | ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20); |
| @@ -320,7 +320,7 @@ struct SourceStatus { | |||
| 320 | INSERT_PADDING_DSPWORDS(1); | 320 | INSERT_PADDING_DSPWORDS(1); |
| 321 | }; | 321 | }; |
| 322 | 322 | ||
| 323 | Status status[AudioCore::num_sources]; | 323 | Status status[num_sources]; |
| 324 | }; | 324 | }; |
| 325 | ASSERT_DSP_STRUCT(SourceStatus::Status, 12); | 325 | ASSERT_DSP_STRUCT(SourceStatus::Status, 12); |
| 326 | 326 | ||
| @@ -413,7 +413,7 @@ ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52); | |||
| 413 | struct AdpcmCoefficients { | 413 | struct AdpcmCoefficients { |
| 414 | /// Coefficients are signed fixed point with 11 fractional bits. | 414 | /// Coefficients are signed fixed point with 11 fractional bits. |
| 415 | /// Each source has 16 coefficients associated with it. | 415 | /// Each source has 16 coefficients associated with it. |
| 416 | s16_le coeff[AudioCore::num_sources][16]; | 416 | s16_le coeff[num_sources][16]; |
| 417 | }; | 417 | }; |
| 418 | ASSERT_DSP_STRUCT(AdpcmCoefficients, 768); | 418 | ASSERT_DSP_STRUCT(AdpcmCoefficients, 768); |
| 419 | 419 | ||
| @@ -427,7 +427,7 @@ ASSERT_DSP_STRUCT(DspStatus, 32); | |||
| 427 | /// Final mixed output in PCM16 stereo format, what you hear out of the speakers. | 427 | /// Final mixed output in PCM16 stereo format, what you hear out of the speakers. |
| 428 | /// When the application writes to this region it has no effect. | 428 | /// When the application writes to this region it has no effect. |
| 429 | struct FinalMixSamples { | 429 | struct FinalMixSamples { |
| 430 | s16_le pcm16[2 * AudioCore::samples_per_frame]; | 430 | s16_le pcm16[2 * samples_per_frame]; |
| 431 | }; | 431 | }; |
| 432 | ASSERT_DSP_STRUCT(FinalMixSamples, 640); | 432 | ASSERT_DSP_STRUCT(FinalMixSamples, 640); |
| 433 | 433 | ||
| @@ -437,7 +437,7 @@ ASSERT_DSP_STRUCT(FinalMixSamples, 640); | |||
| 437 | /// Values that exceed s16 range will be clipped by the DSP after further processing. | 437 | /// Values that exceed s16 range will be clipped by the DSP after further processing. |
| 438 | struct IntermediateMixSamples { | 438 | struct IntermediateMixSamples { |
| 439 | struct Samples { | 439 | struct Samples { |
| 440 | s32_le pcm32[4][AudioCore::samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian. | 440 | s32_le pcm32[4][samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian. |
| 441 | }; | 441 | }; |
| 442 | 442 | ||
| 443 | Samples mix1; | 443 | Samples mix1; |
diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp index 9381883b4..03280780f 100644 --- a/src/audio_core/hle/pipe.cpp +++ b/src/audio_core/hle/pipe.cpp | |||
| @@ -12,12 +12,14 @@ | |||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | 14 | ||
| 15 | #include "core/hle/service/dsp_dsp.h" | ||
| 16 | |||
| 15 | namespace DSP { | 17 | namespace DSP { |
| 16 | namespace HLE { | 18 | namespace HLE { |
| 17 | 19 | ||
| 18 | static DspState dsp_state = DspState::Off; | 20 | static DspState dsp_state = DspState::Off; |
| 19 | 21 | ||
| 20 | static std::array<std::vector<u8>, static_cast<size_t>(DspPipe::DspPipe_MAX)> pipe_data; | 22 | static std::array<std::vector<u8>, NUM_DSP_PIPE> pipe_data; |
| 21 | 23 | ||
| 22 | void ResetPipes() { | 24 | void ResetPipes() { |
| 23 | for (auto& data : pipe_data) { | 25 | for (auto& data : pipe_data) { |
| @@ -27,16 +29,18 @@ void ResetPipes() { | |||
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) { | 31 | std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) { |
| 30 | if (pipe_number >= DspPipe::DspPipe_MAX) { | 32 | const size_t pipe_index = static_cast<size_t>(pipe_number); |
| 31 | LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number); | 33 | |
| 34 | if (pipe_index >= NUM_DSP_PIPE) { | ||
| 35 | LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index); | ||
| 32 | return {}; | 36 | return {}; |
| 33 | } | 37 | } |
| 34 | 38 | ||
| 35 | std::vector<u8>& data = pipe_data[static_cast<size_t>(pipe_number)]; | 39 | std::vector<u8>& data = pipe_data[pipe_index]; |
| 36 | 40 | ||
| 37 | if (length > data.size()) { | 41 | if (length > data.size()) { |
| 38 | LOG_WARNING(Audio_DSP, "pipe_number = %u is out of data, application requested read of %u but %zu remain", | 42 | LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", |
| 39 | pipe_number, length, data.size()); | 43 | pipe_index, length, data.size()); |
| 40 | length = data.size(); | 44 | length = data.size(); |
| 41 | } | 45 | } |
| 42 | 46 | ||
| @@ -49,16 +53,20 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) { | |||
| 49 | } | 53 | } |
| 50 | 54 | ||
| 51 | size_t GetPipeReadableSize(DspPipe pipe_number) { | 55 | size_t GetPipeReadableSize(DspPipe pipe_number) { |
| 52 | if (pipe_number >= DspPipe::DspPipe_MAX) { | 56 | const size_t pipe_index = static_cast<size_t>(pipe_number); |
| 53 | LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number); | 57 | |
| 58 | if (pipe_index >= NUM_DSP_PIPE) { | ||
| 59 | LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index); | ||
| 54 | return 0; | 60 | return 0; |
| 55 | } | 61 | } |
| 56 | 62 | ||
| 57 | return pipe_data[static_cast<size_t>(pipe_number)].size(); | 63 | return pipe_data[pipe_index].size(); |
| 58 | } | 64 | } |
| 59 | 65 | ||
| 60 | static void WriteU16(DspPipe pipe_number, u16 value) { | 66 | static void WriteU16(DspPipe pipe_number, u16 value) { |
| 61 | std::vector<u8>& data = pipe_data[static_cast<size_t>(pipe_number)]; | 67 | const size_t pipe_index = static_cast<size_t>(pipe_number); |
| 68 | |||
| 69 | std::vector<u8>& data = pipe_data.at(pipe_index); | ||
| 62 | // Little endian | 70 | // Little endian |
| 63 | data.emplace_back(value & 0xFF); | 71 | data.emplace_back(value & 0xFF); |
| 64 | data.emplace_back(value >> 8); | 72 | data.emplace_back(value >> 8); |
| @@ -91,6 +99,8 @@ static void AudioPipeWriteStructAddresses() { | |||
| 91 | for (u16 addr : struct_addresses) { | 99 | for (u16 addr : struct_addresses) { |
| 92 | WriteU16(DspPipe::Audio, addr); | 100 | WriteU16(DspPipe::Audio, addr); |
| 93 | } | 101 | } |
| 102 | // Signal that we have data on this pipe. | ||
| 103 | DSP_DSP::SignalPipeInterrupt(DspPipe::Audio); | ||
| 94 | } | 104 | } |
| 95 | 105 | ||
| 96 | void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | 106 | void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { |
| @@ -145,7 +155,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | |||
| 145 | return; | 155 | return; |
| 146 | } | 156 | } |
| 147 | default: | 157 | default: |
| 148 | LOG_CRITICAL(Audio_DSP, "pipe_number = %u unimplemented", pipe_number); | 158 | LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast<size_t>(pipe_number)); |
| 149 | UNIMPLEMENTED(); | 159 | UNIMPLEMENTED(); |
| 150 | return; | 160 | return; |
| 151 | } | 161 | } |
diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h index 382d35e87..64d97f8ba 100644 --- a/src/audio_core/hle/pipe.h +++ b/src/audio_core/hle/pipe.h | |||
| @@ -19,9 +19,9 @@ enum class DspPipe { | |||
| 19 | Debug = 0, | 19 | Debug = 0, |
| 20 | Dma = 1, | 20 | Dma = 1, |
| 21 | Audio = 2, | 21 | Audio = 2, |
| 22 | Binary = 3, | 22 | Binary = 3 |
| 23 | DspPipe_MAX | ||
| 24 | }; | 23 | }; |
| 24 | constexpr size_t NUM_DSP_PIPE = 8; | ||
| 25 | 25 | ||
| 26 | /** | 26 | /** |
| 27 | * Read a DSP pipe. | 27 | * Read a DSP pipe. |
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 351752c1c..43fa06b4e 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt | |||
| @@ -21,7 +21,7 @@ target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) | |||
| 21 | if (MSVC) | 21 | if (MSVC) |
| 22 | target_link_libraries(citra getopt) | 22 | target_link_libraries(citra getopt) |
| 23 | endif() | 23 | endif() |
| 24 | target_link_libraries(citra ${PLATFORM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) | 24 | target_link_libraries(citra ${PLATFORM_LIBRARIES} Threads::Threads) |
| 25 | 25 | ||
| 26 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") | 26 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") |
| 27 | install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | 27 | install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 6660d9879..cc9e0c624 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -92,7 +92,7 @@ else() | |||
| 92 | endif() | 92 | endif() |
| 93 | target_link_libraries(citra-qt core video_core audio_core common qhexedit) | 93 | target_link_libraries(citra-qt core video_core audio_core common qhexedit) |
| 94 | target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) | 94 | target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) |
| 95 | target_link_libraries(citra-qt ${PLATFORM_LIBRARIES}) | 95 | target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads) |
| 96 | 96 | ||
| 97 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") | 97 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") |
| 98 | install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | 98 | install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") |
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 8e60b9cad..01b81c11c 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp | |||
| @@ -71,7 +71,9 @@ void EmuThread::run() { | |||
| 71 | // Shutdown the core emulation | 71 | // Shutdown the core emulation |
| 72 | System::Shutdown(); | 72 | System::Shutdown(); |
| 73 | 73 | ||
| 74 | #if MICROPROFILE_ENABLED | ||
| 74 | MicroProfileOnThreadExit(); | 75 | MicroProfileOnThreadExit(); |
| 76 | #endif | ||
| 75 | 77 | ||
| 76 | render_window->moveContext(); | 78 | render_window->moveContext(); |
| 77 | } | 79 | } |
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp index 819ec7707..c8510128a 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp | |||
| @@ -75,7 +75,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const | |||
| 75 | case Role_IsEnabled: | 75 | case Role_IsEnabled: |
| 76 | { | 76 | { |
| 77 | auto context = context_weak.lock(); | 77 | auto context = context_weak.lock(); |
| 78 | return context && context->breakpoints[event].enabled; | 78 | return context && context->breakpoints[(int)event].enabled; |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | default: | 81 | default: |
| @@ -110,7 +110,7 @@ bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, i | |||
| 110 | if (!context) | 110 | if (!context) |
| 111 | return false; | 111 | return false; |
| 112 | 112 | ||
| 113 | context->breakpoints[event].enabled = value == Qt::Checked; | 113 | context->breakpoints[(int)event].enabled = value == Qt::Checked; |
| 114 | QModelIndex changed_index = createIndex(index.row(), 0); | 114 | QModelIndex changed_index = createIndex(index.row(), 0); |
| 115 | emit dataChanged(changed_index, changed_index); | 115 | emit dataChanged(changed_index, changed_index); |
| 116 | return true; | 116 | return true; |
diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 4f6ba0e1f..7bb010f77 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp | |||
| @@ -9,13 +9,16 @@ | |||
| 9 | #include "citra_qt/debugger/profiler.h" | 9 | #include "citra_qt/debugger/profiler.h" |
| 10 | #include "citra_qt/util/util.h" | 10 | #include "citra_qt/util/util.h" |
| 11 | 11 | ||
| 12 | #include "common/common_types.h" | ||
| 12 | #include "common/microprofile.h" | 13 | #include "common/microprofile.h" |
| 13 | #include "common/profiler_reporting.h" | 14 | #include "common/profiler_reporting.h" |
| 14 | 15 | ||
| 15 | // Include the implementation of the UI in this file. This isn't in microprofile.cpp because the | 16 | // Include the implementation of the UI in this file. This isn't in microprofile.cpp because the |
| 16 | // non-Qt frontends don't need it (and don't implement the UI drawing hooks either). | 17 | // non-Qt frontends don't need it (and don't implement the UI drawing hooks either). |
| 18 | #if MICROPROFILE_ENABLED | ||
| 17 | #define MICROPROFILEUI_IMPL 1 | 19 | #define MICROPROFILEUI_IMPL 1 |
| 18 | #include "common/microprofileui.h" | 20 | #include "common/microprofileui.h" |
| 21 | #endif | ||
| 19 | 22 | ||
| 20 | using namespace Common::Profiling; | 23 | using namespace Common::Profiling; |
| 21 | 24 | ||
| @@ -34,21 +37,9 @@ static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) | |||
| 34 | } | 37 | } |
| 35 | } | 38 | } |
| 36 | 39 | ||
| 37 | static const TimingCategoryInfo* GetCategoryInfo(int id) | ||
| 38 | { | ||
| 39 | const auto& categories = GetProfilingManager().GetTimingCategoriesInfo(); | ||
| 40 | if ((size_t)id >= categories.size()) { | ||
| 41 | return nullptr; | ||
| 42 | } else { | ||
| 43 | return &categories[id]; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) | 40 | ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) |
| 48 | { | 41 | { |
| 49 | updateProfilingInfo(); | 42 | updateProfilingInfo(); |
| 50 | const auto& categories = GetProfilingManager().GetTimingCategoriesInfo(); | ||
| 51 | results.time_per_category.resize(categories.size()); | ||
| 52 | } | 43 | } |
| 53 | 44 | ||
| 54 | QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const | 45 | QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const |
| @@ -85,7 +76,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const | |||
| 85 | if (parent.isValid()) { | 76 | if (parent.isValid()) { |
| 86 | return 0; | 77 | return 0; |
| 87 | } else { | 78 | } else { |
| 88 | return static_cast<int>(results.time_per_category.size() + 2); | 79 | return 2; |
| 89 | } | 80 | } |
| 90 | } | 81 | } |
| 91 | 82 | ||
| @@ -104,17 +95,6 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const | |||
| 104 | } else { | 95 | } else { |
| 105 | return GetDataForColumn(index.column(), results.interframe_time); | 96 | return GetDataForColumn(index.column(), results.interframe_time); |
| 106 | } | 97 | } |
| 107 | } else { | ||
| 108 | if (index.column() == 0) { | ||
| 109 | const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2); | ||
| 110 | return info != nullptr ? QString(info->name) : QVariant(); | ||
| 111 | } else { | ||
| 112 | if (index.row() - 2 < (int)results.time_per_category.size()) { | ||
| 113 | return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]); | ||
| 114 | } else { | ||
| 115 | return QVariant(); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | 98 | } |
| 119 | } | 99 | } |
| 120 | 100 | ||
| @@ -148,6 +128,8 @@ void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) | |||
| 148 | } | 128 | } |
| 149 | } | 129 | } |
| 150 | 130 | ||
| 131 | #if MICROPROFILE_ENABLED | ||
| 132 | |||
| 151 | class MicroProfileWidget : public QWidget { | 133 | class MicroProfileWidget : public QWidget { |
| 152 | public: | 134 | public: |
| 153 | MicroProfileWidget(QWidget* parent = nullptr); | 135 | MicroProfileWidget(QWidget* parent = nullptr); |
| @@ -171,6 +153,8 @@ private: | |||
| 171 | QTimer update_timer; | 153 | QTimer update_timer; |
| 172 | }; | 154 | }; |
| 173 | 155 | ||
| 156 | #endif | ||
| 157 | |||
| 174 | MicroProfileDialog::MicroProfileDialog(QWidget* parent) | 158 | MicroProfileDialog::MicroProfileDialog(QWidget* parent) |
| 175 | : QWidget(parent, Qt::Dialog) | 159 | : QWidget(parent, Qt::Dialog) |
| 176 | { | 160 | { |
| @@ -180,6 +164,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) | |||
| 180 | // Remove the "?" button from the titlebar and enable the maximize button | 164 | // Remove the "?" button from the titlebar and enable the maximize button |
| 181 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint | Qt::WindowMaximizeButtonHint); | 165 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint | Qt::WindowMaximizeButtonHint); |
| 182 | 166 | ||
| 167 | #if MICROPROFILE_ENABLED | ||
| 168 | |||
| 183 | MicroProfileWidget* widget = new MicroProfileWidget(this); | 169 | MicroProfileWidget* widget = new MicroProfileWidget(this); |
| 184 | 170 | ||
| 185 | QLayout* layout = new QVBoxLayout(this); | 171 | QLayout* layout = new QVBoxLayout(this); |
| @@ -191,6 +177,7 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) | |||
| 191 | setFocusProxy(widget); | 177 | setFocusProxy(widget); |
| 192 | widget->setFocusPolicy(Qt::StrongFocus); | 178 | widget->setFocusPolicy(Qt::StrongFocus); |
| 193 | widget->setFocus(); | 179 | widget->setFocus(); |
| 180 | #endif | ||
| 194 | } | 181 | } |
| 195 | 182 | ||
| 196 | QAction* MicroProfileDialog::toggleViewAction() { | 183 | QAction* MicroProfileDialog::toggleViewAction() { |
| @@ -218,6 +205,9 @@ void MicroProfileDialog::hideEvent(QHideEvent* ev) { | |||
| 218 | QWidget::hideEvent(ev); | 205 | QWidget::hideEvent(ev); |
| 219 | } | 206 | } |
| 220 | 207 | ||
| 208 | |||
| 209 | #if MICROPROFILE_ENABLED | ||
| 210 | |||
| 221 | /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the | 211 | /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the |
| 222 | /// QPainter available inside the drawing callbacks. | 212 | /// QPainter available inside the drawing callbacks. |
| 223 | static QPainter* mp_painter = nullptr; | 213 | static QPainter* mp_painter = nullptr; |
| @@ -337,3 +327,4 @@ void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color) | |||
| 337 | mp_painter->drawPolyline(point_buf.data(), vertices_length); | 327 | mp_painter->drawPolyline(point_buf.data(), vertices_length); |
| 338 | point_buf.clear(); | 328 | point_buf.clear(); |
| 339 | } | 329 | } |
| 330 | #endif | ||
diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h index 036054740..3b38ed8ec 100644 --- a/src/citra_qt/debugger/profiler.h +++ b/src/citra_qt/debugger/profiler.h | |||
| @@ -7,8 +7,10 @@ | |||
| 7 | #include <QAbstractItemModel> | 7 | #include <QAbstractItemModel> |
| 8 | #include <QDockWidget> | 8 | #include <QDockWidget> |
| 9 | #include <QTimer> | 9 | #include <QTimer> |
| 10 | |||
| 10 | #include "ui_profiler.h" | 11 | #include "ui_profiler.h" |
| 11 | 12 | ||
| 13 | #include "common/microprofile.h" | ||
| 12 | #include "common/profiler_reporting.h" | 14 | #include "common/profiler_reporting.h" |
| 13 | 15 | ||
| 14 | class ProfilerModel : public QAbstractItemModel | 16 | class ProfilerModel : public QAbstractItemModel |
| @@ -49,6 +51,7 @@ private: | |||
| 49 | QTimer update_timer; | 51 | QTimer update_timer; |
| 50 | }; | 52 | }; |
| 51 | 53 | ||
| 54 | |||
| 52 | class MicroProfileDialog : public QWidget { | 55 | class MicroProfileDialog : public QWidget { |
| 53 | Q_OBJECT | 56 | Q_OBJECT |
| 54 | 57 | ||
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 2ca1e51f6..f1ab29755 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -69,8 +69,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) | |||
| 69 | addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); | 69 | addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); |
| 70 | profilerWidget->hide(); | 70 | profilerWidget->hide(); |
| 71 | 71 | ||
| 72 | #if MICROPROFILE_ENABLED | ||
| 72 | microProfileDialog = new MicroProfileDialog(this); | 73 | microProfileDialog = new MicroProfileDialog(this); |
| 73 | microProfileDialog->hide(); | 74 | microProfileDialog->hide(); |
| 75 | #endif | ||
| 74 | 76 | ||
| 75 | disasmWidget = new DisassemblerWidget(this, emu_thread.get()); | 77 | disasmWidget = new DisassemblerWidget(this, emu_thread.get()); |
| 76 | addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); | 78 | addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); |
| @@ -110,7 +112,9 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) | |||
| 110 | 112 | ||
| 111 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); | 113 | QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); |
| 112 | debug_menu->addAction(profilerWidget->toggleViewAction()); | 114 | debug_menu->addAction(profilerWidget->toggleViewAction()); |
| 115 | #if MICROPROFILE_ENABLED | ||
| 113 | debug_menu->addAction(microProfileDialog->toggleViewAction()); | 116 | debug_menu->addAction(microProfileDialog->toggleViewAction()); |
| 117 | #endif | ||
| 114 | debug_menu->addAction(disasmWidget->toggleViewAction()); | 118 | debug_menu->addAction(disasmWidget->toggleViewAction()); |
| 115 | debug_menu->addAction(registersWidget->toggleViewAction()); | 119 | debug_menu->addAction(registersWidget->toggleViewAction()); |
| 116 | debug_menu->addAction(callstackWidget->toggleViewAction()); | 120 | debug_menu->addAction(callstackWidget->toggleViewAction()); |
| @@ -136,8 +140,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) | |||
| 136 | restoreGeometry(UISettings::values.geometry); | 140 | restoreGeometry(UISettings::values.geometry); |
| 137 | restoreState(UISettings::values.state); | 141 | restoreState(UISettings::values.state); |
| 138 | render_window->restoreGeometry(UISettings::values.renderwindow_geometry); | 142 | render_window->restoreGeometry(UISettings::values.renderwindow_geometry); |
| 143 | #if MICROPROFILE_ENABLED | ||
| 139 | microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); | 144 | microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); |
| 140 | microProfileDialog->setVisible(UISettings::values.microprofile_visible); | 145 | microProfileDialog->setVisible(UISettings::values.microprofile_visible); |
| 146 | #endif | ||
| 141 | 147 | ||
| 142 | game_list->LoadInterfaceLayout(); | 148 | game_list->LoadInterfaceLayout(); |
| 143 | 149 | ||
| @@ -511,9 +517,10 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 511 | UISettings::values.geometry = saveGeometry(); | 517 | UISettings::values.geometry = saveGeometry(); |
| 512 | UISettings::values.state = saveState(); | 518 | UISettings::values.state = saveState(); |
| 513 | UISettings::values.renderwindow_geometry = render_window->saveGeometry(); | 519 | UISettings::values.renderwindow_geometry = render_window->saveGeometry(); |
| 520 | #if MICROPROFILE_ENABLED | ||
| 514 | UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry(); | 521 | UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry(); |
| 515 | UISettings::values.microprofile_visible = microProfileDialog->isVisible(); | 522 | UISettings::values.microprofile_visible = microProfileDialog->isVisible(); |
| 516 | 523 | #endif | |
| 517 | UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked(); | 524 | UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked(); |
| 518 | UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked(); | 525 | UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked(); |
| 519 | UISettings::values.first_start = false; | 526 | UISettings::values.first_start = false; |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index c839ce173..aa6eee2a3 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -47,7 +47,6 @@ set(HEADERS | |||
| 47 | microprofile.h | 47 | microprofile.h |
| 48 | microprofileui.h | 48 | microprofileui.h |
| 49 | platform.h | 49 | platform.h |
| 50 | profiler.h | ||
| 51 | profiler_reporting.h | 50 | profiler_reporting.h |
| 52 | scm_rev.h | 51 | scm_rev.h |
| 53 | scope_exit.h | 52 | scope_exit.h |
diff --git a/src/common/assert.h b/src/common/assert.h index d7f19f5eb..cd9b819a9 100644 --- a/src/common/assert.h +++ b/src/common/assert.h | |||
| @@ -39,7 +39,7 @@ static void assert_noinline_call(const Fn& fn) { | |||
| 39 | }); } while (0) | 39 | }); } while (0) |
| 40 | 40 | ||
| 41 | #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") | 41 | #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") |
| 42 | #define UNREACHABLE_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__) | 42 | #define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__) |
| 43 | 43 | ||
| 44 | #ifdef _DEBUG | 44 | #ifdef _DEBUG |
| 45 | #define DEBUG_ASSERT(_a_) ASSERT(_a_) | 45 | #define DEBUG_ASSERT(_a_) ASSERT(_a_) |
diff --git a/src/common/microprofile.h b/src/common/microprofile.h index d3b6cb97c..ef312c6e1 100644 --- a/src/common/microprofile.h +++ b/src/common/microprofile.h | |||
| @@ -4,6 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | // Uncomment this to disable microprofile. This will get you cleaner profiles when using | ||
| 8 | // external sampling profilers like "Very Sleepy", and will improve performance somewhat. | ||
| 9 | // #define MICROPROFILE_ENABLED 0 | ||
| 10 | |||
| 7 | // Customized Citra settings. | 11 | // Customized Citra settings. |
| 8 | // This file wraps the MicroProfile header so that these are consistent everywhere. | 12 | // This file wraps the MicroProfile header so that these are consistent everywhere. |
| 9 | #define MICROPROFILE_WEBSERVER 0 | 13 | #define MICROPROFILE_WEBSERVER 0 |
diff --git a/src/common/microprofileui.h b/src/common/microprofileui.h index 97c369bd9..41abe6b75 100644 --- a/src/common/microprofileui.h +++ b/src/common/microprofileui.h | |||
| @@ -13,4 +13,7 @@ | |||
| 13 | #define MICROPROFILE_HELP_ALT "Right-Click" | 13 | #define MICROPROFILE_HELP_ALT "Right-Click" |
| 14 | #define MICROPROFILE_HELP_MOD "Ctrl" | 14 | #define MICROPROFILE_HELP_MOD "Ctrl" |
| 15 | 15 | ||
| 16 | // This isn't included by microprofileui.h :( | ||
| 17 | #include <cstdlib> // For std::abs | ||
| 18 | |||
| 16 | #include <microprofileui.h> | 19 | #include <microprofileui.h> |
diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp index 7792edd2f..49eb3f40c 100644 --- a/src/common/profiler.cpp +++ b/src/common/profiler.cpp | |||
| @@ -7,71 +7,16 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/profiler.h" | ||
| 11 | #include "common/profiler_reporting.h" | 10 | #include "common/profiler_reporting.h" |
| 12 | #include "common/synchronized_wrapper.h" | 11 | #include "common/synchronized_wrapper.h" |
| 13 | 12 | ||
| 14 | #if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013. | ||
| 15 | #define WIN32_LEAN_AND_MEAN | ||
| 16 | #include <Windows.h> // For QueryPerformanceCounter/Frequency | ||
| 17 | #endif | ||
| 18 | |||
| 19 | namespace Common { | 13 | namespace Common { |
| 20 | namespace Profiling { | 14 | namespace Profiling { |
| 21 | 15 | ||
| 22 | #if ENABLE_PROFILING | ||
| 23 | thread_local Timer* Timer::current_timer = nullptr; | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013 | ||
| 27 | QPCClock::time_point QPCClock::now() { | ||
| 28 | static LARGE_INTEGER freq; | ||
| 29 | // Use this dummy local static to ensure this gets initialized once. | ||
| 30 | static BOOL dummy = QueryPerformanceFrequency(&freq); | ||
| 31 | |||
| 32 | LARGE_INTEGER ticks; | ||
| 33 | QueryPerformanceCounter(&ticks); | ||
| 34 | |||
| 35 | // This is prone to overflow when multiplying, which is why I'm using micro instead of nano. The | ||
| 36 | // correct way to approach this would be to just return ticks as a time_point and then subtract | ||
| 37 | // and do this conversion when creating a duration from two time_points, however, as far as I | ||
| 38 | // could tell the C++ requirements for these types are incompatible with this approach. | ||
| 39 | return time_point(duration(ticks.QuadPart * std::micro::den / freq.QuadPart)); | ||
| 40 | } | ||
| 41 | #endif | ||
| 42 | |||
| 43 | TimingCategory::TimingCategory(const char* name, TimingCategory* parent) | ||
| 44 | : accumulated_duration(0) { | ||
| 45 | |||
| 46 | ProfilingManager& manager = GetProfilingManager(); | ||
| 47 | category_id = manager.RegisterTimingCategory(this, name); | ||
| 48 | if (parent != nullptr) | ||
| 49 | manager.SetTimingCategoryParent(category_id, parent->category_id); | ||
| 50 | } | ||
| 51 | |||
| 52 | ProfilingManager::ProfilingManager() | 16 | ProfilingManager::ProfilingManager() |
| 53 | : last_frame_end(Clock::now()), this_frame_start(Clock::now()) { | 17 | : last_frame_end(Clock::now()), this_frame_start(Clock::now()) { |
| 54 | } | 18 | } |
| 55 | 19 | ||
| 56 | unsigned int ProfilingManager::RegisterTimingCategory(TimingCategory* category, const char* name) { | ||
| 57 | TimingCategoryInfo info; | ||
| 58 | info.category = category; | ||
| 59 | info.name = name; | ||
| 60 | info.parent = TimingCategoryInfo::NO_PARENT; | ||
| 61 | |||
| 62 | unsigned int id = (unsigned int)timing_categories.size(); | ||
| 63 | timing_categories.push_back(std::move(info)); | ||
| 64 | |||
| 65 | return id; | ||
| 66 | } | ||
| 67 | |||
| 68 | void ProfilingManager::SetTimingCategoryParent(unsigned int category, unsigned int parent) { | ||
| 69 | ASSERT(category < timing_categories.size()); | ||
| 70 | ASSERT(parent < timing_categories.size()); | ||
| 71 | |||
| 72 | timing_categories[category].parent = parent; | ||
| 73 | } | ||
| 74 | |||
| 75 | void ProfilingManager::BeginFrame() { | 20 | void ProfilingManager::BeginFrame() { |
| 76 | this_frame_start = Clock::now(); | 21 | this_frame_start = Clock::now(); |
| 77 | } | 22 | } |
| @@ -82,11 +27,6 @@ void ProfilingManager::FinishFrame() { | |||
| 82 | results.interframe_time = now - last_frame_end; | 27 | results.interframe_time = now - last_frame_end; |
| 83 | results.frame_time = now - this_frame_start; | 28 | results.frame_time = now - this_frame_start; |
| 84 | 29 | ||
| 85 | results.time_per_category.resize(timing_categories.size()); | ||
| 86 | for (size_t i = 0; i < timing_categories.size(); ++i) { | ||
| 87 | results.time_per_category[i] = timing_categories[i].category->GetAccumulatedTime(); | ||
| 88 | } | ||
| 89 | |||
| 90 | last_frame_end = now; | 30 | last_frame_end = now; |
| 91 | } | 31 | } |
| 92 | 32 | ||
| @@ -100,26 +40,9 @@ void TimingResultsAggregator::Clear() { | |||
| 100 | window_size = cursor = 0; | 40 | window_size = cursor = 0; |
| 101 | } | 41 | } |
| 102 | 42 | ||
| 103 | void TimingResultsAggregator::SetNumberOfCategories(size_t n) { | ||
| 104 | size_t old_size = times_per_category.size(); | ||
| 105 | if (n == old_size) | ||
| 106 | return; | ||
| 107 | |||
| 108 | times_per_category.resize(n); | ||
| 109 | |||
| 110 | for (size_t i = old_size; i < n; ++i) { | ||
| 111 | times_per_category[i].resize(max_window_size, Duration::zero()); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) { | 43 | void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) { |
| 116 | SetNumberOfCategories(frame_result.time_per_category.size()); | ||
| 117 | |||
| 118 | interframe_times[cursor] = frame_result.interframe_time; | 44 | interframe_times[cursor] = frame_result.interframe_time; |
| 119 | frame_times[cursor] = frame_result.frame_time; | 45 | frame_times[cursor] = frame_result.frame_time; |
| 120 | for (size_t i = 0; i < frame_result.time_per_category.size(); ++i) { | ||
| 121 | times_per_category[i][cursor] = frame_result.time_per_category[i]; | ||
| 122 | } | ||
| 123 | 46 | ||
| 124 | ++cursor; | 47 | ++cursor; |
| 125 | if (cursor == max_window_size) | 48 | if (cursor == max_window_size) |
| @@ -162,11 +85,6 @@ AggregatedFrameResult TimingResultsAggregator::GetAggregatedResults() const { | |||
| 162 | result.fps = 0.0f; | 85 | result.fps = 0.0f; |
| 163 | } | 86 | } |
| 164 | 87 | ||
| 165 | result.time_per_category.resize(times_per_category.size()); | ||
| 166 | for (size_t i = 0; i < times_per_category.size(); ++i) { | ||
| 167 | result.time_per_category[i] = AggregateField(times_per_category[i], window_size); | ||
| 168 | } | ||
| 169 | |||
| 170 | return result; | 88 | return result; |
| 171 | } | 89 | } |
| 172 | 90 | ||
diff --git a/src/common/profiler.h b/src/common/profiler.h deleted file mode 100644 index 3e967b4bc..000000000 --- a/src/common/profiler.h +++ /dev/null | |||
| @@ -1,152 +0,0 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <chrono> | ||
| 9 | |||
| 10 | #include "common/assert.h" | ||
| 11 | #include "common/thread.h" | ||
| 12 | |||
| 13 | namespace Common { | ||
| 14 | namespace Profiling { | ||
| 15 | |||
| 16 | // If this is defined to 0, it turns all Timers into no-ops. | ||
| 17 | #ifndef ENABLE_PROFILING | ||
| 18 | #define ENABLE_PROFILING 1 | ||
| 19 | #endif | ||
| 20 | |||
| 21 | #if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013 | ||
| 22 | // MSVC up to 2013 doesn't use QueryPerformanceCounter for high_resolution_clock, so it has bad | ||
| 23 | // precision. We manually implement a clock based on QPC to get good results. | ||
| 24 | |||
| 25 | struct QPCClock { | ||
| 26 | using duration = std::chrono::microseconds; | ||
| 27 | using time_point = std::chrono::time_point<QPCClock>; | ||
| 28 | using rep = duration::rep; | ||
| 29 | using period = duration::period; | ||
| 30 | static const bool is_steady = false; | ||
| 31 | |||
| 32 | static time_point now(); | ||
| 33 | }; | ||
| 34 | |||
| 35 | using Clock = QPCClock; | ||
| 36 | #else | ||
| 37 | using Clock = std::chrono::high_resolution_clock; | ||
| 38 | #endif | ||
| 39 | |||
| 40 | using Duration = Clock::duration; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Represents a timing category that measured time can be accounted towards. Should be declared as a | ||
| 44 | * global variable and passed to Timers. | ||
| 45 | */ | ||
| 46 | class TimingCategory final { | ||
| 47 | public: | ||
| 48 | TimingCategory(const char* name, TimingCategory* parent = nullptr); | ||
| 49 | |||
| 50 | unsigned int GetCategoryId() const { | ||
| 51 | return category_id; | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Adds some time to this category. Can safely be called from multiple threads at the same time. | ||
| 55 | void AddTime(Duration amount) { | ||
| 56 | std::atomic_fetch_add_explicit( | ||
| 57 | &accumulated_duration, amount.count(), | ||
| 58 | std::memory_order_relaxed); | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Atomically retrieves the accumulated measured time for this category and resets the counter | ||
| 63 | * to zero. Can be safely called concurrently with AddTime. | ||
| 64 | */ | ||
| 65 | Duration GetAccumulatedTime() { | ||
| 66 | return Duration(std::atomic_exchange_explicit( | ||
| 67 | &accumulated_duration, (Duration::rep)0, | ||
| 68 | std::memory_order_relaxed)); | ||
| 69 | } | ||
| 70 | |||
| 71 | private: | ||
| 72 | unsigned int category_id; | ||
| 73 | std::atomic<Duration::rep> accumulated_duration; | ||
| 74 | }; | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Measures time elapsed between a call to Start and a call to Stop and attributes it to the given | ||
| 78 | * TimingCategory. Start/Stop can be called multiple times on the same timer, but each call must be | ||
| 79 | * appropriately paired. | ||
| 80 | * | ||
| 81 | * When a Timer is started, it automatically pauses a previously running timer on the same thread, | ||
| 82 | * which is resumed when it is stopped. As such, no special action needs to be taken to avoid | ||
| 83 | * double-accounting of time on two categories. | ||
| 84 | */ | ||
| 85 | class Timer { | ||
| 86 | public: | ||
| 87 | Timer(TimingCategory& category) : category(category) { | ||
| 88 | } | ||
| 89 | |||
| 90 | void Start() { | ||
| 91 | #if ENABLE_PROFILING | ||
| 92 | ASSERT(!running); | ||
| 93 | previous_timer = current_timer; | ||
| 94 | current_timer = this; | ||
| 95 | if (previous_timer != nullptr) | ||
| 96 | previous_timer->StopTiming(); | ||
| 97 | |||
| 98 | StartTiming(); | ||
| 99 | #endif | ||
| 100 | } | ||
| 101 | |||
| 102 | void Stop() { | ||
| 103 | #if ENABLE_PROFILING | ||
| 104 | ASSERT(running); | ||
| 105 | StopTiming(); | ||
| 106 | |||
| 107 | if (previous_timer != nullptr) | ||
| 108 | previous_timer->StartTiming(); | ||
| 109 | current_timer = previous_timer; | ||
| 110 | #endif | ||
| 111 | } | ||
| 112 | |||
| 113 | private: | ||
| 114 | #if ENABLE_PROFILING | ||
| 115 | void StartTiming() { | ||
| 116 | start = Clock::now(); | ||
| 117 | running = true; | ||
| 118 | } | ||
| 119 | |||
| 120 | void StopTiming() { | ||
| 121 | auto duration = Clock::now() - start; | ||
| 122 | running = false; | ||
| 123 | category.AddTime(std::chrono::duration_cast<Duration>(duration)); | ||
| 124 | } | ||
| 125 | |||
| 126 | Clock::time_point start; | ||
| 127 | bool running = false; | ||
| 128 | |||
| 129 | Timer* previous_timer; | ||
| 130 | static thread_local Timer* current_timer; | ||
| 131 | #endif | ||
| 132 | |||
| 133 | TimingCategory& category; | ||
| 134 | }; | ||
| 135 | |||
| 136 | /** | ||
| 137 | * A Timer that automatically starts timing when created and stops at the end of the scope. Should | ||
| 138 | * be used in the majority of cases. | ||
| 139 | */ | ||
| 140 | class ScopeTimer : public Timer { | ||
| 141 | public: | ||
| 142 | ScopeTimer(TimingCategory& category) : Timer(category) { | ||
| 143 | Start(); | ||
| 144 | } | ||
| 145 | |||
| 146 | ~ScopeTimer() { | ||
| 147 | Stop(); | ||
| 148 | } | ||
| 149 | }; | ||
| 150 | |||
| 151 | } // namespace Profiling | ||
| 152 | } // namespace Common | ||
diff --git a/src/common/profiler_reporting.h b/src/common/profiler_reporting.h index df98e05b7..fa1ac883f 100644 --- a/src/common/profiler_reporting.h +++ b/src/common/profiler_reporting.h | |||
| @@ -4,22 +4,17 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <chrono> | ||
| 7 | #include <cstddef> | 8 | #include <cstddef> |
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | 10 | ||
| 10 | #include "common/profiler.h" | ||
| 11 | #include "common/synchronized_wrapper.h" | 11 | #include "common/synchronized_wrapper.h" |
| 12 | 12 | ||
| 13 | namespace Common { | 13 | namespace Common { |
| 14 | namespace Profiling { | 14 | namespace Profiling { |
| 15 | 15 | ||
| 16 | struct TimingCategoryInfo { | 16 | using Clock = std::chrono::high_resolution_clock; |
| 17 | static const unsigned int NO_PARENT = -1; | 17 | using Duration = Clock::duration; |
| 18 | |||
| 19 | TimingCategory* category; | ||
| 20 | const char* name; | ||
| 21 | unsigned int parent; | ||
| 22 | }; | ||
| 23 | 18 | ||
| 24 | struct ProfilingFrameResult { | 19 | struct ProfilingFrameResult { |
| 25 | /// Time since the last delivered frame | 20 | /// Time since the last delivered frame |
| @@ -27,22 +22,12 @@ struct ProfilingFrameResult { | |||
| 27 | 22 | ||
| 28 | /// Time spent processing a frame, excluding VSync | 23 | /// Time spent processing a frame, excluding VSync |
| 29 | Duration frame_time; | 24 | Duration frame_time; |
| 30 | |||
| 31 | /// Total amount of time spent inside each category in this frame. Indexed by the category id | ||
| 32 | std::vector<Duration> time_per_category; | ||
| 33 | }; | 25 | }; |
| 34 | 26 | ||
| 35 | class ProfilingManager final { | 27 | class ProfilingManager final { |
| 36 | public: | 28 | public: |
| 37 | ProfilingManager(); | 29 | ProfilingManager(); |
| 38 | 30 | ||
| 39 | unsigned int RegisterTimingCategory(TimingCategory* category, const char* name); | ||
| 40 | void SetTimingCategoryParent(unsigned int category, unsigned int parent); | ||
| 41 | |||
| 42 | const std::vector<TimingCategoryInfo>& GetTimingCategoriesInfo() const { | ||
| 43 | return timing_categories; | ||
| 44 | } | ||
| 45 | |||
| 46 | /// This should be called after swapping screen buffers. | 31 | /// This should be called after swapping screen buffers. |
| 47 | void BeginFrame(); | 32 | void BeginFrame(); |
| 48 | /// This should be called before swapping screen buffers. | 33 | /// This should be called before swapping screen buffers. |
| @@ -54,7 +39,6 @@ public: | |||
| 54 | } | 39 | } |
| 55 | 40 | ||
| 56 | private: | 41 | private: |
| 57 | std::vector<TimingCategoryInfo> timing_categories; | ||
| 58 | Clock::time_point last_frame_end; | 42 | Clock::time_point last_frame_end; |
| 59 | Clock::time_point this_frame_start; | 43 | Clock::time_point this_frame_start; |
| 60 | 44 | ||
| @@ -73,9 +57,6 @@ struct AggregatedFrameResult { | |||
| 73 | AggregatedDuration frame_time; | 57 | AggregatedDuration frame_time; |
| 74 | 58 | ||
| 75 | float fps; | 59 | float fps; |
| 76 | |||
| 77 | /// Total amount of time spent inside each category in this frame. Indexed by the category id | ||
| 78 | std::vector<AggregatedDuration> time_per_category; | ||
| 79 | }; | 60 | }; |
| 80 | 61 | ||
| 81 | class TimingResultsAggregator final { | 62 | class TimingResultsAggregator final { |
| @@ -83,7 +64,6 @@ public: | |||
| 83 | TimingResultsAggregator(size_t window_size); | 64 | TimingResultsAggregator(size_t window_size); |
| 84 | 65 | ||
| 85 | void Clear(); | 66 | void Clear(); |
| 86 | void SetNumberOfCategories(size_t n); | ||
| 87 | 67 | ||
| 88 | void AddFrame(const ProfilingFrameResult& frame_result); | 68 | void AddFrame(const ProfilingFrameResult& frame_result); |
| 89 | 69 | ||
| @@ -95,7 +75,6 @@ public: | |||
| 95 | 75 | ||
| 96 | std::vector<Duration> interframe_times; | 76 | std::vector<Duration> interframe_times; |
| 97 | std::vector<Duration> frame_times; | 77 | std::vector<Duration> frame_times; |
| 98 | std::vector<std::vector<Duration>> times_per_category; | ||
| 99 | }; | 78 | }; |
| 100 | 79 | ||
| 101 | ProfilingManager& GetProfilingManager(); | 80 | ProfilingManager& GetProfilingManager(); |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 647784208..8d4b26815 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/microprofile.h" | 12 | #include "common/microprofile.h" |
| 13 | #include "common/profiler.h" | ||
| 14 | 13 | ||
| 15 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 16 | #include "core/hle/svc.h" | 15 | #include "core/hle/svc.h" |
| @@ -25,9 +24,6 @@ | |||
| 25 | 24 | ||
| 26 | #include "core/gdbstub/gdbstub.h" | 25 | #include "core/gdbstub/gdbstub.h" |
| 27 | 26 | ||
| 28 | Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); | ||
| 29 | Common::Profiling::TimingCategory profile_decode("DynCom::Decode"); | ||
| 30 | |||
| 31 | enum { | 27 | enum { |
| 32 | COND = (1 << 0), | 28 | COND = (1 << 0), |
| 33 | NON_BRANCH = (1 << 1), | 29 | NON_BRANCH = (1 << 1), |
| @@ -3496,7 +3492,6 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons | |||
| 3496 | } | 3492 | } |
| 3497 | 3493 | ||
| 3498 | static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { | 3494 | static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { |
| 3499 | Common::Profiling::ScopeTimer timer_decode(profile_decode); | ||
| 3500 | MICROPROFILE_SCOPE(DynCom_Decode); | 3495 | MICROPROFILE_SCOPE(DynCom_Decode); |
| 3501 | 3496 | ||
| 3502 | // Decode instruction, get index | 3497 | // Decode instruction, get index |
| @@ -3530,7 +3525,6 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) | |||
| 3530 | } | 3525 | } |
| 3531 | 3526 | ||
| 3532 | static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { | 3527 | static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { |
| 3533 | Common::Profiling::ScopeTimer timer_decode(profile_decode); | ||
| 3534 | MICROPROFILE_SCOPE(DynCom_Decode); | 3528 | MICROPROFILE_SCOPE(DynCom_Decode); |
| 3535 | 3529 | ||
| 3536 | ARM_INST_PTR inst_base = nullptr; | 3530 | ARM_INST_PTR inst_base = nullptr; |
| @@ -3565,7 +3559,6 @@ static int clz(unsigned int x) { | |||
| 3565 | MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0)); | 3559 | MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0)); |
| 3566 | 3560 | ||
| 3567 | unsigned InterpreterMainLoop(ARMul_State* cpu) { | 3561 | unsigned InterpreterMainLoop(ARMul_State* cpu) { |
| 3568 | Common::Profiling::ScopeTimer timer_execute(profile_execute); | ||
| 3569 | MICROPROFILE_SCOPE(DynCom_Execute); | 3562 | MICROPROFILE_SCOPE(DynCom_Execute); |
| 3570 | 3563 | ||
| 3571 | GDBStub::BreakpointAddress breakpoint_data; | 3564 | GDBStub::BreakpointAddress breakpoint_data; |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 2d22652d9..53931a106 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | /// Detailed description of the error. This listing is likely incomplete. | 18 | /// Detailed description of the error. This listing is likely incomplete. |
| 19 | enum class ErrorDescription : u32 { | 19 | enum class ErrorDescription : u32 { |
| 20 | Success = 0, | 20 | Success = 0, |
| 21 | OS_InvalidBufferDescriptor = 48, | ||
| 21 | WrongAddress = 53, | 22 | WrongAddress = 53, |
| 22 | FS_NotFound = 120, | 23 | FS_NotFound = 120, |
| 23 | FS_AlreadyExists = 190, | 24 | FS_AlreadyExists = 190, |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 9591522e5..3f71e7f2b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -43,7 +43,7 @@ void FindContentInfos(Service::Interface* self) { | |||
| 43 | am_content_count[media_type] = cmd_buff[4]; | 43 | am_content_count[media_type] = cmd_buff[4]; |
| 44 | 44 | ||
| 45 | cmd_buff[1] = RESULT_SUCCESS.raw; | 45 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 46 | LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016lx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x", | 46 | LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016llx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x", |
| 47 | media_type, title_id, am_content_count[media_type], content_ids_pointer, content_info_pointer); | 47 | media_type, title_id, am_content_count[media_type], content_ids_pointer, content_info_pointer); |
| 48 | } | 48 | } |
| 49 | 49 | ||
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 08e437125..995bee3f9 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <cinttypes> | 6 | #include <cinttypes> |
| 6 | 7 | ||
| 7 | #include "audio_core/hle/pipe.h" | 8 | #include "audio_core/hle/pipe.h" |
| @@ -12,37 +13,80 @@ | |||
| 12 | #include "core/hle/kernel/event.h" | 13 | #include "core/hle/kernel/event.h" |
| 13 | #include "core/hle/service/dsp_dsp.h" | 14 | #include "core/hle/service/dsp_dsp.h" |
| 14 | 15 | ||
| 16 | using DspPipe = DSP::HLE::DspPipe; | ||
| 17 | |||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 16 | // Namespace DSP_DSP | 19 | // Namespace DSP_DSP |
| 17 | 20 | ||
| 18 | namespace DSP_DSP { | 21 | namespace DSP_DSP { |
| 19 | 22 | ||
| 20 | static u32 read_pipe_count; | ||
| 21 | static Kernel::SharedPtr<Kernel::Event> semaphore_event; | 23 | static Kernel::SharedPtr<Kernel::Event> semaphore_event; |
| 22 | 24 | ||
| 23 | struct PairHash { | 25 | /// There are three types of interrupts |
| 24 | template <typename T, typename U> | 26 | enum class InterruptType { |
| 25 | std::size_t operator()(const std::pair<T, U> &x) const { | 27 | Zero, One, Pipe |
| 26 | // TODO(yuriks): Replace with better hash combining function. | 28 | }; |
| 27 | return std::hash<T>()(x.first) ^ std::hash<U>()(x.second); | 29 | constexpr size_t NUM_INTERRUPT_TYPE = 3; |
| 30 | |||
| 31 | class InterruptEvents final { | ||
| 32 | public: | ||
| 33 | void Signal(InterruptType type, DspPipe pipe) { | ||
| 34 | Kernel::SharedPtr<Kernel::Event>& event = Get(type, pipe); | ||
| 35 | if (event) { | ||
| 36 | event->Signal(); | ||
| 37 | } | ||
| 28 | } | 38 | } |
| 39 | |||
| 40 | Kernel::SharedPtr<Kernel::Event>& Get(InterruptType type, DspPipe dsp_pipe) { | ||
| 41 | switch (type) { | ||
| 42 | case InterruptType::Zero: | ||
| 43 | return zero; | ||
| 44 | case InterruptType::One: | ||
| 45 | return one; | ||
| 46 | case InterruptType::Pipe: { | ||
| 47 | const size_t pipe_index = static_cast<size_t>(dsp_pipe); | ||
| 48 | ASSERT(pipe_index < DSP::HLE::NUM_DSP_PIPE); | ||
| 49 | return pipe[pipe_index]; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | UNREACHABLE_MSG("Invalid interrupt type = %zu", static_cast<size_t>(type)); | ||
| 54 | } | ||
| 55 | |||
| 56 | bool HasTooManyEventsRegistered() const { | ||
| 57 | // Actual service implementation only has 6 'slots' for interrupts. | ||
| 58 | constexpr size_t max_number_of_interrupt_events = 6; | ||
| 59 | |||
| 60 | size_t number = std::count_if(pipe.begin(), pipe.end(), [](const auto& evt) { | ||
| 61 | return evt != nullptr; | ||
| 62 | }); | ||
| 63 | |||
| 64 | if (zero != nullptr) | ||
| 65 | number++; | ||
| 66 | if (one != nullptr) | ||
| 67 | number++; | ||
| 68 | |||
| 69 | return number >= max_number_of_interrupt_events; | ||
| 70 | } | ||
| 71 | |||
| 72 | private: | ||
| 73 | /// Currently unknown purpose | ||
| 74 | Kernel::SharedPtr<Kernel::Event> zero = nullptr; | ||
| 75 | /// Currently unknown purpose | ||
| 76 | Kernel::SharedPtr<Kernel::Event> one = nullptr; | ||
| 77 | /// Each DSP pipe has an associated interrupt | ||
| 78 | std::array<Kernel::SharedPtr<Kernel::Event>, DSP::HLE::NUM_DSP_PIPE> pipe = {{}}; | ||
| 29 | }; | 79 | }; |
| 30 | 80 | ||
| 31 | /// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents | 81 | static InterruptEvents interrupt_events; |
| 32 | static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events; | ||
| 33 | 82 | ||
| 34 | // DSP Interrupts: | 83 | // DSP Interrupts: |
| 35 | // Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting | 84 | // The audio-pipe interrupt occurs every frame tick. Userland programs normally have a thread |
| 36 | // for an interrupt event. Immediately after this interrupt event, userland normally updates the | 85 | // that's waiting for an interrupt event. Immediately after this interrupt event, userland |
| 37 | // state in the next region and increments the relevant frame counter by two. | 86 | // normally updates the state in the next region and increments the relevant frame counter by |
| 38 | void SignalAllInterrupts() { | 87 | // two. |
| 39 | // HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case. | 88 | void SignalPipeInterrupt(DspPipe pipe) { |
| 40 | for (auto& interrupt_event : interrupt_events) | 89 | interrupt_events.Signal(InterruptType::Pipe, pipe); |
| 41 | interrupt_event.second->Signal(); | ||
| 42 | } | ||
| 43 | |||
| 44 | void SignalInterrupt(u32 interrupt, u32 channel) { | ||
| 45 | interrupt_events[std::make_pair(interrupt, channel)]->Signal(); | ||
| 46 | } | 90 | } |
| 47 | 91 | ||
| 48 | /** | 92 | /** |
| @@ -58,7 +102,10 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) { | |||
| 58 | 102 | ||
| 59 | u32 addr = cmd_buff[1]; | 103 | u32 addr = cmd_buff[1]; |
| 60 | 104 | ||
| 105 | cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); | ||
| 61 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 106 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 107 | |||
| 108 | // TODO(merry): There is a per-region offset missing in this calculation (that seems to be always zero). | ||
| 62 | cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); | 109 | cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); |
| 63 | 110 | ||
| 64 | LOG_DEBUG(Service_DSP, "addr=0x%08X", addr); | 111 | LOG_DEBUG(Service_DSP, "addr=0x%08X", addr); |
| @@ -113,7 +160,9 @@ static void LoadComponent(Service::Interface* self) { | |||
| 113 | static void GetSemaphoreEventHandle(Service::Interface* self) { | 160 | static void GetSemaphoreEventHandle(Service::Interface* self) { |
| 114 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 161 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 115 | 162 | ||
| 163 | cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); | ||
| 116 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 164 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 165 | // cmd_buff[2] not set | ||
| 117 | cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle | 166 | cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle |
| 118 | 167 | ||
| 119 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | 168 | LOG_WARNING(Service_DSP, "(STUBBED) called"); |
| @@ -138,8 +187,7 @@ static void FlushDataCache(Service::Interface* self) { | |||
| 138 | u32 size = cmd_buff[2]; | 187 | u32 size = cmd_buff[2]; |
| 139 | u32 process = cmd_buff[4]; | 188 | u32 process = cmd_buff[4]; |
| 140 | 189 | ||
| 141 | // TODO(purpasmart96): Verify return header on HW | 190 | cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); |
| 142 | |||
| 143 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 191 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 144 | 192 | ||
| 145 | LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, process); | 193 | LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, process); |
| @@ -148,8 +196,8 @@ static void FlushDataCache(Service::Interface* self) { | |||
| 148 | /** | 196 | /** |
| 149 | * DSP_DSP::RegisterInterruptEvents service function | 197 | * DSP_DSP::RegisterInterruptEvents service function |
| 150 | * Inputs: | 198 | * Inputs: |
| 151 | * 1 : Interrupt Number | 199 | * 1 : Interrupt Type |
| 152 | * 2 : Channel Number | 200 | * 2 : Pipe Number |
| 153 | * 4 : Interrupt event handle | 201 | * 4 : Interrupt event handle |
| 154 | * Outputs: | 202 | * Outputs: |
| 155 | * 1 : Result of function, 0 on success, otherwise error code | 203 | * 1 : Result of function, 0 on success, otherwise error code |
| @@ -157,23 +205,40 @@ static void FlushDataCache(Service::Interface* self) { | |||
| 157 | static void RegisterInterruptEvents(Service::Interface* self) { | 205 | static void RegisterInterruptEvents(Service::Interface* self) { |
| 158 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 206 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 159 | 207 | ||
| 160 | u32 interrupt = cmd_buff[1]; | 208 | u32 type_index = cmd_buff[1]; |
| 161 | u32 channel = cmd_buff[2]; | 209 | u32 pipe_index = cmd_buff[2]; |
| 162 | u32 event_handle = cmd_buff[4]; | 210 | u32 event_handle = cmd_buff[4]; |
| 163 | 211 | ||
| 212 | ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < DSP::HLE::NUM_DSP_PIPE, | ||
| 213 | "Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index); | ||
| 214 | |||
| 215 | InterruptType type = static_cast<InterruptType>(cmd_buff[1]); | ||
| 216 | DspPipe pipe = static_cast<DspPipe>(cmd_buff[2]); | ||
| 217 | |||
| 218 | cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); | ||
| 219 | |||
| 164 | if (event_handle) { | 220 | if (event_handle) { |
| 165 | auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); | 221 | auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); |
| 166 | if (evt) { | 222 | |
| 167 | interrupt_events[std::make_pair(interrupt, channel)] = evt; | 223 | if (!evt) { |
| 168 | cmd_buff[1] = RESULT_SUCCESS.raw; | 224 | LOG_INFO(Service_DSP, "Invalid event handle! type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); |
| 169 | LOG_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); | 225 | ASSERT(false); // TODO: This should really be handled at an IPC translation layer. |
| 170 | } else { | 226 | } |
| 171 | LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); | 227 | |
| 172 | ASSERT(false); // This should really be handled at a IPC translation layer. | 228 | if (interrupt_events.HasTooManyEventsRegistered()) { |
| 229 | LOG_INFO(Service_DSP, "Ran out of space to register interrupts (Attempted to register type=%u, pipe=%u, event_handle=0x%08X)", | ||
| 230 | type_index, pipe_index, event_handle); | ||
| 231 | cmd_buff[1] = ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::DSP, ErrorSummary::OutOfResource, ErrorLevel::Status).raw; | ||
| 232 | return; | ||
| 173 | } | 233 | } |
| 234 | |||
| 235 | interrupt_events.Get(type, pipe) = evt; | ||
| 236 | LOG_INFO(Service_DSP, "Registered type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); | ||
| 237 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 174 | } else { | 238 | } else { |
| 175 | interrupt_events.erase(std::make_pair(interrupt, channel)); | 239 | interrupt_events.Get(type, pipe) = nullptr; |
| 176 | LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); | 240 | LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); |
| 241 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 177 | } | 242 | } |
| 178 | } | 243 | } |
| 179 | 244 | ||
| @@ -187,6 +252,7 @@ static void RegisterInterruptEvents(Service::Interface* self) { | |||
| 187 | static void SetSemaphore(Service::Interface* self) { | 252 | static void SetSemaphore(Service::Interface* self) { |
| 188 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 253 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 189 | 254 | ||
| 255 | cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); | ||
| 190 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 256 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 191 | 257 | ||
| 192 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | 258 | LOG_WARNING(Service_DSP, "(STUBBED) called"); |
| @@ -195,7 +261,7 @@ static void SetSemaphore(Service::Interface* self) { | |||
| 195 | /** | 261 | /** |
| 196 | * DSP_DSP::WriteProcessPipe service function | 262 | * DSP_DSP::WriteProcessPipe service function |
| 197 | * Inputs: | 263 | * Inputs: |
| 198 | * 1 : Channel | 264 | * 1 : Pipe Number |
| 199 | * 2 : Size | 265 | * 2 : Size |
| 200 | * 3 : (size << 14) | 0x402 | 266 | * 3 : (size << 14) | 0x402 |
| 201 | * 4 : Buffer | 267 | * 4 : Buffer |
| @@ -206,24 +272,32 @@ static void SetSemaphore(Service::Interface* self) { | |||
| 206 | static void WriteProcessPipe(Service::Interface* self) { | 272 | static void WriteProcessPipe(Service::Interface* self) { |
| 207 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 273 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 208 | 274 | ||
| 209 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); | 275 | u32 pipe_index = cmd_buff[1]; |
| 210 | u32 size = cmd_buff[2]; | 276 | u32 size = cmd_buff[2]; |
| 211 | u32 buffer = cmd_buff[4]; | 277 | u32 buffer = cmd_buff[4]; |
| 212 | 278 | ||
| 213 | 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); | 279 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); |
| 214 | ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); | ||
| 215 | 280 | ||
| 216 | std::vector<u8> message(size); | 281 | if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) { |
| 282 | LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe_index, size, buffer); | ||
| 283 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 284 | cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; | ||
| 285 | return; | ||
| 286 | } | ||
| 287 | |||
| 288 | ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); | ||
| 217 | 289 | ||
| 290 | std::vector<u8> message(size); | ||
| 218 | for (size_t i = 0; i < size; i++) { | 291 | for (size_t i = 0; i < size; i++) { |
| 219 | message[i] = Memory::Read8(buffer + i); | 292 | message[i] = Memory::Read8(buffer + i); |
| 220 | } | 293 | } |
| 221 | 294 | ||
| 222 | DSP::HLE::PipeWrite(pipe, message); | 295 | DSP::HLE::PipeWrite(pipe, message); |
| 223 | 296 | ||
| 297 | cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); | ||
| 224 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 298 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 225 | 299 | ||
| 226 | LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); | 300 | LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); |
| 227 | } | 301 | } |
| 228 | 302 | ||
| 229 | /** | 303 | /** |
| @@ -243,13 +317,16 @@ static void WriteProcessPipe(Service::Interface* self) { | |||
| 243 | static void ReadPipeIfPossible(Service::Interface* self) { | 317 | static void ReadPipeIfPossible(Service::Interface* self) { |
| 244 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 318 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 245 | 319 | ||
| 246 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); | 320 | u32 pipe_index = cmd_buff[1]; |
| 247 | u32 unknown = cmd_buff[2]; | 321 | u32 unknown = cmd_buff[2]; |
| 248 | u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size | 322 | u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size |
| 249 | VAddr addr = cmd_buff[0x41]; | 323 | VAddr addr = cmd_buff[0x41]; |
| 250 | 324 | ||
| 251 | ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); | 325 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); |
| 326 | |||
| 327 | ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr); | ||
| 252 | 328 | ||
| 329 | cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); | ||
| 253 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 330 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 254 | if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { | 331 | if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { |
| 255 | std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); | 332 | std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); |
| @@ -260,8 +337,10 @@ static void ReadPipeIfPossible(Service::Interface* self) { | |||
| 260 | } else { | 337 | } else { |
| 261 | cmd_buff[2] = 0; // Return no data | 338 | cmd_buff[2] = 0; // Return no data |
| 262 | } | 339 | } |
| 340 | cmd_buff[3] = IPC::StaticBufferDesc(size, 0); | ||
| 341 | cmd_buff[4] = addr; | ||
| 263 | 342 | ||
| 264 | 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]); | 343 | LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]); |
| 265 | } | 344 | } |
| 266 | 345 | ||
| 267 | /** | 346 | /** |
| @@ -278,26 +357,31 @@ static void ReadPipeIfPossible(Service::Interface* self) { | |||
| 278 | static void ReadPipe(Service::Interface* self) { | 357 | static void ReadPipe(Service::Interface* self) { |
| 279 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 358 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 280 | 359 | ||
| 281 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); | 360 | u32 pipe_index = cmd_buff[1]; |
| 282 | u32 unknown = cmd_buff[2]; | 361 | u32 unknown = cmd_buff[2]; |
| 283 | u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size | 362 | u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size |
| 284 | VAddr addr = cmd_buff[0x41]; | 363 | VAddr addr = cmd_buff[0x41]; |
| 285 | 364 | ||
| 286 | ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); | 365 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); |
| 366 | |||
| 367 | ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr); | ||
| 287 | 368 | ||
| 288 | if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { | 369 | if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { |
| 289 | std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); | 370 | std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); |
| 290 | 371 | ||
| 291 | Memory::WriteBlock(addr, response.data(), response.size()); | 372 | Memory::WriteBlock(addr, response.data(), response.size()); |
| 292 | 373 | ||
| 374 | cmd_buff[0] = IPC::MakeHeader(0xE, 2, 2); | ||
| 293 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 375 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 294 | cmd_buff[2] = static_cast<u32>(response.size()); | 376 | cmd_buff[2] = static_cast<u32>(response.size()); |
| 377 | cmd_buff[3] = IPC::StaticBufferDesc(size, 0); | ||
| 378 | cmd_buff[4] = addr; | ||
| 295 | } else { | 379 | } else { |
| 296 | // No more data is in pipe. Hardware hangs in this case; this should never happen. | 380 | // No more data is in pipe. Hardware hangs in this case; this should never happen. |
| 297 | UNREACHABLE(); | 381 | UNREACHABLE(); |
| 298 | } | 382 | } |
| 299 | 383 | ||
| 300 | 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]); | 384 | LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]); |
| 301 | } | 385 | } |
| 302 | 386 | ||
| 303 | /** | 387 | /** |
| @@ -312,13 +396,16 @@ static void ReadPipe(Service::Interface* self) { | |||
| 312 | static void GetPipeReadableSize(Service::Interface* self) { | 396 | static void GetPipeReadableSize(Service::Interface* self) { |
| 313 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 397 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 314 | 398 | ||
| 315 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); | 399 | u32 pipe_index = cmd_buff[1]; |
| 316 | u32 unknown = cmd_buff[2]; | 400 | u32 unknown = cmd_buff[2]; |
| 317 | 401 | ||
| 402 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); | ||
| 403 | |||
| 404 | cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); | ||
| 318 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 405 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 319 | cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe); | 406 | cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe); |
| 320 | 407 | ||
| 321 | LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, cmd_buff[2]); | 408 | LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, cmd_buff[2]); |
| 322 | } | 409 | } |
| 323 | 410 | ||
| 324 | /** | 411 | /** |
| @@ -333,6 +420,7 @@ static void SetSemaphoreMask(Service::Interface* self) { | |||
| 333 | 420 | ||
| 334 | u32 mask = cmd_buff[1]; | 421 | u32 mask = cmd_buff[1]; |
| 335 | 422 | ||
| 423 | cmd_buff[0] = IPC::MakeHeader(0x17, 1, 0); | ||
| 336 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 424 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 337 | 425 | ||
| 338 | LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask); | 426 | LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask); |
| @@ -350,6 +438,7 @@ static void SetSemaphoreMask(Service::Interface* self) { | |||
| 350 | static void GetHeadphoneStatus(Service::Interface* self) { | 438 | static void GetHeadphoneStatus(Service::Interface* self) { |
| 351 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 439 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 352 | 440 | ||
| 441 | cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0); | ||
| 353 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 442 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 354 | cmd_buff[2] = 0; // Not using headphones? | 443 | cmd_buff[2] = 0; // Not using headphones? |
| 355 | 444 | ||
| @@ -376,6 +465,7 @@ static void RecvData(Service::Interface* self) { | |||
| 376 | 465 | ||
| 377 | // Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept. | 466 | // Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept. |
| 378 | 467 | ||
| 468 | cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0); | ||
| 379 | cmd_buff[1] = RESULT_SUCCESS.raw; | 469 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 380 | switch (DSP::HLE::GetDspState()) { | 470 | switch (DSP::HLE::GetDspState()) { |
| 381 | case DSP::HLE::DspState::On: | 471 | case DSP::HLE::DspState::On: |
| @@ -411,6 +501,7 @@ static void RecvDataIsReady(Service::Interface* self) { | |||
| 411 | 501 | ||
| 412 | ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); | 502 | ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); |
| 413 | 503 | ||
| 504 | cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); | ||
| 414 | cmd_buff[1] = RESULT_SUCCESS.raw; | 505 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 415 | cmd_buff[2] = 1; // Ready to read | 506 | cmd_buff[2] = 1; // Ready to read |
| 416 | 507 | ||
| @@ -458,14 +549,14 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 458 | 549 | ||
| 459 | Interface::Interface() { | 550 | Interface::Interface() { |
| 460 | semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); | 551 | semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); |
| 461 | read_pipe_count = 0; | 552 | interrupt_events = {}; |
| 462 | 553 | ||
| 463 | Register(FunctionTable); | 554 | Register(FunctionTable); |
| 464 | } | 555 | } |
| 465 | 556 | ||
| 466 | Interface::~Interface() { | 557 | Interface::~Interface() { |
| 467 | semaphore_event = nullptr; | 558 | semaphore_event = nullptr; |
| 468 | interrupt_events.clear(); | 559 | interrupt_events = {}; |
| 469 | } | 560 | } |
| 470 | 561 | ||
| 471 | } // namespace | 562 | } // namespace |
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index 32b89e9bb..22f6687cc 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h | |||
| @@ -8,6 +8,12 @@ | |||
| 8 | 8 | ||
| 9 | #include "core/hle/service/service.h" | 9 | #include "core/hle/service/service.h" |
| 10 | 10 | ||
| 11 | namespace DSP { | ||
| 12 | namespace HLE { | ||
| 13 | enum class DspPipe; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 12 | // Namespace DSP_DSP | 18 | // Namespace DSP_DSP |
| 13 | 19 | ||
| @@ -23,15 +29,10 @@ public: | |||
| 23 | } | 29 | } |
| 24 | }; | 30 | }; |
| 25 | 31 | ||
| 26 | /// Signal all audio related interrupts. | ||
| 27 | void SignalAllInterrupts(); | ||
| 28 | |||
| 29 | /** | 32 | /** |
| 30 | * Signal a specific audio related interrupt based on interrupt id and channel id. | 33 | * Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe. |
| 31 | * @param interrupt_id The interrupt id | 34 | * @param pipe The DSP pipe for which to signal an interrupt for. |
| 32 | * @param channel_id The channel id | ||
| 33 | * The significance of various values of interrupt_id and channel_id is not yet known. | ||
| 34 | */ | 35 | */ |
| 35 | void SignalInterrupt(u32 interrupt_id, u32 channel_id); | 36 | void SignalPipeInterrupt(DSP::HLE::DspPipe pipe); |
| 36 | 37 | ||
| 37 | } // namespace | 38 | } // namespace DSP_DSP |
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 211fcf599..233592d7f 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/bit_field.h" | 5 | #include "common/bit_field.h" |
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "common/profiler.h" | ||
| 8 | 7 | ||
| 9 | #include "core/memory.h" | 8 | #include "core/memory.h" |
| 10 | #include "core/hle/kernel/event.h" | 9 | #include "core/hle/kernel/event.h" |
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 1672ad775..d16578f87 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | 6 | ||
| 7 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 9 | 10 | ||
| @@ -25,13 +26,17 @@ struct ConversionParameters { | |||
| 25 | u16 input_line_width; | 26 | u16 input_line_width; |
| 26 | u16 input_lines; | 27 | u16 input_lines; |
| 27 | StandardCoefficient standard_coefficient; | 28 | StandardCoefficient standard_coefficient; |
| 28 | u8 reserved; | 29 | u8 padding; |
| 29 | u16 alpha; | 30 | u16 alpha; |
| 30 | }; | 31 | }; |
| 31 | static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size"); | 32 | static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size"); |
| 32 | 33 | ||
| 33 | static Kernel::SharedPtr<Kernel::Event> completion_event; | 34 | static Kernel::SharedPtr<Kernel::Event> completion_event; |
| 34 | static ConversionConfiguration conversion; | 35 | static ConversionConfiguration conversion; |
| 36 | static DitheringWeightParams dithering_weight_params; | ||
| 37 | static u32 temporal_dithering_enabled = 0; | ||
| 38 | static u32 transfer_end_interrupt_enabled = 0; | ||
| 39 | static u32 spacial_dithering_enabled = 0; | ||
| 35 | 40 | ||
| 36 | static const CoefficientSet standard_coefficients[4] = { | 41 | static const CoefficientSet standard_coefficients[4] = { |
| 37 | {{ 0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B }}, // ITU_Rec601 | 42 | {{ 0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B }}, // ITU_Rec601 |
| @@ -70,7 +75,7 @@ ResultCode ConversionConfiguration::SetInputLines(u16 lines) { | |||
| 70 | 75 | ||
| 71 | ResultCode ConversionConfiguration::SetStandardCoefficient(StandardCoefficient standard_coefficient) { | 76 | ResultCode ConversionConfiguration::SetStandardCoefficient(StandardCoefficient standard_coefficient) { |
| 72 | size_t index = static_cast<size_t>(standard_coefficient); | 77 | size_t index = static_cast<size_t>(standard_coefficient); |
| 73 | if (index >= 4) { | 78 | if (index >= ARRAY_SIZE(standard_coefficients)) { |
| 74 | return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, | 79 | return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, |
| 75 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED | 80 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED |
| 76 | } | 81 | } |
| @@ -83,44 +88,183 @@ static void SetInputFormat(Service::Interface* self) { | |||
| 83 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 88 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 84 | 89 | ||
| 85 | conversion.input_format = static_cast<InputFormat>(cmd_buff[1]); | 90 | conversion.input_format = static_cast<InputFormat>(cmd_buff[1]); |
| 91 | |||
| 92 | cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); | ||
| 93 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 94 | |||
| 86 | LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); | 95 | LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); |
| 96 | } | ||
| 87 | 97 | ||
| 98 | static void GetInputFormat(Service::Interface* self) { | ||
| 99 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 100 | |||
| 101 | cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); | ||
| 88 | cmd_buff[1] = RESULT_SUCCESS.raw; | 102 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 103 | cmd_buff[2] = static_cast<u32>(conversion.input_format); | ||
| 104 | |||
| 105 | LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); | ||
| 89 | } | 106 | } |
| 90 | 107 | ||
| 91 | static void SetOutputFormat(Service::Interface* self) { | 108 | static void SetOutputFormat(Service::Interface* self) { |
| 92 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 109 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 93 | 110 | ||
| 94 | conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]); | 111 | conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]); |
| 112 | |||
| 113 | cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); | ||
| 114 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 115 | |||
| 95 | LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); | 116 | LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); |
| 117 | } | ||
| 118 | |||
| 119 | static void GetOutputFormat(Service::Interface* self) { | ||
| 120 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 96 | 121 | ||
| 122 | cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0); | ||
| 97 | cmd_buff[1] = RESULT_SUCCESS.raw; | 123 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 124 | cmd_buff[2] = static_cast<u32>(conversion.output_format); | ||
| 125 | |||
| 126 | LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); | ||
| 98 | } | 127 | } |
| 99 | 128 | ||
| 100 | static void SetRotation(Service::Interface* self) { | 129 | static void SetRotation(Service::Interface* self) { |
| 101 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 130 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 102 | 131 | ||
| 103 | conversion.rotation = static_cast<Rotation>(cmd_buff[1]); | 132 | conversion.rotation = static_cast<Rotation>(cmd_buff[1]); |
| 133 | |||
| 134 | cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); | ||
| 135 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 136 | |||
| 104 | LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); | 137 | LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); |
| 138 | } | ||
| 139 | |||
| 140 | static void GetRotation(Service::Interface* self) { | ||
| 141 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 105 | 142 | ||
| 143 | cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0); | ||
| 106 | cmd_buff[1] = RESULT_SUCCESS.raw; | 144 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 145 | cmd_buff[2] = static_cast<u32>(conversion.rotation); | ||
| 146 | |||
| 147 | LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); | ||
| 107 | } | 148 | } |
| 108 | 149 | ||
| 109 | static void SetBlockAlignment(Service::Interface* self) { | 150 | static void SetBlockAlignment(Service::Interface* self) { |
| 110 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 151 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 111 | 152 | ||
| 112 | conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]); | 153 | conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]); |
| 113 | LOG_DEBUG(Service_Y2R, "called alignment=%hhu", conversion.block_alignment); | ||
| 114 | 154 | ||
| 155 | cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); | ||
| 156 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 157 | |||
| 158 | LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); | ||
| 159 | } | ||
| 160 | |||
| 161 | static void GetBlockAlignment(Service::Interface* self) { | ||
| 162 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 163 | |||
| 164 | cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); | ||
| 165 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 166 | cmd_buff[2] = static_cast<u32>(conversion.block_alignment); | ||
| 167 | |||
| 168 | LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 172 | * Y2R_U::SetSpacialDithering service function | ||
| 173 | * Inputs: | ||
| 174 | * 1 : u8, 0 = Disabled, 1 = Enabled | ||
| 175 | * Outputs: | ||
| 176 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 177 | */ | ||
| 178 | static void SetSpacialDithering(Service::Interface* self) { | ||
| 179 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 180 | spacial_dithering_enabled = cmd_buff[1] & 0xF; | ||
| 181 | |||
| 182 | cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); | ||
| 183 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 184 | |||
| 185 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 186 | } | ||
| 187 | |||
| 188 | /** | ||
| 189 | * Y2R_U::GetSpacialDithering service function | ||
| 190 | * Outputs: | ||
| 191 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 192 | * 2 : u8, 0 = Disabled, 1 = Enabled | ||
| 193 | */ | ||
| 194 | static void GetSpacialDithering(Service::Interface* self) { | ||
| 195 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 196 | |||
| 197 | cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); | ||
| 198 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 199 | cmd_buff[2] = spacial_dithering_enabled; | ||
| 200 | |||
| 201 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 202 | } | ||
| 203 | |||
| 204 | /** | ||
| 205 | * Y2R_U::SetTemporalDithering service function | ||
| 206 | * Inputs: | ||
| 207 | * 1 : u8, 0 = Disabled, 1 = Enabled | ||
| 208 | * Outputs: | ||
| 209 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 210 | */ | ||
| 211 | static void SetTemporalDithering(Service::Interface* self) { | ||
| 212 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 213 | temporal_dithering_enabled = cmd_buff[1] & 0xF; | ||
| 214 | |||
| 215 | cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); | ||
| 216 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 217 | |||
| 218 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * Y2R_U::GetTemporalDithering service function | ||
| 223 | * Outputs: | ||
| 224 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 225 | * 2 : u8, 0 = Disabled, 1 = Enabled | ||
| 226 | */ | ||
| 227 | static void GetTemporalDithering(Service::Interface* self) { | ||
| 228 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 229 | |||
| 230 | cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); | ||
| 115 | cmd_buff[1] = RESULT_SUCCESS.raw; | 231 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 232 | cmd_buff[2] = temporal_dithering_enabled; | ||
| 233 | |||
| 234 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 116 | } | 235 | } |
| 117 | 236 | ||
| 237 | /** | ||
| 238 | * Y2R_U::SetTransferEndInterrupt service function | ||
| 239 | * Inputs: | ||
| 240 | * 1 : u8, 0 = Disabled, 1 = Enabled | ||
| 241 | * Outputs: | ||
| 242 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 243 | */ | ||
| 118 | static void SetTransferEndInterrupt(Service::Interface* self) { | 244 | static void SetTransferEndInterrupt(Service::Interface* self) { |
| 119 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 245 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 246 | transfer_end_interrupt_enabled = cmd_buff[1] & 0xf; | ||
| 120 | 247 | ||
| 121 | cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); | 248 | cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); |
| 122 | cmd_buff[1] = RESULT_SUCCESS.raw; | 249 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 123 | LOG_DEBUG(Service_Y2R, "(STUBBED) called"); | 250 | |
| 251 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 252 | } | ||
| 253 | |||
| 254 | /** | ||
| 255 | * Y2R_U::GetTransferEndInterrupt service function | ||
| 256 | * Outputs: | ||
| 257 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 258 | * 2 : u8, 0 = Disabled, 1 = Enabled | ||
| 259 | */ | ||
| 260 | static void GetTransferEndInterrupt(Service::Interface* self) { | ||
| 261 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 262 | |||
| 263 | cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0); | ||
| 264 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 265 | cmd_buff[2] = transfer_end_interrupt_enabled; | ||
| 266 | |||
| 267 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 124 | } | 268 | } |
| 125 | 269 | ||
| 126 | /** | 270 | /** |
| @@ -132,8 +276,10 @@ static void SetTransferEndInterrupt(Service::Interface* self) { | |||
| 132 | static void GetTransferEndEvent(Service::Interface* self) { | 276 | static void GetTransferEndEvent(Service::Interface* self) { |
| 133 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 277 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 134 | 278 | ||
| 279 | cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); | ||
| 135 | cmd_buff[1] = RESULT_SUCCESS.raw; | 280 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 136 | cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); | 281 | cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); |
| 282 | |||
| 137 | LOG_DEBUG(Service_Y2R, "called"); | 283 | LOG_DEBUG(Service_Y2R, "called"); |
| 138 | } | 284 | } |
| 139 | 285 | ||
| @@ -144,12 +290,12 @@ static void SetSendingY(Service::Interface* self) { | |||
| 144 | conversion.src_Y.image_size = cmd_buff[2]; | 290 | conversion.src_Y.image_size = cmd_buff[2]; |
| 145 | conversion.src_Y.transfer_unit = cmd_buff[3]; | 291 | conversion.src_Y.transfer_unit = cmd_buff[3]; |
| 146 | conversion.src_Y.gap = cmd_buff[4]; | 292 | conversion.src_Y.gap = cmd_buff[4]; |
| 147 | u32 src_process_handle = cmd_buff[6]; | ||
| 148 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||
| 149 | "src_process_handle=0x%08X", conversion.src_Y.image_size, | ||
| 150 | conversion.src_Y.transfer_unit, conversion.src_Y.gap, src_process_handle); | ||
| 151 | 293 | ||
| 294 | cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); | ||
| 152 | cmd_buff[1] = RESULT_SUCCESS.raw; | 295 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 296 | |||
| 297 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", | ||
| 298 | conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap, cmd_buff[6]); | ||
| 153 | } | 299 | } |
| 154 | 300 | ||
| 155 | static void SetSendingU(Service::Interface* self) { | 301 | static void SetSendingU(Service::Interface* self) { |
| @@ -159,12 +305,12 @@ static void SetSendingU(Service::Interface* self) { | |||
| 159 | conversion.src_U.image_size = cmd_buff[2]; | 305 | conversion.src_U.image_size = cmd_buff[2]; |
| 160 | conversion.src_U.transfer_unit = cmd_buff[3]; | 306 | conversion.src_U.transfer_unit = cmd_buff[3]; |
| 161 | conversion.src_U.gap = cmd_buff[4]; | 307 | conversion.src_U.gap = cmd_buff[4]; |
| 162 | u32 src_process_handle = cmd_buff[6]; | ||
| 163 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||
| 164 | "src_process_handle=0x%08X", conversion.src_U.image_size, | ||
| 165 | conversion.src_U.transfer_unit, conversion.src_U.gap, src_process_handle); | ||
| 166 | 308 | ||
| 309 | cmd_buff[0] = IPC::MakeHeader(0x11, 1, 0); | ||
| 167 | cmd_buff[1] = RESULT_SUCCESS.raw; | 310 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 311 | |||
| 312 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", | ||
| 313 | conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap, cmd_buff[6]); | ||
| 168 | } | 314 | } |
| 169 | 315 | ||
| 170 | static void SetSendingV(Service::Interface* self) { | 316 | static void SetSendingV(Service::Interface* self) { |
| @@ -174,12 +320,12 @@ static void SetSendingV(Service::Interface* self) { | |||
| 174 | conversion.src_V.image_size = cmd_buff[2]; | 320 | conversion.src_V.image_size = cmd_buff[2]; |
| 175 | conversion.src_V.transfer_unit = cmd_buff[3]; | 321 | conversion.src_V.transfer_unit = cmd_buff[3]; |
| 176 | conversion.src_V.gap = cmd_buff[4]; | 322 | conversion.src_V.gap = cmd_buff[4]; |
| 177 | u32 src_process_handle = cmd_buff[6]; | ||
| 178 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||
| 179 | "src_process_handle=0x%08X", conversion.src_V.image_size, | ||
| 180 | conversion.src_V.transfer_unit, conversion.src_V.gap, src_process_handle); | ||
| 181 | 323 | ||
| 324 | cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); | ||
| 182 | cmd_buff[1] = RESULT_SUCCESS.raw; | 325 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 326 | |||
| 327 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", | ||
| 328 | conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap, cmd_buff[6]); | ||
| 183 | } | 329 | } |
| 184 | 330 | ||
| 185 | static void SetSendingYUYV(Service::Interface* self) { | 331 | static void SetSendingYUYV(Service::Interface* self) { |
| @@ -189,12 +335,76 @@ static void SetSendingYUYV(Service::Interface* self) { | |||
| 189 | conversion.src_YUYV.image_size = cmd_buff[2]; | 335 | conversion.src_YUYV.image_size = cmd_buff[2]; |
| 190 | conversion.src_YUYV.transfer_unit = cmd_buff[3]; | 336 | conversion.src_YUYV.transfer_unit = cmd_buff[3]; |
| 191 | conversion.src_YUYV.gap = cmd_buff[4]; | 337 | conversion.src_YUYV.gap = cmd_buff[4]; |
| 192 | u32 src_process_handle = cmd_buff[6]; | ||
| 193 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||
| 194 | "src_process_handle=0x%08X", conversion.src_YUYV.image_size, | ||
| 195 | conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, src_process_handle); | ||
| 196 | 338 | ||
| 339 | cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); | ||
| 340 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 341 | |||
| 342 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", | ||
| 343 | conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, cmd_buff[6]); | ||
| 344 | } | ||
| 345 | |||
| 346 | /** | ||
| 347 | * Y2R::IsFinishedSendingYuv service function | ||
| 348 | * Output: | ||
| 349 | * 1 : Result of the function, 0 on success, otherwise error code | ||
| 350 | * 2 : u8, 0 = Not Finished, 1 = Finished | ||
| 351 | */ | ||
| 352 | static void IsFinishedSendingYuv(Service::Interface* self) { | ||
| 353 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 354 | |||
| 355 | cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0); | ||
| 197 | cmd_buff[1] = RESULT_SUCCESS.raw; | 356 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 357 | cmd_buff[2] = 1; | ||
| 358 | |||
| 359 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 360 | } | ||
| 361 | |||
| 362 | /** | ||
| 363 | * Y2R::IsFinishedSendingY service function | ||
| 364 | * Output: | ||
| 365 | * 1 : Result of the function, 0 on success, otherwise error code | ||
| 366 | * 2 : u8, 0 = Not Finished, 1 = Finished | ||
| 367 | */ | ||
| 368 | static void IsFinishedSendingY(Service::Interface* self) { | ||
| 369 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 370 | |||
| 371 | cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0); | ||
| 372 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 373 | cmd_buff[2] = 1; | ||
| 374 | |||
| 375 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 376 | } | ||
| 377 | |||
| 378 | /** | ||
| 379 | * Y2R::IsFinishedSendingU service function | ||
| 380 | * Output: | ||
| 381 | * 1 : Result of the function, 0 on success, otherwise error code | ||
| 382 | * 2 : u8, 0 = Not Finished, 1 = Finished | ||
| 383 | */ | ||
| 384 | static void IsFinishedSendingU(Service::Interface* self) { | ||
| 385 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 386 | |||
| 387 | cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); | ||
| 388 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 389 | cmd_buff[2] = 1; | ||
| 390 | |||
| 391 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 392 | } | ||
| 393 | |||
| 394 | /** | ||
| 395 | * Y2R::IsFinishedSendingV service function | ||
| 396 | * Output: | ||
| 397 | * 1 : Result of the function, 0 on success, otherwise error code | ||
| 398 | * 2 : u8, 0 = Not Finished, 1 = Finished | ||
| 399 | */ | ||
| 400 | static void IsFinishedSendingV(Service::Interface* self) { | ||
| 401 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 402 | |||
| 403 | cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0); | ||
| 404 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 405 | cmd_buff[2] = 1; | ||
| 406 | |||
| 407 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 198 | } | 408 | } |
| 199 | 409 | ||
| 200 | static void SetReceiving(Service::Interface* self) { | 410 | static void SetReceiving(Service::Interface* self) { |
| @@ -204,27 +414,66 @@ static void SetReceiving(Service::Interface* self) { | |||
| 204 | conversion.dst.image_size = cmd_buff[2]; | 414 | conversion.dst.image_size = cmd_buff[2]; |
| 205 | conversion.dst.transfer_unit = cmd_buff[3]; | 415 | conversion.dst.transfer_unit = cmd_buff[3]; |
| 206 | conversion.dst.gap = cmd_buff[4]; | 416 | conversion.dst.gap = cmd_buff[4]; |
| 207 | u32 dst_process_handle = cmd_buff[6]; | ||
| 208 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||
| 209 | "dst_process_handle=0x%08X", conversion.dst.image_size, | ||
| 210 | conversion.dst.transfer_unit, conversion.dst.gap, | ||
| 211 | dst_process_handle); | ||
| 212 | 417 | ||
| 418 | cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0); | ||
| 419 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 420 | |||
| 421 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, dst_process_handle=0x%08X", | ||
| 422 | conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap, cmd_buff[6]); | ||
| 423 | } | ||
| 424 | |||
| 425 | /** | ||
| 426 | * Y2R::IsFinishedReceiving service function | ||
| 427 | * Output: | ||
| 428 | * 1 : Result of the function, 0 on success, otherwise error code | ||
| 429 | * 2 : u8, 0 = Not Finished, 1 = Finished | ||
| 430 | */ | ||
| 431 | static void IsFinishedReceiving(Service::Interface* self) { | ||
| 432 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 433 | |||
| 434 | cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0); | ||
| 213 | cmd_buff[1] = RESULT_SUCCESS.raw; | 435 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 436 | cmd_buff[2] = 1; | ||
| 437 | |||
| 438 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||
| 214 | } | 439 | } |
| 215 | 440 | ||
| 216 | static void SetInputLineWidth(Service::Interface* self) { | 441 | static void SetInputLineWidth(Service::Interface* self) { |
| 217 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 442 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 218 | 443 | ||
| 219 | LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); | 444 | cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0); |
| 220 | cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw; | 445 | cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw; |
| 446 | |||
| 447 | LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); | ||
| 448 | } | ||
| 449 | |||
| 450 | static void GetInputLineWidth(Service::Interface* self) { | ||
| 451 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 452 | |||
| 453 | cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0); | ||
| 454 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 455 | cmd_buff[2] = conversion.input_line_width; | ||
| 456 | |||
| 457 | LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width); | ||
| 221 | } | 458 | } |
| 222 | 459 | ||
| 223 | static void SetInputLines(Service::Interface* self) { | 460 | static void SetInputLines(Service::Interface* self) { |
| 224 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 461 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 225 | 462 | ||
| 226 | LOG_DEBUG(Service_Y2R, "called input_line_number=%u", cmd_buff[1]); | 463 | cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0); |
| 227 | cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw; | 464 | cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw; |
| 465 | |||
| 466 | LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]); | ||
| 467 | } | ||
| 468 | |||
| 469 | static void GetInputLines(Service::Interface* self) { | ||
| 470 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 471 | |||
| 472 | cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0); | ||
| 473 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 474 | cmd_buff[2] = static_cast<u32>(conversion.input_lines); | ||
| 475 | |||
| 476 | LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines); | ||
| 228 | } | 477 | } |
| 229 | 478 | ||
| 230 | static void SetCoefficient(Service::Interface* self) { | 479 | static void SetCoefficient(Service::Interface* self) { |
| @@ -232,44 +481,111 @@ static void SetCoefficient(Service::Interface* self) { | |||
| 232 | 481 | ||
| 233 | const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]); | 482 | const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]); |
| 234 | std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet)); | 483 | std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet)); |
| 484 | |||
| 485 | cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); | ||
| 486 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 487 | |||
| 235 | LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]", | 488 | LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]", |
| 236 | coefficients[0], coefficients[1], coefficients[2], coefficients[3], | 489 | coefficients[0], coefficients[1], coefficients[2], coefficients[3], |
| 237 | coefficients[4], coefficients[5], coefficients[6], coefficients[7]); | 490 | coefficients[4], coefficients[5], coefficients[6], coefficients[7]); |
| 491 | } | ||
| 492 | |||
| 493 | static void GetCoefficient(Service::Interface* self) { | ||
| 494 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 238 | 495 | ||
| 496 | cmd_buff[0] = IPC::MakeHeader(0x1F, 5, 0); | ||
| 239 | cmd_buff[1] = RESULT_SUCCESS.raw; | 497 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 498 | std::memcpy(&cmd_buff[2], conversion.coefficients.data(), sizeof(CoefficientSet)); | ||
| 499 | |||
| 500 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 240 | } | 501 | } |
| 241 | 502 | ||
| 242 | static void SetStandardCoefficient(Service::Interface* self) { | 503 | static void SetStandardCoefficient(Service::Interface* self) { |
| 243 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 504 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 244 | 505 | ||
| 245 | LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", cmd_buff[1]); | 506 | u32 index = cmd_buff[1]; |
| 507 | |||
| 508 | cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); | ||
| 509 | cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw; | ||
| 246 | 510 | ||
| 247 | cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)cmd_buff[1]).raw; | 511 | LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index); |
| 512 | } | ||
| 513 | |||
| 514 | static void GetStandardCoefficient(Service::Interface* self) { | ||
| 515 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 516 | |||
| 517 | u32 index = cmd_buff[1]; | ||
| 518 | |||
| 519 | if (index < ARRAY_SIZE(standard_coefficients)) { | ||
| 520 | cmd_buff[0] = IPC::MakeHeader(0x21, 5, 0); | ||
| 521 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 522 | std::memcpy(&cmd_buff[2], &standard_coefficients[index], sizeof(CoefficientSet)); | ||
| 523 | |||
| 524 | LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u ", index); | ||
| 525 | } else { | ||
| 526 | cmd_buff[0] = IPC::MakeHeader(0x21, 1, 0); | ||
| 527 | cmd_buff[1] = -1; // TODO(bunnei): Identify the correct error code for this | ||
| 528 | |||
| 529 | LOG_ERROR(Service_Y2R, "called standard_coefficient=%u The argument is invalid!", index); | ||
| 530 | } | ||
| 248 | } | 531 | } |
| 249 | 532 | ||
| 250 | static void SetAlpha(Service::Interface* self) { | 533 | static void SetAlpha(Service::Interface* self) { |
| 251 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 534 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 252 | 535 | ||
| 253 | conversion.alpha = cmd_buff[1]; | 536 | conversion.alpha = cmd_buff[1]; |
| 537 | |||
| 538 | cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); | ||
| 539 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 540 | |||
| 541 | LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); | ||
| 542 | } | ||
| 543 | |||
| 544 | static void GetAlpha(Service::Interface* self) { | ||
| 545 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 546 | |||
| 547 | cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0); | ||
| 548 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 549 | cmd_buff[2] = conversion.alpha; | ||
| 550 | |||
| 254 | LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); | 551 | LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); |
| 552 | } | ||
| 553 | |||
| 554 | static void SetDitheringWeightParams(Service::Interface* self) { | ||
| 555 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 556 | std::memcpy(&dithering_weight_params, &cmd_buff[1], sizeof(DitheringWeightParams)); | ||
| 255 | 557 | ||
| 558 | cmd_buff[0] = IPC::MakeHeader(0x24, 1, 0); | ||
| 256 | cmd_buff[1] = RESULT_SUCCESS.raw; | 559 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 560 | |||
| 561 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 562 | } | ||
| 563 | |||
| 564 | static void GetDitheringWeightParams(Service::Interface* self) { | ||
| 565 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 566 | |||
| 567 | cmd_buff[0] = IPC::MakeHeader(0x25, 9, 0); | ||
| 568 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 569 | std::memcpy(&cmd_buff[2], &dithering_weight_params, sizeof(DitheringWeightParams)); | ||
| 570 | |||
| 571 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 257 | } | 572 | } |
| 258 | 573 | ||
| 259 | static void StartConversion(Service::Interface* self) { | 574 | static void StartConversion(Service::Interface* self) { |
| 260 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 575 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 261 | 576 | ||
| 262 | // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( | 577 | // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( |
| 263 | u32 total_output_size = conversion.input_lines * | 578 | u32 total_output_size = conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap); |
| 264 | (conversion.dst.transfer_unit + conversion.dst.gap); | ||
| 265 | Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); | 579 | Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); |
| 266 | 580 | ||
| 267 | HW::Y2R::PerformConversion(conversion); | 581 | HW::Y2R::PerformConversion(conversion); |
| 268 | 582 | ||
| 269 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 270 | completion_event->Signal(); | 583 | completion_event->Signal(); |
| 271 | 584 | ||
| 585 | cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0); | ||
| 272 | cmd_buff[1] = RESULT_SUCCESS.raw; | 586 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 587 | |||
| 588 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 273 | } | 589 | } |
| 274 | 590 | ||
| 275 | static void StopConversion(Service::Interface* self) { | 591 | static void StopConversion(Service::Interface* self) { |
| @@ -277,6 +593,7 @@ static void StopConversion(Service::Interface* self) { | |||
| 277 | 593 | ||
| 278 | cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0); | 594 | cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0); |
| 279 | cmd_buff[1] = RESULT_SUCCESS.raw; | 595 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 596 | |||
| 280 | LOG_DEBUG(Service_Y2R, "called"); | 597 | LOG_DEBUG(Service_Y2R, "called"); |
| 281 | } | 598 | } |
| 282 | 599 | ||
| @@ -289,50 +606,61 @@ static void StopConversion(Service::Interface* self) { | |||
| 289 | static void IsBusyConversion(Service::Interface* self) { | 606 | static void IsBusyConversion(Service::Interface* self) { |
| 290 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 607 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 291 | 608 | ||
| 609 | cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0); | ||
| 292 | cmd_buff[1] = RESULT_SUCCESS.raw; | 610 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 293 | cmd_buff[2] = 0; // StartConversion always finishes immediately | 611 | cmd_buff[2] = 0; // StartConversion always finishes immediately |
| 612 | |||
| 294 | LOG_DEBUG(Service_Y2R, "called"); | 613 | LOG_DEBUG(Service_Y2R, "called"); |
| 295 | } | 614 | } |
| 296 | 615 | ||
| 297 | /** | 616 | /** |
| 298 | * Y2R_U::SetConversionParams service function | 617 | * Y2R_U::SetPackageParameter service function |
| 299 | */ | 618 | */ |
| 300 | static void SetConversionParams(Service::Interface* self) { | 619 | static void SetPackageParameter(Service::Interface* self) { |
| 301 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 620 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 302 | 621 | ||
| 303 | auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]); | 622 | auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]); |
| 304 | LOG_DEBUG(Service_Y2R, | ||
| 305 | "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " | ||
| 306 | "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu " | ||
| 307 | "reserved=%hhu alpha=%hX", | ||
| 308 | params->input_format, params->output_format, params->rotation, params->block_alignment, | ||
| 309 | params->input_line_width, params->input_lines, params->standard_coefficient, | ||
| 310 | params->reserved, params->alpha); | ||
| 311 | |||
| 312 | ResultCode result = RESULT_SUCCESS; | ||
| 313 | 623 | ||
| 314 | conversion.input_format = params->input_format; | 624 | conversion.input_format = params->input_format; |
| 315 | conversion.output_format = params->output_format; | 625 | conversion.output_format = params->output_format; |
| 316 | conversion.rotation = params->rotation; | 626 | conversion.rotation = params->rotation; |
| 317 | conversion.block_alignment = params->block_alignment; | 627 | conversion.block_alignment = params->block_alignment; |
| 318 | result = conversion.SetInputLineWidth(params->input_line_width); | 628 | |
| 319 | if (result.IsError()) goto cleanup; | 629 | ResultCode result = conversion.SetInputLineWidth(params->input_line_width); |
| 630 | |||
| 631 | if (result.IsError()) | ||
| 632 | goto cleanup; | ||
| 633 | |||
| 320 | result = conversion.SetInputLines(params->input_lines); | 634 | result = conversion.SetInputLines(params->input_lines); |
| 321 | if (result.IsError()) goto cleanup; | 635 | |
| 636 | if (result.IsError()) | ||
| 637 | goto cleanup; | ||
| 638 | |||
| 322 | result = conversion.SetStandardCoefficient(params->standard_coefficient); | 639 | result = conversion.SetStandardCoefficient(params->standard_coefficient); |
| 323 | if (result.IsError()) goto cleanup; | 640 | |
| 641 | if (result.IsError()) | ||
| 642 | goto cleanup; | ||
| 643 | |||
| 644 | conversion.padding = params->padding; | ||
| 324 | conversion.alpha = params->alpha; | 645 | conversion.alpha = params->alpha; |
| 325 | 646 | ||
| 326 | cleanup: | 647 | cleanup: |
| 327 | cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); | 648 | cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); |
| 328 | cmd_buff[1] = result.raw; | 649 | cmd_buff[1] = result.raw; |
| 650 | |||
| 651 | LOG_DEBUG(Service_Y2R, "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " | ||
| 652 | "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX", | ||
| 653 | params->input_format, params->output_format, params->rotation, params->block_alignment, | ||
| 654 | params->input_line_width, params->input_lines, params->standard_coefficient, params->padding, params->alpha); | ||
| 329 | } | 655 | } |
| 330 | 656 | ||
| 331 | static void PingProcess(Service::Interface* self) { | 657 | static void PingProcess(Service::Interface* self) { |
| 332 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 658 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 333 | 659 | ||
| 660 | cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0); | ||
| 334 | cmd_buff[1] = RESULT_SUCCESS.raw; | 661 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 335 | cmd_buff[2] = 0; | 662 | cmd_buff[2] = 0; |
| 663 | |||
| 336 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | 664 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); |
| 337 | } | 665 | } |
| 338 | 666 | ||
| @@ -358,6 +686,7 @@ static void DriverInitialize(Service::Interface* self) { | |||
| 358 | 686 | ||
| 359 | cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0); | 687 | cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0); |
| 360 | cmd_buff[1] = RESULT_SUCCESS.raw; | 688 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 689 | |||
| 361 | LOG_DEBUG(Service_Y2R, "called"); | 690 | LOG_DEBUG(Service_Y2R, "called"); |
| 362 | } | 691 | } |
| 363 | 692 | ||
| @@ -366,54 +695,67 @@ static void DriverFinalize(Service::Interface* self) { | |||
| 366 | 695 | ||
| 367 | cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0); | 696 | cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0); |
| 368 | cmd_buff[1] = RESULT_SUCCESS.raw; | 697 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 698 | |||
| 699 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 700 | } | ||
| 701 | |||
| 702 | |||
| 703 | static void GetPackageParameter(Service::Interface* self) { | ||
| 704 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 705 | |||
| 706 | cmd_buff[0] = IPC::MakeHeader(0x2D, 4, 0); | ||
| 707 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 708 | std::memcpy(&cmd_buff[2], &conversion, sizeof(ConversionParameters)); | ||
| 709 | |||
| 369 | LOG_DEBUG(Service_Y2R, "called"); | 710 | LOG_DEBUG(Service_Y2R, "called"); |
| 370 | } | 711 | } |
| 371 | 712 | ||
| 372 | const Interface::FunctionInfo FunctionTable[] = { | 713 | const Interface::FunctionInfo FunctionTable[] = { |
| 373 | {0x00010040, SetInputFormat, "SetInputFormat"}, | 714 | {0x00010040, SetInputFormat, "SetInputFormat"}, |
| 374 | {0x00020000, nullptr, "GetInputFormat"}, | 715 | {0x00020000, GetInputFormat, "GetInputFormat"}, |
| 375 | {0x00030040, SetOutputFormat, "SetOutputFormat"}, | 716 | {0x00030040, SetOutputFormat, "SetOutputFormat"}, |
| 376 | {0x00040000, nullptr, "GetOutputFormat"}, | 717 | {0x00040000, GetOutputFormat, "GetOutputFormat"}, |
| 377 | {0x00050040, SetRotation, "SetRotation"}, | 718 | {0x00050040, SetRotation, "SetRotation"}, |
| 378 | {0x00060000, nullptr, "GetRotation"}, | 719 | {0x00060000, GetRotation, "GetRotation"}, |
| 379 | {0x00070040, SetBlockAlignment, "SetBlockAlignment"}, | 720 | {0x00070040, SetBlockAlignment, "SetBlockAlignment"}, |
| 380 | {0x00080000, nullptr, "GetBlockAlignment"}, | 721 | {0x00080000, GetBlockAlignment, "GetBlockAlignment"}, |
| 381 | {0x00090040, nullptr, "SetSpacialDithering"}, | 722 | {0x00090040, SetSpacialDithering, "SetSpacialDithering"}, |
| 382 | {0x000A0000, nullptr, "GetSpacialDithering"}, | 723 | {0x000A0000, GetSpacialDithering, "GetSpacialDithering"}, |
| 383 | {0x000B0040, nullptr, "SetTemporalDithering"}, | 724 | {0x000B0040, SetTemporalDithering, "SetTemporalDithering"}, |
| 384 | {0x000C0000, nullptr, "GetTemporalDithering"}, | 725 | {0x000C0000, GetTemporalDithering, "GetTemporalDithering"}, |
| 385 | {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"}, | 726 | {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"}, |
| 727 | {0x000E0000, GetTransferEndInterrupt, "GetTransferEndInterrupt"}, | ||
| 386 | {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, | 728 | {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, |
| 387 | {0x00100102, SetSendingY, "SetSendingY"}, | 729 | {0x00100102, SetSendingY, "SetSendingY"}, |
| 388 | {0x00110102, SetSendingU, "SetSendingU"}, | 730 | {0x00110102, SetSendingU, "SetSendingU"}, |
| 389 | {0x00120102, SetSendingV, "SetSendingV"}, | 731 | {0x00120102, SetSendingV, "SetSendingV"}, |
| 390 | {0x00130102, SetSendingYUYV, "SetSendingYUYV"}, | 732 | {0x00130102, SetSendingYUYV, "SetSendingYUYV"}, |
| 391 | {0x00140000, nullptr, "IsFinishedSendingYuv"}, | 733 | {0x00140000, IsFinishedSendingYuv, "IsFinishedSendingYuv"}, |
| 392 | {0x00150000, nullptr, "IsFinishedSendingY"}, | 734 | {0x00150000, IsFinishedSendingY, "IsFinishedSendingY"}, |
| 393 | {0x00160000, nullptr, "IsFinishedSendingU"}, | 735 | {0x00160000, IsFinishedSendingU, "IsFinishedSendingU"}, |
| 394 | {0x00170000, nullptr, "IsFinishedSendingV"}, | 736 | {0x00170000, IsFinishedSendingV, "IsFinishedSendingV"}, |
| 395 | {0x00180102, SetReceiving, "SetReceiving"}, | 737 | {0x00180102, SetReceiving, "SetReceiving"}, |
| 396 | {0x00190000, nullptr, "IsFinishedReceiving"}, | 738 | {0x00190000, IsFinishedReceiving, "IsFinishedReceiving"}, |
| 397 | {0x001A0040, SetInputLineWidth, "SetInputLineWidth"}, | 739 | {0x001A0040, SetInputLineWidth, "SetInputLineWidth"}, |
| 398 | {0x001B0000, nullptr, "GetInputLineWidth"}, | 740 | {0x001B0000, GetInputLineWidth, "GetInputLineWidth"}, |
| 399 | {0x001C0040, SetInputLines, "SetInputLines"}, | 741 | {0x001C0040, SetInputLines, "SetInputLines"}, |
| 400 | {0x001D0000, nullptr, "GetInputLines"}, | 742 | {0x001D0000, GetInputLines, "GetInputLines"}, |
| 401 | {0x001E0100, SetCoefficient, "SetCoefficient"}, | 743 | {0x001E0100, SetCoefficient, "SetCoefficient"}, |
| 402 | {0x001F0000, nullptr, "GetCoefficient"}, | 744 | {0x001F0000, GetCoefficient, "GetCoefficient"}, |
| 403 | {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"}, | 745 | {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"}, |
| 404 | {0x00210040, nullptr, "GetStandardCoefficientParams"}, | 746 | {0x00210040, GetStandardCoefficient, "GetStandardCoefficient"}, |
| 405 | {0x00220040, SetAlpha, "SetAlpha"}, | 747 | {0x00220040, SetAlpha, "SetAlpha"}, |
| 406 | {0x00230000, nullptr, "GetAlpha"}, | 748 | {0x00230000, GetAlpha, "GetAlpha"}, |
| 407 | {0x00240200, nullptr, "SetDitheringWeightParams"}, | 749 | {0x00240200, SetDitheringWeightParams,"SetDitheringWeightParams"}, |
| 408 | {0x00250000, nullptr, "GetDitheringWeightParams"}, | 750 | {0x00250000, GetDitheringWeightParams,"GetDitheringWeightParams"}, |
| 409 | {0x00260000, StartConversion, "StartConversion"}, | 751 | {0x00260000, StartConversion, "StartConversion"}, |
| 410 | {0x00270000, StopConversion, "StopConversion"}, | 752 | {0x00270000, StopConversion, "StopConversion"}, |
| 411 | {0x00280000, IsBusyConversion, "IsBusyConversion"}, | 753 | {0x00280000, IsBusyConversion, "IsBusyConversion"}, |
| 412 | {0x002901C0, SetConversionParams, "SetConversionParams"}, | 754 | {0x002901C0, SetPackageParameter, "SetPackageParameter"}, |
| 413 | {0x002A0000, PingProcess, "PingProcess"}, | 755 | {0x002A0000, PingProcess, "PingProcess"}, |
| 414 | {0x002B0000, DriverInitialize, "DriverInitialize"}, | 756 | {0x002B0000, DriverInitialize, "DriverInitialize"}, |
| 415 | {0x002C0000, DriverFinalize, "DriverFinalize"}, | 757 | {0x002C0000, DriverFinalize, "DriverFinalize"}, |
| 416 | {0x002D0000, nullptr, "GetPackageParameter"}, | 758 | {0x002D0000, GetPackageParameter, "GetPackageParameter"}, |
| 417 | }; | 759 | }; |
| 418 | 760 | ||
| 419 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 761 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 3965a5545..95fa2fdb7 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h | |||
| @@ -97,6 +97,7 @@ struct ConversionConfiguration { | |||
| 97 | u16 input_line_width; | 97 | u16 input_line_width; |
| 98 | u16 input_lines; | 98 | u16 input_lines; |
| 99 | CoefficientSet coefficients; | 99 | CoefficientSet coefficients; |
| 100 | u8 padding; | ||
| 100 | u16 alpha; | 101 | u16 alpha; |
| 101 | 102 | ||
| 102 | /// Input parameters for the Y (luma) plane | 103 | /// Input parameters for the Y (luma) plane |
| @@ -109,6 +110,25 @@ struct ConversionConfiguration { | |||
| 109 | ResultCode SetStandardCoefficient(StandardCoefficient standard_coefficient); | 110 | ResultCode SetStandardCoefficient(StandardCoefficient standard_coefficient); |
| 110 | }; | 111 | }; |
| 111 | 112 | ||
| 113 | struct DitheringWeightParams { | ||
| 114 | u16 w0_xEven_yEven; | ||
| 115 | u16 w0_xOdd_yEven; | ||
| 116 | u16 w0_xEven_yOdd; | ||
| 117 | u16 w0_xOdd_yOdd; | ||
| 118 | u16 w1_xEven_yEven; | ||
| 119 | u16 w1_xOdd_yEven; | ||
| 120 | u16 w1_xEven_yOdd; | ||
| 121 | u16 w1_xOdd_yOdd; | ||
| 122 | u16 w2_xEven_yEven; | ||
| 123 | u16 w2_xOdd_yEven; | ||
| 124 | u16 w2_xEven_yOdd; | ||
| 125 | u16 w2_xOdd_yOdd; | ||
| 126 | u16 w3_xEven_yEven; | ||
| 127 | u16 w3_xOdd_yEven; | ||
| 128 | u16 w3_xEven_yOdd; | ||
| 129 | u16 w3_xOdd_yOdd; | ||
| 130 | }; | ||
| 131 | |||
| 112 | class Interface : public Service::Interface { | 132 | class Interface : public Service::Interface { |
| 113 | public: | 133 | public: |
| 114 | Interface(); | 134 | Interface(); |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index ae54afb1c..a9a1a3244 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/microprofile.h" | 8 | #include "common/microprofile.h" |
| 9 | #include "common/profiler.h" | ||
| 10 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 11 | #include "common/symbols.h" | 10 | #include "common/symbols.h" |
| 12 | 11 | ||
| @@ -1031,8 +1030,6 @@ static const FunctionDef SVC_Table[] = { | |||
| 1031 | {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, | 1030 | {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, |
| 1032 | }; | 1031 | }; |
| 1033 | 1032 | ||
| 1034 | Common::Profiling::TimingCategory profiler_svc("SVC Calls"); | ||
| 1035 | |||
| 1036 | static const FunctionDef* GetSVCInfo(u32 func_num) { | 1033 | static const FunctionDef* GetSVCInfo(u32 func_num) { |
| 1037 | if (func_num >= ARRAY_SIZE(SVC_Table)) { | 1034 | if (func_num >= ARRAY_SIZE(SVC_Table)) { |
| 1038 | LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); | 1035 | LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); |
| @@ -1044,7 +1041,6 @@ static const FunctionDef* GetSVCInfo(u32 func_num) { | |||
| 1044 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | 1041 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); |
| 1045 | 1042 | ||
| 1046 | void CallSVC(u32 immediate) { | 1043 | void CallSVC(u32 immediate) { |
| 1047 | Common::Profiling::ScopeTimer timer_svc(profiler_svc); | ||
| 1048 | MICROPROFILE_SCOPE(Kernel_SVC); | 1044 | MICROPROFILE_SCOPE(Kernel_SVC); |
| 1049 | 1045 | ||
| 1050 | const FunctionDef* info = GetSVCInfo(immediate); | 1046 | const FunctionDef* info = GetSVCInfo(immediate); |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index f181f75d7..58883e374 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/alignment.h" | 8 | #include "common/alignment.h" |
| 9 | #include "common/microprofile.h" | 9 | #include "common/microprofile.h" |
| 10 | #include "common/profiler.h" | ||
| 11 | 10 | ||
| 12 | #include "core/settings.h" | 11 | #include "core/settings.h" |
| 13 | #include "core/hle/service/gsp_gpu.h" | 12 | #include "core/hle/service/gsp_gpu.h" |
| @@ -36,8 +35,6 @@ static int default_attr_counter = 0; | |||
| 36 | 35 | ||
| 37 | static u32 default_attr_write_buffer[3]; | 36 | static u32 default_attr_write_buffer[3]; |
| 38 | 37 | ||
| 39 | Common::Profiling::TimingCategory category_drawing("Drawing"); | ||
| 40 | |||
| 41 | // Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF | 38 | // Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF |
| 42 | static const u32 expand_bits_to_bytes[] = { | 39 | static const u32 expand_bits_to_bytes[] = { |
| 43 | 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, | 40 | 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, |
| @@ -187,7 +184,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 187 | case PICA_REG_INDEX(trigger_draw): | 184 | case PICA_REG_INDEX(trigger_draw): |
| 188 | case PICA_REG_INDEX(trigger_draw_indexed): | 185 | case PICA_REG_INDEX(trigger_draw_indexed): |
| 189 | { | 186 | { |
| 190 | Common::Profiling::ScopeTimer scope_timer(category_drawing); | ||
| 191 | MICROPROFILE_SCOPE(GPU_Drawing); | 187 | MICROPROFILE_SCOPE(GPU_Drawing); |
| 192 | 188 | ||
| 193 | #if PICA_LOG_TEV | 189 | #if PICA_LOG_TEV |
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 1f058c4e2..178a566f7 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -40,10 +40,7 @@ using nihstro::DVLPHeader; | |||
| 40 | 40 | ||
| 41 | namespace Pica { | 41 | namespace Pica { |
| 42 | 42 | ||
| 43 | void DebugContext::OnEvent(Event event, void* data) { | 43 | void DebugContext::DoOnEvent(Event event, void* data) { |
| 44 | if (!breakpoints[event].enabled) | ||
| 45 | return; | ||
| 46 | |||
| 47 | { | 44 | { |
| 48 | std::unique_lock<std::mutex> lock(breakpoint_mutex); | 45 | std::unique_lock<std::mutex> lock(breakpoint_mutex); |
| 49 | 46 | ||
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 8338cc857..dd0828cee 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h | |||
| @@ -114,7 +114,15 @@ public: | |||
| 114 | * @param event Event which has happened | 114 | * @param event Event which has happened |
| 115 | * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called. | 115 | * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called. |
| 116 | */ | 116 | */ |
| 117 | void OnEvent(Event event, void* data); | 117 | void OnEvent(Event event, void* data) { |
| 118 | // This check is left in the header to allow the compiler to inline it. | ||
| 119 | if (!breakpoints[(int)event].enabled) | ||
| 120 | return; | ||
| 121 | // For the rest of event handling, call a separate function. | ||
| 122 | DoOnEvent(event, data); | ||
| 123 | } | ||
| 124 | |||
| 125 | void DoOnEvent(Event event, void *data); | ||
| 118 | 126 | ||
| 119 | /** | 127 | /** |
| 120 | * Resume from the current breakpoint. | 128 | * Resume from the current breakpoint. |
| @@ -126,12 +134,14 @@ public: | |||
| 126 | * Delete all set breakpoints and resume emulation. | 134 | * Delete all set breakpoints and resume emulation. |
| 127 | */ | 135 | */ |
| 128 | void ClearBreakpoints() { | 136 | void ClearBreakpoints() { |
| 129 | breakpoints.clear(); | 137 | for (auto &bp : breakpoints) { |
| 138 | bp.enabled = false; | ||
| 139 | } | ||
| 130 | Resume(); | 140 | Resume(); |
| 131 | } | 141 | } |
| 132 | 142 | ||
| 133 | // TODO: Evaluate if access to these members should be hidden behind a public interface. | 143 | // TODO: Evaluate if access to these members should be hidden behind a public interface. |
| 134 | std::map<Event, BreakPoint> breakpoints; | 144 | std::array<BreakPoint, (int)Event::NumEvents> breakpoints; |
| 135 | Event active_breakpoint; | 145 | Event active_breakpoint; |
| 136 | bool at_breakpoint = false; | 146 | bool at_breakpoint = false; |
| 137 | 147 | ||
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 0434ad05a..9cf77b1f2 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/math_util.h" | 10 | #include "common/math_util.h" |
| 11 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "common/profiler.h" | ||
| 13 | 12 | ||
| 14 | #include "core/memory.h" | 13 | #include "core/memory.h" |
| 15 | #include "core/hw/gpu.h" | 14 | #include "core/hw/gpu.h" |
| @@ -287,7 +286,6 @@ static int SignedArea (const Math::Vec2<Fix12P4>& vtx1, | |||
| 287 | return Math::Cross(vec1, vec2).z; | 286 | return Math::Cross(vec1, vec2).z; |
| 288 | }; | 287 | }; |
| 289 | 288 | ||
| 290 | static Common::Profiling::TimingCategory rasterization_category("Rasterization"); | ||
| 291 | MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240)); | 289 | MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240)); |
| 292 | 290 | ||
| 293 | /** | 291 | /** |
| @@ -300,7 +298,6 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, | |||
| 300 | bool reversed = false) | 298 | bool reversed = false) |
| 301 | { | 299 | { |
| 302 | const auto& regs = g_state.regs; | 300 | const auto& regs = g_state.regs; |
| 303 | Common::Profiling::ScopeTimer timer(rasterization_category); | ||
| 304 | MICROPROFILE_SCOPE(GPU_Rasterization); | 301 | MICROPROFILE_SCOPE(GPU_Rasterization); |
| 305 | 302 | ||
| 306 | // vertex positions in rasterizer coordinates | 303 | // vertex positions in rasterizer coordinates |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 30187d4cf..a8c775c80 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include "common/file_util.h" | 11 | #include "common/file_util.h" |
| 12 | #include "common/math_util.h" | 12 | #include "common/math_util.h" |
| 13 | #include "common/microprofile.h" | 13 | #include "common/microprofile.h" |
| 14 | #include "common/profiler.h" | ||
| 15 | 14 | ||
| 16 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 17 | #include "core/settings.h" | 16 | #include "core/settings.h" |
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index 75301accd..043e99190 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/hash.h" | 10 | #include "common/hash.h" |
| 11 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "common/profiler.h" | ||
| 13 | 12 | ||
| 14 | #include "video_core/debug_utils/debug_utils.h" | 13 | #include "video_core/debug_utils/debug_utils.h" |
| 15 | #include "video_core/pica.h" | 14 | #include "video_core/pica.h" |
| @@ -57,13 +56,11 @@ void Shutdown() { | |||
| 57 | #endif // ARCHITECTURE_x86_64 | 56 | #endif // ARCHITECTURE_x86_64 |
| 58 | } | 57 | } |
| 59 | 58 | ||
| 60 | static Common::Profiling::TimingCategory shader_category("Vertex Shader"); | ||
| 61 | MICROPROFILE_DEFINE(GPU_VertexShader, "GPU", "Vertex Shader", MP_RGB(50, 50, 240)); | 59 | MICROPROFILE_DEFINE(GPU_VertexShader, "GPU", "Vertex Shader", MP_RGB(50, 50, 240)); |
| 62 | 60 | ||
| 63 | OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attributes) { | 61 | OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attributes) { |
| 64 | auto& config = g_state.regs.vs; | 62 | auto& config = g_state.regs.vs; |
| 65 | 63 | ||
| 66 | Common::Profiling::ScopeTimer timer(shader_category); | ||
| 67 | MICROPROFILE_SCOPE(GPU_VertexShader); | 64 | MICROPROFILE_SCOPE(GPU_VertexShader); |
| 68 | 65 | ||
| 69 | state.program_counter = config.main_offset; | 66 | state.program_counter = config.main_offset; |
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index b47d3beda..b7747fa42 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp | |||
| @@ -148,7 +148,7 @@ static Instruction GetVertexShaderInstruction(size_t offset) { | |||
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | static void LogCritical(const char* msg) { | 150 | static void LogCritical(const char* msg) { |
| 151 | LOG_CRITICAL(HW_GPU, msg); | 151 | LOG_CRITICAL(HW_GPU, "%s", msg); |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | void JitShader::Compile_Assert(bool condition, const char* msg) { | 154 | void JitShader::Compile_Assert(bool condition, const char* msg) { |
| @@ -795,6 +795,8 @@ void JitShader::FindReturnOffsets() { | |||
| 795 | case OpCode::Id::CALLU: | 795 | case OpCode::Id::CALLU: |
| 796 | return_offsets.push_back(instr.flow_control.dest_offset + instr.flow_control.num_instructions); | 796 | return_offsets.push_back(instr.flow_control.dest_offset + instr.flow_control.num_instructions); |
| 797 | break; | 797 | break; |
| 798 | default: | ||
| 799 | break; | ||
| 798 | } | 800 | } |
| 799 | } | 801 | } |
| 800 | 802 | ||
| @@ -854,7 +856,7 @@ void JitShader::Compile() { | |||
| 854 | uintptr_t size = reinterpret_cast<uintptr_t>(GetCodePtr()) - reinterpret_cast<uintptr_t>(program); | 856 | uintptr_t size = reinterpret_cast<uintptr_t>(GetCodePtr()) - reinterpret_cast<uintptr_t>(program); |
| 855 | ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!"); | 857 | ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!"); |
| 856 | 858 | ||
| 857 | LOG_DEBUG(HW_GPU, "Compiled shader size=%d", size); | 859 | LOG_DEBUG(HW_GPU, "Compiled shader size=%lu", size); |
| 858 | } | 860 | } |
| 859 | 861 | ||
| 860 | JitShader::JitShader() { | 862 | JitShader::JitShader() { |