每帧更新数兆字节的顶点数据

如何解决每帧更新数兆字节的顶点数据

我正在研究可以使用bufferDataSTATIC_DRAW上传到WebGL 1应用程序的顶点数据量的实际上限。我准备了一个代码段,可让您将每帧推入的三角形的数量从0调整到10,000'000。我没有使用效率更高的bufferSubDataSTREAM_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。

问题

  1. 我是否正确设计了这个性能实验?
  2. 我可以将其结果信任/扩展到现实世界吗?
  3. 您认为对于2020年中端移动设备和笔记本电脑,每帧推送合理数量的数据是什么?

解决方法

  1. 我是否正确设计了这个性能实验

这取决于您对“正确”的定义。 setInterval不能保证绝对准确,但是代码假定您要求1000ms,而您得到了1000ms,而不是1100ms或900ms等。如果是我,我不会使用setInterval并使用时间传递到requestAnimationFrame并在最后1秒钟的时间内获得结果,并考虑到这些帧实际花费的时间

  1. 我可以信任它/将其结果扩展到现实世界吗?

我不知道这个问题是什么意思。每个gpu / cpu /浏览器/驱动程序都可能给出不同的结果。您可以尝试将结果与gpu / cpu /浏览器的某些指纹匹配。指纹可能与用户代理+ WEBGL_debug_renderer_info信息一样简单,尽管并非所有浏览器都支持WEBGL_debug_renderer_info或针对用户代理。

  1. 您认为对于2020年中端移动设备和笔记本电脑,每帧推送合理数量的数据是什么?

我不知道,我必须测试一堆不同的设备并在不同的浏览器上进行测试。

关于测试的更多评论

  • 它正在使用gl.STATIC_DRAW,但可以说应该使用gl.DYNAMIC_DRAWgl.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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-