// Main game loop void MainGame::GameLoop() { while (_gameState != GameState::EXIT) { // use for frame time measuring Uint32 startTicks = SDL_GetTicks(); ProcessInput(); _time += 0.01f; DrawGame(); CalculateFPS(); // print only once every 10 frames static int frameCounter = 0; frameCounter++; if (frameCounter == 10) { //std::cout << _fps << std::endl; frameCounter = 0; } // Limit fps to max fps Uint32 frameTicks = SDL_GetTicks() - startTicks; if (1000.0f / _maxFps > frameTicks) { SDL_Delay(1000.0f / _maxFps - frameTicks); } } }
// Time.Time.Fps_Update /////////////////////////////////////////////////////////////////////////////////// // Updates the variables (Must be called each program loop). void CTimeManager::Fps_Update() { CalculateMS(); CalculateFPS(); CalculateAverageFPS(); CalculateClock(); }
void CTimeManager::Update(f64 dt) { CalculateElapsedTime(); CalculateFPS(); UpdateTimers(); //CCoreEngine::Instance().GetLogManager().LogOutput(LOG_INFO, LOGSUB_TIMER,"dTime = %F | FPS = %F %d", m_ElapsedTimeSeconds ,m_FramesPerSecond); }
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) { #ifdef _DEBUG // this is for enabling memory leak detection _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif MSG msg = { 0 }; double tempDT = 10.0; // create window HWND wndHandle = InitWindow(hInstance); //float test = 5; // window is valid if (wndHandle) { // display window ShowWindow(wndHandle, nCmdShow); //Create engine class Engine* engine = new Engine(); engine->Initialize(&wndHandle, &hInstance); GameTimer* time = GameTimer::GetInstance(); //test = AntTweakBar::GetInstance()->GetBar();s //AntTweakBar::GetInstance()->addSlider("test", test); time->Reset(); // enter message loop, loop until the message WM_QUIT is received. while (WM_QUIT != msg.message) { // read messages if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); // this will call the function WndProc below! DispatchMessage(&msg); } else { time->Tick(); double dt = time->DeltaTime(); CalculateFPS(wndHandle, *time); engine->Frame(&dt); // update/render goes here } } // finish the program engine->Release(); delete engine; //delete time; DestroyWindow(wndHandle); } // return how the program finished. return (int) msg.wParam; }
void App::IdleCB() { glm::vec2 worldMousePos = ConvertWindowPosToWorldPos(m_MousePos); m_pGame->MainGameLoop((int)worldMousePos.x, (int)worldMousePos.y); m_pCamera->CenterOn(m_pGame->GetPlayerCenter()); CalculateFPS(); glutPostRedisplay(); }
//called at the end of the frame float FpsLimiter::End() { CalculateFPS(); float frameTicks = (float)SDL_GetTicks() - m_startTicks; //check if the time it took this frame to be completed is lower than the desired frame time if (1000.0f / m_maxFPS > frameTicks) { //if the frame was completed too fast, delay it to limit the fps SDL_Delay((Uint32)(1000.0f / m_maxFPS - frameTicks)); } return m_fps; }
void Arti3DApp::Run() { while (m_bRunning) { SDL_Event event; while (SDL_PollEvent(&event)) HandleEvent(event, this); RenderScene(); CalculateFPS(); m_pWindow->UpdateSurface(); } }
float FPSLimiter::EndFrame() { CalculateFPS(); auto frameTicks = SDL_GetTicks() - StartTicks; // Limit the FPS if (1000.0f / MaxFPS > frameTicks) { SDL_Delay(1000.0f / MaxFPS - frameTicks); } return FPS; }
void App::Update_Internal() { appTimer.Update(); const uint32 displayWidth = swapChain.Width(); const uint32 displayHeight = swapChain.Height(); ImGuiHelper::BeginFrame(displayWidth, displayHeight, appTimer.DeltaSecondsF()); CalculateFPS(); AppSettings::Update(displayWidth, displayHeight, appViewMatrix); Update(appTimer); }
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; }
int main() { // Init GLFW glfwInit(); // Create a GLFWwindow object that we can use for GLFW's functions Window window = Window(WIDTH, HEIGHT, TITLE); window.DefineViewport(); //glfwSetInputMode(window.getWindowPtr(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); /// ^(Maybe use this later) // Callback functions glfwSetKeyCallback(window.getWindowPtr() , key_callback); // Init GLEW glewExperimental = GL_TRUE; glewInit(); // Enable alpha channel transparency glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Load and compile shaders to a program Shader ShaderProgram = Shader("../deps/shaders/shadervert.vs", "../deps/shaders/shaderfrag.fs"); // Load explosion graphics extern_Explosion.Init(ShaderProgram.GetProgramID()); // Load texture/Game objects Texture2D texture_background1 = Texture2D("../deps/textures/backgroundSpace_01.1.png", PNG_RGB); SpriteMap Background = SpriteMap(texture_background1, 1.0f, 1.0f, glm::vec3(0.0f, 0.0f, 0.0f), 1.0f, BACKGROUND); Player PlayerShip; PlayerShip.Init(moveSpeed); Enemy Enemies; Enemies.Init(); // Projection matrix: ortho for 2D glm::mat4 proj = glm::ortho(0, window.getWidth(), 0, window.getHeight()); // Game loop while (!window.ShouldClose()) { double startFrame = glfwGetTime(); ///< for FPS limiting // Check if any events have been activiated and call callback function (via GLFW) glfwPollEvents(); // Clear the colorbuffer glClearColor(0.6f, 0.8f, 0.8f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Drawing */ ShaderProgram.Use(); // Background position and drawing calculations - just identity matrix glm::mat4 model; GLint modelLoc = glGetUniformLocation(ShaderProgram.GetProgramID(), "model"); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); Background.BackgroundScroll(scrollSpeed); Background.Draw(); Collision::EnemyHit(&PlayerShip, &Enemies); Collision::PlayerHit(&PlayerShip, &Enemies); Collision::ShipsCollide(&PlayerShip, &Enemies); PlayerShip.Move(keys); PlayerShip.AddShots(keys); PlayerShip.Draw(ShaderProgram.GetProgramID()); Enemies.Move(); Enemies.Shoot(PlayerShip.GetPosition()); Enemies.Draw(ShaderProgram.GetProgramID()); extern_Explosion.Draw(); // FPS Calculation/Limiting float fps = CalculateFPS(); static int printFPS = 0; if (printFPS == 100) { Enemies.Add(EN_0, PlayerShip.GetPosition()); Enemies.Add(EN_1, PlayerShip.GetPosition()); std::cout << fps << std::endl; printFPS = 0; } else { printFPS++; } LimitFPS(FPS, startFrame); if (PlayerShip.GetLives() <= 0) { window.Close(); } // Swap the screen buffers window.SwapBuffers(); } Background.Delete(); PlayerShip.Delete(); Enemies.DeleteAll(); extern_Explosion.DeleteAll(); // Close GLFW glfwTerminate(); return 0; }
float RenderFrame (SystemState *RFState) { static unsigned short FrameCounter=0; //********************************Start of frame Render***************************************************** SetBlinkState(BlinkPhase); irq_fs(0); //FS low to High transition start of display Boink needs this for (RFState->LineCounter=0;RFState->LineCounter<13;RFState->LineCounter++) //Vertical Blanking 13 H lines CPUCycle(); for (RFState->LineCounter=0;RFState->LineCounter<4;RFState->LineCounter++) //4 non-Rendered top Boarder lines CPUCycle(); if (!(FrameCounter % RFState->FrameSkip)) if (LockScreen(RFState)) return(0); for (RFState->LineCounter=0;RFState->LineCounter<(TopBoarder-4);RFState->LineCounter++) { if (!(FrameCounter % RFState->FrameSkip)) DrawTopBoarder[RFState->BitDepth](RFState); CPUCycle(); } for (RFState->LineCounter=0;RFState->LineCounter<LinesperScreen;RFState->LineCounter++) //Active Display area { CPUCycle(); if (!(FrameCounter % RFState->FrameSkip)) UpdateScreen[RFState->BitDepth] (RFState); } irq_fs(1); //End of active display FS goes High to Low if (VertInteruptEnabled) GimeAssertVertInterupt(); for (RFState->LineCounter=0;RFState->LineCounter < (BottomBoarder) ;RFState->LineCounter++) // Bottom boarder { // if ( (RFState->LineCounter==1) & (VertInteruptEnabled) ) //Vert Interupt occurs 1 line into // GimeAssertVertInterupt(); // Bottom Boarder MPATDEMO CPUCycle(); if (!(FrameCounter % RFState->FrameSkip)) DrawBottomBoarder[RFState->BitDepth](RFState); } if (!(FrameCounter % RFState->FrameSkip)) { UnlockScreen(RFState); SetBoarderChange(0); } for (RFState->LineCounter=0;RFState->LineCounter<6;RFState->LineCounter++) //Vertical Retrace 6 H lines CPUCycle(); switch (SoundOutputMode) { case 0: FlushAudioBuffer(AudioBuffer,AudioIndex<<2); break; case 1: FlushCassetteBuffer(CassBuffer,AudioIndex); break; case 2: LoadCassetteBuffer(CassBuffer); break; } AudioIndex=0; /* //Debug Code Frames++; if (Frames==60) { Frames=0; sprintf(Msga,"Total Cycles = %i Scan lines = %i LPS= %i\n",TotalCycles,Scans,LinesperScreen+TopBoarder+BottomBoarder+19); WriteLog(Msga,0); TotalCycles=0; Scans=0; } */ return(CalculateFPS()); }
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); }
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; } }
NewtonDemos::NewtonDemos(const wxString& title, const wxPoint& pos, const wxSize& size) :wxFrame(NULL, -1, title, pos, size) ,m_mainMenu(NULL) ,m_joystick(NULL) ,m_statusbar(NULL) ,m_scene(NULL) ,m_broadPhaseType(0) ,m_physicsUpdateMode(0) ,m_suspendVisualUpdates(true) ,m_autoSleepState(true) ,m_useParallelSolver(false) ,m_hideVisualMeshes(false) ,m_showContactPoints(false) ,m_showNormalForces(false) ,m_showAABB(false) ,m_showJoints(false) ,m_showCenterOfMass(false) ,m_showStatistics(false) ,m_concurrentProfilerState(false) ,m_threadProfilerState(false) ,m_hasJoysticController(false) ,m_shiftKey(false) ,m_controlKey(false) ,m_solverModeIndex(3) ,m_solverModeQuality(0) ,m_debugDisplayMode(0) ,m_mousePosX(0) ,m_mousePosY(0) ,m_joytickX(0) ,m_joytickY(0) ,m_joytickButtonMask(0) ,m_framesCount(0) ,m_microthreadIndex(0) ,m_hardwareDevice(0) ,m_timestepAcc(0) ,m_fps(0.0f) { /* //m_broadPhaseType = 1; //m_autoSleepState = false; //m_microthreadIndex = 1; //m_useParallelSolver = true; //m_threadProfilerState = true; m_showNormalForces = true; //m_showCenterOfMass = true; m_hideVisualMeshes = true; //m_physicsUpdateMode = 1; m_showContactPoints = true; //m_hardwareDevice = 2; //m_showStatistics = true; //*/ //m_debugDisplayMode = 2; //m_autoSleepState = false; //m_microthreadIndex = 1; //m_useParallelSolver = true; memset (m_profilerTracksMenu, 0, sizeof (m_profilerTracksMenu)); // clear the key map memset (m_key, 0, sizeof (m_key)); for (int i = 0; i < int (sizeof (m_keyMap)/sizeof (m_keyMap[0])); i ++) { m_keyMap[i] = i; } for (int i = 'a'; i <= 'z'; i ++) { m_keyMap[i] = i - 'a' + 'A'; } #ifdef WIN32 m_keyMap[0] = VK_LBUTTON; m_keyMap[1] = VK_RBUTTON; m_keyMap[2] = VK_MBUTTON; #endif m_scene = new DemoEntityManager(this); m_statusbar = CreateStatusBar(); //int widths[] = {150, 160, 150, 90, 80, 100, 100}; int widths[] = {-1, -1, -1, -1, -1, -1, -1}; m_statusbar->SetFieldsCount (sizeof (widths)/sizeof (widths[0]), widths); CalculateFPS(0.0f); m_mainMenu = CreateMainMenu(); /* wxJoystick stick(wxJOYSTICK1); if (!stick.IsOk()) { wxMessageBox(wxT("No joystick detected!")); return false; } #if wxUSE_SOUND m_fire.Create(wxT("buttonpress.wav")); #endif // wxUSE_SOUND m_minX = stick.GetXMin(); m_minY = stick.GetYMin(); m_maxX = stick.GetXMax(); m_maxY = stick.GetYMax(); */ // f*****g wxwidget require a f*****g library just to read a f*****g joystick f**k you WxWidget // m_joystick = new wxJoystick(wxJOYSTICK1); // m_joystick->SetCapture(this, 10); }
int32 App::Run() { try { if(createConsole) { Win32Call(AllocConsole()); Win32Call(SetConsoleTitle(applicationName.c_str())); FILE* consoleFile = nullptr; freopen_s(&consoleFile, "CONOUT$", "wb", stdout); } window.SetClientArea(deviceManager.BackBufferWidth(), deviceManager.BackBufferHeight()); deviceManager.Initialize(window); if(showWindow) window.ShowWindow(); blendStates.Initialize(deviceManager.Device()); rasterizerStates.Initialize(deviceManager.Device()); depthStencilStates.Initialize(deviceManager.Device()); samplerStates.Initialize(deviceManager.Device()); // Create a font + SpriteRenderer font.Initialize(L"Arial", 18, SpriteFont::Regular, true, deviceManager.Device()); spriteRenderer.Initialize(deviceManager.Device()); Profiler::GlobalProfiler.Initialize(deviceManager.Device(), deviceManager.ImmediateContext()); window.RegisterMessageCallback(WM_SIZE, OnWindowResized, this); // Initialize AntTweakBar TwCall(TwInit(TW_DIRECT3D11, deviceManager.Device())); // Create a tweak bar tweakBar = TwNewBar("Settings"); std::string helpTextDefinition = MakeAnsiString(" GLOBAL help='%s' ", globalHelpText.c_str()); TwCall(TwDefine(helpTextDefinition.c_str())); TwCall(TwDefine(" GLOBAL fontsize=3 ")); Settings.Initialize(tweakBar); TwHelper::SetValuesWidth(Settings.TweakBar(), 120, false); AppSettings::Initialize(deviceManager.Device()); Initialize(); AfterReset(); while(window.IsAlive()) { if(!window.IsMinimized()) { timer.Update(); Settings.Update(); CalculateFPS(); AppSettings::Update(); Update(timer); UpdateShaders(deviceManager.Device()); AppSettings::UpdateCBuffer(deviceManager.ImmediateContext()); Render(timer); // Render the profiler text spriteRenderer.Begin(deviceManager.ImmediateContext(), SpriteRenderer::Point); Profiler::GlobalProfiler.EndFrame(spriteRenderer, font); spriteRenderer.End(); { PIXEvent pixEvent(L"Ant Tweak Bar"); // Render the TweakBar UI TwCall(TwDraw()); } deviceManager.Present(); } window.MessageLoop(); } } catch(SampleFramework11::Exception exception) { exception.ShowErrorMessage(); return -1; } ShutdownShaders(); TwCall(TwTerminate()); if(createConsole) { fclose(stdout); FreeConsole(); } return returnCode; }
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; } }
void Profiler::Update(float dt) { CalculateFPS(dt); Clear(); }