firefox 和 chrome 之间的 webgl 性能差异
webgl performance differences between firefox and chrome
我目前正在为网络应用程序开发图像处理工具。我需要使用相同大小的 png 图像并将它们逐个像素地组合起来。到目前为止,我已经建立了一个原型(很大程度上受到 webglfundamentals.org 上的教程的启发),它拍摄两张图像并将它们的像素相乘。我正在使用来自 http://twgljs.org/ 的 webgl 的 twgl-helper 库。 (不幸的是,我无法将其放入 fiddle)。
我有以下问题:任何人都可以解释或给出提示,为什么 Firefox 78 在这方面比最近的 Chrome 慢得多? FF 平均大约 34 毫秒每次渲染(完全刷新并在样本之间擦除现金)而 Chrome 平均每次渲染 0.27 毫秒。这是两个数量级的差异,我无法解释。我已经尝试过 webgl2,它对两者来说都稍微快一点,但保持两者之间的巨大差异。
如果我需要提供更多信息,请告诉我,我将在星期四回到办公室。感谢您的支持和想法。
function main() {
// Get A WebGL context
var canvas = document.getElementById("webgl");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
var canvas1 = document.getElementById("canvas1");
var canvas2 = document.getElementById("canvas2");
// setup GLSL program
var program = twgl.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);
var time0 = performance.now();
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
// provide texture coordinates for the rectangle.
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
// vertex attributes need to be turned on explicitly
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
// set the resolution
gl.uniform2f(resolutionLocation, canvas1.width, canvas1.height);
// Create a buffer for the position of the rectangle corners.
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// Set a rectangle the same size as the image.
setRectangle(gl, 0, 0, canvas.width, canvas.height);
// setRectangle(gl, 0, 0, 1000, 1000);
function setupTexture(canvas, textureUnit, program, uniformName) {
var tex = gl.createTexture();
updateTextureFromCanvas(tex, canvas, textureUnit);
// Set the parameters so we can render any size image.
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_MAG_FILTER, gl.LINEAR);
// mal ausprobieren
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
var location = gl.getUniformLocation(program, uniformName);
gl.uniform1i(location, textureUnit);
}
function updateTextureFromCanvas(tex, canvas, textureUnit) {
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
}
var tex1 = setupTexture(canvas1, 0, program, "u_canvas1");
var tex2 = setupTexture(canvas2, 1, program, "u_canvas2");
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
var time1 = performance.now();
console.log("Processing image took " + (time1 - time0) + " ms.");
document.getElementById("performance").innerHTML = "Processing image took " + (time1 - time0) + " ms.";
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2]), gl.STATIC_DRAW);
}
const WIDTH = 1600;
const HEIGHT = 900;
addNewImage = function (path, id, width, height) {
console.log(path)
let newElement = document.createElement("canvas");
document.body.appendChild(newElement);
newElement.id = id;
let ctx = newElement.getContext("2d");
ctx.canvas.width = width;
ctx.canvas.height = height;
let input = new Image();
input.crossOrigin = "anonymous";
input.onload = function () {
ctx.drawImage(input, 0, 0);
}
input.src = path;
}
addNewImage("https://i.imgur.com/KjUybBD.png", "canvas1", WIDTH, HEIGHT);
addNewImage("https://i.imgur.com/ZKMnXce.png", "canvas2", WIDTH, HEIGHT);
canvas {
border: 2px solid black;
display: inline-block;
width: 100%;
}
<script src="twgl.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
<button onclick="main()">click</button>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// our 2 canvases
uniform sampler2D u_canvas1;
uniform sampler2D u_canvas2;
// the texCoords passed in from the vertex shader.
// note: we're only using 1 set of texCoords which means
// we're assuming the canvases are the same size.
varying vec2 v_texCoord;
void main() {
// Look up a pixel from first canvas
vec4 color1 = texture2D(u_canvas1, v_texCoord);
// Look up a pixel from second canvas
vec4 color2 = texture2D(u_canvas2, v_texCoord);
// return the 2 colors multiplied
gl_FragColor = color1 * color2;
}
</script>
<!-- <canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas> -->
<div id="performance"></div>
<canvas id="webgl" width="1600" height="900"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
实际上不可能指望浏览器具有类似的性能。有很多测试表明一个浏览器比另一个浏览器快 2 到 40 倍。
在这种特殊情况下,我不知道为什么现代 Firefox 比 Chrome 慢。 Chrome 是多进程的(我认为 Firefox 在这一点上也是如此,但也许不是)所以在 Chrome 中,时间只是计时将命令插入命令缓冲区以从进程进行对话所需的时间那是与 GPU 对话的单独进程的 运行ning 网页。这不是计算 运行 那些 运行 与网页并行的命令实际需要多长时间。
如果我在你的绘图调用之后添加这个
// Force the webpage to wait for the GPU process
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
然后我得到 Chrome(27 毫秒)与 Firefox(32 毫秒)
的可比时间
function main() {
// Get A WebGL context
var canvas = document.getElementById("webgl");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
var canvas1 = document.getElementById("canvas1");
var canvas2 = document.getElementById("canvas2");
// setup GLSL program
var program = twgl.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);
var time0 = performance.now();
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
// provide texture coordinates for the rectangle.
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
// vertex attributes need to be turned on explicitly
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
// set the resolution
gl.uniform2f(resolutionLocation, canvas1.width, canvas1.height);
// Create a buffer for the position of the rectangle corners.
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// Set a rectangle the same size as the image.
setRectangle(gl, 0, 0, canvas.width, canvas.height);
// setRectangle(gl, 0, 0, 1000, 1000);
function setupTexture(canvas, textureUnit, program, uniformName) {
var tex = gl.createTexture();
updateTextureFromCanvas(tex, canvas, textureUnit);
// Set the parameters so we can render any size image.
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_MAG_FILTER, gl.LINEAR);
// mal ausprobieren
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
var location = gl.getUniformLocation(program, uniformName);
gl.uniform1i(location, textureUnit);
}
function updateTextureFromCanvas(tex, canvas, textureUnit) {
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
}
var tex1 = setupTexture(canvas1, 0, program, "u_canvas1");
var tex2 = setupTexture(canvas2, 1, program, "u_canvas2");
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
// Force the webpage to wait for the GPU process
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
var time1 = performance.now();
console.log("Processing image took " + (time1 - time0) + " ms.");
document.getElementById("performance").innerHTML = "Processing image took " + (time1 - time0) + " ms.";
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2]), gl.STATIC_DRAW);
}
const WIDTH = 1600;
const HEIGHT = 900;
addNewImage = function (path, id, width, height) {
console.log(path)
let newElement = document.createElement("canvas");
document.body.appendChild(newElement);
newElement.id = id;
let ctx = newElement.getContext("2d");
ctx.canvas.width = width;
ctx.canvas.height = height;
let input = new Image();
input.crossOrigin = "anonymous";
input.onload = function () {
ctx.drawImage(input, 0, 0);
}
input.src = path;
}
addNewImage("https://i.imgur.com/KjUybBD.png", "canvas1", WIDTH, HEIGHT);
addNewImage("https://i.imgur.com/ZKMnXce.png", "canvas2", WIDTH, HEIGHT);
canvas {
border: 2px solid black;
display: inline-block;
width: 100%;
}
<script src="twgl.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
<button onclick="main()">click</button>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// our 2 canvases
uniform sampler2D u_canvas1;
uniform sampler2D u_canvas2;
// the texCoords passed in from the vertex shader.
// note: we're only using 1 set of texCoords which means
// we're assuming the canvases are the same size.
varying vec2 v_texCoord;
void main() {
// Look up a pixel from first canvas
vec4 color1 = texture2D(u_canvas1, v_texCoord);
// Look up a pixel from second canvas
vec4 color2 = texture2D(u_canvas2, v_texCoord);
// return the 2 colors multiplied
gl_FragColor = color1 * color2;
}
</script>
<!-- <canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas> -->
<div id="performance"></div>
<canvas id="webgl" width="1600" height="900"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
当然,Chrome 运行 在另一个进程中执行命令这一事实意味着您可以免费获得一些并行处理。我认为大多数时候这是 Chrome WebGL 性能优于 Firefox WebGL 性能的胜利,但并非总是如此。
唯一想到的关于剩余速度差异的另一件事是浏览器如何将 canvas 传输到纹理。有很多可能性
首先 2D canvases 将其数据保留为预乘 alpha,但 WebGL 默认需要非预乘 alpha,因此
浏览器在内存中有2D canvas。它必须将该数据转换为未预乘的 alpha,然后通过 glTexImage2D 上传。 (慢)
浏览器在 vram 中有 2D canvas。它将它下载到 ram,将其转换为未预乘的 alpha,然后通过 glTexImage2D 上传(甚至更慢)
浏览器在 vram 中有 2D canvas。它将您的纹理附加到帧缓冲区,并使用不预乘 alpha(快速)的着色器将 canvas 渲染到其中。
我非常肯定 Chrome 会执行最后一种方法。我知道它的代码存在。我不知道确保使用该代码所需的所有条件,但我非常有信心 1900x600 canvas 会采用该路径(在某一点 canvas 低于某个特定尺寸,如 256x256在 CPU 上完成,而不是在 GPU 上完成,但我不知道这是否仍然正确)
Firefox 可能会或可能不会做同样的事情,但如果不是,那可能就是为什么当我们通过调用 gl.readPixels
停止 GPU 进程时 Chrome 在 27 毫秒内执行此操作而 Firefox 在 32 毫秒内执行此操作.
但更重要的一点是,浏览器可以以多种不同的方式进行优化,并且无法保证它们会或不会优化哪些方式。
我目前正在为网络应用程序开发图像处理工具。我需要使用相同大小的 png 图像并将它们逐个像素地组合起来。到目前为止,我已经建立了一个原型(很大程度上受到 webglfundamentals.org 上的教程的启发),它拍摄两张图像并将它们的像素相乘。我正在使用来自 http://twgljs.org/ 的 webgl 的 twgl-helper 库。 (不幸的是,我无法将其放入 fiddle)。
我有以下问题:任何人都可以解释或给出提示,为什么 Firefox 78 在这方面比最近的 Chrome 慢得多? FF 平均大约 34 毫秒每次渲染(完全刷新并在样本之间擦除现金)而 Chrome 平均每次渲染 0.27 毫秒。这是两个数量级的差异,我无法解释。我已经尝试过 webgl2,它对两者来说都稍微快一点,但保持两者之间的巨大差异。
如果我需要提供更多信息,请告诉我,我将在星期四回到办公室。感谢您的支持和想法。
function main() {
// Get A WebGL context
var canvas = document.getElementById("webgl");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
var canvas1 = document.getElementById("canvas1");
var canvas2 = document.getElementById("canvas2");
// setup GLSL program
var program = twgl.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);
var time0 = performance.now();
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
// provide texture coordinates for the rectangle.
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
// vertex attributes need to be turned on explicitly
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
// set the resolution
gl.uniform2f(resolutionLocation, canvas1.width, canvas1.height);
// Create a buffer for the position of the rectangle corners.
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// Set a rectangle the same size as the image.
setRectangle(gl, 0, 0, canvas.width, canvas.height);
// setRectangle(gl, 0, 0, 1000, 1000);
function setupTexture(canvas, textureUnit, program, uniformName) {
var tex = gl.createTexture();
updateTextureFromCanvas(tex, canvas, textureUnit);
// Set the parameters so we can render any size image.
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_MAG_FILTER, gl.LINEAR);
// mal ausprobieren
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
var location = gl.getUniformLocation(program, uniformName);
gl.uniform1i(location, textureUnit);
}
function updateTextureFromCanvas(tex, canvas, textureUnit) {
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
}
var tex1 = setupTexture(canvas1, 0, program, "u_canvas1");
var tex2 = setupTexture(canvas2, 1, program, "u_canvas2");
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
var time1 = performance.now();
console.log("Processing image took " + (time1 - time0) + " ms.");
document.getElementById("performance").innerHTML = "Processing image took " + (time1 - time0) + " ms.";
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2]), gl.STATIC_DRAW);
}
const WIDTH = 1600;
const HEIGHT = 900;
addNewImage = function (path, id, width, height) {
console.log(path)
let newElement = document.createElement("canvas");
document.body.appendChild(newElement);
newElement.id = id;
let ctx = newElement.getContext("2d");
ctx.canvas.width = width;
ctx.canvas.height = height;
let input = new Image();
input.crossOrigin = "anonymous";
input.onload = function () {
ctx.drawImage(input, 0, 0);
}
input.src = path;
}
addNewImage("https://i.imgur.com/KjUybBD.png", "canvas1", WIDTH, HEIGHT);
addNewImage("https://i.imgur.com/ZKMnXce.png", "canvas2", WIDTH, HEIGHT);
canvas {
border: 2px solid black;
display: inline-block;
width: 100%;
}
<script src="twgl.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
<button onclick="main()">click</button>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// our 2 canvases
uniform sampler2D u_canvas1;
uniform sampler2D u_canvas2;
// the texCoords passed in from the vertex shader.
// note: we're only using 1 set of texCoords which means
// we're assuming the canvases are the same size.
varying vec2 v_texCoord;
void main() {
// Look up a pixel from first canvas
vec4 color1 = texture2D(u_canvas1, v_texCoord);
// Look up a pixel from second canvas
vec4 color2 = texture2D(u_canvas2, v_texCoord);
// return the 2 colors multiplied
gl_FragColor = color1 * color2;
}
</script>
<!-- <canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas> -->
<div id="performance"></div>
<canvas id="webgl" width="1600" height="900"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
实际上不可能指望浏览器具有类似的性能。有很多测试表明一个浏览器比另一个浏览器快 2 到 40 倍。
在这种特殊情况下,我不知道为什么现代 Firefox 比 Chrome 慢。 Chrome 是多进程的(我认为 Firefox 在这一点上也是如此,但也许不是)所以在 Chrome 中,时间只是计时将命令插入命令缓冲区以从进程进行对话所需的时间那是与 GPU 对话的单独进程的 运行ning 网页。这不是计算 运行 那些 运行 与网页并行的命令实际需要多长时间。
如果我在你的绘图调用之后添加这个
// Force the webpage to wait for the GPU process
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
然后我得到 Chrome(27 毫秒)与 Firefox(32 毫秒)
的可比时间function main() {
// Get A WebGL context
var canvas = document.getElementById("webgl");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
var canvas1 = document.getElementById("canvas1");
var canvas2 = document.getElementById("canvas2");
// setup GLSL program
var program = twgl.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);
var time0 = performance.now();
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
// provide texture coordinates for the rectangle.
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
// vertex attributes need to be turned on explicitly
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
// set the resolution
gl.uniform2f(resolutionLocation, canvas1.width, canvas1.height);
// Create a buffer for the position of the rectangle corners.
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
// Set a rectangle the same size as the image.
setRectangle(gl, 0, 0, canvas.width, canvas.height);
// setRectangle(gl, 0, 0, 1000, 1000);
function setupTexture(canvas, textureUnit, program, uniformName) {
var tex = gl.createTexture();
updateTextureFromCanvas(tex, canvas, textureUnit);
// Set the parameters so we can render any size image.
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_MAG_FILTER, gl.LINEAR);
// mal ausprobieren
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
var location = gl.getUniformLocation(program, uniformName);
gl.uniform1i(location, textureUnit);
}
function updateTextureFromCanvas(tex, canvas, textureUnit) {
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
}
var tex1 = setupTexture(canvas1, 0, program, "u_canvas1");
var tex2 = setupTexture(canvas2, 1, program, "u_canvas2");
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
// Force the webpage to wait for the GPU process
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
var time1 = performance.now();
console.log("Processing image took " + (time1 - time0) + " ms.");
document.getElementById("performance").innerHTML = "Processing image took " + (time1 - time0) + " ms.";
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2]), gl.STATIC_DRAW);
}
const WIDTH = 1600;
const HEIGHT = 900;
addNewImage = function (path, id, width, height) {
console.log(path)
let newElement = document.createElement("canvas");
document.body.appendChild(newElement);
newElement.id = id;
let ctx = newElement.getContext("2d");
ctx.canvas.width = width;
ctx.canvas.height = height;
let input = new Image();
input.crossOrigin = "anonymous";
input.onload = function () {
ctx.drawImage(input, 0, 0);
}
input.src = path;
}
addNewImage("https://i.imgur.com/KjUybBD.png", "canvas1", WIDTH, HEIGHT);
addNewImage("https://i.imgur.com/ZKMnXce.png", "canvas2", WIDTH, HEIGHT);
canvas {
border: 2px solid black;
display: inline-block;
width: 100%;
}
<script src="twgl.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
<button onclick="main()">click</button>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// our 2 canvases
uniform sampler2D u_canvas1;
uniform sampler2D u_canvas2;
// the texCoords passed in from the vertex shader.
// note: we're only using 1 set of texCoords which means
// we're assuming the canvases are the same size.
varying vec2 v_texCoord;
void main() {
// Look up a pixel from first canvas
vec4 color1 = texture2D(u_canvas1, v_texCoord);
// Look up a pixel from second canvas
vec4 color2 = texture2D(u_canvas2, v_texCoord);
// return the 2 colors multiplied
gl_FragColor = color1 * color2;
}
</script>
<!-- <canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas> -->
<div id="performance"></div>
<canvas id="webgl" width="1600" height="900"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
当然,Chrome 运行 在另一个进程中执行命令这一事实意味着您可以免费获得一些并行处理。我认为大多数时候这是 Chrome WebGL 性能优于 Firefox WebGL 性能的胜利,但并非总是如此。
唯一想到的关于剩余速度差异的另一件事是浏览器如何将 canvas 传输到纹理。有很多可能性
首先 2D canvases 将其数据保留为预乘 alpha,但 WebGL 默认需要非预乘 alpha,因此
浏览器在内存中有2D canvas。它必须将该数据转换为未预乘的 alpha,然后通过 glTexImage2D 上传。 (慢)
浏览器在 vram 中有 2D canvas。它将它下载到 ram,将其转换为未预乘的 alpha,然后通过 glTexImage2D 上传(甚至更慢)
浏览器在 vram 中有 2D canvas。它将您的纹理附加到帧缓冲区,并使用不预乘 alpha(快速)的着色器将 canvas 渲染到其中。
我非常肯定 Chrome 会执行最后一种方法。我知道它的代码存在。我不知道确保使用该代码所需的所有条件,但我非常有信心 1900x600 canvas 会采用该路径(在某一点 canvas 低于某个特定尺寸,如 256x256在 CPU 上完成,而不是在 GPU 上完成,但我不知道这是否仍然正确)
Firefox 可能会或可能不会做同样的事情,但如果不是,那可能就是为什么当我们通过调用 gl.readPixels
停止 GPU 进程时 Chrome 在 27 毫秒内执行此操作而 Firefox 在 32 毫秒内执行此操作.
但更重要的一点是,浏览器可以以多种不同的方式进行优化,并且无法保证它们会或不会优化哪些方式。