diff --git a/internal/graphicsdriver/metal/driver.go b/internal/graphicsdriver/metal/driver.go index 2ec154cad..52fc150e3 100644 --- a/internal/graphicsdriver/metal/driver.go +++ b/internal/graphicsdriver/metal/driver.go @@ -233,10 +233,11 @@ type Driver struct { src *Image dst *Image + vsync bool maxImageSize int } -var theDriver Driver +var theDriver = Driver{vsync: true} func Get() *Driver { return &theDriver @@ -395,7 +396,7 @@ func (d *Driver) Reset() error { // MTLPixelFormatBGRA10_XR_sRGB. d.ml.SetPixelFormat(mtl.PixelFormatBGRA8UNorm) d.ml.SetMaximumDrawableCount(3) - d.ml.SetDisplaySyncEnabled(true) + d.ml.SetDisplaySyncEnabled(d.vsync) replaces := map[string]string{ "{{.FilterNearest}}": fmt.Sprintf("%d", graphics.FilterNearest), @@ -570,7 +571,11 @@ func (d *Driver) ResetSource() { } func (d *Driver) SetVsyncEnabled(enabled bool) { - d.ml.SetDisplaySyncEnabled(enabled) + mainthread.Run(func() error { + d.ml.SetDisplaySyncEnabled(enabled) + d.vsync = enabled + return nil + }) } func (d *Driver) VDirection() graphicsdriver.VDirection { diff --git a/internal/mainthread/mainthread.go b/internal/mainthread/mainthread.go index 0648904a6..30a50f62f 100644 --- a/internal/mainthread/mainthread.go +++ b/internal/mainthread/mainthread.go @@ -14,16 +14,39 @@ package mainthread -var funcs = make(chan func()) +import ( + "runtime" + "sync" +) + +var ( + funcs chan func() + running bool + + m sync.Mutex +) + +func init() { + runtime.LockOSThread() +} // Loop starts the main-thread loop. // // Loop must be called on the main thread. func Loop(ch <-chan error) error { + m.Lock() + funcs = make(chan func()) + m.Unlock() for { select { case f := <-funcs: + m.Lock() + running = true + m.Unlock() f() + m.Lock() + running = false + m.Unlock() case err := <-ch: // ch returns a value not only when an error occur but also it is closed. return err @@ -32,7 +55,19 @@ func Loop(ch <-chan error) error { } // Run calls f on the main thread. +// +// Run can be called even before Loop is called. +// +// Run can be called recursively: Run can be called from the function that are called via Run. func Run(f func() error) error { + // Even if funcs is nil, Run is called from the main thread (e.g. init) + m.Lock() + now := funcs == nil || running + m.Unlock() + if now { + return f() + } + ch := make(chan struct{}) var err error funcs <- func() { diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index b7966de29..f8669b1e8 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -76,7 +76,6 @@ var ( ) func init() { - runtime.LockOSThread() hideConsoleWindowOnWindows() if err := initialize(); err != nil { panic(err)