From 409025890465e1b3b2a745d662c182c778904fdf Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 4 Nov 2018 23:32:18 +0900 Subject: [PATCH] opengl: Remove Init (except for mobile) --- internal/mainthread/mainthread.go | 44 +++++++++++++++ internal/opengl/context_desktop.go | 88 ++++++++++++++---------------- internal/opengl/context_js.go | 56 ++++++++++++++++--- internal/ui/ui_glfw.go | 88 ++++++++++-------------------- internal/ui/ui_js.go | 5 +- internal/ui/ui_mobile.go | 7 +-- run.go | 2 +- 7 files changed, 167 insertions(+), 123 deletions(-) create mode 100644 internal/mainthread/mainthread.go diff --git a/internal/mainthread/mainthread.go b/internal/mainthread/mainthread.go new file mode 100644 index 000000000..0648904a6 --- /dev/null +++ b/internal/mainthread/mainthread.go @@ -0,0 +1,44 @@ +// Copyright 2018 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mainthread + +var funcs = make(chan func()) + +// Loop starts the main-thread loop. +// +// Loop must be called on the main thread. +func Loop(ch <-chan error) error { + for { + select { + case f := <-funcs: + f() + case err := <-ch: + // ch returns a value not only when an error occur but also it is closed. + return err + } + } +} + +// Run calls f on the main thread. +func Run(f func() error) error { + ch := make(chan struct{}) + var err error + funcs <- func() { + err = f() + close(ch) + } + <-ch + return err +} diff --git a/internal/opengl/context_desktop.go b/internal/opengl/context_desktop.go index 55a9d37c0..4d6909c50 100644 --- a/internal/opengl/context_desktop.go +++ b/internal/opengl/context_desktop.go @@ -26,6 +26,7 @@ import ( "github.com/go-gl/gl/v2.1/gl" "github.com/hajimehoshi/ebiten/internal/graphics" + "github.com/hajimehoshi/ebiten/internal/mainthread" ) type ( @@ -74,22 +75,15 @@ func init() { } type context struct { - init bool - runOnMainThread func(func() error) error + init bool } -func Init(runOnMainThread func(func() error) error) { - c := &Context{} - c.runOnMainThread = runOnMainThread - theContext = c -} - -func (c *Context) runOnContextThread(f func() error) error { - return c.runOnMainThread(f) +func init() { + theContext = &Context{} } func (c *Context) reset() error { - if err := c.runOnContextThread(func() error { + if err := mainthread.Run(func() error { if c.init { return nil } @@ -108,12 +102,12 @@ func (c *Context) reset() error { c.lastViewportWidth = 0 c.lastViewportHeight = 0 c.lastCompositeMode = graphics.CompositeModeUnknown - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.Enable(gl.BLEND) return nil }) c.BlendFunc(graphics.CompositeModeSourceOver) - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { f := int32(0) gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f) c.screenFramebuffer = framebufferNative(f) @@ -123,7 +117,7 @@ func (c *Context) reset() error { } func (c *Context) BlendFunc(mode graphics.CompositeMode) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { if c.lastCompositeMode == mode { return nil } @@ -137,7 +131,7 @@ func (c *Context) BlendFunc(mode graphics.CompositeMode) { func (c *Context) newTexture(width, height int) (textureNative, error) { var texture textureNative - if err := c.runOnContextThread(func() error { + if err := mainthread.Run(func() error { var t uint32 gl.GenTextures(1, &t) // TODO: Use gl.IsTexture @@ -151,7 +145,7 @@ func (c *Context) newTexture(width, height int) (textureNative, error) { return 0, err } c.bindTexture(texture) - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) @@ -163,7 +157,7 @@ func (c *Context) newTexture(width, height int) (textureNative, error) { } func (c *Context) bindFramebufferImpl(f framebufferNative) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f)) return nil }) @@ -171,12 +165,12 @@ func (c *Context) bindFramebufferImpl(f framebufferNative) { func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) { var pixels []byte - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.Flush() return nil }) c.bindFramebuffer(f.native) - if err := c.runOnContextThread(func() error { + if err := mainthread.Run(func() error { pixels = make([]byte, 4*width*height) gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels)) if e := gl.GetError(); e != gl.NO_ERROR { @@ -191,14 +185,14 @@ func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte, } func (c *Context) bindTextureImpl(t textureNative) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.BindTexture(gl.TEXTURE_2D, uint32(t)) return nil }) } func (c *Context) deleteTexture(t textureNative) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { tt := uint32(t) if !gl.IsTexture(tt) { return nil @@ -213,7 +207,7 @@ func (c *Context) deleteTexture(t textureNative) { func (c *Context) isTexture(t textureNative) bool { r := false - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { r = gl.IsTexture(uint32(t)) return nil }) @@ -222,7 +216,7 @@ func (c *Context) isTexture(t textureNative) bool { func (c *Context) texSubImage2D(t textureNative, p []byte, x, y, width, height int) { c.bindTexture(t) - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.TexSubImage2D(gl.TEXTURE_2D, 0, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(p)) return nil }) @@ -235,7 +229,7 @@ func (c *Context) BeforeSwapping() { func (c *Context) newFramebuffer(texture textureNative) (framebufferNative, error) { var framebuffer framebufferNative var f uint32 - if err := c.runOnContextThread(func() error { + if err := mainthread.Run(func() error { gl.GenFramebuffersEXT(1, &f) // TODO: Use gl.IsFramebuffer if f <= 0 { @@ -246,7 +240,7 @@ func (c *Context) newFramebuffer(texture textureNative) (framebufferNative, erro return 0, err } c.bindFramebuffer(framebufferNative(f)) - if err := c.runOnContextThread(func() error { + if err := mainthread.Run(func() error { gl.FramebufferTexture2DEXT(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(texture), 0) s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER) if s != gl.FRAMEBUFFER_COMPLETE { @@ -267,14 +261,14 @@ func (c *Context) newFramebuffer(texture textureNative) (framebufferNative, erro } func (c *Context) setViewportImpl(width, height int) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.Viewport(0, 0, int32(width), int32(height)) return nil }) } func (c *Context) deleteFramebuffer(f framebufferNative) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { ff := uint32(f) if !gl.IsFramebufferEXT(ff) { return nil @@ -291,7 +285,7 @@ func (c *Context) deleteFramebuffer(f framebufferNative) { func (c *Context) newShader(shaderType shaderType, source string) (shader, error) { var sh shader - if err := c.runOnContextThread(func() error { + if err := mainthread.Run(func() error { s := gl.CreateShader(uint32(shaderType)) if s == 0 { return fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) @@ -321,7 +315,7 @@ func (c *Context) newShader(shaderType shaderType, source string) (shader, error } func (c *Context) deleteShader(s shader) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.DeleteShader(uint32(s)) return nil }) @@ -329,7 +323,7 @@ func (c *Context) deleteShader(s shader) { func (c *Context) newProgram(shaders []shader) (program, error) { var pr program - if err := c.runOnContextThread(func() error { + if err := mainthread.Run(func() error { p := gl.CreateProgram() if p == 0 { return errors.New("opengl: glCreateProgram failed") @@ -353,14 +347,14 @@ func (c *Context) newProgram(shaders []shader) (program, error) { } func (c *Context) useProgram(p program) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.UseProgram(uint32(p)) return nil }) } func (c *Context) deleteProgram(p program) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { if !gl.IsProgram(uint32(p)) { return nil } @@ -380,7 +374,7 @@ func (c *Context) getUniformLocationImpl(p program, location string) uniformLoca } func (c *Context) uniformInt(p program, location string, v int) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { l := int32(c.locationCache.GetUniformLocation(c, p, location)) gl.Uniform1i(l, int32(v)) return nil @@ -388,7 +382,7 @@ func (c *Context) uniformInt(p program, location string, v int) { } func (c *Context) uniformFloat(p program, location string, v float32) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { l := int32(c.locationCache.GetUniformLocation(c, p, location)) gl.Uniform1f(l, v) return nil @@ -396,7 +390,7 @@ func (c *Context) uniformFloat(p program, location string, v float32) { } func (c *Context) uniformFloats(p program, location string, v []float32) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { l := int32(c.locationCache.GetUniformLocation(c, p, location)) switch len(v) { case 2: @@ -423,7 +417,7 @@ func (c *Context) getAttribLocationImpl(p program, location string) attribLocati } func (c *Context) vertexAttribPointer(p program, location string, size int, dataType DataType, stride int, offset int) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { l := c.locationCache.GetAttribLocation(c, p, location) gl.VertexAttribPointer(uint32(l), int32(size), uint32(dataType), false, int32(stride), gl.PtrOffset(offset)) return nil @@ -431,7 +425,7 @@ func (c *Context) vertexAttribPointer(p program, location string, size int, data } func (c *Context) enableVertexAttribArray(p program, location string) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { l := c.locationCache.GetAttribLocation(c, p, location) gl.EnableVertexAttribArray(uint32(l)) return nil @@ -439,7 +433,7 @@ func (c *Context) enableVertexAttribArray(p program, location string) { } func (c *Context) disableVertexAttribArray(p program, location string) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { l := c.locationCache.GetAttribLocation(c, p, location) gl.DisableVertexAttribArray(uint32(l)) return nil @@ -448,7 +442,7 @@ func (c *Context) disableVertexAttribArray(p program, location string) { func (c *Context) newArrayBuffer(size int) buffer { var bf buffer - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { var b uint32 gl.GenBuffers(1, &b) gl.BindBuffer(uint32(arrayBuffer), b) @@ -461,7 +455,7 @@ func (c *Context) newArrayBuffer(size int) buffer { func (c *Context) newElementArrayBuffer(size int) buffer { var bf buffer - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { var b uint32 gl.GenBuffers(1, &b) gl.BindBuffer(uint32(elementArrayBuffer), b) @@ -473,28 +467,28 @@ func (c *Context) newElementArrayBuffer(size int) buffer { } func (c *Context) bindBuffer(bufferType bufferType, b buffer) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.BindBuffer(uint32(bufferType), uint32(b)) return nil }) } func (c *Context) arrayBufferSubData(data []float32) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.BufferSubData(uint32(arrayBuffer), 0, len(data)*4, gl.Ptr(data)) return nil }) } func (c *Context) elementArrayBufferSubData(data []uint16) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.BufferSubData(uint32(elementArrayBuffer), 0, len(data)*2, gl.Ptr(data)) return nil }) } func (c *Context) deleteBuffer(b buffer) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { bb := uint32(b) gl.DeleteBuffers(1, &bb) return nil @@ -502,7 +496,7 @@ func (c *Context) deleteBuffer(b buffer) { } func (c *Context) DrawElements(len int, offsetInBytes int) { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.DrawElements(gl.TRIANGLES, int32(len), gl.UNSIGNED_SHORT, gl.PtrOffset(offsetInBytes)) return nil }) @@ -510,7 +504,7 @@ func (c *Context) DrawElements(len int, offsetInBytes int) { func (c *Context) maxTextureSizeImpl() int { size := 0 - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { s := int32(0) gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &s) size = int(s) @@ -520,7 +514,7 @@ func (c *Context) maxTextureSizeImpl() int { } func (c *Context) Flush() { - _ = c.runOnContextThread(func() error { + _ = mainthread.Run(func() error { gl.Flush() return nil }) diff --git a/internal/opengl/context_js.go b/internal/opengl/context_js.go index cf6864082..be843a696 100644 --- a/internal/opengl/context_js.go +++ b/internal/opengl/context_js.go @@ -119,11 +119,18 @@ type context struct { lastProgramID programID } -func Init() error { - if js.Global().Get("WebGLRenderingContext") == js.Undefined() { - return fmt.Errorf("opengl: WebGL is not supported") +func init() { + theContext = &Context{} +} + +func (c *Context) ensureGL() { + if c.gl != (js.Value{}) { + return } + if js.Global().Get("WebGLRenderingContext") == js.Undefined() { + panic("opengl: WebGL is not supported") + } // TODO: Define id? canvas := js.Global().Get("document").Call("querySelector", "canvas") attr := js.Global().Get("Object").New() @@ -133,18 +140,15 @@ func Init() error { if gl == js.Null() { gl = canvas.Call("getContext", "experimental-webgl", attr) if gl == js.Null() { - return fmt.Errorf("opengl: getContext failed") + panic("opengl: getContext failed") } } - c := &Context{} + c.gl = gl // Getting an extension might fail after the context is lost, so // it is required to get the extension here. c.loseContext = gl.Call("getExtension", "WEBGL_lose_context") - - theContext = c - return nil } func (c *Context) reset() error { @@ -154,6 +158,8 @@ func (c *Context) reset() error { c.lastViewportWidth = 0 c.lastViewportHeight = 0 c.lastCompositeMode = graphics.CompositeModeUnknown + + c.ensureGL() gl := c.gl gl.Call("enable", blend) c.BlendFunc(graphics.CompositeModeSourceOver) @@ -169,11 +175,13 @@ func (c *Context) BlendFunc(mode graphics.CompositeMode) { c.lastCompositeMode = mode s, d := mode.Operations() s2, d2 := convertOperation(s), convertOperation(d) + c.ensureGL() gl := c.gl gl.Call("blendFunc", int(s2), int(d2)) } func (c *Context) newTexture(width, height int) (textureNative, error) { + c.ensureGL() gl := c.gl t := gl.Call("createTexture") if t == js.Null() { @@ -200,11 +208,13 @@ func (c *Context) newTexture(width, height int) (textureNative, error) { } func (c *Context) bindFramebufferImpl(f framebufferNative) { + c.ensureGL() gl := c.gl gl.Call("bindFramebuffer", framebuffer_, js.Value(f)) } func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) { + c.ensureGL() gl := c.gl c.bindFramebuffer(f.native) @@ -221,11 +231,13 @@ func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte, } func (c *Context) bindTextureImpl(t textureNative) { + c.ensureGL() gl := c.gl gl.Call("bindTexture", texture2d, js.Value(t)) } func (c *Context) deleteTexture(t textureNative) { + c.ensureGL() gl := c.gl if !gl.Call("isTexture", js.Value(t)).Bool() { return @@ -237,12 +249,14 @@ func (c *Context) deleteTexture(t textureNative) { } func (c *Context) isTexture(t textureNative) bool { + c.ensureGL() gl := c.gl return gl.Call("isTexture", js.Value(t)).Bool() } func (c *Context) texSubImage2D(t textureNative, pixels []byte, x, y, width, height int) { c.bindTexture(t) + c.ensureGL() gl := c.gl // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, // GLsizei width, GLsizei height, @@ -253,6 +267,7 @@ func (c *Context) texSubImage2D(t textureNative, pixels []byte, x, y, width, hei } func (c *Context) newFramebuffer(t textureNative) (framebufferNative, error) { + c.ensureGL() gl := c.gl f := gl.Call("createFramebuffer") c.bindFramebuffer(framebufferNative(f)) @@ -266,11 +281,13 @@ func (c *Context) newFramebuffer(t textureNative) (framebufferNative, error) { } func (c *Context) setViewportImpl(width, height int) { + c.ensureGL() gl := c.gl gl.Call("viewport", 0, 0, width, height) } func (c *Context) deleteFramebuffer(f framebufferNative) { + c.ensureGL() gl := c.gl if !gl.Call("isFramebuffer", js.Value(f)).Bool() { return @@ -287,6 +304,7 @@ func (c *Context) deleteFramebuffer(f framebufferNative) { } func (c *Context) newShader(shaderType shaderType, source string) (shader, error) { + c.ensureGL() gl := c.gl s := gl.Call("createShader", int(shaderType)) if s == js.Null() { @@ -304,11 +322,13 @@ func (c *Context) newShader(shaderType shaderType, source string) (shader, error } func (c *Context) deleteShader(s shader) { + c.ensureGL() gl := c.gl gl.Call("deleteShader", js.Value(s)) } func (c *Context) newProgram(shaders []shader) (program, error) { + c.ensureGL() gl := c.gl v := gl.Call("createProgram") if v == js.Null() { @@ -332,11 +352,13 @@ func (c *Context) newProgram(shaders []shader) (program, error) { } func (c *Context) useProgram(p program) { + c.ensureGL() gl := c.gl gl.Call("useProgram", p.value) } func (c *Context) deleteProgram(p program) { + c.ensureGL() gl := c.gl if !gl.Call("isProgram", p.value).Bool() { return @@ -345,17 +367,20 @@ func (c *Context) deleteProgram(p program) { } func (c *Context) getUniformLocationImpl(p program, location string) uniformLocation { + c.ensureGL() gl := c.gl return uniformLocation(gl.Call("getUniformLocation", p.value, location)) } func (c *Context) uniformInt(p program, location string, v int) { + c.ensureGL() gl := c.gl l := c.locationCache.GetUniformLocation(c, p, location) gl.Call("uniform1i", js.Value(l), v) } func (c *Context) uniformFloat(p program, location string, v float32) { + c.ensureGL() gl := c.gl l := c.locationCache.GetUniformLocation(c, p, location) gl.Call("uniform1f", js.Value(l), v) @@ -366,6 +391,7 @@ var ( ) func (c *Context) uniformFloats(p program, location string, v []float32) { + c.ensureGL() gl := c.gl l := c.locationCache.GetUniformLocation(c, p, location) switch len(v) { @@ -383,29 +409,34 @@ func (c *Context) uniformFloats(p program, location string, v []float32) { } func (c *Context) getAttribLocationImpl(p program, location string) attribLocation { + c.ensureGL() gl := c.gl return attribLocation(gl.Call("getAttribLocation", p.value, location).Int()) } func (c *Context) vertexAttribPointer(p program, location string, size int, dataType DataType, stride int, offset int) { + c.ensureGL() gl := c.gl l := c.locationCache.GetAttribLocation(c, p, location) gl.Call("vertexAttribPointer", int(l), size, int(dataType), false, stride, offset) } func (c *Context) enableVertexAttribArray(p program, location string) { + c.ensureGL() gl := c.gl l := c.locationCache.GetAttribLocation(c, p, location) gl.Call("enableVertexAttribArray", int(l)) } func (c *Context) disableVertexAttribArray(p program, location string) { + c.ensureGL() gl := c.gl l := c.locationCache.GetAttribLocation(c, p, location) gl.Call("disableVertexAttribArray", int(l)) } func (c *Context) newArrayBuffer(size int) buffer { + c.ensureGL() gl := c.gl b := gl.Call("createBuffer") gl.Call("bindBuffer", int(arrayBuffer), js.Value(b)) @@ -414,6 +445,7 @@ func (c *Context) newArrayBuffer(size int) buffer { } func (c *Context) newElementArrayBuffer(size int) buffer { + c.ensureGL() gl := c.gl b := gl.Call("createBuffer") gl.Call("bindBuffer", int(elementArrayBuffer), js.Value(b)) @@ -422,11 +454,13 @@ func (c *Context) newElementArrayBuffer(size int) buffer { } func (c *Context) bindBuffer(bufferType bufferType, b buffer) { + c.ensureGL() gl := c.gl gl.Call("bindBuffer", int(bufferType), js.Value(b)) } func (c *Context) arrayBufferSubData(data []float32) { + c.ensureGL() gl := c.gl arr := js.TypedArrayOf(data) gl.Call("bufferSubData", int(arrayBuffer), 0, arr) @@ -434,6 +468,7 @@ func (c *Context) arrayBufferSubData(data []float32) { } func (c *Context) elementArrayBufferSubData(data []uint16) { + c.ensureGL() gl := c.gl arr := js.TypedArrayOf(data) gl.Call("bufferSubData", int(elementArrayBuffer), 0, arr) @@ -441,26 +476,31 @@ func (c *Context) elementArrayBufferSubData(data []uint16) { } func (c *Context) deleteBuffer(b buffer) { + c.ensureGL() gl := c.gl gl.Call("deleteBuffer", js.Value(b)) } func (c *Context) DrawElements(len int, offsetInBytes int) { + c.ensureGL() gl := c.gl gl.Call("drawElements", triangles, len, unsignedShort, offsetInBytes) } func (c *Context) maxTextureSizeImpl() int { + c.ensureGL() gl := c.gl return gl.Call("getParameter", maxTextureSize).Int() } func (c *Context) Flush() { + c.ensureGL() gl := c.gl gl.Call("flush") } func (c *Context) IsContextLost() bool { + c.ensureGL() gl := c.gl return gl.Call("isContextLost").Bool() } diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 7e80d1ec9..43ce0b013 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -31,6 +31,7 @@ import ( "github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/hooks" "github.com/hajimehoshi/ebiten/internal/input" + "github.com/hajimehoshi/ebiten/internal/mainthread" "github.com/hajimehoshi/ebiten/internal/opengl" ) @@ -58,8 +59,6 @@ type userInterface struct { initWindowDecorated bool initIconImages []image.Image - funcs chan func() - m sync.Mutex } @@ -102,7 +101,6 @@ func initialize() error { } hideConsoleWindowOnWindows() currentUI.window = window - currentUI.funcs = make(chan func()) currentUI.window.MakeContextCurrent() @@ -120,23 +118,13 @@ func initialize() error { return nil } -func RunMainThreadLoop(ch <-chan error) error { - // This must be called on the main thread. - - // TODO: Check this is done on the main thread. +func Loop(ch <-chan error) error { currentUI.setRunning(true) - defer func() { - currentUI.setRunning(false) - }() - for { - select { - case f := <-currentUI.funcs: - f() - case err := <-ch: - // ch returns a value not only when an error occur but also it is closed. - return err - } + if err := mainthread.Loop(ch); err != nil { + return err } + currentUI.setRunning(false) + return nil } func (u *userInterface) isRunning() bool { @@ -217,27 +205,12 @@ func (u *userInterface) setInitIconImages(iconImages []image.Image) { u.m.Unlock() } -func (u *userInterface) runOnMainThread(f func() error) error { - if u.funcs == nil { - // already closed - return nil - } - ch := make(chan struct{}) - var err error - u.funcs <- func() { - err = f() - close(ch) - } - <-ch - return err -} - func ScreenSizeInFullscreen() (int, int) { u := currentUI var v *glfw.VidMode s := 0.0 if u.isRunning() { - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { v = u.currentMonitor().GetVideoMode() s = glfwScale() return nil @@ -255,7 +228,7 @@ func SetScreenSize(width, height int) bool { panic("ui: Run is not called yet") } r := false - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { r = u.setScreenSize(width, height, u.scale, u.fullscreen(), u.vsync) return nil }) @@ -268,7 +241,7 @@ func SetScreenScale(scale float64) bool { panic("ui: Run is not called yet") } r := false - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { r = u.setScreenSize(u.width, u.height, scale, u.fullscreen(), u.vsync) return nil }) @@ -281,7 +254,7 @@ func ScreenScale() float64 { return 0 } s := 0.0 - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { s = u.scale return nil }) @@ -302,7 +275,7 @@ func IsFullscreen() bool { return u.isInitFullscreen() } b := false - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { b = u.fullscreen() return nil }) @@ -315,7 +288,7 @@ func SetFullscreen(fullscreen bool) { u.setInitFullscreen(fullscreen) return } - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { u := currentUI u.setScreenSize(u.width, u.height, u.scale, fullscreen, u.vsync) return nil @@ -342,7 +315,7 @@ func SetVsyncEnabled(enabled bool) { u.m.Unlock() return } - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { u := currentUI u.setScreenSize(u.width, u.height, u.scale, u.fullscreen(), enabled) return nil @@ -361,7 +334,7 @@ func SetWindowTitle(title string) { if !currentUI.isRunning() { return } - _ = currentUI.runOnMainThread(func() error { + _ = mainthread.Run(func() error { currentUI.window.SetTitle(title) return nil }) @@ -372,7 +345,7 @@ func SetWindowIcon(iconImages []image.Image) { currentUI.setInitIconImages(iconImages) return } - _ = currentUI.runOnMainThread(func() error { + _ = mainthread.Run(func() error { currentUI.window.SetIcon(iconImages) return nil }) @@ -399,7 +372,7 @@ func ScreenPadding() (x0, y0, x1, y1 float64) { sx := 0.0 sy := 0.0 gs := 0.0 - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { sx = float64(u.width) * u.actualScreenScale() sy = float64(u.height) * u.actualScreenScale() gs = glfwScale() @@ -424,7 +397,7 @@ func adjustCursorPosition(x, y int) (int, int) { } ox, oy, _, _ := ScreenPadding() s := 0.0 - _ = currentUI.runOnMainThread(func() error { + _ = mainthread.Run(func() error { s = currentUI.actualScreenScale() return nil }) @@ -442,7 +415,7 @@ func IsCursorVisible() bool { return u.isInitCursorVisible() } v := false - _ = currentUI.runOnMainThread(func() error { + _ = mainthread.Run(func() error { v = currentUI.window.GetInputMode(glfw.CursorMode) == glfw.CursorNormal return nil }) @@ -455,7 +428,7 @@ func SetCursorVisible(visible bool) { u.setInitCursorVisible(visible) return } - _ = currentUI.runOnMainThread(func() error { + _ = mainthread.Run(func() error { c := glfw.CursorNormal if !visible { c = glfw.CursorHidden @@ -471,7 +444,7 @@ func IsWindowDecorated() bool { return u.isInitWindowDecorated() } v := false - _ = currentUI.runOnMainThread(func() error { + _ = mainthread.Run(func() error { v = currentUI.window.GetAttrib(glfw.Decorated) == glfw.True return nil }) @@ -490,7 +463,7 @@ func SetWindowDecorated(decorated bool) { // TODO: Now SetAttrib doesn't exist on GLFW 3.2. Revisit later (#556). // If SetAttrib exists, the implementation would be: // - // _ = currentUI.runOnMainThread(func() error { + // _ = mainthread.Run(func() error { // v := glfw.False // if decorated { // v = glfw.True @@ -506,7 +479,7 @@ func DeviceScaleFactor() float64 { return devicescale.GetAt(u.currentMonitor().GetPos()) } - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { m := u.currentMonitor() f = devicescale.GetAt(m.GetPos()) return nil @@ -516,10 +489,7 @@ func DeviceScaleFactor() float64 { func Run(width, height int, scale float64, title string, g GraphicsContext, mainloop bool) error { u := currentUI - // GLContext must be created before setting the screen size, which requires - // swapping buffers. - opengl.Init(currentUI.runOnMainThread) - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { // Get the monitor before showing the window. // // On Windows, there are two types of windows: @@ -596,7 +566,7 @@ func (u *userInterface) updateGraphicsContext(g GraphicsContext) { actualScale := 0.0 sizeChanged := false // TODO: Is it possible to reduce 'runOnMainThread' calls? - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { actualScale = u.actualScreenScale() if u.lastActualScale != actualScale { u.forceSetScreenSize(u.width, u.height, u.scale, u.fullscreen(), u.vsync) @@ -618,7 +588,7 @@ func (u *userInterface) updateGraphicsContext(g GraphicsContext) { func (u *userInterface) update(g GraphicsContext) error { shouldClose := false - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { shouldClose = u.window.ShouldClose() return nil }) @@ -626,7 +596,7 @@ func (u *userInterface) update(g GraphicsContext) error { return RegularTermination } - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { if u.isInitFullscreen() { u := currentUI u.setScreenSize(u.width, u.height, u.scale, true, u.vsync) @@ -638,7 +608,7 @@ func (u *userInterface) update(g GraphicsContext) error { // This call is needed for initialization. u.updateGraphicsContext(g) - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { u.pollEvents() defer hooks.ResumeAudio() for !u.isRunnableInBackground() && u.window.GetAttrib(glfw.Focused) == 0 { @@ -665,7 +635,7 @@ func (u *userInterface) update(g GraphicsContext) error { func (u *userInterface) loop(g GraphicsContext) error { defer func() { - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { glfw.Terminate() return nil }) @@ -683,7 +653,7 @@ func (u *userInterface) loop(g GraphicsContext) error { // before swapping buffers. opengl.GetContext().BeforeSwapping() - _ = u.runOnMainThread(func() error { + _ = mainthread.Run(func() error { if !vsync { u.swapBuffers() return nil diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index 54989d453..42b04867d 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -357,7 +357,7 @@ func init() { })) } -func RunMainThreadLoop(ch <-chan error) error { +func Loop(ch <-chan error) error { return <-ch } @@ -366,9 +366,6 @@ func Run(width, height int, scale float64, title string, g GraphicsContext, main document.Set("title", title) u.setScreenSize(width, height, scale, u.fullscreen) canvas.Call("focus") - if err := opengl.Init(); err != nil { - return err - } return u.loop(g) } diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index 53bb9fe64..ae6e0cf9e 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -166,11 +166,10 @@ func Run(width, height int, scale float64, title string, g GraphicsContext, main } } -// RunMainThreadLoop runs the main routine for gomobile-build. -func RunMainThreadLoop(ch <-chan error) error { +// Loop runs the main routine for gomobile-build. +func Loop(ch <-chan error) error { go func() { - // As mobile apps never ends, RunMainThreadLoop can't return. - // Just panic here. + // As mobile apps never ends, Loop can't return. Just panic here. err := <-ch panic(err) }() diff --git a/run.go b/run.go index 371fc11b4..e73f5642f 100644 --- a/run.go +++ b/run.go @@ -318,7 +318,7 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e } }() // TODO: Use context in Go 1.7? - if err := ui.RunMainThreadLoop(ch); err != nil { + if err := ui.Loop(ch); err != nil { return err } return nil