From a30f075896b76b2f6fb1536c490aead650e4157f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 8 Jan 2024 03:50:14 +0900 Subject: [PATCH] cmd/ebitenmobile: use setPreserveEGLContextOnPause(true) setPreserveEGLContextOnPause(true) suppresses context losts, and basically we will no longer need our restoring logic. Updates #805 --- .../_files/EbitenSurfaceView.java | 24 ++++++++- internal/graphicsdriver/opengl/graphics.go | 6 +++ internal/restorable/images.go | 50 ++++++------------- mobile/ebitenmobileview/mobile.go | 5 -- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/cmd/ebitenmobile/_files/EbitenSurfaceView.java b/cmd/ebitenmobile/_files/EbitenSurfaceView.java index d23b7e87c..a4340a7f8 100644 --- a/cmd/ebitenmobile/_files/EbitenSurfaceView.java +++ b/cmd/ebitenmobile/_files/EbitenSurfaceView.java @@ -33,12 +33,17 @@ class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { private class EbitenRenderer implements GLSurfaceView.Renderer { private boolean errored_ = false; + private boolean onceSurfaceCreated_ = false; + private boolean contextLost_ = false; @Override public void onDrawFrame(GL10 gl) { if (errored_) { return; } + if (contextLost_) { + return; + } try { Ebitenmobileview.update(); } catch (final Exception e) { @@ -54,7 +59,17 @@ class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { - Ebitenmobileview.onContextLost(); + if (!onceSurfaceCreated_) { + onceSurfaceCreated_ = true; + return; + } + contextLost_ = true; + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + onContextLost(); + } + }); } @Override @@ -76,6 +91,7 @@ class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { setEGLContextClientVersion(3); setEGLConfigChooser(8, 8, 8, 8, 0, 0); setRenderer(new EbitenRenderer()); + setPreserveEGLContextOnPause(true); Ebitenmobileview.setRenderRequester(this); } @@ -83,6 +99,12 @@ class EbitenSurfaceView extends GLSurfaceView implements RenderRequester { ((EbitenView)getParent()).onErrorOnGameUpdate(e); } + private void onContextLost() { + Log.v("Go", "Kill the application due to a context lost"); + // TODO: Relaunch this application for better UX (#805). + Runtime.getRuntime().exit(0); + } + @Override public synchronized void setExplicitRenderingMode(boolean explicitRendering) { if (explicitRendering) { diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 14a9fa1e7..748428423 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -315,6 +315,12 @@ func (g *Graphics) NeedsRestoring() bool { if runtime.GOOS == "js" { return false } + // On Android, `setPreserveEGLContextOnPause(true)` is called and then context loss unlikely happens. + // If this happens, Ebitengine tries to relaunch the app (#805). + if runtime.GOOS == "android" { + return false + } + // TODO: Remove the entire logic of the restoring (#805). return g.context.ctx.IsES() } diff --git a/internal/restorable/images.go b/internal/restorable/images.go index f65cf0fdd..229452245 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -15,8 +15,6 @@ package restorable import ( - "runtime" - "github.com/hajimehoshi/ebiten/v2/internal/debug" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" @@ -44,10 +42,9 @@ func EnableRestoringForTesting() { // images is a set of Image objects. type images struct { - images map[*Image]struct{} - shaders map[*Shader]struct{} - lastTarget *Image - contextLost bool + images map[*Image]struct{} + shaders map[*Shader]struct{} + lastTarget *Image } // theImages represents the images for the current process. @@ -91,23 +88,19 @@ func RestoreIfNeeded(graphicsDriver graphicsdriver.Graphics) error { if !forceRestoring { var r bool - if canDetectContextLostExplicitly { - r = theImages.contextLost - } else { - // As isInvalidated() is expensive, call this only for one image. - // This assumes that if there is one image that is invalidated, all images are invalidated. - for img := range theImages.images { - // The screen image might not have a texture. Skip this. - if img.imageType == ImageTypeScreen { - continue - } - var err error - r, err = img.isInvalidated(graphicsDriver) - if err != nil { - return err - } - break + // As isInvalidated() is expensive, call this only for one image. + // This assumes that if there is one image that is invalidated, all images are invalidated. + for img := range theImages.images { + // The screen image might not have a texture. Skip this. + if img.imageType == ImageTypeScreen { + continue } + var err error + r, err = img.isInvalidated(graphicsDriver) + if err != nil { + return err + } + break } if !r { @@ -265,8 +258,6 @@ func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error { } } - i.contextLost = false - return nil } @@ -283,14 +274,3 @@ func InitializeGraphicsDriverState(graphicsDriver graphicsdriver.Graphics) error func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int { return graphicscommand.MaxImageSize(graphicsDriver) } - -// OnContextLost is called when the context lost is detected in an explicit way. -func OnContextLost() { - canDetectContextLostExplicitly = true - theImages.contextLost = true -} - -// canDetectContextLostExplicitly reports whether Ebitengine can detect a context lost in an explicit way. -// On Android, a context lost can be detected via GLSurfaceView.Renderer.onSurfaceCreated. -// On iOS w/ OpenGL ES, this can be detected only when gomobile-build is used. -var canDetectContextLostExplicitly = runtime.GOOS == "android" diff --git a/mobile/ebitenmobileview/mobile.go b/mobile/ebitenmobileview/mobile.go index 501a7b429..7f5193564 100644 --- a/mobile/ebitenmobileview/mobile.go +++ b/mobile/ebitenmobileview/mobile.go @@ -30,7 +30,6 @@ import ( "sync" "github.com/hajimehoshi/ebiten/v2" - "github.com/hajimehoshi/ebiten/v2/internal/restorable" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -113,10 +112,6 @@ func Resume() error { return ui.Get().SetForeground(true) } -func OnContextLost() { - restorable.OnContextLost() -} - func DeviceScale() float64 { return ui.Get().DeviceScaleFactor() }