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
// 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
// to RunGame.
// to RunGame. `ebitenginesinglethread` works only with desktops.
//
// `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 nil
}
func (e *egl) makeContextCurrent() error {
if r := C.eglMakeCurrent(e.display, e.surface, e.surface, e.context); r == 0 {
return fmt.Errorf("ui: eglMakeCurrent failed")
}
return nil
}

View File

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