Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/FrameTiming.cpp
3185 views
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 "ppsspp_config.h"
23
#include "Common/Profiler/Profiler.h"
24
#include "Common/Log.h"
25
#include "Common/TimeUtil.h"
26
27
#include "Core/RetroAchievements.h"
28
#include "Core/CoreParameter.h"
29
#include "Core/Core.h"
30
#include "Core/System.h"
31
#include "Core/Config.h"
32
#include "Core/HW/Display.h"
33
#include "Core/HLE/sceNet.h"
34
#include "Core/FrameTiming.h"
35
36
FrameTiming g_frameTiming;
37
38
void WaitUntil(double now, double timestamp, const char *reason) {
39
#if 1
40
// Use precise timing.
41
sleep_precise(timestamp - now);
42
#else
43
44
#if PPSSPP_PLATFORM(WINDOWS)
45
// Old method. TODO: Should we make an option?
46
while (time_now_d() < timestamp) {
47
sleep_ms(1, reason); // Sleep for 1ms on this thread
48
}
49
#else
50
const double left = timestamp - now;
51
if (left > 0.0 && left < 3.0) {
52
usleep((long)(left * 1000000));
53
}
54
#endif
55
56
#endif
57
}
58
59
inline Draw::PresentMode GetBestImmediateMode(Draw::PresentMode supportedModes) {
60
if (supportedModes & Draw::PresentMode::MAILBOX) {
61
return Draw::PresentMode::MAILBOX;
62
} else {
63
return Draw::PresentMode::IMMEDIATE;
64
}
65
}
66
67
void FrameTiming::Reset(Draw::DrawContext *draw) {
68
if (g_Config.bVSync || !(draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::MAILBOX | Draw::PresentMode::IMMEDIATE))) {
69
presentMode = Draw::PresentMode::FIFO;
70
presentInterval = 1;
71
} else {
72
presentMode = GetBestImmediateMode(draw->GetDeviceCaps().presentModesSupported);
73
presentInterval = 0;
74
}
75
}
76
77
void FrameTiming::DeferWaitUntil(double until, double *curTimePtr) {
78
_dbg_assert_(until > 0.0);
79
waitUntil_ = until;
80
curTimePtr_ = curTimePtr;
81
}
82
83
void FrameTiming::PostSubmit() {
84
if (waitUntil_ != 0.0) {
85
WaitUntil(time_now_d(), waitUntil_, "post-submit");
86
if (curTimePtr_) {
87
*curTimePtr_ = waitUntil_;
88
curTimePtr_ = nullptr;
89
}
90
waitUntil_ = 0.0;
91
}
92
}
93
94
Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval) {
95
_assert_(draw);
96
97
Draw::PresentMode mode = Draw::PresentMode::FIFO;
98
99
if (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::IMMEDIATE | Draw::PresentMode::MAILBOX)) {
100
// Switch to immediate if desired and possible.
101
bool wantInstant = false;
102
if (!g_Config.bVSync) {
103
wantInstant = true;
104
}
105
106
if (PSP_CoreParameter().fastForward && NetworkAllowSpeedControl()) {
107
wantInstant = true;
108
}
109
110
FPSLimit limit = PSP_CoreParameter().fpsLimit;
111
if (!NetworkAllowSpeedControl()) {
112
limit = FPSLimit::NORMAL;
113
}
114
115
if (limit != FPSLimit::NORMAL) {
116
int limit;
117
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1)
118
limit = g_Config.iFpsLimit1;
119
else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2)
120
limit = g_Config.iFpsLimit2;
121
else
122
limit = PSP_CoreParameter().analogFpsLimit;
123
124
// For an alternative speed that is a clean factor of 60, the user probably still wants vsync.
125
// TODO: Should take the user's display refresh rate into account...
126
if (limit == 0 || (limit >= 0 && limit != 15 && limit != 30 && limit != 60)) {
127
wantInstant = true;
128
}
129
}
130
131
if (wantInstant && g_Config.bVSync && !draw->GetDeviceCaps().presentInstantModeChange) {
132
// If in vsync mode (which will be FIFO), and the backend can't switch immediately,
133
// stick to FIFO.
134
wantInstant = false;
135
}
136
137
// The outer if checks that instant modes are available.
138
if (wantInstant) {
139
mode = GetBestImmediateMode(draw->GetDeviceCaps().presentModesSupported);
140
}
141
}
142
143
*interval = (mode == Draw::PresentMode::FIFO) ? 1 : 0;
144
return mode;
145
}
146
147