【问题标题】:WebGL/OpenGL: comparing the performanceWebGL/OpenGL:性能比较
【发布时间】:2014-08-03 03:32:33
【问题描述】:

出于教育目的,我需要比较 WebGL 与 OpenGL 的性能。我有两个用 WebGL 和 OpenGL 编写的等效程序,现在我需要获取它们的帧速率并比较它们。

在 Javascript 中,我使用 requestAnimationFrame 进行动画处理,我注意到它会导致帧速率始终保持在 60 FPS,并且只有在我切换选项卡或窗口时才会下降。另一方面,如果我总是递归调用渲染函数,那么窗口会因为明显的原因而冻结。

这就是我参加 FPS 的方式:

var stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '450px';
stats.domElement.style.top = '750px';

document.body.appendChild( stats.domElement );

setInterval( function () {

    stats.begin();
    stats.end();
}, 1000 / 60 );

            
var render= function() {
    requestAnimationFrame(render);
    renderer.render(scene,camera);
}

render();

如果场景总是以 60 FPS 运行,问题是我实际上无法将其与 OpenGL 的帧速率进行比较,因为 OpenGL 仅在以某种方式修改场景时(例如,如果我旋转对象)和 @ 987654323@ 被调用。

所以我猜 WebGL 中是否有一种方法可以仅在必要时重绘场景,例如当对象旋转或着色器中的某些属性发生更改时。

【问题讨论】:

    标签: javascript opengl opengl-es three.js webgl


    【解决方案1】:

    在 WebGL 中,您无法通过推送帧直接跨 GPU 比较帧速率。相反,您需要弄清楚在一个框架内可以完成多少工作。

    所以,基本上选择一些目标帧率,然后继续做越来越多的工作,直到超过目标。当您达到目标时,您可以做多少工作。您可以将其与使用相同技术的其他机器或 GPU 进行比较。

    有些人会建议使用glFinish 来检查时间。不幸的是,这实际上不起作用,因为它会停止图形管道,而且这种停止本身并不是在真实应用程序中通常会发生的事情。这就像计算汽车从 A 点到 B 点的速度,但不是在 A 点之前开始很久,在 B 点之后很久结束,而是在到达 B 点之前猛踩刹车并测量到达 B 点的时间。时间包括减速所花费的所有时间,这在每个 GPU 上是不同的,在 WebGL 和 OpenGL 之间是不同的,甚至对于每个浏览器也是不同的。您无法知道有多少时间花在了减速上,又有多少时间花在了您真正想要衡量的事情上。

    因此,您需要全程全速行驶。就像汽车一样,您会在到达 A 点之前加速到最高速度,然后一直保持最高速度直到您通过 B 点。就像他们在排位赛中为赛车计时一样。

    您通常不会通过猛击休息时间 (glFinish) 来停止 GPU,因此将停止时间添加到您的计时测量中是无关紧要的,并且不会为您提供有用的信息。使用 glFinish 你会计时绘图+停止。如果一个 GPU 在 1 秒内绘制并在 2 内停止,而另一个 GPU 在 2 秒内绘制并在 1 内停止,则两个 GPU 的计时将为 3 秒。但是如果你在不停止的情况下运行它们,一个 GPU 每秒会绘制 3 个东西,而另一个 GPU 每秒只能绘制 1.5 个东西。一个 GPU 显然更快,但使用 glFinish 你永远不会知道这一点。

    相反,您通过尽可能多地绘制来全速运行,然后测量您能够完成多少并保持全速。

    这是一个例子: http://webglsamples.org/lots-o-objects/lots-o-objects-draw-elements.html

    它基本上绘制每一帧。如果帧速率为 60fps,它会在下一帧再绘制 10 个对象。如果帧速率低于 60 fps,则绘制较少。

    由于浏览器计时并不完美,您可能会选择一个稍低的目标,例如 57fps,以了解它的速度。

    最重要的是,WebGL 和 OpenGL 真的只是与 GPU 对话,而 GPU 做真正的工作。无论是 WebGL 要求 GPU 还是 OpenGL,GPU 完成的工作都将花费完全相同的时间。唯一的区别在于设置 GPU 的开销。这意味着你真的不想画任何沉重的东西。理想情况下,你几乎什么都不会画。将您的画布设为 1x1 像素,绘制一个三角形,然后检查时间(例如在 60fps 的 WebGL 和 OpenGL 中一次可以绘制多少个三角形)。

    但情况会变得更糟。一个真正的应用程序会经常切换着色器、切换缓冲区、切换纹理、更新属性和制服。那么,你的时间是什么?您可以以 60 帧/秒的速度呼叫gl.drawBuffers 多少次?您可以以 60fps 调用多少次 gl.enablegl.vertexAttribPointergl.uniform4fv?某种组合?什么是合理的组合? 10% 呼叫gl.verterAttribPointer + 5% 呼叫gl.bindBuffer + 10% 呼叫gl.uniform。这些调用的时间是 WebGL 和 OpenGL 之间唯一不同的地方,因为最终它们与同一个 GPU 进行通信,而 GPU 无论如何都会以相同的速度运行。

    【讨论】:

    • 这句话:“这就像计算汽车从 A 点到 B 点的速度,但不是在 A 之前开始很长时间,在 B 之后很长时间结束,而是在你猛踩刹车之前您到达 B 并测量到达 B 的时间。” 完全令人困惑。从字面上看,这就是汽车杂志中衡量性能的方式,0-60-0 时间和断裂距离。只要所有测试汽车的程序相同,它就是一种有效的测量方法,可用于比较。
    • 如果您想知道汽车的加速和停止速度有多快,那么可以。这就是你如何计时。同样,如果您想知道某个 API 渲染 AND STOP 的速度有多快。但这不是你想要的。你想要表演。那是多快,而不是多快+停止。如果一辆车需要 2 秒加速 + 3 秒刹车。而在另一辆车上,它需要 3 秒的加速和 2 秒的刹车?您拥有的唯一数据是两辆车都花了 5 秒。这并不能告诉你哪辆车的性能更快。其中一辆车实际上比另一辆车跑得更快。
    • 不,这不是衡量汽车性能的方式。有时间去看比赛。赛车绕第一圈行驶,第二圈开始计时开始,通过起点全速行驶。时钟在第二圈结束时停止,赛车在冲过终点线时仍然全速行驶。这就是你如何找到最快的汽车。您不会在休息时猛冲并试图在终点线停下车。
    • 您通常不会通过猛击休息时间 (glFinish) 来停止 GPU,因此将停止时间添加到您的计时测量中是无关紧要的,并且不会为您提供有用的信息。你定时绘图+停止。就像汽车一样,如果一个 GPU 在 1 秒内绘制并停止 2,另一个 GPU 在 2 秒内绘制并在 1 内停止,您的时间将为两个 GPU 显示 3 秒。但是如果你在不停止的情况下运行它们,一个 GPU 每秒会绘制 3 个东西,而另一个 GPU 每秒只能绘制 1.5 个东西。一个 GPU 显然更快,但使用 glFinish 你永远不会知道。
    • 如果我不使用three.js 而是编写一个普通的WebGL 程序会怎样?还能用老方法算帧率吗?
    【解决方案2】:

    您实际上不想使用帧速率来比较这些东西,因为正如您刚才提到的,由于 VSYNC,您被人为地限制为 60 FPS。

    当使用 VSYNC 并且您希望将这一混乱因素排除在性能测量之外时,交换缓冲区操作会限制显示的帧数。您应该做的是在帧开始时启动计时器,然后在帧结束时(就在缓冲区交换之前)发出glFinish (...) 并结束计时器。比较要绘制的毫秒数(或您的计时器测量的任何分辨率)而不是绘制的帧数。

    【讨论】:

    • 请注意,这里使用glFinish (...) 可以绕过 VSYNC 进行帧性能计时,但它也会强制 CPU/GPU 同步。您不想在正常渲染期间使用此技术,只是为了对帧的持续时间进行计时以进行性能比较。如果这仅与 OpenGL 而不是 WebGL(OpenGL ES)有关,我建议使用计时器查询(OpenGL 3.3+ 功能),因为它做同样的事情的侵入性要小得多。
    • 我正在使用three.js,所有都是在较低级别抽象完成的,我自己从不交换缓冲区。
    • 更好的是,只需在用于创建框架和结束计时器的函数结束时调用glFinish (...)。要获得您的有效帧率(基本上是没有 VSYNC 时您的帧率),将每帧的持续时间相加,直到达到 1 秒,然后检查所花费的帧数。
    • glFinish 在 WebGL 中不起作用,没有任何意义。它实际上也会在实际 GL 中给出错误的结果
    • 我什至没有动画,我只是不断地旋转对象,渲染器为我绘制场景。
    【解决方案3】:

    正确的解决方案是在可用时使用ANGLE_timer_query 扩展。

    引用规范:

    OpenGL 实现在历史上几乎没有提供任何用处 时间信息。应用程序可以通过 在 CPU 上读取计时器,但这些计时器与 图形渲染管道。读取 CPU 定时器不会 保证完成潜在的大量图形 在读取计时器之前累积的工作,因此会产生 非常不准确的结果。 glFinish() 可用于 确定何时 之前的渲染命令已经完成,但是会闲置 图形管道并对应用程序性能产生不利影响。

    此扩展提供了一种查询机制,可用于 确定完全完成一组 GL 所需的时间 命令,并且不会停止渲染管道。它使用 查询对象机制首先在遮挡查询中引入 扩展,它允许通过异步轮询时间间隔 应用程序。

    (强调我的)

    【讨论】:

    • 该扩展仅适用于 WebGL 的一种实现。我已经在补充我的答案的第一条评论中提出了这一点。
    • 它在 Chrome 和 Firefox 上都可用,并且是在这些浏览器上收集准确时间信息的唯一方法。
    • ANGLE 仅适用于 Windows,在 Chrome 中是可选。 Chrome 还可以直接与 OpenGL GPU 通信,而不是使用 ANGLE 在 D3D 之上分层。这就是我的观点。
    • ANGLE 始终启用,除非使用 chrome.exe --use-gl=desktop 启动 chrome。我还没有验证这一点,但如果 ANGLE_timer_query 在非 Windows 平台上可用,我不会感到惊讶,尽管没有 ANGLE。
    • 我可以为您验证 ANGLE_timer_query 在 Windows 上的最新版本 Internet Explorer 或 OS X 10.9 上的 Chrome/Safari/Firefox 中不可用。不要误解我的意思——你的答案在少数 Windows 浏览器上完全有效——只是定时器查询目前在任何其他 WebGL 平台上都不可用:-\
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多