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/HW/Display.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <algorithm>
19
#include <cmath>
20
#include <mutex>
21
#include <vector>
22
#include "Common/CommonTypes.h"
23
#include "Common/Serialize/SerializeFuncs.h"
24
#include "Common/System/System.h"
25
#include "Common/TimeUtil.h"
26
#include "Core/Config.h"
27
#include "Core/Core.h"
28
#include "Core/CoreTiming.h"
29
#include "Core/HLE/sceKernel.h"
30
#include "Core/HW/Display.h"
31
#include "GPU/GPU.h"
32
#include "GPU/GPUInterface.h"
33
34
// Called when vblank happens (like an internal interrupt.) Not part of state, should be static.
35
static std::mutex listenersLock;
36
static std::vector<VblankCallback> vblankListeners;
37
typedef std::pair<FlipCallback, void *> FlipListener;
38
static std::vector<FlipListener> flipListeners;
39
40
static uint64_t frameStartTicks;
41
static int numVBlanks;
42
// hCount is computed now.
43
static int vCount;
44
// The "AccumulatedHcount" can be adjusted, this is the base.
45
static uint32_t hCountBase;
46
static int isVblank;
47
static constexpr int hCountPerVblank = 286;
48
49
// FPS stats for frameskip, FPS display, etc.
50
static int lastFpsFrame = 0;
51
static double lastFpsTime = 0.0;
52
static double fps = 0.0;
53
static int lastNumFlips = 0;
54
static float flips = 0.0f;
55
static int actualFlips = 0; // taking frameskip into account
56
static int lastActualFlips = 0;
57
static float actualFps = 0;
58
59
// FPS stats for averaging.
60
static double fpsHistory[120];
61
static constexpr int fpsHistorySize = (int)ARRAY_SIZE(fpsHistory);
62
static int fpsHistoryPos = 0;
63
static int fpsHistoryValid = 0;
64
65
// Frame time stats.
66
static double frameTimeHistory[600];
67
static double frameSleepHistory[600];
68
static constexpr int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory);
69
static int frameTimeHistoryPos = 0;
70
static int frameTimeHistoryValid = 0;
71
static double lastFrameTimeHistory = 0.0;
72
73
static void CalculateFPS() {
74
double now = time_now_d();
75
76
if (now >= lastFpsTime + 1.0) {
77
double frames = (numVBlanks - lastFpsFrame);
78
actualFps = (float)(actualFlips - lastActualFlips);
79
80
fps = frames / (now - lastFpsTime);
81
flips = (float)(g_Config.iDisplayRefreshRate * (double)(gpuStats.numFlips - lastNumFlips) / frames);
82
83
lastFpsFrame = numVBlanks;
84
lastNumFlips = gpuStats.numFlips;
85
lastActualFlips = actualFlips;
86
lastFpsTime = now;
87
88
fpsHistory[fpsHistoryPos++] = fps;
89
fpsHistoryPos = fpsHistoryPos % fpsHistorySize;
90
if (fpsHistoryValid < fpsHistorySize) {
91
++fpsHistoryValid;
92
}
93
}
94
95
if ((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::FRAME_GRAPH || coreCollectDebugStats) {
96
frameTimeHistory[frameTimeHistoryPos++] = now - lastFrameTimeHistory;
97
lastFrameTimeHistory = now;
98
frameTimeHistoryPos = frameTimeHistoryPos % frameTimeHistorySize;
99
if (frameTimeHistoryValid < frameTimeHistorySize) {
100
++frameTimeHistoryValid;
101
}
102
frameSleepHistory[frameTimeHistoryPos] = 0.0;
103
}
104
}
105
106
// TODO: Also average actualFps
107
void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps) {
108
*out_vps = (float)fps;
109
*out_fps = flips;
110
*out_actual_fps = actualFps;
111
}
112
113
void __DisplayGetVPS(float *out_vps) {
114
*out_vps = (float)fps;
115
}
116
117
void __DisplayGetAveragedFPS(float *out_vps, float *out_fps) {
118
double avg = 0.0;
119
if (fpsHistoryValid > 0) {
120
for (int i = 0; i < fpsHistoryValid; ++i) {
121
avg += fpsHistory[i];
122
}
123
avg /= (double)fpsHistoryValid;
124
}
125
126
*out_vps = *out_fps = (float)avg;
127
}
128
129
int __DisplayGetFlipCount() {
130
return actualFlips;
131
}
132
133
int __DisplayGetNumVblanks() {
134
return numVBlanks;
135
}
136
137
int __DisplayGetVCount() {
138
return vCount;
139
}
140
141
bool DisplayIsVblank() {
142
return isVblank != 0;
143
}
144
145
uint64_t DisplayFrameStartTicks() {
146
return frameStartTicks;
147
}
148
149
uint32_t __DisplayGetCurrentHcount() {
150
const int ticksIntoFrame = (int)(CoreTiming::GetTicks() - frameStartTicks);
151
const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank;
152
// Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right.
153
return 1 + (ticksIntoFrame / ticksPerVblank);
154
}
155
156
uint32_t __DisplayGetAccumulatedHcount() {
157
// The hCount is always a positive int, and wraps from 0x7FFFFFFF -> 0.
158
int value = hCountBase + __DisplayGetCurrentHcount();
159
return value & 0x7FFFFFFF;
160
}
161
162
void DisplayAdjustAccumulatedHcount(uint32_t diff) {
163
hCountBase += diff;
164
}
165
166
double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep) {
167
*out_valid = frameTimeHistoryValid;
168
*out_pos = frameTimeHistoryPos;
169
*out_sleep = frameSleepHistory;
170
return frameTimeHistory;
171
}
172
173
int DisplayGetSleepPos() {
174
return frameTimeHistoryPos;
175
}
176
177
void DisplayNotifySleep(double t, int pos) {
178
if (pos < 0)
179
pos = frameTimeHistoryPos;
180
frameSleepHistory[pos] += t;
181
}
182
183
void __DisplayGetDebugStats(char *stats, size_t bufsize) {
184
char statbuf[4096];
185
if (!gpu) {
186
snprintf(stats, bufsize, "N/A");
187
return;
188
}
189
gpu->GetStats(statbuf, sizeof(statbuf));
190
191
snprintf(stats, bufsize,
192
"Kernel processing time: %0.2f ms\n"
193
"Slowest syscall: %s : %0.2f ms\n"
194
"Most active syscall: %s : %0.2f ms\n%s",
195
kernelStats.msInSyscalls * 1000.0f,
196
kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)",
197
kernelStats.slowestSyscallTime * 1000.0f,
198
kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)",
199
kernelStats.summedSlowestSyscallTime * 1000.0f,
200
statbuf);
201
}
202
203
// On like 90hz, 144hz, etc, we return 60.0f as the framerate target. We only target other
204
// framerates if they're close to 60.
205
static float FramerateTarget() {
206
float target = System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE);
207
if (target < 57.0 || target > 63.0f) {
208
return 60.0f;
209
} else {
210
return target;
211
}
212
}
213
214
bool DisplayIsRunningSlow() {
215
// Allow for some startup turbulence for 8 seconds before assuming things are bad.
216
if (fpsHistoryValid >= 8) {
217
// Look at only the last 15 samples (starting at the 14th sample behind current.)
218
int rangeStart = fpsHistoryPos - std::min(fpsHistoryValid, 14);
219
220
double best = 0.0;
221
for (int i = rangeStart; i <= fpsHistoryPos; ++i) {
222
// rangeStart may have been negative if near a wrap around.
223
int index = (fpsHistorySize + i) % fpsHistorySize;
224
best = std::max(fpsHistory[index], best);
225
}
226
227
return best < FramerateTarget() * 0.97;
228
}
229
230
return false;
231
}
232
233
void DisplayFireVblankStart() {
234
frameStartTicks = CoreTiming::GetTicks();
235
numVBlanks++;
236
237
isVblank = 1;
238
vCount++; // vCount increases at each VBLANK.
239
hCountBase += hCountPerVblank; // This is the "accumulated" hcount base.
240
if (hCountBase > 0x7FFFFFFF) {
241
hCountBase -= 0x80000000;
242
}
243
}
244
245
void DisplayFireVblankEnd() {
246
isVblank = 0;
247
std::vector<VblankCallback> toCall;
248
{
249
std::lock_guard<std::mutex> guard(listenersLock);
250
toCall = vblankListeners;
251
}
252
253
for (VblankCallback cb : toCall) {
254
cb();
255
}
256
}
257
258
void DisplayFireFlip() {
259
std::vector<FlipListener> toCall = [] {
260
std::lock_guard<std::mutex> guard(listenersLock);
261
return flipListeners;
262
}();
263
264
// This is also the right time to calculate FPS.
265
CalculateFPS();
266
267
for (FlipListener cb : toCall) {
268
cb.first(cb.second);
269
}
270
}
271
272
void DisplayFireActualFlip() {
273
actualFlips++;
274
}
275
276
void __DisplayListenVblank(VblankCallback callback) {
277
std::lock_guard<std::mutex> guard(listenersLock);
278
vblankListeners.push_back(callback);
279
}
280
281
void __DisplayListenFlip(FlipCallback callback, void *userdata) {
282
std::lock_guard<std::mutex> guard(listenersLock);
283
flipListeners.emplace_back(callback, userdata);
284
}
285
286
void __DisplayForgetFlip(FlipCallback callback, void *userdata) {
287
std::lock_guard<std::mutex> guard(listenersLock);
288
flipListeners.erase(std::remove_if(flipListeners.begin(), flipListeners.end(), [&](FlipListener item) {
289
return item.first == callback && item.second == userdata;
290
}), flipListeners.end());
291
}
292
293
int DisplayCalculateFrameSkip() {
294
int frameSkipNum;
295
if (g_Config.iFrameSkipType == 1) {
296
// Calculate the frames to skip dynamically using the set percentage of the current fps
297
frameSkipNum = (int)ceil(flips * (static_cast<double>(g_Config.iFrameSkip) / 100.00));
298
} else {
299
// Use the set number of frames to skip
300
frameSkipNum = g_Config.iFrameSkip;
301
}
302
return frameSkipNum;
303
}
304
305
void DisplayHWInit() {
306
frameStartTicks = 0;
307
numVBlanks = 0;
308
isVblank = 0;
309
vCount = 0;
310
hCountBase = 0;
311
312
flips = 0;
313
fps = 0.0;
314
actualFlips = 0;
315
lastActualFlips = 0;
316
lastNumFlips = 0;
317
318
fpsHistoryValid = 0;
319
fpsHistoryPos = 0;
320
321
frameTimeHistoryValid = 0;
322
frameTimeHistoryPos = 0;
323
lastFrameTimeHistory = 0.0;
324
}
325
326
void DisplayHWShutdown() {
327
std::lock_guard<std::mutex> guard(listenersLock);
328
vblankListeners.clear();
329
flipListeners.clear();
330
}
331
332
void DisplayHWDoState(PointerWrap &p, int hleCompatV2) {
333
Do(p, frameStartTicks);
334
Do(p, vCount);
335
if (hleCompatV2) {
336
double oldHCountBase;
337
Do(p, oldHCountBase);
338
hCountBase = (int)oldHCountBase;
339
} else {
340
Do(p, hCountBase);
341
}
342
Do(p, isVblank);
343
}
344
345