internal/ui: bug fix: IsGL / SetUIView can be called before initialization is done

The functions in the package `mobile/ebitenmobileview` could be invoked
from EbitenViewController even before the graphics driver initialization
is done in theory.

This change fixes this issue by waiting the initialization by
channels. Also, this change adds error handlings at these functions.

Closes #2455
This commit is contained in:
Hajime Hoshi 2022-11-15 01:34:25 +09:00
parent f4e63602d3
commit 7d146fb70b
4 changed files with 100 additions and 21 deletions

View File

@ -59,7 +59,18 @@
started_ = true;
}
if (EbitenmobileviewIsGL()) {
NSError* err = nil;
BOOL isGL = NO;
EbitenmobileviewIsGL(&isGL, &err);
if (err != nil) {
[self onErrorOnGameUpdate:err];
@synchronized(self) {
error_ = true;
}
return;
}
if (isGL) {
self.glkView.delegate = (id<GLKViewDelegate>)(self);
[self.view addSubview: self.glkView];
@ -69,7 +80,14 @@
[EAGLContext setCurrentContext:context];
} else {
[self.view addSubview: self.metalView];
EbitenmobileviewSetUIView((uintptr_t)(self.metalView));
EbitenmobileviewSetUIView((uintptr_t)(self.metalView), &err);
if (err != nil) {
[self onErrorOnGameUpdate:err];
@synchronized(self) {
error_ = true;
}
return;
}
}
displayLink_ = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawFrame)];
@ -78,8 +96,19 @@
}
- (void)viewWillLayoutSubviews {
NSError* err = nil;
BOOL isGL = NO;
EbitenmobileviewIsGL(&isGL, &err);
if (err != nil) {
[self onErrorOnGameUpdate:err];
@synchronized(self) {
error_ = true;
}
return;
}
CGRect viewRect = [[self view] frame];
if (EbitenmobileviewIsGL()) {
if (isGL) {
[[self glkView] setFrame:viewRect];
} else {
[[self metalView] setFrame:viewRect];
@ -106,7 +135,18 @@
}
}
if (EbitenmobileviewIsGL()) {
NSError* err = nil;
BOOL isGL = NO;
EbitenmobileviewIsGL(&isGL, &err);
if (err != nil) {
[self onErrorOnGameUpdate:err];
@synchronized(self) {
error_ = true;
}
return;
}
if (isGL) {
[[self glkView] setNeedsDisplay];
} else {
[self updateEbiten];
@ -124,8 +164,10 @@
}
- (void)updateEbiten {
if (error_) {
return;
@synchronized(self) {
if (error_) {
return;
}
}
NSError* err = nil;
@ -134,7 +176,9 @@
[self performSelectorOnMainThread:@selector(onErrorOnGameUpdate:)
withObject:err
waitUntilDone:NO];
error_ = true;
@synchronized(self) {
error_ = true;
}
}
}
@ -143,8 +187,19 @@
}
- (void)updateTouches:(NSSet*)touches {
NSError* err = nil;
BOOL isGL = NO;
EbitenmobileviewIsGL(&isGL, &err);
if (err != nil) {
[self onErrorOnGameUpdate:err];
@synchronized(self) {
error_ = true;
}
return;
}
for (UITouch* touch in touches) {
if (EbitenmobileviewIsGL()) {
if (isGL) {
if (touch.view != [self glkView]) {
continue;
}

View File

@ -55,13 +55,34 @@ func (g *graphicsDriverCreatorImpl) newMetal() (graphicsdriver.Graphics, error)
return metal.NewGraphics()
}
func SetUIView(uiview uintptr) {
// This function should be called only when the graphics library is Metal.
if g, ok := theUI.graphicsDriver.(interface{ SetUIView(uintptr) }); ok {
g.SetUIView(uiview)
}
func SetUIView(uiview uintptr) error {
return theUI.setUIView(uiview)
}
func IsGL() bool {
return theUI.graphicsDriver.IsGL()
func IsGL() (bool, error) {
return theUI.isGL()
}
func (u *userInterfaceImpl) setUIView(uiview uintptr) error {
select {
case err := <-u.errCh:
return err
case <-u.graphicsDriverInitCh:
}
// This function should be called only when the graphics library is Metal.
if g, ok := u.graphicsDriver.(interface{ SetUIView(uintptr) }); ok {
g.SetUIView(uiview)
}
return nil
}
func (u *userInterfaceImpl) isGL() (bool, error) {
select {
case err := <-u.errCh:
return false, err
case <-u.graphicsDriverInitCh:
}
return u.graphicsDriver.IsGL(), nil
}

View File

@ -52,8 +52,9 @@ var (
func init() {
theUI.userInterfaceImpl = userInterfaceImpl{
foreground: 1,
errCh: make(chan error),
foreground: 1,
graphicsDriverInitCh: make(chan struct{}),
errCh: make(chan error),
// Give a default outside size so that the game can start without initializing them.
outsideWidth: 640,
@ -90,7 +91,8 @@ func (u *userInterfaceImpl) Update() error {
}
type userInterfaceImpl struct {
graphicsDriver graphicsdriver.Graphics
graphicsDriver graphicsdriver.Graphics
graphicsDriverInitCh chan struct{}
outsideWidth float64
outsideHeight float64
@ -287,6 +289,7 @@ func (u *userInterfaceImpl) run(game Game, mainloop bool) (err error) {
return err
}
u.graphicsDriver = g
close(u.graphicsDriverInitCh)
// If gomobile-build is used, wait for the outside size fixed.
if u.setGBuildSizeCh != nil {

View File

@ -18,10 +18,10 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
func SetUIView(uiview int64) {
ui.SetUIView(uintptr(uiview))
func SetUIView(uiview int64) error {
return ui.SetUIView(uintptr(uiview))
}
func IsGL() bool {
func IsGL() (bool, error) {
return ui.IsGL()
}