mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 05:19:56 +00:00
Experiment with nudging the present timing to eliminate time gaps
This commit is contained in:
parent
864528303a
commit
fb4c167d37
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Data/Collections/FastVec.h"
|
||||
|
||||
// Flags and structs shared between backends that haven't found a good home.
|
||||
|
||||
@ -34,4 +35,6 @@ struct FrameTimeData {
|
||||
double earliestPresentTime;
|
||||
double presentMargin;
|
||||
};
|
||||
|
||||
constexpr size_t FRAME_TIME_HISTORY_LENGTH = 32;
|
||||
typedef HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> FrameHistoryBuffer;
|
||||
|
@ -37,7 +37,7 @@ GLRTexture::~GLRTexture() {
|
||||
}
|
||||
}
|
||||
|
||||
GLRenderManager::GLRenderManager(HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory) : frameTimeHistory_(frameTimeHistory) {
|
||||
GLRenderManager::GLRenderManager(FrameHistoryBuffer &frameTimeHistory) : frameTimeHistory_(frameTimeHistory) {
|
||||
// size_t sz = sizeof(GLRRenderData);
|
||||
// _dbg_assert_(sz == 88);
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ struct GLRRenderThreadTask {
|
||||
// directly in the destructor.
|
||||
class GLRenderManager {
|
||||
public:
|
||||
GLRenderManager(HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);
|
||||
GLRenderManager(FrameHistoryBuffer &frameTimeHistory);
|
||||
~GLRenderManager();
|
||||
|
||||
GLRenderManager(GLRenderManager &) = delete;
|
||||
@ -912,5 +912,5 @@ private:
|
||||
InvalidationCallback invalidationCallback_;
|
||||
|
||||
uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;
|
||||
HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;
|
||||
FrameHistoryBuffer &frameTimeHistory_;
|
||||
};
|
||||
|
@ -248,7 +248,7 @@ bool VKRComputePipeline::CreateAsync(VulkanContext *vulkan) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory)
|
||||
VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan, bool useThread, FrameHistoryBuffer &frameTimeHistory)
|
||||
: vulkan_(vulkan), queueRunner_(vulkan),
|
||||
initTimeMs_("initTimeMs"),
|
||||
totalGPUTimeMs_("totalGPUTimeMs"),
|
||||
|
@ -182,7 +182,7 @@ struct CompileQueueEntry {
|
||||
|
||||
class VulkanRenderManager {
|
||||
public:
|
||||
VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);
|
||||
VulkanRenderManager(VulkanContext *vulkan, bool useThread, FrameHistoryBuffer &frameTimeHistory);
|
||||
~VulkanRenderManager();
|
||||
|
||||
// Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again.
|
||||
@ -542,5 +542,5 @@ private:
|
||||
std::function<void(InvalidationCallbackFlags)> invalidationCallback_;
|
||||
|
||||
uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;
|
||||
HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;
|
||||
FrameHistoryBuffer &frameTimeHistory_;
|
||||
};
|
||||
|
@ -858,12 +858,12 @@ public:
|
||||
return "";
|
||||
}
|
||||
|
||||
const HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &FrameTimeHistory() const {
|
||||
const FrameHistoryBuffer &FrameTimeHistory() const {
|
||||
return frameTimeHistory_;
|
||||
}
|
||||
|
||||
protected:
|
||||
HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> frameTimeHistory_;
|
||||
FrameHistoryBuffer frameTimeHistory_;
|
||||
|
||||
ShaderModule *vsPresets_[VS_MAX_PRESET];
|
||||
ShaderModule *fsPresets_[FS_MAX_PRESET];
|
||||
|
@ -96,8 +96,26 @@ Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval) {
|
||||
return mode;
|
||||
}
|
||||
|
||||
void FrameTiming::BeforeCPUSlice() {
|
||||
void FrameTiming::BeforeCPUSlice(const FrameHistoryBuffer &frameHistory) {
|
||||
cpuSliceStartTime = time_now_d();
|
||||
|
||||
// Here we can examine the frame history for anomalies to correct.
|
||||
nudge_ = 0.0;
|
||||
|
||||
const FrameTimeData &oldData = frameHistory[3];
|
||||
if (oldData.queuePresent == 0.0) {
|
||||
// No data to look at.
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldData.afterFenceWait - oldData.frameBegin > 0.001) {
|
||||
nudge_ = (oldData.afterFenceWait - oldData.frameBegin) * 0.1;
|
||||
}
|
||||
|
||||
if (oldData.firstSubmit - oldData.afterFenceWait > cpuTime) {
|
||||
// Not sure how this grows so large sometimes.
|
||||
nudge_ = (oldData.firstSubmit - oldData.afterFenceWait - cpuTime) * 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameTiming::SetTimeStep(float scaledTimeStep) {
|
||||
@ -106,9 +124,9 @@ void FrameTiming::SetTimeStep(float scaledTimeStep) {
|
||||
double now = time_now_d();
|
||||
|
||||
cpuTime = now - cpuSliceStartTime;
|
||||
this->timeStep = scaledTimeStep + nudge_;
|
||||
this->timeStep = scaledTimeStep;
|
||||
|
||||
// Sync up lastPresentTime with the current time if it's way off.
|
||||
// Sync up lastPresentTime with the current time if it's way off. TODO: This should probably drift.
|
||||
if (lastPresentTime < now - 0.5f) {
|
||||
lastPresentTime = now;
|
||||
}
|
||||
@ -128,7 +146,7 @@ void FrameTiming::BeforePresent() {
|
||||
return;
|
||||
|
||||
// Wait until we hit the next present time. Ideally we'll be fairly close here due to the previous AfterPresent wait.
|
||||
nextPresentTime = lastPresentTime + this->timeStep;
|
||||
nextPresentTime = lastPresentTime + this->timeStep + nudge_;
|
||||
while (true) {
|
||||
double remaining = nextPresentTime - time_now_d();
|
||||
if (remaining <= 0.0)
|
||||
|
@ -27,7 +27,7 @@ public:
|
||||
|
||||
void Reset(Draw::DrawContext *draw);
|
||||
|
||||
void BeforeCPUSlice();
|
||||
void BeforeCPUSlice(const FrameHistoryBuffer &frameHistory);
|
||||
void SetTimeStep(float scaledTimeStep);
|
||||
void AfterCPUSlice();
|
||||
void BeforePresent();
|
||||
|
@ -442,7 +442,7 @@ static void DoFrameTiming(bool throttle, bool *skipFrame, float scaledTimestep)
|
||||
#ifdef _WIN32
|
||||
sleep_ms(1); // Sleep for 1ms on this thread
|
||||
#else
|
||||
const double left = g_frameTiming.nextFrameTime - g_frameTiming.curFrameTime;
|
||||
const double left = nextFrameTime - curFrameTime;
|
||||
usleep((long)(left * 1000000));
|
||||
#endif
|
||||
}
|
||||
@ -637,9 +637,13 @@ void __DisplayFlip(int cyclesLate) {
|
||||
bool throttle = FrameTimingThrottled();
|
||||
int fpsLimit = FrameTimingLimit();
|
||||
float scaledTimestep = (float)numVBlanksSinceFlip * timePerVblank;
|
||||
const bool fbReallyDirty = gpu->FramebufferReallyDirty();
|
||||
if (fpsLimit > 0 && fpsLimit != framerate) {
|
||||
scaledTimestep *= (float)framerate / fpsLimit;
|
||||
}
|
||||
bool skipFrame;
|
||||
int maxFrameskip;
|
||||
int frameSkipNum;
|
||||
|
||||
// If the ideal case, use the new timing path.
|
||||
g_frameTiming.usePresentTiming = g_Config.iFrameSkip == 0 && !refreshRateNeedsSkip;
|
||||
@ -659,7 +663,6 @@ void __DisplayFlip(int cyclesLate) {
|
||||
}
|
||||
|
||||
// Setting CORE_NEXTFRAME (which Core_NextFrame does) causes a swap.
|
||||
const bool fbReallyDirty = gpu->FramebufferReallyDirty();
|
||||
if (fbReallyDirty || noRecentFlip || postEffectRequiresFlip) {
|
||||
// Check first though, might've just quit / been paused.
|
||||
if (!forceNoFlip && Core_NextFrame()) {
|
||||
@ -674,11 +677,10 @@ void __DisplayFlip(int cyclesLate) {
|
||||
gpuStats.numFlips++;
|
||||
}
|
||||
|
||||
bool skipFrame;
|
||||
DoFrameTiming(throttle, &skipFrame, scaledTimestep);
|
||||
|
||||
int maxFrameskip = 8;
|
||||
int frameSkipNum = DisplayCalculateFrameSkip();
|
||||
maxFrameskip = 8;
|
||||
frameSkipNum = DisplayCalculateFrameSkip();
|
||||
if (throttle) {
|
||||
// 4 here means 1 drawn, 4 skipped - so 12 fps minimum.
|
||||
maxFrameskip = frameSkipNum;
|
||||
|
@ -115,12 +115,13 @@ static void DrawFrameTiming(UIContext *ctx, const Bounds &bounds) {
|
||||
ctx->Draw()->SetFontScale(0.5f, 0.5f);
|
||||
|
||||
snprintf(statBuf, sizeof(statBuf),
|
||||
"Mode (interval): %s (%d)"
|
||||
"Mode (interval): %s (%d) (%s)\n"
|
||||
"CPU time: %0.1fms\n"
|
||||
"Timestep: %0.1fms\n"
|
||||
"Postsleep: %0.1fms\n",
|
||||
Draw::PresentModeToString(g_frameTiming.presentMode),
|
||||
g_frameTiming.presentInterval,
|
||||
g_frameTiming.usePresentTiming ? "new" : "old",
|
||||
g_frameTiming.cpuTime * 1000.0,
|
||||
g_frameTiming.timeStep * 1000.0,
|
||||
g_frameTiming.postSleep * 1000.0
|
||||
|
@ -1164,7 +1164,7 @@ void NativeFrame(GraphicsContext *graphicsContext) {
|
||||
|
||||
g_screenManager->getUIContext()->SetTintSaturation(g_Config.fUITint, g_Config.fUISaturation);
|
||||
|
||||
g_frameTiming.BeforeCPUSlice();
|
||||
g_frameTiming.BeforeCPUSlice(g_draw->FrameTimeHistory());
|
||||
// All actual rendering happen in here.
|
||||
g_screenManager->render();
|
||||
if (g_screenManager->getUIContext()->Text()) {
|
||||
|
Loading…
Reference in New Issue
Block a user