twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

context_js.go (18718B)


      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/jsutil"
     24 	"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
     25 	"github.com/hajimehoshi/ebiten/v2/internal/web"
     26 )
     27 
     28 type (
     29 	textureNative     js.Value
     30 	framebufferNative js.Value
     31 	shader            js.Value
     32 	buffer            js.Value
     33 	uniformLocation   js.Value
     34 
     35 	attribLocation int
     36 	programID      int
     37 	program        struct {
     38 		value js.Value
     39 		id    programID
     40 	}
     41 )
     42 
     43 func (t textureNative) equal(rhs textureNative) bool {
     44 	return jsutil.Equal(js.Value(t), js.Value(rhs))
     45 }
     46 
     47 func (f framebufferNative) equal(rhs framebufferNative) bool {
     48 	return jsutil.Equal(js.Value(f), js.Value(rhs))
     49 }
     50 
     51 func (s shader) equal(rhs shader) bool {
     52 	return jsutil.Equal(js.Value(s), js.Value(rhs))
     53 }
     54 
     55 func (b buffer) equal(rhs buffer) bool {
     56 	return jsutil.Equal(js.Value(b), js.Value(rhs))
     57 }
     58 
     59 func (u uniformLocation) equal(rhs uniformLocation) bool {
     60 	return jsutil.Equal(js.Value(u), js.Value(rhs))
     61 }
     62 
     63 func (p program) equal(rhs program) bool {
     64 	return jsutil.Equal(p.value, rhs.value) && p.id == rhs.id
     65 }
     66 
     67 var InvalidTexture = textureNative(js.Null())
     68 
     69 var invalidUniform = uniformLocation(js.Null())
     70 
     71 func getProgramID(p program) programID {
     72 	return p.id
     73 }
     74 
     75 var (
     76 	vertexShader       shaderType
     77 	fragmentShader     shaderType
     78 	arrayBuffer        bufferType
     79 	elementArrayBuffer bufferType
     80 	dynamicDraw        bufferUsage
     81 	streamDraw         bufferUsage
     82 	pixelUnpackBuffer  bufferType
     83 	short              dataType
     84 	float              dataType
     85 
     86 	zero             operation
     87 	one              operation
     88 	srcAlpha         operation
     89 	dstAlpha         operation
     90 	oneMinusSrcAlpha operation
     91 	oneMinusDstAlpha operation
     92 	dstColor         operation
     93 
     94 	blend               js.Value
     95 	clampToEdge         js.Value
     96 	compileStatus       js.Value
     97 	colorAttachment0    js.Value
     98 	framebuffer_        js.Value
     99 	framebufferBinding  js.Value
    100 	framebufferComplete js.Value
    101 	highFloat           js.Value
    102 	linkStatus          js.Value
    103 	maxTextureSize      js.Value
    104 	nearest             js.Value
    105 	noError             js.Value
    106 	rgba                js.Value
    107 	texture2d           js.Value
    108 	textureMagFilter    js.Value
    109 	textureMinFilter    js.Value
    110 	textureWrapS        js.Value
    111 	textureWrapT        js.Value
    112 	triangles           js.Value
    113 	unpackAlignment     js.Value
    114 	unsignedByte        js.Value
    115 	unsignedShort       js.Value
    116 
    117 	texture0 int
    118 
    119 	isWebGL2Available bool
    120 )
    121 
    122 func init() {
    123 	// Accessing the prototype is rquired on Safari.
    124 	var contextPrototype js.Value
    125 	if !jsutil.Equal(js.Global().Get("WebGL2RenderingContext"), js.Undefined()) {
    126 		contextPrototype = js.Global().Get("WebGL2RenderingContext").Get("prototype")
    127 		isWebGL2Available = true
    128 	} else {
    129 		contextPrototype = js.Global().Get("WebGLRenderingContext").Get("prototype")
    130 	}
    131 
    132 	vertexShader = shaderType(contextPrototype.Get("VERTEX_SHADER").Int())
    133 	fragmentShader = shaderType(contextPrototype.Get("FRAGMENT_SHADER").Int())
    134 	arrayBuffer = bufferType(contextPrototype.Get("ARRAY_BUFFER").Int())
    135 	elementArrayBuffer = bufferType(contextPrototype.Get("ELEMENT_ARRAY_BUFFER").Int())
    136 	dynamicDraw = bufferUsage(contextPrototype.Get("DYNAMIC_DRAW").Int())
    137 	streamDraw = bufferUsage(contextPrototype.Get("STREAM_DRAW").Int())
    138 	short = dataType(contextPrototype.Get("SHORT").Int())
    139 	float = dataType(contextPrototype.Get("FLOAT").Int())
    140 
    141 	zero = operation(contextPrototype.Get("ZERO").Int())
    142 	one = operation(contextPrototype.Get("ONE").Int())
    143 	srcAlpha = operation(contextPrototype.Get("SRC_ALPHA").Int())
    144 	dstAlpha = operation(contextPrototype.Get("DST_ALPHA").Int())
    145 	oneMinusSrcAlpha = operation(contextPrototype.Get("ONE_MINUS_SRC_ALPHA").Int())
    146 	oneMinusDstAlpha = operation(contextPrototype.Get("ONE_MINUS_DST_ALPHA").Int())
    147 	dstColor = operation(contextPrototype.Get("DST_COLOR").Int())
    148 
    149 	blend = contextPrototype.Get("BLEND")
    150 	clampToEdge = contextPrototype.Get("CLAMP_TO_EDGE")
    151 	compileStatus = contextPrototype.Get("COMPILE_STATUS")
    152 	colorAttachment0 = contextPrototype.Get("COLOR_ATTACHMENT0")
    153 	framebuffer_ = contextPrototype.Get("FRAMEBUFFER")
    154 	framebufferBinding = contextPrototype.Get("FRAMEBUFFER_BINDING")
    155 	framebufferComplete = contextPrototype.Get("FRAMEBUFFER_COMPLETE")
    156 	highFloat = contextPrototype.Get("HIGH_FLOAT")
    157 	linkStatus = contextPrototype.Get("LINK_STATUS")
    158 	maxTextureSize = contextPrototype.Get("MAX_TEXTURE_SIZE")
    159 	nearest = contextPrototype.Get("NEAREST")
    160 	noError = contextPrototype.Get("NO_ERROR")
    161 	rgba = contextPrototype.Get("RGBA")
    162 	texture0 = contextPrototype.Get("TEXTURE0").Int()
    163 	texture2d = contextPrototype.Get("TEXTURE_2D")
    164 	textureMagFilter = contextPrototype.Get("TEXTURE_MAG_FILTER")
    165 	textureMinFilter = contextPrototype.Get("TEXTURE_MIN_FILTER")
    166 	textureWrapS = contextPrototype.Get("TEXTURE_WRAP_S")
    167 	textureWrapT = contextPrototype.Get("TEXTURE_WRAP_T")
    168 	triangles = contextPrototype.Get("TRIANGLES")
    169 	unpackAlignment = contextPrototype.Get("UNPACK_ALIGNMENT")
    170 	unsignedByte = contextPrototype.Get("UNSIGNED_BYTE")
    171 	unsignedShort = contextPrototype.Get("UNSIGNED_SHORT")
    172 
    173 	if isWebGL2Available {
    174 		pixelUnpackBuffer = bufferType(contextPrototype.Get("PIXEL_UNPACK_BUFFER").Int())
    175 	}
    176 }
    177 
    178 type contextImpl struct {
    179 	gl            js.Value
    180 	lastProgramID programID
    181 }
    182 
    183 func (c *context) ensureGL() {
    184 	if !jsutil.Equal(c.gl, js.Value{}) {
    185 		return
    186 	}
    187 
    188 	// TODO: Define id?
    189 	canvas := js.Global().Get("document").Call("querySelector", "canvas")
    190 	attr := js.Global().Get("Object").New()
    191 	attr.Set("alpha", true)
    192 	attr.Set("premultipliedAlpha", true)
    193 
    194 	var gl js.Value
    195 	if isWebGL2Available {
    196 		gl = canvas.Call("getContext", "webgl2", attr)
    197 	} else {
    198 		gl = canvas.Call("getContext", "webgl", attr)
    199 		if jsutil.Equal(gl, js.Null()) {
    200 			gl = canvas.Call("getContext", "experimental-webgl", attr)
    201 			if jsutil.Equal(gl, js.Null()) {
    202 				panic("opengl: getContext failed")
    203 			}
    204 		}
    205 	}
    206 
    207 	c.gl = gl
    208 }
    209 
    210 func (c *context) reset() error {
    211 	c.locationCache = newLocationCache()
    212 	c.lastTexture = textureNative(js.Null())
    213 	c.lastFramebuffer = framebufferNative(js.Null())
    214 	c.lastViewportWidth = 0
    215 	c.lastViewportHeight = 0
    216 	c.lastCompositeMode = driver.CompositeModeUnknown
    217 
    218 	c.gl = js.Value{}
    219 	c.ensureGL()
    220 	if c.gl.Call("isContextLost").Bool() {
    221 		return driver.GraphicsNotReady
    222 	}
    223 	gl := c.gl
    224 	gl.Call("enable", blend)
    225 	c.blendFunc(driver.CompositeModeSourceOver)
    226 	f := gl.Call("getParameter", framebufferBinding)
    227 	c.screenFramebuffer = framebufferNative(f)
    228 	return nil
    229 }
    230 
    231 func (c *context) blendFunc(mode driver.CompositeMode) {
    232 	if c.lastCompositeMode == mode {
    233 		return
    234 	}
    235 	c.lastCompositeMode = mode
    236 	s, d := mode.Operations()
    237 	s2, d2 := convertOperation(s), convertOperation(d)
    238 	c.ensureGL()
    239 	gl := c.gl
    240 	gl.Call("blendFunc", int(s2), int(d2))
    241 }
    242 
    243 func (c *context) newTexture(width, height int) (textureNative, error) {
    244 	c.ensureGL()
    245 	gl := c.gl
    246 	t := gl.Call("createTexture")
    247 	if jsutil.Equal(t, js.Null()) {
    248 		return textureNative(js.Null()), errors.New("opengl: glGenTexture failed")
    249 	}
    250 	gl.Call("pixelStorei", unpackAlignment, 4)
    251 	c.bindTexture(textureNative(t))
    252 
    253 	gl.Call("texParameteri", texture2d, textureMagFilter, nearest)
    254 	gl.Call("texParameteri", texture2d, textureMinFilter, nearest)
    255 	gl.Call("texParameteri", texture2d, textureWrapS, clampToEdge)
    256 	gl.Call("texParameteri", texture2d, textureWrapT, clampToEdge)
    257 
    258 	// Firefox warns the usage of textures without specifying pixels (#629)
    259 	//
    260 	//     Error: WebGL warning: drawElements: This operation requires zeroing texture data. This is slow.
    261 	//
    262 	// In Ebiten, textures are filled with pixels laster by the filter that ignores destination, so it is fine
    263 	// to leave textures as uninitialized here. Rather, extra memory allocating for initialization should be
    264 	// avoided.
    265 	gl.Call("texImage2D", texture2d, 0, rgba, width, height, 0, rgba, unsignedByte, nil)
    266 
    267 	return textureNative(t), nil
    268 }
    269 
    270 func (c *context) bindFramebufferImpl(f framebufferNative) {
    271 	c.ensureGL()
    272 	gl := c.gl
    273 	gl.Call("bindFramebuffer", framebuffer_, js.Value(f))
    274 }
    275 
    276 func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) {
    277 	c.ensureGL()
    278 	gl := c.gl
    279 
    280 	c.bindFramebuffer(f.native)
    281 
    282 	p := jsutil.TemporaryUint8Array(4 * width * height)
    283 	gl.Call("readPixels", 0, 0, width, height, rgba, unsignedByte, p)
    284 
    285 	return jsutil.Uint8ArrayToSlice(p), nil
    286 }
    287 
    288 func (c *context) activeTexture(idx int) {
    289 	c.ensureGL()
    290 	gl := c.gl
    291 	gl.Call("activeTexture", texture0+idx)
    292 }
    293 
    294 func (c *context) bindTextureImpl(t textureNative) {
    295 	c.ensureGL()
    296 	gl := c.gl
    297 	gl.Call("bindTexture", texture2d, js.Value(t))
    298 }
    299 
    300 func (c *context) deleteTexture(t textureNative) {
    301 	c.ensureGL()
    302 	gl := c.gl
    303 	if !gl.Call("isTexture", js.Value(t)).Bool() {
    304 		return
    305 	}
    306 	if c.lastTexture.equal(t) {
    307 		c.lastTexture = textureNative(js.Null())
    308 	}
    309 	gl.Call("deleteTexture", js.Value(t))
    310 }
    311 
    312 func (c *context) isTexture(t textureNative) bool {
    313 	// isTexture should not be called to detect context-lost since this performance is not good (#1175).
    314 	panic("opengl: isTexture is not implemented")
    315 }
    316 
    317 func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
    318 	c.ensureGL()
    319 	gl := c.gl
    320 	f := gl.Call("createFramebuffer")
    321 	c.bindFramebuffer(framebufferNative(f))
    322 
    323 	gl.Call("framebufferTexture2D", framebuffer_, colorAttachment0, texture2d, js.Value(t), 0)
    324 	if s := gl.Call("checkFramebufferStatus", framebuffer_); s.Int() != framebufferComplete.Int() {
    325 		return framebufferNative(js.Null()), errors.New(fmt.Sprintf("opengl: creating framebuffer failed: %d", s.Int()))
    326 	}
    327 
    328 	return framebufferNative(f), nil
    329 }
    330 
    331 func (c *context) setViewportImpl(width, height int) {
    332 	c.ensureGL()
    333 	gl := c.gl
    334 	gl.Call("viewport", 0, 0, width, height)
    335 }
    336 
    337 func (c *context) deleteFramebuffer(f framebufferNative) {
    338 	c.ensureGL()
    339 	gl := c.gl
    340 	if !gl.Call("isFramebuffer", js.Value(f)).Bool() {
    341 		return
    342 	}
    343 	// If a framebuffer to be deleted is bound, a newly bound framebuffer
    344 	// will be a default framebuffer.
    345 	// https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffers.xml
    346 	if c.lastFramebuffer.equal(f) {
    347 		c.lastFramebuffer = framebufferNative(js.Null())
    348 		c.lastViewportWidth = 0
    349 		c.lastViewportHeight = 0
    350 	}
    351 	gl.Call("deleteFramebuffer", js.Value(f))
    352 }
    353 
    354 func (c *context) newShader(shaderType shaderType, source string) (shader, error) {
    355 	c.ensureGL()
    356 	gl := c.gl
    357 	s := gl.Call("createShader", int(shaderType))
    358 	if jsutil.Equal(s, js.Null()) {
    359 		return shader(js.Null()), fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
    360 	}
    361 
    362 	gl.Call("shaderSource", js.Value(s), source)
    363 	gl.Call("compileShader", js.Value(s))
    364 
    365 	if !gl.Call("getShaderParameter", js.Value(s), compileStatus).Bool() {
    366 		log := gl.Call("getShaderInfoLog", js.Value(s))
    367 		return shader(js.Null()), fmt.Errorf("opengl: shader compile failed: %s", log)
    368 	}
    369 	return shader(s), nil
    370 }
    371 
    372 func (c *context) deleteShader(s shader) {
    373 	c.ensureGL()
    374 	gl := c.gl
    375 	gl.Call("deleteShader", js.Value(s))
    376 }
    377 
    378 func (c *context) newProgram(shaders []shader, attributes []string) (program, error) {
    379 	c.ensureGL()
    380 	gl := c.gl
    381 	v := gl.Call("createProgram")
    382 	if jsutil.Equal(v, js.Null()) {
    383 		return program{}, errors.New("opengl: glCreateProgram failed")
    384 	}
    385 
    386 	for _, shader := range shaders {
    387 		gl.Call("attachShader", v, js.Value(shader))
    388 	}
    389 
    390 	for i, name := range attributes {
    391 		gl.Call("bindAttribLocation", v, i, name)
    392 	}
    393 
    394 	gl.Call("linkProgram", v)
    395 	if !gl.Call("getProgramParameter", v, linkStatus).Bool() {
    396 		info := gl.Call("getProgramInfoLog", v).String()
    397 		return program{}, fmt.Errorf("opengl: program error: %s", info)
    398 	}
    399 
    400 	id := c.lastProgramID
    401 	c.lastProgramID++
    402 	return program{
    403 		value: v,
    404 		id:    id,
    405 	}, nil
    406 }
    407 
    408 func (c *context) useProgram(p program) {
    409 	c.ensureGL()
    410 	gl := c.gl
    411 	gl.Call("useProgram", p.value)
    412 }
    413 
    414 func (c *context) deleteProgram(p program) {
    415 	c.ensureGL()
    416 	gl := c.gl
    417 	if !gl.Call("isProgram", p.value).Bool() {
    418 		return
    419 	}
    420 	gl.Call("deleteProgram", p.value)
    421 }
    422 
    423 func (c *context) getUniformLocationImpl(p program, location string) uniformLocation {
    424 	c.ensureGL()
    425 	gl := c.gl
    426 	return uniformLocation(gl.Call("getUniformLocation", p.value, location))
    427 }
    428 
    429 func (c *context) uniformInt(p program, location string, v int) bool {
    430 	c.ensureGL()
    431 	gl := c.gl
    432 	l := c.locationCache.GetUniformLocation(c, p, location)
    433 	if l.equal(invalidUniform) {
    434 		return false
    435 	}
    436 	gl.Call("uniform1i", js.Value(l), v)
    437 	return true
    438 }
    439 
    440 func (c *context) uniformFloat(p program, location string, v float32) bool {
    441 	c.ensureGL()
    442 	gl := c.gl
    443 	l := c.locationCache.GetUniformLocation(c, p, location)
    444 	if l.equal(invalidUniform) {
    445 		return false
    446 	}
    447 	gl.Call("uniform1f", js.Value(l), v)
    448 	return true
    449 }
    450 
    451 func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool {
    452 	c.ensureGL()
    453 	gl := c.gl
    454 	l := c.locationCache.GetUniformLocation(c, p, location)
    455 	if l.equal(invalidUniform) {
    456 		return false
    457 	}
    458 
    459 	base := typ.Main
    460 	if base == shaderir.Array {
    461 		base = typ.Sub[0].Main
    462 	}
    463 
    464 	arr8 := jsutil.TemporaryUint8Array(len(v) * 4)
    465 	arr := js.Global().Get("Float32Array").New(arr8.Get("buffer"), arr8.Get("byteOffset"), len(v))
    466 	jsutil.CopySliceToJS(arr, v)
    467 
    468 	switch base {
    469 	case shaderir.Float:
    470 		gl.Call("uniform1fv", js.Value(l), arr)
    471 	case shaderir.Vec2:
    472 		gl.Call("uniform2fv", js.Value(l), arr)
    473 	case shaderir.Vec3:
    474 		gl.Call("uniform3fv", js.Value(l), arr)
    475 	case shaderir.Vec4:
    476 		gl.Call("uniform4fv", js.Value(l), arr)
    477 	case shaderir.Mat2:
    478 		gl.Call("uniformMatrix2fv", js.Value(l), false, arr)
    479 	case shaderir.Mat3:
    480 		gl.Call("uniformMatrix3fv", js.Value(l), false, arr)
    481 	case shaderir.Mat4:
    482 		gl.Call("uniformMatrix4fv", js.Value(l), false, arr)
    483 	default:
    484 		panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String()))
    485 	}
    486 
    487 	return true
    488 }
    489 
    490 func (c *context) vertexAttribPointer(p program, index int, size int, dataType dataType, stride int, offset int) {
    491 	c.ensureGL()
    492 	gl := c.gl
    493 	gl.Call("vertexAttribPointer", index, size, int(dataType), false, stride, offset)
    494 }
    495 
    496 func (c *context) enableVertexAttribArray(p program, index int) {
    497 	c.ensureGL()
    498 	gl := c.gl
    499 	gl.Call("enableVertexAttribArray", index)
    500 }
    501 
    502 func (c *context) disableVertexAttribArray(p program, index int) {
    503 	c.ensureGL()
    504 	gl := c.gl
    505 	gl.Call("disableVertexAttribArray", index)
    506 }
    507 
    508 func (c *context) newArrayBuffer(size int) buffer {
    509 	c.ensureGL()
    510 	gl := c.gl
    511 	b := gl.Call("createBuffer")
    512 	gl.Call("bindBuffer", int(arrayBuffer), js.Value(b))
    513 	gl.Call("bufferData", int(arrayBuffer), size, int(dynamicDraw))
    514 	return buffer(b)
    515 }
    516 
    517 func (c *context) newElementArrayBuffer(size int) buffer {
    518 	c.ensureGL()
    519 	gl := c.gl
    520 	b := gl.Call("createBuffer")
    521 	gl.Call("bindBuffer", int(elementArrayBuffer), js.Value(b))
    522 	gl.Call("bufferData", int(elementArrayBuffer), size, int(dynamicDraw))
    523 	return buffer(b)
    524 }
    525 
    526 func (c *context) bindBuffer(bufferType bufferType, b buffer) {
    527 	c.ensureGL()
    528 	gl := c.gl
    529 	gl.Call("bindBuffer", int(bufferType), js.Value(b))
    530 }
    531 
    532 func (c *context) arrayBufferSubData(data []float32) {
    533 	c.ensureGL()
    534 	gl := c.gl
    535 	arr8 := jsutil.TemporaryUint8Array(len(data) * 4)
    536 	arr := js.Global().Get("Float32Array").New(arr8.Get("buffer"), arr8.Get("byteOffset"), len(data))
    537 	jsutil.CopySliceToJS(arr, data)
    538 	gl.Call("bufferSubData", int(arrayBuffer), 0, arr)
    539 }
    540 
    541 func (c *context) elementArrayBufferSubData(data []uint16) {
    542 	c.ensureGL()
    543 	gl := c.gl
    544 	arr8 := jsutil.TemporaryUint8Array(len(data) * 2)
    545 	arr := js.Global().Get("Uint16Array").New(arr8.Get("buffer"), arr8.Get("byteOffset"), len(data))
    546 	jsutil.CopySliceToJS(arr, data)
    547 	gl.Call("bufferSubData", int(elementArrayBuffer), 0, arr)
    548 }
    549 
    550 func (c *context) deleteBuffer(b buffer) {
    551 	c.ensureGL()
    552 	gl := c.gl
    553 	gl.Call("deleteBuffer", js.Value(b))
    554 }
    555 
    556 func (c *context) drawElements(len int, offsetInBytes int) {
    557 	c.ensureGL()
    558 	gl := c.gl
    559 	gl.Call("drawElements", triangles, len, unsignedShort, offsetInBytes)
    560 }
    561 
    562 func (c *context) maxTextureSizeImpl() int {
    563 	c.ensureGL()
    564 	gl := c.gl
    565 	return gl.Call("getParameter", maxTextureSize).Int()
    566 }
    567 
    568 func (c *context) getShaderPrecisionFormatPrecision() int {
    569 	c.ensureGL()
    570 	gl := c.gl
    571 	return gl.Call("getShaderPrecisionFormat", js.ValueOf(int(fragmentShader)), highFloat).Get("precision").Int()
    572 }
    573 
    574 func (c *context) flush() {
    575 	c.ensureGL()
    576 	gl := c.gl
    577 	gl.Call("flush")
    578 }
    579 
    580 func (c *context) needsRestoring() bool {
    581 	return !web.IsMobileBrowser()
    582 }
    583 
    584 func (c *context) canUsePBO() bool {
    585 	return isWebGL2Available
    586 }
    587 
    588 func (c *context) texSubImage2D(t textureNative, width, height int, args []*driver.ReplacePixelsArgs) {
    589 	c.ensureGL()
    590 	c.bindTexture(t)
    591 	gl := c.gl
    592 	// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
    593 	//                    GLsizei width, GLsizei height,
    594 	//                    GLenum format, GLenum type, ArrayBufferView? pixels);
    595 	for _, a := range args {
    596 		arr := jsutil.TemporaryUint8Array(len(a.Pixels))
    597 		jsutil.CopySliceToJS(arr, a.Pixels)
    598 		gl.Call("texSubImage2D", texture2d, 0, a.X, a.Y, a.Width, a.Height, rgba, unsignedByte, arr)
    599 	}
    600 }
    601 
    602 func (c *context) newPixelBufferObject(width, height int) buffer {
    603 	c.ensureGL()
    604 	gl := c.gl
    605 	b := gl.Call("createBuffer")
    606 	gl.Call("bindBuffer", int(pixelUnpackBuffer), js.Value(b))
    607 	gl.Call("bufferData", int(pixelUnpackBuffer), 4*width*height, int(streamDraw))
    608 	gl.Call("bindBuffer", int(pixelUnpackBuffer), nil)
    609 	return buffer(b)
    610 }
    611 
    612 func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, height int, args []*driver.ReplacePixelsArgs) {
    613 	c.ensureGL()
    614 	c.bindTexture(t)
    615 	gl := c.gl
    616 	gl.Call("bindBuffer", int(pixelUnpackBuffer), js.Value(buffer))
    617 
    618 	stride := 4 * width
    619 	for _, a := range args {
    620 		arr := jsutil.TemporaryUint8Array(len(a.Pixels))
    621 		jsutil.CopySliceToJS(arr, a.Pixels)
    622 		offset := 4 * (a.Y*width + a.X)
    623 		for j := 0; j < a.Height; j++ {
    624 			gl.Call("bufferSubData", int(pixelUnpackBuffer), offset+stride*j, arr, 4*a.Width*j, 4*a.Width)
    625 		}
    626 	}
    627 
    628 	// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
    629 	//                    GLsizei width, GLsizei height,
    630 	//                    GLenum format, GLenum type, GLintptr offset);
    631 	gl.Call("texSubImage2D", texture2d, 0, 0, 0, width, height, rgba, unsignedByte, 0)
    632 	gl.Call("bindBuffer", int(pixelUnpackBuffer), nil)
    633 }