internal/ui: refactoring: integrate (*UserInterface).run

This commit is contained in:
Hajime Hoshi 2023-10-29 18:34:18 +09:00
parent ea842495cf
commit 5fe8c29b4c
8 changed files with 49 additions and 68 deletions

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !android && !ios && !js && !nintendosdk && !playstation5 && !ebitenginesinglethread && !ebitensinglethread
//go:build !android && !ios && !js && !ebitenginesinglethread && !ebitensinglethread
package ui
@ -25,7 +25,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/thread"
)
func (u *UserInterface) Run(game Game, options *RunOptions) error {
func (u *UserInterface) run(game Game, options *RunOptions) error {
u.mainThread = thread.NewOSThread()
u.renderThread = thread.NewOSThread()
graphicscommand.SetRenderThread(u.renderThread)

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !android && !ios && !js && !nintendosdk && !playstation5 && (ebitenginesinglethread || ebitensinglethread)
//go:build js || (!android && !ios && (ebitenginesinglethread || ebitensinglethread))
package ui
@ -21,7 +21,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/thread"
)
func (u *UserInterface) Run(game Game, options *RunOptions) error {
func (u *UserInterface) run(game Game, options *RunOptions) error {
// Initialize the main thread first so the thread is available at u.run (#809).
u.mainThread = thread.NewNoopThread()
u.renderThread = thread.NewNoopThread()

View File

@ -22,6 +22,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/mipmap"
"github.com/hajimehoshi/ebiten/v2/internal/thread"
)
// RegularTermination represents a regular termination.
@ -79,6 +80,9 @@ type UserInterface struct {
whiteImage *Image
mainThread thread.Thread
renderThread thread.Thread
userInterfaceImpl
}

View File

@ -107,9 +107,7 @@ type userInterfaceImpl struct {
darwinInitOnce sync.Once
bufferOnceSwappedOnce sync.Once
mainThread thread.Thread
renderThread thread.Thread
m sync.RWMutex
m sync.RWMutex
}
const (
@ -123,6 +121,10 @@ func init() {
runtime.LockOSThread()
}
func (u *UserInterface) Run(game Game, options *RunOptions) error {
return u.run(game, options)
}
func (u *UserInterface) init() error {
u.userInterfaceImpl = userInterfaceImpl{
runnableOnUnfocused: true,

View File

@ -390,9 +390,7 @@ func (u *UserInterface) needsUpdate() bool {
return false
}
func (u *UserInterface) loop(game Game) <-chan error {
u.context = newContext(game)
func (u *UserInterface) loopGame() error {
errCh := make(chan error, 1)
reqStopAudioCh := make(chan struct{})
resStopAudioCh := make(chan struct{})
@ -476,7 +474,7 @@ func (u *UserInterface) loop(game Game) <-chan error {
}
}()
return errCh
return <-errCh
}
func (u *UserInterface) init() error {
@ -743,9 +741,10 @@ func (u *UserInterface) forceUpdateOnMinimumFPSMode() {
}
func (u *UserInterface) Run(game Game, options *RunOptions) error {
u.setRunning(true)
defer u.setRunning(false)
return u.run(game, options)
}
func (u *UserInterface) initOnMainThread(options *RunOptions) error {
if !options.InitUnfocused && window.Truthy() {
// Do not focus the canvas when the current document is in an iframe.
// Otherwise, the parent page tries to focus the iframe on every loading, which is annoying (#1373).
@ -770,7 +769,7 @@ func (u *UserInterface) Run(game Game, options *RunOptions) error {
bodyStyle.Set("backgroundColor", "#000")
}
return <-u.loop(game)
return nil
}
func (u *UserInterface) updateScreenSize() {

View File

@ -241,7 +241,7 @@ func (u *UserInterface) SetForeground(foreground bool) error {
func (u *UserInterface) Run(game Game, options *RunOptions) error {
u.setGBuildSizeCh = make(chan struct{})
go func() {
if err := u.run(game, true, options); err != nil {
if err := u.runMobile(game, true, options); err != nil {
// As mobile apps never ends, Loop can't return. Just panic here.
panic(err)
}
@ -252,13 +252,13 @@ func (u *UserInterface) Run(game Game, options *RunOptions) error {
func (u *UserInterface) RunWithoutMainLoop(game Game, options *RunOptions) {
go func() {
if err := u.run(game, false, options); err != nil {
if err := u.runMobile(game, false, options); err != nil {
u.errCh <- err
}
}()
}
func (u *UserInterface) run(game Game, mainloop bool, options *RunOptions) (err error) {
func (u *UserInterface) runMobile(game Game, mainloop bool, options *RunOptions) (err error) {
// Convert the panic to a regular error so that Java/Objective-C layer can treat this easily e.g., for
// Crashlytics. A panic is treated as SIGABRT, and there is no way to handle this on Java/Objective-C layer
// unfortunately.

View File

@ -21,15 +21,11 @@ package ui
import "C"
import (
stdcontext "context"
"errors"
"image"
"runtime"
"sync"
"golang.org/x/sync/errgroup"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
"github.com/hajimehoshi/ebiten/v2/internal/thread"
@ -84,15 +80,10 @@ func (u *UserInterface) init() error {
}
func (u *UserInterface) Run(game Game, options *RunOptions) error {
u.mainThread = thread.NewOSThread()
u.renderThread = thread.NewOSThread()
graphicscommand.SetRenderThread(u.renderThread)
u.setRunning(true)
defer u.setRunning(false)
u.context = newContext(game)
return u.run(game, options)
}
func (u *UserInterface) initOnMainThread(options *RunOptions) error {
g, lib, err := newGraphicsDriver(&graphicsDriverCreatorImpl{}, options.GraphicsLibrary)
if err != nil {
return err
@ -107,45 +98,25 @@ func (u *UserInterface) Run(game Game, options *RunOptions) error {
initializeProfiler()
ctx, cancel := stdcontext.WithCancel(stdcontext.Background())
defer cancel()
var wg errgroup.Group
// Run the render thread.
wg.Go(func() error {
defer cancel()
_ = u.renderThread.Loop(ctx)
return nil
})
// Run the game thread.
wg.Go(func() error {
defer cancel()
u.renderThread.Call(func() {
u.egl.makeContextCurrent()
})
for {
recordProfilerHeartbeat()
if err := u.context.updateFrame(u.graphicsDriver, float64(C.kScreenWidth), float64(C.kScreenHeight), deviceScaleFactor, u, func() {
u.egl.swapBuffers()
}); err != nil {
return err
}
}
})
// Run the main thread.
_ = u.mainThread.Loop(ctx)
if err := wg.Wait(); err != nil {
return err
}
return nil
}
func (u *UserInterface) loopGame() error {
u.renderThread.Call(func() {
u.egl.makeContextCurrent()
})
for {
recordProfilerHeartbeat()
if err := u.context.updateFrame(u.graphicsDriver, float64(C.kScreenWidth), float64(C.kScreenHeight), deviceScaleFactor, u, func() {
u.egl.swapBuffers()
}); err != nil {
return err
}
}
}
func (*UserInterface) DeviceScaleFactor() float64 {
return deviceScaleFactor
}

View File

@ -64,10 +64,15 @@ func (u *UserInterface) init() error {
}
func (u *UserInterface) Run(game Game, options *RunOptions) error {
u.setRunning(true)
defer u.setRunning(false)
return u.run(game, options)
}
// TODO: Implement this.
func (u *UserInterface) initOnMainThread(options *RunOptions) error {
return nil
}
func (u *UserInterface) loopGame() error {
// TODO: Implement this
return nil
}