Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HW/Display.cpp
3186 views
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/System.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/GPUCommon.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 float frameTimeHistory[600];
67
static float 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++] = (float)(now - lastFrameTimeHistory);
97
lastFrameTimeHistory = now;
98
frameTimeHistoryPos = frameTimeHistoryPos % frameTimeHistorySize;
99
if (frameTimeHistoryValid < frameTimeHistorySize) {
100
++frameTimeHistoryValid;
101
}
102
frameSleepHistory[frameTimeHistoryPos] = 0.0f;
103
}
104
}
105
106
// TODO: Also average actualFps
107
void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps) {
108
if (out_vps) {
109
*out_vps = (float)fps;
110
}
111
if (out_fps) {
112
*out_fps = flips;
113
}
114
if (out_actual_fps) {
115
*out_actual_fps = actualFps;
116
}
117
}
118
119
void __DisplayGetVPS(float *out_vps) {
120
*out_vps = (float)fps;
121
}
122
123
void __DisplayGetAveragedFPS(float *out_vps, float *out_fps) {
124
double avg = 0.0;
125
if (fpsHistoryValid > 0) {
126
for (int i = 0; i < fpsHistoryValid; ++i) {
127
avg += fpsHistory[i];
128
}
129
avg /= (double)fpsHistoryValid;
130
}
131
132
*out_vps = *out_fps = (float)avg;
133
}
134
135
int __DisplayGetFlipCount() {
136
return actualFlips;
137
}
138
139
int __DisplayGetNumVblanks() {
140
return numVBlanks;
141
}
142
143
int __DisplayGetVCount() {
144
return vCount;
145
}
146
147
bool DisplayIsVblank() {
148
return isVblank != 0;
149
}
150
151
uint64_t DisplayFrameStartTicks() {
152
return frameStartTicks;
153
}
154
155
uint32_t __DisplayGetCurrentHcount() {
156
const int ticksIntoFrame = (int)(CoreTiming::GetTicks() - frameStartTicks);
157
const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank;
158
// Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right.
159
return 1 + (ticksIntoFrame / ticksPerVblank);
160
}
161
162
uint32_t __DisplayGetAccumulatedHcount() {
163
// The hCount is always a positive int, and wraps from 0x7FFFFFFF -> 0.
164
int value = hCountBase + __DisplayGetCurrentHcount();
165
return value & 0x7FFFFFFF;
166
}
167
168
void DisplayAdjustAccumulatedHcount(uint32_t diff) {
169
hCountBase += diff;
170
}
171
172
float *__DisplayGetFrameTimes(int *out_valid, int *out_pos, float **out_sleep) {
173
*out_valid = frameTimeHistoryValid;
174
*out_pos = frameTimeHistoryPos;
175
*out_sleep = frameSleepHistory;
176
return frameTimeHistory;
177
}
178
179
int DisplayGetSleepPos() {
180
return frameTimeHistoryPos;
181
}
182
183
void DisplayNotifySleep(double t, int pos) {
184
if (pos < 0)
185
pos = frameTimeHistoryPos;
186
frameSleepHistory[pos] += t;
187
}
188
189
void __DisplayGetDebugStats(char *stats, size_t bufsize) {
190
char statbuf[4096];
191
if (!gpu) {
192
snprintf(stats, bufsize, "N/A");
193
return;
194
}
195
gpu->GetStats(statbuf, sizeof(statbuf));
196
197
snprintf(stats, bufsize,
198
"Kernel processing time: %0.2f ms\n"
199
"Slowest syscall: %s : %0.2f ms\n"
200
"Most active syscall: %s : %0.2f ms\n%s",
201
kernelStats.msInSyscalls * 1000.0f,
202
kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)",
203
kernelStats.slowestSyscallTime * 1000.0f,
204
kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)",
205
kernelStats.summedSlowestSyscallTime * 1000.0f,
206
statbuf);
207
}
208
209
// On like 90hz, 144hz, etc, we return 60.0f as the framerate target. We only target other
210
// framerates if they're close to 60.
211
static float FramerateTarget() {
212
float target = System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE);
213
if (target < 57.0 || target > 63.0f) {
214
return 60.0f;
215
} else {
216
return target;
217
}
218
}
219
220
bool DisplayIsRunningSlow() {
221
// Allow for some startup turbulence for 8 seconds before assuming things are bad.
222
if (fpsHistoryValid >= 8) {
223
// Look at only the last 15 samples (starting at the 14th sample behind current.)
224
int rangeStart = fpsHistoryPos - std::min(fpsHistoryValid, 14);
225
226
double best = 0.0;
227
for (int i = rangeStart; i <= fpsHistoryPos; ++i) {
228
// rangeStart may have been negative if near a wrap around.
229
int index = (fpsHistorySize + i) % fpsHistorySize;
230
best = std::max(fpsHistory[index], best);
231
}
232
233
return best < FramerateTarget() * 0.97;
234
}
235
236
return false;
237
}
238
239
void DisplayFireVblankStart() {
240
frameStartTicks = CoreTiming::GetTicks();
241
numVBlanks++;
242
243
isVblank = 1;
244
vCount++; // vCount increases at each VBLANK.
245
hCountBase += hCountPerVblank; // This is the "accumulated" hcount base.
246
if (hCountBase > 0x7FFFFFFF) {
247
hCountBase -= 0x80000000;
248
}
249
}
250
251
void DisplayFireVblankEnd() {
252
isVblank = 0;
253
std::vector<VblankCallback> toCall;
254
{
255
std::lock_guard<std::mutex> guard(listenersLock);
256
toCall = vblankListeners;
257
}
258
259
for (VblankCallback cb : toCall) {
260
cb();
261
}
262
}
263
264
void DisplayFireFlip() {
265
std::vector<FlipListener> toCall = [] {
266
std::lock_guard<std::mutex> guard(listenersLock);
267
return flipListeners;
268
}();
269
270
// This is also the right time to calculate FPS.
271
CalculateFPS();
272
273
for (FlipListener cb : toCall) {
274
cb.first(cb.second);
275
}
276
}
277
278
void DisplayFireActualFlip() {
279
actualFlips++;
280
}
281
282
void __DisplayListenVblank(VblankCallback callback) {
283
std::lock_guard<std::mutex> guard(listenersLock);
284
vblankListeners.push_back(callback);
285
}
286
287
void __DisplayListenFlip(FlipCallback callback, void *userdata) {
288
std::lock_guard<std::mutex> guard(listenersLock);
289
flipListeners.emplace_back(callback, userdata);
290
}
291
292
void __DisplayForgetFlip(FlipCallback callback, void *userdata) {
293
std::lock_guard<std::mutex> guard(listenersLock);
294
flipListeners.erase(std::remove_if(flipListeners.begin(), flipListeners.end(), [&](FlipListener item) {
295
return item.first == callback && item.second == userdata;
296
}), flipListeners.end());
297
}
298
299
int DisplayCalculateFrameSkip() {
300
int frameSkipNum;
301
if (g_Config.iFrameSkipType == 1) {
302
// Calculate the frames to skip dynamically using the set percentage of the current fps
303
frameSkipNum = (int)ceil(flips * (static_cast<double>(g_Config.iFrameSkip) / 100.00));
304
} else {
305
// Use the set number of frames to skip
306
frameSkipNum = g_Config.iFrameSkip;
307
}
308
return frameSkipNum;
309
}
310
311
void DisplayHWReset() {
312
frameStartTicks = 0;
313
numVBlanks = 0;
314
isVblank = 0;
315
vCount = 0;
316
hCountBase = 0;
317
318
flips = 0;
319
fps = 0.0;
320
actualFlips = 0;
321
lastActualFlips = 0;
322
lastNumFlips = 0;
323
324
fpsHistoryValid = 0;
325
fpsHistoryPos = 0;
326
327
frameTimeHistoryValid = 0;
328
frameTimeHistoryPos = 0;
329
lastFrameTimeHistory = 0.0;
330
}
331
332
void DisplayHWInit() {}
333
334
void DisplayHWShutdown() {
335
std::lock_guard<std::mutex> guard(listenersLock);
336
vblankListeners.clear();
337
flipListeners.clear();
338
}
339
340
void DisplayHWDoState(PointerWrap &p, int hleCompatV2) {
341
Do(p, frameStartTicks);
342
Do(p, vCount);
343
if (hleCompatV2) {
344
double oldHCountBase;
345
Do(p, oldHCountBase);
346
hCountBase = (int)oldHCountBase;
347
} else {
348
Do(p, hCountBase);
349
}
350
Do(p, isVblank);
351
}
352
353