opengl: Remove Init (except for mobile)

This commit is contained in:
Hajime Hoshi 2018-11-04 23:32:18 +09:00
parent 16ada03928
commit 4090258904
7 changed files with 167 additions and 123 deletions

View File

@ -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
}

View File

@ -26,6 +26,7 @@ import (
"github.com/go-gl/gl/v2.1/gl" "github.com/go-gl/gl/v2.1/gl"
"github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/mainthread"
) )
type ( type (
@ -74,22 +75,15 @@ func init() {
} }
type context struct { type context struct {
init bool init bool
runOnMainThread func(func() error) error
} }
func Init(runOnMainThread func(func() error) error) { func init() {
c := &Context{} theContext = &Context{}
c.runOnMainThread = runOnMainThread
theContext = c
}
func (c *Context) runOnContextThread(f func() error) error {
return c.runOnMainThread(f)
} }
func (c *Context) reset() error { func (c *Context) reset() error {
if err := c.runOnContextThread(func() error { if err := mainthread.Run(func() error {
if c.init { if c.init {
return nil return nil
} }
@ -108,12 +102,12 @@ func (c *Context) reset() error {
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
c.lastCompositeMode = graphics.CompositeModeUnknown c.lastCompositeMode = graphics.CompositeModeUnknown
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.Enable(gl.BLEND) gl.Enable(gl.BLEND)
return nil return nil
}) })
c.BlendFunc(graphics.CompositeModeSourceOver) c.BlendFunc(graphics.CompositeModeSourceOver)
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
f := int32(0) f := int32(0)
gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f) gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f)
c.screenFramebuffer = framebufferNative(f) c.screenFramebuffer = framebufferNative(f)
@ -123,7 +117,7 @@ func (c *Context) reset() error {
} }
func (c *Context) BlendFunc(mode graphics.CompositeMode) { func (c *Context) BlendFunc(mode graphics.CompositeMode) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
if c.lastCompositeMode == mode { if c.lastCompositeMode == mode {
return nil return nil
} }
@ -137,7 +131,7 @@ func (c *Context) BlendFunc(mode graphics.CompositeMode) {
func (c *Context) newTexture(width, height int) (textureNative, error) { func (c *Context) newTexture(width, height int) (textureNative, error) {
var texture textureNative var texture textureNative
if err := c.runOnContextThread(func() error { if err := mainthread.Run(func() error {
var t uint32 var t uint32
gl.GenTextures(1, &t) gl.GenTextures(1, &t)
// TODO: Use gl.IsTexture // TODO: Use gl.IsTexture
@ -151,7 +145,7 @@ func (c *Context) newTexture(width, height int) (textureNative, error) {
return 0, err return 0, err
} }
c.bindTexture(texture) 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_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_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) 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) { func (c *Context) bindFramebufferImpl(f framebufferNative) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f)) gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f))
return nil return nil
}) })
@ -171,12 +165,12 @@ func (c *Context) bindFramebufferImpl(f framebufferNative) {
func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) { func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) {
var pixels []byte var pixels []byte
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.Flush() gl.Flush()
return nil return nil
}) })
c.bindFramebuffer(f.native) c.bindFramebuffer(f.native)
if err := c.runOnContextThread(func() error { if err := mainthread.Run(func() error {
pixels = make([]byte, 4*width*height) pixels = make([]byte, 4*width*height)
gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels)) gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels))
if e := gl.GetError(); e != gl.NO_ERROR { 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) { func (c *Context) bindTextureImpl(t textureNative) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.BindTexture(gl.TEXTURE_2D, uint32(t)) gl.BindTexture(gl.TEXTURE_2D, uint32(t))
return nil return nil
}) })
} }
func (c *Context) deleteTexture(t textureNative) { func (c *Context) deleteTexture(t textureNative) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
tt := uint32(t) tt := uint32(t)
if !gl.IsTexture(tt) { if !gl.IsTexture(tt) {
return nil return nil
@ -213,7 +207,7 @@ func (c *Context) deleteTexture(t textureNative) {
func (c *Context) isTexture(t textureNative) bool { func (c *Context) isTexture(t textureNative) bool {
r := false r := false
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
r = gl.IsTexture(uint32(t)) r = gl.IsTexture(uint32(t))
return nil 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) { func (c *Context) texSubImage2D(t textureNative, p []byte, x, y, width, height int) {
c.bindTexture(t) 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)) gl.TexSubImage2D(gl.TEXTURE_2D, 0, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(p))
return nil return nil
}) })
@ -235,7 +229,7 @@ func (c *Context) BeforeSwapping() {
func (c *Context) newFramebuffer(texture textureNative) (framebufferNative, error) { func (c *Context) newFramebuffer(texture textureNative) (framebufferNative, error) {
var framebuffer framebufferNative var framebuffer framebufferNative
var f uint32 var f uint32
if err := c.runOnContextThread(func() error { if err := mainthread.Run(func() error {
gl.GenFramebuffersEXT(1, &f) gl.GenFramebuffersEXT(1, &f)
// TODO: Use gl.IsFramebuffer // TODO: Use gl.IsFramebuffer
if f <= 0 { if f <= 0 {
@ -246,7 +240,7 @@ func (c *Context) newFramebuffer(texture textureNative) (framebufferNative, erro
return 0, err return 0, err
} }
c.bindFramebuffer(framebufferNative(f)) 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) gl.FramebufferTexture2DEXT(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(texture), 0)
s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER) s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER)
if s != gl.FRAMEBUFFER_COMPLETE { if s != gl.FRAMEBUFFER_COMPLETE {
@ -267,14 +261,14 @@ func (c *Context) newFramebuffer(texture textureNative) (framebufferNative, erro
} }
func (c *Context) setViewportImpl(width, height int) { func (c *Context) setViewportImpl(width, height int) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.Viewport(0, 0, int32(width), int32(height)) gl.Viewport(0, 0, int32(width), int32(height))
return nil return nil
}) })
} }
func (c *Context) deleteFramebuffer(f framebufferNative) { func (c *Context) deleteFramebuffer(f framebufferNative) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
ff := uint32(f) ff := uint32(f)
if !gl.IsFramebufferEXT(ff) { if !gl.IsFramebufferEXT(ff) {
return nil return nil
@ -291,7 +285,7 @@ func (c *Context) deleteFramebuffer(f framebufferNative) {
func (c *Context) newShader(shaderType shaderType, source string) (shader, error) { func (c *Context) newShader(shaderType shaderType, source string) (shader, error) {
var sh shader var sh shader
if err := c.runOnContextThread(func() error { if err := mainthread.Run(func() error {
s := gl.CreateShader(uint32(shaderType)) s := gl.CreateShader(uint32(shaderType))
if s == 0 { if s == 0 {
return fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) 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) { func (c *Context) deleteShader(s shader) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.DeleteShader(uint32(s)) gl.DeleteShader(uint32(s))
return nil return nil
}) })
@ -329,7 +323,7 @@ func (c *Context) deleteShader(s shader) {
func (c *Context) newProgram(shaders []shader) (program, error) { func (c *Context) newProgram(shaders []shader) (program, error) {
var pr program var pr program
if err := c.runOnContextThread(func() error { if err := mainthread.Run(func() error {
p := gl.CreateProgram() p := gl.CreateProgram()
if p == 0 { if p == 0 {
return errors.New("opengl: glCreateProgram failed") return errors.New("opengl: glCreateProgram failed")
@ -353,14 +347,14 @@ func (c *Context) newProgram(shaders []shader) (program, error) {
} }
func (c *Context) useProgram(p program) { func (c *Context) useProgram(p program) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.UseProgram(uint32(p)) gl.UseProgram(uint32(p))
return nil return nil
}) })
} }
func (c *Context) deleteProgram(p program) { func (c *Context) deleteProgram(p program) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
if !gl.IsProgram(uint32(p)) { if !gl.IsProgram(uint32(p)) {
return nil 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) { 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)) l := int32(c.locationCache.GetUniformLocation(c, p, location))
gl.Uniform1i(l, int32(v)) gl.Uniform1i(l, int32(v))
return nil 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) { 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)) l := int32(c.locationCache.GetUniformLocation(c, p, location))
gl.Uniform1f(l, v) gl.Uniform1f(l, v)
return nil 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) { 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)) l := int32(c.locationCache.GetUniformLocation(c, p, location))
switch len(v) { switch len(v) {
case 2: 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) { 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) l := c.locationCache.GetAttribLocation(c, p, location)
gl.VertexAttribPointer(uint32(l), int32(size), uint32(dataType), false, int32(stride), gl.PtrOffset(offset)) gl.VertexAttribPointer(uint32(l), int32(size), uint32(dataType), false, int32(stride), gl.PtrOffset(offset))
return nil 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) { func (c *Context) enableVertexAttribArray(p program, location string) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
l := c.locationCache.GetAttribLocation(c, p, location) l := c.locationCache.GetAttribLocation(c, p, location)
gl.EnableVertexAttribArray(uint32(l)) gl.EnableVertexAttribArray(uint32(l))
return nil return nil
@ -439,7 +433,7 @@ func (c *Context) enableVertexAttribArray(p program, location string) {
} }
func (c *Context) disableVertexAttribArray(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) l := c.locationCache.GetAttribLocation(c, p, location)
gl.DisableVertexAttribArray(uint32(l)) gl.DisableVertexAttribArray(uint32(l))
return nil return nil
@ -448,7 +442,7 @@ func (c *Context) disableVertexAttribArray(p program, location string) {
func (c *Context) newArrayBuffer(size int) buffer { func (c *Context) newArrayBuffer(size int) buffer {
var bf buffer var bf buffer
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
var b uint32 var b uint32
gl.GenBuffers(1, &b) gl.GenBuffers(1, &b)
gl.BindBuffer(uint32(arrayBuffer), b) gl.BindBuffer(uint32(arrayBuffer), b)
@ -461,7 +455,7 @@ func (c *Context) newArrayBuffer(size int) buffer {
func (c *Context) newElementArrayBuffer(size int) buffer { func (c *Context) newElementArrayBuffer(size int) buffer {
var bf buffer var bf buffer
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
var b uint32 var b uint32
gl.GenBuffers(1, &b) gl.GenBuffers(1, &b)
gl.BindBuffer(uint32(elementArrayBuffer), 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) { func (c *Context) bindBuffer(bufferType bufferType, b buffer) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.BindBuffer(uint32(bufferType), uint32(b)) gl.BindBuffer(uint32(bufferType), uint32(b))
return nil return nil
}) })
} }
func (c *Context) arrayBufferSubData(data []float32) { 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)) gl.BufferSubData(uint32(arrayBuffer), 0, len(data)*4, gl.Ptr(data))
return nil return nil
}) })
} }
func (c *Context) elementArrayBufferSubData(data []uint16) { 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)) gl.BufferSubData(uint32(elementArrayBuffer), 0, len(data)*2, gl.Ptr(data))
return nil return nil
}) })
} }
func (c *Context) deleteBuffer(b buffer) { func (c *Context) deleteBuffer(b buffer) {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
bb := uint32(b) bb := uint32(b)
gl.DeleteBuffers(1, &bb) gl.DeleteBuffers(1, &bb)
return nil return nil
@ -502,7 +496,7 @@ func (c *Context) deleteBuffer(b buffer) {
} }
func (c *Context) DrawElements(len int, offsetInBytes int) { 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)) gl.DrawElements(gl.TRIANGLES, int32(len), gl.UNSIGNED_SHORT, gl.PtrOffset(offsetInBytes))
return nil return nil
}) })
@ -510,7 +504,7 @@ func (c *Context) DrawElements(len int, offsetInBytes int) {
func (c *Context) maxTextureSizeImpl() int { func (c *Context) maxTextureSizeImpl() int {
size := 0 size := 0
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
s := int32(0) s := int32(0)
gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &s) gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &s)
size = int(s) size = int(s)
@ -520,7 +514,7 @@ func (c *Context) maxTextureSizeImpl() int {
} }
func (c *Context) Flush() { func (c *Context) Flush() {
_ = c.runOnContextThread(func() error { _ = mainthread.Run(func() error {
gl.Flush() gl.Flush()
return nil return nil
}) })

View File

@ -119,11 +119,18 @@ type context struct {
lastProgramID programID lastProgramID programID
} }
func Init() error { func init() {
if js.Global().Get("WebGLRenderingContext") == js.Undefined() { theContext = &Context{}
return fmt.Errorf("opengl: WebGL is not supported") }
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? // TODO: Define id?
canvas := js.Global().Get("document").Call("querySelector", "canvas") canvas := js.Global().Get("document").Call("querySelector", "canvas")
attr := js.Global().Get("Object").New() attr := js.Global().Get("Object").New()
@ -133,18 +140,15 @@ func Init() error {
if gl == js.Null() { if gl == js.Null() {
gl = canvas.Call("getContext", "experimental-webgl", attr) gl = canvas.Call("getContext", "experimental-webgl", attr)
if gl == js.Null() { if gl == js.Null() {
return fmt.Errorf("opengl: getContext failed") panic("opengl: getContext failed")
} }
} }
c := &Context{}
c.gl = gl c.gl = gl
// Getting an extension might fail after the context is lost, so // Getting an extension might fail after the context is lost, so
// it is required to get the extension here. // it is required to get the extension here.
c.loseContext = gl.Call("getExtension", "WEBGL_lose_context") c.loseContext = gl.Call("getExtension", "WEBGL_lose_context")
theContext = c
return nil
} }
func (c *Context) reset() error { func (c *Context) reset() error {
@ -154,6 +158,8 @@ func (c *Context) reset() error {
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
c.lastCompositeMode = graphics.CompositeModeUnknown c.lastCompositeMode = graphics.CompositeModeUnknown
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("enable", blend) gl.Call("enable", blend)
c.BlendFunc(graphics.CompositeModeSourceOver) c.BlendFunc(graphics.CompositeModeSourceOver)
@ -169,11 +175,13 @@ func (c *Context) BlendFunc(mode graphics.CompositeMode) {
c.lastCompositeMode = mode c.lastCompositeMode = mode
s, d := mode.Operations() s, d := mode.Operations()
s2, d2 := convertOperation(s), convertOperation(d) s2, d2 := convertOperation(s), convertOperation(d)
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("blendFunc", int(s2), int(d2)) gl.Call("blendFunc", int(s2), int(d2))
} }
func (c *Context) newTexture(width, height int) (textureNative, error) { func (c *Context) newTexture(width, height int) (textureNative, error) {
c.ensureGL()
gl := c.gl gl := c.gl
t := gl.Call("createTexture") t := gl.Call("createTexture")
if t == js.Null() { if t == js.Null() {
@ -200,11 +208,13 @@ func (c *Context) newTexture(width, height int) (textureNative, error) {
} }
func (c *Context) bindFramebufferImpl(f framebufferNative) { func (c *Context) bindFramebufferImpl(f framebufferNative) {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("bindFramebuffer", framebuffer_, js.Value(f)) gl.Call("bindFramebuffer", framebuffer_, js.Value(f))
} }
func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) { func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) {
c.ensureGL()
gl := c.gl gl := c.gl
c.bindFramebuffer(f.native) c.bindFramebuffer(f.native)
@ -221,11 +231,13 @@ func (c *Context) framebufferPixels(f *framebuffer, width, height int) ([]byte,
} }
func (c *Context) bindTextureImpl(t textureNative) { func (c *Context) bindTextureImpl(t textureNative) {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("bindTexture", texture2d, js.Value(t)) gl.Call("bindTexture", texture2d, js.Value(t))
} }
func (c *Context) deleteTexture(t textureNative) { func (c *Context) deleteTexture(t textureNative) {
c.ensureGL()
gl := c.gl gl := c.gl
if !gl.Call("isTexture", js.Value(t)).Bool() { if !gl.Call("isTexture", js.Value(t)).Bool() {
return return
@ -237,12 +249,14 @@ func (c *Context) deleteTexture(t textureNative) {
} }
func (c *Context) isTexture(t textureNative) bool { func (c *Context) isTexture(t textureNative) bool {
c.ensureGL()
gl := c.gl gl := c.gl
return gl.Call("isTexture", js.Value(t)).Bool() return gl.Call("isTexture", js.Value(t)).Bool()
} }
func (c *Context) texSubImage2D(t textureNative, pixels []byte, x, y, width, height int) { func (c *Context) texSubImage2D(t textureNative, pixels []byte, x, y, width, height int) {
c.bindTexture(t) c.bindTexture(t)
c.ensureGL()
gl := c.gl gl := c.gl
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height, // 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) { func (c *Context) newFramebuffer(t textureNative) (framebufferNative, error) {
c.ensureGL()
gl := c.gl gl := c.gl
f := gl.Call("createFramebuffer") f := gl.Call("createFramebuffer")
c.bindFramebuffer(framebufferNative(f)) c.bindFramebuffer(framebufferNative(f))
@ -266,11 +281,13 @@ func (c *Context) newFramebuffer(t textureNative) (framebufferNative, error) {
} }
func (c *Context) setViewportImpl(width, height int) { func (c *Context) setViewportImpl(width, height int) {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("viewport", 0, 0, width, height) gl.Call("viewport", 0, 0, width, height)
} }
func (c *Context) deleteFramebuffer(f framebufferNative) { func (c *Context) deleteFramebuffer(f framebufferNative) {
c.ensureGL()
gl := c.gl gl := c.gl
if !gl.Call("isFramebuffer", js.Value(f)).Bool() { if !gl.Call("isFramebuffer", js.Value(f)).Bool() {
return return
@ -287,6 +304,7 @@ func (c *Context) deleteFramebuffer(f framebufferNative) {
} }
func (c *Context) newShader(shaderType shaderType, source string) (shader, error) { func (c *Context) newShader(shaderType shaderType, source string) (shader, error) {
c.ensureGL()
gl := c.gl gl := c.gl
s := gl.Call("createShader", int(shaderType)) s := gl.Call("createShader", int(shaderType))
if s == js.Null() { if s == js.Null() {
@ -304,11 +322,13 @@ func (c *Context) newShader(shaderType shaderType, source string) (shader, error
} }
func (c *Context) deleteShader(s shader) { func (c *Context) deleteShader(s shader) {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("deleteShader", js.Value(s)) gl.Call("deleteShader", js.Value(s))
} }
func (c *Context) newProgram(shaders []shader) (program, error) { func (c *Context) newProgram(shaders []shader) (program, error) {
c.ensureGL()
gl := c.gl gl := c.gl
v := gl.Call("createProgram") v := gl.Call("createProgram")
if v == js.Null() { if v == js.Null() {
@ -332,11 +352,13 @@ func (c *Context) newProgram(shaders []shader) (program, error) {
} }
func (c *Context) useProgram(p program) { func (c *Context) useProgram(p program) {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("useProgram", p.value) gl.Call("useProgram", p.value)
} }
func (c *Context) deleteProgram(p program) { func (c *Context) deleteProgram(p program) {
c.ensureGL()
gl := c.gl gl := c.gl
if !gl.Call("isProgram", p.value).Bool() { if !gl.Call("isProgram", p.value).Bool() {
return return
@ -345,17 +367,20 @@ func (c *Context) deleteProgram(p program) {
} }
func (c *Context) getUniformLocationImpl(p program, location string) uniformLocation { func (c *Context) getUniformLocationImpl(p program, location string) uniformLocation {
c.ensureGL()
gl := c.gl gl := c.gl
return uniformLocation(gl.Call("getUniformLocation", p.value, location)) return uniformLocation(gl.Call("getUniformLocation", p.value, location))
} }
func (c *Context) uniformInt(p program, location string, v int) { func (c *Context) uniformInt(p program, location string, v int) {
c.ensureGL()
gl := c.gl gl := c.gl
l := c.locationCache.GetUniformLocation(c, p, location) l := c.locationCache.GetUniformLocation(c, p, location)
gl.Call("uniform1i", js.Value(l), v) gl.Call("uniform1i", js.Value(l), v)
} }
func (c *Context) uniformFloat(p program, location string, v float32) { func (c *Context) uniformFloat(p program, location string, v float32) {
c.ensureGL()
gl := c.gl gl := c.gl
l := c.locationCache.GetUniformLocation(c, p, location) l := c.locationCache.GetUniformLocation(c, p, location)
gl.Call("uniform1f", js.Value(l), v) gl.Call("uniform1f", js.Value(l), v)
@ -366,6 +391,7 @@ var (
) )
func (c *Context) uniformFloats(p program, location string, v []float32) { func (c *Context) uniformFloats(p program, location string, v []float32) {
c.ensureGL()
gl := c.gl gl := c.gl
l := c.locationCache.GetUniformLocation(c, p, location) l := c.locationCache.GetUniformLocation(c, p, location)
switch len(v) { 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 { func (c *Context) getAttribLocationImpl(p program, location string) attribLocation {
c.ensureGL()
gl := c.gl gl := c.gl
return attribLocation(gl.Call("getAttribLocation", p.value, location).Int()) 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) { func (c *Context) vertexAttribPointer(p program, location string, size int, dataType DataType, stride int, offset int) {
c.ensureGL()
gl := c.gl gl := c.gl
l := c.locationCache.GetAttribLocation(c, p, location) l := c.locationCache.GetAttribLocation(c, p, location)
gl.Call("vertexAttribPointer", int(l), size, int(dataType), false, stride, offset) gl.Call("vertexAttribPointer", int(l), size, int(dataType), false, stride, offset)
} }
func (c *Context) enableVertexAttribArray(p program, location string) { func (c *Context) enableVertexAttribArray(p program, location string) {
c.ensureGL()
gl := c.gl gl := c.gl
l := c.locationCache.GetAttribLocation(c, p, location) l := c.locationCache.GetAttribLocation(c, p, location)
gl.Call("enableVertexAttribArray", int(l)) gl.Call("enableVertexAttribArray", int(l))
} }
func (c *Context) disableVertexAttribArray(p program, location string) { func (c *Context) disableVertexAttribArray(p program, location string) {
c.ensureGL()
gl := c.gl gl := c.gl
l := c.locationCache.GetAttribLocation(c, p, location) l := c.locationCache.GetAttribLocation(c, p, location)
gl.Call("disableVertexAttribArray", int(l)) gl.Call("disableVertexAttribArray", int(l))
} }
func (c *Context) newArrayBuffer(size int) buffer { func (c *Context) newArrayBuffer(size int) buffer {
c.ensureGL()
gl := c.gl gl := c.gl
b := gl.Call("createBuffer") b := gl.Call("createBuffer")
gl.Call("bindBuffer", int(arrayBuffer), js.Value(b)) 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 { func (c *Context) newElementArrayBuffer(size int) buffer {
c.ensureGL()
gl := c.gl gl := c.gl
b := gl.Call("createBuffer") b := gl.Call("createBuffer")
gl.Call("bindBuffer", int(elementArrayBuffer), js.Value(b)) 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) { func (c *Context) bindBuffer(bufferType bufferType, b buffer) {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("bindBuffer", int(bufferType), js.Value(b)) gl.Call("bindBuffer", int(bufferType), js.Value(b))
} }
func (c *Context) arrayBufferSubData(data []float32) { func (c *Context) arrayBufferSubData(data []float32) {
c.ensureGL()
gl := c.gl gl := c.gl
arr := js.TypedArrayOf(data) arr := js.TypedArrayOf(data)
gl.Call("bufferSubData", int(arrayBuffer), 0, arr) gl.Call("bufferSubData", int(arrayBuffer), 0, arr)
@ -434,6 +468,7 @@ func (c *Context) arrayBufferSubData(data []float32) {
} }
func (c *Context) elementArrayBufferSubData(data []uint16) { func (c *Context) elementArrayBufferSubData(data []uint16) {
c.ensureGL()
gl := c.gl gl := c.gl
arr := js.TypedArrayOf(data) arr := js.TypedArrayOf(data)
gl.Call("bufferSubData", int(elementArrayBuffer), 0, arr) gl.Call("bufferSubData", int(elementArrayBuffer), 0, arr)
@ -441,26 +476,31 @@ func (c *Context) elementArrayBufferSubData(data []uint16) {
} }
func (c *Context) deleteBuffer(b buffer) { func (c *Context) deleteBuffer(b buffer) {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("deleteBuffer", js.Value(b)) gl.Call("deleteBuffer", js.Value(b))
} }
func (c *Context) DrawElements(len int, offsetInBytes int) { func (c *Context) DrawElements(len int, offsetInBytes int) {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("drawElements", triangles, len, unsignedShort, offsetInBytes) gl.Call("drawElements", triangles, len, unsignedShort, offsetInBytes)
} }
func (c *Context) maxTextureSizeImpl() int { func (c *Context) maxTextureSizeImpl() int {
c.ensureGL()
gl := c.gl gl := c.gl
return gl.Call("getParameter", maxTextureSize).Int() return gl.Call("getParameter", maxTextureSize).Int()
} }
func (c *Context) Flush() { func (c *Context) Flush() {
c.ensureGL()
gl := c.gl gl := c.gl
gl.Call("flush") gl.Call("flush")
} }
func (c *Context) IsContextLost() bool { func (c *Context) IsContextLost() bool {
c.ensureGL()
gl := c.gl gl := c.gl
return gl.Call("isContextLost").Bool() return gl.Call("isContextLost").Bool()
} }

View File

@ -31,6 +31,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/hooks" "github.com/hajimehoshi/ebiten/internal/hooks"
"github.com/hajimehoshi/ebiten/internal/input" "github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/mainthread"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
) )
@ -58,8 +59,6 @@ type userInterface struct {
initWindowDecorated bool initWindowDecorated bool
initIconImages []image.Image initIconImages []image.Image
funcs chan func()
m sync.Mutex m sync.Mutex
} }
@ -102,7 +101,6 @@ func initialize() error {
} }
hideConsoleWindowOnWindows() hideConsoleWindowOnWindows()
currentUI.window = window currentUI.window = window
currentUI.funcs = make(chan func())
currentUI.window.MakeContextCurrent() currentUI.window.MakeContextCurrent()
@ -120,23 +118,13 @@ func initialize() error {
return nil return nil
} }
func RunMainThreadLoop(ch <-chan error) error { func Loop(ch <-chan error) error {
// This must be called on the main thread.
// TODO: Check this is done on the main thread.
currentUI.setRunning(true) currentUI.setRunning(true)
defer func() { if err := mainthread.Loop(ch); err != nil {
currentUI.setRunning(false) return err
}()
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
}
} }
currentUI.setRunning(false)
return nil
} }
func (u *userInterface) isRunning() bool { func (u *userInterface) isRunning() bool {
@ -217,27 +205,12 @@ func (u *userInterface) setInitIconImages(iconImages []image.Image) {
u.m.Unlock() 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) { func ScreenSizeInFullscreen() (int, int) {
u := currentUI u := currentUI
var v *glfw.VidMode var v *glfw.VidMode
s := 0.0 s := 0.0
if u.isRunning() { if u.isRunning() {
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
v = u.currentMonitor().GetVideoMode() v = u.currentMonitor().GetVideoMode()
s = glfwScale() s = glfwScale()
return nil return nil
@ -255,7 +228,7 @@ func SetScreenSize(width, height int) bool {
panic("ui: Run is not called yet") panic("ui: Run is not called yet")
} }
r := false r := false
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
r = u.setScreenSize(width, height, u.scale, u.fullscreen(), u.vsync) r = u.setScreenSize(width, height, u.scale, u.fullscreen(), u.vsync)
return nil return nil
}) })
@ -268,7 +241,7 @@ func SetScreenScale(scale float64) bool {
panic("ui: Run is not called yet") panic("ui: Run is not called yet")
} }
r := false r := false
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
r = u.setScreenSize(u.width, u.height, scale, u.fullscreen(), u.vsync) r = u.setScreenSize(u.width, u.height, scale, u.fullscreen(), u.vsync)
return nil return nil
}) })
@ -281,7 +254,7 @@ func ScreenScale() float64 {
return 0 return 0
} }
s := 0.0 s := 0.0
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
s = u.scale s = u.scale
return nil return nil
}) })
@ -302,7 +275,7 @@ func IsFullscreen() bool {
return u.isInitFullscreen() return u.isInitFullscreen()
} }
b := false b := false
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
b = u.fullscreen() b = u.fullscreen()
return nil return nil
}) })
@ -315,7 +288,7 @@ func SetFullscreen(fullscreen bool) {
u.setInitFullscreen(fullscreen) u.setInitFullscreen(fullscreen)
return return
} }
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
u := currentUI u := currentUI
u.setScreenSize(u.width, u.height, u.scale, fullscreen, u.vsync) u.setScreenSize(u.width, u.height, u.scale, fullscreen, u.vsync)
return nil return nil
@ -342,7 +315,7 @@ func SetVsyncEnabled(enabled bool) {
u.m.Unlock() u.m.Unlock()
return return
} }
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
u := currentUI u := currentUI
u.setScreenSize(u.width, u.height, u.scale, u.fullscreen(), enabled) u.setScreenSize(u.width, u.height, u.scale, u.fullscreen(), enabled)
return nil return nil
@ -361,7 +334,7 @@ func SetWindowTitle(title string) {
if !currentUI.isRunning() { if !currentUI.isRunning() {
return return
} }
_ = currentUI.runOnMainThread(func() error { _ = mainthread.Run(func() error {
currentUI.window.SetTitle(title) currentUI.window.SetTitle(title)
return nil return nil
}) })
@ -372,7 +345,7 @@ func SetWindowIcon(iconImages []image.Image) {
currentUI.setInitIconImages(iconImages) currentUI.setInitIconImages(iconImages)
return return
} }
_ = currentUI.runOnMainThread(func() error { _ = mainthread.Run(func() error {
currentUI.window.SetIcon(iconImages) currentUI.window.SetIcon(iconImages)
return nil return nil
}) })
@ -399,7 +372,7 @@ func ScreenPadding() (x0, y0, x1, y1 float64) {
sx := 0.0 sx := 0.0
sy := 0.0 sy := 0.0
gs := 0.0 gs := 0.0
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
sx = float64(u.width) * u.actualScreenScale() sx = float64(u.width) * u.actualScreenScale()
sy = float64(u.height) * u.actualScreenScale() sy = float64(u.height) * u.actualScreenScale()
gs = glfwScale() gs = glfwScale()
@ -424,7 +397,7 @@ func adjustCursorPosition(x, y int) (int, int) {
} }
ox, oy, _, _ := ScreenPadding() ox, oy, _, _ := ScreenPadding()
s := 0.0 s := 0.0
_ = currentUI.runOnMainThread(func() error { _ = mainthread.Run(func() error {
s = currentUI.actualScreenScale() s = currentUI.actualScreenScale()
return nil return nil
}) })
@ -442,7 +415,7 @@ func IsCursorVisible() bool {
return u.isInitCursorVisible() return u.isInitCursorVisible()
} }
v := false v := false
_ = currentUI.runOnMainThread(func() error { _ = mainthread.Run(func() error {
v = currentUI.window.GetInputMode(glfw.CursorMode) == glfw.CursorNormal v = currentUI.window.GetInputMode(glfw.CursorMode) == glfw.CursorNormal
return nil return nil
}) })
@ -455,7 +428,7 @@ func SetCursorVisible(visible bool) {
u.setInitCursorVisible(visible) u.setInitCursorVisible(visible)
return return
} }
_ = currentUI.runOnMainThread(func() error { _ = mainthread.Run(func() error {
c := glfw.CursorNormal c := glfw.CursorNormal
if !visible { if !visible {
c = glfw.CursorHidden c = glfw.CursorHidden
@ -471,7 +444,7 @@ func IsWindowDecorated() bool {
return u.isInitWindowDecorated() return u.isInitWindowDecorated()
} }
v := false v := false
_ = currentUI.runOnMainThread(func() error { _ = mainthread.Run(func() error {
v = currentUI.window.GetAttrib(glfw.Decorated) == glfw.True v = currentUI.window.GetAttrib(glfw.Decorated) == glfw.True
return nil return nil
}) })
@ -490,7 +463,7 @@ func SetWindowDecorated(decorated bool) {
// TODO: Now SetAttrib doesn't exist on GLFW 3.2. Revisit later (#556). // TODO: Now SetAttrib doesn't exist on GLFW 3.2. Revisit later (#556).
// If SetAttrib exists, the implementation would be: // If SetAttrib exists, the implementation would be:
// //
// _ = currentUI.runOnMainThread(func() error { // _ = mainthread.Run(func() error {
// v := glfw.False // v := glfw.False
// if decorated { // if decorated {
// v = glfw.True // v = glfw.True
@ -506,7 +479,7 @@ func DeviceScaleFactor() float64 {
return devicescale.GetAt(u.currentMonitor().GetPos()) return devicescale.GetAt(u.currentMonitor().GetPos())
} }
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
m := u.currentMonitor() m := u.currentMonitor()
f = devicescale.GetAt(m.GetPos()) f = devicescale.GetAt(m.GetPos())
return nil return nil
@ -516,10 +489,7 @@ func DeviceScaleFactor() float64 {
func Run(width, height int, scale float64, title string, g GraphicsContext, mainloop bool) error { func Run(width, height int, scale float64, title string, g GraphicsContext, mainloop bool) error {
u := currentUI u := currentUI
// GLContext must be created before setting the screen size, which requires _ = mainthread.Run(func() error {
// swapping buffers.
opengl.Init(currentUI.runOnMainThread)
_ = u.runOnMainThread(func() error {
// Get the monitor before showing the window. // Get the monitor before showing the window.
// //
// On Windows, there are two types of windows: // On Windows, there are two types of windows:
@ -596,7 +566,7 @@ func (u *userInterface) updateGraphicsContext(g GraphicsContext) {
actualScale := 0.0 actualScale := 0.0
sizeChanged := false sizeChanged := false
// TODO: Is it possible to reduce 'runOnMainThread' calls? // TODO: Is it possible to reduce 'runOnMainThread' calls?
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
actualScale = u.actualScreenScale() actualScale = u.actualScreenScale()
if u.lastActualScale != actualScale { if u.lastActualScale != actualScale {
u.forceSetScreenSize(u.width, u.height, u.scale, u.fullscreen(), u.vsync) 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 { func (u *userInterface) update(g GraphicsContext) error {
shouldClose := false shouldClose := false
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
shouldClose = u.window.ShouldClose() shouldClose = u.window.ShouldClose()
return nil return nil
}) })
@ -626,7 +596,7 @@ func (u *userInterface) update(g GraphicsContext) error {
return RegularTermination return RegularTermination
} }
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
if u.isInitFullscreen() { if u.isInitFullscreen() {
u := currentUI u := currentUI
u.setScreenSize(u.width, u.height, u.scale, true, u.vsync) 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. // This call is needed for initialization.
u.updateGraphicsContext(g) u.updateGraphicsContext(g)
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
u.pollEvents() u.pollEvents()
defer hooks.ResumeAudio() defer hooks.ResumeAudio()
for !u.isRunnableInBackground() && u.window.GetAttrib(glfw.Focused) == 0 { 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 { func (u *userInterface) loop(g GraphicsContext) error {
defer func() { defer func() {
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
glfw.Terminate() glfw.Terminate()
return nil return nil
}) })
@ -683,7 +653,7 @@ func (u *userInterface) loop(g GraphicsContext) error {
// before swapping buffers. // before swapping buffers.
opengl.GetContext().BeforeSwapping() opengl.GetContext().BeforeSwapping()
_ = u.runOnMainThread(func() error { _ = mainthread.Run(func() error {
if !vsync { if !vsync {
u.swapBuffers() u.swapBuffers()
return nil return nil

View File

@ -357,7 +357,7 @@ func init() {
})) }))
} }
func RunMainThreadLoop(ch <-chan error) error { func Loop(ch <-chan error) error {
return <-ch return <-ch
} }
@ -366,9 +366,6 @@ func Run(width, height int, scale float64, title string, g GraphicsContext, main
document.Set("title", title) document.Set("title", title)
u.setScreenSize(width, height, scale, u.fullscreen) u.setScreenSize(width, height, scale, u.fullscreen)
canvas.Call("focus") canvas.Call("focus")
if err := opengl.Init(); err != nil {
return err
}
return u.loop(g) return u.loop(g)
} }

View File

@ -166,11 +166,10 @@ func Run(width, height int, scale float64, title string, g GraphicsContext, main
} }
} }
// RunMainThreadLoop runs the main routine for gomobile-build. // Loop runs the main routine for gomobile-build.
func RunMainThreadLoop(ch <-chan error) error { func Loop(ch <-chan error) error {
go func() { go func() {
// As mobile apps never ends, RunMainThreadLoop can't return. // As mobile apps never ends, Loop can't return. Just panic here.
// Just panic here.
err := <-ch err := <-ch
panic(err) panic(err)
}() }()

2
run.go
View File

@ -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? // TODO: Use context in Go 1.7?
if err := ui.RunMainThreadLoop(ch); err != nil { if err := ui.Loop(ch); err != nil {
return err return err
} }
return nil return nil