internal/ui: refactoring

This commit is contained in:
Hajime Hoshi 2022-12-29 21:09:30 +09:00
parent 4d89b5de07
commit dd853050e9

View File

@ -1071,95 +1071,102 @@ func (u *userInterfaceImpl) loopGame() error {
u.window.Destroy()
glfw.Terminate()
})
for {
var unfocused bool
// On Windows, the focusing state might be always false (#987).
// On Windows, even if a window is in another workspace, vsync seems to work.
// Then let's assume the window is always 'focused' as a workaround.
if runtime.GOOS != "windows" {
unfocused = u.window.GetAttrib(glfw.Focused) == glfw.False
}
var t1, t2 time.Time
if unfocused {
t1 = time.Now()
}
var outsideWidth, outsideHeight float64
var deviceScaleFactor float64
var err error
if u.mainThread.Call(func() {
outsideWidth, outsideHeight, err = u.update()
deviceScaleFactor = u.deviceScaleFactor(u.currentMonitor())
}); err != nil {
if err := u.updateGame(); err != nil {
return err
}
if err := u.context.updateFrame(u.graphicsDriver, outsideWidth, outsideHeight, deviceScaleFactor, u); err != nil {
return err
}
// Create icon images in a different goroutine (#1478).
// In the fullscreen mode, SetIcon fails (#1578).
if imgs := u.getIconImages(); len(imgs) > 0 && !u.isFullscreen() {
u.setIconImages(nil)
// Convert the icons in the different goroutine, as (*ebiten.Image).At cannot be invoked
// from this goroutine. At works only in between BeginFrame and EndFrame.
go func() {
newImgs := make([]image.Image, len(imgs))
for i, img := range imgs {
// TODO: If img is not *ebiten.Image, this converting is not necessary.
// However, this package cannot refer *ebiten.Image due to the package
// dependencies.
b := img.Bounds()
rgba := image.NewRGBA(b)
for j := b.Min.Y; j < b.Max.Y; j++ {
for i := b.Min.X; i < b.Max.X; i++ {
rgba.Set(i, j, img.At(i, j))
}
}
newImgs[i] = rgba
}
u.mainThread.Call(func() {
// In the fullscreen mode, reset the icon images and try again later.
if u.isFullscreen() {
u.setIconImages(imgs)
return
}
u.window.SetIcon(newImgs)
})
}()
}
// swapBuffers also checks IsGL, so this condition is redundant.
// However, (*thread).Call is not good for performance due to channels.
// Let's avoid this whenever possible (#1367).
if u.graphicsDriver.IsGL() {
u.mainThread.Call(u.swapBuffers)
}
if unfocused {
t2 = time.Now()
}
// When a window is not focused, SwapBuffers might return immediately and CPU might be busy.
// Mitigate this by sleeping (#982).
if unfocused {
d := t2.Sub(t1)
const wait = time.Second / 60
if d < wait {
time.Sleep(wait - d)
}
}
}
}
func (u *userInterfaceImpl) updateGame() error {
var unfocused bool
// On Windows, the focusing state might be always false (#987).
// On Windows, even if a window is in another workspace, vsync seems to work.
// Then let's assume the window is always 'focused' as a workaround.
if runtime.GOOS != "windows" {
unfocused = u.window.GetAttrib(glfw.Focused) == glfw.False
}
var t1, t2 time.Time
if unfocused {
t1 = time.Now()
}
var outsideWidth, outsideHeight float64
var deviceScaleFactor float64
var err error
if u.mainThread.Call(func() {
outsideWidth, outsideHeight, err = u.update()
deviceScaleFactor = u.deviceScaleFactor(u.currentMonitor())
}); err != nil {
return err
}
if err := u.context.updateFrame(u.graphicsDriver, outsideWidth, outsideHeight, deviceScaleFactor, u); err != nil {
return err
}
// Create icon images in a different goroutine (#1478).
// In the fullscreen mode, SetIcon fails (#1578).
if imgs := u.getIconImages(); len(imgs) > 0 && !u.isFullscreen() {
u.setIconImages(nil)
// Convert the icons in the different goroutine, as (*ebiten.Image).At cannot be invoked
// from this goroutine. At works only in between BeginFrame and EndFrame.
go func() {
newImgs := make([]image.Image, len(imgs))
for i, img := range imgs {
// TODO: If img is not *ebiten.Image, this converting is not necessary.
// However, this package cannot refer *ebiten.Image due to the package
// dependencies.
b := img.Bounds()
rgba := image.NewRGBA(b)
for j := b.Min.Y; j < b.Max.Y; j++ {
for i := b.Min.X; i < b.Max.X; i++ {
rgba.Set(i, j, img.At(i, j))
}
}
newImgs[i] = rgba
}
u.mainThread.Call(func() {
// In the fullscreen mode, reset the icon images and try again later.
if u.isFullscreen() {
u.setIconImages(imgs)
return
}
u.window.SetIcon(newImgs)
})
}()
}
// swapBuffers also checks IsGL, so this condition is redundant.
// However, (*thread).Call is not good for performance due to channels.
// Let's avoid this whenever possible (#1367).
if u.graphicsDriver.IsGL() {
u.mainThread.Call(u.swapBuffers)
}
if unfocused {
t2 = time.Now()
}
// When a window is not focused, SwapBuffers might return immediately and CPU might be busy.
// Mitigate this by sleeping (#982).
if unfocused {
d := t2.Sub(t1)
const wait = time.Second / 60
if d < wait {
time.Sleep(wait - d)
}
}
return nil
}
// swapBuffers must be called from the main thread.
func (u *userInterfaceImpl) swapBuffers() {
if u.graphicsDriver.IsGL() {