在WebGL中衡量任何时间的唯一方法是计算出在一定的时间内你可以做多少工作。选择一个目标速度,比如30 the,使用requestAnimationFrame,继续增加工作直到你超过目标为止。
var targetSpeed = 1/30;
var amountOfWork = 1;
var then = 0;
function test(time) {
time *= 0.001; // because I like seconds
var deltaTime = time - then;
then = time;
if (deltaTime < targetTime) {
amountOfWork += 1;
}
for (var ii = 0; ii < amountOfWork; ++ii) {
doWork();
}
requestAnimationFrame(test);
}
requestAnimationFrame(test);
这并不是很简单,因为浏览器,至少在我的经验中,似乎没有给出一个真正稳定的帧的时间。
注意事项
- 不要以为requestAnimationFrame会达到60英尺。
有许多设备运行速度更快(VR)或速度较慢(低端hd显示器)。
- 停止之前不要测量开始发出命令的时间
测量自上一次requestAnimationFrame以来的时间。WebGL只是在缓冲区中插入命令。这些命令可能在驱动程序中执行,即使在另一个进程中也是如此
变量start = performance.now;//错误!Gl.someCommand(.);//错误!Gl.flush(.);//错误!var time = performance.now - start;//错误!
- 实际上是在使用资源。
许多资源都是延迟初始化的,所以仅仅上传一个资源而不使用它不会给你一个精确的度量。你需要用你上传的每一个纹理来画一幅画。当然,用一个简单的着色器把它画成一个小的1像素的三角形。着色器必须实际访问资源,否则驱动程序不会执行任何延迟初始化。
- 不要假设不同类型/大小的纹理将有比例变化的速度。
不同事物的司机。例如,一些GPU可能不支持任何东西,但RGBA纹理。如果你上传一个亮度纹理,驱动程序会将其扩展到RGBA。所以,如果你使用RGBA纹理计时,并且假设相同尺寸的亮度纹理会上传4倍,你就错了。
同样,不要假设不同大小的纹理会以与其大小成正比的速度上传。驱动程序的内部缓冲区和其他限制意味着不同的大小可能采用不同的路径。
换句话说,您不能假设1024x1024纹理会像512x512纹理那样缓慢地上传4x。
- 要知道,即使这样也不能保证现实世界的结果
我的意思是,例如,如果您使用的是平铺硬件(例如iPhone),那么GPU的工作方式是收集所有绘图命令,将它们分割成块,裁剪任何不可见的绘图,只绘制剩下的部分,因为大多数桌面GPU都会绘制每个三角形的每个像素。
因为一个平铺的GPU在最后做所有的事情,这意味着如果你继续上传数据到相同的纹理,并在每次上传之间绘制,它将不得不保留所有纹理的副本,直到它绘制。在内部,在再次缓冲之前,它可能会刷新和绘制它所拥有的内容。
甚至桌面驱动程序也希望通过管道上传内容到纹理B,绘制,上传新内容到纹理B,绘制。如果驱动程序正在执行第一次绘图,它不希望等待GPU,这样它就可以替换内容。相反,它只是想将新内容上传到其他没有被使用的地方,然后当它能够将纹理指向新内容时。
在正常的使用中,这不是一个问题,因为几乎没有人上传的纹理一直。他们最多上传1或2个视频帧或1或2个程序生成的纹理。但是当你在做基准测试的时候,你是在给司机施加压力,让他做一些实际上并不正常的事情。在上面的例子中,它可能假设一个纹理不太可能被上传10000次--一个框架--你会遇到一个限制,它必须冻结管道,直到你的一些排队的纹理被绘制出来。这种冻结会使您的结果看起来比在正常用例中实际得到的要慢。
关键是,您可能会基准测试,并被告知,它需要5ms上传一个纹理,但实际上,它只需要3ms,你只是暂停了很多次,在你的基准是不太可能发生的管道。