diff --git a/internal/atlas/image.go b/internal/atlas/image.go index c062ba98c..ddf77298c 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -788,10 +788,10 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path stri return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(0, 0, i.width, i.height)) } -func EndFrame(graphicsDriver graphicsdriver.Graphics) error { +func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) error { backendsM.Lock() - if err := restorable.EndFrame(graphicsDriver); err != nil { + if err := restorable.EndFrame(graphicsDriver, swapBuffersForGL); err != nil { return err } diff --git a/internal/buffered/image.go b/internal/buffered/image.go index afba1b6d8..321d53ec5 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -42,8 +42,8 @@ func BeginFrame(graphicsDriver graphicsdriver.Graphics) error { return nil } -func EndFrame(graphicsDriver graphicsdriver.Graphics) error { - return atlas.EndFrame(graphicsDriver) +func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) error { + return atlas.EndFrame(graphicsDriver, swapBuffersForGL) } func NewImage(width, height int, imageType atlas.ImageType) *Image { diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index e86fc3ef1..f7e1abe01 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -164,9 +164,16 @@ func (q *commandQueue) Enqueue(command command) { } // Flush flushes the command queue. -func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bool) (err error) { +func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bool, swapBuffersForGL func()) (err error) { runOnRenderThread(func() { err = q.flush(graphicsDriver, endFrame) + if err != nil { + return + } + + if endFrame && swapBuffersForGL != nil { + swapBuffersForGL() + } }) if endFrame { q.uint32sBuffer.reset() @@ -253,9 +260,9 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo // FlushCommands flushes the command queue and present the screen if needed. // If endFrame is true, the current screen might be used to present. -func FlushCommands(graphicsDriver graphicsdriver.Graphics, endFrame bool) error { +func FlushCommands(graphicsDriver graphicsdriver.Graphics, endFrame bool, swapBuffersForGL func()) error { flushImageBuffers() - return theCommandQueue.Flush(graphicsDriver, endFrame) + return theCommandQueue.Flush(graphicsDriver, endFrame, swapBuffersForGL) } // drawTrianglesCommand represents a drawing command to draw an image on another image. diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 0e61ca75d..28d35d8ce 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -171,7 +171,7 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte, r result: buf, } theCommandQueue.Enqueue(c) - if err := theCommandQueue.Flush(graphicsDriver, false); err != nil { + if err := theCommandQueue.Flush(graphicsDriver, false, nil); err != nil { return err } return nil @@ -201,7 +201,7 @@ func (i *Image) IsInvalidated(graphicsDriver graphicsdriver.Graphics) (bool, err image: i, } theCommandQueue.Enqueue(c) - if err := theCommandQueue.Flush(graphicsDriver, false); err != nil { + if err := theCommandQueue.Flush(graphicsDriver, false, nil); err != nil { return false, err } return c.result, nil diff --git a/internal/restorable/export_test.go b/internal/restorable/export_test.go index 10071b879..1be76b611 100644 --- a/internal/restorable/export_test.go +++ b/internal/restorable/export_test.go @@ -21,7 +21,7 @@ import ( ) func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { - return resolveStaleImages(graphicsDriver, false) + return resolveStaleImages(graphicsDriver, false, nil) } func AppendRegionRemovingDuplicates(regions *[]image.Rectangle, region image.Rectangle) { diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 3258338c2..1aa46a3f0 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -56,7 +56,7 @@ var theImages = &images{ shaders: map[*Shader]struct{}{}, } -func EndFrame(graphicsDriver graphicsdriver.Graphics) error { +func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) error { if debug.IsDebug { debug.Logf("Internal image sizes:\n") imgs := make([]*graphicscommand.Image, 0, len(theImages.images)) @@ -65,13 +65,13 @@ func EndFrame(graphicsDriver graphicsdriver.Graphics) error { } graphicscommand.LogImagesInfo(imgs) } - return resolveStaleImages(graphicsDriver, true) + return resolveStaleImages(graphicsDriver, true, swapBuffersForGL) } // resolveStaleImages flushes the queued draw commands and resolves all stale images. // If endFrame is true, the current screen might be used to present when flushing the commands. -func resolveStaleImages(graphicsDriver graphicsdriver.Graphics, endFrame bool) error { - if err := graphicscommand.FlushCommands(graphicsDriver, endFrame); err != nil { +func resolveStaleImages(graphicsDriver graphicsdriver.Graphics, endFrame bool, swapBuffersForGL func()) error { + if err := graphicscommand.FlushCommands(graphicsDriver, endFrame, swapBuffersForGL); err != nil { return err } if !needsRestoring() { diff --git a/internal/ui/context.go b/internal/ui/context.go index e1cd16f2b..c4d5d1b17 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -68,12 +68,12 @@ func newContext(game Game) *context { } } -func (c *context) updateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *userInterfaceImpl) error { +func (c *context) updateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *userInterfaceImpl, swapBuffersForGL func()) error { // 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) + return c.updateFrameImpl(graphicsDriver, clock.UpdateFrame(), outsideWidth, outsideHeight, deviceScaleFactor, ui, false, swapBuffersForGL) } -func (c *context) forceUpdateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *userInterfaceImpl) error { +func (c *context) forceUpdateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *userInterfaceImpl, swapBuffersForGL func()) error { n := 1 if graphicsDriver.IsDirectX() { // On DirectX, both framebuffers in the swap chain should be updated. @@ -81,14 +81,14 @@ func (c *context) forceUpdateFrame(graphicsDriver graphicsdriver.Graphics, outsi n = 2 } for i := 0; i < n; i++ { - if err := c.updateFrameImpl(graphicsDriver, 1, outsideWidth, outsideHeight, deviceScaleFactor, ui, true); err != nil { + if err := c.updateFrameImpl(graphicsDriver, 1, outsideWidth, outsideHeight, deviceScaleFactor, ui, true, swapBuffersForGL); err != nil { return err } } return nil } -func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, updateCount int, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *userInterfaceImpl, forceDraw bool) (err error) { +func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, updateCount int, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *userInterfaceImpl, forceDraw bool, swapBuffersForGL func()) (err error) { if err := theGlobalState.error(); err != nil { return err } @@ -108,7 +108,7 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update return err } defer func() { - if err1 := buffered.EndFrame(graphicsDriver); err == nil && err1 != nil { + if err1 := buffered.EndFrame(graphicsDriver, swapBuffersForGL); err == nil && err1 != nil { err = err1 } }() diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 14b162e71..ae7521d7b 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -1087,18 +1087,16 @@ func (u *userInterfaceImpl) updateGame() error { }) }) - if err := u.context.updateFrame(u.graphicsDriver, outsideWidth, outsideHeight, deviceScaleFactor, u); err != nil { - return err - } - - u.renderThread.Call(func() { + if err := u.context.updateFrame(u.graphicsDriver, outsideWidth, outsideHeight, deviceScaleFactor, u, func() { // Call updateVsync even though fpsMode is not updated. // When toggling to fullscreen, vsync state might be reset unexpectedly (#1787). u.updateVsyncOnRenderThread() // This works only for OpenGL. u.swapBuffersOnRenderThread() - }) + }); err != nil { + return err + } u.bufferOnceSwappedOnce.Do(func() { u.mainThread.Call(func() { diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index 689f27d97..5a34124c3 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -343,11 +343,11 @@ func (u *userInterfaceImpl) updateImpl(force bool) error { w, h := u.outsideSize() if force { - if err := u.context.forceUpdateFrame(u.graphicsDriver, w, h, u.DeviceScaleFactor(), u); err != nil { + if err := u.context.forceUpdateFrame(u.graphicsDriver, w, h, u.DeviceScaleFactor(), u, nil); err != nil { return err } } else { - if err := u.context.updateFrame(u.graphicsDriver, w, h, u.DeviceScaleFactor(), u); err != nil { + if err := u.context.updateFrame(u.graphicsDriver, w, h, u.DeviceScaleFactor(), u, nil); err != nil { return err } } diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index 60ef2724e..7ca8f1ab2 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -331,7 +331,7 @@ func (u *userInterfaceImpl) update() error { }() w, h := u.outsideSize() - if err := u.context.updateFrame(u.graphicsDriver, w, h, deviceScale(), u); err != nil { + if err := u.context.updateFrame(u.graphicsDriver, w, h, deviceScale(), u, nil); err != nil { return err } return nil diff --git a/internal/ui/ui_nintendosdk.go b/internal/ui/ui_nintendosdk.go index 2f450e80c..a5f7e5722 100644 --- a/internal/ui/ui_nintendosdk.go +++ b/internal/ui/ui_nintendosdk.go @@ -121,13 +121,11 @@ func (u *userInterfaceImpl) Run(game Game, options *RunOptions) error { u.updateInputState() }) - if err := u.context.updateFrame(u.graphicsDriver, float64(C.kScreenWidth), float64(C.kScreenHeight), deviceScaleFactor, u); err != nil { + if err := u.context.updateFrame(u.graphicsDriver, float64(C.kScreenWidth), float64(C.kScreenHeight), deviceScaleFactor, u, func() { + u.egl.swapBuffers() + }); err != nil { return err } - - u.renderThread.Call(func() { - u.egl.swapBuffers() - }) } })