Enable to compile Ebiten on js/wasm with Go 1.14

Fixes #1024
This commit is contained in:
Hajime Hoshi 2019-12-19 00:03:35 +09:00
parent 216eacb0ba
commit 85cbc7e56b
13 changed files with 151 additions and 27 deletions

2
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/hajimehoshi/bitmapfont v1.2.0 github.com/hajimehoshi/bitmapfont v1.2.0
github.com/hajimehoshi/go-mp3 v0.2.1 github.com/hajimehoshi/go-mp3 v0.2.1
github.com/hajimehoshi/oto v0.5.3 github.com/hajimehoshi/oto v0.5.4-0.20191218145825-0644fbe4140c
github.com/jakecoffman/cp v0.1.0 github.com/jakecoffman/cp v0.1.0
github.com/jfreymuth/oggvorbis v1.0.0 github.com/jfreymuth/oggvorbis v1.0.0
github.com/jfreymuth/vorbis v1.0.0 // indirect github.com/jfreymuth/vorbis v1.0.0 // indirect

4
go.sum
View File

@ -10,8 +10,8 @@ github.com/hajimehoshi/bitmapfont v1.2.0/go.mod h1:h9QrPk6Ktb2neObTlAbma6Ini1xgM
github.com/hajimehoshi/go-mp3 v0.2.1 h1:DH4ns3cPv39n3cs8MPcAlWqPeAwLCK8iNgqvg0QBWI8= github.com/hajimehoshi/go-mp3 v0.2.1 h1:DH4ns3cPv39n3cs8MPcAlWqPeAwLCK8iNgqvg0QBWI8=
github.com/hajimehoshi/go-mp3 v0.2.1/go.mod h1:Rr+2P46iH6PwTPVgSsEwBkon0CK5DxCAeX/Rp65DCTE= github.com/hajimehoshi/go-mp3 v0.2.1/go.mod h1:Rr+2P46iH6PwTPVgSsEwBkon0CK5DxCAeX/Rp65DCTE=
github.com/hajimehoshi/oto v0.3.4/go.mod h1:PgjqsBJff0efqL2nlMJidJgVJywLn6M4y8PI4TfeWfA= github.com/hajimehoshi/oto v0.3.4/go.mod h1:PgjqsBJff0efqL2nlMJidJgVJywLn6M4y8PI4TfeWfA=
github.com/hajimehoshi/oto v0.5.3 h1:IccIFFUkT0oAr/KdH3LH7USyvI1ZE84HXiGtuCjs3y0= github.com/hajimehoshi/oto v0.5.4-0.20191218145825-0644fbe4140c h1:Hr7DqgpqLqfeStmF2aTHbWuZGO3/J64Xq2crXC069v8=
github.com/hajimehoshi/oto v0.5.3/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= github.com/hajimehoshi/oto v0.5.4-0.20191218145825-0644fbe4140c/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/jakecoffman/cp v0.1.0 h1:sgSYEGUgfwiT447fRjloa2c5b6UyYP+7muR3gQK+Ep0= github.com/jakecoffman/cp v0.1.0 h1:sgSYEGUgfwiT447fRjloa2c5b6UyYP+7muR3gQK+Ep0=
github.com/jakecoffman/cp v0.1.0/go.mod h1:a3xPx9N8RyFAACD644t2dj/nK4SuLg1v+jL61m2yVo4= github.com/jakecoffman/cp v0.1.0/go.mod h1:a3xPx9N8RyFAACD644t2dj/nK4SuLg1v+jL61m2yVo4=
github.com/jfreymuth/oggvorbis v1.0.0 h1:aOpiihGrFLXpsh2osOlEvTcg5/aluzGQeC7m3uYWOZ0= github.com/jfreymuth/oggvorbis v1.0.0 h1:aOpiihGrFLXpsh2osOlEvTcg5/aluzGQeC7m3uYWOZ0=

View File

@ -60,7 +60,7 @@ type context struct {
} }
func (c *context) bindTexture(t textureNative) { func (c *context) bindTexture(t textureNative) {
if c.lastTexture == t { if c.lastTexture.equal(t) {
return return
} }
c.bindTextureImpl(t) c.bindTextureImpl(t)
@ -68,7 +68,7 @@ func (c *context) bindTexture(t textureNative) {
} }
func (c *context) bindFramebuffer(f framebufferNative) { func (c *context) bindFramebuffer(f framebufferNative) {
if c.lastFramebuffer == f { if c.lastFramebuffer.equal(f) {
return return
} }
c.bindFramebufferImpl(f) c.bindFramebufferImpl(f)
@ -86,7 +86,7 @@ func (c *context) setViewport(f *framebuffer) {
// glViewport must be called at least at every frame on iOS. // glViewport must be called at least at every frame on iOS.
// As the screen framebuffer is the last render target, next SetViewport should be // As the screen framebuffer is the last render target, next SetViewport should be
// the first call at a frame. // the first call at a frame.
if f.native == c.screenFramebuffer { if f.native.equal(c.screenFramebuffer) {
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
} else { } else {

View File

@ -35,6 +35,30 @@ type (
buffer uint32 buffer uint32
) )
func (t textureNative) equal(rhs textureNative) bool {
return t == rhs
}
func (f framebufferNative) equal(rhs framebufferNative) bool {
return f == rhs
}
func (s shader) equal(rhs shader) bool {
return s == rhs
}
func (b buffer) equal(rhs buffer) bool {
return b == rhs
}
func (u uniformLocation) equal(rhs uniformLocation) bool {
return u == rhs
}
func (p program) equal(rhs program) bool {
return p == rhs
}
var InvalidTexture textureNative var InvalidTexture textureNative
type ( type (

View File

@ -41,6 +41,30 @@ type (
} }
) )
func (t textureNative) equal(rhs textureNative) bool {
return jsutil.Equal(js.Value(t), js.Value(rhs))
}
func (f framebufferNative) equal(rhs framebufferNative) bool {
return jsutil.Equal(js.Value(f), js.Value(rhs))
}
func (s shader) equal(rhs shader) bool {
return jsutil.Equal(js.Value(s), js.Value(rhs))
}
func (b buffer) equal(rhs buffer) bool {
return jsutil.Equal(js.Value(b), js.Value(rhs))
}
func (u uniformLocation) equal(rhs uniformLocation) bool {
return jsutil.Equal(js.Value(u), js.Value(rhs))
}
func (p program) equal(rhs program) bool {
return jsutil.Equal(p.value, rhs.value) && p.id == rhs.id
}
var InvalidTexture = textureNative(js.Null()) var InvalidTexture = textureNative(js.Null())
func getProgramID(p program) programID { func getProgramID(p program) programID {
@ -96,11 +120,11 @@ type contextImpl struct {
} }
func (c *context) ensureGL() { func (c *context) ensureGL() {
if c.gl != (js.Value{}) { if !jsutil.Equal(c.gl, js.Value{}) {
return return
} }
if js.Global().Get("WebGLRenderingContext") == js.Undefined() { if jsutil.Equal(js.Global().Get("WebGLRenderingContext"), js.Undefined()) {
panic("opengl: WebGL is not supported") panic("opengl: WebGL is not supported")
} }
// TODO: Define id? // TODO: Define id?
@ -109,9 +133,9 @@ func (c *context) ensureGL() {
attr.Set("alpha", true) attr.Set("alpha", true)
attr.Set("premultipliedAlpha", true) attr.Set("premultipliedAlpha", true)
gl := canvas.Call("getContext", "webgl", attr) gl := canvas.Call("getContext", "webgl", attr)
if gl == js.Null() { if jsutil.Equal(gl, js.Null()) {
gl = canvas.Call("getContext", "experimental-webgl", attr) gl = canvas.Call("getContext", "experimental-webgl", attr)
if gl == js.Null() { if jsutil.Equal(gl, js.Null()) {
panic("opengl: getContext failed") panic("opengl: getContext failed")
} }
} }
@ -156,7 +180,7 @@ func (c *context) newTexture(width, height int) (textureNative, error) {
c.ensureGL() c.ensureGL()
gl := c.gl gl := c.gl
t := gl.Call("createTexture") t := gl.Call("createTexture")
if t == js.Null() { if jsutil.Equal(t, js.Null()) {
return textureNative(js.Null()), errors.New("opengl: glGenTexture failed") return textureNative(js.Null()), errors.New("opengl: glGenTexture failed")
} }
gl.Call("pixelStorei", unpackAlignment, 4) gl.Call("pixelStorei", unpackAlignment, 4)
@ -209,7 +233,7 @@ func (c *context) deleteTexture(t textureNative) {
if !gl.Call("isTexture", js.Value(t)).Bool() { if !gl.Call("isTexture", js.Value(t)).Bool() {
return return
} }
if c.lastTexture == t { if c.lastTexture.equal(t) {
c.lastTexture = textureNative(js.Null()) c.lastTexture = textureNative(js.Null())
} }
gl.Call("deleteTexture", js.Value(t)) gl.Call("deleteTexture", js.Value(t))
@ -262,7 +286,7 @@ func (c *context) deleteFramebuffer(f framebufferNative) {
// If a framebuffer to be deleted is bound, a newly bound framebuffer // If a framebuffer to be deleted is bound, a newly bound framebuffer
// will be a default framebuffer. // will be a default framebuffer.
// https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffers.xml // https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffers.xml
if c.lastFramebuffer == f { if c.lastFramebuffer.equal(f) {
c.lastFramebuffer = framebufferNative(js.Null()) c.lastFramebuffer = framebufferNative(js.Null())
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
@ -274,7 +298,7 @@ func (c *context) newShader(shaderType shaderType, source string) (shader, error
c.ensureGL() 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 jsutil.Equal(s, js.Null()) {
return shader(js.Null()), fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) return shader(js.Null()), fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
} }
@ -298,7 +322,7 @@ func (c *context) newProgram(shaders []shader, attributes []string) (program, er
c.ensureGL() c.ensureGL()
gl := c.gl gl := c.gl
v := gl.Call("createProgram") v := gl.Call("createProgram")
if v == js.Null() { if jsutil.Equal(v, js.Null()) {
return program{}, errors.New("opengl: glCreateProgram failed") return program{}, errors.New("opengl: glCreateProgram failed")
} }

View File

@ -33,6 +33,26 @@ type (
buffer mgl.Buffer buffer mgl.Buffer
) )
func (t textureNative) equal(rhs textureNative) bool {
return t == rhs
}
func (f framebufferNative) equal(rhs framebufferNative) bool {
return f == rhs
}
func (s shader) equal(rhs shader) bool {
return s == rhs
}
func (b buffer) equal(rhs buffer) bool {
return b == rhs
}
func (p program) equal(rhs program) bool {
return p == rhs
}
var InvalidTexture textureNative var InvalidTexture textureNative
type ( type (
@ -40,6 +60,10 @@ type (
attribLocation mgl.Attrib attribLocation mgl.Attrib
) )
func (u uniformLocation) equal(rhs uniformLocation) bool {
return u == rhs
}
type programID uint32 type programID uint32
var ( var (

View File

@ -45,7 +45,7 @@ func newScreenFramebuffer(context *context, width, height int) *framebuffer {
} }
func (f *framebuffer) delete(context *context) { func (f *framebuffer) delete(context *context) {
if f.native != context.getScreenFramebuffer() { if !f.native.equal(context.getScreenFramebuffer()) {
context.deleteFramebuffer(f.native) context.deleteFramebuffer(f.native)
} }
} }

View File

@ -34,13 +34,13 @@ func (i *Image) IsInvalidated() bool {
} }
func (i *Image) Dispose() { func (i *Image) Dispose() {
if i.pbo != *new(buffer) { if !i.pbo.equal(*new(buffer)) {
i.driver.context.deleteBuffer(i.pbo) i.driver.context.deleteBuffer(i.pbo)
} }
if i.framebuffer != nil { if i.framebuffer != nil {
i.framebuffer.delete(&i.driver.context) i.framebuffer.delete(&i.driver.context)
} }
if i.textureNative != *new(textureNative) { if !i.textureNative.equal(*new(textureNative)) {
i.driver.context.deleteTexture(i.textureNative) i.driver.context.deleteTexture(i.textureNative)
} }
} }

View File

@ -183,10 +183,10 @@ func (s *openGLState) reset(context *context) error {
// On browsers (at least Chrome), buffers are already detached from the context // On browsers (at least Chrome), buffers are already detached from the context
// and must not be deleted by DeleteBuffer. // and must not be deleted by DeleteBuffer.
if !web.IsBrowser() { if !web.IsBrowser() {
if s.arrayBuffer != zeroBuffer { if !s.arrayBuffer.equal(zeroBuffer) {
context.deleteBuffer(s.arrayBuffer) context.deleteBuffer(s.arrayBuffer)
} }
if s.elementArrayBuffer != zeroBuffer { if !s.elementArrayBuffer.equal(zeroBuffer) {
context.deleteBuffer(s.elementArrayBuffer) context.deleteBuffer(s.elementArrayBuffer)
} }
} }
@ -278,9 +278,9 @@ func (d *Driver) useProgram(mode driver.CompositeMode, colorM *affine.ColorM, fi
filter: filter, filter: filter,
address: address, address: address,
}] }]
if d.state.lastProgram != program { if !d.state.lastProgram.equal(program) {
d.context.useProgram(program) d.context.useProgram(program)
if d.state.lastProgram == zeroProgram { if d.state.lastProgram.equal(zeroProgram) {
theArrayBufferLayout.enable(&d.context, program) theArrayBufferLayout.enable(&d.context, program)
d.context.bindBuffer(arrayBuffer, d.state.arrayBuffer) d.context.bindBuffer(arrayBuffer, d.state.arrayBuffer)
d.context.bindBuffer(elementArrayBuffer, d.state.elementArrayBuffer) d.context.bindBuffer(elementArrayBuffer, d.state.elementArrayBuffer)

View File

@ -0,0 +1,25 @@
// Copyright 2019 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.
// +build !go1.14
package jsutil
import (
"syscall/js"
)
func Equal(a, b js.Value) bool {
return a == b
}

View File

@ -0,0 +1,25 @@
// Copyright 2019 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.
// +build go1.14
package jsutil
import (
"syscall/js"
)
func Equal(a, b js.Value) bool {
return a.Equal(b)
}

View File

@ -21,6 +21,7 @@ import (
"unicode" "unicode"
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/jsutil"
) )
type pos struct { type pos struct {
@ -220,7 +221,7 @@ func (i *Input) setMouseCursor(x, y int) {
func (i *Input) UpdateGamepads() { func (i *Input) UpdateGamepads() {
nav := js.Global().Get("navigator") nav := js.Global().Get("navigator")
if nav.Get("getGamepads") == js.Undefined() { if jsutil.Equal(nav.Get("getGamepads"), js.Undefined()) {
return return
} }
gamepads := nav.Call("getGamepads") gamepads := nav.Call("getGamepads")
@ -228,7 +229,7 @@ func (i *Input) UpdateGamepads() {
for id := 0; id < l; id++ { for id := 0; id < l; id++ {
i.gamepads[id].valid = false i.gamepads[id].valid = false
gamepad := gamepads.Index(id) gamepad := gamepads.Index(id)
if gamepad == js.Undefined() || gamepad == js.Null() { if jsutil.Equal(gamepad, js.Undefined()) || jsutil.Equal(gamepad, js.Null()) {
continue continue
} }
i.gamepads[id].valid = true i.gamepads[id].valid = true
@ -261,7 +262,7 @@ func (i *Input) Update(e js.Value) {
switch e.Get("type").String() { switch e.Get("type").String() {
case "keydown": case "keydown":
c := e.Get("code") c := e.Get("code")
if c == js.Undefined() { if jsutil.Equal(c, js.Undefined()) {
code := e.Get("keyCode").Int() code := e.Get("keyCode").Int()
if keyCodeToKeyEdge[code] == driver.KeyUp || if keyCodeToKeyEdge[code] == driver.KeyUp ||
keyCodeToKeyEdge[code] == driver.KeyDown || keyCodeToKeyEdge[code] == driver.KeyDown ||
@ -289,7 +290,7 @@ func (i *Input) Update(e js.Value) {
i.runeBuffer = append(i.runeBuffer, r) i.runeBuffer = append(i.runeBuffer, r)
} }
case "keyup": case "keyup":
if e.Get("code") == js.Undefined() { if jsutil.Equal(e.Get("code"), js.Undefined()) {
// Assume that UA is Edge. // Assume that UA is Edge.
code := e.Get("keyCode").Int() code := e.Get("keyCode").Int()
i.keyUpEdge(code) i.keyUpEdge(code)

View File

@ -26,6 +26,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/hooks" "github.com/hajimehoshi/ebiten/internal/hooks"
"github.com/hajimehoshi/ebiten/internal/jsutil"
) )
type UserInterface struct { type UserInterface struct {
@ -247,7 +248,7 @@ func (u *UserInterface) loop(context driver.UIContext) <-chan error {
} }
func init() { func init() {
if document.Get("body") == js.Null() { if jsutil.Equal(document.Get("body"), js.Null()) {
ch := make(chan struct{}) ch := make(chan struct{})
window.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} { window.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
close(ch) close(ch)