Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defer frame waits if possible #18573

Merged
merged 4 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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