mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
internal/ui: add sleep for an environment where vsync doesn't work
Updates #2952
This commit is contained in:
parent
50f0a8343c
commit
24e5751ece
@ -70,7 +70,14 @@ func newContext(game Game) *context {
|
|||||||
|
|
||||||
func (c *context) updateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface) error {
|
func (c *context) updateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface) error {
|
||||||
// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.
|
// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.
|
||||||
return c.updateFrameImpl(graphicsDriver, clock.UpdateFrame(), outsideWidth, outsideHeight, deviceScaleFactor, ui, false)
|
needsSwapBuffers, err := c.updateFrameImpl(graphicsDriver, clock.UpdateFrame(), outsideWidth, outsideHeight, deviceScaleFactor, ui, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.swapBuffersOrWait(needsSwapBuffers, graphicsDriver, ui.FPSMode() == FPSModeVsyncOn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) forceUpdateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface) error {
|
func (c *context) forceUpdateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface) error {
|
||||||
@ -81,63 +88,50 @@ func (c *context) forceUpdateFrame(graphicsDriver graphicsdriver.Graphics, outsi
|
|||||||
n = 2
|
n = 2
|
||||||
}
|
}
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
if err := c.updateFrameImpl(graphicsDriver, 1, outsideWidth, outsideHeight, deviceScaleFactor, ui, true); err != nil {
|
needsSwapBuffers, err := c.updateFrameImpl(graphicsDriver, 1, outsideWidth, outsideHeight, deviceScaleFactor, ui, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.swapBuffersOrWait(needsSwapBuffers, graphicsDriver, ui.FPSMode() == FPSModeVsyncOn); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, updateCount int, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface, forceDraw bool) (err error) {
|
func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, updateCount int, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface, forceDraw bool) (needsSwapBuffers bool, err error) {
|
||||||
// The given outside size can be 0 e.g. just after restoring from the fullscreen mode on Windows (#1589)
|
// The given outside size can be 0 e.g. just after restoring from the fullscreen mode on Windows (#1589)
|
||||||
// Just ignore such cases. Otherwise, creating a zero-sized framebuffer causes a panic.
|
// Just ignore such cases. Otherwise, creating a zero-sized framebuffer causes a panic.
|
||||||
if outsideWidth == 0 || outsideHeight == 0 {
|
if outsideWidth == 0 || outsideHeight == 0 {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.FrameLogf("----\n")
|
debug.FrameLogf("----\n")
|
||||||
|
|
||||||
var needSwapBuffers bool
|
|
||||||
|
|
||||||
if err := atlas.BeginFrame(graphicsDriver); err != nil {
|
if err := atlas.BeginFrame(graphicsDriver); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err1 := atlas.EndFrame(); err1 != nil && err == nil {
|
if err1 := atlas.EndFrame(); err1 != nil && err == nil {
|
||||||
|
needsSwapBuffers = false
|
||||||
err = err1
|
err = err1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if needSwapBuffers {
|
|
||||||
if err1 := atlas.SwapBuffers(graphicsDriver); err1 != nil && err == nil {
|
|
||||||
err = err1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 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 false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForceUpdate can be invoked even if the context is not initialized yet (#1591).
|
// ForceUpdate can be invoked even if the context is not initialized yet (#1591).
|
||||||
if w, h := c.layoutGame(outsideWidth, outsideHeight, deviceScaleFactor); w == 0 || h == 0 {
|
if w, h := c.layoutGame(outsideWidth, outsideHeight, deviceScaleFactor); w == 0 || h == 0 {
|
||||||
return nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the input state after the layout is updated as a cursor position is affected by the layout.
|
// Update the input state after the layout is updated as a cursor position is affected by the layout.
|
||||||
if err := ui.updateInputState(); err != nil {
|
if err := ui.updateInputState(); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that Update is called once before Draw so that Update can be used for initialization.
|
// Ensure that Update is called once before Draw so that Update can be used for initialization.
|
||||||
@ -155,15 +149,15 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err := hook.RunBeforeUpdateHooks(); err != nil {
|
if err := hook.RunBeforeUpdateHooks(); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
if err := c.game.Update(); err != nil {
|
if err := c.game.Update(); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch the error that happened at (*Image).At.
|
// Catch the error that happened at (*Image).At.
|
||||||
if err := ui.error(); err != nil {
|
if err := ui.error(); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.tick.Add(1)
|
ui.tick.Add(1)
|
||||||
@ -172,13 +166,39 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
|
|||||||
// Update window icons during a frame, since an icon might be *ebiten.Image and
|
// Update window icons during a frame, since an icon might be *ebiten.Image and
|
||||||
// getting pixels from it needs to be in a frame (#1468).
|
// getting pixels from it needs to be in a frame (#1468).
|
||||||
if err := ui.updateIconIfNeeded(); err != nil {
|
if err := ui.updateIconIfNeeded(); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the game.
|
// Draw the game.
|
||||||
needSwapBuffers, err = c.drawGame(graphicsDriver, ui, forceDraw)
|
return c.drawGame(graphicsDriver, ui, forceDraw)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
|
func (c *context) swapBuffersOrWait(needsSwapBuffers bool, graphicsDriver graphicsdriver.Graphics, vsyncEnabled bool) error {
|
||||||
|
now := time.Now()
|
||||||
|
defer func() {
|
||||||
|
c.lastSwapBufferTime = now
|
||||||
|
}()
|
||||||
|
|
||||||
|
if needsSwapBuffers {
|
||||||
|
if err := atlas.SwapBuffers(graphicsDriver); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var waitTime time.Duration
|
||||||
|
if !needsSwapBuffers {
|
||||||
|
// When swapping buffers is skipped and Draw is called too early, sleep for a while to suppress CPU usages (#2890).
|
||||||
|
waitTime = time.Second / 60
|
||||||
|
} else if vsyncEnabled {
|
||||||
|
// In some environments, e.g. Linux on Parallels, SwapBuffers doesn't wait for the vsync (#2952).
|
||||||
|
// In the case when the display has high refresh rates like 240 [Hz], the wait time should be small.
|
||||||
|
waitTime = time.Millisecond
|
||||||
|
}
|
||||||
|
if waitTime > 0 {
|
||||||
|
if delta := waitTime - now.Sub(c.lastSwapBufferTime); delta > 0 {
|
||||||
|
println(waitTime.String(), now.Sub(c.lastSwapBufferTime).String())
|
||||||
|
time.Sleep(delta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user