CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HW/Display.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include <cmath>19#include <mutex>20#include <vector>21#include "Common/CommonTypes.h"22#include "Common/Serialize/SerializeFuncs.h"23#include "Common/System/System.h"24#include "Common/TimeUtil.h"25#include "Core/Config.h"26#include "Core/Core.h"27#include "Core/CoreTiming.h"28#include "Core/HLE/sceKernel.h"29#include "Core/HW/Display.h"30#include "GPU/GPU.h"31#include "GPU/GPUInterface.h"3233// Called when vblank happens (like an internal interrupt.) Not part of state, should be static.34static std::mutex listenersLock;35static std::vector<VblankCallback> vblankListeners;36typedef std::pair<FlipCallback, void *> FlipListener;37static std::vector<FlipListener> flipListeners;3839static uint64_t frameStartTicks;40static int numVBlanks;41// hCount is computed now.42static int vCount;43// The "AccumulatedHcount" can be adjusted, this is the base.44static uint32_t hCountBase;45static int isVblank;46static constexpr int hCountPerVblank = 286;4748// FPS stats for frameskip, FPS display, etc.49static int lastFpsFrame = 0;50static double lastFpsTime = 0.0;51static double fps = 0.0;52static int lastNumFlips = 0;53static float flips = 0.0f;54static int actualFlips = 0; // taking frameskip into account55static int lastActualFlips = 0;56static float actualFps = 0;5758// FPS stats for averaging.59static double fpsHistory[120];60static constexpr int fpsHistorySize = (int)ARRAY_SIZE(fpsHistory);61static int fpsHistoryPos = 0;62static int fpsHistoryValid = 0;6364// Frame time stats.65static double frameTimeHistory[600];66static double frameSleepHistory[600];67static constexpr int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory);68static int frameTimeHistoryPos = 0;69static int frameTimeHistoryValid = 0;70static double lastFrameTimeHistory = 0.0;7172static void CalculateFPS() {73double now = time_now_d();7475if (now >= lastFpsTime + 1.0) {76double frames = (numVBlanks - lastFpsFrame);77actualFps = (float)(actualFlips - lastActualFlips);7879fps = frames / (now - lastFpsTime);80flips = (float)(g_Config.iDisplayRefreshRate * (double)(gpuStats.numFlips - lastNumFlips) / frames);8182lastFpsFrame = numVBlanks;83lastNumFlips = gpuStats.numFlips;84lastActualFlips = actualFlips;85lastFpsTime = now;8687fpsHistory[fpsHistoryPos++] = fps;88fpsHistoryPos = fpsHistoryPos % fpsHistorySize;89if (fpsHistoryValid < fpsHistorySize) {90++fpsHistoryValid;91}92}9394if ((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::FRAME_GRAPH || coreCollectDebugStats) {95frameTimeHistory[frameTimeHistoryPos++] = now - lastFrameTimeHistory;96lastFrameTimeHistory = now;97frameTimeHistoryPos = frameTimeHistoryPos % frameTimeHistorySize;98if (frameTimeHistoryValid < frameTimeHistorySize) {99++frameTimeHistoryValid;100}101frameSleepHistory[frameTimeHistoryPos] = 0.0;102}103}104105// TODO: Also average actualFps106void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps) {107*out_vps = (float)fps;108*out_fps = flips;109*out_actual_fps = actualFps;110}111112void __DisplayGetVPS(float *out_vps) {113*out_vps = (float)fps;114}115116void __DisplayGetAveragedFPS(float *out_vps, float *out_fps) {117double avg = 0.0;118if (fpsHistoryValid > 0) {119for (int i = 0; i < fpsHistoryValid; ++i) {120avg += fpsHistory[i];121}122avg /= (double)fpsHistoryValid;123}124125*out_vps = *out_fps = (float)avg;126}127128int __DisplayGetFlipCount() {129return actualFlips;130}131132int __DisplayGetNumVblanks() {133return numVBlanks;134}135136int __DisplayGetVCount() {137return vCount;138}139140bool DisplayIsVblank() {141return isVblank != 0;142}143144uint64_t DisplayFrameStartTicks() {145return frameStartTicks;146}147148uint32_t __DisplayGetCurrentHcount() {149const int ticksIntoFrame = (int)(CoreTiming::GetTicks() - frameStartTicks);150const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank;151// Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right.152return 1 + (ticksIntoFrame / ticksPerVblank);153}154155uint32_t __DisplayGetAccumulatedHcount() {156// The hCount is always a positive int, and wraps from 0x7FFFFFFF -> 0.157int value = hCountBase + __DisplayGetCurrentHcount();158return value & 0x7FFFFFFF;159}160161void DisplayAdjustAccumulatedHcount(uint32_t diff) {162hCountBase += diff;163}164165double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep) {166*out_valid = frameTimeHistoryValid;167*out_pos = frameTimeHistoryPos;168*out_sleep = frameSleepHistory;169return frameTimeHistory;170}171172int DisplayGetSleepPos() {173return frameTimeHistoryPos;174}175176void DisplayNotifySleep(double t, int pos) {177if (pos < 0)178pos = frameTimeHistoryPos;179frameSleepHistory[pos] += t;180}181182void __DisplayGetDebugStats(char *stats, size_t bufsize) {183char statbuf[4096];184if (!gpu) {185snprintf(stats, bufsize, "N/A");186return;187}188gpu->GetStats(statbuf, sizeof(statbuf));189190snprintf(stats, bufsize,191"Kernel processing time: %0.2f ms\n"192"Slowest syscall: %s : %0.2f ms\n"193"Most active syscall: %s : %0.2f ms\n%s",194kernelStats.msInSyscalls * 1000.0f,195kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)",196kernelStats.slowestSyscallTime * 1000.0f,197kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)",198kernelStats.summedSlowestSyscallTime * 1000.0f,199statbuf);200}201202// On like 90hz, 144hz, etc, we return 60.0f as the framerate target. We only target other203// framerates if they're close to 60.204static float FramerateTarget() {205float target = System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE);206if (target < 57.0 || target > 63.0f) {207return 60.0f;208} else {209return target;210}211}212213bool DisplayIsRunningSlow() {214// Allow for some startup turbulence for 8 seconds before assuming things are bad.215if (fpsHistoryValid >= 8) {216// Look at only the last 15 samples (starting at the 14th sample behind current.)217int rangeStart = fpsHistoryPos - std::min(fpsHistoryValid, 14);218219double best = 0.0;220for (int i = rangeStart; i <= fpsHistoryPos; ++i) {221// rangeStart may have been negative if near a wrap around.222int index = (fpsHistorySize + i) % fpsHistorySize;223best = std::max(fpsHistory[index], best);224}225226return best < FramerateTarget() * 0.97;227}228229return false;230}231232void DisplayFireVblankStart() {233frameStartTicks = CoreTiming::GetTicks();234numVBlanks++;235236isVblank = 1;237vCount++; // vCount increases at each VBLANK.238hCountBase += hCountPerVblank; // This is the "accumulated" hcount base.239if (hCountBase > 0x7FFFFFFF) {240hCountBase -= 0x80000000;241}242}243244void DisplayFireVblankEnd() {245isVblank = 0;246std::vector<VblankCallback> toCall;247{248std::lock_guard<std::mutex> guard(listenersLock);249toCall = vblankListeners;250}251252for (VblankCallback cb : toCall) {253cb();254}255}256257void DisplayFireFlip() {258std::vector<FlipListener> toCall = [] {259std::lock_guard<std::mutex> guard(listenersLock);260return flipListeners;261}();262263// This is also the right time to calculate FPS.264CalculateFPS();265266for (FlipListener cb : toCall) {267cb.first(cb.second);268}269}270271void DisplayFireActualFlip() {272actualFlips++;273}274275void __DisplayListenVblank(VblankCallback callback) {276std::lock_guard<std::mutex> guard(listenersLock);277vblankListeners.push_back(callback);278}279280void __DisplayListenFlip(FlipCallback callback, void *userdata) {281std::lock_guard<std::mutex> guard(listenersLock);282flipListeners.emplace_back(callback, userdata);283}284285void __DisplayForgetFlip(FlipCallback callback, void *userdata) {286std::lock_guard<std::mutex> guard(listenersLock);287flipListeners.erase(std::remove_if(flipListeners.begin(), flipListeners.end(), [&](FlipListener item) {288return item.first == callback && item.second == userdata;289}), flipListeners.end());290}291292int DisplayCalculateFrameSkip() {293int frameSkipNum;294if (g_Config.iFrameSkipType == 1) {295// Calculate the frames to skip dynamically using the set percentage of the current fps296frameSkipNum = (int)ceil(flips * (static_cast<double>(g_Config.iFrameSkip) / 100.00));297} else {298// Use the set number of frames to skip299frameSkipNum = g_Config.iFrameSkip;300}301return frameSkipNum;302}303304void DisplayHWInit() {305frameStartTicks = 0;306numVBlanks = 0;307isVblank = 0;308vCount = 0;309hCountBase = 0;310311flips = 0;312fps = 0.0;313actualFlips = 0;314lastActualFlips = 0;315lastNumFlips = 0;316317fpsHistoryValid = 0;318fpsHistoryPos = 0;319320frameTimeHistoryValid = 0;321frameTimeHistoryPos = 0;322lastFrameTimeHistory = 0.0;323}324325void DisplayHWShutdown() {326std::lock_guard<std::mutex> guard(listenersLock);327vblankListeners.clear();328flipListeners.clear();329}330331void DisplayHWDoState(PointerWrap &p, int hleCompatV2) {332Do(p, frameStartTicks);333Do(p, vCount);334if (hleCompatV2) {335double oldHCountBase;336Do(p, oldHCountBase);337hCountBase = (int)oldHCountBase;338} else {339Do(p, hCountBase);340}341Do(p, isVblank);342}343344345