Solution
I've finally figured out how to solve it:
When I've placed WebGL context initialization in very first call of animation frame, while actual rendering is done in all other calls, it worked as expected on my Android device. Here is main.js I've changed:
export default class Main {
constructor() {
this.render = this.render_first
requestAnimationFrame(() => this.animate())
}
showmsg(t,c) {
wx.showModal({
title: ""+t,
content: ""+c,
showCancel: false,
confirmText:'OK',
success: function(res){}
});
}
animate() {
this.render();
requestAnimationFrame(() => this.animate())
}
render_first() {
this.render = this.render_normal
var _this = this
this.domElement = canvas
var contextAttributes = {
alpha: false,
depth: true,
stencil: false,
antialias: false
}
this.domElement.addEventListener("webglcontextlost", function(e){
_this.showmsg("WebGL","Context lost");
}, false)
this.domElement.addEventListener("webglcontextrestored", function(e){
_this.showmsg("WebGL","Context restored");
}, false)
this._gl = this.domElement.getContext( 'webgl', contextAttributes ) || this.domElement.getContext( 'experimental-webgl', contextAttributes )
var _gl = this._gl
var vsrc = ""
vsrc += "uniform mat4 uModelView;"
vsrc += "uniform mat4 uProjView;"
vsrc += "attribute highp vec4 aPosition;"
vsrc += "void main(void) {"
vsrc += " gl_Position = ( uProjView * uModelView ) * aPosition;"
vsrc += "}"
var vid = _gl.createShader(_gl.VERTEX_SHADER)
_gl.shaderSource(vid,vsrc)
_gl.compileShader(vid)
if (!_gl.getShaderParameter(vid, _gl.COMPILE_STATUS)) {
console.error("Vertex shader failed: ", _gl.getShaderInfoLog(vid))
}
this._vid = vid
var fsrc = ""
fsrc += "void main(void) {"
fsrc += " gl_FragColor = vec4(1.0,1.0,0.0,1.0);"
fsrc += "}"
var fid = _gl.createShader(_gl.FRAGMENT_SHADER)
_gl.shaderSource(fid,fsrc)
_gl.compileShader(fid)
if (!_gl.getShaderParameter(fid, _gl.COMPILE_STATUS)) {
console.error("Fragment shader failed: ", _gl.getShaderInfoLog(fid))
}
this._fid = fid
var pid = _gl.createProgram()
_gl.attachShader(pid,vid)
_gl.attachShader(pid,fid)
_gl.linkProgram(pid)
if (!_gl.getProgramParameter(pid, _gl.LINK_STATUS)) {
let info = _gl.getProgramInfoLog(pid)
console.error("Program link failed:", info )
}
_gl.useProgram(pid)
this._pid = pid
var aPosition = _gl.getAttribLocation(pid,"aPosition")
var uModelView = _gl.getUniformLocation(pid,"uModelView")
var uProjView = _gl.getUniformLocation(pid,"uProjView")
_gl.uniformMatrix4fv( uModelView, false, [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1] )
_gl.uniformMatrix4fv( uProjView, false, [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1] )
this.uni = [uModelView, uProjView]
this.phase = 0.0
var data = [0,0,0,1,0,0,0,1,0]
var idata = [0,1,2]
var vbID = _gl.createBuffer()
_gl.bindBuffer(_gl.ARRAY_BUFFER,vbID)
_gl.bufferData(_gl.ARRAY_BUFFER, new Float32Array(data), _gl.STATIC_DRAW)
var vbiID = _gl.createBuffer();
_gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, vbiID)
_gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(idata), _gl.STATIC_DRAW)
_gl.vertexAttribPointer( aPosition, 3, _gl.FLOAT, false, 0, 0 )
_gl.enableVertexAttribArray( aPosition )
_gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, vbiID)
this.vb = [vbID,vbiID]
_gl.clearColor(1.0,0.0,1.0,1.0)
}
render_normal() {
var _gl = this._gl
var et = 60.0 / 1000.0
this.phase += 180.0 * 60.0 / 1000.0
var py = Math.sin(this.phase * Math.PI/180.0) * 0.5
_gl.uniformMatrix4fv( this.uni[0], false, [1,0,0,0,0,1,0,0,0,0,1,0,py,0,0,1] )
_gl.clear( _gl.COLOR_BUFFER_BIT )
_gl.drawElements(_gl.TRIANGLES, 3, _gl.UNSIGNED_SHORT, 0)
}
}
What was the problem
It seems that on an actual device, WeChat minigame runs animation loop in a separate thread than main JavaScript execution. Since WebGL (OpenGL) context is accessible only within the same thread (except in native apps where multiple threads can share same context), it would crash on the device since render function would attempt to access gl context that was initialized in a different thread.
This wasn't visible in WeChat Developer Tools, since that tool does not simulate exactly how device architecture work, and animation frame and JavaScript execution seems happen in the same thread.