diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 1796b75d2..e9e7832a5 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -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) - g.screenDrawable = g.view.nextDrawable() + 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) diff --git a/internal/ui/context.go b/internal/ui/context.go index 601c63e00..69e460e81 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -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()