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 <vector>
19 #include <map>
20 #include <cmath>
21 #include <algorithm>
22
23 // TODO: Move this somewhere else, cleanup.
24 #ifndef _WIN32
25 #include <unistd.h>
26 #include <sys/time.h>
27 #endif
28
29 #include "Common/Data/Text/I18n.h"
30 #include "Common/Profiler/Profiler.h"
31 #include "Common/System/System.h"
32 #include "Common/Serialize/Serializer.h"
33 #include "Common/Serialize/SerializeFuncs.h"
34 #include "Common/Serialize/SerializeMap.h"
35 #include "Common/TimeUtil.h"
36 #include "Core/Config.h"
37 #include "Core/CoreTiming.h"
38 #include "Core/CoreParameter.h"
39 #include "Core/Host.h"
40 #include "Core/Reporting.h"
41 #include "Core/Core.h"
42 #include "Core/System.h"
43 #include "Core/HLE/HLE.h"
44 #include "Core/HLE/FunctionWrappers.h"
45 #include "Core/HLE/sceDisplay.h"
46 #include "Core/HLE/sceKernel.h"
47 #include "Core/HLE/sceKernelThread.h"
48 #include "Core/HLE/sceKernelInterrupt.h"
49 #include "Core/Util/PPGeDraw.h"
50
51 #include "GPU/GPU.h"
52 #include "GPU/GPUState.h"
53 #include "GPU/GPUInterface.h"
54 #include "GPU/Common/FramebufferManagerCommon.h"
55 #include "GPU/Common/PostShader.h"
56 #include "GPU/Debugger/Record.h"
57
58 struct FrameBufferState {
59 u32 topaddr;
60 GEBufferFormat fmt;
61 int stride;
62 };
63
64 struct WaitVBlankInfo {
WaitVBlankInfoWaitVBlankInfo65 WaitVBlankInfo(u32 tid) : threadID(tid), vcountUnblock(1) {}
WaitVBlankInfoWaitVBlankInfo66 WaitVBlankInfo(u32 tid, int vcount) : threadID(tid), vcountUnblock(vcount) {}
67 SceUID threadID;
68 // Number of vcounts to block for.
69 int vcountUnblock;
70
DoStateWaitVBlankInfo71 void DoState(PointerWrap &p) {
72 auto s = p.Section("WaitVBlankInfo", 1);
73 if (!s)
74 return;
75
76 Do(p, threadID);
77 Do(p, vcountUnblock);
78 }
79 };
80
81 // STATE BEGIN
82 static FrameBufferState framebuf;
83 static FrameBufferState latchedFramebuf;
84 static bool framebufIsLatched;
85
86 static int enterVblankEvent = -1;
87 static int leaveVblankEvent = -1;
88 static int afterFlipEvent = -1;
89 static int lagSyncEvent = -1;
90
91 static double lastLagSync = 0.0;
92 static bool lagSyncScheduled = false;
93
94 // hCount is computed now.
95 static int vCount;
96 // The "AccumulatedHcount" can be adjusted, this is the base.
97 static u32 hCountBase;
98 static int isVblank;
99 static int numSkippedFrames;
100 static bool hasSetMode;
101 static int resumeMode;
102 static int holdMode;
103 static int brightnessLevel;
104 static int mode;
105 static int width;
106 static int height;
107 static bool wasPaused;
108 static bool flippedThisFrame;
109
110 // 1.001f to compensate for the classic 59.94 NTSC framerate that the PSP seems to have.
111 static const double timePerVblank = 1.001f / 60.0f;
112
113 // Don't include this in the state, time increases regardless of state.
114 static double curFrameTime;
115 static double lastFrameTime;
116 static double nextFrameTime;
117 static int numVBlanks;
118 static int numVBlanksSinceFlip;
119
120 static u64 frameStartTicks;
121 const int hCountPerVblank = 286;
122
123 const int PSP_DISPLAY_MODE_LCD = 0;
124
125 std::vector<WaitVBlankInfo> vblankWaitingThreads;
126 // Key is the callback id it was for, or if no callback, the thread id.
127 // Value is the goal vcount number (in case the callback takes >= 1 vcount to return.)
128 std::map<SceUID, int> vblankPausedWaits;
129
130 // STATE END
131
132 // Called when vblank happens (like an internal interrupt.) Not part of state, should be static.
133 std::vector<VblankCallback> vblankListeners;
134
135 // The vblank period is 731.5 us (0.7315 ms)
136 const double vblankMs = 0.7315;
137 // These are guesses based on tests.
138 const double vsyncStartMs = 0.5925;
139 const double vsyncEndMs = 0.7265;
140 const double frameMs = 1001.0 / 60.0;
141
142 enum {
143 PSP_DISPLAY_SETBUF_IMMEDIATE = 0,
144 PSP_DISPLAY_SETBUF_NEXTFRAME = 1
145 };
146
147 static int lastFpsFrame = 0;
148 static double lastFpsTime = 0.0;
149 static double fps = 0.0;
150 static double fpsHistory[120];
151 static int fpsHistorySize = (int)ARRAY_SIZE(fpsHistory);
152 static int fpsHistoryPos = 0;
153 static int fpsHistoryValid = 0;
154 static double frameTimeHistory[600];
155 static double frameSleepHistory[600];
156 static const int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory);
157 static int frameTimeHistoryPos = 0;
158 static int frameTimeHistoryValid = 0;
159 static double lastFrameTimeHistory = 0.0;
160 static int lastNumFlips = 0;
161 static float flips = 0.0f;
162 static int actualFlips = 0; // taking frameskip into account
163 static int lastActualFlips = 0;
164 static float actualFps = 0;
165 // For the "max 60 fps" setting.
166 static int lastFlipsTooFrequent = 0;
167 static u64 lastFlipCycles = 0;
168 static u64 nextFlipCycles = 0;
169
170 void hleEnterVblank(u64 userdata, int cyclesLate);
171 void hleLeaveVblank(u64 userdata, int cyclesLate);
172 void hleAfterFlip(u64 userdata, int cyclesLate);
173 void hleLagSync(u64 userdata, int cyclesLate);
174
175 void __DisplayVblankBeginCallback(SceUID threadID, SceUID prevCallbackId);
176 void __DisplayVblankEndCallback(SceUID threadID, SceUID prevCallbackId);
__DisplayGetFlipCount()177 int __DisplayGetFlipCount() { return actualFlips; }
__DisplayGetVCount()178 int __DisplayGetVCount() { return vCount; }
__DisplayGetNumVblanks()179 int __DisplayGetNumVblanks() { return numVBlanks; }
180
181 void __DisplayFlip(int cyclesLate);
182
ScheduleLagSync(int over=0)183 static void ScheduleLagSync(int over = 0) {
184 lagSyncScheduled = g_Config.bForceLagSync;
185 if (lagSyncScheduled) {
186 // Reset over if it became too high, such as after pausing or initial loading.
187 // There's no real sense in it being more than 1/60th of a second.
188 if (over > 1000000 / 60) {
189 over = 0;
190 }
191 CoreTiming::ScheduleEvent(usToCycles(1000 + over), lagSyncEvent, 0);
192 lastLagSync = time_now_d();
193 }
194 }
195
__DisplayInit()196 void __DisplayInit() {
197 hasSetMode = false;
198 mode = 0;
199 resumeMode = 0;
200 holdMode = 0;
201 brightnessLevel = 84;
202 width = 480;
203 height = 272;
204 numSkippedFrames = 0;
205 numVBlanks = 0;
206 numVBlanksSinceFlip = 0;
207 flippedThisFrame = false;
208 framebufIsLatched = false;
209 framebuf.topaddr = 0x04000000;
210 framebuf.fmt = GE_FORMAT_8888;
211 framebuf.stride = 512;
212 memcpy(&latchedFramebuf, &framebuf, sizeof(latchedFramebuf));
213 lastFlipsTooFrequent = 0;
214 lastFlipCycles = 0;
215 nextFlipCycles = 0;
216 wasPaused = false;
217
218 enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
219 leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);
220 afterFlipEvent = CoreTiming::RegisterEvent("AfterFlip", &hleAfterFlip);
221
222 lagSyncEvent = CoreTiming::RegisterEvent("LagSync", &hleLagSync);
223 ScheduleLagSync();
224
225 CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0);
226 isVblank = 0;
227 frameStartTicks = 0;
228 vCount = 0;
229 hCountBase = 0;
230 curFrameTime = 0.0;
231 nextFrameTime = 0.0;
232 lastFrameTime = 0.0;
233
234 flips = 0;
235 fps = 0.0;
236 actualFlips = 0;
237 lastActualFlips = 0;
238 lastNumFlips = 0;
239 fpsHistoryValid = 0;
240 fpsHistoryPos = 0;
241 frameTimeHistoryValid = 0;
242 frameTimeHistoryPos = 0;
243 lastFrameTimeHistory = 0.0;
244
245 __KernelRegisterWaitTypeFuncs(WAITTYPE_VBLANK, __DisplayVblankBeginCallback, __DisplayVblankEndCallback);
246 }
247
248 struct GPUStatistics_v0 {
249 int firstInts[11];
250 double msProcessingDisplayLists;
251 int moreInts[15];
252 };
253
__DisplayDoState(PointerWrap & p)254 void __DisplayDoState(PointerWrap &p) {
255 auto s = p.Section("sceDisplay", 1, 7);
256 if (!s)
257 return;
258
259 Do(p, framebuf);
260 Do(p, latchedFramebuf);
261 Do(p, framebufIsLatched);
262 Do(p, frameStartTicks);
263 Do(p, vCount);
264 if (s <= 2) {
265 double oldHCountBase;
266 Do(p, oldHCountBase);
267 hCountBase = (int) oldHCountBase;
268 } else {
269 Do(p, hCountBase);
270 }
271 Do(p, isVblank);
272 Do(p, hasSetMode);
273 Do(p, mode);
274 Do(p, resumeMode);
275 Do(p, holdMode);
276 if (s >= 4) {
277 Do(p, brightnessLevel);
278 }
279 Do(p, width);
280 Do(p, height);
281 WaitVBlankInfo wvi(0);
282 Do(p, vblankWaitingThreads, wvi);
283 Do(p, vblankPausedWaits);
284
285 Do(p, enterVblankEvent);
286 CoreTiming::RestoreRegisterEvent(enterVblankEvent, "EnterVBlank", &hleEnterVblank);
287 Do(p, leaveVblankEvent);
288 CoreTiming::RestoreRegisterEvent(leaveVblankEvent, "LeaveVBlank", &hleLeaveVblank);
289 Do(p, afterFlipEvent);
290 CoreTiming::RestoreRegisterEvent(afterFlipEvent, "AfterFlip", &hleAfterFlip);
291
292 if (s >= 5) {
293 Do(p, lagSyncEvent);
294 Do(p, lagSyncScheduled);
295 CoreTiming::RestoreRegisterEvent(lagSyncEvent, "LagSync", &hleLagSync);
296 lastLagSync = time_now_d();
297 if (lagSyncScheduled != g_Config.bForceLagSync) {
298 ScheduleLagSync();
299 }
300 } else {
301 lagSyncEvent = -1;
302 CoreTiming::RestoreRegisterEvent(lagSyncEvent, "LagSync", &hleLagSync);
303 ScheduleLagSync();
304 }
305
306 Do(p, gstate);
307
308 // TODO: GPU stuff is really not the responsibility of sceDisplay.
309 // Display just displays the buffers the GPU has drawn, they are really completely distinct.
310 // Maybe a bit tricky to move at this point, though...
311
312 gstate_c.DoState(p);
313 if (s < 2) {
314 // This shouldn't have been savestated anyway, but it was.
315 // It's unlikely to overlap with the first value in gpuStats.
316 int gpuVendorTemp = 0;
317 p.ExpectVoid(&gpuVendorTemp, sizeof(gpuVendorTemp));
318 }
319 if (s < 6) {
320 GPUStatistics_v0 oldStats;
321 Do(p, oldStats);
322 }
323
324 if (s < 7) {
325 u64 now = CoreTiming::GetTicks();
326 lastFlipCycles = now;
327 nextFlipCycles = now;
328 } else {
329 Do(p, lastFlipCycles);
330 Do(p, nextFlipCycles);
331 }
332
333 gpu->DoState(p);
334
335 if (p.mode == p.MODE_READ) {
336 gpu->ReapplyGfxState();
337
338 if (hasSetMode) {
339 gpu->InitClear();
340 }
341 gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.stride, framebuf.fmt);
342 }
343 }
344
__DisplayShutdown()345 void __DisplayShutdown() {
346 vblankListeners.clear();
347 vblankWaitingThreads.clear();
348 }
349
__DisplayListenVblank(VblankCallback callback)350 void __DisplayListenVblank(VblankCallback callback) {
351 vblankListeners.push_back(callback);
352 }
353
__DisplayFireVblank()354 static void __DisplayFireVblank() {
355 for (std::vector<VblankCallback>::iterator iter = vblankListeners.begin(), end = vblankListeners.end(); iter != end; ++iter) {
356 VblankCallback cb = *iter;
357 cb();
358 }
359 }
360
__DisplayVblankBeginCallback(SceUID threadID,SceUID prevCallbackId)361 void __DisplayVblankBeginCallback(SceUID threadID, SceUID prevCallbackId) {
362 SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
363
364 // This means two callbacks in a row. PSP crashes if the same callback waits inside itself (may need more testing.)
365 // TODO: Handle this better?
366 if (vblankPausedWaits.find(pauseKey) != vblankPausedWaits.end()) {
367 return;
368 }
369
370 WaitVBlankInfo waitData(0);
371 for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
372 WaitVBlankInfo *t = &vblankWaitingThreads[i];
373 if (t->threadID == threadID) {
374 waitData = *t;
375 vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i);
376 break;
377 }
378 }
379
380 if (waitData.threadID != threadID) {
381 WARN_LOG_REPORT(SCEDISPLAY, "sceDisplayWaitVblankCB: could not find waiting thread info.");
382 return;
383 }
384
385 vblankPausedWaits[pauseKey] = vCount + waitData.vcountUnblock;
386 DEBUG_LOG(SCEDISPLAY, "sceDisplayWaitVblankCB: Suspending vblank wait for callback");
387 }
388
__DisplayVblankEndCallback(SceUID threadID,SceUID prevCallbackId)389 void __DisplayVblankEndCallback(SceUID threadID, SceUID prevCallbackId) {
390 SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
391
392 // Probably should not be possible.
393 if (vblankPausedWaits.find(pauseKey) == vblankPausedWaits.end()) {
394 __KernelResumeThreadFromWait(threadID, 0);
395 return;
396 }
397
398 int vcountUnblock = vblankPausedWaits[pauseKey];
399 vblankPausedWaits.erase(pauseKey);
400 if (vcountUnblock <= vCount) {
401 __KernelResumeThreadFromWait(threadID, 0);
402 return;
403 }
404
405 // Still have to wait a bit longer.
406 vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vcountUnblock - vCount));
407 DEBUG_LOG(SCEDISPLAY, "sceDisplayWaitVblankCB: Resuming vblank wait from callback");
408 }
409
410 // TODO: Also average actualFps
__DisplayGetFPS(float * out_vps,float * out_fps,float * out_actual_fps)411 void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps) {
412 *out_vps = fps;
413 *out_fps = flips;
414 *out_actual_fps = actualFps;
415 }
416
__DisplayGetVPS(float * out_vps)417 void __DisplayGetVPS(float *out_vps) {
418 *out_vps = fps;
419 }
420
__DisplayGetAveragedFPS(float * out_vps,float * out_fps)421 void __DisplayGetAveragedFPS(float *out_vps, float *out_fps) {
422 float avg = 0.0;
423 if (fpsHistoryValid > 0) {
424 for (int i = 0; i < fpsHistoryValid; ++i) {
425 avg += fpsHistory[i];
426 }
427 avg /= (double) fpsHistoryValid;
428 }
429
430 *out_vps = *out_fps = avg;
431 }
432
IsRunningSlow()433 static bool IsRunningSlow() {
434 // Allow for some startup turbulence for 8 seconds before assuming things are bad.
435 if (fpsHistoryValid >= 8) {
436 // Look at only the last 15 samples (starting at the 14th sample behind current.)
437 int rangeStart = fpsHistoryPos - std::min(fpsHistoryValid, 14);
438
439 double best = 0.0;
440 for (int i = rangeStart; i <= fpsHistoryPos; ++i) {
441 // rangeStart may have been negative if near a wrap around.
442 int index = (fpsHistorySize + i) % fpsHistorySize;
443 best = std::max(fpsHistory[index], best);
444 }
445
446 return best < System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE) * 0.97;
447 }
448
449 return false;
450 }
451
CalculateFPS()452 static void CalculateFPS() {
453 double now = time_now_d();
454
455 if (now >= lastFpsTime + 1.0) {
456 double frames = (numVBlanks - lastFpsFrame);
457 actualFps = (actualFlips - lastActualFlips);
458
459 fps = frames / (now - lastFpsTime);
460 flips = 60.0 * (double) (gpuStats.numFlips - lastNumFlips) / frames;
461
462 lastFpsFrame = numVBlanks;
463 lastNumFlips = gpuStats.numFlips;
464 lastActualFlips = actualFlips;
465 lastFpsTime = now;
466
467 fpsHistory[fpsHistoryPos++] = fps;
468 fpsHistoryPos = fpsHistoryPos % fpsHistorySize;
469 if (fpsHistoryValid < fpsHistorySize) {
470 ++fpsHistoryValid;
471 }
472 }
473
474 if (g_Config.bDrawFrameGraph) {
475 frameTimeHistory[frameTimeHistoryPos++] = now - lastFrameTimeHistory;
476 lastFrameTimeHistory = now;
477 frameTimeHistoryPos = frameTimeHistoryPos % frameTimeHistorySize;
478 if (frameTimeHistoryValid < frameTimeHistorySize) {
479 ++frameTimeHistoryValid;
480 }
481 frameSleepHistory[frameTimeHistoryPos] = 0.0;
482 }
483 }
484
__DisplayGetFrameTimes(int * out_valid,int * out_pos,double ** out_sleep)485 double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep) {
486 *out_valid = frameTimeHistoryValid;
487 *out_pos = frameTimeHistoryPos;
488 *out_sleep = frameSleepHistory;
489 return frameTimeHistory;
490 }
491
__DisplayGetDebugStats(char * stats,size_t bufsize)492 void __DisplayGetDebugStats(char *stats, size_t bufsize) {
493 char statbuf[4096];
494 gpu->GetStats(statbuf, sizeof(statbuf));
495
496 snprintf(stats, bufsize,
497 "Kernel processing time: %0.2f ms\n"
498 "Slowest syscall: %s : %0.2f ms\n"
499 "Most active syscall: %s : %0.2f ms\n%s",
500 kernelStats.msInSyscalls * 1000.0f,
501 kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)",
502 kernelStats.slowestSyscallTime * 1000.0f,
503 kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)",
504 kernelStats.summedSlowestSyscallTime * 1000.0f,
505 statbuf);
506 }
507
508
509
__DisplaySetWasPaused()510 void __DisplaySetWasPaused() {
511 wasPaused = true;
512 }
513
FrameTimingLimit()514 static int FrameTimingLimit() {
515 if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1)
516 return g_Config.iFpsLimit1;
517 if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2)
518 return g_Config.iFpsLimit2;
519 if (PSP_CoreParameter().fastForward)
520 return 0;
521 return 60;
522 }
523
FrameTimingThrottled()524 static bool FrameTimingThrottled() {
525 return FrameTimingLimit() != 0;
526 }
527
DoFrameDropLogging(float scaledTimestep)528 static void DoFrameDropLogging(float scaledTimestep) {
529 if (lastFrameTime != 0.0 && !wasPaused && lastFrameTime + scaledTimestep < curFrameTime) {
530 const double actualTimestep = curFrameTime - lastFrameTime;
531
532 char stats[4096];
533 __DisplayGetDebugStats(stats, sizeof(stats));
534 NOTICE_LOG(SCEDISPLAY, "Dropping frames - budget = %.2fms / %.1ffps, actual = %.2fms (+%.2fms) / %.1ffps\n%s", scaledTimestep * 1000.0, 1.0 / scaledTimestep, actualTimestep * 1000.0, (actualTimestep - scaledTimestep) * 1000.0, 1.0 / actualTimestep, stats);
535 }
536 }
537
CalculateFrameSkip()538 static int CalculateFrameSkip() {
539 int frameSkipNum;
540 if (g_Config.iFrameSkipType == 1) {
541 // Calculate the frames to skip dynamically using the set percentage of the current fps
542 frameSkipNum = ceil( flips * (static_cast<double>(g_Config.iFrameSkip) / 100.00) );
543 } else {
544 // Use the set number of frames to skip
545 frameSkipNum = g_Config.iFrameSkip;
546 }
547 return frameSkipNum;
548 }
549
550 // Let's collect all the throttling and frameskipping logic here.
DoFrameTiming(bool & throttle,bool & skipFrame,float timestep)551 static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) {
552 PROFILE_THIS_SCOPE("timing");
553 int fpsLimit = FrameTimingLimit();
554 throttle = FrameTimingThrottled();
555 skipFrame = false;
556
557 // Check if the frameskipping code should be enabled. If neither throttling or frameskipping is on,
558 // we have nothing to do here.
559 bool doFrameSkip = g_Config.iFrameSkip != 0;
560
561 bool fastForwardNeedsSkip = g_Config.iFastForwardMode == (int)FastForwardMode::SKIP_DRAW;
562 if (!throttle && fastForwardNeedsSkip) {
563 skipFrame = true;
564 if (numSkippedFrames >= 7) {
565 skipFrame = false;
566 }
567 return;
568 }
569
570 if (!throttle && !doFrameSkip)
571 return;
572
573 float scaledTimestep = timestep;
574 if (fpsLimit > 0 && fpsLimit != 60) {
575 scaledTimestep *= 60.0f / fpsLimit;
576 }
577
578 if (lastFrameTime == 0.0 || wasPaused) {
579 nextFrameTime = time_now_d() + scaledTimestep;
580 } else {
581 // Advance lastFrameTime by a constant amount each frame,
582 // but don't let it get too far behind as things can get very jumpy.
583 const double maxFallBehindFrames = 5.5;
584
585 nextFrameTime = std::max(lastFrameTime + scaledTimestep, time_now_d() - maxFallBehindFrames * scaledTimestep);
586 }
587 curFrameTime = time_now_d();
588
589 if (g_Config.bLogFrameDrops) {
590 DoFrameDropLogging(scaledTimestep);
591 }
592
593 // Auto-frameskip automatically if speed limit is set differently than the default.
594 bool forceFrameskip = fpsLimit > 60 && fastForwardNeedsSkip;
595 int frameSkipNum = CalculateFrameSkip();
596 if (g_Config.bAutoFrameSkip || forceFrameskip) {
597 // autoframeskip
598 // Argh, we are falling behind! Let's skip a frame and see if we catch up.
599 if (curFrameTime > nextFrameTime && doFrameSkip) {
600 skipFrame = true;
601 if (forceFrameskip) {
602 throttle = false;
603 }
604 }
605 } else if (frameSkipNum >= 1) {
606 // fixed frameskip
607 if (numSkippedFrames >= frameSkipNum)
608 skipFrame = false;
609 else
610 skipFrame = true;
611 }
612
613 // TODO: This is NOT where we should wait, really! We should mark each outgoing frame with the desired
614 // timestamp to push it to display, and sleep in the render thread to achieve that.
615
616 if (curFrameTime < nextFrameTime && throttle) {
617 // If time gap is huge just jump (somebody fast-forwarded)
618 if (nextFrameTime - curFrameTime > 2*scaledTimestep) {
619 nextFrameTime = curFrameTime;
620 } else {
621 // Wait until we've caught up.
622 while (time_now_d() < nextFrameTime) {
623 #ifdef _WIN32
624 sleep_ms(1); // Sleep for 1ms on this thread
625 #else
626 const double left = nextFrameTime - curFrameTime;
627 usleep((long)(left * 1000000));
628 #endif
629 }
630 }
631 curFrameTime = time_now_d();
632 }
633
634 lastFrameTime = nextFrameTime;
635 wasPaused = false;
636 }
637
DoFrameIdleTiming()638 static void DoFrameIdleTiming() {
639 PROFILE_THIS_SCOPE("timing");
640 if (!FrameTimingThrottled() || !g_Config.bEnableSound || wasPaused) {
641 return;
642 }
643
644 double before = time_now_d();
645 double dist = before - lastFrameTime;
646 // Ignore if the distance is just crazy. May mean wrap or pause.
647 if (dist < 0.0 || dist >= 15 * timePerVblank) {
648 return;
649 }
650
651 float scaledVblank = timePerVblank;
652 int fpsLimit = FrameTimingLimit();
653 if (fpsLimit != 0 && fpsLimit != 60) {
654 // 0 is handled in FrameTimingThrottled().
655 scaledVblank *= 60.0f / fpsLimit;
656 }
657
658 // If we have over at least a vblank of spare time, maintain at least 30fps in delay.
659 // This prevents fast forward during loading screens.
660 // Give a little extra wiggle room in case the next vblank does more work.
661 const double goal = lastFrameTime + (numVBlanksSinceFlip - 1) * scaledVblank - 0.001;
662 if (numVBlanksSinceFlip >= 2 && before < goal) {
663 double cur_time;
664 while ((cur_time = time_now_d()) < goal) {
665 #ifdef _WIN32
666 sleep_ms(1);
667 #else
668 const double left = goal - cur_time;
669 usleep((long)(left * 1000000));
670 #endif
671 }
672
673 if (g_Config.bDrawFrameGraph) {
674 frameSleepHistory[frameTimeHistoryPos] += time_now_d() - before;
675 }
676 }
677 }
678
679
hleEnterVblank(u64 userdata,int cyclesLate)680 void hleEnterVblank(u64 userdata, int cyclesLate) {
681 int vbCount = userdata;
682
683 VERBOSE_LOG(SCEDISPLAY, "Enter VBlank %i", vbCount);
684
685 isVblank = 1;
686 vCount++; // vCount increases at each VBLANK.
687 hCountBase += hCountPerVblank; // This is the "accumulated" hcount base.
688 if (hCountBase > 0x7FFFFFFF) {
689 hCountBase -= 0x80000000;
690 }
691 frameStartTicks = CoreTiming::GetTicks();
692
693 CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1);
694
695 // Trigger VBlank interrupt handlers.
696 __TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL);
697
698 // Wake up threads waiting for VBlank
699 u32 error;
700 bool wokeThreads = false;
701 for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
702 if (--vblankWaitingThreads[i].vcountUnblock == 0) {
703 // Only wake it if it wasn't already released by someone else.
704 SceUID waitID = __KernelGetWaitID(vblankWaitingThreads[i].threadID, WAITTYPE_VBLANK, error);
705 if (waitID == 1) {
706 __KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
707 wokeThreads = true;
708 }
709 vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
710 }
711 }
712 if (wokeThreads) {
713 __KernelReSchedule("entered vblank");
714 }
715
716 numVBlanks++;
717 numVBlanksSinceFlip++;
718
719 // TODO: Should this be done here or in hleLeaveVblank?
720 if (framebufIsLatched) {
721 DEBUG_LOG(SCEDISPLAY, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
722 framebuf = latchedFramebuf;
723 framebufIsLatched = false;
724 gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.stride, framebuf.fmt);
725 __DisplayFlip(cyclesLate);
726 } else if (!flippedThisFrame) {
727 // Gotta flip even if sceDisplaySetFramebuf was not called.
728 __DisplayFlip(cyclesLate);
729 }
730 }
731
__DisplayFlip(int cyclesLate)732 void __DisplayFlip(int cyclesLate) {
733 flippedThisFrame = true;
734 // We flip only if the framebuffer was dirty. This eliminates flicker when using
735 // non-buffered rendering. The interaction with frame skipping seems to need
736 // some work.
737 // But, let's flip at least once every 10 vblanks, to update fps, etc.
738 const bool noRecentFlip = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE && numVBlanksSinceFlip >= 10;
739 // Also let's always flip for animated shaders.
740 bool postEffectRequiresFlip = false;
741
742 bool duplicateFrames = g_Config.bRenderDuplicateFrames && g_Config.iFrameSkip == 0;
743
744 bool fastForwardNeedsSkip = g_Config.iFastForwardMode != (int)FastForwardMode::CONTINUOUS;
745 bool fastForwardSkipFlip = g_Config.iFastForwardMode == (int)FastForwardMode::SKIP_FLIP;
746 if (g_Config.bVSync && GetGPUBackend() == GPUBackend::VULKAN) {
747 // Vulkan doesn't support the interval setting, so we force skipping the flip.
748 fastForwardSkipFlip = true;
749 }
750
751 // postEffectRequiresFlip is not compatible with frameskip fast-forward, see #12325.
752 if (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE && !(fastForwardNeedsSkip && !FrameTimingThrottled())) {
753 postEffectRequiresFlip = duplicateFrames || g_Config.bShaderChainRequires60FPS;
754 }
755
756 const bool fbDirty = gpu->FramebufferDirty();
757
758 if (fbDirty || noRecentFlip || postEffectRequiresFlip) {
759 int frameSleepPos = frameTimeHistoryPos;
760 CalculateFPS();
761
762 // Let the user know if we're running slow, so they know to adjust settings.
763 // Sometimes users just think the sound emulation is broken.
764 static bool hasNotifiedSlow = false;
765 if (!g_Config.bHideSlowWarnings &&
766 !hasNotifiedSlow &&
767 PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL &&
768 IsRunningSlow()) {
769 #ifndef _DEBUG
770 auto err = GetI18NCategory("Error");
771 if (g_Config.bSoftwareRendering) {
772 host->NotifyUserMessage(err->T("Running slow: Try turning off Software Rendering"), 6.0f, 0xFF30D0D0);
773 } else {
774 host->NotifyUserMessage(err->T("Running slow: try frameskip, sound is choppy when slow"), 6.0f, 0xFF30D0D0);
775 }
776 #endif
777 hasNotifiedSlow = true;
778 }
779
780 bool forceNoFlip = false;
781 float refreshRate = System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE);
782 // Avoid skipping on devices that have 58 or 59 FPS, except when alternate speed is set.
783 bool refreshRateNeedsSkip = FrameTimingLimit() != 60 && FrameTimingLimit() > refreshRate;
784 // Alternative to frameskip fast-forward, where we draw everything.
785 // Useful if skipping a frame breaks graphics or for checking drawing speed.
786 if (fastForwardSkipFlip && (!FrameTimingThrottled() || refreshRateNeedsSkip)) {
787 static double lastFlip = 0;
788 double now = time_now_d();
789 if ((now - lastFlip) < 1.0f / refreshRate) {
790 forceNoFlip = true;
791 } else {
792 lastFlip = now;
793 }
794 }
795
796 // Setting CORE_NEXTFRAME causes a swap.
797 const bool fbReallyDirty = gpu->FramebufferReallyDirty();
798 if (fbReallyDirty || noRecentFlip || postEffectRequiresFlip) {
799 // Check first though, might've just quit / been paused.
800 if (!forceNoFlip && Core_NextFrame()) {
801 gpu->CopyDisplayToOutput(fbReallyDirty);
802 if (fbReallyDirty) {
803 actualFlips++;
804 }
805 }
806 }
807
808 if (fbDirty) {
809 gpuStats.numFlips++;
810 }
811
812 bool throttle, skipFrame;
813 DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * timePerVblank);
814
815 int maxFrameskip = 8;
816 int frameSkipNum = CalculateFrameSkip();
817 if (throttle) {
818 // 4 here means 1 drawn, 4 skipped - so 12 fps minimum.
819 maxFrameskip = frameSkipNum;
820 }
821 if (numSkippedFrames >= maxFrameskip || GPURecord::IsActivePending()) {
822 skipFrame = false;
823 }
824
825 if (skipFrame) {
826 gstate_c.skipDrawReason |= SKIPDRAW_SKIPFRAME;
827 numSkippedFrames++;
828 } else {
829 gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;
830 numSkippedFrames = 0;
831 }
832
833 // Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame).
834 // Right after, we regain control for a little bit in hleAfterFlip. I think that's a great
835 // place to do housekeeping.
836
837 CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0);
838 numVBlanksSinceFlip = 0;
839
840 if (g_Config.bDrawFrameGraph) {
841 // Track how long we sleep (whether vsync or sleep_ms.)
842 frameSleepHistory[frameSleepPos] += time_now_d() - lastFrameTimeHistory;
843 }
844 } else {
845 // Okay, there's no new frame to draw. But audio may be playing, so we need to time still.
846 DoFrameIdleTiming();
847 }
848 }
849
hleAfterFlip(u64 userdata,int cyclesLate)850 void hleAfterFlip(u64 userdata, int cyclesLate) {
851 gpu->BeginFrame(); // doesn't really matter if begin or end of frame.
852 PPGeNotifyFrame();
853
854 // This seems like as good a time as any to check if the config changed.
855 if (lagSyncScheduled != g_Config.bForceLagSync) {
856 ScheduleLagSync();
857 }
858 }
859
hleLeaveVblank(u64 userdata,int cyclesLate)860 void hleLeaveVblank(u64 userdata, int cyclesLate) {
861 isVblank = 0;
862 flippedThisFrame = false;
863 VERBOSE_LOG(SCEDISPLAY,"Leave VBlank %i", (int)userdata - 1);
864 CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs) - cyclesLate, enterVblankEvent, userdata);
865
866 // Fire the vblank listeners after the vblank completes.
867 __DisplayFireVblank();
868 }
869
hleLagSync(u64 userdata,int cyclesLate)870 void hleLagSync(u64 userdata, int cyclesLate) {
871 // The goal here is to prevent network, audio, and input lag from the real world.
872 // Our normal timing is very "stop and go". This is efficient, but causes real world lag.
873 // This event (optionally) runs every 1ms to sync with the real world.
874 PROFILE_THIS_SCOPE("timing");
875
876 if (!FrameTimingThrottled()) {
877 lagSyncScheduled = false;
878 return;
879 }
880
881 float scale = 1.0f;
882 int fpsLimit = FrameTimingLimit();
883 if (fpsLimit != 0 && fpsLimit != 60) {
884 // 0 is handled in FrameTimingThrottled().
885 scale = 60.0f / fpsLimit;
886 }
887
888 const double goal = lastLagSync + (scale / 1000.0f);
889 double before = time_now_d();
890 // Don't lag too long ever, if they leave it paused.
891 double now = before;
892 while (now < goal && goal < now + 0.01) {
893 // Tight loop on win32 - intentionally, as timing is otherwise not precise enough.
894 #ifndef _WIN32
895 const double left = goal - now;
896 usleep((long)(left * 1000000.0));
897 #endif
898 now = time_now_d();
899 }
900
901 const int emuOver = (int)cyclesToUs(cyclesLate);
902 const int over = (int)((now - goal) * 1000000);
903 ScheduleLagSync(over - emuOver);
904
905 if (g_Config.bDrawFrameGraph) {
906 frameSleepHistory[frameTimeHistoryPos] += now - before;
907 }
908 }
909
sceDisplayIsVblank()910 static u32 sceDisplayIsVblank() {
911 return hleLogSuccessI(SCEDISPLAY, isVblank);
912 }
913
DisplayWaitForVblanks(const char * reason,int vblanks,bool callbacks=false)914 static int DisplayWaitForVblanks(const char *reason, int vblanks, bool callbacks = false) {
915 const s64 ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks;
916 const s64 cyclesToNextVblank = msToCycles(frameMs) - ticksIntoFrame;
917
918 // These syscalls take about 115 us, so if the next vblank is before then, we're waiting extra.
919 // At least, on real firmware a wait >= 16500 into the frame will wait two.
920 if (cyclesToNextVblank <= usToCycles(115)) {
921 ++vblanks;
922 }
923
924 vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vblanks));
925 __KernelWaitCurThread(WAITTYPE_VBLANK, 1, 0, 0, callbacks, reason);
926
927 return hleLogSuccessVerboseI(SCEDISPLAY, 0, "waiting for %d vblanks", vblanks);
928 }
929
sceDisplaySetMode(int displayMode,int displayWidth,int displayHeight)930 static u32 sceDisplaySetMode(int displayMode, int displayWidth, int displayHeight) {
931 if (displayMode != PSP_DISPLAY_MODE_LCD || displayWidth != 480 || displayHeight != 272) {
932 WARN_LOG_REPORT(SCEDISPLAY, "Video out requested, not supported: mode=%d size=%d,%d", displayMode, displayWidth, displayHeight);
933 }
934 if (displayMode != PSP_DISPLAY_MODE_LCD) {
935 return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_MODE, "invalid mode");
936 }
937 if (displayWidth != 480 || displayHeight != 272) {
938 return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_SIZE, "invalid size");
939 }
940
941 if (!hasSetMode) {
942 gpu->InitClear();
943 hasSetMode = true;
944 }
945 mode = displayMode;
946 width = displayWidth;
947 height = displayHeight;
948
949 hleLogSuccessI(SCEDISPLAY, 0);
950 // On success, this implicitly waits for a vblank start.
951 return DisplayWaitForVblanks("display mode", 1);
952 }
953
__DisplaySetFramebuf(u32 topaddr,int linesize,int pixelFormat,int sync)954 void __DisplaySetFramebuf(u32 topaddr, int linesize, int pixelFormat, int sync) {
955 FrameBufferState fbstate = {0};
956 fbstate.topaddr = topaddr;
957 fbstate.fmt = (GEBufferFormat)pixelFormat;
958 fbstate.stride = linesize;
959
960 if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) {
961 // Write immediately to the current framebuffer parameters.
962 framebuf = fbstate;
963 // Also update latchedFramebuf for any sceDisplayGetFramebuf() after this.
964 latchedFramebuf = fbstate;
965 gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.stride, framebuf.fmt);
966 // IMMEDIATE means that the buffer is fine. We can just flip immediately.
967 // Doing it in non-buffered though creates problems (black screen) on occasion though
968 // so let's not.
969 if (!flippedThisFrame && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) {
970 double before_flip = time_now_d();
971 __DisplayFlip(0);
972 double after_flip = time_now_d();
973 // Ignore for debug stats.
974 hleSetFlipTime(after_flip - before_flip);
975 }
976 } else {
977 // Delay the write until vblank
978 latchedFramebuf = fbstate;
979 framebufIsLatched = true;
980
981 // If we update the format or stride, this affects the current framebuf immediately.
982 framebuf.fmt = latchedFramebuf.fmt;
983 framebuf.stride = latchedFramebuf.stride;
984 }
985 }
986
987 // Some games (GTA) never call this during gameplay, so bad place to put a framerate counter.
sceDisplaySetFramebuf(u32 topaddr,int linesize,int pixelformat,int sync)988 u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) {
989 if (sync != PSP_DISPLAY_SETBUF_IMMEDIATE && sync != PSP_DISPLAY_SETBUF_NEXTFRAME) {
990 return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_MODE, "invalid sync mode");
991 }
992 if (topaddr != 0 && !Memory::IsRAMAddress(topaddr) && !Memory::IsVRAMAddress(topaddr)) {
993 return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_POINTER, "invalid address");
994 }
995 if ((topaddr & 0xF) != 0) {
996 return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_POINTER, "misaligned address");
997 }
998 if ((linesize & 0x3F) != 0 || (linesize == 0 && topaddr != 0)) {
999 return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_SIZE, "invalid stride");
1000 }
1001 if (pixelformat < 0 || pixelformat > GE_FORMAT_8888) {
1002 return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_FORMAT, "invalid format");
1003 }
1004
1005 if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) {
1006 if ((GEBufferFormat)pixelformat != latchedFramebuf.fmt || linesize != latchedFramebuf.stride) {
1007 return hleReportError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_MODE, "must change latched framebuf first");
1008 }
1009 }
1010
1011 hleEatCycles(290);
1012
1013 s64 delayCycles = 0;
1014 // Don't count transitions between display off and display on.
1015 if (topaddr != 0 && topaddr != framebuf.topaddr && framebuf.topaddr != 0 && PSP_CoreParameter().compat.flags().ForceMax60FPS) {
1016 // sceDisplaySetFramebuf() isn't supposed to delay threads at all. This is a hack.
1017 // So let's only delay when it's more than 1ms.
1018 const s64 FLIP_DELAY_CYCLES_MIN = usToCycles(1000);
1019 // Some games (like Final Fantasy 4) only call this too much in spurts.
1020 // The goal is to fix games where this would result in a consistent overhead.
1021 const int FLIP_DELAY_MIN_FLIPS = 30;
1022 // Since we move nextFlipCycles forward a whole frame each time, we allow it to be a little ahead.
1023 // Otherwise it'll always be ahead if the game messes up even once.
1024 const s64 LEEWAY_CYCLES_PER_FLIP = usToCycles(10);
1025
1026 u64 now = CoreTiming::GetTicks();
1027 s64 cyclesAhead = nextFlipCycles - now;
1028 if (cyclesAhead > FLIP_DELAY_CYCLES_MIN) {
1029 if (lastFlipsTooFrequent >= FLIP_DELAY_MIN_FLIPS) {
1030 delayCycles = cyclesAhead;
1031 } else {
1032 ++lastFlipsTooFrequent;
1033 }
1034 } else if (-lastFlipsTooFrequent < FLIP_DELAY_MIN_FLIPS) {
1035 --lastFlipsTooFrequent;
1036 }
1037
1038 // 1001 to account for NTSC timing (59.94 fps.)
1039 u64 expected = msToCycles(1001) / 60 - LEEWAY_CYCLES_PER_FLIP;
1040 lastFlipCycles = now;
1041 nextFlipCycles = std::max(lastFlipCycles, nextFlipCycles) + expected;
1042 }
1043
1044 __DisplaySetFramebuf(topaddr, linesize, pixelformat, sync);
1045
1046 // No delaying while inside an interrupt. It'll cause idle threads to starve.
1047 if (delayCycles > 0 && !__IsInInterrupt()) {
1048 // Okay, the game is going at too high a frame rate. God of War and Fat Princess both do this.
1049 // Simply eating the cycles works and is fast, but breaks other games (like Jeanne d'Arc.)
1050 // So, instead, we delay this HLE thread only (a small deviation from correct behavior.)
1051 return hleDelayResult(hleLogSuccessI(SCEDISPLAY, 0, "delaying frame thread"), "set framebuf", cyclesToUs(delayCycles));
1052 } else {
1053 if (topaddr == 0) {
1054 return hleLogSuccessI(SCEDISPLAY, 0, "disabling display");
1055 } else {
1056 return hleLogSuccessI(SCEDISPLAY, 0);
1057 }
1058 }
1059 }
1060
__DisplayGetFramebuf(PSPPointer<u8> * topaddr,u32 * linesize,u32 * pixelFormat,int latchedMode)1061 bool __DisplayGetFramebuf(PSPPointer<u8> *topaddr, u32 *linesize, u32 *pixelFormat, int latchedMode) {
1062 const FrameBufferState &fbState = latchedMode == PSP_DISPLAY_SETBUF_NEXTFRAME ? latchedFramebuf : framebuf;
1063 if (topaddr != nullptr)
1064 (*topaddr).ptr = fbState.topaddr;
1065 if (linesize != nullptr)
1066 *linesize = fbState.stride;
1067 if (pixelFormat != nullptr)
1068 *pixelFormat = fbState.fmt;
1069
1070 return true;
1071 }
1072
sceDisplayGetFramebuf(u32 topaddrPtr,u32 linesizePtr,u32 pixelFormatPtr,int latchedMode)1073 static u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int latchedMode) {
1074 const FrameBufferState &fbState = latchedMode == PSP_DISPLAY_SETBUF_NEXTFRAME ? latchedFramebuf : framebuf;
1075
1076 if (Memory::IsValidAddress(topaddrPtr))
1077 Memory::Write_U32(fbState.topaddr, topaddrPtr);
1078 if (Memory::IsValidAddress(linesizePtr))
1079 Memory::Write_U32(fbState.stride, linesizePtr);
1080 if (Memory::IsValidAddress(pixelFormatPtr))
1081 Memory::Write_U32(fbState.fmt, pixelFormatPtr);
1082
1083 return hleLogSuccessI(SCEDISPLAY, 0);
1084 }
1085
DisplayWaitForVblanksCB(const char * reason,int vblanks)1086 static int DisplayWaitForVblanksCB(const char *reason, int vblanks) {
1087 return DisplayWaitForVblanks(reason, vblanks, true);
1088 }
1089
sceDisplayWaitVblankStart()1090 static u32 sceDisplayWaitVblankStart() {
1091 return DisplayWaitForVblanks("vblank start waited", 1);
1092 }
1093
sceDisplayWaitVblank()1094 static u32 sceDisplayWaitVblank() {
1095 if (!isVblank) {
1096 return DisplayWaitForVblanks("vblank waited", 1);
1097 } else {
1098 hleEatCycles(1110);
1099 hleReSchedule("vblank wait skipped");
1100 return hleLogSuccessI(SCEDISPLAY, 1, "not waiting since in vblank");
1101 }
1102 }
1103
sceDisplayWaitVblankStartMulti(int vblanks)1104 static u32 sceDisplayWaitVblankStartMulti(int vblanks) {
1105 if (vblanks <= 0) {
1106 return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid number of vblanks");
1107 }
1108 if (!__KernelIsDispatchEnabled())
1109 return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
1110 if (__IsInInterrupt())
1111 return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "in interrupt");
1112
1113 return DisplayWaitForVblanks("vblank start multi waited", vblanks);
1114 }
1115
sceDisplayWaitVblankCB()1116 static u32 sceDisplayWaitVblankCB() {
1117 if (!isVblank) {
1118 return DisplayWaitForVblanksCB("vblank waited", 1);
1119 } else {
1120 hleEatCycles(1110);
1121 hleReSchedule("vblank wait skipped");
1122 return hleLogSuccessI(SCEDISPLAY, 1, "not waiting since in vblank");
1123 }
1124 }
1125
sceDisplayWaitVblankStartCB()1126 static u32 sceDisplayWaitVblankStartCB() {
1127 return DisplayWaitForVblanksCB("vblank start waited", 1);
1128 }
1129
sceDisplayWaitVblankStartMultiCB(int vblanks)1130 static u32 sceDisplayWaitVblankStartMultiCB(int vblanks) {
1131 if (vblanks <= 0) {
1132 return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid number of vblanks");
1133 }
1134 if (!__KernelIsDispatchEnabled())
1135 return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
1136 if (__IsInInterrupt())
1137 return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "in interrupt");
1138
1139 return DisplayWaitForVblanksCB("vblank start multi waited", vblanks);
1140 }
1141
sceDisplayGetVcount()1142 static u32 sceDisplayGetVcount() {
1143 hleEatCycles(150);
1144 hleReSchedule("get vcount");
1145 return hleLogSuccessVerboseI(SCEDISPLAY, vCount);
1146 }
1147
__DisplayGetCurrentHcount()1148 static u32 __DisplayGetCurrentHcount() {
1149 const int ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks;
1150 const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank;
1151 // Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right.
1152 return 1 + (ticksIntoFrame / ticksPerVblank);
1153 }
1154
__DisplayGetAccumulatedHcount()1155 static u32 __DisplayGetAccumulatedHcount() {
1156 // The hCount is always a positive int, and wraps from 0x7FFFFFFF -> 0.
1157 int value = hCountBase + __DisplayGetCurrentHcount();
1158 return value & 0x7FFFFFFF;
1159 }
1160
sceDisplayGetCurrentHcount()1161 static u32 sceDisplayGetCurrentHcount() {
1162 hleEatCycles(275);
1163 return hleLogSuccessI(SCEDISPLAY, __DisplayGetCurrentHcount());
1164 }
1165
sceDisplayAdjustAccumulatedHcount(int value)1166 static int sceDisplayAdjustAccumulatedHcount(int value) {
1167 if (value < 0) {
1168 return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid value");
1169 }
1170
1171 // Since it includes the current hCount, find the difference to apply to the base.
1172 u32 accumHCount = __DisplayGetAccumulatedHcount();
1173 int diff = value - accumHCount;
1174 hCountBase += diff;
1175
1176 return hleLogSuccessI(SCEDISPLAY, 0);
1177 }
1178
sceDisplayGetAccumulatedHcount()1179 static int sceDisplayGetAccumulatedHcount() {
1180 u32 accumHCount = __DisplayGetAccumulatedHcount();
1181 hleEatCycles(235);
1182 return hleLogSuccessI(SCEDISPLAY, accumHCount);
1183 }
1184
sceDisplayGetFramePerSec()1185 static float sceDisplayGetFramePerSec() {
1186 const static float framePerSec = 59.9400599f;
1187 VERBOSE_LOG(SCEDISPLAY,"%f=sceDisplayGetFramePerSec()", framePerSec);
1188 return framePerSec; // (9MHz * 1)/(525 * 286)
1189 }
1190
sceDisplayIsForeground()1191 static u32 sceDisplayIsForeground() {
1192 int result = hasSetMode && framebuf.topaddr != 0 ? 1 : 0;
1193 return hleLogSuccessI(SCEDISPLAY, result);
1194 }
1195
sceDisplayGetMode(u32 modeAddr,u32 widthAddr,u32 heightAddr)1196 static u32 sceDisplayGetMode(u32 modeAddr, u32 widthAddr, u32 heightAddr) {
1197 if (Memory::IsValidAddress(modeAddr))
1198 Memory::Write_U32(mode, modeAddr);
1199 if (Memory::IsValidAddress(widthAddr))
1200 Memory::Write_U32(width, widthAddr);
1201 if (Memory::IsValidAddress(heightAddr))
1202 Memory::Write_U32(height, heightAddr);
1203 return hleLogSuccessI(SCEDISPLAY, 0);
1204 }
1205
sceDisplayIsVsync()1206 static u32 sceDisplayIsVsync() {
1207 u64 now = CoreTiming::GetTicks();
1208 u64 start = frameStartTicks + msToCycles(vsyncStartMs);
1209 u64 end = frameStartTicks + msToCycles(vsyncEndMs);
1210
1211 return hleLogSuccessI(SCEDISPLAY, now >= start && now <= end ? 1 : 0);
1212 }
1213
sceDisplayGetResumeMode(u32 resumeModeAddr)1214 static u32 sceDisplayGetResumeMode(u32 resumeModeAddr) {
1215 if (Memory::IsValidAddress(resumeModeAddr))
1216 Memory::Write_U32(resumeMode, resumeModeAddr);
1217 return hleLogSuccessI(SCEDISPLAY, 0);
1218 }
1219
sceDisplaySetResumeMode(u32 rMode)1220 static u32 sceDisplaySetResumeMode(u32 rMode) {
1221 // Not sure what this does, seems to do nothing in tests and accept all values.
1222 resumeMode = rMode;
1223 return hleReportError(SCEDISPLAY, 0, "unsupported");
1224 }
1225
sceDisplayGetBrightness(u32 levelAddr,u32 otherAddr)1226 static u32 sceDisplayGetBrightness(u32 levelAddr, u32 otherAddr) {
1227 // Standard levels on a PSP: 44, 60, 72, 84 (AC only)
1228
1229 if (Memory::IsValidAddress(levelAddr)) {
1230 Memory::Write_U32(brightnessLevel, levelAddr);
1231 }
1232 // Always seems to write zero?
1233 if (Memory::IsValidAddress(otherAddr)) {
1234 Memory::Write_U32(0, otherAddr);
1235 }
1236 return hleLogWarning(SCEDISPLAY, 0);
1237 }
1238
sceDisplaySetBrightness(int level,int other)1239 static u32 sceDisplaySetBrightness(int level, int other) {
1240 // Note: Only usable in kernel mode.
1241 brightnessLevel = level;
1242 return hleLogWarning(SCEDISPLAY, 0);
1243 }
1244
sceDisplaySetHoldMode(u32 hMode)1245 static u32 sceDisplaySetHoldMode(u32 hMode) {
1246 // Not sure what this does, seems to do nothing in tests and accept all values.
1247 holdMode = hMode;
1248 return hleReportError(SCEDISPLAY, 0, "unsupported");
1249 }
1250
1251 const HLEFunction sceDisplay[] = {
1252 {0X0E20F177, &WrapU_III<sceDisplaySetMode>, "sceDisplaySetMode", 'x', "iii" },
1253 {0X289D82FE, &WrapU_UIII<sceDisplaySetFramebuf>, "sceDisplaySetFrameBuf", 'x', "xiii"},
1254 {0XEEDA2E54, &WrapU_UUUI<sceDisplayGetFramebuf>, "sceDisplayGetFrameBuf", 'x', "pppi"},
1255 {0X36CDFADE, &WrapU_V<sceDisplayWaitVblank>, "sceDisplayWaitVblank", 'x', "", HLE_NOT_DISPATCH_SUSPENDED },
1256 {0X984C27E7, &WrapU_V<sceDisplayWaitVblankStart>, "sceDisplayWaitVblankStart", 'x', "", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
1257 {0X40F1469C, &WrapU_I<sceDisplayWaitVblankStartMulti>, "sceDisplayWaitVblankStartMulti", 'x', "i" },
1258 {0X8EB9EC49, &WrapU_V<sceDisplayWaitVblankCB>, "sceDisplayWaitVblankCB", 'x', "", HLE_NOT_DISPATCH_SUSPENDED },
1259 {0X46F186C3, &WrapU_V<sceDisplayWaitVblankStartCB>, "sceDisplayWaitVblankStartCB", 'x', "", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
1260 {0X77ED8B3A, &WrapU_I<sceDisplayWaitVblankStartMultiCB>, "sceDisplayWaitVblankStartMultiCB", 'x', "i" },
1261 {0XDBA6C4C4, &WrapF_V<sceDisplayGetFramePerSec>, "sceDisplayGetFramePerSec", 'f', "" },
1262 {0X773DD3A3, &WrapU_V<sceDisplayGetCurrentHcount>, "sceDisplayGetCurrentHcount", 'x', "" },
1263 {0X210EAB3A, &WrapI_V<sceDisplayGetAccumulatedHcount>, "sceDisplayGetAccumulatedHcount", 'i', "" },
1264 {0XA83EF139, &WrapI_I<sceDisplayAdjustAccumulatedHcount>, "sceDisplayAdjustAccumulatedHcount", 'i', "i" },
1265 {0X9C6EAAD7, &WrapU_V<sceDisplayGetVcount>, "sceDisplayGetVcount", 'x', "" },
1266 {0XDEA197D4, &WrapU_UUU<sceDisplayGetMode>, "sceDisplayGetMode", 'x', "ppp" },
1267 {0X7ED59BC4, &WrapU_U<sceDisplaySetHoldMode>, "sceDisplaySetHoldMode", 'x', "x" },
1268 {0XA544C486, &WrapU_U<sceDisplaySetResumeMode>, "sceDisplaySetResumeMode", 'x', "x" },
1269 {0XBF79F646, &WrapU_U<sceDisplayGetResumeMode>, "sceDisplayGetResumeMode", 'x', "p" },
1270 {0XB4F378FA, &WrapU_V<sceDisplayIsForeground>, "sceDisplayIsForeground", 'x', "" },
1271 {0X31C4BAA8, &WrapU_UU<sceDisplayGetBrightness>, "sceDisplayGetBrightness", 'x', "pp" },
1272 {0X9E3C6DC6, &WrapU_II<sceDisplaySetBrightness>, "sceDisplaySetBrightness", 'x', "ii" },
1273 {0X4D4E10EC, &WrapU_V<sceDisplayIsVblank>, "sceDisplayIsVblank", 'x', "" },
1274 {0X21038913, &WrapU_V<sceDisplayIsVsync>, "sceDisplayIsVsync", 'x', "" },
1275 };
1276
Register_sceDisplay()1277 void Register_sceDisplay() {
1278 RegisterModule("sceDisplay", ARRAY_SIZE(sceDisplay), sceDisplay);
1279 }
1280
Register_sceDisplay_driver()1281 void Register_sceDisplay_driver() {
1282 RegisterModule("sceDisplay_driver", ARRAY_SIZE(sceDisplay), sceDisplay);
1283 }
1284