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

View File

@ -198,11 +198,7 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics, forceDraw boo
c.skipCount = 0 c.skipCount = 0
} }
skippable := c.skipCount >= maxSkipCount if 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 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()