如何解决每帧更新数兆字节的顶点数据
我正在研究可以使用bufferData
和STATIC_DRAW
上传到WebGL 1应用程序的顶点数据量的实际上限。我准备了一个代码段,可让您将每帧推入的三角形的数量从0调整到10,000'000。我没有使用效率更高的bufferSubData
和STREAM_DRAW
选项,因为我真的想体验最坏的情况。另外,我在屏幕上绘制的三角形非常小,这样填充率不会严重影响结果。
const canvas = document.createElement("canvas");
canvas.width = 320;
canvas.height = 180;
canvas.style.border = "1px solid black";
const gl = canvas.getContext("webgl");
document.body.appendChild(canvas);
const MAX_TRIANGLES = 10000000;
document.body.appendChild(document.createTextNode("Triangles to upload each frame: "));
const range = document.createElement("input");
range.type = "range";
range.min = 0;
range.max = MAX_TRIANGLES;
range.value = 100;
document.body.appendChild(range);
let nTriangles = range.value;
range.addEventListener("input",() => {
nTriangles = range.value;
});
const mbPerFrame = document.createElement("div");
document.body.appendChild(mbPerFrame);
const fps = document.createElement("div");
document.body.appendChild(fps);
gl.clearColor(0.2,0.3,0.5,1.0);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE,gl.ONE_MINUS_SRC_ALPHA);
const vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs,"attribute vec2 a_position; void main(void) { gl_Position = vec4(a_position / 100.0,0.0,1.0); }");
gl.compileShader(vs);
const fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs,"precision mediump float; void main(void) { gl_FragColor = vec4(0.05,0.05); }");
gl.compileShader(fs);
const program = gl.createProgram();
gl.attachShader(program,vs);
gl.attachShader(program,fs);
gl.linkProgram(program);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.vertexAttribPointer(0,2,gl.FLOAT,false,8,0);
gl.enableVertexAttribArray(0);
const randomVertexData = new Float32Array(3 * 2 * MAX_TRIANGLES);
for (let i = 0; i < 3 * MAX_TRIANGLES; i++) {
randomVertexData[i * 2 + 0] = Math.random() * 2 - 1;
randomVertexData[i * 2 + 1] = Math.random() * 2 - 1;
}
gl.useProgram(program);
const frameTimes = new Set();
function frame() {
frameTimes.add(performance.now());
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(randomVertexData.buffer,nTriangles * 3 * 2),gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLES,nTriangles * 3);
requestAnimationFrame(frame);
}
setInterval(() => {
const now = performance.now();
let framesLastSecond = 0;
frameTimes.forEach((frameTime) => {
if (now - frameTime < 1000) {
framesLastSecond++;
} else {
frameTimes.delete(frameTime);
}
});
mbPerFrame.innerText = `${Math.floor((nTriangles * 3 * 2 * 4 / (1024 * 1024)) * 100) / 100} MB per frame`;
fps.innerText = `${framesLastSecond} FPS`;
},1000);
requestAnimationFrame(frame);
render循环基本上是这样做的:
gl.bufferData(gl.ARRAY_BUFFER,gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLES,nTriangles * 3);
randomVertexData
是预先准备的一些数据,nTriangles
由滑块控制。
向右滑动滑块时,您会看到以MB为单位的大小增加而帧速率下降。
在我的机器(具有i7 vPro和Quadro M1000的戴尔笔记本电脑)上,每帧上传30 MB仍会产生60 FPS,这对于我的应用程序来说绰绰有余。在我的智能手机(Motorola G7 Power)上,每帧上传4 MB仍会产生60 FPS。
问题
- 我是否正确设计了这个性能实验?
- 我可以将其结果信任/扩展到现实世界吗?
- 您认为对于2020年中端移动设备和笔记本电脑,每帧推送合理数量的数据是什么?
解决方法
- 我是否正确设计了这个性能实验
这取决于您对“正确”的定义。 setInterval
不能保证绝对准确,但是代码假定您要求1000ms,而您得到了1000ms,而不是1100ms或900ms等。如果是我,我不会使用setInterval
并使用时间传递到requestAnimationFrame
并在最后1秒钟的时间内获得结果,并考虑到这些帧实际花费的时间
- 我可以信任它/将其结果扩展到现实世界吗?
我不知道这个问题是什么意思。每个gpu / cpu /浏览器/驱动程序都可能给出不同的结果。您可以尝试将结果与gpu / cpu /浏览器的某些指纹匹配。指纹可能与用户代理+ WEBGL_debug_renderer_info信息一样简单,尽管并非所有浏览器都支持WEBGL_debug_renderer_info或针对用户代理。
- 您认为对于2020年中端移动设备和笔记本电脑,每帧推送合理数量的数据是什么?
我不知道,我必须测试一堆不同的设备并在不同的浏览器上进行测试。
关于测试的更多评论
-
它正在使用
gl.STATIC_DRAW
,但可以说应该使用gl.DYNAMIC_DRAW
或gl.STREAM_DRAW
,因为这告诉浏览器/驱动程序您将经常更改数据(不知道哪个驱动程序使用此信息) -
这取决于驱动程序,但是对于许多驱动程序来说,一次分配缓冲区并使用
bufferSubData
上传新数据可能更快。在语义上bufferData
分配内存,而bufferSubData
不分配内存,尽管我听说情况相反,但内存分配通常比不分配要慢。 -
如果仅关心顶点上载速度,则可能希望将三角形绘制得尽可能小或根本不绘制(零尺寸三角形),以便消除绘制开销。换句话说,1000个512x512三角形的绘制速度比1000个2x2三角形慢得多,因为一个绘制4000像素,另一个绘制2.62亿像素。
const canvas = document.createElement("canvas");
canvas.width = 1;
canvas.height = 1;
canvas.style.border = "1px solid black";
const gl = canvas.getContext("webgl");
document.body.appendChild(canvas);
const MAX_TRIANGLES = 10000000;
document.body.appendChild(document.createTextNode("Triangles to upload each frame: "));
const range = document.createElement("input");
range.type = "range";
range.min = 0;
range.max = MAX_TRIANGLES;
range.value = 100;
document.body.appendChild(range);
let nTriangles = range.value;
range.addEventListener("input",() => {
nTriangles = range.value;
});
const mbPerFrame = document.createElement("div");
document.body.appendChild(mbPerFrame);
const fps = document.createElement("div");
document.body.appendChild(fps);
gl.clearColor(0.2,0.3,0.5,1.0);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE,gl.ONE_MINUS_SRC_ALPHA);
const vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs,"attribute vec2 a_position; void main(void) { gl_Position = vec4(a_position / 100.0,0.0,1.0); }");
gl.compileShader(vs);
const fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs,"precision mediump float; void main(void) { gl_FragColor = vec4(0.05,0.05); }");
gl.compileShader(fs);
const program = gl.createProgram();
gl.attachShader(program,vs);
gl.attachShader(program,fs);
gl.linkProgram(program);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,MAX_TRIANGLES * 3 * 2 * 4,gl.STREAM_DRAW);
gl.vertexAttribPointer(0,2,gl.FLOAT,false,8,0);
gl.enableVertexAttribArray(0);
const randomVertexData = new Float32Array(3 * 2 * MAX_TRIANGLES);
for (let i = 0; i < 3 * MAX_TRIANGLES; i++) {
randomVertexData[i * 2 + 0] = Math.random() * 2 - 1;
randomVertexData[i * 2 + 1] = Math.random() * 2 - 1;
}
gl.useProgram(program);
let then = 0;
let numFrames = 0;
function frame(now) {
const elapsedTime = now - then;
if (elapsedTime >= 1000) {
then = now;
mbPerFrame.innerText = `${(nTriangles * 3 * 2 * 4 / 1000 / 1000).toFixed(1)} MB per frame`;
fps.innerText = `${(numFrames / (elapsedTime * 0.001)).toFixed(1)} FPS`;
numFrames = 0;
}
++numFrames;
gl.bufferSubData(gl.ARRAY_BUFFER,new Float32Array(randomVertexData.buffer,nTriangles * 3 * 2));
gl.drawArrays(gl.TRIANGLES,nTriangles * 3);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。