Пример #1
0
void hleLagSync(u64 userdata, int cyclesLate) {
	// The goal here is to prevent network, audio, and input lag from the real world.
	// Our normal timing is very "stop and go".  This is efficient, but causes real world lag.
	// This event (optionally) runs every 1ms to sync with the real world.

	if (!FrameTimingThrottled()) {
		lagSyncScheduled = false;
		return;
	}

	float scale = 1.0f;
	if (PSP_CoreParameter().fpsLimit == FPS_LIMIT_CUSTOM) {
		// 0 is handled in FrameTimingThrottled().
		scale = 60.0f / g_Config.iFpsLimit;
	}

	const double goal = lastLagSync + (scale / 1000.0f);
	time_update();
	// Don't lag too long ever, if they leave it paused.
	while (time_now_d() < goal && goal < time_now_d() + 0.01) {
#ifndef _WIN32
		const double left = goal - time_now_d();
		usleep((long)(left * 1000000));
#endif
		time_update();
	}

	const int emuOver = (int)cyclesToUs(cyclesLate);
	const int over = (int)((time_now_d() - goal) * 1000000);
	ScheduleLagSync(over - emuOver);
}
Пример #2
0
// Let's collect all the throttling and frameskipping logic here.
void DoFrameTiming(bool &throttle, bool &skipFrame, bool &skipFlip) {
#ifdef _WIN32
	throttle = !GetAsyncKeyState(VK_TAB);
#else
	throttle = false;
#endif
	skipFlip = false;
	skipFrame = false;
	if (PSP_CoreParameter().headLess)
		throttle = false;

	// Check if the frameskipping code should be enabled. If neither throttling or frameskipping is on,
	// we have nothing to do here.
	bool doFrameSkip = g_Config.iFrameSkip == 1;
	if (!throttle && !doFrameSkip)
		return;
	
	time_update();
	
	curFrameTime = time_now_d();
	if (nextFrameTime == 0.0)
		nextFrameTime = time_now_d() + 1.0 / 60.0;
	
	if (curFrameTime > nextFrameTime && doFrameSkip) {
		// Argh, we are falling behind! Let's skip a frame and see if we catch up.
		skipFrame = true;
		skipFlip = true;
		INFO_LOG(HLE,"FRAMESKIP %i", numSkippedFrames);
	}
	
	if (curFrameTime < nextFrameTime && throttle)
	{
		// If time gap is huge just jump (somebody unthrottled)
		if (nextFrameTime - curFrameTime > 1.0 / 30.0) {
			nextFrameTime = curFrameTime + 1.0 / 60.0;
		} else {
			// Wait until we've catched up.
			while (time_now_d() < nextFrameTime) {
				Common::SleepCurrentThread(1);
				time_update();
			}
		}
		curFrameTime = time_now_d();
	}
	// Advance lastFrameTime by a constant amount each frame,
	// but don't let it get too far behind as things can get very jumpy.
	const double maxFallBehindFrames = 5.5;

	if (throttle || doFrameSkip) {
		nextFrameTime = std::max(nextFrameTime + 1.0 / 60.0, time_now_d() - maxFallBehindFrames / 60.0);
	} else {
		nextFrameTime = nextFrameTime + 1.0 / 60.0;
	}

	// Max 6 skipped frames in a row - 10 fps is really the bare minimum for playability.
	if (numSkippedFrames >= 4) {
		skipFrame = false;
		skipFlip = false;
	}
}
Пример #3
0
void Core_RunLoop() {
	while ((globalUIState != UISTATE_INGAME || !PSP_IsInited()) && globalUIState != UISTATE_EXIT) {
		time_update();

#if defined(_WIN32) && !defined(USING_QT_UI)
		double startTime = time_now_d();
		UpdateRunLoop();

		// Simple throttling to not burn the GPU in the menu.
		time_update();
		double diffTime = time_now_d() - startTime;
		int sleepTime = (int) (1000000.0 / 60.0) - (int) (diffTime * 1000000.0);
		if (sleepTime > 0)
			Sleep(sleepTime / 1000);
		GL_SwapBuffers();
#else
		UpdateRunLoop();
#endif
	}

	while (!coreState && globalUIState == UISTATE_INGAME) {
		time_update();
		UpdateRunLoop();
#if defined(_WIN32) && !defined(USING_QT_UI)
		if (!Core_IsStepping()) {
			GL_SwapBuffers();
		}
#endif
	}
}
Пример #4
0
bool LoggingDeadline::End() {
	endCalled_ = true;
	time_update();
	if (time_now_d() > endTime_) {
		double late = (time_now_d() - endTime_);
		double totalTime = late + totalTime_;
		ELOG("===== %0.2fms DEADLINE PASSED FOR %s at %0.2fms - %0.2fms late =====", totalTime_ * 1000.0, name_, 1000.0 * totalTime, 1000.0 * late);
		return false;
	}
	return true;
}
Пример #5
0
void OnScreenMessagesView::Draw(UIContext &dc) {
	// First, clean out old messages.
	osm.Lock();
	osm.Clean();

	// Get height
	float w, h;
	dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, "Wg", &w, &h);

	float y = 10.0f;
	// Then draw them all. 
	const std::list<OnScreenMessages::Message> &messages = osm.Messages();
	for (auto iter = messages.begin(); iter != messages.end(); ++iter) {
		float alpha = (iter->endTime - time_now_d()) * 4.0f;
		if (alpha > 1.0) alpha = 1.0f;
		if (alpha < 0.0) alpha = 0.0f;
		// Messages that are wider than the screen are left-aligned instead of centered.
		float tw, th;
		dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, iter->text.c_str(), &tw, &th);
		float x = bounds_.centerX();
		int align = ALIGN_TOP | ALIGN_HCENTER;
		if (tw > bounds_.w) {
			align = ALIGN_TOP | ALIGN_LEFT;
			x = 2;
		}
		dc.SetFontStyle(dc.theme->uiFont);
		dc.DrawTextShadow(iter->text.c_str(), x, y, colorAlpha(iter->color, alpha), align);
		y += h;
	}

	osm.Unlock();
}
Пример #6
0
Файл: view.cpp Проект: kg/ppsspp
void PopupHeader::Draw(UIContext &dc) {
	const float paddingHorizontal = 12;
	const float availableWidth = bounds_.w - paddingHorizontal * 2;

	float tw, th;
	dc.SetFontStyle(dc.theme->uiFont);
	dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text_.c_str(), &tw, &th, 0);

	float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f;

	float tx = paddingHorizontal;
	if (availableWidth < tw) {
		float overageRatio = 1.5f * availableWidth * 1.0f / tw;
		tx -= (1.0f + sin(time_now_d() * overageRatio)) * sineWidth;
		Bounds tb = bounds_;
		tb.x = bounds_.x + paddingHorizontal;
		tb.w = bounds_.w - paddingHorizontal * 2;
		dc.PushScissor(tb);
	}

	dc.DrawText(text_.c_str(), bounds_.x + tx, bounds_.centerY(), dc.theme->popupTitle.fgColor, ALIGN_LEFT | ALIGN_VCENTER);
	dc.Draw()->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.theme->popupTitle.fgColor);

	if (availableWidth < tw) {
		dc.PopScissor();
	}
}
Пример #7
0
bool GPUCommon::InterpretList(DisplayList &list)
{
	time_update();
	double start = time_now_d();
	currentList = &list;
	// Reset stackptr for safety
	stackptr = 0;
	u32 op = 0;
	prev = 0;
	finished = false;

	if (!Memory::IsValidAddress(list.pc)) {
		ERROR_LOG(G3D, "DL PC = %08x WTF!!!!", list.pc);
		return true;
	}

	while (!finished)
	{
		list.status = PSP_GE_LIST_DRAWING;
		if (list.pc == list.stall)
		{
			list.status = PSP_GE_LIST_STALL_REACHED;
			return false;
		}
		op = Memory::ReadUnchecked_U32(list.pc); //read from memory
		u32 cmd = op >> 24;
		u32 diff = op ^ gstate.cmdmem[cmd];
		PreExecuteOp(op, diff);
		// TODO: Add a compiler flag to remove stuff like this at very-final build time.
		if (dumpThisFrame_) {
			char temp[256];
			GeDisassembleOp(list.pc, op, prev, temp);
			NOTICE_LOG(G3D, "%s", temp);
		}
		gstate.cmdmem[cmd] = op;	 // crashes if I try to put the whole op there??
		
		ExecuteOp(op, diff);
		
		list.pc += 4;
		prev = op;
	}
	time_update();
	gpuStats.msProcessingDisplayLists += time_now_d() - start;
	return true;
}
Пример #8
0
void sceRtcGetCurrentTick()
{
#ifdef _WIN32
	RETURN(GetTickCount());
#else
	time_update();
	RETURN(time_now_d());
#endif
}
Пример #9
0
void Core_RunLoop(GraphicsContext *ctx, InputState *input_state) {
	graphicsContext = ctx;
	while ((GetUIState() != UISTATE_INGAME || !PSP_IsInited()) && GetUIState() != UISTATE_EXIT) {
		time_update();
#if defined(USING_WIN_UI)
		double startTime = time_now_d();
		UpdateRunLoop(input_state);

		// Simple throttling to not burn the GPU in the menu.
		time_update();
		double diffTime = time_now_d() - startTime;
		int sleepTime = (int)(1000.0 / 60.0) - (int)(diffTime * 1000.0);
		if (sleepTime > 0)
			Sleep(sleepTime);
		if (!windowHidden) {
			ctx->SwapBuffers();
		}
#else
		UpdateRunLoop(input_state);
#endif
	}

	while (!coreState && GetUIState() == UISTATE_INGAME) {
		time_update();
		UpdateRunLoop(input_state);
#if defined(USING_WIN_UI)
		if (!windowHidden && !Core_IsStepping()) {
			ctx->SwapBuffers();

			// Keep the system awake for longer than normal for cutscenes and the like.
			const double now = time_now_d();
			if (now < lastActivity + ACTIVITY_IDLE_TIMEOUT) {
				// Only resetting it ever prime number seconds in case the call is expensive.
				// Using a prime number to ensure there's no interaction with other periodic events.
				if (now - lastKeepAwake > 89.0 || now < lastKeepAwake) {
					SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
					lastKeepAwake = now;
				}
			}
		}
#endif
	}
}
Пример #10
0
void OnScreenMessages::Clean() {
restart:
	double now = time_now_d();
	for (auto iter = messages_.begin(); iter != messages_.end(); iter++) {
		if (iter->endTime < now) {
			messages_.erase(iter);
			goto restart;
		}
	}
}
Пример #11
0
void GameInfoCache::SetupTexture(GameInfo *info, std::string &textureData, Thin3DContext *thin3d, Thin3DTexture *&tex, double &loadTime) {
	if (textureData.size()) {
		if (!tex) {
			tex = thin3d->CreateTextureFromFileData((const uint8_t *)textureData.data(), (int)textureData.size(), T3DImageType::PNG);
			if (tex) {
				loadTime = time_now_d();
			}
		}
		textureData.clear();
	}
}
Пример #12
0
void Core_RunLoop()
{
	while (!coreState) {
		time_update();
		double startTime = time_now_d();
		UpdateScreenScale();
		{
			{
#ifdef _WIN32
				lock_guard guard(input_state.lock);
				input_state.pad_buttons = 0;
				input_state.pad_lstick_x = 0;
				input_state.pad_lstick_y = 0;
				input_state.pad_rstick_x = 0;
				input_state.pad_rstick_y = 0;
				// Temporary hack.
				if (GetAsyncKeyState(VK_ESCAPE)) {
					input_state.pad_buttons |= PAD_BUTTON_MENU;
				}
				host->PollControllers(input_state);
				UpdateInputState(&input_state);
#endif
			}
			NativeUpdate(input_state);
			EndInputState(&input_state);
		}
		NativeRender();
		time_update();
		// Simple throttling to not burn the GPU in the menu.
#ifdef _WIN32
		if (globalUIState != UISTATE_INGAME) {
			double sleepTime = 16.666 - (time_now_d() - startTime) * 1000.0;
			if (sleepTime > 0.0)
				Sleep((int)sleepTime);
			GL_SwapBuffers();
		} else if (!Core_IsStepping()) {
			GL_SwapBuffers();
		}
#endif
	}
}
Пример #13
0
void Core_RunLoop()
{
	while (!coreState) {
		time_update();
		double startTime = time_now_d();
		UpdateScreenScale();
		{
			{
#ifdef _WIN32
				lock_guard guard(input_state.lock);
				input_state.pad_buttons = 0;
				input_state.pad_lstick_x = 0;
				input_state.pad_lstick_y = 0;
				input_state.pad_rstick_x = 0;
				input_state.pad_rstick_y = 0;
				host->PollControllers(input_state);
				UpdateInputState(&input_state);
#endif
			}
			NativeUpdate(input_state);
			EndInputState(&input_state);
		}
		NativeRender();
		time_update();
		// Simple throttling to not burn the GPU in the menu.
#ifdef _WIN32
		if (globalUIState != UISTATE_INGAME) {
			double diffTime = time_now_d() - startTime;
			int sleepTime = (int) (1000000.0 / 60.0) - (int) (diffTime * 1000000.0);
			if (sleepTime > 0)
				Sleep(sleepTime / 1000);
			GL_SwapBuffers();
		} else if (!Core_IsStepping()) {
			GL_SwapBuffers();
		}
#endif
	}
}
Пример #14
0
static void DoFrameIdleTiming() {
	PROFILE_THIS_SCOPE("timing");
	if (!FrameTimingThrottled() || !g_Config.bEnableSound || wasPaused) {
		return;
	}

	time_update();

	double dist = time_now_d() - lastFrameTime;
	// Ignore if the distance is just crazy.  May mean wrap or pause.
	if (dist < 0.0 || dist >= 15 * timePerVblank) {
		return;
	}

	float scaledVblank = timePerVblank;
	if (PSP_CoreParameter().fpsLimit == FPS_LIMIT_CUSTOM) {
		// 0 is handled in FrameTimingThrottled().
		scaledVblank *= 60.0f / g_Config.iFpsLimit;
	}

	// If we have over at least a vblank of spare time, maintain at least 30fps in delay.
	// This prevents fast forward during loading screens.
	const double thresh = lastFrameTime + (numVBlanksSinceFlip - 1) * scaledVblank;
	if (numVBlanksSinceFlip >= 2 && time_now_d() < thresh) {
		// Give a little extra wiggle room in case the next vblank does more work.
		const double goal = lastFrameTime + numVBlanksSinceFlip * scaledVblank - 0.001;
		while (time_now_d() < goal) {
#ifdef _WIN32
			sleep_ms(1);
#else
			const double left = goal - time_now_d();
			usleep((long)(left * 1000000));
#endif
			time_update();
		}
	}
}
Пример #15
0
// Runs on the main thread.
GameInfo *GameInfoCache::GetInfo(Thin3DContext *thin3d, const std::string &gamePath, int wantFlags) {
	GameInfo *info = 0;

	auto iter = info_.find(gamePath);
	if (iter != info_.end()) {
		info = iter->second;
		if ((info->wantFlags & wantFlags) != wantFlags) {
			// Need to start over. We'll just add a new work item.
			goto again;
		}
		if (thin3d && info->iconDataLoaded) {
			SetupTexture(info, info->iconTextureData, thin3d, info->iconTexture, info->timeIconWasLoaded);
			info->iconDataLoaded = false;
		}
		if (thin3d && info->pic0DataLoaded) {
			SetupTexture(info, info->pic0TextureData, thin3d, info->pic0Texture, info->timePic0WasLoaded);
			info->pic0DataLoaded = false;
		}
		if (thin3d && info->pic1DataLoaded) {
			SetupTexture(info, info->pic1TextureData, thin3d, info->pic1Texture, info->timePic1WasLoaded);
			info->pic1DataLoaded = false;
		}
		iter->second->lastAccessedTime = time_now_d();
		return iter->second;
	}

again:

	if (!info) {
		info = new GameInfo();
	}

	{
		lock_guard lock(info->lock);
		if (info->IsWorking()) {
			// Uh oh, it's currently in process.  It could mark pending = false with the wrong wantFlags.
			// Let's wait it out, then queue.
			WaitUntilDone(info);
		}
		info->wantFlags |= wantFlags;
		info->pending = true;
	}

	GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info);
	gameInfoWQ_->Add(item);

	info_[gamePath] = info;
	return info;
}
Пример #16
0
void CalculateFPS()
{
	time_update();
	double now = time_now_d();

	if (now >= lastFpsTime + 1.0)
	{
		fps = (gpuStats.numFrames - lastFpsFrame) / (now - lastFpsTime);
		if (fps > highestFps)
			highestFps = fps;

		lastFpsFrame = gpuStats.numFrames;	
		lastFpsTime = now;
	}
}
Пример #17
0
void FileListAdapter::drawItem(int item, int x, int y, int w, int h, bool selected) const
{
	int icon = -1;
	if ((*items_)[item].isDirectory) {
		icon = options_.folderIcon;
	} else {
		std::string extension = getFileExtension((*items_)[item].name);
		auto iter = options_.iconMapping.find(extension);
		if (iter != options_.iconMapping.end())
			icon = iter->second;
	}

	float scaled_h = ui_atlas.images[I_BUTTON].h;
	float scaled_w = scaled_h * (144.f / 80.f);

	int iconSpace = scaled_w + 10;
	ui_draw2d.DrawImage2GridH(selected ? I_BUTTON_SELECTED: I_BUTTON, x, y, x + w);
	ui_draw2d.DrawTextShadow(UBUNTU24, (*items_)[item].name.c_str(), x + UI_SPACE + iconSpace, y + 25, 0xFFFFFFFF, ALIGN_LEFT | ALIGN_VCENTER);

	// This might create a texture so we must flush first.
	UIFlush();
	GameInfo *ginfo = 0;
	if (!(*items_)[item].isDirectory) {
		ginfo = g_gameInfoCache.GetInfo((*items_)[item].fullName, false);
		if (!ginfo) {
			ELOG("No ginfo :( %s", (*items_)[item].fullName.c_str());
		}
	}
	if (ginfo) {
		if (ginfo->iconTexture) {
			uint32_t color = whiteAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 2));
			UIFlush();
			ginfo->iconTexture->Bind(0);
			ui_draw2d.DrawTexRect(x + 10, y, x + 10 + scaled_w, y + scaled_h, 0, 0, 1, 1, color);
			ui_draw2d.Flush();
			ctx_->RebindTexture();
		}
	} else {
		if (icon != -1)
			ui_draw2d.DrawImage(icon, x + UI_SPACE, y + 25, 1.0f, 0xFFFFFFFF, ALIGN_VCENTER | ALIGN_LEFT);
	}
}
Пример #18
0
// Runs on the main thread.
GameInfo *GameInfoCache::GetInfo(Thin3DContext *thin3d, const std::string &gamePath, int wantFlags) {
	GameInfo *info = 0;

	auto iter = info_.find(gamePath);
	if (iter != info_.end()) {
		info = iter->second;
		if ((info->wantFlags & wantFlags) != wantFlags) {
			// Need to start over. We'll just add a new work item.
			goto again;
		}
		if (thin3d && info->iconDataLoaded) {
			SetupTexture(info, info->iconTextureData, thin3d, info->iconTexture, info->timeIconWasLoaded);
			info->iconDataLoaded = false;
		}
		if (thin3d && info->pic0DataLoaded) {
			SetupTexture(info, info->pic0TextureData, thin3d, info->pic0Texture, info->timePic0WasLoaded);
			info->pic0DataLoaded = false;
		}
		if (thin3d && info->pic1DataLoaded) {
			SetupTexture(info, info->pic1TextureData, thin3d, info->pic1Texture, info->timePic1WasLoaded);
			info->pic1DataLoaded = false;
		}
		iter->second->lastAccessedTime = time_now_d();
		return iter->second;
	}

again:

	if (!info) {
		info = new GameInfo();
	}
	{
		lock_guard lock(info->lock);
		info->wantFlags |= wantFlags;
	}

	GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info);
	gameInfoWQ_->Add(item);

	info_[gamePath] = info;
	return info;
}
Пример #19
0
void CalculateFPS()
{
	time_update();
	double now = time_now_d();

	if (now >= lastFpsTime + 1.0)
	{
		double frames = (gpuStats.numVBlanks - lastFpsFrame);
		fps = frames / (now - lastFpsTime);
		flips = 60.0 * (double) (gpuStats.numFlips - lastNumFlips) / frames;

		lastFpsFrame = gpuStats.numVBlanks;
		lastNumFlips = gpuStats.numFlips;
		lastFpsTime = now;

		fpsHistory[fpsHistoryPos++] = fps;
		fpsHistoryPos = fpsHistoryPos % ARRAY_SIZE(fpsHistory);
		++fpsHistoryValid;
	}
}
Пример #20
0
float calculateFPS()
{
	static double highestFps = 0.0;
	static int lastFpsFrame = 0;
	static double lastFpsTime = 0.0;
	static double fps = 0.0;

	time_update();
	double now = time_now_d();

	if (now >= lastFpsTime + 1.0)
	{
		fps = (gpuStats.numFrames - lastFpsFrame) / (now - lastFpsTime);
		if (fps > highestFps)
			highestFps = fps;

		lastFpsFrame = gpuStats.numFrames;	
		lastFpsTime = now;
	}
	return fps;
}
Пример #21
0
void OnScreenMessages::Show(const std::string &message, float duration_s, uint32_t color, int icon, bool checkUnique) {
	double now = time_now_d();
	std::lock_guard<std::recursive_mutex> guard(mutex_);
	if (checkUnique) {
		for (auto iter = messages_.begin(); iter != messages_.end(); ++iter) {
			if (iter->text == message) {
				Message msg = *iter;
				msg.endTime = now + duration_s;
				messages_.erase(iter);
				messages_.insert(messages_.begin(), msg);
				return;
			}
		}
	}
	Message msg;
	msg.text = message;
	msg.color = color;
	msg.endTime = now + duration_s;
	msg.icon = icon;
	messages_.insert(messages_.begin(), msg);
}
Пример #22
0
void CalculateFPS()
{
	static double highestFps = 0.0;
	static int lastFpsFrame = 0;
	static double lastFpsTime = 0.0;
	static double fps = 0.0;

	time_update();
	double now = time_now_d();

	if (now >= lastFpsTime + 1.0)
	{
		fps = (gpuStats.numFrames - lastFpsFrame) / (now - lastFpsTime);
		if (fps > highestFps)
			highestFps = fps;

		lastFpsFrame = gpuStats.numFrames;	
		lastFpsTime = now;
	}

	char stats[50];
	sprintf(stats, "VPS: %0.1f", fps);

	#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();
}
Пример #23
0
void OnScreenMessages::Show(const std::string &text, float duration_s, uint32_t color, int icon, bool checkUnique, const char *id) {
	double now = time_now_d();
	lock_guard guard(mutex_);
	if (checkUnique) {
		for (auto iter = messages_.begin(); iter != messages_.end(); ++iter) {
			if (iter->text == text || (id && iter->id && !strcmp(iter->id, id))) {
				Message msg = *iter;
				msg.endTime = now + duration_s;
				msg.text = text;
				msg.color = color;
				messages_.erase(iter);
				messages_.insert(messages_.begin(), msg);
				return;
			}
		}
	}
	Message msg;
	msg.text = text;
	msg.color = color;
	msg.endTime = now + duration_s;
	msg.icon = icon;
	msg.id = id;
	messages_.insert(messages_.begin(), msg);
}
Пример #24
0
bool GPUCommon::InterpretList(DisplayList &list)
{
	time_update();
	double start = time_now_d();
	currentList = &list;
	u32 op = 0;
	prev = 0;
	finished = false;

	// I don't know if this is the correct place to zero this, but something
	// need to do it. See Sol Trigger title screen.
	gstate_c.offsetAddr = 0;

	if (!Memory::IsValidAddress(list.pc)) {
		ERROR_LOG(G3D, "DL PC = %08x WTF!!!!", list.pc);
		return true;
	}
#if defined(USING_QT_UI)
		if(host->GpuStep())
		{
			host->SendGPUStart();
		}
#endif

	while (!finished)
	{
		list.status = PSP_GE_LIST_DRAWING;
		if (list.pc == list.stall)
		{
			list.status = PSP_GE_LIST_STALL_REACHED;
			return false;
		}

		op = Memory::ReadUnchecked_U32(list.pc); //read from memory
		u32 cmd = op >> 24;

#if defined(USING_QT_UI)
		if(host->GpuStep())
		{
			host->SendGPUWait(cmd, list.pc, &gstate);
		}
#endif
		u32 diff = op ^ gstate.cmdmem[cmd];
		PreExecuteOp(op, diff);
		// TODO: Add a compiler flag to remove stuff like this at very-final build time.
		if (dumpThisFrame_) {
			char temp[256];
			GeDisassembleOp(list.pc, op, prev, temp);
			NOTICE_LOG(HLE, "%s", temp);
		}
		gstate.cmdmem[cmd] = op;	 // crashes if I try to put the whole op there??
		
		ExecuteOp(op, diff);
		
		list.pc += 4;
		prev = op;
	}
	time_update();
	gpuStats.msProcessingDisplayLists += time_now_d() - start;
	return true;
}
Пример #25
0
bool GPUCommon::InterpretList(DisplayList &list) {
	// Initialized to avoid a race condition with bShowDebugStats changing.
	double start = 0.0;
	if (g_Config.bShowDebugStats) {
		time_update();
		start = time_now_d();
	}

	easy_guard guard(listLock);

	// TODO: This has to be right... but it freezes right now?
	//if (list.state == PSP_GE_DL_STATE_PAUSED)
	//	return false;
	currentList = &list;

	// I don't know if this is the correct place to zero this, but something
	// need to do it. See Sol Trigger title screen.
	// TODO: Maybe this is per list?  Should a stalled list remember the old value?
	gstate_c.offsetAddr = 0;

	if (!Memory::IsValidAddress(list.pc)) {
		ERROR_LOG_REPORT(G3D, "DL PC = %08x WTF!!!!", list.pc);
		return true;
	}

#if defined(USING_QT_UI)
	if (host->GpuStep()) {
		host->SendGPUStart();
	}
#endif

	cycleLastPC = list.pc;
	downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
	list.state = PSP_GE_DL_STATE_RUNNING;
	list.interrupted = false;

	gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
	guard.unlock();

	const bool dumpThisFrame = dumpThisFrame_;
	// TODO: Add check for displaylist debugger.
	const bool useFastRunLoop = !dumpThisFrame;
	while (gpuState == GPUSTATE_RUNNING) {
		{
			easy_guard innerGuard(listLock);
			if (list.pc == list.stall) {
				gpuState = GPUSTATE_STALL;
				downcount = 0;
			}
		}

		if (useFastRunLoop) {
			FastRunLoop(list);
		} else {
			SlowRunLoop(list);
		}

		{
			easy_guard innerGuard(listLock);
			downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;

			if (gpuState == GPUSTATE_STALL && list.stall != list.pc) {
				// Unstalled.
				gpuState = GPUSTATE_RUNNING;
			}
		}
	}

	// We haven't run the op at list.pc, so it shouldn't count.
	if (cycleLastPC != list.pc) {
		UpdatePC(list.pc - 4, list.pc);
	}

	if (g_Config.bShowDebugStats) {
		time_update();
		gpuStats.msProcessingDisplayLists += time_now_d() - start;
	}
	return gpuState == GPUSTATE_DONE || gpuState == GPUSTATE_ERROR;
}
Пример #26
0
// Let's collect all the throttling and frameskipping logic here.
void DoFrameTiming(bool &throttle, bool &skipFrame) {
	throttle = !PSP_CoreParameter().unthrottle;

	skipFrame = false;
	if (PSP_CoreParameter().headLess)
		throttle = false;

	// Check if the frameskipping code should be enabled. If neither throttling or frameskipping is on,
	// we have nothing to do here.
	bool doFrameSkip = g_Config.iFrameSkip != 0;

	// On non windows, which is always vsync locked, we need to force frameskip when
	// unthrottled.
#ifndef _WIN32
	if (!throttle) {
		doFrameSkip = true;
		skipFrame = true;
		if (numSkippedFrames >= 6) {
			skipFrame = false;
		}
		return;	
	}
#endif

	if (!throttle && !doFrameSkip)
		return;
	
	time_update();
	
	curFrameTime = time_now_d();
	if (nextFrameTime == 0.0)
		nextFrameTime = time_now_d() + 1.0 / 60.0;
	
	if (curFrameTime > nextFrameTime && doFrameSkip) {
		// Argh, we are falling behind! Let's skip a frame and see if we catch up.
		skipFrame = true;
		// INFO_LOG(HLE,"FRAMESKIP %i", numSkippedFrames);
	}

	
	if (curFrameTime < nextFrameTime && throttle)
	{
		// If time gap is huge just jump (somebody unthrottled)
		if (nextFrameTime - curFrameTime > 1.0 / 30.0) {
			nextFrameTime = curFrameTime + 1.0 / 60.0;
		} else {
			// Wait until we've catched up.
			while (time_now_d() < nextFrameTime) {
				Common::SleepCurrentThread(1);
				time_update();
			}
		}
		curFrameTime = time_now_d();
	}
	// Advance lastFrameTime by a constant amount each frame,
	// but don't let it get too far behind as things can get very jumpy.
	const double maxFallBehindFrames = 5.5;

	if (throttle || doFrameSkip) {
		nextFrameTime = std::max(nextFrameTime + 1.0 / 60.0, time_now_d() - maxFallBehindFrames / 60.0);
	} else {
		nextFrameTime = nextFrameTime + 1.0 / 60.0;
	}

	// Max 4 skipped frames in a row - 15 fps is really the bare minimum for playability.
	// We check for 3 here so it's 3 skipped frames, 1 non skipped, 3 skipped, etc.
	if (numSkippedFrames >= g_Config.iFrameSkip) {
		skipFrame = false;
	}
}
Пример #27
0
// This may run off-main-thread and we thus can't use the global
// pspFileSystem (well, we could with synchronization but there might not
// even be a game running).
GameInfo *GameInfoCache::GetInfo(const std::string &gamePath, bool wantBG) {
	auto iter = info_.find(gamePath);
	if (iter != info_.end()) {
		GameInfo *info = iter->second;
		if (!info->wantBG && wantBG) {
			// Need to start over.
			delete info;
			goto again;
		}
		if (info->iconTextureData.size()) {
			info->iconTexture = new Texture();
			// TODO: We could actually do the PNG decoding as well on the async thread.
			// We'd have to split up Texture->LoadPNG though, creating some intermediate Image class maybe.
			if (info->iconTexture->LoadPNG((const u8 *)info->iconTextureData.data(), info->iconTextureData.size(), false)) {
				info->timeIconWasLoaded = time_now_d();
			}
			info->iconTextureData.clear();
		}
		if (info->pic0TextureData.size()) {
			info->pic0Texture = new Texture();
			if (info->pic0Texture->LoadPNG((const u8 *)info->pic0TextureData.data(), info->pic0TextureData.size(), false)) {
				info->timePic0WasLoaded = time_now_d();
			}
			info->pic0TextureData.clear();
		}
		if (info->pic1TextureData.size()) {
			info->pic1Texture = new Texture();
			if (info->pic1Texture->LoadPNG((const u8 *)info->pic1TextureData.data(), info->pic1TextureData.size(), false)) {
				info->timePic1WasLoaded = time_now_d();
			}
			info->pic1TextureData.clear();
		}
		iter->second->lastAccessedTime = time_now_d();
		return iter->second;
	}

again:

	// return info;

	// TODO: Everything below here should be asynchronous and run on a thread,
	// filling in the info as it goes.

	// A game can be either an UMD or a directory under ms0:/PSP/GAME .
	if (startsWith(gamePath, "ms0:/PSP/GAME")) {
		return 0;
	// TODO: The case of these extensions is not perfect.
	} else if (endsWith(gamePath, ".PBP") || endsWith(gamePath, ".elf")) {
		return 0;
	} else {
		SequentialHandleAllocator handles;
		// Let's assume it's an ISO.
		// TODO: This will currently read in the whole directory tree. Not really necessary for just a
		// few files.
		BlockDevice *bd = constructBlockDevice(gamePath.c_str());
		if (!bd)
			return 0;  // nothing to do here..
		ISOFileSystem umd(&handles, bd, "/PSP_GAME");

		GameInfo *info = new GameInfo();
		info->wantBG = wantBG;

		// Alright, let's fetch the PARAM.SFO.
		std::string paramSFOcontents;
		if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", &paramSFOcontents)) {
			lock_guard lock(info->lock);
			info->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size());
			info->title = info->paramSFO.GetValueString("TITLE");
		}

		ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info->iconTextureData);
		if (wantBG) {
			{
				lock_guard lock(info->lock);
				ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info->pic0TextureData);
			}
			{
				lock_guard lock(info->lock);
				ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info->pic1TextureData);
			}
		}
		info_[gamePath] = info;
		return info;
	}

	return 0;
}
Пример #28
0
void PauseScreen::render() {
	UIShader_Prepare();
	UIBegin(UIShader_Get());
	DrawBackground(1.0f);

	std::string title = game_title.c_str();
	// Try to ignore (tm) etc.
	//if (UTF8StringNonASCIICount(game_title.c_str()) > 2) {
	//	title = "(can't display japanese title)";
	//} else {
	//}


	UIContext *ctx = screenManager()->getUIContext();
	// This might create a texture so we must flush first.
	UIFlush();
	GameInfo *ginfo = g_gameInfoCache.GetInfo(PSP_CoreParameter().fileToStart, true);

	if (ginfo) {
		title = ginfo->title;
	}

	if (ginfo && ginfo->pic1Texture) {
		ginfo->pic1Texture->Bind(0);
		uint32_t color = whiteAlpha(ease((time_now_d() - ginfo->timePic1WasLoaded) * 3)) & 0xFFc0c0c0;
		ui_draw2d.DrawTexRect(0,0,dp_xres, dp_yres, 0,0,1,1,color);
		ui_draw2d.Flush();
		ctx->RebindTexture();
	}

	if (ginfo && ginfo->pic0Texture) {
		ginfo->pic0Texture->Bind(0);
		// Pic0 is drawn in the bottom right corner, overlaying pic1.
		float sizeX = dp_xres / 480 * ginfo->pic0Texture->Width();
		float sizeY = dp_yres / 272 * ginfo->pic0Texture->Height();
		uint32_t color = whiteAlpha(ease((time_now_d() - ginfo->timePic1WasLoaded) * 2)) & 0xFFc0c0c0;
		ui_draw2d.DrawTexRect(dp_xres - sizeX, dp_yres - sizeY, dp_xres, dp_yres, 0,0,1,1,color);
		ui_draw2d.Flush();
		ctx->RebindTexture();
	}

	if (ginfo && ginfo->iconTexture) {
		uint32_t color = whiteAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 1.5));
		ginfo->iconTexture->Bind(0);

		// Maintain the icon's aspect ratio.  Minis are square, for example.
		float iconAspect = (float)ginfo->iconTexture->Width() / (float)ginfo->iconTexture->Height();
		float h = 80.0f;
		float w = 144.0f;
		float x = 10.0f + (w - h * iconAspect) / 2.0f;
		w = h * iconAspect;

		ui_draw2d.DrawTexRect(x, 10, x + w, 10 + h, 0, 0, 1, 1, 0xFFFFFFFF);
		ui_draw2d.Flush();
		ctx->RebindTexture();
	}

	ui_draw2d.DrawText(UBUNTU24, title.c_str(), 10+144+10, 30, 0xFFFFFFFF, ALIGN_LEFT);

	int x = 30;
	int y = 50;
	int stride = 40;
	int columnw = 400;

	// Shared with settings
	I18NCategory *ss = GetI18NCategory("System");
	I18NCategory *gs = GetI18NCategory("Graphics");
	I18NCategory *a = GetI18NCategory("Audio");
	
	UICheckBox(GEN_ID, x, y += stride, ss->T("Show FPS"), ALIGN_TOPLEFT, &g_Config.bShowFPSCounter);
	UICheckBox(GEN_ID, x, y += stride, a->T("Enable Sound"), ALIGN_TOPLEFT, &g_Config.bEnableSound);
	// TODO: Maybe shouldn't show this if the screen ratios are very close...
#ifdef BLACKBERRY10
	if (pixel_xres == pixel_yres)
		UICheckBox(GEN_ID, x, y += stride, gs->T("Partial Vertical Stretch"), ALIGN_TOPLEFT, &g_Config.bPartialStretch);
#endif
	UICheckBox(GEN_ID, x, y += stride, gs->T("Stretch to Display"), ALIGN_TOPLEFT, &g_Config.bStretchToDisplay);

	UICheckBox(GEN_ID, x, y += stride, gs->T("Hardware Transform"), ALIGN_TOPLEFT, &g_Config.bHardwareTransform);
	if (UICheckBox(GEN_ID, x, y += stride, gs->T("Buffered Rendering"), ALIGN_TOPLEFT, &g_Config.bBufferedRendering)) {
		if (gpu)
			gpu->Resized();
	}
	UICheckBox(GEN_ID, x, y += stride, gs->T("Media Engine"), ALIGN_TOPLEFT, &g_Config.bUseMediaEngine);
	bool enableFrameSkip = g_Config.iFrameSkip != 0;
	UICheckBox(GEN_ID, x, y += stride, gs->T("Frame Skipping"), ALIGN_TOPLEFT, &enableFrameSkip);
	if (enableFrameSkip) {
		if (g_Config.iFrameSkip == 0)
			g_Config.iFrameSkip = 3;

		float getfskip= g_Config.iFrameSkip;
		char showfskip[256];
		sprintf(showfskip, "Skip Frames: %0.0f", getfskip);
		ui_draw2d.DrawText(UBUNTU24, showfskip, dp_xres - 8, 12, 0xc0000000, ALIGN_TOPRIGHT);
		ui_draw2d.DrawText(UBUNTU24, showfskip, dp_xres - 10, 10, 0xFF3fFF3f, ALIGN_TOPRIGHT);
		ui_draw2d.DrawText(UBUNTU24, gs->T("Frames :"), x + 60, y += stride + 10, 0xFFFFFFFF, ALIGN_LEFT);
		HLinear hlinear1(x + 200 , y + 5, 20);
		if (UIButton(GEN_ID, hlinear1, 80, 0, "Auto", ALIGN_LEFT))
			g_Config.iFrameSkip = 3;
		if (UIButton(GEN_ID, hlinear1, 40, 0, "-1", ALIGN_LEFT))
			if(g_Config.iFrameSkip>0){
			g_Config.iFrameSkip -= 1;}
		if (UIButton(GEN_ID, hlinear1, 40, 0, "+1", ALIGN_LEFT))
			if(g_Config.iFrameSkip!=9){
			g_Config.iFrameSkip += 1;}
	}
	else {
		g_Config.iFrameSkip = 0;
	}

	I18NCategory *i = GetI18NCategory("Pause");

	// TODO: Add UI for more than one slot.
	HLinear hlinear1(x, y + 80, 20);
	if (UIButton(GEN_ID, hlinear1, LARGE_BUTTON_WIDTH, 0, i->T("Save State"), ALIGN_LEFT)) {
		SaveState::SaveSlot(0, 0, 0);
		screenManager()->finishDialog(this, DR_CANCEL);
	}
	if (UIButton(GEN_ID, hlinear1, LARGE_BUTTON_WIDTH, 0, i->T("Load State"), ALIGN_LEFT)) {
		SaveState::LoadSlot(0, 0, 0);
		screenManager()->finishDialog(this, DR_CANCEL);
	}

	VLinear vlinear(dp_xres - 10, 160, 20);
	if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH + 20, 0, i->T("Continue"), ALIGN_RIGHT)) {
		screenManager()->finishDialog(this, DR_CANCEL);
	}

	if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH + 20, 0, i->T("Settings"), ALIGN_RIGHT)) {
		screenManager()->push(new SettingsScreen(), 0);
	}

	if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH + 20, 0, i->T("Back to Menu"), ALIGN_RIGHT)) {
		screenManager()->finishDialog(this, DR_OK);
	}

	/*
	if (UIButton(GEN_ID, Pos(dp_xres - 10, dp_yres - 10), LARGE_BUTTON_WIDTH*2, 0, "Debug: Dump Next Frame", ALIGN_BOTTOMRIGHT)) {
		gpu->DumpNextFrame();
	}
	*/

	DrawWatermark();
	UIEnd();
}
Пример #29
0
void MenuScreen::render() {
	UIShader_Prepare();
	UIBegin(UIShader_Get());
	DrawBackground(1.0f);

	double xoff = 150 - frames_ * frames_ * 0.4f;
	if (xoff < -20)
		xoff = -20;
	if (frames_ > 200)  // seems the above goes nuts after a while...
		xoff = -20;

	int w = LARGE_BUTTON_WIDTH + 60;

	ui_draw2d.DrawTextShadow(UBUNTU48, "PPSSPP", dp_xres + xoff - w/2, 75, 0xFFFFFFFF, ALIGN_HCENTER | ALIGN_BOTTOM);
	ui_draw2d.SetFontScale(0.7f, 0.7f);
	ui_draw2d.DrawTextShadow(UBUNTU24, PPSSPP_GIT_VERSION, dp_xres + xoff, 85, 0xFFFFFFFF, ALIGN_RIGHT | ALIGN_BOTTOM);
	ui_draw2d.SetFontScale(1.0f, 1.0f);
	VLinear vlinear(dp_xres + xoff, 100, 20);

	I18NCategory *m = GetI18NCategory("MainMenu");

	if (UIButton(GEN_ID, vlinear, w, 0, m->T("Load", "Load..."), ALIGN_RIGHT)) {
#if defined(USING_QT_UI) && !defined(MEEGO_EDITION_HARMATTAN)
		QString fileName = QFileDialog::getOpenFileName(NULL, "Load ROM", g_Config.currentDirectory.c_str(), "PSP ROMs (*.iso *.cso *.pbp *.elf)");
		if (QFile::exists(fileName)) {
			QDir newPath;
			g_Config.currentDirectory = newPath.filePath(fileName).toStdString();
			g_Config.Save();
			screenManager()->switchScreen(new EmuScreen(fileName.toStdString()));
		}
#elif _WIN32
		MainWindow::BrowseAndBoot("");
#else
		FileSelectScreenOptions options;
		options.allowChooseDirectory = true;
		options.filter = "iso:cso:pbp:elf:prx:";
		options.folderIcon = I_ICON_FOLDER;
		options.iconMapping["iso"] = I_ICON_UMD;
		options.iconMapping["cso"] = I_ICON_UMD;
		options.iconMapping["pbp"] = I_ICON_EXE;
		options.iconMapping["elf"] = I_ICON_EXE;
		screenManager()->switchScreen(new FileSelectScreen(options));
#endif
		UIReset();
	}

	if (UIButton(GEN_ID, vlinear, w, 0, m->T("Settings"), ALIGN_RIGHT)) {
		screenManager()->push(new SettingsScreen(), 0);
		UIReset();
	}

	if (UIButton(GEN_ID, vlinear, w, 0, m->T("Credits"), ALIGN_RIGHT)) {
		screenManager()->switchScreen(new CreditsScreen());
		UIReset();
	}

	if (UIButton(GEN_ID, vlinear, w, 0, m->T("Exit"), ALIGN_RIGHT)) {
		// TODO: Save when setting changes, rather than when we quit
		NativeShutdown();
		// TODO: Need a more elegant way to quit
#ifdef _WIN32
		ExitProcess(0);
#else
		exit(0);
#endif
	}

	if (UIButton(GEN_ID, vlinear, w, 0, "www.ppsspp.org", ALIGN_RIGHT)) {
		LaunchBrowser("http://www.ppsspp.org/");
	}

	int recentW = 350;
	if (g_Config.recentIsos.size()) {
		ui_draw2d.DrawText(UBUNTU24, m->T("Recent"), -xoff, 80, 0xFFFFFFFF, ALIGN_BOTTOMLEFT);
	}

	int spacing = 15;

	float textureButtonWidth = 144;
	float textureButtonHeight = 80;

	if (dp_yres < 480)
		spacing = 8;
	// On small screens, we can't fit four vertically.
	if (100 + spacing * 6 + textureButtonHeight * 4 > dp_yres) {
		textureButtonHeight = (dp_yres - 100 - spacing * 6) / 4;
		textureButtonWidth = (textureButtonHeight / 80) * 144;
	}

	VGrid vgrid_recent(-xoff, 100, std::min(dp_yres-spacing*2, 480), spacing, spacing);

	for (size_t i = 0; i < g_Config.recentIsos.size(); i++) {
		std::string filename;
		std::string rec = g_Config.recentIsos[i];
		for (size_t j = 0; j < rec.size(); j++)
			if (rec[j] == '\\') rec[j] = '/';
		SplitPath(rec, nullptr, &filename, nullptr);

		UIContext *ctx = screenManager()->getUIContext();
		// This might create a texture so we must flush first.
		UIFlush();
		GameInfo *ginfo = g_gameInfoCache.GetInfo(g_Config.recentIsos[i], false);
		if (ginfo && ginfo->fileType != FILETYPE_PSP_ELF) {
			u32 color;
			if (ginfo->iconTexture == 0) {
				color = 0;
			} else {
				color = whiteAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 2));
			}
			if (UITextureButton(ctx, (int)GEN_ID_LOOP(i), vgrid_recent, textureButtonWidth, textureButtonHeight, ginfo->iconTexture, ALIGN_LEFT, color, I_DROP_SHADOW)) {
				UIEnd();
				screenManager()->switchScreen(new EmuScreen(g_Config.recentIsos[i]));
				return;
			}
		} else {
			if (UIButton((int)GEN_ID_LOOP(i), vgrid_recent, textureButtonWidth, textureButtonHeight, filename.c_str(), ALIGN_LEFT)) {
				UIEnd();
				screenManager()->switchScreen(new EmuScreen(g_Config.recentIsos[i]));
				return;
			}
		}
	}

#if defined(_DEBUG) & defined(_WIN32)
	// Print the current dp_xres/yres in the corner. For UI scaling testing - just
	// resize to 800x480 to get an idea of what it will look like on a Nexus S.
	ui_draw2d.SetFontScale(0.4, 0.4);
	char temptext[64];
	sprintf(temptext, "%ix%i", dp_xres, dp_yres);
	ui_draw2d.DrawTextShadow(UBUNTU24, temptext, 5, dp_yres-5, 0xFFFFFFFF, ALIGN_BOTTOMLEFT);
	ui_draw2d.SetFontScale(1.0, 1.0);
#endif

	DrawWatermark();

	UIEnd();
}
Пример #30
0
bool GPUCommon::InterpretList(DisplayList &list) {
	// Initialized to avoid a race condition with bShowDebugStats changing.
	double start = 0.0;
	if (g_Config.bShowDebugStats) {
		time_update();
		start = time_now_d();
	}

	easy_guard guard(listLock);

	// TODO: This has to be right... but it freezes right now?
	//if (list.state == PSP_GE_DL_STATE_PAUSED)
	//	return false;
	currentList = &list;

	if (!list.started && list.context != NULL) {
		gstate.Save(list.context);
	}
	list.started = true;

	gstate_c.offsetAddr = list.offsetAddr;

	if (!Memory::IsValidAddress(list.pc)) {
		ERROR_LOG_REPORT(G3D, "DL PC = %08x WTF!!!!", list.pc);
		return true;
	}

	// TODO: Use new interface.
#if defined(USING_QT_UI)
	if (host->GpuStep()) {
		host->SendGPUStart();
	}
#endif

	cycleLastPC = list.pc;
	downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;
	list.state = PSP_GE_DL_STATE_RUNNING;
	list.interrupted = false;

	gpuState = list.pc == list.stall ? GPUSTATE_STALL : GPUSTATE_RUNNING;
	guard.unlock();

	const bool useDebugger = host->GPUDebuggingActive();
	const bool useFastRunLoop = !dumpThisFrame_ && !useDebugger;
	while (gpuState == GPUSTATE_RUNNING) {
		{
			easy_guard innerGuard(listLock);
			if (list.pc == list.stall) {
				gpuState = GPUSTATE_STALL;
				downcount = 0;
			}
		}

		if (useFastRunLoop) {
			FastRunLoop(list);
		} else {
			SlowRunLoop(list);
		}

		{
			easy_guard innerGuard(listLock);
			downcount = list.stall == 0 ? 0x0FFFFFFF : (list.stall - list.pc) / 4;

			if (gpuState == GPUSTATE_STALL && list.stall != list.pc) {
				// Unstalled.
				gpuState = GPUSTATE_RUNNING;
			}
		}
	}

	// We haven't run the op at list.pc, so it shouldn't count.
	if (cycleLastPC != list.pc) {
		UpdatePC(list.pc - 4, list.pc);
	}

	list.offsetAddr = gstate_c.offsetAddr;

	if (g_Config.bShowDebugStats) {
		time_update();
		gpuStats.msProcessingDisplayLists += time_now_d() - start;
	}
	return gpuState == GPUSTATE_DONE || gpuState == GPUSTATE_ERROR;
}