Reputation: 3295
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?
Upvotes: 1
Views: 4210
Reputation: 8123
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:
Upvotes: 5
Reputation: 493
The problem is probably this line in render: glixl.context.uniformMatrix3fv(glixl.matrix, false, this.matrix);
.
In my experience, passing uniforms for each model is very slow in webGL, and I was unable to maintain 60FPS after ~1,000 unique models. Unfortunately there is no uniform buffers in webgl to alleviate this problem.
I solved my problem by just calculating all the vertex positions on the CPU and draw them all using one drawArray
call. This should work if the vertex count isnt overwhelming. I can draw 2k moving + rotating cubes at 60 FPS. I dont recall exactly how many cubes you can draw at 60 FPS but it is quite a bit higher than 2k. If that isnt fast enough then you have to look into drawArrayInstanced
. Basically, store all the matrices on an arraybuffer and draw all your models using one drawArrayInstanced
call with correct offset and such.
EDIT: also to the OP, if you want to see how PIXI does the vertex update rendering (NOT uniform instancing), see https://github.com/GoodBoyDigital/pixi.js/blob/master/src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js.
Upvotes: 1