internal/ui: re-enable skipping to render the final screen when possible

With Metal, nextDrawable could return immediately when a command buffer
is empty. To avoid high CPU usage, this change adds a slight sleep in
this case.

With DirectX, Present waits for a while even though nothing is updated,
then that's fine.

Updates #2341
Updates #2342
Updates #2520
This commit is contained in:
Hajime Hoshi 2023-01-03 22:58:14 +09:00
parent edb952c9e7
commit 7418576c16
2 changed files with 19 additions and 8 deletions

View File

@ -19,6 +19,7 @@ import (
"math"
"runtime"
"sort"
"time"
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
@ -58,6 +59,8 @@ type Graphics struct {
maxImageSize int
tmpTextures []mtl.Texture
lastFlush time.Time
pool cocoa.NSAutoreleasePool
}
@ -211,14 +214,26 @@ func (g *Graphics) flushIfNeeded(present bool) {
if g.cb == (mtl.CommandBuffer{}) && !present {
return
}
now := time.Now()
defer func() {
g.lastFlush = now
}()
g.flushRenderCommandEncoderIfNeeded()
if present {
// This check is necessary when skipping to render the screen (SetScreenClearedEveryFrame(false)).
if g.screenDrawable == (ca.MetalDrawable{}) {
// nextDrawable can return immediately when the command buffer is empty.
// TODO: Can we wait for a while to get the next drawable? (#2520)
if g.cb != (mtl.CommandBuffer{}) {
g.screenDrawable = g.view.nextDrawable()
} else {
if delta := time.Second/60 - now.Sub(g.lastFlush); delta > 0 {
// nextDrawable can return immediately when the command buffer is empty.
// To avoid busy, sleep instead (#2520).
time.Sleep(delta)
}
}
}
if g.screenDrawable != (ca.MetalDrawable{}) {
g.cb.PresentDrawable(g.screenDrawable)

View File

@ -198,11 +198,7 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, forceDraw boo
c.skipCount = 0
}
skippable := c.skipCount >= maxSkipCount
// TODO: Metal (and maybe DirectX) cannot vsync without swapping the buffer by rendering the screen framebuffer (#2520).
// Implement this skipping appropriately for Metal and DirectX.
if !skippable || !graphicsDriver.IsGL() {
if c.skipCount < maxSkipCount {
if graphicsDriver.NeedsClearingScreen() {
// This clear is needed for fullscreen mode or some mobile platforms (#622).
c.screen.clear()