CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/FrameTiming.cpp
Views: 1401
1
// Frame timing
2
//
3
// A frame on the main thread should look a bit like this:
4
//
5
// 1. -- Wait for the right time to start the frame (alternatively, see this is step 8).
6
// 2. Sample inputs (on some platforms, this is done continouously during step 3)
7
// 3. Run CPU
8
// 4. Submit GPU commands (there's no reason to ever wait before this).
9
// 5. -- Wait for the right time to present
10
// 6. Send Present command
11
// 7. Do other end-of-frame stuff
12
//
13
// To minimize latency, we should *maximize* 1 and *minimize* 5 (while still keeping some margin to soak up hitches).
14
// Additionally, if too many completed frames have been buffered up, we need a feedback mechanism, so we can temporarily
15
// artificially increase 1 in order to "catch the CPU up".
16
//
17
// There are some other things that can influence the frame timing:
18
// * Unthrottling. If vsync is off or the backend can change present mode dynamically, we can simply disable all waits during unthrottle.
19
// * Frame skipping. This gets complicated.
20
// * The game not actually asking for flips, like in static loading screens
21
22
#include "Common/Profiler/Profiler.h"
23
#include "Common/Log.h"
24
#include "Common/TimeUtil.h"
25
26
#include "Core/RetroAchievements.h"
27
#include "Core/CoreParameter.h"
28
#include "Core/Core.h"
29
#include "Core/Config.h"
30
#include "Core/HW/Display.h"
31
#include "Core/FrameTiming.h"
32
33
FrameTiming g_frameTiming;
34
35
void WaitUntil(double now, double timestamp) {
36
#ifdef _WIN32
37
while (time_now_d() < timestamp) {
38
sleep_ms(1); // Sleep for 1ms on this thread
39
}
40
#else
41
const double left = timestamp - now;
42
if (left > 0.0 && left < 3.0) {
43
usleep((long)(left * 1000000));
44
}
45
#endif
46
}
47
48
inline Draw::PresentMode GetBestImmediateMode(Draw::PresentMode supportedModes) {
49
if (supportedModes & Draw::PresentMode::MAILBOX) {
50
return Draw::PresentMode::MAILBOX;
51
} else {
52
return Draw::PresentMode::IMMEDIATE;
53
}
54
}
55
56
void FrameTiming::Reset(Draw::DrawContext *draw) {
57
if (g_Config.bVSync || !(draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::MAILBOX | Draw::PresentMode::IMMEDIATE))) {
58
presentMode = Draw::PresentMode::FIFO;
59
presentInterval = 1;
60
} else {
61
presentMode = GetBestImmediateMode(draw->GetDeviceCaps().presentModesSupported);
62
presentInterval = 0;
63
}
64
}
65
66
void FrameTiming::DeferWaitUntil(double until, double *curTimePtr) {
67
_dbg_assert_(until > 0.0);
68
waitUntil_ = until;
69
curTimePtr_ = curTimePtr;
70
}
71
72
void FrameTiming::PostSubmit() {
73
if (waitUntil_ != 0.0) {
74
WaitUntil(time_now_d(), waitUntil_);
75
if (curTimePtr_) {
76
*curTimePtr_ = waitUntil_;
77
curTimePtr_ = nullptr;
78
}
79
waitUntil_ = 0.0;
80
}
81
}
82
83
Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval) {
84
_assert_(draw);
85
86
Draw::PresentMode mode = Draw::PresentMode::FIFO;
87
88
if (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::IMMEDIATE | Draw::PresentMode::MAILBOX)) {
89
// Switch to immediate if desired and possible.
90
bool wantInstant = false;
91
if (!g_Config.bVSync) {
92
wantInstant = true;
93
}
94
95
if (PSP_CoreParameter().fastForward) {
96
wantInstant = true;
97
}
98
if (PSP_CoreParameter().fpsLimit != FPSLimit::NORMAL) {
99
int limit;
100
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1)
101
limit = g_Config.iFpsLimit1;
102
else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2)
103
limit = g_Config.iFpsLimit2;
104
else
105
limit = PSP_CoreParameter().analogFpsLimit;
106
107
// For an alternative speed that is a clean factor of 60, the user probably still wants vsync.
108
// TODO: Should take the user's display refresh rate into account...
109
if (limit == 0 || (limit >= 0 && limit != 15 && limit != 30 && limit != 60)) {
110
wantInstant = true;
111
}
112
}
113
114
if (wantInstant && g_Config.bVSync && !draw->GetDeviceCaps().presentInstantModeChange) {
115
// If in vsync mode (which will be FIFO), and the backend can't switch immediately,
116
// stick to FIFO.
117
wantInstant = false;
118
}
119
120
// The outer if checks that instant modes are available.
121
if (wantInstant) {
122
mode = GetBestImmediateMode(draw->GetDeviceCaps().presentModesSupported);
123
}
124
}
125
126
*interval = (mode == Draw::PresentMode::FIFO) ? 1 : 0;
127
return mode;
128
}
129
130