internal/ui: use a separate render thread for Nintendo Switch

Updates #2512
This commit is contained in:
Hajime Hoshi 2023-01-03 21:12:10 +09:00
parent 312dce950c
commit 0bdfeec610
3 changed files with 56 additions and 10 deletions

2
doc.go
View File

@ -99,7 +99,7 @@
// `ebitenginesinglethread` disables Ebitengine's thread safety to unlock maximum performance. If you use this you will have // `ebitenginesinglethread` disables Ebitengine's thread safety to unlock maximum performance. If you use this you will have
// to manage threads yourself. Functions like IsKeyPressed will no longer be concurrent-safe with this build tag. // to manage threads yourself. Functions like IsKeyPressed will no longer be concurrent-safe with this build tag.
// They must be called from the main thread or the same goroutine as the given game's callback functions like Update // They must be called from the main thread or the same goroutine as the given game's callback functions like Update
// to RunGame. // to RunGame. `ebitenginesinglethread` works only with desktops.
// //
// `microsoftgdk` is for Microsoft GDK (e.g. Xbox). // `microsoftgdk` is for Microsoft GDK (e.g. Xbox).
// //

View File

@ -81,10 +81,13 @@ func (e *egl) init(nativeWindowHandle C.NativeWindowType) error {
return fmt.Errorf("ui: eglCreateContext failed: error: %d", C.eglGetError()) return fmt.Errorf("ui: eglCreateContext failed: error: %d", C.eglGetError())
} }
return nil
}
func (e *egl) makeContextCurrent() error {
if r := C.eglMakeCurrent(e.display, e.surface, e.surface, e.context); r == 0 { if r := C.eglMakeCurrent(e.display, e.surface, e.surface, e.context); r == 0 {
return fmt.Errorf("ui: eglMakeCurrent failed") return fmt.Errorf("ui: eglMakeCurrent failed")
} }
return nil return nil
} }

View File

@ -21,11 +21,16 @@ package ui
import "C" import "C"
import ( import (
stdcontext "context"
"runtime" "runtime"
"golang.org/x/sync/errgroup"
"github.com/hajimehoshi/ebiten/v2/internal/gamepad" "github.com/hajimehoshi/ebiten/v2/internal/gamepad"
"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"
) )
type graphicsDriverCreatorImpl struct{} type graphicsDriverCreatorImpl struct{}
@ -61,6 +66,9 @@ type userInterfaceImpl struct {
nativeTouches []C.struct_Touch nativeTouches []C.struct_Touch
egl egl egl egl
mainThread *thread.OSThread
renderThread *thread.OSThread
} }
func (u *userInterfaceImpl) Run(game Game, options *RunOptions) error { func (u *userInterfaceImpl) Run(game Game, options *RunOptions) error {
@ -78,19 +86,54 @@ func (u *userInterfaceImpl) Run(game Game, options *RunOptions) error {
initializeProfiler() initializeProfiler()
u.mainThread = thread.NewOSThread()
u.renderThread = thread.NewOSThread()
graphicscommand.SetRenderThread(u.renderThread)
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 { for {
recordProfilerHeartbeat() recordProfilerHeartbeat()
// TODO: Make a separate thread for rendering (#2512). u.mainThread.Call(func() {
gamepad.Update() gamepad.Update()
u.updateInputState() u.updateInputState()
})
if err := u.context.updateFrame(u.graphicsDriver, float64(C.kScreenWidth), float64(C.kScreenHeight), deviceScaleFactor, u); err != nil { if err := u.context.updateFrame(u.graphicsDriver, float64(C.kScreenWidth), float64(C.kScreenHeight), deviceScaleFactor, u); err != nil {
return err return err
} }
u.renderThread.Call(func() {
u.egl.swapBuffers() u.egl.swapBuffers()
})
} }
})
// Run the main thread.
_ = u.mainThread.Loop(ctx)
if err := wg.Wait(); err != nil {
return err
}
return nil
} }
func (*userInterfaceImpl) DeviceScaleFactor() float64 { func (*userInterfaceImpl) DeviceScaleFactor() float64 {