internal/ui: skip SwapBuffers call if needed

Updates #2890
Updates #2952
This commit is contained in:
Hajime Hoshi 2024-09-16 23:15:09 +09:00
parent 9a8d6e7b41
commit b9dce05ca1

View File

@ -54,7 +54,7 @@ type context struct {
offscreenHeight float64 offscreenHeight float64
isOffscreenModified bool isOffscreenModified bool
lastDrawTime time.Time lastSwapBufferTime time.Time
skipCount int skipCount int
@ -101,18 +101,6 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
return err return err
} }
defer func() {
if err1 := atlas.EndFrame(); err1 != nil && err == nil {
err = err1
return
}
if err1 := atlas.SwapBuffers(graphicsDriver); err1 != nil && err == nil {
err = err1
return
}
}()
// Flush deferred functions, like reading pixels from GPU. // Flush deferred functions, like reading pixels from GPU.
if err := c.processFuncsInFrame(ui); err != nil { if err := c.processFuncsInFrame(ui); err != nil {
return err return err
@ -164,10 +152,30 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
} }
// Draw the game. // Draw the game.
if err := c.drawGame(graphicsDriver, ui, forceDraw); err != nil { needSwapBuffers, err := c.drawGame(graphicsDriver, ui, forceDraw)
if err != nil {
return err return err
} }
if err := atlas.EndFrame(); err != nil {
return err
}
if needSwapBuffers {
if err := atlas.SwapBuffers(graphicsDriver); err != nil {
return err
}
} else {
now := time.Now()
defer func() {
c.lastSwapBufferTime = now
}()
if delta := time.Second/60 - now.Sub(c.lastSwapBufferTime); delta > 0 {
// When swapping buffers is skipped and Draw is called too early, sleep for a while to suppress CPU usages (#2890).
time.Sleep(delta)
}
}
return nil return nil
} }
@ -179,7 +187,7 @@ func (c *context) newOffscreenImage(w, h int) *Image {
return img return img
} }
func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, ui *UserInterface, forceDraw bool) error { func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, ui *UserInterface, forceDraw bool) (needSwapBuffers bool, err error) {
if (c.offscreen.imageType == atlas.ImageTypeVolatile) != ui.IsScreenClearedEveryFrame() { if (c.offscreen.imageType == atlas.ImageTypeVolatile) != ui.IsScreenClearedEveryFrame() {
w, h := c.offscreen.width, c.offscreen.height w, h := c.offscreen.width, c.offscreen.height
c.offscreen.Deallocate() c.offscreen.Deallocate()
@ -197,7 +205,7 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, ui *UserInter
} }
if err := c.game.DrawOffscreen(); err != nil { if err := c.game.DrawOffscreen(); err != nil {
return err return false, err
} }
const maxSkipCount = 4 const maxSkipCount = 4
@ -210,12 +218,10 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, ui *UserInter
c.skipCount = 0 c.skipCount = 0
} }
now := time.Now() if c.skipCount >= maxSkipCount {
defer func() { return false, nil
c.lastDrawTime = now }
}()
if c.skipCount < maxSkipCount {
if graphicsDriver.NeedsClearingScreen() { if graphicsDriver.NeedsClearingScreen() {
// This clear is needed for fullscreen mode or some mobile platforms (#622). // This clear is needed for fullscreen mode or some mobile platforms (#622).
c.screen.clear() c.screen.clear()
@ -226,12 +232,7 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, ui *UserInter
// The final screen is never used as the rendering source. // The final screen is never used as the rendering source.
// Flush its buffer here just in case. // Flush its buffer here just in case.
c.screen.flushBufferIfNeeded() c.screen.flushBufferIfNeeded()
} else if delta := time.Second/60 - now.Sub(c.lastDrawTime); delta > 0 { return true, nil
// When swapping buffers is skipped and Draw is called too early, sleep for a while to suppress CPU usages (#2890).
time.Sleep(delta)
}
return nil
} }
func (c *context) layoutGame(outsideWidth, outsideHeight float64, deviceScaleFactor float64) (int, int) { func (c *context) layoutGame(outsideWidth, outsideHeight float64, deviceScaleFactor float64) (int, int) {