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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//go:build !android && !ios && !js && !nintendosdk && !playstation5 && !ebitenginesinglethread && !ebitensinglethread //go:build !android && !ios && !js && !ebitenginesinglethread && !ebitensinglethread
package ui package ui
@ -25,7 +25,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/thread" "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.mainThread = thread.NewOSThread()
u.renderThread = thread.NewOSThread() u.renderThread = thread.NewOSThread()
graphicscommand.SetRenderThread(u.renderThread) graphicscommand.SetRenderThread(u.renderThread)

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//go:build !android && !ios && !js && !nintendosdk && !playstation5 && (ebitenginesinglethread || ebitensinglethread) //go:build js || (!android && !ios && (ebitenginesinglethread || ebitensinglethread))
package ui package ui
@ -21,7 +21,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/thread" "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). // Initialize the main thread first so the thread is available at u.run (#809).
u.mainThread = thread.NewNoopThread() u.mainThread = thread.NewNoopThread()
u.renderThread = 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/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/mipmap" "github.com/hajimehoshi/ebiten/v2/internal/mipmap"
"github.com/hajimehoshi/ebiten/v2/internal/thread"
) )
// RegularTermination represents a regular termination. // RegularTermination represents a regular termination.
@ -79,6 +80,9 @@ type UserInterface struct {
whiteImage *Image whiteImage *Image
mainThread thread.Thread
renderThread thread.Thread
userInterfaceImpl userInterfaceImpl
} }

View File

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

View File

@ -390,9 +390,7 @@ func (u *UserInterface) needsUpdate() bool {
return false return false
} }
func (u *UserInterface) loop(game Game) <-chan error { func (u *UserInterface) loopGame() error {
u.context = newContext(game)
errCh := make(chan error, 1) errCh := make(chan error, 1)
reqStopAudioCh := make(chan struct{}) reqStopAudioCh := make(chan struct{})
resStopAudioCh := 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 { func (u *UserInterface) init() error {
@ -743,9 +741,10 @@ func (u *UserInterface) forceUpdateOnMinimumFPSMode() {
} }
func (u *UserInterface) Run(game Game, options *RunOptions) error { func (u *UserInterface) Run(game Game, options *RunOptions) error {
u.setRunning(true) return u.run(game, options)
defer u.setRunning(false) }
func (u *UserInterface) initOnMainThread(options *RunOptions) error {
if !options.InitUnfocused && window.Truthy() { if !options.InitUnfocused && window.Truthy() {
// Do not focus the canvas when the current document is in an iframe. // 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). // 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") bodyStyle.Set("backgroundColor", "#000")
} }
return <-u.loop(game) return nil
} }
func (u *UserInterface) updateScreenSize() { 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 { func (u *UserInterface) Run(game Game, options *RunOptions) error {
u.setGBuildSizeCh = make(chan struct{}) u.setGBuildSizeCh = make(chan struct{})
go func() { 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. // As mobile apps never ends, Loop can't return. Just panic here.
panic(err) panic(err)
} }
@ -252,13 +252,13 @@ func (u *UserInterface) Run(game Game, options *RunOptions) error {
func (u *UserInterface) RunWithoutMainLoop(game Game, options *RunOptions) { func (u *UserInterface) RunWithoutMainLoop(game Game, options *RunOptions) {
go func() { go func() {
if err := u.run(game, false, options); err != nil { if err := u.runMobile(game, false, options); err != nil {
u.errCh <- err 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 // 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 // Crashlytics. A panic is treated as SIGABRT, and there is no way to handle this on Java/Objective-C layer
// unfortunately. // unfortunately.

View File

@ -21,15 +21,11 @@ package ui
import "C" import "C"
import ( import (
stdcontext "context"
"errors" "errors"
"image" "image"
"runtime" "runtime"
"sync" "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"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
"github.com/hajimehoshi/ebiten/v2/internal/thread" "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 { func (u *UserInterface) Run(game Game, options *RunOptions) error {
u.mainThread = thread.NewOSThread() return u.run(game, options)
u.renderThread = thread.NewOSThread() }
graphicscommand.SetRenderThread(u.renderThread)
u.setRunning(true)
defer u.setRunning(false)
u.context = newContext(game)
func (u *UserInterface) initOnMainThread(options *RunOptions) error {
g, lib, err := newGraphicsDriver(&graphicsDriverCreatorImpl{}, options.GraphicsLibrary) g, lib, err := newGraphicsDriver(&graphicsDriverCreatorImpl{}, options.GraphicsLibrary)
if err != nil { if err != nil {
return err return err
@ -107,45 +98,25 @@ func (u *UserInterface) Run(game Game, options *RunOptions) error {
initializeProfiler() 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 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 { func (*UserInterface) DeviceScaleFactor() float64 {
return deviceScaleFactor return deviceScaleFactor
} }

View File

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