예제 #1
0
// Some games (GTA) never call this during gameplay, so bad place to put a framerate counter.
static u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) {
	FrameBufferState fbstate = {0};
	if (topaddr != 0) {
		fbstate.topaddr = topaddr;
		fbstate.pspFramebufFormat = (GEBufferFormat)pixelformat;
		fbstate.pspFramebufLinesize = linesize;
	}

	hleEatCycles(290);

	s64 delayCycles = 0;
	if (topaddr != framebuf.topaddr && g_Config.iForceMaxEmulatedFPS > 0) {
		// Sometimes we get a small number, there's probably no need to delay the thread for this.
		// sceDisplaySetFramebuf() isn't supposed to delay threads at all.  This is a hack.
		const int FLIP_DELAY_CYCLES_MIN = 10;
		// Some games (like Final Fantasy 4) only call this too much in spurts.
		// The goal is to fix games where this would result in a consistent overhead.
		const int FLIP_DELAY_MIN_FLIPS = 30;

		u64 now = CoreTiming::GetTicks();
		// 1001 to account for NTSC timing (59.94 fps.)
		u64 expected = msToCycles(1001) / g_Config.iForceMaxEmulatedFPS;
		u64 actual = now - lastFlipCycles;
		if (actual < expected - FLIP_DELAY_CYCLES_MIN) {
			if (lastFlipsTooFrequent >= FLIP_DELAY_MIN_FLIPS) {
				delayCycles = expected - actual;
			} else {
				++lastFlipsTooFrequent;
			}
		} else {
			--lastFlipsTooFrequent;
		}
		lastFlipCycles = CoreTiming::GetTicks();
	}

	if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) {
		// Write immediately to the current framebuffer parameters
		framebuf = fbstate;
		gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
	} else {
		// Delay the write until vblank
		latchedFramebuf = fbstate;
		framebufIsLatched = true;
	}

	if (delayCycles > 0) {
		// Okay, the game is going at too high a frame rate.  God of War and Fat Princess both do this.
		// Simply eating the cycles works and is fast, but breaks other games (like Jeanne d'Arc.)
		// So, instead, we delay this HLE thread only (a small deviation from correct behavior.)
		return hleDelayResult(hleLogSuccessI(SCEDISPLAY, 0, "delaying frame thread"), "set framebuf", cyclesToUs(delayCycles));
	} else {
		if (topaddr == 0) {
			return hleLogSuccessI(SCEDISPLAY, 0, "disabling display");
		} else {
			return hleLogSuccessI(SCEDISPLAY, 0);
		}
	}
}
예제 #2
0
static u32 sceDisplayIsVsync() {
	u64 now = CoreTiming::GetTicks();
	u64 start = frameStartTicks + msToCycles(vsyncStartMs);
	u64 end = frameStartTicks + msToCycles(vsyncEndMs);

	return hleLogSuccessI(SCEDISPLAY, now >= start && now <= end ? 1 : 0);
}
예제 #3
0
static u32 sceDisplayWaitVblankCB() {
	if (!isVblank) {
		return DisplayWaitForVblanksCB("vblank waited", 1);
	} else {
		hleEatCycles(1110);
		hleReSchedule("vblank wait skipped");
		return hleLogSuccessI(SCEDISPLAY, 1, "not waiting since in vblank");
	}
}
예제 #4
0
static u32 sceDisplayGetMode(u32 modeAddr, u32 widthAddr, u32 heightAddr) {
	if (Memory::IsValidAddress(modeAddr))
		Memory::Write_U32(mode, modeAddr);
	if (Memory::IsValidAddress(widthAddr))
		Memory::Write_U32(width, widthAddr);
	if (Memory::IsValidAddress(heightAddr))
		Memory::Write_U32(height, heightAddr);
	return hleLogSuccessI(SCEDISPLAY, 0);
}
예제 #5
0
static int sceDisplayAdjustAccumulatedHcount(int value) {
	if (value < 0) {
		return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid value");
	}

	// Since it includes the current hCount, find the difference to apply to the base.
	u32 accumHCount = __DisplayGetAccumulatedHcount();
	int diff = value - accumHCount;
	hCountBase += diff;

	return hleLogSuccessI(SCEDISPLAY, 0);
}
예제 #6
0
static u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int latchedMode) {
	const FrameBufferState &fbState = latchedMode == 1 && framebufIsLatched ? latchedFramebuf : framebuf;

	if (Memory::IsValidAddress(topaddrPtr))
		Memory::Write_U32(fbState.topaddr, topaddrPtr);
	if (Memory::IsValidAddress(linesizePtr))
		Memory::Write_U32(fbState.pspFramebufLinesize, linesizePtr);
	if (Memory::IsValidAddress(pixelFormatPtr))
		Memory::Write_U32(fbState.pspFramebufFormat, pixelFormatPtr);

	return hleLogSuccessI(SCEDISPLAY, 0);
}
예제 #7
0
static u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int latchedMode) {
	// NOTE: This is wrong and partially reverts #8753. Presumably there's something else involved here as well.
	// See #8816. Could also be a firmware version difference, there are a few of those...
	const FrameBufferState &fbState = (latchedMode == PSP_DISPLAY_SETBUF_NEXTFRAME && framebufIsLatched) ? latchedFramebuf : framebuf;

	if (Memory::IsValidAddress(topaddrPtr))
		Memory::Write_U32(fbState.topaddr, topaddrPtr);
	if (Memory::IsValidAddress(linesizePtr))
		Memory::Write_U32(fbState.stride, linesizePtr);
	if (Memory::IsValidAddress(pixelFormatPtr))
		Memory::Write_U32(fbState.fmt, pixelFormatPtr);

	return hleLogSuccessI(SCEDISPLAY, 0);
}
예제 #8
0
static u32 sceDisplaySetMode(int displayMode, int displayWidth, int displayHeight) {
	if (displayMode != PSP_DISPLAY_MODE_LCD || displayWidth != 480 || displayHeight != 272) {
		WARN_LOG_REPORT(SCEDISPLAY, "Video out requested, not supported: mode=%d size=%d,%d", displayMode, displayWidth, displayHeight);
	}
	if (displayMode != PSP_DISPLAY_MODE_LCD) {
		return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_MODE, "invalid mode");
	}
	if (displayWidth != 480 || displayHeight != 272) {
		return hleLogWarning(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_SIZE, "invalid size");
	}

	if (!hasSetMode) {
		gpu->InitClear();
		hasSetMode = true;
	}
	mode = displayMode;
	width = displayWidth;
	height = displayHeight;

	hleLogSuccessI(SCEDISPLAY, 0);
	// On success, this implicitly waits for a vblank start.
	return DisplayWaitForVblanks("display mode", 1);
}
예제 #9
0
static u32 sceDisplayIsForeground() {
	int result = hasSetMode && framebuf.topaddr != 0 ? 1 : 0;
	return hleLogSuccessI(SCEDISPLAY, result);
}
예제 #10
0
static int sceDisplayGetAccumulatedHcount() {
	u32 accumHCount = __DisplayGetAccumulatedHcount();
	hleEatCycles(235);
	return hleLogSuccessI(SCEDISPLAY, accumHCount);
}
예제 #11
0
static u32 sceDisplayGetCurrentHcount() {
	hleEatCycles(275);
	return hleLogSuccessI(SCEDISPLAY, __DisplayGetCurrentHcount());
}
예제 #12
0
// Some games (GTA) never call this during gameplay, so bad place to put a framerate counter.
static u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) {
	FrameBufferState fbstate = {0};
	fbstate.topaddr = topaddr;
	fbstate.fmt = (GEBufferFormat)pixelformat;
	fbstate.stride = linesize;

	if (sync != PSP_DISPLAY_SETBUF_IMMEDIATE && sync != PSP_DISPLAY_SETBUF_NEXTFRAME) {
		return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_MODE, "invalid sync mode");
	}
	if (topaddr != 0 && !Memory::IsRAMAddress(topaddr) && !Memory::IsVRAMAddress(topaddr)) {
		return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_POINTER, "invalid address");
	}
	if ((topaddr & 0xF) != 0) {
		return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_POINTER, "misaligned address");
	}
	if ((linesize & 0x3F) != 0 || (linesize == 0 && topaddr != 0)) {
		return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_SIZE, "invalid stride");
	}
	if (pixelformat < 0 || pixelformat > GE_FORMAT_8888) {
		return hleLogError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_FORMAT, "invalid format");
	}

	if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) {
		if (fbstate.fmt != latchedFramebuf.fmt || fbstate.stride != latchedFramebuf.stride) {
			return hleReportError(SCEDISPLAY, SCE_KERNEL_ERROR_INVALID_MODE, "must change latched framebuf first");
		}
	}

	hleEatCycles(290);

	s64 delayCycles = 0;
	// Don't count transitions between display off and display on.
	if (topaddr != 0 && topaddr != framebuf.topaddr && framebuf.topaddr != 0 && g_Config.iForceMaxEmulatedFPS > 0) {
		// Sometimes we get a small number, there's probably no need to delay the thread for this.
		// sceDisplaySetFramebuf() isn't supposed to delay threads at all.  This is a hack.
		const int FLIP_DELAY_CYCLES_MIN = 10;
		// Some games (like Final Fantasy 4) only call this too much in spurts.
		// The goal is to fix games where this would result in a consistent overhead.
		const int FLIP_DELAY_MIN_FLIPS = 30;

		u64 now = CoreTiming::GetTicks();
		// 1001 to account for NTSC timing (59.94 fps.)
		u64 expected = msToCycles(1001) / g_Config.iForceMaxEmulatedFPS;
		u64 actual = now - lastFlipCycles;
		if (actual < expected - FLIP_DELAY_CYCLES_MIN) {
			if (lastFlipsTooFrequent >= FLIP_DELAY_MIN_FLIPS) {
				delayCycles = expected - actual;
			} else {
				++lastFlipsTooFrequent;
			}
		} else {
			--lastFlipsTooFrequent;
		}
		lastFlipCycles = CoreTiming::GetTicks();
	}

	if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) {
		// Write immediately to the current framebuffer parameters
		framebuf = fbstate;
		gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.stride, framebuf.fmt);
	} else {
		// Delay the write until vblank
		latchedFramebuf = fbstate;
		framebufIsLatched = true;

		// If we update the format or stride, this affects the current framebuf immediately.
		framebuf.fmt = latchedFramebuf.fmt;
		framebuf.stride = latchedFramebuf.stride;
	}

	if (delayCycles > 0) {
		// Okay, the game is going at too high a frame rate.  God of War and Fat Princess both do this.
		// Simply eating the cycles works and is fast, but breaks other games (like Jeanne d'Arc.)
		// So, instead, we delay this HLE thread only (a small deviation from correct behavior.)
		return hleDelayResult(hleLogSuccessI(SCEDISPLAY, 0, "delaying frame thread"), "set framebuf", cyclesToUs(delayCycles));
	} else {
		if (topaddr == 0) {
			return hleLogSuccessI(SCEDISPLAY, 0, "disabling display");
		} else {
			return hleLogSuccessI(SCEDISPLAY, 0);
		}
	}
}
예제 #13
0
static u32 sceDisplayIsVblank() {
	return hleLogSuccessI(SCEDISPLAY, isVblank);
}
예제 #14
0
static u32 sceDisplayGetResumeMode(u32 resumeModeAddr) {
	if (Memory::IsValidAddress(resumeModeAddr))
		Memory::Write_U32(resumeMode, resumeModeAddr);
	return hleLogSuccessI(SCEDISPLAY, 0);
}