From 74d95153457051b87aeedceb1e8902766801453a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 16 Nov 2022 01:07:16 +0900 Subject: [PATCH] internal/graphicsdriver/opengl: refactoring: move gl_js to gl/context_js.go This unifies the OpenGL context interface for all the platforms. --- internal/graphicsdriver/opengl/context.go | 8 +- internal/graphicsdriver/opengl/context_js.go | 428 +++++------- .../graphicsdriver/opengl/context_notjs.go | 45 +- internal/graphicsdriver/opengl/framebuffer.go | 2 +- .../graphicsdriver/opengl/gl/default_js.go | 645 ++++++++++++++++++ internal/graphicsdriver/opengl/gl_js.go | 174 ----- internal/graphicsdriver/opengl/image.go | 6 +- .../graphicsdriver/opengl/locationcache.go | 17 +- internal/graphicsdriver/opengl/program.go | 19 +- 9 files changed, 844 insertions(+), 500 deletions(-) create mode 100644 internal/graphicsdriver/opengl/gl/default_js.go delete mode 100644 internal/graphicsdriver/opengl/gl_js.go diff --git a/internal/graphicsdriver/opengl/context.go b/internal/graphicsdriver/opengl/context.go index 96b80cf3e..93707e494 100644 --- a/internal/graphicsdriver/opengl/context.go +++ b/internal/graphicsdriver/opengl/context.go @@ -105,7 +105,7 @@ type context struct { } func (c *context) bindTexture(t textureNative) { - if c.lastTexture.equal(t) { + if c.lastTexture == t { return } c.bindTextureImpl(t) @@ -113,7 +113,7 @@ func (c *context) bindTexture(t textureNative) { } func (c *context) bindRenderbuffer(r renderbufferNative) { - if c.lastRenderbuffer.equal(r) { + if c.lastRenderbuffer == r { return } c.bindRenderbufferImpl(r) @@ -121,7 +121,7 @@ func (c *context) bindRenderbuffer(r renderbufferNative) { } func (c *context) bindFramebuffer(f framebufferNative) { - if c.lastFramebuffer.equal(f) { + if c.lastFramebuffer == f { return } c.bindFramebufferImpl(f) @@ -139,7 +139,7 @@ func (c *context) setViewport(f *framebuffer) { // glViewport must be called at least at every frame on iOS. // As the screen framebuffer is the last render target, next SetViewport should be // the first call at a frame. - if f.native.equal(c.screenFramebuffer) { + if f.native == c.screenFramebuffer { c.lastViewportWidth = 0 c.lastViewportHeight = 0 } else { diff --git a/internal/graphicsdriver/opengl/context_js.go b/internal/graphicsdriver/opengl/context_js.go index ac1bbb5cd..f613ce5cc 100644 --- a/internal/graphicsdriver/opengl/context_js.go +++ b/internal/graphicsdriver/opengl/context_js.go @@ -20,62 +20,31 @@ import ( "os" "strings" "syscall/js" + "unsafe" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl" - "github.com/hajimehoshi/ebiten/v2/internal/jsutil" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" ) type ( - textureNative js.Value - renderbufferNative js.Value - framebufferNative js.Value - shader js.Value - buffer js.Value - uniformLocation js.Value - - attribLocation int - programID int - program struct { - value js.Value - id programID - } + textureNative uint32 + renderbufferNative uint32 + framebufferNative uint32 + shader uint32 + program uint32 + buffer uint32 ) -func (t textureNative) equal(rhs textureNative) bool { - return js.Value(t).Equal(js.Value(rhs)) -} +type ( + uniformLocation int32 + attribLocation int32 +) -func (r renderbufferNative) equal(rhs renderbufferNative) bool { - return js.Value(r).Equal(js.Value(rhs)) -} - -func (f framebufferNative) equal(rhs framebufferNative) bool { - return js.Value(f).Equal(js.Value(rhs)) -} - -func (s shader) equal(rhs shader) bool { - return js.Value(s).Equal(js.Value(rhs)) -} - -func (b buffer) equal(rhs buffer) bool { - return js.Value(b).Equal(js.Value(rhs)) -} - -func (u uniformLocation) equal(rhs uniformLocation) bool { - return js.Value(u).Equal(js.Value(rhs)) -} - -func (p program) equal(rhs program) bool { - return p.value.Equal(rhs.value) && p.id == rhs.id -} - -var invalidUniform = uniformLocation(js.Null()) - -func getProgramID(p program) programID { - return p.id -} +const ( + invalidFramebuffer = (1 << 32) - 1 + invalidUniform = -1 +) type webGLVersion int @@ -97,10 +66,12 @@ func webGL2MightBeAvailable() bool { } type contextImpl struct { - gl *jsGL - canvas js.Value - lastProgramID programID - webGLVersion webGLVersion + ctx gl.Context + canvas js.Value + webGLVersion webGLVersion + + fnGetExtension js.Value + fnIsContextLost js.Value } func (c *context) usesWebGL2() bool { @@ -110,7 +81,7 @@ func (c *context) usesWebGL2() bool { func (c *context) initGL() error { c.webGLVersion = webGLVersionUnknown - var gl js.Value + var glContext js.Value if doc := js.Global().Get("document"); doc.Truthy() { canvas := c.canvas @@ -120,36 +91,44 @@ func (c *context) initGL() error { attr.Set("stencil", true) if webGL2MightBeAvailable() { - gl = canvas.Call("getContext", "webgl2", attr) - if gl.Truthy() { + glContext = canvas.Call("getContext", "webgl2", attr) + if glContext.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 !glContext.Truthy() { + glContext = canvas.Call("getContext", "webgl", attr) + if !glContext.Truthy() { + glContext = canvas.Call("getContext", "experimental-webgl", attr) } - if gl.Truthy() { + if glContext.Truthy() { c.webGLVersion = webGLVersion1 } } - if !gl.Truthy() { + if !glContext.Truthy() { return fmt.Errorf("opengl: getContext failed") } } - c.gl = c.newJSGL(gl) + ctx, err := gl.NewDefaultContext(glContext) + if err != nil { + return err + } + c.ctx = ctx + + c.fnGetExtension = glContext.Get("getExtension").Call("bind", glContext) + c.fnIsContextLost = glContext.Get("isContextLost").Call("bind", glContext) + return nil } func (c *context) reset() error { c.locationCache = newLocationCache() - c.lastTexture = textureNative(js.Null()) - c.lastFramebuffer = framebufferNative(js.Null()) + c.lastTexture = 0 + c.lastFramebuffer = invalidFramebuffer c.lastViewportWidth = 0 c.lastViewportHeight = 0 c.lastBlend = graphicsdriver.Blend{} @@ -158,17 +137,17 @@ func (c *context) reset() error { return err } - if c.gl.isContextLost.Invoke().Bool() { + if c.fnIsContextLost.Invoke().Bool() { return graphicsdriver.GraphicsNotReady } - c.gl.enable.Invoke(gl.BLEND) - c.gl.enable.Invoke(gl.SCISSOR_TEST) + c.ctx.Enable(gl.BLEND) + c.ctx.Enable(gl.SCISSOR_TEST) c.blend(graphicsdriver.BlendSourceOver) - f := c.gl.getParameter.Invoke(gl.FRAMEBUFFER_BINDING) + f := c.ctx.GetInteger(gl.FRAMEBUFFER_BINDING) c.screenFramebuffer = framebufferNative(f) if !c.usesWebGL2() { - c.gl.getExtension.Invoke("OES_standard_derivatives") + c.fnGetExtension.Invoke("OES_standard_derivatives") } return nil } @@ -178,87 +157,85 @@ func (c *context) blend(blend graphicsdriver.Blend) { return } c.lastBlend = blend - c.gl.blendFuncSeparate.Invoke( - int(convertBlendFactor(blend.BlendFactorSourceRGB)), - int(convertBlendFactor(blend.BlendFactorDestinationRGB)), - int(convertBlendFactor(blend.BlendFactorSourceAlpha)), - int(convertBlendFactor(blend.BlendFactorDestinationAlpha)), + c.ctx.BlendFuncSeparate( + uint32(convertBlendFactor(blend.BlendFactorSourceRGB)), + uint32(convertBlendFactor(blend.BlendFactorDestinationRGB)), + uint32(convertBlendFactor(blend.BlendFactorSourceAlpha)), + uint32(convertBlendFactor(blend.BlendFactorDestinationAlpha)), ) - c.gl.blendEquationSeparate.Invoke( - int(convertBlendOperation(blend.BlendOperationRGB)), - int(convertBlendOperation(blend.BlendOperationAlpha)), + c.ctx.BlendEquationSeparate( + uint32(convertBlendOperation(blend.BlendOperationRGB)), + uint32(convertBlendOperation(blend.BlendOperationAlpha)), ) } func (c *context) scissor(x, y, width, height int) { - c.gl.scissor.Invoke(x, y, width, height) + c.ctx.Scissor(int32(x), int32(y), int32(width), int32(height)) } func (c *context) newTexture(width, height int) (textureNative, error) { - t := c.gl.createTexture.Invoke() - if !t.Truthy() { - return textureNative(js.Null()), errors.New("opengl: createTexture failed") + t := c.ctx.CreateTexture() + if t <= 0 { + return 0, errors.New("opengl: createTexture failed") } c.bindTexture(textureNative(t)) - c.gl.texParameteri.Invoke(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) - c.gl.texParameteri.Invoke(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) - c.gl.texParameteri.Invoke(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - c.gl.texParameteri.Invoke(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + + c.ctx.PixelStorei(gl.UNPACK_ALIGNMENT, 4) - c.gl.pixelStorei.Invoke(gl.UNPACK_ALIGNMENT, 4) // Firefox warns the usage of textures without specifying pixels (#629) // // Error: WebGL warning: drawElements: This operation requires zeroing texture data. This is slow. // - // In Ebitengine, textures are filled with pixels laster by the filter that ignores destination, so it is fine + // In Ebitengine, textures are filled with pixels later by the filter that ignores destination, so it is fine // to leave textures as uninitialized here. Rather, extra memory allocating for initialization should be // avoided. - c.gl.texImage2D.Invoke(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) + c.ctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, nil) return textureNative(t), nil } func (c *context) bindFramebufferImpl(f framebufferNative) { - c.gl.bindFramebuffer.Invoke(gl.FRAMEBUFFER, js.Value(f)) + c.ctx.BindFramebuffer(gl.FRAMEBUFFER, uint32(f)) } func (c *context) framebufferPixels(buf []byte, f *framebuffer, x, y, width, height int) { - c.bindFramebuffer(f.native) - if got, want := len(buf), 4*width*height; got != want { panic(fmt.Sprintf("opengl: len(buf) must be %d but %d", got, want)) } - p := jsutil.TemporaryUint8ArrayFromUint8Slice(len(buf), nil) - c.gl.readPixels.Invoke(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, p) - js.CopyBytesToGo(buf, p) + c.bindFramebuffer(f.native) + c.ctx.ReadPixels(buf, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE) } func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) { c.bindFramebuffer(f.native) - c.gl.bindBuffer.Invoke(gl.PIXEL_PACK_BUFFER, js.Value(buffer)) + c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, uint32(buffer)) // void gl.readPixels(x, y, width, height, format, type, GLintptr offset); - c.gl.readPixels.Invoke(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, 0) - c.gl.bindBuffer.Invoke(gl.PIXEL_PACK_BUFFER, nil) + c.ctx.ReadPixels(nil, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE) + c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, 0) } func (c *context) activeTexture(idx int) { - c.gl.activeTexture.Invoke(gl.TEXTURE0 + idx) + c.ctx.ActiveTexture(uint32(gl.TEXTURE0 + idx)) } func (c *context) bindTextureImpl(t textureNative) { - c.gl.bindTexture.Invoke(gl.TEXTURE_2D, js.Value(t)) + c.ctx.BindTexture(gl.TEXTURE_2D, uint32(t)) } func (c *context) deleteTexture(t textureNative) { - if !c.gl.isTexture.Invoke(js.Value(t)).Bool() { + if !c.ctx.IsTexture(uint32(t)) { return } - if c.lastTexture.equal(t) { - c.lastTexture = textureNative(js.Null()) + if c.lastTexture == t { + c.lastTexture = 0 } - c.gl.deleteTexture.Invoke(js.Value(t)) + c.ctx.DeleteTexture(uint32(t)) } func (c *context) isTexture(t textureNative) bool { @@ -267,40 +244,45 @@ func (c *context) isTexture(t textureNative) bool { } func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error) { - r := c.gl.createRenderbuffer.Invoke() - if !r.Truthy() { - return renderbufferNative(js.Null()), errors.New("opengl: createRenderbuffer failed") + r := c.ctx.CreateRenderbuffer() + if r <= 0 { + return 0, errors.New("opengl: createRenderbuffer failed") } - c.bindRenderbuffer(renderbufferNative(r)) + renderbuffer := renderbufferNative(r) + c.bindRenderbuffer(renderbuffer) + // TODO: Is STENCIL_INDEX8 portable? // https://stackoverflow.com/questions/11084961/binding-a-stencil-render-buffer-to-a-frame-buffer-in-opengl - c.gl.renderbufferStorage.Invoke(gl.RENDERBUFFER, gl.STENCIL_INDEX8, width, height) + c.ctx.RenderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, int32(width), int32(height)) - return renderbufferNative(r), nil + return renderbuffer, nil } func (c *context) bindRenderbufferImpl(r renderbufferNative) { - c.gl.bindRenderbuffer.Invoke(gl.RENDERBUFFER, js.Value(r)) + c.ctx.BindRenderbuffer(gl.RENDERBUFFER, uint32(r)) } func (c *context) deleteRenderbuffer(r renderbufferNative) { - if !c.gl.isRenderbuffer.Invoke(js.Value(r)).Bool() { + if !c.ctx.IsRenderbuffer(uint32(r)) { return } - if c.lastRenderbuffer.equal(r) { - c.lastRenderbuffer = renderbufferNative(js.Null()) + if c.lastRenderbuffer == r { + c.lastRenderbuffer = 0 } - c.gl.deleteRenderbuffer.Invoke(js.Value(r)) + c.ctx.DeleteRenderbuffer(uint32(r)) } func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) { - f := c.gl.createFramebuffer.Invoke() + f := c.ctx.CreateFramebuffer() + if f <= 0 { + return 0, fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f) + } c.bindFramebuffer(framebufferNative(f)) - c.gl.framebufferTexture2D.Invoke(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, js.Value(t), 0) - if s := c.gl.checkFramebufferStatus.Invoke(gl.FRAMEBUFFER); s.Int() != gl.FRAMEBUFFER_COMPLETE { - return framebufferNative(js.Null()), errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s.Int())) + c.ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(t), 0) + if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE { + return 0, errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s)) } return framebufferNative(f), nil @@ -309,30 +291,30 @@ func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) { func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) error { c.bindFramebuffer(f) - c.gl.framebufferRenderbuffer.Invoke(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, js.Value(r)) - if s := c.gl.checkFramebufferStatus.Invoke(gl.FRAMEBUFFER); s.Int() != gl.FRAMEBUFFER_COMPLETE { - return errors.New(fmt.Sprintf("opengl: framebufferRenderbuffer failed: %d", s.Int())) + c.ctx.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, uint32(r)) + if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE { + return errors.New(fmt.Sprintf("opengl: framebufferRenderbuffer failed: %d", s)) } return nil } func (c *context) setViewportImpl(width, height int) { - c.gl.viewport.Invoke(0, 0, width, height) + c.ctx.Viewport(0, 0, int32(width), int32(height)) } func (c *context) deleteFramebuffer(f framebufferNative) { - if !c.gl.isFramebuffer.Invoke(js.Value(f)).Bool() { + if !c.ctx.IsFramebuffer(uint32(f)) { return } // If a framebuffer to be deleted is bound, a newly bound framebuffer // will be a default framebuffer. // https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffers.xml - if c.lastFramebuffer.equal(f) { - c.lastFramebuffer = framebufferNative(js.Null()) + if c.lastFramebuffer == f { + c.lastFramebuffer = invalidFramebuffer c.lastViewportWidth = 0 c.lastViewportHeight = 0 } - c.gl.deleteFramebuffer.Invoke(js.Value(f)) + c.ctx.DeleteFramebuffer(uint32(f)) } func (c *context) newVertexShader(source string) (shader, error) { @@ -343,83 +325,78 @@ func (c *context) newFragmentShader(source string) (shader, error) { return c.newShader(gl.FRAGMENT_SHADER, source) } -func (c *context) newShader(shaderType int, source string) (shader, error) { - s := c.gl.createShader.Invoke(int(shaderType)) - if !s.Truthy() { - return shader(js.Null()), fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) +func (c *context) newShader(shaderType uint32, source string) (shader, error) { + s := c.ctx.CreateShader(shaderType) + if s == 0 { + return 0, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) } - c.gl.shaderSource.Invoke(js.Value(s), source) - c.gl.compileShader.Invoke(js.Value(s)) + c.ctx.ShaderSource(s, source) + c.ctx.CompileShader(s) - if !c.gl.getShaderParameter.Invoke(js.Value(s), gl.COMPILE_STATUS).Bool() { - log := c.gl.getShaderInfoLog.Invoke(js.Value(s)) - return shader(js.Null()), fmt.Errorf("opengl: shader compile failed: %s", log) + if c.ctx.GetShaderi(s, gl.COMPILE_STATUS) == gl.FALSE { + log := c.ctx.GetShaderInfoLog(s) + return 0, fmt.Errorf("opengl: shader compile failed: %s", log) } return shader(s), nil } func (c *context) deleteShader(s shader) { - c.gl.deleteShader.Invoke(js.Value(s)) + c.ctx.DeleteShader(uint32(s)) } func (c *context) newProgram(shaders []shader, attributes []string) (program, error) { - v := c.gl.createProgram.Invoke() - if !v.Truthy() { - return program{}, errors.New("opengl: glCreateProgram failed") + p := c.ctx.CreateProgram() + if p == 0 { + return 0, errors.New("opengl: glCreateProgram failed") } for _, shader := range shaders { - c.gl.attachShader.Invoke(v, js.Value(shader)) + c.ctx.AttachShader(p, uint32(shader)) } for i, name := range attributes { - c.gl.bindAttribLocation.Invoke(v, i, name) + c.ctx.BindAttribLocation(p, uint32(i), name) } - c.gl.linkProgram.Invoke(v) - if !c.gl.getProgramParameter.Invoke(v, gl.LINK_STATUS).Bool() { - info := c.gl.getProgramInfoLog.Invoke(v).String() - return program{}, fmt.Errorf("opengl: program error: %s", info) + c.ctx.LinkProgram(p) + if c.ctx.GetProgrami(p, gl.LINK_STATUS) == gl.FALSE { + info := c.ctx.GetProgramInfoLog(p) + return 0, fmt.Errorf("opengl: program error: %s", info) } - id := c.lastProgramID - c.lastProgramID++ - return program{ - value: v, - id: id, - }, nil + return program(p), nil } func (c *context) useProgram(p program) { - c.gl.useProgram.Invoke(p.value) + c.ctx.UseProgram(uint32(p)) } func (c *context) deleteProgram(p program) { c.locationCache.deleteProgram(p) - if !c.gl.isProgram.Invoke(p.value).Bool() { + if !c.ctx.IsProgram(uint32(p)) { return } - c.gl.deleteProgram.Invoke(p.value) + c.ctx.DeleteProgram(uint32(p)) } func (c *context) getUniformLocationImpl(p program, location string) uniformLocation { - return uniformLocation(c.gl.getUniformLocation.Invoke(p.value, location)) + return uniformLocation(c.ctx.GetUniformLocation(uint32(p), location)) } func (c *context) uniformInt(p program, location string, v int) bool { l := c.locationCache.GetUniformLocation(c, p, location) - if l.equal(invalidUniform) { + if l == invalidUniform { return false } - c.gl.uniform1i.Invoke(js.Value(l), v) + c.ctx.Uniform1i(int32(l), int32(v)) return true } func (c *context) uniforms(p program, location string, v []uint32, typ shaderir.Type) bool { l := c.locationCache.GetUniformLocation(c, p, location) - if l.equal(invalidUniform) { + if l == invalidUniform { return false } @@ -430,178 +407,115 @@ func (c *context) uniforms(p program, location string, v []uint32, typ shaderir. switch base { case shaderir.Float: - arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v)) - if c.usesWebGL2() { - c.gl.uniform1fv.Invoke(js.Value(l), arr, 0, len(v)) - } else { - c.gl.uniform1fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) - } + c.ctx.Uniform1fv(int32(l), uint32sToFloat32s(v)) case shaderir.Int: - arr := jsutil.TemporaryInt32Array(len(v), uint32sToInt32s(v)) - if c.usesWebGL2() { - c.gl.uniform1iv.Invoke(js.Value(l), arr, 0, len(v)) - } else { - c.gl.uniform1iv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) - } + c.ctx.Uniform1iv(int32(l), uint32sToInt32s(v)) case shaderir.Vec2: - arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v)) - if c.usesWebGL2() { - c.gl.uniform2fv.Invoke(js.Value(l), arr, 0, len(v)) - } else { - c.gl.uniform2fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) - } + c.ctx.Uniform2fv(int32(l), uint32sToFloat32s(v)) case shaderir.Vec3: - arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v)) - if c.usesWebGL2() { - c.gl.uniform3fv.Invoke(js.Value(l), arr, 0, len(v)) - } else { - c.gl.uniform3fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) - } + c.ctx.Uniform3fv(int32(l), uint32sToFloat32s(v)) case shaderir.Vec4: - arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v)) - if c.usesWebGL2() { - c.gl.uniform4fv.Invoke(js.Value(l), arr, 0, len(v)) - } else { - c.gl.uniform4fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v))) - } + c.ctx.Uniform4fv(int32(l), uint32sToFloat32s(v)) case shaderir.Mat2: - arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v)) - if c.usesWebGL2() { - c.gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr, 0, len(v)) - } else { - c.gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v))) - } + c.ctx.UniformMatrix2fv(int32(l), uint32sToFloat32s(v)) case shaderir.Mat3: - arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v)) - if c.usesWebGL2() { - c.gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr, 0, len(v)) - } else { - c.gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v))) - } + c.ctx.UniformMatrix3fv(int32(l), uint32sToFloat32s(v)) case shaderir.Mat4: - arr := jsutil.TemporaryFloat32Array(len(v), uint32sToFloat32s(v)) - if c.usesWebGL2() { - c.gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr, 0, len(v)) - } else { - c.gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v))) - } + c.ctx.UniformMatrix4fv(int32(l), uint32sToFloat32s(v)) default: panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String())) } - return true } func (c *context) vertexAttribPointer(index int, size int, stride int, offset int) { - c.gl.vertexAttribPointer.Invoke(index, size, gl.FLOAT, false, stride, offset) + c.ctx.VertexAttribPointer(uint32(index), int32(size), gl.FLOAT, false, int32(stride), offset) } func (c *context) enableVertexAttribArray(index int) { - c.gl.enableVertexAttribArray.Invoke(index) + c.ctx.EnableVertexAttribArray(uint32(index)) } func (c *context) disableVertexAttribArray(index int) { - c.gl.disableVertexAttribArray.Invoke(index) + c.ctx.DisableVertexAttribArray(uint32(index)) } func (c *context) newArrayBuffer(size int) buffer { - b := c.gl.createBuffer.Invoke() - c.gl.bindBuffer.Invoke(gl.ARRAY_BUFFER, js.Value(b)) - c.gl.bufferData.Invoke(gl.ARRAY_BUFFER, size, gl.DYNAMIC_DRAW) + b := c.ctx.CreateBuffer() + c.ctx.BindBuffer(gl.ARRAY_BUFFER, b) + c.ctx.BufferInit(gl.ARRAY_BUFFER, size, gl.DYNAMIC_DRAW) return buffer(b) } func (c *context) newElementArrayBuffer(size int) buffer { - b := c.gl.createBuffer.Invoke() - c.gl.bindBuffer.Invoke(gl.ELEMENT_ARRAY_BUFFER, js.Value(b)) - c.gl.bufferData.Invoke(gl.ELEMENT_ARRAY_BUFFER, size, gl.DYNAMIC_DRAW) + b := c.ctx.CreateBuffer() + c.ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b) + c.ctx.BufferInit(gl.ELEMENT_ARRAY_BUFFER, size, gl.DYNAMIC_DRAW) return buffer(b) } func (c *context) bindArrayBuffer(b buffer) { - c.gl.bindBuffer.Invoke(gl.ARRAY_BUFFER, js.Value(b)) + c.ctx.BindBuffer(gl.ARRAY_BUFFER, uint32(b)) } func (c *context) bindElementArrayBuffer(b buffer) { - c.gl.bindBuffer.Invoke(gl.ELEMENT_ARRAY_BUFFER, js.Value(b)) + c.ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, uint32(b)) } func (c *context) arrayBufferSubData(data []float32) { - l := len(data) * 4 - arr := jsutil.TemporaryUint8ArrayFromFloat32Slice(l, data) - if c.usesWebGL2() { - c.gl.bufferSubData.Invoke(gl.ARRAY_BUFFER, 0, arr, 0, l) - } else { - c.gl.bufferSubData.Invoke(gl.ARRAY_BUFFER, 0, arr.Call("subarray", 0, l)) - } + s := unsafe.Slice((*byte)(unsafe.Pointer(&data[0])), len(data)*4) + c.ctx.BufferSubData(gl.ARRAY_BUFFER, 0, s) } func (c *context) elementArrayBufferSubData(data []uint16) { - l := len(data) * 2 - arr := jsutil.TemporaryUint8ArrayFromUint16Slice(l, data) - if c.usesWebGL2() { - c.gl.bufferSubData.Invoke(gl.ELEMENT_ARRAY_BUFFER, 0, arr, 0, l) - } else { - c.gl.bufferSubData.Invoke(gl.ELEMENT_ARRAY_BUFFER, 0, arr.Call("subarray", 0, l)) - } + s := unsafe.Slice((*byte)(unsafe.Pointer(&data[0])), len(data)*2) + c.ctx.BufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, s) } func (c *context) deleteBuffer(b buffer) { - c.gl.deleteBuffer.Invoke(js.Value(b)) + c.ctx.DeleteBuffer(uint32(b)) } func (c *context) drawElements(len int, offsetInBytes int) { - c.gl.drawElements.Invoke(gl.TRIANGLES, len, gl.UNSIGNED_SHORT, offsetInBytes) + c.ctx.DrawElements(gl.TRIANGLES, int32(len), gl.UNSIGNED_SHORT, offsetInBytes) } func (c *context) maxTextureSizeImpl() int { - return c.gl.getParameter.Invoke(gl.MAX_TEXTURE_SIZE).Int() + return c.ctx.GetInteger(gl.MAX_TEXTURE_SIZE) } func (c *context) flush() { - c.gl.flush.Invoke() + c.ctx.Flush() } func (c *context) texSubImage2D(t textureNative, args []*graphicsdriver.WritePixelsArgs) { c.bindTexture(t) for _, a := range args { - arr := jsutil.TemporaryUint8ArrayFromUint8Slice(len(a.Pixels), a.Pixels) - if c.usesWebGL2() { - // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLsizei width, GLsizei height, - // GLenum format, GLenum type, ArrayBufferView pixels, srcOffset); - c.gl.texSubImage2D.Invoke(gl.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gl.RGBA, gl.UNSIGNED_BYTE, arr, 0) - } else { - // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, - // GLsizei width, GLsizei height, - // GLenum format, GLenum type, ArrayBufferView? pixels); - c.gl.texSubImage2D.Invoke(gl.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gl.RGBA, gl.UNSIGNED_BYTE, arr) - } + c.ctx.TexSubImage2D(gl.TEXTURE_2D, 0, int32(a.X), int32(a.Y), int32(a.Width), int32(a.Height), gl.RGBA, gl.UNSIGNED_BYTE, a.Pixels) } } func (c *context) enableStencilTest() { - c.gl.enable.Invoke(gl.STENCIL_TEST) + c.ctx.Enable(gl.STENCIL_TEST) } func (c *context) disableStencilTest() { - c.gl.disable.Invoke(gl.STENCIL_TEST) + c.ctx.Disable(gl.STENCIL_TEST) } func (c *context) beginStencilWithEvenOddRule() { - c.gl.clear.Invoke(gl.STENCIL_BUFFER_BIT) - c.gl.stencilFunc.Invoke(gl.ALWAYS, 0x00, 0xff) - c.gl.stencilOp.Invoke(gl.KEEP, gl.KEEP, gl.INVERT) - c.gl.colorMask.Invoke(false, false, false, false) + c.ctx.Clear(gl.STENCIL_BUFFER_BIT) + c.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff) + c.ctx.StencilOp(gl.KEEP, gl.KEEP, gl.INVERT) + c.ctx.ColorMask(false, false, false, false) } func (c *context) endStencilWithEvenOddRule() { - c.gl.stencilFunc.Invoke(gl.NOTEQUAL, 0x00, 0xff) - c.gl.stencilOp.Invoke(gl.KEEP, gl.KEEP, gl.KEEP) - c.gl.colorMask.Invoke(true, true, true, true) + c.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff) + c.ctx.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) + c.ctx.ColorMask(true, true, true, true) } func (c *context) isES() bool { - // WebGL is compatible with GLES. - return true + return c.ctx.IsES() } diff --git a/internal/graphicsdriver/opengl/context_notjs.go b/internal/graphicsdriver/opengl/context_notjs.go index db53aa311..cbe3af6ff 100644 --- a/internal/graphicsdriver/opengl/context_notjs.go +++ b/internal/graphicsdriver/opengl/context_notjs.go @@ -35,50 +35,16 @@ type ( buffer uint32 ) -func (t textureNative) equal(rhs textureNative) bool { - return t == rhs -} - -func (r renderbufferNative) equal(rhs renderbufferNative) bool { - return r == 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 -} - type ( uniformLocation int32 attribLocation int32 ) -func (u uniformLocation) equal(rhs uniformLocation) bool { - return u == rhs -} - -type programID uint32 - const ( invalidFramebuffer = (1 << 32) - 1 invalidUniform = -1 ) -func getProgramID(p program) programID { - return programID(p) -} - type contextImpl struct { ctx gl.Context init bool @@ -150,10 +116,12 @@ func (c *context) bindFramebufferImpl(f framebufferNative) { } func (c *context) framebufferPixels(buf []byte, f *framebuffer, x, y, width, height int) { + if got, want := len(buf), 4*width*height; got != want { + panic(fmt.Sprintf("opengl: len(buf) must be %d but %d", got, want)) + } + c.ctx.Flush() - c.bindFramebuffer(f.native) - c.ctx.ReadPixels(buf, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE) } @@ -213,7 +181,7 @@ func (c *context) deleteRenderbuffer(r renderbufferNative) { if !c.ctx.IsRenderbuffer(uint32(r)) { return } - if c.lastRenderbuffer.equal(r) { + if c.lastRenderbuffer == r { c.lastRenderbuffer = 0 } c.ctx.DeleteRenderbuffer(uint32(r)) @@ -332,8 +300,7 @@ func (c *context) deleteProgram(p program) { } func (c *context) getUniformLocationImpl(p program, location string) uniformLocation { - u := uniformLocation(c.ctx.GetUniformLocation(uint32(p), location)) - return u + return uniformLocation(c.ctx.GetUniformLocation(uint32(p), location)) } func (c *context) uniformInt(p program, location string, v int) bool { diff --git a/internal/graphicsdriver/opengl/framebuffer.go b/internal/graphicsdriver/opengl/framebuffer.go index 9833539d9..18f45813d 100644 --- a/internal/graphicsdriver/opengl/framebuffer.go +++ b/internal/graphicsdriver/opengl/framebuffer.go @@ -45,7 +45,7 @@ func newScreenFramebuffer(context *context, width, height int) *framebuffer { } func (f *framebuffer) delete(context *context) { - if !f.native.equal(context.getScreenFramebuffer()) { + if f.native != context.getScreenFramebuffer() { context.deleteFramebuffer(f.native) } } diff --git a/internal/graphicsdriver/opengl/gl/default_js.go b/internal/graphicsdriver/opengl/gl/default_js.go new file mode 100644 index 000000000..903bb7241 --- /dev/null +++ b/internal/graphicsdriver/opengl/gl/default_js.go @@ -0,0 +1,645 @@ +// Copyright 2022 The Ebitengine 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 gl + +import ( + "fmt" + "syscall/js" + + "github.com/hajimehoshi/ebiten/v2/internal/jsutil" +) + +type defaultContext struct { + fnActiveTexture js.Value + fnAttachShader js.Value + fnBindAttribLocation js.Value + fnBindBuffer js.Value + fnBindFramebuffer js.Value + fnBindRenderbuffer js.Value + fnBindTexture js.Value + fnBlendEquationSeparate js.Value + fnBlendFuncSeparate js.Value + fnBufferData js.Value + fnBufferSubData js.Value + fnCheckFramebufferStatus js.Value + fnClear js.Value + fnColorMask js.Value + fnCompileShader js.Value + fnCreateBuffer js.Value + fnCreateFramebuffer js.Value + fnCreateProgram js.Value + fnCreateRenderbuffer js.Value + fnCreateShader js.Value + fnCreateTexture js.Value + fnDeleteBuffer js.Value + fnDeleteFramebuffer js.Value + fnDeleteProgram js.Value + fnDeleteRenderbuffer js.Value + fnDeleteShader js.Value + fnDeleteTexture js.Value + fnDisable js.Value + fnDisableVertexAttribArray js.Value + fnDrawElements js.Value + fnEnable js.Value + fnEnableVertexAttribArray js.Value + fnFramebufferRenderbuffer js.Value + fnFramebufferTexture2D js.Value + fnFlush js.Value + fnGetError js.Value + fnGetParameter js.Value + fnGetProgramInfoLog js.Value + fnGetProgramParameter js.Value + fnGetShaderInfoLog js.Value + fnGetShaderParameter js.Value + fnGetUniformLocation js.Value + fnIsFramebuffer js.Value + fnIsProgram js.Value + fnIsRenderbuffer js.Value + fnIsTexture js.Value + fnLinkProgram js.Value + fnPixelStorei js.Value + fnReadPixels js.Value + fnRenderbufferStorage js.Value + fnScissor js.Value + fnShaderSource js.Value + fnStencilFunc js.Value + fnStencilMask js.Value + fnStencilOp js.Value + fnTexImage2D js.Value + fnTexSubImage2D js.Value + fnTexParameteri js.Value + fnUniform1fv js.Value + fnUniform1i js.Value + fnUniform1iv js.Value + fnUniform2fv js.Value + fnUniform3fv js.Value + fnUniform4fv js.Value + fnUniformMatrix2fv js.Value + fnUniformMatrix3fv js.Value + fnUniformMatrix4fv js.Value + fnUseProgram js.Value + fnVertexAttribPointer js.Value + fnViewport js.Value + + webGL2 bool + + buffers values + framebuffers values + programs values + renderbuffers values + shaders values + textures values + uniformLocations map[uint32]*values +} + +type values struct { + idToValue map[uint32]js.Value + lastID uint32 +} + +func (v *values) create(value js.Value) uint32 { + v.lastID++ + id := v.lastID + if v.idToValue == nil { + v.idToValue = map[uint32]js.Value{} + } + v.idToValue[id] = value + return id +} + +func (v *values) get(id uint32) js.Value { + return v.idToValue[id] +} + +func (v *values) getID(value js.Value) (uint32, bool) { + for id, v := range v.idToValue { + if v.Equal(value) { + return id, true + } + } + return 0, false +} + +func (v *values) getOrCreate(value js.Value) uint32 { + id, ok := v.getID(value) + if ok { + return id + } + return v.create(value) +} + +func (v *values) delete(id uint32) { + delete(v.idToValue, id) +} + +func NewDefaultContext(v js.Value) (Context, error) { + // 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 := &defaultContext{ + fnActiveTexture: v.Get("activeTexture").Call("bind", v), + fnAttachShader: v.Get("attachShader").Call("bind", v), + fnBindAttribLocation: v.Get("bindAttribLocation").Call("bind", v), + fnBindBuffer: v.Get("bindBuffer").Call("bind", v), + fnBindFramebuffer: v.Get("bindFramebuffer").Call("bind", v), + fnBindRenderbuffer: v.Get("bindRenderbuffer").Call("bind", v), + fnBindTexture: v.Get("bindTexture").Call("bind", v), + fnBlendEquationSeparate: v.Get("blendEquationSeparate").Call("bind", v), + fnBlendFuncSeparate: v.Get("blendFuncSeparate").Call("bind", v), + fnBufferData: v.Get("bufferData").Call("bind", v), + fnBufferSubData: v.Get("bufferSubData").Call("bind", v), + fnCheckFramebufferStatus: v.Get("checkFramebufferStatus").Call("bind", v), + fnClear: v.Get("clear").Call("bind", v), + fnColorMask: v.Get("colorMask").Call("bind", v), + fnCompileShader: v.Get("compileShader").Call("bind", v), + fnCreateBuffer: v.Get("createBuffer").Call("bind", v), + fnCreateFramebuffer: v.Get("createFramebuffer").Call("bind", v), + fnCreateProgram: v.Get("createProgram").Call("bind", v), + fnCreateRenderbuffer: v.Get("createRenderbuffer").Call("bind", v), + fnCreateShader: v.Get("createShader").Call("bind", v), + fnCreateTexture: v.Get("createTexture").Call("bind", v), + fnDeleteBuffer: v.Get("deleteBuffer").Call("bind", v), + fnDeleteFramebuffer: v.Get("deleteFramebuffer").Call("bind", v), + fnDeleteProgram: v.Get("deleteProgram").Call("bind", v), + fnDeleteRenderbuffer: v.Get("deleteRenderbuffer").Call("bind", v), + fnDeleteShader: v.Get("deleteShader").Call("bind", v), + fnDeleteTexture: v.Get("deleteTexture").Call("bind", v), + fnDisable: v.Get("disable").Call("bind", v), + fnDisableVertexAttribArray: v.Get("disableVertexAttribArray").Call("bind", v), + fnDrawElements: v.Get("drawElements").Call("bind", v), + fnEnable: v.Get("enable").Call("bind", v), + fnEnableVertexAttribArray: v.Get("enableVertexAttribArray").Call("bind", v), + fnFramebufferRenderbuffer: v.Get("framebufferRenderbuffer").Call("bind", v), + fnFramebufferTexture2D: v.Get("framebufferTexture2D").Call("bind", v), + fnFlush: v.Get("flush").Call("bind", v), + fnGetError: v.Get("getError").Call("bind", v), + fnGetParameter: v.Get("getParameter").Call("bind", v), + fnGetProgramInfoLog: v.Get("getProgramInfoLog").Call("bind", v), + fnGetProgramParameter: v.Get("getProgramParameter").Call("bind", v), + fnGetShaderInfoLog: v.Get("getShaderInfoLog").Call("bind", v), + fnGetShaderParameter: v.Get("getShaderParameter").Call("bind", v), + fnGetUniformLocation: v.Get("getUniformLocation").Call("bind", v), + fnIsFramebuffer: v.Get("isFramebuffer").Call("bind", v), + fnIsProgram: v.Get("isProgram").Call("bind", v), + fnIsRenderbuffer: v.Get("isRenderbuffer").Call("bind", v), + fnIsTexture: v.Get("isTexture").Call("bind", v), + fnLinkProgram: v.Get("linkProgram").Call("bind", v), + fnPixelStorei: v.Get("pixelStorei").Call("bind", v), + fnReadPixels: v.Get("readPixels").Call("bind", v), + fnRenderbufferStorage: v.Get("renderbufferStorage").Call("bind", v), + fnScissor: v.Get("scissor").Call("bind", v), + fnShaderSource: v.Get("shaderSource").Call("bind", v), + fnStencilFunc: v.Get("stencilFunc").Call("bind", v), + fnStencilMask: v.Get("stencilMask").Call("bind", v), + fnStencilOp: v.Get("stencilOp").Call("bind", v), + fnTexImage2D: v.Get("texImage2D").Call("bind", v), + fnTexSubImage2D: v.Get("texSubImage2D").Call("bind", v), + fnTexParameteri: v.Get("texParameteri").Call("bind", v), + fnUniform1fv: v.Get("uniform1fv").Call("bind", v), + fnUniform1i: v.Get("uniform1i").Call("bind", v), + fnUniform1iv: v.Get("uniform1iv").Call("bind", v), + fnUniform2fv: v.Get("uniform2fv").Call("bind", v), + fnUniform3fv: v.Get("uniform3fv").Call("bind", v), + fnUniform4fv: v.Get("uniform4fv").Call("bind", v), + fnUniformMatrix2fv: v.Get("uniformMatrix2fv").Call("bind", v), + fnUniformMatrix3fv: v.Get("uniformMatrix3fv").Call("bind", v), + fnUniformMatrix4fv: v.Get("uniformMatrix4fv").Call("bind", v), + fnUseProgram: v.Get("useProgram").Call("bind", v), + fnVertexAttribPointer: v.Get("vertexAttribPointer").Call("bind", v), + fnViewport: v.Get("viewport").Call("bind", v), + } + + if webGL2 := js.Global().Get("WebGL2RenderingContext"); webGL2.Truthy() { + g.webGL2 = v.InstanceOf(webGL2) + } + + return g, nil +} + +func (c *defaultContext) getUniformLocation(location int32) js.Value { + program := uint32(location) >> 5 + return c.uniformLocations[program].get(uint32(location) & ((1 << 5) - 1)) +} + +func (c *defaultContext) LoadFunctions() error { + return nil +} + +func (c *defaultContext) IsES() bool { + // WebGL is compatible with GLES. + return true +} + +func (c *defaultContext) ActiveTexture(texture uint32) { + c.fnActiveTexture.Invoke(texture) +} + +func (c *defaultContext) AttachShader(program uint32, shader uint32) { + c.fnAttachShader.Invoke(c.programs.get(program), c.shaders.get(shader)) +} + +func (c *defaultContext) BindAttribLocation(program uint32, index uint32, name string) { + c.fnBindAttribLocation.Invoke(c.programs.get(program), index, name) +} + +func (c *defaultContext) BindBuffer(target uint32, buffer uint32) { + c.fnBindBuffer.Invoke(target, c.buffers.get(buffer)) +} + +func (c *defaultContext) BindFramebuffer(target uint32, framebuffer uint32) { + c.fnBindFramebuffer.Invoke(target, c.framebuffers.get(framebuffer)) +} + +func (c *defaultContext) BindRenderbuffer(target uint32, renderbuffer uint32) { + c.fnBindRenderbuffer.Invoke(target, c.renderbuffers.get(renderbuffer)) +} + +func (c *defaultContext) BindTexture(target uint32, texture uint32) { + c.fnBindTexture.Invoke(target, c.textures.get(texture)) +} + +func (c *defaultContext) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) { + c.fnBlendEquationSeparate.Invoke(modeRGB, modeAlpha) +} + +func (c *defaultContext) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) { + c.fnBlendFuncSeparate.Invoke(srcRGB, dstRGB, srcAlpha, dstAlpha) +} + +func (c *defaultContext) BufferInit(target uint32, size int, usage uint32) { + c.fnBufferData.Invoke(target, size, usage) +} + +func (c *defaultContext) BufferSubData(target uint32, offset int, data []byte) { + l := len(data) + arr := jsutil.TemporaryUint8ArrayFromUint8Slice(l, data) + if c.webGL2 { + c.fnBufferSubData.Invoke(target, offset, arr, 0, l) + } else { + c.fnBufferSubData.Invoke(target, offset, arr.Call("subarray", 0, l)) + } +} + +func (c *defaultContext) CheckFramebufferStatus(target uint32) uint32 { + return uint32(c.fnCheckFramebufferStatus.Invoke(target).Int()) +} + +func (c *defaultContext) Clear(mask uint32) { + c.fnClear.Invoke(mask) +} + +func (c *defaultContext) ColorMask(red, green, blue, alpha bool) { + c.fnColorMask.Invoke(red, green, blue, alpha) +} + +func (c *defaultContext) CompileShader(shader uint32) { + c.fnCompileShader.Invoke(c.shaders.get(shader)) +} + +func (c *defaultContext) CreateBuffer() uint32 { + return c.buffers.create(c.fnCreateBuffer.Invoke()) +} + +func (c *defaultContext) CreateFramebuffer() uint32 { + return c.framebuffers.create(c.fnCreateFramebuffer.Invoke()) +} + +func (c *defaultContext) CreateProgram() uint32 { + return c.programs.create(c.fnCreateProgram.Invoke()) +} + +func (c *defaultContext) CreateRenderbuffer() uint32 { + return c.renderbuffers.create(c.fnCreateRenderbuffer.Invoke()) +} + +func (c *defaultContext) CreateShader(xtype uint32) uint32 { + return c.shaders.create(c.fnCreateShader.Invoke(xtype)) +} + +func (c *defaultContext) CreateTexture() uint32 { + return c.textures.create(c.fnCreateTexture.Invoke()) +} + +func (c *defaultContext) DeleteBuffer(buffer uint32) { + c.fnDeleteBuffer.Invoke(c.buffers.get(buffer)) + c.buffers.delete(buffer) +} + +func (c *defaultContext) DeleteFramebuffer(framebuffer uint32) { + c.fnDeleteFramebuffer.Invoke(c.framebuffers.get(framebuffer)) + c.framebuffers.delete(framebuffer) +} + +func (c *defaultContext) DeleteProgram(program uint32) { + c.fnDeleteProgram.Invoke(c.programs.get(program)) + c.programs.delete(program) + delete(c.uniformLocations, program) +} + +func (c *defaultContext) DeleteRenderbuffer(renderbuffer uint32) { + c.fnDeleteRenderbuffer.Invoke(c.renderbuffers.get(renderbuffer)) + c.renderbuffers.delete(renderbuffer) +} + +func (c *defaultContext) DeleteShader(shader uint32) { + c.fnDeleteShader.Invoke(c.shaders.get(shader)) + c.shaders.delete(shader) +} + +func (c *defaultContext) DeleteTexture(texture uint32) { + c.fnDeleteTexture.Invoke(c.textures.get(texture)) + c.textures.delete(texture) +} + +func (c *defaultContext) Disable(cap uint32) { + c.fnDisable.Invoke(cap) +} + +func (c *defaultContext) DisableVertexAttribArray(index uint32) { + c.fnDisableVertexAttribArray.Invoke(index) +} + +func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) { + c.fnDrawElements.Invoke(mode, count, xtype, offset) +} + +func (c *defaultContext) Enable(cap uint32) { + c.fnEnable.Invoke(cap) +} + +func (c *defaultContext) EnableVertexAttribArray(index uint32) { + c.fnEnableVertexAttribArray.Invoke(index) +} + +func (c *defaultContext) Flush() { + c.fnFlush.Invoke() +} + +func (c *defaultContext) FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32) { + c.fnFramebufferRenderbuffer.Invoke(target, attachment, renderbuffertarget, c.renderbuffers.get(renderbuffer)) +} + +func (c *defaultContext) FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32) { + c.fnFramebufferTexture2D.Invoke(target, attachment, textarget, c.textures.get(texture), level) +} + +func (c *defaultContext) GetError() uint32 { + return uint32(c.fnGetError.Invoke().Int()) +} + +func (c *defaultContext) GetInteger(pname uint32) int { + ret := c.fnGetParameter.Invoke(pname) + switch pname { + case FRAMEBUFFER_BINDING: + id, ok := c.framebuffers.getID(ret) + if !ok { + return 0 + } + return int(id) + case MAX_TEXTURE_SIZE: + return ret.Int() + default: + panic(fmt.Sprintf("gl: unexpected pname at GetInteger: %d", pname)) + } +} + +func (c *defaultContext) GetProgramInfoLog(program uint32) string { + return c.fnGetProgramInfoLog.Invoke(c.programs.get(program)).String() +} + +func (c *defaultContext) GetProgrami(program uint32, pname uint32) int { + v := c.fnGetProgramParameter.Invoke(c.programs.get(program), pname) + switch v.Type() { + case js.TypeNumber: + return v.Int() + case js.TypeBoolean: + if v.Bool() { + return TRUE + } + return FALSE + default: + panic(fmt.Sprintf("gl: unexpected return type at GetProgrami: %v", v)) + } +} + +func (c *defaultContext) GetShaderInfoLog(shader uint32) string { + return c.fnGetShaderInfoLog.Invoke(c.shaders.get(shader)).String() +} + +func (c *defaultContext) GetShaderi(shader uint32, pname uint32) int { + v := c.fnGetShaderParameter.Invoke(c.shaders.get(shader), pname) + switch v.Type() { + case js.TypeNumber: + return v.Int() + case js.TypeBoolean: + if v.Bool() { + return TRUE + } + return FALSE + default: + panic(fmt.Sprintf("gl: unexpected return type at GetShaderi: %v", v)) + } + +} + +func (c *defaultContext) GetUniformLocation(program uint32, name string) int32 { + location := c.fnGetUniformLocation.Invoke(c.programs.get(program), name) + if c.uniformLocations == nil { + c.uniformLocations = map[uint32]*values{} + } + vs, ok := c.uniformLocations[program] + if !ok { + vs = &values{} + c.uniformLocations[program] = vs + } + idx := vs.getOrCreate(location) + return int32((program << 5) | idx) +} + +func (c *defaultContext) IsFramebuffer(framebuffer uint32) bool { + return c.fnIsFramebuffer.Invoke(c.framebuffers.get(framebuffer)).Bool() +} + +func (c *defaultContext) IsProgram(program uint32) bool { + return c.fnIsProgram.Invoke(c.programs.get(program)).Bool() +} + +func (c *defaultContext) IsRenderbuffer(renderbuffer uint32) bool { + return c.fnIsRenderbuffer.Invoke(c.renderbuffers.get(renderbuffer)).Bool() +} + +func (c *defaultContext) IsTexture(texture uint32) bool { + return c.fnIsTexture.Invoke(c.textures.get(texture)).Bool() +} + +func (c *defaultContext) LinkProgram(program uint32) { + c.fnLinkProgram.Invoke(c.programs.get(program)) +} + +func (c *defaultContext) PixelStorei(pname uint32, param int32) { + c.fnPixelStorei.Invoke(pname, param) +} + +func (c *defaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) { + if dst == nil { + c.fnReadPixels.Invoke(x, y, width, height, format, xtype, 0) + return + } + p := jsutil.TemporaryUint8ArrayFromUint8Slice(len(dst), nil) + c.fnReadPixels.Invoke(x, y, width, height, format, xtype, p) + js.CopyBytesToGo(dst, p) +} + +func (c *defaultContext) RenderbufferStorage(target uint32, internalFormat uint32, width int32, height int32) { + c.fnRenderbufferStorage.Invoke(target, internalFormat, width, height) +} + +func (c *defaultContext) Scissor(x, y, width, height int32) { + c.fnScissor.Invoke(x, y, width, height) +} + +func (c *defaultContext) ShaderSource(shader uint32, xstring string) { + c.fnShaderSource.Invoke(c.shaders.get(shader), xstring) +} + +func (c *defaultContext) StencilFunc(func_ uint32, ref int32, mask uint32) { + c.fnStencilFunc.Invoke(func_, ref, mask) +} + +func (c *defaultContext) StencilOp(sfail, dpfail, dppass uint32) { + c.fnStencilOp.Invoke(sfail, dpfail, dppass) +} + +func (c *defaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) { + if pixels != nil { + panic("gl: TexImage2D with non-nil pixels is not implemented") + } + c.fnTexImage2D.Invoke(target, level, internalformat, width, height, 0, format, xtype, nil) +} + +func (c *defaultContext) TexParameteri(target uint32, pname uint32, param int32) { + c.fnTexParameteri.Invoke(target, pname, param) +} + +func (c *defaultContext) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) { + arr := jsutil.TemporaryUint8ArrayFromUint8Slice(len(pixels), pixels) + if c.webGL2 { + // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + // GLsizei width, GLsizei height, + // GLenum format, GLenum type, ArrayBufferView pixels, srcOffset); + c.fnTexSubImage2D.Invoke(target, level, xoffset, yoffset, width, height, format, xtype, arr, 0) + } else { + // void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, + // GLsizei width, GLsizei height, + // GLenum format, GLenum type, ArrayBufferView? pixels); + c.fnTexSubImage2D.Invoke(target, level, xoffset, yoffset, width, height, format, xtype, arr) + } +} + +func (c *defaultContext) Uniform1fv(location int32, value []float32) { + l := c.getUniformLocation(location) + arr := jsutil.TemporaryFloat32Array(len(value), value) + if c.webGL2 { + c.fnUniform1fv.Invoke(l, arr, 0, len(value)) + } else { + c.fnUniform1fv.Invoke(l, arr.Call("subarray", 0, len(value))) + } +} + +func (c *defaultContext) Uniform1i(location int32, v0 int32) { + l := c.getUniformLocation(location) + c.fnUniform1i.Invoke(l, v0) +} + +func (c *defaultContext) Uniform1iv(location int32, value []int32) { + l := c.getUniformLocation(location) + arr := jsutil.TemporaryInt32Array(len(value), value) + if c.webGL2 { + c.fnUniform1iv.Invoke(l, arr, 0, len(value)) + } else { + c.fnUniform1iv.Invoke(l, arr.Call("subarray", 0, len(value))) + } +} + +func (c *defaultContext) Uniform2fv(location int32, value []float32) { + l := c.getUniformLocation(location) + arr := jsutil.TemporaryFloat32Array(len(value), value) + if c.webGL2 { + c.fnUniform2fv.Invoke(l, arr, 0, len(value)) + } else { + c.fnUniform2fv.Invoke(l, arr.Call("subarray", 0, len(value))) + } +} + +func (c *defaultContext) Uniform3fv(location int32, value []float32) { + l := c.getUniformLocation(location) + arr := jsutil.TemporaryFloat32Array(len(value), value) + if c.webGL2 { + c.fnUniform3fv.Invoke(l, arr, 0, len(value)) + } else { + c.fnUniform3fv.Invoke(l, arr.Call("subarray", 0, len(value))) + } +} + +func (c *defaultContext) Uniform4fv(location int32, value []float32) { + l := c.getUniformLocation(location) + arr := jsutil.TemporaryFloat32Array(len(value), value) + if c.webGL2 { + c.fnUniform4fv.Invoke(l, arr, 0, len(value)) + } else { + c.fnUniform4fv.Invoke(l, arr.Call("subarray", 0, len(value))) + } +} + +func (c *defaultContext) UniformMatrix2fv(location int32, value []float32) { + l := c.getUniformLocation(location) + arr := jsutil.TemporaryFloat32Array(len(value), value) + if c.webGL2 { + c.fnUniformMatrix2fv.Invoke(l, false, arr, 0, len(value)) + } else { + c.fnUniformMatrix2fv.Invoke(l, false, arr.Call("subarray", 0, len(value))) + } +} + +func (c *defaultContext) UniformMatrix3fv(location int32, value []float32) { + l := c.getUniformLocation(location) + arr := jsutil.TemporaryFloat32Array(len(value), value) + if c.webGL2 { + c.fnUniformMatrix3fv.Invoke(l, false, arr, 0, len(value)) + } else { + c.fnUniformMatrix3fv.Invoke(l, false, arr.Call("subarray", 0, len(value))) + } +} + +func (c *defaultContext) UniformMatrix4fv(location int32, value []float32) { + l := c.getUniformLocation(location) + arr := jsutil.TemporaryFloat32Array(len(value), value) + if c.webGL2 { + c.fnUniformMatrix4fv.Invoke(l, false, arr, 0, len(value)) + } else { + c.fnUniformMatrix4fv.Invoke(l, false, arr.Call("subarray", 0, len(value))) + } +} + +func (c *defaultContext) UseProgram(program uint32) { + c.fnUseProgram.Invoke(c.programs.get(program)) +} + +func (c *defaultContext) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int) { + c.fnVertexAttribPointer.Invoke(index, size, xtype, normalized, stride, offset) +} + +func (c *defaultContext) Viewport(x int32, y int32, width int32, height int32) { + c.fnViewport.Invoke(x, y, width, height) +} diff --git a/internal/graphicsdriver/opengl/gl_js.go b/internal/graphicsdriver/opengl/gl_js.go deleted file mode 100644 index 16a5d795f..000000000 --- a/internal/graphicsdriver/opengl/gl_js.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2021 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 opengl - -import ( - "syscall/js" -) - -type jsGL struct { - activeTexture js.Value - attachShader js.Value - bindAttribLocation js.Value - bindBuffer js.Value - bindFramebuffer js.Value - bindRenderbuffer js.Value - bindTexture js.Value - blendEquationSeparate js.Value - blendFuncSeparate js.Value - bufferData js.Value - bufferSubData js.Value - checkFramebufferStatus js.Value - clear js.Value - colorMask js.Value - compileShader js.Value - createBuffer js.Value - createFramebuffer js.Value - createProgram js.Value - createRenderbuffer js.Value - createShader js.Value - createTexture js.Value - deleteBuffer js.Value - deleteFramebuffer js.Value - deleteProgram js.Value - deleteRenderbuffer js.Value - deleteShader js.Value - deleteTexture js.Value - disable js.Value - disableVertexAttribArray js.Value - drawElements js.Value - enable js.Value - enableVertexAttribArray js.Value - framebufferRenderbuffer js.Value - framebufferTexture2D js.Value - flush js.Value - getExtension js.Value - getParameter js.Value - getProgramInfoLog js.Value - getProgramParameter js.Value - getShaderInfoLog js.Value - getShaderParameter js.Value - getUniformLocation js.Value - isContextLost js.Value - isFramebuffer js.Value - isProgram js.Value - isRenderbuffer js.Value - isTexture js.Value - linkProgram js.Value - pixelStorei js.Value - readPixels js.Value - renderbufferStorage js.Value - scissor js.Value - shaderSource js.Value - stencilFunc js.Value - stencilMask js.Value - stencilOp js.Value - texImage2D js.Value - texSubImage2D js.Value - texParameteri js.Value - uniform1fv js.Value - uniform1i js.Value - uniform1iv js.Value - uniform2fv js.Value - uniform3fv js.Value - uniform4fv js.Value - uniformMatrix2fv js.Value - uniformMatrix3fv js.Value - uniformMatrix4fv js.Value - useProgram js.Value - vertexAttribPointer js.Value - viewport js.Value -} - -func (c *context) newJSGL(v js.Value) *jsGL { - // 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 := &jsGL{ - activeTexture: v.Get("activeTexture").Call("bind", v), - attachShader: v.Get("attachShader").Call("bind", v), - bindAttribLocation: v.Get("bindAttribLocation").Call("bind", v), - bindBuffer: v.Get("bindBuffer").Call("bind", v), - bindFramebuffer: v.Get("bindFramebuffer").Call("bind", v), - bindRenderbuffer: v.Get("bindRenderbuffer").Call("bind", v), - bindTexture: v.Get("bindTexture").Call("bind", v), - blendEquationSeparate: v.Get("blendEquationSeparate").Call("bind", v), - blendFuncSeparate: v.Get("blendFuncSeparate").Call("bind", v), - bufferData: v.Get("bufferData").Call("bind", v), - bufferSubData: v.Get("bufferSubData").Call("bind", v), - checkFramebufferStatus: v.Get("checkFramebufferStatus").Call("bind", v), - clear: v.Get("clear").Call("bind", v), - colorMask: v.Get("colorMask").Call("bind", v), - compileShader: v.Get("compileShader").Call("bind", v), - createBuffer: v.Get("createBuffer").Call("bind", v), - createFramebuffer: v.Get("createFramebuffer").Call("bind", v), - createProgram: v.Get("createProgram").Call("bind", v), - createRenderbuffer: v.Get("createRenderbuffer").Call("bind", v), - createShader: v.Get("createShader").Call("bind", v), - createTexture: v.Get("createTexture").Call("bind", v), - deleteBuffer: v.Get("deleteBuffer").Call("bind", v), - deleteFramebuffer: v.Get("deleteFramebuffer").Call("bind", v), - deleteProgram: v.Get("deleteProgram").Call("bind", v), - deleteRenderbuffer: v.Get("deleteRenderbuffer").Call("bind", v), - deleteShader: v.Get("deleteShader").Call("bind", v), - deleteTexture: v.Get("deleteTexture").Call("bind", v), - disable: v.Get("disable").Call("bind", v), - disableVertexAttribArray: v.Get("disableVertexAttribArray").Call("bind", v), - drawElements: v.Get("drawElements").Call("bind", v), - enable: v.Get("enable").Call("bind", v), - enableVertexAttribArray: v.Get("enableVertexAttribArray").Call("bind", v), - framebufferRenderbuffer: v.Get("framebufferRenderbuffer").Call("bind", v), - framebufferTexture2D: v.Get("framebufferTexture2D").Call("bind", v), - flush: v.Get("flush").Call("bind", v), - getParameter: v.Get("getParameter").Call("bind", v), - getProgramInfoLog: v.Get("getProgramInfoLog").Call("bind", v), - getProgramParameter: v.Get("getProgramParameter").Call("bind", v), - getShaderInfoLog: v.Get("getShaderInfoLog").Call("bind", v), - getShaderParameter: v.Get("getShaderParameter").Call("bind", v), - getUniformLocation: v.Get("getUniformLocation").Call("bind", v), - isContextLost: v.Get("isContextLost").Call("bind", v), - isFramebuffer: v.Get("isFramebuffer").Call("bind", v), - isProgram: v.Get("isProgram").Call("bind", v), - isRenderbuffer: v.Get("isRenderbuffer").Call("bind", v), - isTexture: v.Get("isTexture").Call("bind", v), - linkProgram: v.Get("linkProgram").Call("bind", v), - pixelStorei: v.Get("pixelStorei").Call("bind", v), - readPixels: v.Get("readPixels").Call("bind", v), - renderbufferStorage: v.Get("renderbufferStorage").Call("bind", v), - scissor: v.Get("scissor").Call("bind", v), - shaderSource: v.Get("shaderSource").Call("bind", v), - stencilFunc: v.Get("stencilFunc").Call("bind", v), - stencilMask: v.Get("stencilMask").Call("bind", v), - stencilOp: v.Get("stencilOp").Call("bind", v), - texImage2D: v.Get("texImage2D").Call("bind", v), - texSubImage2D: v.Get("texSubImage2D").Call("bind", v), - texParameteri: v.Get("texParameteri").Call("bind", v), - uniform1fv: v.Get("uniform1fv").Call("bind", v), - uniform1i: v.Get("uniform1i").Call("bind", v), - uniform1iv: v.Get("uniform1iv").Call("bind", v), - uniform2fv: v.Get("uniform2fv").Call("bind", v), - uniform3fv: v.Get("uniform3fv").Call("bind", v), - uniform4fv: v.Get("uniform4fv").Call("bind", v), - uniformMatrix2fv: v.Get("uniformMatrix2fv").Call("bind", v), - uniformMatrix3fv: v.Get("uniformMatrix3fv").Call("bind", v), - uniformMatrix4fv: v.Get("uniformMatrix4fv").Call("bind", v), - useProgram: v.Get("useProgram").Call("bind", v), - vertexAttribPointer: v.Get("vertexAttribPointer").Call("bind", v), - viewport: v.Get("viewport").Call("bind", v), - } - if !c.usesWebGL2() { - g.getExtension = v.Get("getExtension").Call("bind", v) - } - return g -} diff --git a/internal/graphicsdriver/opengl/image.go b/internal/graphicsdriver/opengl/image.go index a8c7d9b34..b318f37cb 100644 --- a/internal/graphicsdriver/opengl/image.go +++ b/internal/graphicsdriver/opengl/image.go @@ -44,10 +44,10 @@ func (i *Image) Dispose() { if i.framebuffer != nil { i.framebuffer.delete(&i.graphics.context) } - if !i.texture.equal(*new(textureNative)) { + if i.texture != 0 { i.graphics.context.deleteTexture(i.texture) } - if !i.stencil.equal(*new(renderbufferNative)) { + if i.stencil != 0 { i.graphics.context.deleteRenderbuffer(i.stencil) } @@ -100,7 +100,7 @@ func (i *Image) ensureFramebuffer() error { } func (i *Image) ensureStencilBuffer() error { - if !i.stencil.equal(*new(renderbufferNative)) { + if i.stencil != 0 { return nil } diff --git a/internal/graphicsdriver/opengl/locationcache.go b/internal/graphicsdriver/opengl/locationcache.go index 4d7c05497..b14943d7a 100644 --- a/internal/graphicsdriver/opengl/locationcache.go +++ b/internal/graphicsdriver/opengl/locationcache.go @@ -14,31 +14,28 @@ package opengl -// Since js.Object (Program) can't be keys of a map, use integers (programID) instead. - type locationCache struct { - uniformLocationCache map[programID]map[string]uniformLocation + uniformLocationCache map[program]map[string]uniformLocation } func newLocationCache() *locationCache { return &locationCache{ - uniformLocationCache: map[programID]map[string]uniformLocation{}, + uniformLocationCache: map[program]map[string]uniformLocation{}, } } func (c *locationCache) GetUniformLocation(context *context, p program, location string) uniformLocation { - id := getProgramID(p) - if _, ok := c.uniformLocationCache[id]; !ok { - c.uniformLocationCache[id] = map[string]uniformLocation{} + if _, ok := c.uniformLocationCache[p]; !ok { + c.uniformLocationCache[p] = map[string]uniformLocation{} } - l, ok := c.uniformLocationCache[id][location] + l, ok := c.uniformLocationCache[p][location] if !ok { l = context.getUniformLocationImpl(p, location) - c.uniformLocationCache[id][location] = l + c.uniformLocationCache[p][location] = l } return l } func (c *locationCache) deleteProgram(p program) { - delete(c.uniformLocationCache, getProgramID(p)) + delete(c.uniformLocationCache, p) } diff --git a/internal/graphicsdriver/opengl/program.go b/internal/graphicsdriver/opengl/program.go index b60bb8fc1..404f4b4f2 100644 --- a/internal/graphicsdriver/opengl/program.go +++ b/internal/graphicsdriver/opengl/program.go @@ -127,19 +127,14 @@ type openGLState struct { lastActiveTexture int } -var ( - zeroBuffer buffer - zeroProgram program -) - // reset resets or initializes the OpenGL state. func (s *openGLState) reset(context *context) error { if err := context.reset(); err != nil { return err } - s.lastProgram = zeroProgram - context.useProgram(zeroProgram) + s.lastProgram = 0 + context.useProgram(0) for key := range s.lastUniforms { delete(s.lastUniforms, key) } @@ -147,10 +142,10 @@ func (s *openGLState) reset(context *context) error { // On browsers (at least Chrome), buffers are already detached from the context // and must not be deleted by DeleteBuffer. if runtime.GOOS != "js" { - if !s.arrayBuffer.equal(zeroBuffer) { + if s.arrayBuffer != 0 { context.deleteBuffer(s.arrayBuffer) } - if !s.elementArrayBuffer.equal(zeroBuffer) { + if s.elementArrayBuffer != 0 { context.deleteBuffer(s.elementArrayBuffer) } } @@ -203,9 +198,9 @@ func (g *Graphics) textureVariableName(idx int) string { // useProgram uses the program (programTexture). func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textures [graphics.ShaderImageCount]textureVariable) error { - if !g.state.lastProgram.equal(program) { + if g.state.lastProgram != program { g.context.useProgram(program) - if g.state.lastProgram.equal(zeroProgram) { + if g.state.lastProgram == 0 { theArrayBufferLayout.enable(&g.context) g.context.bindArrayBuffer(g.state.arrayBuffer) g.context.bindElementArrayBuffer(g.state.elementArrayBuffer) @@ -251,7 +246,7 @@ loop: // If the texture is already bound, set the texture variable to point to the texture. // Rebinding the same texture seems problematic (#1193). for _, at := range g.activatedTextures { - if t.native.equal(at.textureNative) { + if t.native == at.textureNative { g.context.uniformInt(program, g.textureVariableName(i), at.index) continue loop }