为什么我的简单 webgl 演示这么慢 [英] Why is my simple webgl demo so slow

查看:67
本文介绍了为什么我的简单 webgl 演示这么慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试使用这些很棒的教程来学习 Web GL.我的目标是制作一个非常简单的 2D 游戏框架来替代基于画布的jawsJS.

我基本上只是希望能够创建一堆精灵并四处移动它们,然后可能再创建一些图块.

我整理了一个基本演示来执行此操作,但遇到了无法追踪的性能问题.一旦我在屏幕上看到大约 2000 个左右的精灵,帧率就会下降,我不知道为什么.与这个 pixi.js webgl 框架的演示相比,它在大约 ~30000个兔子左右(在我的机器上),我有点失望.

我的演示 (框架源)有5002个精灵,其中两个在移动,帧率在马桶里.>

我已经尝试通过 pixi.js 框架来尝试找出它们的不同之处,但它是 500kloc,而且比我的要多得多,我无法解决.

我发现这个答案基本上证实了我所做的大致是对 - 我的算法与答案中的算法几乎相同,但肯定还有更多.

到目前为止,我已经尝试了一些方法 - 只使用一个定义了单个形状的帧缓冲区",然后为每个精灵翻译 5000 次.这确实对帧速率有所帮助,但没有什么能关闭 pixi 演示(这意味着所有精灵必须具有相同的形状!).我删除了所有不动的矩阵数学,所以也不是那样.这一切似乎都归结为 drawArrays() 函数 - 它对我来说真的很慢,但仅限于我的演示!

我还尝试删除所有基于纹理的东西,用简单的块颜色替换片段着色器.它几乎没有任何区别,所以我消除了作为罪魁祸首的狡猾纹理处理.

如果能帮助我追查我做过的极其愚蠢的事情,我真的很感激!

我绝对误解了这里的关键.我将整个事情剥离回到基础,将顶点和片段着色器更改为超级简单:

属性 vec2 a_position;无效主(){gl_Position = vec4(a_position, 0, 1);}

和:

void main() {gl_FragColor = vec4(0,1,0,1);//绿色}

然后将精灵设置为绘制到 (0,0), (1,1).

如果有 5000 个精灵,绘制一帧大约需要 5 秒.这是怎么回事?

解决方案

使用 WebGLInspector 或 Chrome 中的实验性 canvas 检查器完全未优化的渲染循环.

你可以而且应该使用同一个顶点缓冲区来渲染你的所有几何体,通过这种方式,您可以保存 bindBuffer 以及 vertexAttribPointer 调用.您还可以节省 99% 的纹理绑定,因为您可以反复重新绑定一个相同的纹理.只要您不将其他内容绑定到同一纹理单元,纹理就会保持绑定状态.

拥有状态缓存有助于避免绑定已经绑定的数据.

看看我在这里的回答gpu 作为状态机.

优化渲染循环后,您可以继续考虑以下事项:

  • 使用 ANGLE_instanced_arrays 扩展
  • 避免在渲染循环中构建数据.
  • 使用隔行顶点缓冲区.
  • 在某些情况下使用索引缓冲区也会增加性能.
  • 检查是否可以在着色器中减少几个 GPU 周期
  • 将您的对象分成多个块,并在 CPU 端进行视锥体剔除.

I've been trying to learn Web GL using these awesome tutorials. My goal is to make a very simple 2D game framework to replace the canvas-based jawsJS.

I basically just want to be able to create a bunch of sprites and move them around, and then maybe some tiles later.

I put together a basic demo that does this, but I hit a performance problem that I can't track down. once I get to ~2000 or so sprites on screen, the frame rate tanks and I can't work out why. Compared to this demo of the pixi.js webgl framework, which starts losing frames at about ~30000 bunnies or so (on my machine), I'm a bit disappointed.

My demo (framework source) has 5002 sprites, two of which are moving, and the frame rate is in the toilet.

I've tried working through the pixi.js framework to try to work out what they do differently, but it's 500kloc and does so much more than mine that I can't work it out.

I found this answer that basically confirmed that what I'm doing is roughly right - my algorithm is pretty much the same as the one in the answer, but there must be more to it.

I have so far tried a few things - using just a single 'frame buffer' with a single shape defined which then gets translated 5000 times for each sprite. This did help the frame rate a little bit, but nothing close the the pixi demo (it then meant that all sprites had to be the same shape!). I cut out all of the matrix maths for anything that doesn't move, so it's not that either. It all seems to come down to the drawArrays() function - it's just going really slow for me, but only for my demo!

I've also tried removing all of the texture based stuff, replacing the fragment shader with a simple block colour for everything instead. It made virtually no difference so I eliminated dodgy texture handling as a culprit.

I'd really appreciate some help in tracking down what incredibly stupid thing I've done!

Edit: I'm definitely misunderstanding something key here. I stripped the whole thing right back to basics, changing the vertex and fragment shaders to super simple:

attribute vec2 a_position;

void main() {
    gl_Position = vec4(a_position, 0, 1);
}

and:

void main() {
    gl_FragColor = vec4(0,1,0,1);  // green
}

then set the sprites up to draw to (0,0), (1,1).

With 5000 sprites, it takes about 5 seconds to draw a single frame. What is going on here?

解决方案

A look at a the frame calls using WebGLInspector or the experimental canvas inspector in chrome reveals a totally not optimized rendering loop.

You can and should use one and the same vertexbuffer to render all your geometry, this way you can save the bindBuffer aswell as the vertexAttribPointer calls. You can also save 99% of your texture binds as you're repetively rebinding one and the same texture. A texture remains bound as long as you do not bind something else to the same texture unit.

Having a state cache is helpful to avoid binding data that is already bound.

Take a look at my answer here about the gpu as a statemachine.

Once your rendering loop is optimized you can go ahead and consider the following things:

  • Use ANGLE_instanced_arrays extension
  • Avoid constructing data in your render loop.
  • Use an interlaced vertexbuffer.
  • In some cases not using an indexbuffer also increases performance.
  • Check if you can shave off a few GPU cycles in your shaders
  • Break up your objects into chunks and do view frustum culling on the CPU side.

这篇关于为什么我的简单 webgl 演示这么慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆