diff options
Diffstat (limited to 'src/core/hw/gpu.cpp')
| -rw-r--r-- | src/core/hw/gpu.cpp | 58 |
1 files changed, 36 insertions, 22 deletions
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 28cb97d8e..1503b45da 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -8,7 +8,10 @@ #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" @@ -29,16 +32,20 @@ namespace GPU { Regs g_regs; -/// True if the current frame was skipped -bool g_skip_frame; /// 268MHz CPU clocks / 60Hz frames per second const u64 frame_ticks = 268123480ull / 60; /// Event id for CoreTiming static int vblank_event; /// Total number of frames drawn static u64 frame_count; -/// True if the last frame was skipped -static bool last_skip_frame; +/// 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) { @@ -480,8 +487,8 @@ inline void Write(u32 addr, const T data) { u32* buffer = (u32*)Memory::GetPhysicalPointer(config.GetPhysicalAddress()); if (Pica::g_debug_context && Pica::g_debug_context->recorder) { - Pica::g_debug_context->recorder->MemoryAccessed( - (u8*)buffer, config.size * sizeof(u32), config.GetPhysicalAddress()); + Pica::g_debug_context->recorder->MemoryAccessed((u8*)buffer, config.size, + config.GetPhysicalAddress()); } Pica::CommandProcessor::ProcessCommandList(buffer, config.size); @@ -516,23 +523,25 @@ 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++; - last_skip_frame = g_skip_frame; - g_skip_frame = (frame_count & Settings::values.frame_skip) != 0; - - // Swap buffers based on the frameskip mode, which is a little bit tricky. When - // a frame is being skipped, nothing is being rendered to the internal framebuffer(s). - // So, we should only swap frames if the last frame was rendered. The rules are: - // - If frameskip == 0 (disabled), always swap buffers - // - If frameskip == 1, swap buffers every other frame (starting from the first frame) - // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame) - if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && - last_skip_frame != g_skip_frame) || - Settings::values.frame_skip == 0) { - VideoCore::g_renderer->SwapBuffers(); - } + VideoCore::g_renderer->SwapBuffers(); // Signal to GSP that GPU interrupt has occurred // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub @@ -545,6 +554,12 @@ static void VBlankCallback(u64 userdata, int cycles_late) { // Check for user input updates Service::HID::Update(); + 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); } @@ -579,9 +594,8 @@ void Init() { framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8); framebuffer_sub.active_fb = 0; - last_skip_frame = false; - g_skip_frame = false; frame_count = 0; + time_point = Common::Timer::GetTimeMs(); vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); CoreTiming::ScheduleEvent(frame_ticks, vblank_event); |
