internal/graphicsdriver/opengl: Prepare function objects by bind

Passing a Go string to the JS world is expensive. This change reduces
this cost by preparing function objects by bind.

Closes #1438
This commit is contained in:
Hajime Hoshi 2021-04-05 23:53:17 +09:00
parent 5c8d8ab2eb
commit bd8367588e
2 changed files with 249 additions and 99 deletions

View File

@ -89,13 +89,11 @@ var (
) )
type contextImpl struct { type contextImpl struct {
gl js.Value gl *gl
lastProgramID programID lastProgramID programID
} }
func (c *context) initGL() { func (c *context) initGL() {
c.gl = js.Value{}
var gl js.Value var gl js.Value
// TODO: Define id? // TODO: Define id?
@ -120,7 +118,7 @@ func (c *context) initGL() {
gl = go2cpp.Get("gl") gl = go2cpp.Get("gl")
} }
c.gl = gl c.gl = newGL(gl)
} }
func (c *context) reset() error { func (c *context) reset() error {
@ -133,18 +131,18 @@ func (c *context) reset() error {
c.initGL() c.initGL()
if c.gl.Call("isContextLost").Bool() { if c.gl.isContextLost.Invoke().Bool() {
return driver.GraphicsNotReady return driver.GraphicsNotReady
} }
gl := c.gl gl := c.gl
gl.Call("enable", gles.BLEND) gl.enable.Invoke(gles.BLEND)
gl.Call("enable", gles.SCISSOR_TEST) gl.enable.Invoke(gles.SCISSOR_TEST)
c.blendFunc(driver.CompositeModeSourceOver) c.blendFunc(driver.CompositeModeSourceOver)
f := gl.Call("getParameter", gles.FRAMEBUFFER_BINDING) f := gl.getParameter.Invoke(gles.FRAMEBUFFER_BINDING)
c.screenFramebuffer = framebufferNative(f) c.screenFramebuffer = framebufferNative(f)
if !isWebGL2Available { if !isWebGL2Available {
gl.Call("getExtension", "OES_standard_derivatives") gl.getExtension.Invoke("OES_standard_derivatives")
} }
return nil return nil
} }
@ -157,27 +155,27 @@ func (c *context) blendFunc(mode driver.CompositeMode) {
s, d := mode.Operations() s, d := mode.Operations()
s2, d2 := convertOperation(s), convertOperation(d) s2, d2 := convertOperation(s), convertOperation(d)
gl := c.gl gl := c.gl
gl.Call("blendFunc", int(s2), int(d2)) gl.blendFunc.Invoke(int(s2), int(d2))
} }
func (c *context) scissor(x, y, width, height int) { func (c *context) scissor(x, y, width, height int) {
gl := c.gl gl := c.gl
gl.Call("scissor", x, y, width, height) gl.scissor.Invoke(x, y, width, height)
} }
func (c *context) newTexture(width, height int) (textureNative, error) { func (c *context) newTexture(width, height int) (textureNative, error) {
gl := c.gl gl := c.gl
t := gl.Call("createTexture") t := gl.createTexture.Invoke()
if jsutil.Equal(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", gles.UNPACK_ALIGNMENT, 4) gl.pixelStorei.Invoke(gles.UNPACK_ALIGNMENT, 4)
c.bindTexture(textureNative(t)) c.bindTexture(textureNative(t))
gl.Call("texParameteri", gles.TEXTURE_2D, gles.TEXTURE_MAG_FILTER, gles.NEAREST) gl.texParameteri.Invoke(gles.TEXTURE_2D, gles.TEXTURE_MAG_FILTER, gles.NEAREST)
gl.Call("texParameteri", gles.TEXTURE_2D, gles.TEXTURE_MIN_FILTER, gles.NEAREST) gl.texParameteri.Invoke(gles.TEXTURE_2D, gles.TEXTURE_MIN_FILTER, gles.NEAREST)
gl.Call("texParameteri", gles.TEXTURE_2D, gles.TEXTURE_WRAP_S, gles.CLAMP_TO_EDGE) gl.texParameteri.Invoke(gles.TEXTURE_2D, gles.TEXTURE_WRAP_S, gles.CLAMP_TO_EDGE)
gl.Call("texParameteri", gles.TEXTURE_2D, gles.TEXTURE_WRAP_T, gles.CLAMP_TO_EDGE) gl.texParameteri.Invoke(gles.TEXTURE_2D, gles.TEXTURE_WRAP_T, gles.CLAMP_TO_EDGE)
// Firefox warns the usage of textures without specifying pixels (#629) // Firefox warns the usage of textures without specifying pixels (#629)
// //
@ -186,14 +184,14 @@ func (c *context) newTexture(width, height int) (textureNative, error) {
// In Ebiten, textures are filled with pixels laster by the filter that ignores destination, so it is fine // In Ebiten, textures are filled with pixels laster by the filter that ignores destination, so it is fine
// to leave textures as uninitialized here. Rather, extra memory allocating for initialization should be // to leave textures as uninitialized here. Rather, extra memory allocating for initialization should be
// avoided. // avoided.
gl.Call("texImage2D", gles.TEXTURE_2D, 0, gles.RGBA, width, height, 0, gles.RGBA, gles.UNSIGNED_BYTE, nil) gl.texImage2D.Invoke(gles.TEXTURE_2D, 0, gles.RGBA, width, height, 0, gles.RGBA, gles.UNSIGNED_BYTE, nil)
return textureNative(t), nil return textureNative(t), nil
} }
func (c *context) bindFramebufferImpl(f framebufferNative) { func (c *context) bindFramebufferImpl(f framebufferNative) {
gl := c.gl gl := c.gl
gl.Call("bindFramebuffer", gles.FRAMEBUFFER, js.Value(f)) gl.bindFramebuffer.Invoke(gles.FRAMEBUFFER, js.Value(f))
} }
func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte {
@ -203,7 +201,7 @@ func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte {
l := 4 * width * height l := 4 * width * height
p := jsutil.TemporaryUint8Array(l, nil) p := jsutil.TemporaryUint8Array(l, nil)
gl.Call("readPixels", 0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, p) gl.readPixels.Invoke(0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, p)
return jsutil.Uint8ArrayToSlice(p, l) return jsutil.Uint8ArrayToSlice(p, l)
} }
@ -212,31 +210,31 @@ func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width
gl := c.gl gl := c.gl
c.bindFramebuffer(f.native) c.bindFramebuffer(f.native)
gl.Call("bindBuffer", gles.PIXEL_PACK_BUFFER, js.Value(buffer)) gl.bindBuffer.Invoke(gles.PIXEL_PACK_BUFFER, js.Value(buffer))
// void gl.readPixels(x, y, width, height, format, type, GLintptr offset); // void gl.readPixels(x, y, width, height, format, type, GLintptr offset);
gl.Call("readPixels", 0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, 0) gl.readPixels.Invoke(0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, 0)
gl.Call("bindBuffer", gles.PIXEL_PACK_BUFFER, nil) gl.bindBuffer.Invoke(gles.PIXEL_PACK_BUFFER, nil)
} }
func (c *context) activeTexture(idx int) { func (c *context) activeTexture(idx int) {
gl := c.gl gl := c.gl
gl.Call("activeTexture", gles.TEXTURE0+idx) gl.activeTexture.Invoke(gles.TEXTURE0 + idx)
} }
func (c *context) bindTextureImpl(t textureNative) { func (c *context) bindTextureImpl(t textureNative) {
gl := c.gl gl := c.gl
gl.Call("bindTexture", gles.TEXTURE_2D, js.Value(t)) gl.bindTexture.Invoke(gles.TEXTURE_2D, js.Value(t))
} }
func (c *context) deleteTexture(t textureNative) { func (c *context) deleteTexture(t textureNative) {
gl := c.gl gl := c.gl
if !gl.Call("isTexture", js.Value(t)).Bool() { if !gl.isTexture.Invoke(js.Value(t)).Bool() {
return return
} }
if c.lastTexture.equal(t) { if c.lastTexture.equal(t) {
c.lastTexture = textureNative(js.Null()) c.lastTexture = textureNative(js.Null())
} }
gl.Call("deleteTexture", js.Value(t)) gl.deleteTexture.Invoke(js.Value(t))
} }
func (c *context) isTexture(t textureNative) bool { func (c *context) isTexture(t textureNative) bool {
@ -246,11 +244,11 @@ func (c *context) isTexture(t textureNative) bool {
func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) { func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
gl := c.gl gl := c.gl
f := gl.Call("createFramebuffer") f := gl.createFramebuffer.Invoke()
c.bindFramebuffer(framebufferNative(f)) c.bindFramebuffer(framebufferNative(f))
gl.Call("framebufferTexture2D", gles.FRAMEBUFFER, gles.COLOR_ATTACHMENT0, gles.TEXTURE_2D, js.Value(t), 0) gl.framebufferTexture2D.Invoke(gles.FRAMEBUFFER, gles.COLOR_ATTACHMENT0, gles.TEXTURE_2D, js.Value(t), 0)
if s := gl.Call("checkFramebufferStatus", gles.FRAMEBUFFER); s.Int() != gles.FRAMEBUFFER_COMPLETE { if s := gl.checkFramebufferStatus.Invoke(gles.FRAMEBUFFER); s.Int() != gles.FRAMEBUFFER_COMPLETE {
return framebufferNative(js.Null()), errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s.Int())) return framebufferNative(js.Null()), errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s.Int()))
} }
@ -259,12 +257,12 @@ func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
func (c *context) setViewportImpl(width, height int) { func (c *context) setViewportImpl(width, height int) {
gl := c.gl gl := c.gl
gl.Call("viewport", 0, 0, width, height) gl.viewport.Invoke(0, 0, width, height)
} }
func (c *context) deleteFramebuffer(f framebufferNative) { func (c *context) deleteFramebuffer(f framebufferNative) {
gl := c.gl gl := c.gl
if !gl.Call("isFramebuffer", js.Value(f)).Bool() { if !gl.isFramebuffer.Invoke(js.Value(f)).Bool() {
return return
} }
// If a framebuffer to be deleted is bound, a newly bound framebuffer // If a framebuffer to be deleted is bound, a newly bound framebuffer
@ -275,7 +273,7 @@ func (c *context) deleteFramebuffer(f framebufferNative) {
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
} }
gl.Call("deleteFramebuffer", js.Value(f)) gl.deleteFramebuffer.Invoke(js.Value(f))
} }
func (c *context) newVertexShader(source string) (shader, error) { func (c *context) newVertexShader(source string) (shader, error) {
@ -288,16 +286,16 @@ func (c *context) newFragmentShader(source string) (shader, error) {
func (c *context) newShader(shaderType int, source string) (shader, error) { func (c *context) newShader(shaderType int, source string) (shader, error) {
gl := c.gl gl := c.gl
s := gl.Call("createShader", int(shaderType)) s := gl.createShader.Invoke(int(shaderType))
if jsutil.Equal(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)
} }
gl.Call("shaderSource", js.Value(s), source) gl.shaderSource.Invoke(js.Value(s), source)
gl.Call("compileShader", js.Value(s)) gl.compileShader.Invoke(js.Value(s))
if !gl.Call("getShaderParameter", js.Value(s), gles.COMPILE_STATUS).Bool() { if !gl.getShaderParameter.Invoke(js.Value(s), gles.COMPILE_STATUS).Bool() {
log := gl.Call("getShaderInfoLog", js.Value(s)) log := gl.getShaderInfoLog.Invoke(js.Value(s))
return shader(js.Null()), fmt.Errorf("opengl: shader compile failed: %s", log) return shader(js.Null()), fmt.Errorf("opengl: shader compile failed: %s", log)
} }
return shader(s), nil return shader(s), nil
@ -305,27 +303,27 @@ func (c *context) newShader(shaderType int, source string) (shader, error) {
func (c *context) deleteShader(s shader) { func (c *context) deleteShader(s shader) {
gl := c.gl gl := c.gl
gl.Call("deleteShader", js.Value(s)) gl.deleteShader.Invoke(js.Value(s))
} }
func (c *context) newProgram(shaders []shader, attributes []string) (program, error) { func (c *context) newProgram(shaders []shader, attributes []string) (program, error) {
gl := c.gl gl := c.gl
v := gl.Call("createProgram") v := gl.createProgram.Invoke()
if jsutil.Equal(v, js.Null()) { if jsutil.Equal(v, js.Null()) {
return program{}, errors.New("opengl: glCreateProgram failed") return program{}, errors.New("opengl: glCreateProgram failed")
} }
for _, shader := range shaders { for _, shader := range shaders {
gl.Call("attachShader", v, js.Value(shader)) gl.attachShader.Invoke(v, js.Value(shader))
} }
for i, name := range attributes { for i, name := range attributes {
gl.Call("bindAttribLocation", v, i, name) gl.bindAttribLocation.Invoke(v, i, name)
} }
gl.Call("linkProgram", v) gl.linkProgram.Invoke(v)
if !gl.Call("getProgramParameter", v, gles.LINK_STATUS).Bool() { if !gl.getProgramParameter.Invoke(v, gles.LINK_STATUS).Bool() {
info := gl.Call("getProgramInfoLog", v).String() info := gl.getProgramInfoLog.Invoke(v).String()
return program{}, fmt.Errorf("opengl: program error: %s", info) return program{}, fmt.Errorf("opengl: program error: %s", info)
} }
@ -339,20 +337,20 @@ func (c *context) newProgram(shaders []shader, attributes []string) (program, er
func (c *context) useProgram(p program) { func (c *context) useProgram(p program) {
gl := c.gl gl := c.gl
gl.Call("useProgram", p.value) gl.useProgram.Invoke(p.value)
} }
func (c *context) deleteProgram(p program) { func (c *context) deleteProgram(p program) {
gl := c.gl gl := c.gl
if !gl.Call("isProgram", p.value).Bool() { if !gl.isProgram.Invoke(p.value).Bool() {
return return
} }
gl.Call("deleteProgram", p.value) gl.deleteProgram.Invoke(p.value)
} }
func (c *context) getUniformLocationImpl(p program, location string) uniformLocation { func (c *context) getUniformLocationImpl(p program, location string) uniformLocation {
gl := c.gl gl := c.gl
return uniformLocation(gl.Call("getUniformLocation", p.value, location)) return uniformLocation(gl.getUniformLocation.Invoke(p.value, location))
} }
func (c *context) uniformInt(p program, location string, v int) bool { func (c *context) uniformInt(p program, location string, v int) bool {
@ -361,7 +359,7 @@ func (c *context) uniformInt(p program, location string, v int) bool {
if l.equal(invalidUniform) { if l.equal(invalidUniform) {
return false return false
} }
gl.Call("uniform1i", js.Value(l), v) gl.uniform1i.Invoke(js.Value(l), v)
return true return true
} }
@ -371,7 +369,7 @@ func (c *context) uniformFloat(p program, location string, v float32) bool {
if l.equal(invalidUniform) { if l.equal(invalidUniform) {
return false return false
} }
gl.Call("uniform1f", js.Value(l), v) gl.uniform1f.Invoke(js.Value(l), v)
return true return true
} }
@ -392,45 +390,45 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha
switch base { switch base {
case shaderir.Float: case shaderir.Float:
if isWebGL2Available { if isWebGL2Available {
gl.Call("uniform1fv", js.Value(l), arr, 0, len(v)) gl.uniform1fv.Invoke(js.Value(l), arr, 0, len(v))
} else { } else {
gl.Call("uniform1fv", js.Value(l), arr.Call("subarray", 0, len(v))) gl.uniform1fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
} }
case shaderir.Vec2: case shaderir.Vec2:
if isWebGL2Available { if isWebGL2Available {
gl.Call("uniform2fv", js.Value(l), arr, 0, len(v)) gl.uniform2fv.Invoke(js.Value(l), arr, 0, len(v))
} else { } else {
gl.Call("uniform2fv", js.Value(l), arr.Call("subarray", 0, len(v))) gl.uniform2fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
} }
case shaderir.Vec3: case shaderir.Vec3:
if isWebGL2Available { if isWebGL2Available {
gl.Call("uniform3fv", js.Value(l), arr, 0, len(v)) gl.uniform3fv.Invoke(js.Value(l), arr, 0, len(v))
} else { } else {
gl.Call("uniform3fv", js.Value(l), arr.Call("subarray", 0, len(v))) gl.uniform3fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
} }
case shaderir.Vec4: case shaderir.Vec4:
if isWebGL2Available { if isWebGL2Available {
gl.Call("uniform4fv", js.Value(l), arr, 0, len(v)) gl.uniform4fv.Invoke(js.Value(l), arr, 0, len(v))
} else { } else {
gl.Call("uniform4fv", js.Value(l), arr.Call("subarray", 0, len(v))) gl.uniform4fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
} }
case shaderir.Mat2: case shaderir.Mat2:
if isWebGL2Available { if isWebGL2Available {
gl.Call("uniformMatrix2fv", js.Value(l), false, arr, 0, len(v)) gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else { } else {
gl.Call("uniformMatrix2fv", js.Value(l), false, arr.Call("subarray", 0, len(v))) gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
} }
case shaderir.Mat3: case shaderir.Mat3:
if isWebGL2Available { if isWebGL2Available {
gl.Call("uniformMatrix3fv", js.Value(l), false, arr, 0, len(v)) gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else { } else {
gl.Call("uniformMatrix3fv", js.Value(l), false, arr.Call("subarray", 0, len(v))) gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
} }
case shaderir.Mat4: case shaderir.Mat4:
if isWebGL2Available { if isWebGL2Available {
gl.Call("uniformMatrix4fv", js.Value(l), false, arr, 0, len(v)) gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr, 0, len(v))
} else { } else {
gl.Call("uniformMatrix4fv", js.Value(l), false, arr.Call("subarray", 0, len(v))) gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
} }
default: default:
panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String())) panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String()))
@ -441,43 +439,43 @@ func (c *context) uniformFloats(p program, location string, v []float32, typ sha
func (c *context) vertexAttribPointer(index int, size int, stride int, offset int) { func (c *context) vertexAttribPointer(index int, size int, stride int, offset int) {
gl := c.gl gl := c.gl
gl.Call("vertexAttribPointer", index, size, gles.FLOAT, false, stride, offset) gl.vertexAttribPointer.Invoke(index, size, gles.FLOAT, false, stride, offset)
} }
func (c *context) enableVertexAttribArray(index int) { func (c *context) enableVertexAttribArray(index int) {
gl := c.gl gl := c.gl
gl.Call("enableVertexAttribArray", index) gl.enableVertexAttribArray.Invoke(index)
} }
func (c *context) disableVertexAttribArray(index int) { func (c *context) disableVertexAttribArray(index int) {
gl := c.gl gl := c.gl
gl.Call("disableVertexAttribArray", index) gl.disableVertexAttribArray.Invoke(index)
} }
func (c *context) newArrayBuffer(size int) buffer { func (c *context) newArrayBuffer(size int) buffer {
gl := c.gl gl := c.gl
b := gl.Call("createBuffer") b := gl.createBuffer.Invoke()
gl.Call("bindBuffer", gles.ARRAY_BUFFER, js.Value(b)) gl.bindBuffer.Invoke(gles.ARRAY_BUFFER, js.Value(b))
gl.Call("bufferData", gles.ARRAY_BUFFER, size, gles.DYNAMIC_DRAW) gl.bufferData.Invoke(gles.ARRAY_BUFFER, size, gles.DYNAMIC_DRAW)
return buffer(b) return buffer(b)
} }
func (c *context) newElementArrayBuffer(size int) buffer { func (c *context) newElementArrayBuffer(size int) buffer {
gl := c.gl gl := c.gl
b := gl.Call("createBuffer") b := gl.createBuffer.Invoke()
gl.Call("bindBuffer", gles.ELEMENT_ARRAY_BUFFER, js.Value(b)) gl.bindBuffer.Invoke(gles.ELEMENT_ARRAY_BUFFER, js.Value(b))
gl.Call("bufferData", gles.ELEMENT_ARRAY_BUFFER, size, gles.DYNAMIC_DRAW) gl.bufferData.Invoke(gles.ELEMENT_ARRAY_BUFFER, size, gles.DYNAMIC_DRAW)
return buffer(b) return buffer(b)
} }
func (c *context) bindArrayBuffer(b buffer) { func (c *context) bindArrayBuffer(b buffer) {
gl := c.gl gl := c.gl
gl.Call("bindBuffer", gles.ARRAY_BUFFER, js.Value(b)) gl.bindBuffer.Invoke(gles.ARRAY_BUFFER, js.Value(b))
} }
func (c *context) bindElementArrayBuffer(b buffer) { func (c *context) bindElementArrayBuffer(b buffer) {
gl := c.gl gl := c.gl
gl.Call("bindBuffer", gles.ELEMENT_ARRAY_BUFFER, js.Value(b)) gl.bindBuffer.Invoke(gles.ELEMENT_ARRAY_BUFFER, js.Value(b))
} }
func (c *context) arrayBufferSubData(data []float32) { func (c *context) arrayBufferSubData(data []float32) {
@ -485,9 +483,9 @@ func (c *context) arrayBufferSubData(data []float32) {
l := len(data) * 4 l := len(data) * 4
arr := jsutil.TemporaryUint8Array(l, data) arr := jsutil.TemporaryUint8Array(l, data)
if isWebGL2Available { if isWebGL2Available {
gl.Call("bufferSubData", gles.ARRAY_BUFFER, 0, arr, 0, l) gl.bufferSubData.Invoke(gles.ARRAY_BUFFER, 0, arr, 0, l)
} else { } else {
gl.Call("bufferSubData", gles.ARRAY_BUFFER, 0, arr.Call("subarray", 0, l)) gl.bufferSubData.Invoke(gles.ARRAY_BUFFER, 0, arr.Call("subarray", 0, l))
} }
} }
@ -496,35 +494,35 @@ func (c *context) elementArrayBufferSubData(data []uint16) {
l := len(data) * 2 l := len(data) * 2
arr := jsutil.TemporaryUint8Array(l, data) arr := jsutil.TemporaryUint8Array(l, data)
if isWebGL2Available { if isWebGL2Available {
gl.Call("bufferSubData", gles.ELEMENT_ARRAY_BUFFER, 0, arr, 0, l) gl.bufferSubData.Invoke(gles.ELEMENT_ARRAY_BUFFER, 0, arr, 0, l)
} else { } else {
gl.Call("bufferSubData", gles.ELEMENT_ARRAY_BUFFER, 0, arr.Call("subarray", 0, l)) gl.bufferSubData.Invoke(gles.ELEMENT_ARRAY_BUFFER, 0, arr.Call("subarray", 0, l))
} }
} }
func (c *context) deleteBuffer(b buffer) { func (c *context) deleteBuffer(b buffer) {
gl := c.gl gl := c.gl
gl.Call("deleteBuffer", js.Value(b)) gl.deleteBuffer.Invoke(js.Value(b))
} }
func (c *context) drawElements(len int, offsetInBytes int) { func (c *context) drawElements(len int, offsetInBytes int) {
gl := c.gl gl := c.gl
gl.Call("drawElements", gles.TRIANGLES, len, gles.UNSIGNED_SHORT, offsetInBytes) gl.drawElements.Invoke(gles.TRIANGLES, len, gles.UNSIGNED_SHORT, offsetInBytes)
} }
func (c *context) maxTextureSizeImpl() int { func (c *context) maxTextureSizeImpl() int {
gl := c.gl gl := c.gl
return gl.Call("getParameter", gles.MAX_TEXTURE_SIZE).Int() return gl.getParameter.Invoke(gles.MAX_TEXTURE_SIZE).Int()
} }
func (c *context) getShaderPrecisionFormatPrecision() int { func (c *context) getShaderPrecisionFormatPrecision() int {
gl := c.gl gl := c.gl
return gl.Call("getShaderPrecisionFormat", gles.FRAGMENT_SHADER, gles.HIGH_FLOAT).Get("precision").Int() return gl.getShaderPrecisionFormat.Invoke(gles.FRAGMENT_SHADER, gles.HIGH_FLOAT).Get("precision").Int()
} }
func (c *context) flush() { func (c *context) flush() {
gl := c.gl gl := c.gl
gl.Call("flush") gl.flush.Invoke()
} }
func (c *context) needsRestoring() bool { func (c *context) needsRestoring() bool {
@ -544,22 +542,22 @@ func (c *context) texSubImage2D(t textureNative, width, height int, args []*driv
// 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,
// GLenum format, GLenum type, ArrayBufferView pixels, srcOffset); // GLenum format, GLenum type, ArrayBufferView pixels, srcOffset);
gl.Call("texSubImage2D", gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr, 0) gl.texSubImage2D.Invoke(gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr, 0)
} else { } else {
// 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,
// GLenum format, GLenum type, ArrayBufferView? pixels); // GLenum format, GLenum type, ArrayBufferView? pixels);
gl.Call("texSubImage2D", gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr) gl.texSubImage2D.Invoke(gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr)
} }
} }
} }
func (c *context) newPixelBufferObject(width, height int) buffer { func (c *context) newPixelBufferObject(width, height int) buffer {
gl := c.gl gl := c.gl
b := gl.Call("createBuffer") b := gl.createBuffer.Invoke()
gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, js.Value(b)) gl.bindBuffer.Invoke(gles.PIXEL_UNPACK_BUFFER, js.Value(b))
gl.Call("bufferData", gles.PIXEL_UNPACK_BUFFER, 4*width*height, gles.STREAM_DRAW) gl.bufferData.Invoke(gles.PIXEL_UNPACK_BUFFER, 4*width*height, gles.STREAM_DRAW)
gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, nil) gl.bindBuffer.Invoke(gles.PIXEL_UNPACK_BUFFER, nil)
return buffer(b) return buffer(b)
} }
@ -570,34 +568,34 @@ func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, he
c.bindTexture(t) c.bindTexture(t)
gl := c.gl gl := c.gl
gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, js.Value(buffer)) gl.bindBuffer.Invoke(gles.PIXEL_UNPACK_BUFFER, js.Value(buffer))
stride := 4 * width stride := 4 * width
for _, a := range args { for _, a := range args {
arr := jsutil.TemporaryUint8Array(len(a.Pixels), a.Pixels) arr := jsutil.TemporaryUint8Array(len(a.Pixels), a.Pixels)
offset := 4 * (a.Y*width + a.X) offset := 4 * (a.Y*width + a.X)
for j := 0; j < a.Height; j++ { for j := 0; j < a.Height; j++ {
gl.Call("bufferSubData", gles.PIXEL_UNPACK_BUFFER, offset+stride*j, arr, 4*a.Width*j, 4*a.Width) gl.bufferSubData.Invoke(gles.PIXEL_UNPACK_BUFFER, offset+stride*j, arr, 4*a.Width*j, 4*a.Width)
} }
} }
// 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,
// GLenum format, GLenum type, GLintptr offset); // GLenum format, GLenum type, GLintptr offset);
gl.Call("texSubImage2D", gles.TEXTURE_2D, 0, 0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, 0) gl.texSubImage2D.Invoke(gles.TEXTURE_2D, 0, 0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, 0)
gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, nil) gl.bindBuffer.Invoke(gles.PIXEL_UNPACK_BUFFER, nil)
} }
func (c *context) getBufferSubData(buffer buffer, width, height int) []byte { func (c *context) getBufferSubData(buffer buffer, width, height int) []byte {
gl := c.gl gl := c.gl
gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, js.Value(buffer)) gl.bindBuffer.Invoke(gles.PIXEL_UNPACK_BUFFER, js.Value(buffer))
l := 4 * width * height l := 4 * width * height
arr := jsutil.TemporaryUint8Array(l, nil) arr := jsutil.TemporaryUint8Array(l, nil)
if isWebGL2Available { if isWebGL2Available {
gl.Call("getBufferSubData", gles.PIXEL_UNPACK_BUFFER, 0, arr, 0, l) gl.getBufferSubData.Invoke(gles.PIXEL_UNPACK_BUFFER, 0, arr, 0, l)
} else { } else {
gl.Call("getBufferSubData", gles.PIXEL_UNPACK_BUFFER, 0, arr.Call("subarray", 0, l)) gl.getBufferSubData.Invoke(gles.PIXEL_UNPACK_BUFFER, 0, arr.Call("subarray", 0, l))
} }
gl.Call("bindBuffer", gles.PIXEL_UNPACK_BUFFER, nil) gl.bindBuffer.Invoke(gles.PIXEL_UNPACK_BUFFER, nil)
return jsutil.Uint8ArrayToSlice(arr, l) return jsutil.Uint8ArrayToSlice(arr, l)
} }

View File

@ -0,0 +1,152 @@
// 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 gl struct {
activeTexture js.Value
attachShader js.Value
bindAttribLocation js.Value
bindBuffer js.Value
bindFramebuffer js.Value
bindTexture js.Value
blendFunc js.Value
bufferData js.Value
bufferSubData js.Value
checkFramebufferStatus js.Value
compileShader js.Value
createBuffer js.Value
createFramebuffer js.Value
createProgram js.Value
createShader js.Value
createTexture js.Value
deleteBuffer js.Value
deleteFramebuffer js.Value
deleteProgram js.Value
deleteShader js.Value
deleteTexture js.Value
disableVertexAttribArray js.Value
drawElements js.Value
enable js.Value
enableVertexAttribArray js.Value
framebufferTexture2D js.Value
flush js.Value
getBufferSubData js.Value
getExtension js.Value
getParameter js.Value
getProgramInfoLog js.Value
getProgramParameter js.Value
getShaderInfoLog js.Value
getShaderParameter js.Value
getShaderPrecisionFormat js.Value
getUniformLocation js.Value
isContextLost js.Value
isFramebuffer js.Value
isProgram js.Value
isTexture js.Value
linkProgram js.Value
pixelStorei js.Value
readPixels js.Value
scissor js.Value
shaderSource js.Value
texImage2D js.Value
texSubImage2D js.Value
texParameteri js.Value
uniform1f js.Value
uniform1fv js.Value
uniform2fv js.Value
uniform3fv js.Value
uniform4fv js.Value
uniform1i js.Value
uniformMatrix2fv js.Value
uniformMatrix3fv js.Value
uniformMatrix4fv js.Value
useProgram js.Value
vertexAttribPointer js.Value
viewport js.Value
}
func 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{
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),
bindTexture: v.Get("bindTexture").Call("bind", v),
blendFunc: v.Get("blendFunc").Call("bind", v),
bufferData: v.Get("bufferData").Call("bind", v),
bufferSubData: v.Get("bufferSubData").Call("bind", v),
checkFramebufferStatus: v.Get("checkFramebufferStatus").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),
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),
deleteShader: v.Get("deleteShader").Call("bind", v),
deleteTexture: v.Get("deleteTexture").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),
framebufferTexture2D: v.Get("framebufferTexture2D").Call("bind", v),
flush: v.Get("flush").Call("bind", v),
getBufferSubData: v.Get("getBufferSubData").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),
getShaderPrecisionFormat: v.Get("getShaderPrecisionFormat").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),
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),
scissor: v.Get("scissor").Call("bind", v),
shaderSource: v.Get("shaderSource").Call("bind", v),
texImage2D: v.Get("texImage2D").Call("bind", v),
texSubImage2D: v.Get("texSubImage2D").Call("bind", v),
texParameteri: v.Get("texParameteri").Call("bind", v),
uniform1f: v.Get("uniform1f").Call("bind", v),
uniform1fv: v.Get("uniform1fv").Call("bind", v),
uniform2fv: v.Get("uniform2fv").Call("bind", v),
uniform3fv: v.Get("uniform3fv").Call("bind", v),
uniform4fv: v.Get("uniform4fv").Call("bind", v),
uniform1i: v.Get("uniform1i").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 !isWebGL2Available {
g.getExtension = v.Get("getExtension").Call("bind", v)
}
return g
}