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); }
// 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; } }
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 } }
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; }
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(); }
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(); } }
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; }
void sceRtcGetCurrentTick() { #ifdef _WIN32 RETURN(GetTickCount()); #else time_update(); RETURN(time_now_d()); #endif }
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 } }
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; } } }
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(); } }
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 } }
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 } }
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(); } } }
// 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; }
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; } }
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); } }
// 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; }
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; } }
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; }
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); }
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(); }
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); }
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; }
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; }
// 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; } }
// 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", ¶mSFOcontents)) { 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; }
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(); }
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(); }
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; }