Another frameskipping attempt. Now: 0=no frameskip, 1=auto frameskipt, 2-9=fixed frameskip.

There is still some flicker remaining in non-buffered rendering that I can't seem to get rid off.
This commit is contained in:
Henrik Rydgard 2013-08-16 01:00:26 +02:00
parent 94096976ff
commit 664d74a9b7
8 changed files with 97 additions and 41 deletions

View File

@ -21,6 +21,7 @@
// TODO: Move the relevant parts into common. Don't want the core
// to be dependent on "native", I think. Or maybe should get rid of common
// and move everything into native...
#include "base/logging.h"
#include "base/timeutil.h"
#include "Common/Thread.h"
@ -321,7 +322,7 @@ void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) {
if (!throttle) {
doFrameSkip = true;
skipFrame = true;
if (numSkippedFrames >= 6) {
if (numSkippedFrames >= 7) {
skipFrame = false;
}
return;
@ -337,13 +338,21 @@ void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) {
if (nextFrameTime == 0.0)
nextFrameTime = time_now_d() + timestep;
if (curFrameTime > nextFrameTime && doFrameSkip) {
// Argh, we are falling behind! Let's skip a frame and see if we catch up.
skipFrame = true;
// INFO_LOG(HLE,"FRAMESKIP %i", numSkippedFrames);
if (g_Config.iFrameSkip == 1) {
// 1 == autoframeskip
if (curFrameTime > nextFrameTime && doFrameSkip) {
skipFrame = true;
}
} else if (g_Config.iFrameSkip > 1) {
// Other values = fixed frameskip
if (numSkippedFrames >= g_Config.iFrameSkip - 1)
skipFrame = false;
else
skipFrame = true;
}
if (curFrameTime < nextFrameTime && throttle)
{
// If time gap is huge just jump (somebody unthrottled)
@ -371,12 +380,6 @@ void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) {
} else {
nextFrameTime = nextFrameTime + timestep;
}
// Max 4 skipped frames in a row - 15 fps is really the bare minimum for playability.
// We check for 3 here so it's 3 skipped frames, 1 non skipped, 3 skipped, etc.
if (numSkippedFrames >= g_Config.iFrameSkip) {
skipFrame = false;
}
}
@ -404,6 +407,15 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1);
gpuStats.numVBlanks++;
numVBlanksSinceFlip++;
if (g_Config.iShowFPSCounter) {
CalculateFPS();
}
// TODO: Should this be done here or in hleLeaveVblank?
if (framebufIsLatched) {
DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
@ -413,49 +425,47 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
}
}
gpuStats.numVBlanks++;
numVBlanksSinceFlip++;
if (g_Config.iShowFPSCounter) {
CalculateFPS();
}
// We flip only if the framebuffer was dirty. This eliminates flicker when using
// non-buffered rendering. The interaction with frame skipping seems to need
// some work.
if (gpu->FramebufferDirty()) {
gpuStats.numFlips++;
// Setting CORE_NEXTFRAME causes a swap.
// Check first though, might've just quit / been paused.
if (gpu->FramebufferReallyDirty()) {
if (coreState == CORE_RUNNING) {
coreState = CORE_NEXTFRAME;
gpu->CopyDisplayToOutput();
}
}
bool wasSkipped = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0;
gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;
gpuStats.numFlips++;
bool throttle, skipFrame;
DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * (1.0f / 60.0f));
// Max 4 skipped frames in a row - 15 fps is really the bare minimum for playability.
// We check for 3 here so it's 3 skipped frames, 1 non skipped, 3 skipped, etc.
int maxFrameskip = throttle ? g_Config.iFrameSkip : 8;
if (numSkippedFrames >= maxFrameskip) {
skipFrame = false;
}
if (skipFrame) {
gstate_c.skipDrawReason |= SKIPDRAW_SKIPFRAME;
numSkippedFrames++;
} else {
// bool wasSkipped = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0;
gstate_c.skipDrawReason &= ~SKIPDRAW_SKIPFRAME;
numSkippedFrames = 0;
}
// Setting CORE_NEXTFRAME causes a swap.
// Check first though, might've just quit / been paused.
if (!wasSkipped) {
if (coreState == CORE_RUNNING) {
coreState = CORE_NEXTFRAME;
gpu->CopyDisplayToOutput();
}
}
// Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame).
// Right after, we regain control for a little bit in hleAfterFlip. I think that's a great
// place to do housekeeping.
CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0);
numVBlanksSinceFlip = 0;
}
}
void hleAfterFlip(u64 userdata, int cyclesLate)

View File

@ -132,7 +132,7 @@ public:
//if(dl->status < 0 || dl->status > PSP_GE_LIST_PAUSED)
// ERROR_LOG(HLE, "Weird DL status after signal suspend %x", dl->status);
if (newState != PSP_GE_DL_STATE_RUNNING)
WARN_LOG_REPORT(HLE, "GE Interrupt: newState might be %d", newState);
INFO_LOG_REPORT(HLE, "GE Interrupt: newState might be %d", newState);
dl->state = PSP_GE_DL_STATE_RUNNING;
}

View File

@ -15,6 +15,7 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "base/logging.h"
#include "gfx_es2/gl_state.h"
#include "Core/MemMap.h"
@ -313,8 +314,31 @@ bool GLES_GPU::FramebufferDirty() {
}
VirtualFramebuffer *vfb = framebufferManager_.GetDisplayFBO();
if (vfb)
return vfb->dirtyAfterDisplay;
if (vfb) {
bool dirty = vfb->dirtyAfterDisplay;
vfb->dirtyAfterDisplay = false;
return dirty;
}
return true;
}
bool GLES_GPU::FramebufferReallyDirty() {
// FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause.
if (g_Config.bSeparateCPUThread) {
// FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause.
ScheduleEvent(GPU_EVENT_PROCESS_QUEUE);
// Allow it to process fully before deciding if it's dirty.
SyncThread();
}
VirtualFramebuffer *vfb = framebufferManager_.GetDisplayFBO();
if (vfb) {
bool dirty = vfb->reallyDirtyAfterDisplay;
vfb->reallyDirtyAfterDisplay = false;
return dirty;
} else {
ILOG("reallydirty: No display FBO");
}
return true;
}
@ -354,7 +378,11 @@ void GLES_GPU::FastRunLoop(DisplayList &list) {
u32 cmd = op >> 24;
u32 diff = op ^ gstate.cmdmem[cmd];
CheckFlushOp(cmd, diff);
// Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check.
u8 flushCmd = flushBeforeCommand_[cmd];
if (flushCmd == 1 || (diff && flushCmd == 2)) {
transformDraw_.Flush();
}
gstate.cmdmem[cmd] = op;
ExecuteOp(op, diff);

View File

@ -57,6 +57,7 @@ public:
return textureCache_.DecodeTexture(dest, state);
}
virtual bool FramebufferDirty();
virtual bool FramebufferReallyDirty();
virtual void GetReportingInfo(std::string &primaryInfo, std::string &fullInfo) {
primaryInfo = reportingPrimaryInfo_;

View File

@ -451,6 +451,9 @@ void FramebufferManager::DestroyFramebuf(VirtualFramebuffer *v) {
void FramebufferManager::SetRenderFrameBuffer() {
if (!gstate_c.framebufChanged && currentRenderVfb_) {
currentRenderVfb_->last_frame_used = gpuStats.numFlips;
currentRenderVfb_->dirtyAfterDisplay = true;
if (!gstate_c.skipDrawReason)
currentRenderVfb_->reallyDirtyAfterDisplay = true;
return;
}
gstate_c.framebufChanged = false;
@ -515,6 +518,8 @@ void FramebufferManager::SetRenderFrameBuffer() {
vfb->format = fmt;
vfb->usageFlags = FB_USAGE_RENDERTARGET;
vfb->dirtyAfterDisplay = true;
if ((gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0)
vfb->reallyDirtyAfterDisplay = true;
vfb->memoryUpdated = false;
if (g_Config.bTrueColor) {
@ -584,6 +589,8 @@ void FramebufferManager::SetRenderFrameBuffer() {
vfb->last_frame_used = gpuStats.numFlips;
frameLastFramebufUsed = gpuStats.numFlips;
vfb->dirtyAfterDisplay = true;
if ((gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0)
vfb->reallyDirtyAfterDisplay = true;
vfb->memoryUpdated = false;
if (useBufferedRendering_) {
@ -631,6 +638,9 @@ void FramebufferManager::SetRenderFrameBuffer() {
} else {
vfb->last_frame_used = gpuStats.numFlips;
frameLastFramebufUsed = gpuStats.numFlips;
vfb->dirtyAfterDisplay = true;
if ((gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) == 0)
vfb->reallyDirtyAfterDisplay = true;
}
// ugly...
@ -663,6 +673,7 @@ void FramebufferManager::CopyDisplayToOutput() {
vfb->usageFlags |= FB_USAGE_DISPLAYED_FRAMEBUFFER;
vfb->dirtyAfterDisplay = false;
vfb->reallyDirtyAfterDisplay = false;
if (prevDisplayFramebuf_ != displayFramebuf_) {
prevPrevDisplayFramebuf_ = prevDisplayFramebuf_;
@ -672,6 +683,10 @@ void FramebufferManager::CopyDisplayToOutput() {
}
displayFramebuf_ = vfb;
if (resized_) {
ClearBuffer();
}
if (vfb->fbo) {
glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
DEBUG_LOG(HLE, "Displaying FBO %08x", vfb->fb_address);
@ -679,16 +694,12 @@ void FramebufferManager::CopyDisplayToOutput() {
fbo_bind_color_as_texture(vfb->fbo, 0);
// These are in the output display coordinates
// These are in the output display coordinates
float x, y, w, h;
CenterRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight);
DrawActiveTexture(x, y, w, h, true, 480.0f / (float)vfb->width, 272.0f / (float)vfb->height);
glBindTexture(GL_TEXTURE_2D, 0);
}
if (resized_) {
ClearBuffer();
}
}
void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync) {

View File

@ -87,6 +87,7 @@ struct VirtualFramebuffer {
FBO *fbo;
bool dirtyAfterDisplay;
bool reallyDirtyAfterDisplay; // takes frame skipping into account
};
void CenterRect(float *x, float *y, float *w, float *h,

View File

@ -39,6 +39,10 @@ public:
SyncThread();
return true;
}
virtual bool FramebufferReallyDirty() {
SyncThread();
return true;
}
virtual u32 Continue();
virtual u32 Break(int mode);
virtual void ReapplyGfxState();

View File

@ -230,6 +230,7 @@ public:
// Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*.
virtual void Resized() = 0;
virtual bool FramebufferDirty() = 0;
virtual bool FramebufferReallyDirty() = 0;
// Debugging
virtual void DumpNextFrame() = 0;