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