Canvas repaint

Benchmark created on


Preparation HTML

<canvas id="canvas1" width="500" height="500"></canvas>
<canvas id="canvas2" width="500" height="500"></canvas>
<canvas id="canvas3" width="500" height="500"></canvas>

Setup

const c1 = document.querySelector('#canvas1');
const ctx1 = c1.getContext('2d');

ctx1.rect(30, 30, 400, 400);
ctx1.fillStyle = 'red';
ctx1.fill();

const c2 = document.querySelector('#canvas2');
const ctx2 = c2.getContext('2d');

const c3 = document.querySelector('#canvas3');
const gl = c3.getContext('webgl');

const vertexSource = `
attribute vec2 a_position;

uniform vec2 u_resolution;
uniform mat3 u_matrix;

varying vec2 v_texCoord;

void main() {

   gl_Position = vec4(u_matrix * vec3(a_position, 1), 1);
   v_texCoord = a_position;
}
`

const fragSource = `
precision mediump float;

uniform sampler2D u_image;
varying vec2 v_texCoord;

void main() {
   gl_FragColor = texture2D(u_image, v_texCoord);
}
`;

const program = gl.createProgram();
	
const vertexShader = compileShader(gl, vertexSource, gl.VERTEX_SHADER);
gl.attachShader(program, vertexShader);

const fragShader = compileShader(gl, fragSource, gl.FRAGMENT_SHADER);
gl.attachShader(program, fragShader);

gl.linkProgram(program);
	
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
	throw new Error(`${gl.getProgramInfoLog(program)}`);
}
	
const positionLocation = gl.getAttribLocation(program, 'a_position');
matrixLocation = gl.getUniformLocation(program, 'u_matrix');
// const textureLocation = gl.getUniformLocation(program, 'u_image');

gl.useProgram(program);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
	0, 0,
	1, 0,
	0, 1,
	0, 1,
	1, 0,
	1, 1
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

function compileShader(gl, source, type) {
	const shader = gl.createShader(type);
	gl.shaderSource(shader, source);
	gl.compileShader(shader);
	return shader;
}

Test runner

Ready to run.

Testing in
TestOps/sec
No transfer
createImageBitmap(c1)
	.then(() => deferred.resolve())
ready
Transfer image over 2D
createImageBitmap(c1)
	.then(image1 => ctx2.drawImage(image1, 0, 0))
	.then(() => deferred.resolve());
ready
Transfer canvas over 2D
ctx2.drawImage(c1, 0, 0);
ready
Transfer image over WebGL
createImageBitmap(c1).then(image => {
	const tex = gl.createTexture();
	gl.bindTexture(gl.TEXTURE_2D, tex);
	
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
	
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
	
	const dstX = 0;
	const dstY = 0;
	const dstWidth = image.width;
	const dstHeight = image.height;
	
	const clipX = dstX / gl.canvas.width * 2 - 1;
	const clipY = dstY / gl.canvas.height * -2 + 1;
	const clipWidth = dstWidth / gl.canvas.width * 2;
	const clipHeight = dstHeight / gl.canvas.height * -2;
	
	gl.uniformMatrix3fv(matrixLocation, false, [
      clipWidth, 0, 0,
      0, clipHeight, 0,
      clipX, clipY, 1,
    ]);
    
    gl.drawArrays(gl.TRIANGLES, 0, 6);
    deferred.resolve();
}).catch(err => {
	deferred.reject(err);
});
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.