diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/core.cpp | 8 | ||||
-rw-r--r-- | src/core/core.h | 7 | ||||
-rw-r--r-- | src/core/file_sys/archive_extsavedata.h | 2 | ||||
-rw-r--r-- | src/core/frontend/camera/factory.h | 4 | ||||
-rw-r--r-- | src/core/frontend/emu_window.cpp | 5 | ||||
-rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 1 | ||||
-rw-r--r-- | src/core/hle/ipc.h | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/server_session.h | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 1 | ||||
-rw-r--r-- | src/core/hle/kernel/timer.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/cam/cam.h | 2 | ||||
-rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 8 | ||||
-rw-r--r-- | src/core/hle/service/ldr_ro/cro_helper.h | 4 | ||||
-rw-r--r-- | src/core/hle/service/ldr_ro/ldr_ro.cpp | 1 | ||||
-rw-r--r-- | src/core/hw/gpu.cpp | 41 | ||||
-rw-r--r-- | src/core/hw/gpu.h | 2 | ||||
-rw-r--r-- | src/core/loader/loader.h | 2 | ||||
-rw-r--r-- | src/core/perf_stats.cpp | 105 | ||||
-rw-r--r-- | src/core/perf_stats.h | 83 |
20 files changed, 229 insertions, 59 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8334fece9..ffd67f074 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -173,6 +173,7 @@ set(SRCS loader/smdh.cpp tracer/recorder.cpp memory.cpp + perf_stats.cpp settings.cpp ) @@ -363,6 +364,7 @@ set(HEADERS memory.h memory_setup.h mmio.h + perf_stats.h settings.h ) diff --git a/src/core/core.cpp b/src/core/core.cpp index c9c9b7615..140ff6451 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -109,6 +109,10 @@ void System::PrepareReschedule() { reschedule_pending = true; } +PerfStats::Results System::GetAndResetPerfStats() { + return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); +} + void System::Reschedule() { if (!reschedule_pending) { return; @@ -140,6 +144,10 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { LOG_DEBUG(Core, "Initialized OK"); + // Reset counters and set time origin to current frame + GetAndResetPerfStats(); + perf_stats.BeginSystemFrame(); + return ResultStatus::Success; } diff --git a/src/core/core.h b/src/core/core.h index 17572a74f..6c9c936b5 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -6,9 +6,9 @@ #include <memory> #include <string> - #include "common/common_types.h" #include "core/memory.h" +#include "core/perf_stats.h" class EmuWindow; class ARM_Interface; @@ -83,6 +83,8 @@ public: /// Prepare the core emulation for a reschedule void PrepareReschedule(); + PerfStats::Results GetAndResetPerfStats(); + /** * Gets a reference to the emulated CPU. * @returns A reference to the emulated CPU. @@ -91,6 +93,9 @@ public: return *cpu_core; } + PerfStats perf_stats; + FrameLimiter frame_limiter; + private: /** * Initialize the emulated system. diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index 6a3431e94..f705ade1c 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h @@ -52,7 +52,7 @@ private: /** * This holds the full directory path for this archive, it is only set after a successful call - * to Open, this is formed as <base extsavedatapath>/<type>/<high>/<low>. + * to Open, this is formed as `<base extsavedatapath>/<type>/<high>/<low>`. * See GetExtSaveDataPath for the code that extracts this data from an archive path. */ std::string mount_point; diff --git a/src/core/frontend/camera/factory.h b/src/core/frontend/camera/factory.h index d68be16e5..f46413fa7 100644 --- a/src/core/frontend/camera/factory.h +++ b/src/core/frontend/camera/factory.h @@ -16,8 +16,8 @@ public: /** * Creates a camera object based on the configuration string. - * @params config Configuration string to create the camera. The implementation can decide the - * meaning of this string. + * @param config Configuration string to create the camera. The implementation can decide the + * meaning of this string. * @returns a unique_ptr to the created camera object. */ virtual std::unique_ptr<CameraInterface> Create(const std::string& config) const = 0; diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 6b4637741..a155b657d 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -5,7 +5,7 @@ #include <algorithm> #include <cmath> #include "common/assert.h" -#include "common/profiler_reporting.h" +#include "core/core.h" #include "core/frontend/emu_window.h" #include "core/frontend/key_map.h" #include "video_core/video_core.h" @@ -104,8 +104,7 @@ void EmuWindow::AccelerometerChanged(float x, float y, float z) { void EmuWindow::GyroscopeChanged(float x, float y, float z) { constexpr float FULL_FPS = 60; float coef = GetGyroscopeRawToDpsCoefficient(); - float stretch = - FULL_FPS / Common::Profiling::GetTimingResultsAggregator()->GetAggregatedResults().fps; + float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); std::lock_guard<std::mutex> lock(gyro_mutex); gyro_x = static_cast<s16>(x * coef * stretch); gyro_y = static_cast<s16>(y * coef * stretch); diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 5cf45ada5..123fe7cd4 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -230,6 +230,7 @@ static void GdbHexToMem(u8* dest, const u8* src, size_t len) { * Convert a u32 into a gdb-formatted hex string. * * @param dest Pointer to buffer to store output hex string characters. + * @param v Value to convert. */ static void IntToGdbHex(u8* dest, u32 v) { for (int i = 0; i < 8; i += 2) { diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index bbaae8b79..cd9a5863d 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -72,8 +72,8 @@ union Header { * through modifications and checks by the kernel. * The translate parameters are described by headers generated with the IPC::*Desc functions. * - * @note While #normal_params_size is equivalent to the number of normal parameters, - * #translate_params_size includes the size occupied by the translate parameters headers. + * @note While @p normal_params_size is equivalent to the number of normal parameters, + * @p translate_params_size includes the size occupied by the translate parameters headers. */ inline u32 MakeHeader(u16 command_id, unsigned int normal_params_size, unsigned int translate_params_size) { diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index c088b9a19..761fc4781 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -4,6 +4,7 @@ #pragma once +#include <memory> #include <string> #include "common/assert.h" #include "common/common_types.h" @@ -44,7 +45,8 @@ public: /** * Creates a pair of ServerSession and an associated ClientSession. - * @param name Optional name of the ports + * @param name Optional name of the ports. + * @param hle_handler Optional HLE handler for this server session. * @return The created session tuple */ static SessionPair CreateSessionPair( diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c557a2279..6ab31c70b 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -11,7 +11,6 @@ #include <boost/container/flat_set.hpp> #include "common/common_types.h" #include "core/arm/arm_interface.h" -#include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index c42003e9d..90e4b1f00 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -78,7 +78,7 @@ void Timer::WakeupAllWaitingThreads() { } void Timer::Signal(int cycles_late) { - LOG_TRACE(Kernel, "Timer %08" PRIx64 " fired", timer_handle); + LOG_TRACE(Kernel, "Timer %u fired", GetObjectId()); // Resume all waiting threads WakeupAllWaitingThreads(); diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index f6bff8bc6..34a9c8479 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -518,7 +518,7 @@ void FlipImage(Service::Interface* self); void SetDetailSize(Service::Interface* self); /** - * Sets camera resolution from preset resolution parameters. . + * Sets camera resolution from preset resolution parameters. * Inputs: * 0: 0x001F00C0 * 1: u8 selected camera diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 1457518d4..a960778a7 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -4,6 +4,7 @@ #include "common/bit_field.h" #include "common/microprofile.h" +#include "core/core.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" @@ -118,10 +119,10 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_va * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. * For each register, the value is updated only where the mask is high * - * @param base_address The address of the first register in the sequence + * @param base_address The address of the first register in the sequence * @param size_in_bytes The number of registers to update (size of data) - * @param data A pointer to the source data to use for updates - * @param masks A pointer to the masks + * @param data_vaddr A virtual address to the source data to use for updates + * @param masks_vaddr A virtual address to the masks * @return RESULT_SUCCESS if the parameters are valid, error code otherwise */ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr data_vaddr, @@ -280,6 +281,7 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { if (screen_id == 0) { MicroProfileFlip(); + Core::System::GetInstance().perf_stats.EndGameFrame(); } return RESULT_SUCCESS; diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h index 060d5a55f..3bc10dbdc 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.h +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -57,7 +57,7 @@ public: * @param is_crs true if the module itself is the static module * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. */ - ResultCode Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment_addresss, + ResultCode Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment_address, u32 data_segment_size, VAddr bss_segment_address, u32 bss_segment_size, bool is_crs); @@ -102,7 +102,7 @@ public: /** * Registers this module and adds it to the module list. * @param crs_address the virtual address of the static module - * @auto_link whether to register as an auto link module + * @param auto_link whether to register as an auto link module */ void Register(VAddr crs_address, bool auto_link); diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index 8d00a7577..7af76676b 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" +#include "core/core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/service/ldr_ro/cro_helper.h" diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index fa8c13d36..42809c731 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -8,17 +8,13 @@ #include "common/color.h" #include "common/common_types.h" #include "common/logging/log.h" -#include "common/math_util.h" #include "common/microprofile.h" -#include "common/thread.h" -#include "common/timer.h" #include "common/vector_math.h" #include "core/core_timing.h" #include "core/hle/service/gsp_gpu.h" #include "core/hw/gpu.h" #include "core/hw/hw.h" #include "core/memory.h" -#include "core/settings.h" #include "core/tracer/recorder.h" #include "video_core/command_processor.h" #include "video_core/debug_utils/debug_utils.h" @@ -32,19 +28,9 @@ namespace GPU { Regs g_regs; /// 268MHz CPU clocks / 60Hz frames per second -const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / 60; +const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE; /// Event id for CoreTiming static int vblank_event; -/// Total number of frames drawn -static u64 frame_count; -/// Start clock for frame limiter -static u32 time_point; -/// Total delay caused by slow frames -static float time_delay; -constexpr float FIXED_FRAME_TIME = 1000.0f / 60; -// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher -// values increases time needed to limit frame rate after spikes -constexpr float MAX_LAG_TIME = 18; template <typename T> inline void Read(T& var, const u32 raw_addr) { @@ -522,24 +508,8 @@ template void Write<u32>(u32 addr, const u32 data); template void Write<u16>(u32 addr, const u16 data); template void Write<u8>(u32 addr, const u8 data); -static void FrameLimiter() { - time_delay += FIXED_FRAME_TIME; - time_delay = MathUtil::Clamp(time_delay, -MAX_LAG_TIME, MAX_LAG_TIME); - s32 desired_time = static_cast<s32>(time_delay); - s32 elapsed_time = static_cast<s32>(Common::Timer::GetTimeMs() - time_point); - - if (elapsed_time < desired_time) { - Common::SleepCurrentThread(desired_time - elapsed_time); - } - - u32 frame_time = Common::Timer::GetTimeMs() - time_point; - - time_delay -= frame_time; -} - /// Update hardware static void VBlankCallback(u64 userdata, int cycles_late) { - frame_count++; VideoCore::g_renderer->SwapBuffers(); // Signal to GSP that GPU interrupt has occurred @@ -550,12 +520,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) { Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); - if (!Settings::values.use_vsync && Settings::values.toggle_framelimit) { - FrameLimiter(); - } - - time_point = Common::Timer::GetTimeMs(); - // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); } @@ -590,9 +554,6 @@ void Init() { framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8); framebuffer_sub.active_fb = 0; - frame_count = 0; - time_point = Common::Timer::GetTimeMs(); - vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); CoreTiming::ScheduleEvent(frame_ticks, vblank_event); diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index d53381216..bdd997b2a 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -13,6 +13,8 @@ namespace GPU { +constexpr float SCREEN_REFRESH_RATE = 60; + // Returns index corresponding to the Regs member labeled by field_name // TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions // when used with array elements (e.g. GPU_REG_INDEX(memory_fill_config[0])). diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index a6c2a745f..1d80766ae 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -54,7 +54,7 @@ FileType IdentifyFile(const std::string& file_name); * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine * a filetype, and will never return FileType::Error. */ -FileType GuessFromExtension(const std::string& extension_); +FileType GuessFromExtension(const std::string& extension); /** * Convert a FileType into a string which can be displayed to the user. diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp new file mode 100644 index 000000000..2cdfb9ded --- /dev/null +++ b/src/core/perf_stats.cpp @@ -0,0 +1,105 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <chrono> +#include <mutex> +#include <thread> +#include "common/math_util.h" +#include "core/hw/gpu.h" +#include "core/perf_stats.h" +#include "core/settings.h" + +using namespace std::chrono_literals; +using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>; +using std::chrono::duration_cast; +using std::chrono::microseconds; + +namespace Core { + +void PerfStats::BeginSystemFrame() { + std::lock_guard<std::mutex> lock(object_mutex); + + frame_begin = Clock::now(); +} + +void PerfStats::EndSystemFrame() { + std::lock_guard<std::mutex> lock(object_mutex); + + auto frame_end = Clock::now(); + accumulated_frametime += frame_end - frame_begin; + system_frames += 1; + + previous_frame_length = frame_end - previous_frame_end; + previous_frame_end = frame_end; +} + +void PerfStats::EndGameFrame() { + std::lock_guard<std::mutex> lock(object_mutex); + + game_frames += 1; +} + +PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { + std::lock_guard<std::mutex> lock(object_mutex); + + auto now = Clock::now(); + // Walltime elapsed since stats were reset + auto interval = duration_cast<DoubleSecs>(now - reset_point).count(); + + auto system_us_per_second = + static_cast<double>(current_system_time_us - reset_point_system_us) / interval; + + Results results{}; + results.system_fps = static_cast<double>(system_frames) / interval; + results.game_fps = static_cast<double>(game_frames) / interval; + results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / + static_cast<double>(system_frames); + results.emulation_speed = system_us_per_second / 1'000'000.0; + + // Reset counters + reset_point = now; + reset_point_system_us = current_system_time_us; + accumulated_frametime = Clock::duration::zero(); + system_frames = 0; + game_frames = 0; + + return results; +} + +double PerfStats::GetLastFrameTimeScale() { + std::lock_guard<std::mutex> lock(object_mutex); + + constexpr double FRAME_LENGTH = 1.0 / GPU::SCREEN_REFRESH_RATE; + return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; +} + +void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { + // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher + // values increase the time needed to recover and limit framerate again after spikes. + constexpr microseconds MAX_LAG_TIME_US = 25ms; + + if (!Settings::values.toggle_framelimit) { + return; + } + + auto now = Clock::now(); + + frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us); + frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); + frame_limiting_delta_err = + MathUtil::Clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); + + if (frame_limiting_delta_err > microseconds::zero()) { + std::this_thread::sleep_for(frame_limiting_delta_err); + + auto now_after_sleep = Clock::now(); + frame_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now); + now = now_after_sleep; + } + + previous_system_time_us = current_system_time_us; + previous_walltime = now; +} + +} // namespace Core diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h new file mode 100644 index 000000000..362b205c8 --- /dev/null +++ b/src/core/perf_stats.h @@ -0,0 +1,83 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <chrono> +#include <mutex> +#include "common/common_types.h" + +namespace Core { + +/** + * Class to manage and query performance/timing statistics. All public functions of this class are + * thread-safe unless stated otherwise. + */ +class PerfStats { +public: + using Clock = std::chrono::high_resolution_clock; + + struct Results { + /// System FPS (LCD VBlanks) in Hz + double system_fps; + /// Game FPS (GSP frame submissions) in Hz + double game_fps; + /// Walltime per system frame, in seconds, excluding any waits + double frametime; + /// Ratio of walltime / emulated time elapsed + double emulation_speed; + }; + + void BeginSystemFrame(); + void EndSystemFrame(); + void EndGameFrame(); + + Results GetAndResetStats(u64 current_system_time_us); + + /** + * Gets the ratio between walltime and the emulated time of the previous system frame. This is + * useful for scaling inputs or outputs moving between the two time domains. + */ + double GetLastFrameTimeScale(); + +private: + std::mutex object_mutex; + + /// Point when the cumulative counters were reset + Clock::time_point reset_point = Clock::now(); + /// System time when the cumulative counters were reset + u64 reset_point_system_us = 0; + + /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset + Clock::duration accumulated_frametime = Clock::duration::zero(); + /// Cumulative number of system frames (LCD VBlanks) presented since last reset + u32 system_frames = 0; + /// Cumulative number of game frames (GSP frame submissions) since last reset + u32 game_frames = 0; + + /// Point when the previous system frame ended + Clock::time_point previous_frame_end = reset_point; + /// Point when the current system frame began + Clock::time_point frame_begin = reset_point; + /// Total visible duration (including frame-limiting, etc.) of the previous system frame + Clock::duration previous_frame_length = Clock::duration::zero(); +}; + +class FrameLimiter { +public: + using Clock = std::chrono::high_resolution_clock; + + void DoFrameLimiting(u64 current_system_time_us); + +private: + /// Emulated system time (in microseconds) at the last limiter invocation + u64 previous_system_time_us = 0; + /// Walltime at the last limiter invocation + Clock::time_point previous_walltime = Clock::now(); + + /// Accumulated difference between walltime and emulated time + std::chrono::microseconds frame_limiting_delta_err{0}; +}; + +} // namespace Core |