From 8967384dacc4539505552bbb453a9123a859e5f4 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 1 Aug 2021 21:50:21 +0900 Subject: [PATCH] internal/graphicsdriver: Bug fix: getting a WebGL2 context might fail even though WebGL2RenderingContext exists Closes #1738 --- internal/graphicsdriver/opengl/context_js.go | 62 +++++++++++++------ internal/graphicsdriver/opengl/gl_js.go | 4 +- internal/graphicsdriver/opengl/shader.go | 2 +- .../graphicsdriver/opengl/shader_desktop.go | 2 +- internal/graphicsdriver/opengl/shader_js.go | 9 ++- .../graphicsdriver/opengl/shader_mobile.go | 2 +- 6 files changed, 55 insertions(+), 26 deletions(-) diff --git a/internal/graphicsdriver/opengl/context_js.go b/internal/graphicsdriver/opengl/context_js.go index c6fb58aa3..7ddc5f957 100644 --- a/internal/graphicsdriver/opengl/context_js.go +++ b/internal/graphicsdriver/opengl/context_js.go @@ -87,16 +87,31 @@ const ( dstColor = operation(gles.DST_COLOR) ) +type webGLVersion int + +const ( + webGLVersionUnknown webGLVersion = iota + webGLVersion1 + webGLVersion2 +) + var ( - isWebGL2Available = !forceWebGL1 && (js.Global().Get("WebGL2RenderingContext").Truthy() || js.Global().Get("go2cpp").Truthy()) + webGL2MightBeAvailable = !forceWebGL1 && (js.Global().Get("WebGL2RenderingContext").Truthy() || js.Global().Get("go2cpp").Truthy()) ) type contextImpl struct { gl *gl lastProgramID programID + webGLVersion webGLVersion +} + +func (c *context) usesWebGL2() bool { + return c.webGLVersion == webGLVersion2 } func (c *context) initGL() { + c.webGLVersion = webGLVersionUnknown + var gl js.Value // TODO: Define id? @@ -107,22 +122,33 @@ func (c *context) initGL() { attr.Set("premultipliedAlpha", true) attr.Set("stencil", true) - if isWebGL2Available { + if webGL2MightBeAvailable { gl = canvas.Call("getContext", "webgl2", attr) - } else { + if gl.Truthy() { + c.webGLVersion = webGLVersion2 + } + } + + // Even though WebGL2RenderingContext exists, getting a webgl2 context might fail (#1738). + if !gl.Truthy() { gl = canvas.Call("getContext", "webgl", attr) if !gl.Truthy() { gl = canvas.Call("getContext", "experimental-webgl", attr) - if !gl.Truthy() { - panic("opengl: getContext failed") - } } + if gl.Truthy() { + c.webGLVersion = webGLVersion1 + } + } + + if !gl.Truthy() { + panic("opengl: getContext failed") } } else if go2cpp := js.Global().Get("go2cpp"); go2cpp.Truthy() { gl = go2cpp.Get("gl") + c.webGLVersion = webGLVersion2 } - c.gl = newGL(gl) + c.gl = c.newGL(gl) } func (c *context) reset() error { @@ -145,7 +171,7 @@ func (c *context) reset() error { f := gl.getParameter.Invoke(gles.FRAMEBUFFER_BINDING) c.screenFramebuffer = framebufferNative(f) - if !isWebGL2Available { + if !c.usesWebGL2() { gl.getExtension.Invoke("OES_standard_derivatives") } return nil @@ -435,43 +461,43 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha switch base { case shaderir.Float: - if isWebGL2Available { + if c.usesWebGL2() { gl.uniform1fv.Invoke(js.Value(l), arr, 0, len(v)) } else { gl.uniform1fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) } case shaderir.Vec2: - if isWebGL2Available { + if c.usesWebGL2() { gl.uniform2fv.Invoke(js.Value(l), arr, 0, len(v)) } else { gl.uniform2fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) } case shaderir.Vec3: - if isWebGL2Available { + if c.usesWebGL2() { gl.uniform3fv.Invoke(js.Value(l), arr, 0, len(v)) } else { gl.uniform3fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) } case shaderir.Vec4: - if isWebGL2Available { + if c.usesWebGL2() { gl.uniform4fv.Invoke(js.Value(l), arr, 0, len(v)) } else { gl.uniform4fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) } case shaderir.Mat2: - if isWebGL2Available { + if c.usesWebGL2() { gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr, 0, len(v)) } else { gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v))) } case shaderir.Mat3: - if isWebGL2Available { + if c.usesWebGL2() { gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr, 0, len(v)) } else { gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v))) } case shaderir.Mat4: - if isWebGL2Available { + if c.usesWebGL2() { gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr, 0, len(v)) } else { gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v))) @@ -528,7 +554,7 @@ func (c *context) arrayBufferSubData(data []float32) { gl := c.gl l := len(data) * 4 arr := jsutil.TemporaryUint8Array(l, data) - if isWebGL2Available { + if c.usesWebGL2() { gl.bufferSubData.Invoke(gles.ARRAY_BUFFER, 0, arr, 0, l) } else { gl.bufferSubData.Invoke(gles.ARRAY_BUFFER, 0, arr.Call("subarray", 0, l)) @@ -539,7 +565,7 @@ func (c *context) elementArrayBufferSubData(data []uint16) { gl := c.gl l := len(data) * 2 arr := jsutil.TemporaryUint8Array(l, data) - if isWebGL2Available { + if c.usesWebGL2() { gl.bufferSubData.Invoke(gles.ELEMENT_ARRAY_BUFFER, 0, arr, 0, l) } else { gl.bufferSubData.Invoke(gles.ELEMENT_ARRAY_BUFFER, 0, arr.Call("subarray", 0, l)) @@ -585,7 +611,7 @@ func (c *context) texSubImage2D(t textureNative, args []*driver.ReplacePixelsArg gl := c.gl for _, a := range args { arr := jsutil.TemporaryUint8Array(len(a.Pixels), a.Pixels) - if isWebGL2Available { + if c.usesWebGL2() { // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, // GLsizei width, GLsizei height, // GLenum format, GLenum type, ArrayBufferView pixels, srcOffset); diff --git a/internal/graphicsdriver/opengl/gl_js.go b/internal/graphicsdriver/opengl/gl_js.go index 0f758ff6e..cf703e970 100644 --- a/internal/graphicsdriver/opengl/gl_js.go +++ b/internal/graphicsdriver/opengl/gl_js.go @@ -93,7 +93,7 @@ type gl struct { viewport js.Value } -func newGL(v js.Value) *gl { +func (c *context) newGL(v js.Value) *gl { // Passing a Go string to the JS world is expensive. This causes conversion to UTF-16 (#1438). // In order to reduce the cost when calling functions, create the function objects by bind and use them. g := &gl{ @@ -168,7 +168,7 @@ func newGL(v js.Value) *gl { vertexAttribPointer: v.Get("vertexAttribPointer").Call("bind", v), viewport: v.Get("viewport").Call("bind", v), } - if isWebGL2Available { + if c.usesWebGL2() { g.getExtension = v.Get("getBufferSubData").Call("bind", v) } else { g.getExtension = v.Get("getExtension").Call("bind", v) diff --git a/internal/graphicsdriver/opengl/shader.go b/internal/graphicsdriver/opengl/shader.go index 0ecb82c85..ba81ac8ee 100644 --- a/internal/graphicsdriver/opengl/shader.go +++ b/internal/graphicsdriver/opengl/shader.go @@ -52,7 +52,7 @@ func (s *Shader) Dispose() { } func (s *Shader) compile() error { - vssrc, fssrc := glsl.Compile(s.ir, glslVersion()) + vssrc, fssrc := glsl.Compile(s.ir, s.graphics.context.glslVersion()) vs, err := s.graphics.context.newVertexShader(vssrc) if err != nil { diff --git a/internal/graphicsdriver/opengl/shader_desktop.go b/internal/graphicsdriver/opengl/shader_desktop.go index 1efd760df..61e346d4e 100644 --- a/internal/graphicsdriver/opengl/shader_desktop.go +++ b/internal/graphicsdriver/opengl/shader_desktop.go @@ -21,6 +21,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/shaderir/glsl" ) -func glslVersion() glsl.GLSLVersion { +func (c *context) glslVersion() glsl.GLSLVersion { return glsl.GLSLVersionDefault } diff --git a/internal/graphicsdriver/opengl/shader_js.go b/internal/graphicsdriver/opengl/shader_js.go index 654ab180a..87912a996 100644 --- a/internal/graphicsdriver/opengl/shader_js.go +++ b/internal/graphicsdriver/opengl/shader_js.go @@ -18,9 +18,12 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/shaderir/glsl" ) -func glslVersion() glsl.GLSLVersion { - if isWebGL2Available { +func (c *context) glslVersion() glsl.GLSLVersion { + switch c.webGLVersion { + case webGLVersion1: + return glsl.GLSLVersionES100 + case webGLVersion2: return glsl.GLSLVersionES300 } - return glsl.GLSLVersionES100 + panic("opengl: WebGL context is not initialized yet at glslVersion") } diff --git a/internal/graphicsdriver/opengl/shader_mobile.go b/internal/graphicsdriver/opengl/shader_mobile.go index 89843c703..d75482918 100644 --- a/internal/graphicsdriver/opengl/shader_mobile.go +++ b/internal/graphicsdriver/opengl/shader_mobile.go @@ -21,6 +21,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/shaderir/glsl" ) -func glslVersion() glsl.GLSLVersion { +func (c *context) glslVersion() glsl.GLSLVersion { return glsl.GLSLVersionES100 }