diff --git a/internal/uidriver/mobile/ui.go b/internal/uidriver/mobile/ui.go index 69f05e4a7..4856688b8 100644 --- a/internal/uidriver/mobile/ui.go +++ b/internal/uidriver/mobile/ui.go @@ -68,6 +68,8 @@ func Get() *UserInterface { } // Update is called from mobile/ebitenmobileview. +// +// Update must be called on the rendering thread. func (u *UserInterface) Update() error { select { case err := <-u.errCh: @@ -358,6 +360,8 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, int) { } // SetOutsideSize is called from mobile/ebitenmobileview. +// +// SetOutsideSize is concurrent safe. func (u *UserInterface) SetOutsideSize(outsideWidth, outsideHeight float64) { // Called from ebitenmobileview. u.m.Lock() diff --git a/mobile/ebitenmobileview/mobile.go b/mobile/ebitenmobileview/mobile.go index a9bd03095..b1b6a95dc 100644 --- a/mobile/ebitenmobileview/mobile.go +++ b/mobile/ebitenmobileview/mobile.go @@ -27,7 +27,7 @@ import "C" import ( "runtime" - "sync" + "sync/atomic" "github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten/internal/restorable" @@ -37,33 +37,26 @@ import ( var theState state type state struct { - started bool - - // m is a mutex required for each function. - // For example, on Android, Update can be called from a different thread: - // https://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer - m sync.Mutex + running int32 } func (s *state) isRunning() bool { - return s.started + return atomic.LoadInt32(&s.running) != 0 +} + +func (s *state) run() { + atomic.StoreInt32(&s.running, 1) } func SetGame(game ebiten.Game) { - theState.m.Lock() - defer theState.m.Unlock() - - if theState.started { + if theState.isRunning() { panic("ebitenmobileview: SetGame cannot be called twice or more") } ebiten.RunGameWithoutMainLoop(game) - theState.started = true + theState.run() } func Layout(viewWidth, viewHeight float64) { - theState.m.Lock() - defer theState.m.Unlock() - mobile.Get().SetOutsideSize(viewWidth, viewHeight) } @@ -72,9 +65,7 @@ func Update() error { runtime.LockOSThread() defer runtime.UnlockOSThread() - theState.m.Lock() - defer theState.m.Unlock() - if !theState.started { + if !theState.isRunning() { // start is not called yet, but as update can be called from another thread, it is OK. Just ignore // this. return nil