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