Skip to content

Commit

Permalink
Merge pull request #18573 from hrydgard/defer-frame-waits
Browse files Browse the repository at this point in the history
Defer frame waits if possible
  • Loading branch information
hrydgard committed Dec 18, 2023
2 parents ef149bd + e1f1af6 commit e64299f
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 13 deletions.
29 changes: 29 additions & 0 deletions Core/FrameTiming.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@

FrameTiming g_frameTiming;

void WaitUntil(double now, double timestamp) {
#ifdef _WIN32
while (time_now_d() < timestamp) {
sleep_ms(1); // Sleep for 1ms on this thread
}
#else
const double left = timestamp - now;
if (left > 0.0) {
usleep((long)(left * 1000000));
}
#endif
}

inline Draw::PresentMode GetBestImmediateMode(Draw::PresentMode supportedModes) {
if (supportedModes & Draw::PresentMode::MAILBOX) {
return Draw::PresentMode::MAILBOX;
Expand All @@ -50,6 +63,22 @@ void FrameTiming::Reset(Draw::DrawContext *draw) {
}
}

void FrameTiming::DeferWaitUntil(double until, double *curTimePtr) {
_dbg_assert_(until > 0.0);
waitUntil_ = until;
curTimePtr_ = curTimePtr;
}

void FrameTiming::PostSubmit() {
if (waitUntil_ != 0.0) {
WaitUntil(time_now_d(), waitUntil_);
if (curTimePtr_) {
*curTimePtr_ = waitUntil_;
}
waitUntil_ = 0.0;
}
}

Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval) {
Draw::PresentMode mode = Draw::PresentMode::FIFO;

Expand Down
13 changes: 11 additions & 2 deletions Core/FrameTiming.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,23 @@ namespace Draw {
class DrawContext;
}

struct FrameTiming {
class FrameTiming {
public:
void DeferWaitUntil(double until, double *curTimePtr);
void PostSubmit();
void Reset(Draw::DrawContext *draw);

// Some backends won't allow changing this willy nilly.
Draw::PresentMode presentMode;
int presentInterval;

void Reset(Draw::DrawContext *draw);
private:
double waitUntil_;
double *curTimePtr_;
};

extern FrameTiming g_frameTiming;

Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval);

void WaitUntil(double now, double timestamp);
26 changes: 15 additions & 11 deletions Core/HLE/sceDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ static void DoFrameDropLogging(float scaledTimestep) {

// All the throttling and frameskipping logic is here.
// This is called just before we drop out of the main loop, in order to allow the submit and present to happen.
static void DoFrameTiming(bool throttle, bool *skipFrame, float scaledTimestep) {
static void DoFrameTiming(bool throttle, bool *skipFrame, float scaledTimestep, bool endOfFrame) {
PROFILE_THIS_SCOPE("timing");
*skipFrame = false;

Expand Down Expand Up @@ -438,16 +438,15 @@ static void DoFrameTiming(bool throttle, bool *skipFrame, float scaledTimestep)
nextFrameTime = curFrameTime;
} else {
// Wait until we've caught up.
while (time_now_d() < nextFrameTime) {
#ifdef _WIN32
sleep_ms(1); // Sleep for 1ms on this thread
#else
const double left = nextFrameTime - curFrameTime;
usleep((long)(left * 1000000));
#endif
// If we're ending the frame here, we'll defer the sleep until after the command buffers
// have been handed off to the render thread, for some more overlap.
if (endOfFrame) {
g_frameTiming.DeferWaitUntil(nextFrameTime, &curFrameTime);
} else {
WaitUntil(curFrameTime, nextFrameTime);
curFrameTime = time_now_d(); // I guess we could also just set it to nextFrameTime...
}
}
curFrameTime = time_now_d();
}

lastFrameTime = nextFrameTime;
Expand Down Expand Up @@ -637,9 +636,14 @@ void __DisplayFlip(int cyclesLate) {

// Setting CORE_NEXTFRAME (which Core_NextFrame does) causes a swap.
const bool fbReallyDirty = gpu->FramebufferReallyDirty();

bool nextFrame = false;

if (fbReallyDirty || noRecentFlip || postEffectRequiresFlip) {
// Check first though, might've just quit / been paused.
if (!forceNoFlip && Core_NextFrame()) {
if (!forceNoFlip)
nextFrame = Core_NextFrame();
if (nextFrame) {
gpu->CopyDisplayToOutput(fbReallyDirty);
if (fbReallyDirty) {
DisplayFireActualFlip();
Expand All @@ -659,7 +663,7 @@ void __DisplayFlip(int cyclesLate) {
scaledTimestep *= (float)framerate / fpsLimit;
}
bool skipFrame;
DoFrameTiming(throttle, &skipFrame, scaledTimestep);
DoFrameTiming(throttle, &skipFrame, scaledTimestep, nextFrame);

int maxFrameskip = 8;
int frameSkipNum = DisplayCalculateFrameSkip();
Expand Down
1 change: 1 addition & 0 deletions UI/NativeApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,7 @@ void NativeFrame(GraphicsContext *graphicsContext) {

// This, between EndFrame and Present, is where we should actually wait to do present time management.
// There might not be a meaningful distinction here for all backends..
g_frameTiming.PostSubmit();

if (renderCounter < 10 && ++renderCounter == 10) {
// We're rendering fine, clear out failure info.
Expand Down

0 comments on commit e64299f

Please sign in to comment.