예제 #1
0
파일: System.cpp 프로젝트: Resharc/ppsspp
void PSP_RunLoopUntil(u64 globalticks) {
    SaveState::Process();
    if (coreState == CORE_POWERDOWN || coreState == CORE_ERROR) {
        return;
    }

    // Switch the CPU thread on or off, as the case may be.
    bool useCPUThread = g_Config.bSeparateCPUThread;
    if (useCPUThread && cpuThread == nullptr) {
        // Need to start the cpu thread.
        Core_ListenShutdown(System_Wake);
        CPU_SetState(CPU_THREAD_RESUME);
        cpuThread = new std::thread(&CPU_RunLoop);
        cpuThreadID = cpuThread->get_id();
        cpuThread->detach();
        if (gpu) {
            gpu->SetThreadEnabled(true);
        }
        CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady);
    } else if (!useCPUThread && cpuThread != nullptr) {
        CPU_SetState(CPU_THREAD_QUIT);
        CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsShutdown);
        delete cpuThread;
        cpuThread = nullptr;
        cpuThreadID = std::thread::id();
        if (gpu) {
            gpu->SetThreadEnabled(false);
        }
    }

    if (cpuThread != nullptr) {
        // Tell the gpu a new frame is about to begin, before we start the CPU.
        gpu->SyncBeginFrame();

        cpuThreadUntil = globalticks;
        if (CPU_NextState(CPU_THREAD_RUNNING, CPU_THREAD_EXECUTE)) {
            // The CPU doesn't actually respect cpuThreadUntil well, especially when skipping frames.
            // TODO: Something smarter?  Or force CPU to bail periodically?
            while (!CPU_IsReady()) {
                gpu->RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000));
                if (coreState != CORE_RUNNING) {
                    CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady);
                }
            }
        } else {
            ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState);
        }
    } else {
        mipsr4k.RunLoopUntil(globalticks);
    }

    gpu->CleanupBeforeUI();
}
예제 #2
0
void ExtraHID::HandleConfigureHIDPollingRequest(const std::vector<u8>& request) {
    if (request.size() != 3) {
        LOG_ERROR(Service_IR, "Wrong request size ({}): {}", request.size(),
                  fmt::format("{:02x}", fmt::join(request, " ")));
        return;
    }

    // Change HID input polling interval
    Core::System::GetInstance().CoreTiming().UnscheduleEvent(hid_polling_callback_id, 0);
    hid_period = request[1];
    Core::System::GetInstance().CoreTiming().ScheduleEvent(msToCycles(hid_period),
                                                           hid_polling_callback_id);
}
예제 #3
0
void __CheatInit() {
	// Always register the event, want savestates to be compatible whether cheats on or off.
	CheatEvent = CoreTiming::RegisterEvent("CheatEvent", &hleCheat);

	if (g_Config.bEnableCheats) {
		__CheatStart();
	}

	int refresh = g_Config.iCwCheatRefreshRate;

	// Only check once a second for cheats to be enabled.
	CoreTiming::ScheduleEvent(msToCycles(cheatsEnabled ? refresh : 1000), CheatEvent, 0);
}
예제 #4
0
static void UpdateTimeCallback(u64 userdata, int cycles_late) {
    DateTime& date_time = shared_page.date_time_counter % 2 ?
        shared_page.date_time_0 : shared_page.date_time_1;

    date_time.date_time = GetSystemTime();
    date_time.update_tick = CoreTiming::GetTicks();
    date_time.tick_to_second_coefficient = g_clock_rate_arm11;
    date_time.tick_offset = 0;

    ++shared_page.date_time_counter;

    // system time is updated hourly
    CoreTiming::ScheduleEvent(msToCycles(60 * 60 * 1000) - cycles_late, update_time_event);
}
예제 #5
0
파일: CwCheat.cpp 프로젝트: Freetos/ppsspp
void hleCheat(u64 userdata, int cyclesLate) {
	CoreTiming::ScheduleEvent(msToCycles(77), CheatEvent, 0);

	if (!cheatEngine)
		return;
	
	if (g_Config.bReloadCheats) { //Checks if the "reload cheats" button has been pressed.
		cheatEngine->CreateCodeList();
		g_Config.bReloadCheats = false;
	}
	if (g_Config.bEnableCheats) {
		cheatEngine->Run();
	}
}
예제 #6
0
static int DisplayWaitForVblanks(const char *reason, int vblanks, bool callbacks = false) {
	const s64 ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks;
	const s64 cyclesToNextVblank = msToCycles(frameMs) - ticksIntoFrame;

	// These syscalls take about 115 us, so if the next vblank is before then, we're waiting extra.
	// At least, on real firmware a wait >= 16500 into the frame will wait two.
	if (cyclesToNextVblank <= usToCycles(115)) {
		++vblanks;
	}

	vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vblanks));
	__KernelWaitCurThread(WAITTYPE_VBLANK, 1, 0, 0, callbacks, reason);

	return hleLogSuccessVerboseI(SCEDISPLAY, 0, "waiting for %d vblanks", vblanks);
}
예제 #7
0
void __CheatDoState(PointerWrap &p) {
	auto s = p.Section("CwCheat", 0, 2);
	if (!s) {
		return;
	}

	p.Do(CheatEvent);
	CoreTiming::RestoreRegisterEvent(CheatEvent, "CheatEvent", &hleCheat);

	if (s < 2) {
		// Before this we didn't have a checkpoint, so reset didn't work.
		// Let's just force one in.
		CoreTiming::RemoveEvent(CheatEvent);
		CoreTiming::ScheduleEvent(msToCycles(cheatsEnabled ? 77 : 1000), CheatEvent, 0);
	}
}
예제 #8
0
void __DisplayInit() {
	gpuStats.Reset();
	hasSetMode = false;
	mode = 0;
	resumeMode = 0;
	holdMode = 0;
	brightnessLevel = 100;
	width = 480;
	height = 272;
	numSkippedFrames = 0;
	numVBlanksSinceFlip = 0;
	framebufIsLatched = false;
	framebuf.topaddr = 0x04000000;
	framebuf.pspFramebufFormat = GE_FORMAT_8888;
	framebuf.pspFramebufLinesize = 480; // ??
	lastFlipCycles = 0;
	lastFlipsTooFrequent = 0;
	wasPaused = false;

	enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
	leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);
	afterFlipEvent = CoreTiming::RegisterEvent("AfterFlip", &hleAfterFlip);

	lagSyncEvent = CoreTiming::RegisterEvent("LagSync", &hleLagSync);
	ScheduleLagSync();

	CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0);
	isVblank = 0;
	vCount = 0;
	hCountBase = 0;
	curFrameTime = 0.0;
	nextFrameTime = 0.0;
	lastFrameTime = 0.0;

	flips = 0;
	fps = 0.0;
	actualFlips = 0;
	lastActualFlips = 0;
	lastNumFlips = 0;
	fpsHistoryValid = 0;
	fpsHistoryPos = 0;

	InitGfxState();

	__KernelRegisterWaitTypeFuncs(WAITTYPE_VBLANK, __DisplayVblankBeginCallback, __DisplayVblankEndCallback);
}
예제 #9
0
void __DisplayInit()
{
	framebufIsLatched = false;
	framebuf.topaddr = 0x04000000;
	framebuf.pspframebuf = Memory::GetPointer(0x04000000);
	framebuf.pspFramebufFormat = PSP_DISPLAY_PIXEL_FORMAT_8888;
	framebuf.pspFramebufLinesize = 480; // ??

	enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
	leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);

	CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0);
	isVblank = 0;
	vCount = 0;

	InitGfxState();
}
예제 #10
0
void PSP_RunLoopUntil(u64 globalticks) {
	SaveState::Process();

	if (cpuThread != NULL) {
		cpuThreadUntil = globalticks;
		if (CPU_NextState(CPU_THREAD_RUNNING, CPU_THREAD_EXECUTE)) {
			// The CPU doesn't actually respect cpuThreadUntil well, especially when skipping frames.
			// TODO: Something smarter?  Or force CPU to bail periodically?
			while (!CPU_IsReady()) {
				gpu->RunEventsUntil(CoreTiming::GetTicks() + msToCycles(100));
			}
		} else {
			ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState);
		}
	} else {
		mipsr4k.RunLoopUntil(globalticks);
	}
}
예제 #11
0
파일: CwCheat.cpp 프로젝트: Swyter/ppsspp
void __CheatInit() {
	//Moved createFullPath to CheatInit from the constructor because it spams the log and constantly checks if exists. In here, only checks once.
	activeCheatFile = CHEATS_DIR + "/" + g_paramSFO.GetValueString("DISC_ID").c_str() + ".ini";

	File::CreateFullPath(CHEATS_DIR);
	if (g_Config.bEnableCheats) {
		if (!File::Exists(activeCheatFile)) {
			File::CreateEmptyFile(activeCheatFile);
		}

		cheatEngine = new CWCheatEngine();

		cheatEngine->CreateCodeList();
		g_Config.bReloadCheats = false;
		CheatEvent = CoreTiming::RegisterEvent("CheatEvent", &hleCheat);
		CoreTiming::ScheduleEvent(msToCycles(77), CheatEvent, 0);
	}
}
예제 #12
0
void hleEnterVblank(u64 userdata, int cyclesLate)
{
	int vbCount = userdata;

	DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);

	isVblank = 1;

	// Wake up threads waiting for VBlank
	__KernelTriggerWait(WAITTYPE_VBLANK, 0, true);

	// Trigger VBlank interrupt handlers.
	__TriggerInterrupt(PSP_VBLANK_INTR);

	CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1);

	// TODO: Should this be done here or in hleLeaveVblank?
	if (framebufIsLatched) 
	{
		DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
		framebuf = latchedFramebuf;
		framebufIsLatched = false;
	}

	// Yeah, this has to be the right moment to end the frame. Should possibly blit the right buffer
	// depending on what's set in sceDisplaySetFramebuf, in order to support half-framerate games -
	// an initial hack could be to NOT end the frame if the buffer didn't change? that should work okay.
	{
		host->EndFrame();

		host->BeginFrame();
		if (g_Config.bDisplayFramebuffer)
		{
			INFO_LOG(HLE, "Drawing the framebuffer");
			DisplayDrawer_DrawFramebuffer(framebuf.pspframebuf, framebuf.pspFramebufFormat, framebuf.pspFramebufLinesize);
		}

		shaderManager.DirtyShader();
		shaderManager.DirtyUniform(DIRTY_ALL);
	}

	// TODO: Find a way to tell the CPU core to stop emulating here, when running on Android.
}
예제 #13
0
void __DisplayInit() {
	gpuStats.reset();
	hasSetMode = false;
	framebufIsLatched = false;
	framebuf.topaddr = 0x04000000;
	framebuf.pspFramebufFormat = PSP_DISPLAY_PIXEL_FORMAT_8888;
	framebuf.pspFramebufLinesize = 480; // ??

	enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
	leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);

	CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0);
	isVblank = 0;
	vCount = 0;
	hCount = 0;
	hCountTotal = 0;
	lastFrameTime = 0;

	InitGfxState();
}
예제 #14
0
u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) {
	FrameBufferState fbstate;
	DEBUG_LOG(HLE,"sceDisplaySetFramebuf(topaddr=%08x,linesize=%d,pixelsize=%d,sync=%d)", topaddr, linesize, pixelformat, sync);
	if (topaddr == 0) {
		DEBUG_LOG(HLE,"- screen off");
	} else {
		fbstate.topaddr = topaddr;
		fbstate.pspFramebufFormat = (GEBufferFormat)pixelformat;
		fbstate.pspFramebufLinesize = linesize;
	}

	if (g_Config.iShowFPSCounter) {
		CalculateFPS();
	}

	if (topaddr != framebuf.topaddr) {
		if (g_Config.iForceMaxEmulatedFPS) {
			u64 now = CoreTiming::GetTicks();
			u64 expected = msToCycles(1000) / g_Config.iForceMaxEmulatedFPS;
			u64 actual = now - lastFlipCycles;
			if (actual < expected)
				hleEatCycles((int)(expected - actual));
			lastFlipCycles = CoreTiming::GetTicks();
		}
	}

	if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) {
		// Write immediately to the current framebuffer parameters
		if (topaddr != 0) {
			framebuf = fbstate;
			gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
		} else {
			WARN_LOG(HLE, "%s: PSP_DISPLAY_SETBUF_IMMEDIATE without topaddr?", __FUNCTION__);
		}
	} else if (topaddr != 0) {
		// Delay the write until vblank
		latchedFramebuf = fbstate;
		framebufIsLatched = true;
	}
	return 0;
}
예제 #15
0
void hleCheat(u64 userdata, int cyclesLate) {
	if (cheatsEnabled != g_Config.bEnableCheats) {
		// Okay, let's move to the desired state, then.
		if (g_Config.bEnableCheats) {
			__CheatStart();
		} else {
			__CheatStop();
		}
	}

	// Only check once a second for cheats to be enabled.
	CoreTiming::ScheduleEvent(msToCycles(cheatsEnabled ? 77 : 1000), CheatEvent, 0);

	if (!cheatEngine || !cheatsEnabled)
		return;
	
	if (g_Config.bReloadCheats) { //Checks if the "reload cheats" button has been pressed.
		cheatEngine->CreateCodeList();
		g_Config.bReloadCheats = false;
	}
	cheatEngine->Run();
}
예제 #16
0
void __DisplayInit() {
	gpuStats.reset();
	hasSetMode = false;
	mode = 0;
	width = 480;
	height = 272;
	numSkippedFrames = 0;
	framebufIsLatched = false;
	framebuf.topaddr = 0x04000000;
	framebuf.pspFramebufFormat = PSP_DISPLAY_PIXEL_FORMAT_8888;
	framebuf.pspFramebufLinesize = 480; // ??

	enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
	leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);
	afterFlipEvent = CoreTiming::RegisterEvent("AfterFlip", &hleAfterFlip);

	CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0);
	isVblank = 0;
	vCount = 0;
	curFrameTime = 0.0;
	nextFrameTime = 0.0;

	InitGfxState();
}
예제 #17
0
void hleLeaveVblank(u64 userdata, int cyclesLate) {
	isVblank = 0;
	DEBUG_LOG(HLE,"Leave VBlank %i", (int)userdata - 1);
	frameStartTicks = CoreTiming::GetTicks();
	CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs) - cyclesLate, enterVblankEvent, userdata);
}
예제 #18
0
void hleEnterVblank(u64 userdata, int cyclesLate) {
	int vbCount = userdata;

	DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);

	isVblank = 1;
	vCount++; // // vCount increases at each VBLANK.

	// Fire the vblank listeners before we wake threads.
	__DisplayFireVblank();

	// Wake up threads waiting for VBlank
	for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
		if (--vblankWaitingThreads[i].vcountUnblock == 0) {
			__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
			vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
		}
	}

	// Trigger VBlank interrupt handlers.
	__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL);

	CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1);

	// TODO: Should this be done here or in hleLeaveVblank?
	if (framebufIsLatched) {
		DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
		framebuf = latchedFramebuf;
		framebufIsLatched = false;
		gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
	}

	gpuStats.numFrames++;

	if (g_Config.bShowFPSCounter) {
		CalculateFPS();
	}

	bool skipFlip = false;

	// This frame was skipped, so no need to flip.
	if (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) {
		skipFlip = true;
	}

	// Draw screen overlays before blitting. Saves and restores the Ge context.
	// Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity
	// to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have
	// anything to draw here.
	gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;

	bool throttle, skipFrame;
	
	DoFrameTiming(throttle, skipFrame);

	if (skipFrame) {
		gstate_c.skipDrawReason |= SKIPDRAW_SKIPFRAME;
		numSkippedFrames++;
	} else {
		numSkippedFrames = 0;
	}

	if (!skipFlip) {
		// Setting CORE_NEXTFRAME causes a swap.
		// Check first though, might've just quit / been paused.
		if (coreState == CORE_RUNNING && gpu->FramebufferDirty()) {
			coreState = CORE_NEXTFRAME;
		}

		gpu->CopyDisplayToOutput();
	}

	// Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame).
	// Right after, we regain control for a little bit in hleAfterFlip. I think that's a great
	// place to do housekeeping.
	CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0);
}
예제 #19
0
void hleEnterVblank(u64 userdata, int cyclesLate) {
	int vbCount = userdata;

	DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);

	isVblank = 1;

	// Fire the vblank listeners before we wake threads.
	__DisplayFireVblank();

	// Wake up threads waiting for VBlank
	for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
		if (--vblankWaitingThreads[i].vcountUnblock == 0) {
			__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
			vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
		}
	}

	// Trigger VBlank interrupt handlers.
	__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL);

	CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1);

	// TODO: Should this be done here or in hleLeaveVblank?
	if (framebufIsLatched) {
		DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
		framebuf = latchedFramebuf;
		framebufIsLatched = false;
		gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
	}

	gpuStats.numFrames++;

	// Now we can subvert the Ge engine in order to draw custom overlays like stat counters etc.
	if (g_Config.bShowDebugStats && gpuStats.numDrawCalls) {
		DebugStats();
	}

	if (g_Config.bShowFPSCounter) {
		char stats[50];

		sprintf(stats, "%0.1f", calculateFPS());

		#ifdef USING_GLES2
			float zoom = 0.7f; /// g_Config.iWindowZoom;
			float soff = 0.7f;
		#else
			float zoom = 0.5f; /// g_Config.iWindowZoom;
			float soff = 0.5f;
		#endif
		PPGeBegin();
		PPGeDrawText(stats, 476 + soff, 4 + soff, PPGE_ALIGN_RIGHT, zoom, 0xCC000000);
		PPGeDrawText(stats, 476 + -soff, 4 -soff, PPGE_ALIGN_RIGHT, zoom, 0xCC000000);
		PPGeDrawText(stats, 476, 4, PPGE_ALIGN_RIGHT, zoom, 0xFF30FF30);
		PPGeEnd();
	}

	// Draw screen overlays before blitting. Saves and restores the Ge context.
	// Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity
	// to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have
	// anything to draw here.
	gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;

	bool throttle, skipFrame, skipFlip;
	
	DoFrameTiming(throttle, skipFrame, skipFlip);

	// Setting CORE_NEXTFRAME causes a swap.
	if (skipFrame) {
		gstate_c.skipDrawReason |= SKIPDRAW_SKIPFRAME;
		numSkippedFrames++;
	} else {
		numSkippedFrames = 0;
	}

	if (!skipFlip) {
		// Might've just quit / been paused.
		if (coreState == CORE_RUNNING) {
			coreState = CORE_NEXTFRAME;
		}
		CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0);

		gpu->CopyDisplayToOutput();
	}

	// Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame).
	// Right after, we regain control for a little bit in hleAfterFlip. I think that's a great
	// place to do housekeeping.
}
예제 #20
0
void hleEnterVblank(u64 userdata, int cyclesLate) {
	int vbCount = userdata;

	DEBUG_LOG(SCEDISPLAY, "Enter VBlank %i", vbCount);

	isVblank = 1;
	vCount++; // vCount increases at each VBLANK.
	hCountBase += hCountPerVblank; // This is the "accumulated" hcount base.
	if (hCountBase > 0x7FFFFFFF) {
		hCountBase -= 0x80000000;
	}
	frameStartTicks = CoreTiming::GetTicks();

	// Wake up threads waiting for VBlank
	u32 error;
	for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
		if (--vblankWaitingThreads[i].vcountUnblock == 0) {
			// Only wake it if it wasn't already released by someone else.
			SceUID waitID = __KernelGetWaitID(vblankWaitingThreads[i].threadID, WAITTYPE_VBLANK, error);
			if (waitID == 1) {
				__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
			}
			vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
		}
	}

	// Trigger VBlank interrupt handlers.
	__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL);

	CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1);

	gpuStats.numVBlanks++;

	numVBlanksSinceFlip++;

	// TODO: Should this be done here or in hleLeaveVblank?
	if (framebufIsLatched) {
		DEBUG_LOG(SCEDISPLAY, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
		framebuf = latchedFramebuf;
		framebufIsLatched = false;
		gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
	}
	// We flip only if the framebuffer was dirty. This eliminates flicker when using
	// non-buffered rendering. The interaction with frame skipping seems to need
	// some work.
	// But, let's flip at least once every 10 frames if possible, since there may be sound effects.
	if (gpu->FramebufferDirty() || (g_Config.iRenderingMode != 0 && numVBlanksSinceFlip >= 10)) {
		if (g_Config.iShowFPSCounter && g_Config.iShowFPSCounter < 4) {
			CalculateFPS();
		}

		// Setting CORE_NEXTFRAME causes a swap.
		// Check first though, might've just quit / been paused.
		if (gpu->FramebufferReallyDirty()) {
			if (coreState == CORE_RUNNING) {
				coreState = CORE_NEXTFRAME;
				gpu->CopyDisplayToOutput();
				actualFlips++;
			}
		}

		gpuStats.numFlips++;

		bool throttle, skipFrame;
		// 1.001f to compensate for the classic 59.94 NTSC framerate that the PSP seems to have.
		DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * (1.001f / 60.0f));

		int maxFrameskip = 8;
		if (throttle) {
			// 4 here means 1 drawn, 4 skipped - so 12 fps minimum.
			maxFrameskip = g_Config.iFrameSkip;
		}
		if (numSkippedFrames >= maxFrameskip) {
			skipFrame = false;
		}

		if (skipFrame) {
			gstate_c.skipDrawReason |= SKIPDRAW_SKIPFRAME;
			numSkippedFrames++;
		} else {
			gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;
			numSkippedFrames = 0;
		}

		// Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame).
		// Right after, we regain control for a little bit in hleAfterFlip. I think that's a great
		// place to do housekeeping.
		CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0);
		numVBlanksSinceFlip = 0;
	}
}
예제 #21
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);
		}
	}
}
예제 #22
0
void hleEnterVblank(u64 userdata, int cyclesLate) {
	int vbCount = userdata;

	DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);

	isVblank = 1;
	vCount++; // // vCount increases at each VBLANK.

	// Fire the vblank listeners before we wake threads.
	__DisplayFireVblank();

	// Wake up threads waiting for VBlank
	for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
		if (--vblankWaitingThreads[i].vcountUnblock == 0) {
			__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
			vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
		}
	}

	// Trigger VBlank interrupt handlers.
	__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL);

	CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1);

	// TODO: Should this be done here or in hleLeaveVblank?
	if (framebufIsLatched) {
		DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
		framebuf = latchedFramebuf;
		framebufIsLatched = false;
		gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
	}

	gpuStats.numVBlanks++;

	numVBlanksSinceFlip++;

	if (g_Config.iShowFPSCounter) {
		CalculateFPS();
	}

	// We flip only if the framebuffer was dirty. This eliminates flicker when using
	// non-buffered rendering. The interaction with frame skipping seems to need
	// some work.
	if (gpu->FramebufferDirty()) {
		gpuStats.numFlips++;

		bool wasSkipped = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0;
		gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;

		bool throttle, skipFrame;
		DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * (1.0f / 60.0f));

		if (skipFrame) {
			gstate_c.skipDrawReason |= SKIPDRAW_SKIPFRAME;
			numSkippedFrames++;
		} else {
			numSkippedFrames = 0;
		}

		// Setting CORE_NEXTFRAME causes a swap.
		// Check first though, might've just quit / been paused.
		if (!wasSkipped) {
			if (coreState == CORE_RUNNING) {
				coreState = CORE_NEXTFRAME;
				gpu->CopyDisplayToOutput();
			}
		}

		// Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame).
		// Right after, we regain control for a little bit in hleAfterFlip. I think that's a great
		// place to do housekeeping.
		CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0);
		numVBlanksSinceFlip = 0;
	}
}
예제 #23
0
void hleLeaveVblank(u64 userdata, int cyclesLate) {
	isVblank = 0;
	DEBUG_LOG(SCEDISPLAY,"Leave VBlank %i", (int)userdata - 1);
	CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs) - cyclesLate, enterVblankEvent, userdata);
}
예제 #24
0
void hleEnterVblank(u64 userdata, int cyclesLate) {
	int vbCount = userdata;

	DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);

	isVblank = 1;

	// Fire the vblank listeners before we wake threads.
	__DisplayFireVblank();

	// Wake up threads waiting for VBlank
	for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
		__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
	}
	vblankWaitingThreads.clear();

	// Trigger VBlank interrupt handlers.
	__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED, PSP_VBLANK_INTR);

	CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1);

	// TODO: Should this be done here or in hleLeaveVblank?
	if (framebufIsLatched) {
		DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
		framebuf = latchedFramebuf;
		framebufIsLatched = false;
		gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
	}

	// Draw screen overlays before blitting. Saves and restores the Ge context.

	gpuStats.numFrames++;

	// Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity
	// to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have
	// anything to draw here.
	gpu->CopyDisplayToOutput();

	// Now we can subvert the Ge engine in order to draw custom overlays like stat counters etc.
	// Here we will be drawing to the non buffered front surface.
	if (g_Config.bShowDebugStats && gpuStats.numDrawCalls) {
		gpu->UpdateStats();
		char stats[512];
		sprintf(stats,
			"Frames: %i\n"
			"Draw calls: %i\n"
			"Draw flushes: %i\n"
			"Vertices Transformed: %i\n"
			"Textures active: %i\n"
			"Textures decoded: %i\n"
			"Vertex shaders loaded: %i\n"
			"Fragment shaders loaded: %i\n"
			"Combined shaders loaded: %i\n",
			gpuStats.numFrames,
			gpuStats.numDrawCalls,
			gpuStats.numFlushes,
			gpuStats.numVertsTransformed,
			gpuStats.numTextures,
			gpuStats.numTexturesDecoded,
			gpuStats.numVertexShaders,
			gpuStats.numFragmentShaders,
			gpuStats.numShaders
			);

		float zoom = 0.7f; /// g_Config.iWindowZoom;
		PPGeBegin();
		PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFc0c0c0);
		PPGeEnd();

		gpuStats.resetFrame();
	}

	host->EndFrame();

#ifdef _WIN32
	static double lastFrameTime = 0.0;
	// Best place to throttle the frame rate on non vsynced platforms is probably here. Let's try it.
	time_update();
	if (lastFrameTime == 0.0)
		lastFrameTime = time_now_d();
	if (!GetAsyncKeyState(VK_TAB)) {
		while (time_now_d() < lastFrameTime + 1.0 / 60.0) {
			Common::SleepCurrentThread(1);
			time_update();
		}
		// Advance lastFrameTime by a constant amount each frame,
		// but don't let it get too far behind.
		lastFrameTime = std::max(lastFrameTime + 1.0 / 60.0, time_now_d() - 1.5 / 60.0);
	}

	// We are going to have to do something about audio timing for platforms that
	// are vsynced to something that's not exactly 60fps..

#endif

	host->BeginFrame();
	gpu->BeginFrame();

	// Tell the emu core that it's time to stop emulating
	// Win32 doesn't need this.
#ifndef _WIN32
	coreState = CORE_NEXTFRAME;
#endif
}
예제 #25
0
void hleEnterVblank(u64 userdata, int cyclesLate) {
	int vbCount = userdata;

	DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);

	isVblank = 1;

	// Fire the vblank listeners before we wake threads.
	__DisplayFireVblank();

	// Wake up threads waiting for VBlank
	for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
		if (--vblankWaitingThreads[i].vcountUnblock == 0) {
			__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
			vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
		}
	}

	// Trigger VBlank interrupt handlers.
	__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL);

	CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1);

	// TODO: Should this be done here or in hleLeaveVblank?
	if (framebufIsLatched) {
		DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
		framebuf = latchedFramebuf;
		framebufIsLatched = false;
		gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
	}

	// Draw screen overlays before blitting. Saves and restores the Ge context.
	gpuStats.numFrames++;

	// Now we can subvert the Ge engine in order to draw custom overlays like stat counters etc.
	if (g_Config.bShowDebugStats && gpuStats.numDrawCalls) {
		gpu->UpdateStats();
		char stats[2048];
		
		sprintf(stats,
			"Frames: %i\n"
			"DL processing time: %0.2f ms\n"
			"Kernel processing time: %0.2f ms\n"
			"Slowest syscall: %s : %0.2f ms\n"
			"Most active syscall: %s : %0.2f ms\n"
			"Draw calls: %i, flushes %i\n"
			"Cached Draw calls: %i\n"
			"Num Tracked Vertex Arrays: %i\n"
			"Vertices Submitted: %i\n"
			"Cached Vertices Drawn: %i\n"
			"Uncached Vertices Drawn: %i\n"
			"FBOs active: %i\n"
			"Textures active: %i, decoded: %i\n"
			"Texture invalidations: %i\n"
			"Vertex shaders loaded: %i\n"
			"Fragment shaders loaded: %i\n"
			"Combined shaders loaded: %i\n",
			gpuStats.numFrames,
			gpuStats.msProcessingDisplayLists * 1000.0f,
			kernelStats.msInSyscalls * 1000.0f,
			kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)",
			kernelStats.slowestSyscallTime * 1000.0f,
			kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)",
			kernelStats.summedSlowestSyscallTime * 1000.0f,
			gpuStats.numDrawCalls,
			gpuStats.numFlushes,
			gpuStats.numCachedDrawCalls,
			gpuStats.numTrackedVertexArrays,
			gpuStats.numVertsSubmitted,
			gpuStats.numCachedVertsDrawn,
			gpuStats.numUncachedVertsDrawn,
			gpuStats.numFBOs,
			gpuStats.numTextures,
			gpuStats.numTexturesDecoded,
			gpuStats.numTextureInvalidations,
			gpuStats.numVertexShaders,
			gpuStats.numFragmentShaders,
			gpuStats.numShaders
			);

		float zoom = 0.3f; /// g_Config.iWindowZoom;
		float soff = 0.3f;
		PPGeBegin();
		PPGeDrawText(stats, soff, soff, 0, zoom, 0xCC000000);
		PPGeDrawText(stats, -soff, -soff, 0, zoom, 0xCC000000);
		PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFFFFFFF);
		PPGeEnd();

		gpuStats.resetFrame();
		kernelStats.ResetFrame();
	}

	if (g_Config.bShowFPSCounter) {
		char stats[50];

		sprintf(stats, "%0.1f", calculateFPS());

		#ifdef USING_GLES2
			float zoom = 0.7f; /// g_Config.iWindowZoom;
			float soff = 0.7f;
		#else
			float zoom = 0.5f; /// g_Config.iWindowZoom;
			float soff = 0.5f;
		#endif
		PPGeBegin();
		PPGeDrawText(stats, 476 + soff, 4 + soff, PPGE_ALIGN_RIGHT, zoom, 0xCC000000);
		PPGeDrawText(stats, 476 + -soff, 4 -soff, PPGE_ALIGN_RIGHT, zoom, 0xCC000000);
		PPGeDrawText(stats, 476, 4, PPGE_ALIGN_RIGHT, zoom, 0xFF30FF30);
		PPGeEnd();
	}

	// Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity
	// to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have
	// anything to draw here.
	gpu->CopyDisplayToOutput();

	host->EndFrame();

#ifdef _WIN32
	// Best place to throttle the frame rate on non vsynced platforms is probably here. Let's try it.
	time_update();
	if (lastFrameTime == 0.0)
		lastFrameTime = time_now_d();
	if (!GetAsyncKeyState(VK_TAB) && !PSP_CoreParameter().headLess) {
		while (time_now_d() < lastFrameTime + 1.0 / 60.0) {
			Common::SleepCurrentThread(1);
			time_update();
		}
		// Advance lastFrameTime by a constant amount each frame,
		// but don't let it get too far behind.
		lastFrameTime = std::max(lastFrameTime + 1.0 / 60.0, time_now_d() - 1.5 / 60.0);
	}

	// We are going to have to do something about audio timing for platforms that
	// are vsynced to something that's not exactly 60fps..

#endif

	host->BeginFrame();
	gpu->BeginFrame();

	// Tell the emu core that it's time to stop emulating
	// Win32 doesn't need this.
#ifndef _WIN32
	coreState = CORE_NEXTFRAME;
#endif
}