zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

context_js.go (18628B)


      1 // Copyright 2014 Hajime Hoshi
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package opengl
     16 
     17 import (
     18 	"errors"
     19 	"fmt"
     20 	"syscall/js"
     21 
     22 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     23 	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gles"
     24 	"github.com/hajimehoshi/ebiten/v2/internal/jsutil"
     25 	"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
     26 )
     27 
     28 type (
     29 	textureNative      js.Value
     30 	renderbufferNative js.Value
     31 	framebufferNative  js.Value
     32 	shader             js.Value
     33 	buffer             js.Value
     34 	uniformLocation    js.Value
     35 
     36 	attribLocation int
     37 	programID      int
     38 	program        struct {
     39 		value js.Value
     40 		id    programID
     41 	}
     42 )
     43 
     44 func (t textureNative) equal(rhs textureNative) bool {
     45 	return js.Value(t).Equal(js.Value(rhs))
     46 }
     47 
     48 func (r renderbufferNative) equal(rhs renderbufferNative) bool {
     49 	return js.Value(r).Equal(js.Value(rhs))
     50 }
     51 
     52 func (f framebufferNative) equal(rhs framebufferNative) bool {
     53 	return js.Value(f).Equal(js.Value(rhs))
     54 }
     55 
     56 func (s shader) equal(rhs shader) bool {
     57 	return js.Value(s).Equal(js.Value(rhs))
     58 }
     59 
     60 func (b buffer) equal(rhs buffer) bool {
     61 	return js.Value(b).Equal(js.Value(rhs))
     62 }
     63 
     64 func (u uniformLocation) equal(rhs uniformLocation) bool {
     65 	return js.Value(u).Equal(js.Value(rhs))
     66 }
     67 
     68 func (p program) equal(rhs program) bool {
     69 	return p.value.Equal(rhs.value) && p.id == rhs.id
     70 }
     71 
     72 var InvalidTexture = textureNative(js.Null())
     73 
     74 var invalidUniform = uniformLocation(js.Null())
     75 
     76 func getProgramID(p program) programID {
     77 	return p.id
     78 }
     79 
     80 const (
     81 	zero             = operation(gles.ZERO)
     82 	one              = operation(gles.ONE)
     83 	srcAlpha         = operation(gles.SRC_ALPHA)
     84 	dstAlpha         = operation(gles.DST_ALPHA)
     85 	oneMinusSrcAlpha = operation(gles.ONE_MINUS_SRC_ALPHA)
     86 	oneMinusDstAlpha = operation(gles.ONE_MINUS_DST_ALPHA)
     87 	dstColor         = operation(gles.DST_COLOR)
     88 )
     89 
     90 type webGLVersion int
     91 
     92 const (
     93 	webGLVersionUnknown webGLVersion = iota
     94 	webGLVersion1
     95 	webGLVersion2
     96 )
     97 
     98 var (
     99 	webGL2MightBeAvailable = !forceWebGL1 && (js.Global().Get("WebGL2RenderingContext").Truthy() || js.Global().Get("go2cpp").Truthy())
    100 )
    101 
    102 type contextImpl struct {
    103 	gl            *gl
    104 	lastProgramID programID
    105 	webGLVersion  webGLVersion
    106 }
    107 
    108 func (c *context) usesWebGL2() bool {
    109 	return c.webGLVersion == webGLVersion2
    110 }
    111 
    112 func (c *context) initGL() {
    113 	c.webGLVersion = webGLVersionUnknown
    114 
    115 	var gl js.Value
    116 
    117 	// TODO: Define id?
    118 	if doc := js.Global().Get("document"); doc.Truthy() {
    119 		canvas := doc.Call("querySelector", "canvas")
    120 		attr := js.Global().Get("Object").New()
    121 		attr.Set("alpha", true)
    122 		attr.Set("premultipliedAlpha", true)
    123 		attr.Set("stencil", true)
    124 
    125 		if webGL2MightBeAvailable {
    126 			gl = canvas.Call("getContext", "webgl2", attr)
    127 			if gl.Truthy() {
    128 				c.webGLVersion = webGLVersion2
    129 			}
    130 		}
    131 
    132 		// Even though WebGL2RenderingContext exists, getting a webgl2 context might fail (#1738).
    133 		if !gl.Truthy() {
    134 			gl = canvas.Call("getContext", "webgl", attr)
    135 			if !gl.Truthy() {
    136 				gl = canvas.Call("getContext", "experimental-webgl", attr)
    137 			}
    138 			if gl.Truthy() {
    139 				c.webGLVersion = webGLVersion1
    140 			}
    141 		}
    142 
    143 		if !gl.Truthy() {
    144 			panic("opengl: getContext failed")
    145 		}
    146 	} else if go2cpp := js.Global().Get("go2cpp"); go2cpp.Truthy() {
    147 		gl = go2cpp.Get("gl")
    148 		c.webGLVersion = webGLVersion2
    149 	}
    150 
    151 	c.gl = c.newGL(gl)
    152 }
    153 
    154 func (c *context) reset() error {
    155 	c.locationCache = newLocationCache()
    156 	c.lastTexture = textureNative(js.Null())
    157 	c.lastFramebuffer = framebufferNative(js.Null())
    158 	c.lastViewportWidth = 0
    159 	c.lastViewportHeight = 0
    160 	c.lastCompositeMode = driver.CompositeModeUnknown
    161 
    162 	c.initGL()
    163 
    164 	if c.gl.isContextLost.Invoke().Bool() {
    165 		return driver.GraphicsNotReady
    166 	}
    167 	gl := c.gl
    168 	gl.enable.Invoke(gles.BLEND)
    169 	gl.enable.Invoke(gles.SCISSOR_TEST)
    170 	c.blendFunc(driver.CompositeModeSourceOver)
    171 	f := gl.getParameter.Invoke(gles.FRAMEBUFFER_BINDING)
    172 	c.screenFramebuffer = framebufferNative(f)
    173 
    174 	if !c.usesWebGL2() {
    175 		gl.getExtension.Invoke("OES_standard_derivatives")
    176 	}
    177 	return nil
    178 }
    179 
    180 func (c *context) blendFunc(mode driver.CompositeMode) {
    181 	if c.lastCompositeMode == mode {
    182 		return
    183 	}
    184 	c.lastCompositeMode = mode
    185 	s, d := mode.Operations()
    186 	s2, d2 := convertOperation(s), convertOperation(d)
    187 	gl := c.gl
    188 	gl.blendFunc.Invoke(int(s2), int(d2))
    189 }
    190 
    191 func (c *context) scissor(x, y, width, height int) {
    192 	gl := c.gl
    193 	gl.scissor.Invoke(x, y, width, height)
    194 }
    195 
    196 func (c *context) newTexture(width, height int) (textureNative, error) {
    197 	gl := c.gl
    198 	t := gl.createTexture.Invoke()
    199 	if !t.Truthy() {
    200 		return textureNative(js.Null()), errors.New("opengl: createTexture failed")
    201 	}
    202 	c.bindTexture(textureNative(t))
    203 
    204 	gl.texParameteri.Invoke(gles.TEXTURE_2D, gles.TEXTURE_MAG_FILTER, gles.NEAREST)
    205 	gl.texParameteri.Invoke(gles.TEXTURE_2D, gles.TEXTURE_MIN_FILTER, gles.NEAREST)
    206 	gl.texParameteri.Invoke(gles.TEXTURE_2D, gles.TEXTURE_WRAP_S, gles.CLAMP_TO_EDGE)
    207 	gl.texParameteri.Invoke(gles.TEXTURE_2D, gles.TEXTURE_WRAP_T, gles.CLAMP_TO_EDGE)
    208 
    209 	gl.pixelStorei.Invoke(gles.UNPACK_ALIGNMENT, 4)
    210 	// Firefox warns the usage of textures without specifying pixels (#629)
    211 	//
    212 	//     Error: WebGL warning: drawElements: This operation requires zeroing texture data. This is slow.
    213 	//
    214 	// In Ebiten, textures are filled with pixels laster by the filter that ignores destination, so it is fine
    215 	// to leave textures as uninitialized here. Rather, extra memory allocating for initialization should be
    216 	// avoided.
    217 	gl.texImage2D.Invoke(gles.TEXTURE_2D, 0, gles.RGBA, width, height, 0, gles.RGBA, gles.UNSIGNED_BYTE, nil)
    218 
    219 	return textureNative(t), nil
    220 }
    221 
    222 func (c *context) bindFramebufferImpl(f framebufferNative) {
    223 	gl := c.gl
    224 	gl.bindFramebuffer.Invoke(gles.FRAMEBUFFER, js.Value(f))
    225 }
    226 
    227 func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte {
    228 	gl := c.gl
    229 
    230 	c.bindFramebuffer(f.native)
    231 
    232 	l := 4 * width * height
    233 	p := jsutil.TemporaryUint8Array(l, nil)
    234 	gl.readPixels.Invoke(0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, p)
    235 
    236 	return jsutil.Uint8ArrayToSlice(p, l)
    237 }
    238 
    239 func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) {
    240 	gl := c.gl
    241 
    242 	c.bindFramebuffer(f.native)
    243 	gl.bindBuffer.Invoke(gles.PIXEL_PACK_BUFFER, js.Value(buffer))
    244 	// void gl.readPixels(x, y, width, height, format, type, GLintptr offset);
    245 	gl.readPixels.Invoke(0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, 0)
    246 	gl.bindBuffer.Invoke(gles.PIXEL_PACK_BUFFER, nil)
    247 }
    248 
    249 func (c *context) activeTexture(idx int) {
    250 	gl := c.gl
    251 	gl.activeTexture.Invoke(gles.TEXTURE0 + idx)
    252 }
    253 
    254 func (c *context) bindTextureImpl(t textureNative) {
    255 	gl := c.gl
    256 	gl.bindTexture.Invoke(gles.TEXTURE_2D, js.Value(t))
    257 }
    258 
    259 func (c *context) deleteTexture(t textureNative) {
    260 	gl := c.gl
    261 	if !gl.isTexture.Invoke(js.Value(t)).Bool() {
    262 		return
    263 	}
    264 	if c.lastTexture.equal(t) {
    265 		c.lastTexture = textureNative(js.Null())
    266 	}
    267 	gl.deleteTexture.Invoke(js.Value(t))
    268 }
    269 
    270 func (c *context) isTexture(t textureNative) bool {
    271 	// isTexture should not be called to detect context-lost since this performance is not good (#1175).
    272 	panic("opengl: isTexture is not implemented")
    273 }
    274 
    275 func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error) {
    276 	gl := c.gl
    277 	r := gl.createRenderbuffer.Invoke()
    278 	if !r.Truthy() {
    279 		return renderbufferNative(js.Null()), errors.New("opengl: createRenderbuffer failed")
    280 	}
    281 
    282 	c.bindRenderbuffer(renderbufferNative(r))
    283 	// TODO: Is STENCIL_INDEX8 portable?
    284 	// https://stackoverflow.com/questions/11084961/binding-a-stencil-render-buffer-to-a-frame-buffer-in-opengl
    285 	gl.renderbufferStorage.Invoke(gles.RENDERBUFFER, gles.STENCIL_INDEX8, width, height)
    286 
    287 	return renderbufferNative(r), nil
    288 }
    289 
    290 func (c *context) bindRenderbufferImpl(r renderbufferNative) {
    291 	gl := c.gl
    292 	gl.bindRenderbuffer.Invoke(gles.RENDERBUFFER, js.Value(r))
    293 }
    294 
    295 func (c *context) deleteRenderbuffer(r renderbufferNative) {
    296 	gl := c.gl
    297 	if !gl.isRenderbuffer.Invoke(js.Value(r)).Bool() {
    298 		return
    299 	}
    300 	if c.lastRenderbuffer.equal(r) {
    301 		c.lastRenderbuffer = renderbufferNative(js.Null())
    302 	}
    303 	gl.deleteRenderbuffer.Invoke(js.Value(r))
    304 }
    305 
    306 func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
    307 	gl := c.gl
    308 	f := gl.createFramebuffer.Invoke()
    309 	c.bindFramebuffer(framebufferNative(f))
    310 
    311 	gl.framebufferTexture2D.Invoke(gles.FRAMEBUFFER, gles.COLOR_ATTACHMENT0, gles.TEXTURE_2D, js.Value(t), 0)
    312 	if s := gl.checkFramebufferStatus.Invoke(gles.FRAMEBUFFER); s.Int() != gles.FRAMEBUFFER_COMPLETE {
    313 		return framebufferNative(js.Null()), errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s.Int()))
    314 	}
    315 
    316 	return framebufferNative(f), nil
    317 }
    318 
    319 func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) error {
    320 	gl := c.gl
    321 	c.bindFramebuffer(f)
    322 
    323 	gl.framebufferRenderbuffer.Invoke(gles.FRAMEBUFFER, gles.STENCIL_ATTACHMENT, gles.RENDERBUFFER, js.Value(r))
    324 	if s := gl.checkFramebufferStatus.Invoke(gles.FRAMEBUFFER); s.Int() != gles.FRAMEBUFFER_COMPLETE {
    325 		return errors.New(fmt.Sprintf("opengl: framebufferRenderbuffer failed: %d", s.Int()))
    326 	}
    327 	return nil
    328 }
    329 
    330 func (c *context) setViewportImpl(width, height int) {
    331 	gl := c.gl
    332 	gl.viewport.Invoke(0, 0, width, height)
    333 }
    334 
    335 func (c *context) deleteFramebuffer(f framebufferNative) {
    336 	gl := c.gl
    337 	if !gl.isFramebuffer.Invoke(js.Value(f)).Bool() {
    338 		return
    339 	}
    340 	// If a framebuffer to be deleted is bound, a newly bound framebuffer
    341 	// will be a default framebuffer.
    342 	// https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffers.xml
    343 	if c.lastFramebuffer.equal(f) {
    344 		c.lastFramebuffer = framebufferNative(js.Null())
    345 		c.lastViewportWidth = 0
    346 		c.lastViewportHeight = 0
    347 	}
    348 	gl.deleteFramebuffer.Invoke(js.Value(f))
    349 }
    350 
    351 func (c *context) newVertexShader(source string) (shader, error) {
    352 	return c.newShader(gles.VERTEX_SHADER, source)
    353 }
    354 
    355 func (c *context) newFragmentShader(source string) (shader, error) {
    356 	return c.newShader(gles.FRAGMENT_SHADER, source)
    357 }
    358 
    359 func (c *context) newShader(shaderType int, source string) (shader, error) {
    360 	gl := c.gl
    361 	s := gl.createShader.Invoke(int(shaderType))
    362 	if !s.Truthy() {
    363 		return shader(js.Null()), fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
    364 	}
    365 
    366 	gl.shaderSource.Invoke(js.Value(s), source)
    367 	gl.compileShader.Invoke(js.Value(s))
    368 
    369 	if !gl.getShaderParameter.Invoke(js.Value(s), gles.COMPILE_STATUS).Bool() {
    370 		log := gl.getShaderInfoLog.Invoke(js.Value(s))
    371 		return shader(js.Null()), fmt.Errorf("opengl: shader compile failed: %s", log)
    372 	}
    373 	return shader(s), nil
    374 }
    375 
    376 func (c *context) deleteShader(s shader) {
    377 	gl := c.gl
    378 	gl.deleteShader.Invoke(js.Value(s))
    379 }
    380 
    381 func (c *context) newProgram(shaders []shader, attributes []string) (program, error) {
    382 	gl := c.gl
    383 	v := gl.createProgram.Invoke()
    384 	if !v.Truthy() {
    385 		return program{}, errors.New("opengl: glCreateProgram failed")
    386 	}
    387 
    388 	for _, shader := range shaders {
    389 		gl.attachShader.Invoke(v, js.Value(shader))
    390 	}
    391 
    392 	for i, name := range attributes {
    393 		gl.bindAttribLocation.Invoke(v, i, name)
    394 	}
    395 
    396 	gl.linkProgram.Invoke(v)
    397 	if !gl.getProgramParameter.Invoke(v, gles.LINK_STATUS).Bool() {
    398 		info := gl.getProgramInfoLog.Invoke(v).String()
    399 		return program{}, fmt.Errorf("opengl: program error: %s", info)
    400 	}
    401 
    402 	id := c.lastProgramID
    403 	c.lastProgramID++
    404 	return program{
    405 		value: v,
    406 		id:    id,
    407 	}, nil
    408 }
    409 
    410 func (c *context) useProgram(p program) {
    411 	gl := c.gl
    412 	gl.useProgram.Invoke(p.value)
    413 }
    414 
    415 func (c *context) deleteProgram(p program) {
    416 	c.locationCache.deleteProgram(p)
    417 
    418 	gl := c.gl
    419 	if !gl.isProgram.Invoke(p.value).Bool() {
    420 		return
    421 	}
    422 	gl.deleteProgram.Invoke(p.value)
    423 }
    424 
    425 func (c *context) getUniformLocationImpl(p program, location string) uniformLocation {
    426 	gl := c.gl
    427 	return uniformLocation(gl.getUniformLocation.Invoke(p.value, location))
    428 }
    429 
    430 func (c *context) uniformInt(p program, location string, v int) bool {
    431 	gl := c.gl
    432 	l := c.locationCache.GetUniformLocation(c, p, location)
    433 	if l.equal(invalidUniform) {
    434 		return false
    435 	}
    436 	gl.uniform1i.Invoke(js.Value(l), v)
    437 	return true
    438 }
    439 
    440 func (c *context) uniformFloat(p program, location string, v float32) bool {
    441 	gl := c.gl
    442 	l := c.locationCache.GetUniformLocation(c, p, location)
    443 	if l.equal(invalidUniform) {
    444 		return false
    445 	}
    446 	gl.uniform1f.Invoke(js.Value(l), v)
    447 	return true
    448 }
    449 
    450 func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool {
    451 	gl := c.gl
    452 	l := c.locationCache.GetUniformLocation(c, p, location)
    453 	if l.equal(invalidUniform) {
    454 		return false
    455 	}
    456 
    457 	base := typ.Main
    458 	if base == shaderir.Array {
    459 		base = typ.Sub[0].Main
    460 	}
    461 
    462 	arr := jsutil.TemporaryFloat32Array(len(v), v)
    463 
    464 	switch base {
    465 	case shaderir.Float:
    466 		if c.usesWebGL2() {
    467 			gl.uniform1fv.Invoke(js.Value(l), arr, 0, len(v))
    468 		} else {
    469 			gl.uniform1fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
    470 		}
    471 	case shaderir.Vec2:
    472 		if c.usesWebGL2() {
    473 			gl.uniform2fv.Invoke(js.Value(l), arr, 0, len(v))
    474 		} else {
    475 			gl.uniform2fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
    476 		}
    477 	case shaderir.Vec3:
    478 		if c.usesWebGL2() {
    479 			gl.uniform3fv.Invoke(js.Value(l), arr, 0, len(v))
    480 		} else {
    481 			gl.uniform3fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
    482 		}
    483 	case shaderir.Vec4:
    484 		if c.usesWebGL2() {
    485 			gl.uniform4fv.Invoke(js.Value(l), arr, 0, len(v))
    486 		} else {
    487 			gl.uniform4fv.Invoke(js.Value(l), arr.Call("subarray", 0, len(v)))
    488 		}
    489 	case shaderir.Mat2:
    490 		if c.usesWebGL2() {
    491 			gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr, 0, len(v))
    492 		} else {
    493 			gl.uniformMatrix2fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
    494 		}
    495 	case shaderir.Mat3:
    496 		if c.usesWebGL2() {
    497 			gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr, 0, len(v))
    498 		} else {
    499 			gl.uniformMatrix3fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
    500 		}
    501 	case shaderir.Mat4:
    502 		if c.usesWebGL2() {
    503 			gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr, 0, len(v))
    504 		} else {
    505 			gl.uniformMatrix4fv.Invoke(js.Value(l), false, arr.Call("subarray", 0, len(v)))
    506 		}
    507 	default:
    508 		panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String()))
    509 	}
    510 
    511 	return true
    512 }
    513 
    514 func (c *context) vertexAttribPointer(index int, size int, stride int, offset int) {
    515 	gl := c.gl
    516 	gl.vertexAttribPointer.Invoke(index, size, gles.FLOAT, false, stride, offset)
    517 }
    518 
    519 func (c *context) enableVertexAttribArray(index int) {
    520 	gl := c.gl
    521 	gl.enableVertexAttribArray.Invoke(index)
    522 }
    523 
    524 func (c *context) disableVertexAttribArray(index int) {
    525 	gl := c.gl
    526 	gl.disableVertexAttribArray.Invoke(index)
    527 }
    528 
    529 func (c *context) newArrayBuffer(size int) buffer {
    530 	gl := c.gl
    531 	b := gl.createBuffer.Invoke()
    532 	gl.bindBuffer.Invoke(gles.ARRAY_BUFFER, js.Value(b))
    533 	gl.bufferData.Invoke(gles.ARRAY_BUFFER, size, gles.DYNAMIC_DRAW)
    534 	return buffer(b)
    535 }
    536 
    537 func (c *context) newElementArrayBuffer(size int) buffer {
    538 	gl := c.gl
    539 	b := gl.createBuffer.Invoke()
    540 	gl.bindBuffer.Invoke(gles.ELEMENT_ARRAY_BUFFER, js.Value(b))
    541 	gl.bufferData.Invoke(gles.ELEMENT_ARRAY_BUFFER, size, gles.DYNAMIC_DRAW)
    542 	return buffer(b)
    543 }
    544 
    545 func (c *context) bindArrayBuffer(b buffer) {
    546 	gl := c.gl
    547 	gl.bindBuffer.Invoke(gles.ARRAY_BUFFER, js.Value(b))
    548 }
    549 
    550 func (c *context) bindElementArrayBuffer(b buffer) {
    551 	gl := c.gl
    552 	gl.bindBuffer.Invoke(gles.ELEMENT_ARRAY_BUFFER, js.Value(b))
    553 }
    554 
    555 func (c *context) arrayBufferSubData(data []float32) {
    556 	gl := c.gl
    557 	l := len(data) * 4
    558 	arr := jsutil.TemporaryUint8Array(l, data)
    559 	if c.usesWebGL2() {
    560 		gl.bufferSubData.Invoke(gles.ARRAY_BUFFER, 0, arr, 0, l)
    561 	} else {
    562 		gl.bufferSubData.Invoke(gles.ARRAY_BUFFER, 0, arr.Call("subarray", 0, l))
    563 	}
    564 }
    565 
    566 func (c *context) elementArrayBufferSubData(data []uint16) {
    567 	gl := c.gl
    568 	l := len(data) * 2
    569 	arr := jsutil.TemporaryUint8Array(l, data)
    570 	if c.usesWebGL2() {
    571 		gl.bufferSubData.Invoke(gles.ELEMENT_ARRAY_BUFFER, 0, arr, 0, l)
    572 	} else {
    573 		gl.bufferSubData.Invoke(gles.ELEMENT_ARRAY_BUFFER, 0, arr.Call("subarray", 0, l))
    574 	}
    575 }
    576 
    577 func (c *context) deleteBuffer(b buffer) {
    578 	gl := c.gl
    579 	gl.deleteBuffer.Invoke(js.Value(b))
    580 }
    581 
    582 func (c *context) drawElements(len int, offsetInBytes int) {
    583 	gl := c.gl
    584 	gl.drawElements.Invoke(gles.TRIANGLES, len, gles.UNSIGNED_SHORT, offsetInBytes)
    585 }
    586 
    587 func (c *context) maxTextureSizeImpl() int {
    588 	gl := c.gl
    589 	return gl.getParameter.Invoke(gles.MAX_TEXTURE_SIZE).Int()
    590 }
    591 
    592 func (c *context) getShaderPrecisionFormatPrecision() int {
    593 	gl := c.gl
    594 	return gl.getShaderPrecisionFormat.Invoke(gles.FRAGMENT_SHADER, gles.HIGH_FLOAT).Get("precision").Int()
    595 }
    596 
    597 func (c *context) flush() {
    598 	gl := c.gl
    599 	gl.flush.Invoke()
    600 }
    601 
    602 func (c *context) needsRestoring() bool {
    603 	// Though it is possible to have a logic to restore the graphics data for GPU, do not use it for performance (#1603).
    604 	return false
    605 }
    606 
    607 func (c *context) canUsePBO() bool {
    608 	return false
    609 }
    610 
    611 func (c *context) texSubImage2D(t textureNative, args []*driver.ReplacePixelsArgs) {
    612 	c.bindTexture(t)
    613 	gl := c.gl
    614 	for _, a := range args {
    615 		arr := jsutil.TemporaryUint8Array(len(a.Pixels), a.Pixels)
    616 		if c.usesWebGL2() {
    617 			// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
    618 			//                    GLsizei width, GLsizei height,
    619 			//                    GLenum format, GLenum type, ArrayBufferView pixels, srcOffset);
    620 			gl.texSubImage2D.Invoke(gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr, 0)
    621 		} else {
    622 			// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
    623 			//                    GLsizei width, GLsizei height,
    624 			//                    GLenum format, GLenum type, ArrayBufferView? pixels);
    625 			gl.texSubImage2D.Invoke(gles.TEXTURE_2D, 0, a.X, a.Y, a.Width, a.Height, gles.RGBA, gles.UNSIGNED_BYTE, arr)
    626 		}
    627 	}
    628 }
    629 
    630 func (c *context) enableStencilTest() {
    631 	gl := c.gl
    632 	gl.enable.Invoke(gles.STENCIL_TEST)
    633 }
    634 
    635 func (c *context) disableStencilTest() {
    636 	gl := c.gl
    637 	gl.disable.Invoke(gles.STENCIL_TEST)
    638 }
    639 
    640 func (c *context) beginStencilWithEvenOddRule() {
    641 	gl := c.gl
    642 	gl.clear.Invoke(gles.STENCIL_BUFFER_BIT)
    643 	gl.stencilFunc.Invoke(gles.ALWAYS, 0x00, 0xff)
    644 	gl.stencilOp.Invoke(gles.KEEP, gles.KEEP, gles.INVERT)
    645 	gl.colorMask.Invoke(false, false, false, false)
    646 }
    647 
    648 func (c *context) endStencilWithEvenOddRule() {
    649 	gl := c.gl
    650 	gl.stencilFunc.Invoke(gles.NOTEQUAL, 0x00, 0xff)
    651 	gl.stencilOp.Invoke(gles.KEEP, gles.KEEP, gles.KEEP)
    652 	gl.colorMask.Invoke(true, true, true, true)
    653 }