Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/FrameTiming.cpp
5668 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/System/Display.h"
26
#include "Common/TimeUtil.h"
27
28
#include "Core/RetroAchievements.h"
29
#include "Core/CoreParameter.h"
30
#include "Core/Core.h"
31
#include "Core/System.h"
32
#include "Core/Config.h"
33
#include "Core/HW/Display.h"
34
#include "Core/HLE/sceNet.h"
35
#include "Core/FrameTiming.h"
36
37
FrameTiming g_frameTiming;
38
39
void WaitUntil(double now, double timestamp, const char *reason) {
40
#if 1
41
// Use precise timing.
42
sleep_precise(timestamp - now, reason);
43
#else
44
45
#if PPSSPP_PLATFORM(WINDOWS)
46
// Old method. TODO: Should we make an option?
47
while (time_now_d() < timestamp) {
48
sleep_ms(1, reason); // Sleep for 1ms on this thread
49
}
50
#else
51
const double left = timestamp - now;
52
if (left > 0.0 && left < 3.0) {
53
usleep((long)(left * 1000000));
54
}
55
#endif
56
57
#endif
58
}
59
60
void FrameTiming::DeferWaitUntil(double until, double *curTimePtr) {
61
_dbg_assert_(until > 0.0);
62
waitUntil_ = until;
63
curTimePtr_ = curTimePtr;
64
}
65
66
void FrameTiming::PostSubmit() {
67
if (waitUntil_ != 0.0) {
68
WaitUntil(time_now_d(), waitUntil_, "post-submit");
69
if (curTimePtr_) {
70
*curTimePtr_ = waitUntil_;
71
curTimePtr_ = nullptr;
72
}
73
waitUntil_ = 0.0;
74
}
75
}
76
77
void FrameTiming::ComputePresentMode(Draw::DrawContext *draw, bool fastForward) {
78
if (!draw) {
79
// This happens in headless mode.
80
fastForwardSkipFlip_ = true;
81
presentMode_ = Draw::PresentMode::FIFO;
82
return;
83
}
84
85
_dbg_assert_(draw->GetDeviceCaps().presentModesSupported != (Draw::PresentMode)0);
86
87
if (draw->GetDeviceCaps().presentModesSupported == Draw::PresentMode::FIFO) {
88
// Only FIFO mode is supported (like on iOS and some GLES backends).
89
fastForwardSkipFlip_ = true;
90
presentMode_ = Draw::PresentMode::FIFO;
91
return;
92
}
93
94
// More than one present mode is supported. Use careful logic.
95
96
// The user has requested vsync off.
97
if (!g_Config.bVSync) {
98
if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::IMMEDIATE) {
99
// Use immediate mode, whether fast-forwarding or not.
100
presentMode_ = Draw::PresentMode::IMMEDIATE;
101
fastForwardSkipFlip_ = false;
102
return;
103
}
104
// Inconsistent state - vsync is off but immediate mode is not supported.
105
// We will simply force on VSync.
106
g_Config.bVSync = true;
107
}
108
109
// At this point, vsync is always on. What decides now is whether MAILBOX or IMMEDIATE is available,
110
// and also if we need an unsynced mode.
111
112
// OK, vsync is requested (or immediate is not available). If mailbox is supported, it works the same as IMMEDIATE above.
113
114
if (g_Config.bLowLatencyPresent) {
115
// Use mailbox if available. It works fine for both fast-forward and normal.
116
if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX) {
117
presentMode_ = Draw::PresentMode::MAILBOX;
118
fastForwardSkipFlip_ = false;
119
return;
120
}
121
// We could force off lowLatencyPresent here, but it's no good when changing between backends.
122
// So let's leave it on in the background, maybe the user just went from Vulkan to OpenGL.
123
}
124
125
// At this point, low-latency mode is not available, and vsync is on. We see if we can use INSTANT
126
// mode for fast-forwarding, or if we need to resort to frameskipping.
127
if (draw->GetDeviceCaps().presentInstantModeChange) {
128
if (fastForward) {
129
presentMode_ = Draw::PresentMode::IMMEDIATE;
130
fastForwardSkipFlip_ = false;
131
return;
132
}
133
}
134
135
// Finally, fallback to FIFO mode, with skip-flip in fast-forward.
136
presentMode_ = Draw::PresentMode::FIFO;
137
fastForwardSkipFlip_ = true;
138
}
139
140