CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/FrameTiming.cpp
Views: 1401
// Frame timing1//2// A frame on the main thread should look a bit like this:3//4// 1. -- Wait for the right time to start the frame (alternatively, see this is step 8).5// 2. Sample inputs (on some platforms, this is done continouously during step 3)6// 3. Run CPU7// 4. Submit GPU commands (there's no reason to ever wait before this).8// 5. -- Wait for the right time to present9// 6. Send Present command10// 7. Do other end-of-frame stuff11//12// To minimize latency, we should *maximize* 1 and *minimize* 5 (while still keeping some margin to soak up hitches).13// Additionally, if too many completed frames have been buffered up, we need a feedback mechanism, so we can temporarily14// artificially increase 1 in order to "catch the CPU up".15//16// There are some other things that can influence the frame timing:17// * Unthrottling. If vsync is off or the backend can change present mode dynamically, we can simply disable all waits during unthrottle.18// * Frame skipping. This gets complicated.19// * The game not actually asking for flips, like in static loading screens2021#include "Common/Profiler/Profiler.h"22#include "Common/Log.h"23#include "Common/TimeUtil.h"2425#include "Core/RetroAchievements.h"26#include "Core/CoreParameter.h"27#include "Core/Core.h"28#include "Core/Config.h"29#include "Core/HW/Display.h"30#include "Core/FrameTiming.h"3132FrameTiming g_frameTiming;3334void WaitUntil(double now, double timestamp) {35#ifdef _WIN3236while (time_now_d() < timestamp) {37sleep_ms(1); // Sleep for 1ms on this thread38}39#else40const double left = timestamp - now;41if (left > 0.0 && left < 3.0) {42usleep((long)(left * 1000000));43}44#endif45}4647inline Draw::PresentMode GetBestImmediateMode(Draw::PresentMode supportedModes) {48if (supportedModes & Draw::PresentMode::MAILBOX) {49return Draw::PresentMode::MAILBOX;50} else {51return Draw::PresentMode::IMMEDIATE;52}53}5455void FrameTiming::Reset(Draw::DrawContext *draw) {56if (g_Config.bVSync || !(draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::MAILBOX | Draw::PresentMode::IMMEDIATE))) {57presentMode = Draw::PresentMode::FIFO;58presentInterval = 1;59} else {60presentMode = GetBestImmediateMode(draw->GetDeviceCaps().presentModesSupported);61presentInterval = 0;62}63}6465void FrameTiming::DeferWaitUntil(double until, double *curTimePtr) {66_dbg_assert_(until > 0.0);67waitUntil_ = until;68curTimePtr_ = curTimePtr;69}7071void FrameTiming::PostSubmit() {72if (waitUntil_ != 0.0) {73WaitUntil(time_now_d(), waitUntil_);74if (curTimePtr_) {75*curTimePtr_ = waitUntil_;76curTimePtr_ = nullptr;77}78waitUntil_ = 0.0;79}80}8182Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval) {83_assert_(draw);8485Draw::PresentMode mode = Draw::PresentMode::FIFO;8687if (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::IMMEDIATE | Draw::PresentMode::MAILBOX)) {88// Switch to immediate if desired and possible.89bool wantInstant = false;90if (!g_Config.bVSync) {91wantInstant = true;92}9394if (PSP_CoreParameter().fastForward) {95wantInstant = true;96}97if (PSP_CoreParameter().fpsLimit != FPSLimit::NORMAL) {98int limit;99if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1)100limit = g_Config.iFpsLimit1;101else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2)102limit = g_Config.iFpsLimit2;103else104limit = PSP_CoreParameter().analogFpsLimit;105106// For an alternative speed that is a clean factor of 60, the user probably still wants vsync.107// TODO: Should take the user's display refresh rate into account...108if (limit == 0 || (limit >= 0 && limit != 15 && limit != 30 && limit != 60)) {109wantInstant = true;110}111}112113if (wantInstant && g_Config.bVSync && !draw->GetDeviceCaps().presentInstantModeChange) {114// If in vsync mode (which will be FIFO), and the backend can't switch immediately,115// stick to FIFO.116wantInstant = false;117}118119// The outer if checks that instant modes are available.120if (wantInstant) {121mode = GetBestImmediateMode(draw->GetDeviceCaps().presentModesSupported);122}123}124125*interval = (mode == Draw::PresentMode::FIFO) ? 1 : 0;126return mode;127}128129130