2
\$\begingroup\$

Introduction

The game scene contains a lot of 10x10 pixel elements: for 1980x1200 px. screen the number of elements on the scene can vary from 0 to 23760 (elements cannot intersect).

The live example: http://fintank.ru/game/

The entire game space comprised of tiles: each tile is responsible for some area and keeps elements for that area (similar to google maps).

Each tile exist as 2 entities: S and G. S is a set of elements in the tile, G is rendered image of a tile. When the server put/remove elements to the tile, we change S and then re-render the G.

To draw a frame (at 20 fps) I just put the G of each visible tile using the putImageData(). That is done without WebGL.

The question

What is the most CPU effective approach to implement such many-elements management using WebGL? Should I render each tile to a texture or each element may be implemented as a separate textured rectangle? The main concern is that each tile may be updated 30-60 times a second (elements adding/removal).

How to avoid memory leaks (how to reuse buffers?) if we make many new textures every second?

What is the most effective way to manipulate pixels during rendering a 100x100 tile?

How to avoid re-uploading the unchanged textures to graphic card?

\$\endgroup\$
3
\$\begingroup\$

There are several ways to approach this problem. When it comes to WebGL performance the key is to: (a) reduce number of drawcalls, (b) reduce gpu overdraw (fill-rate) and vertices. (c) reduce buffer data transfers. Because you're filling the screen at most once and scene complexity is low, we can totally ignore (b). (c) is a bit more complex and I'll get back to it later.

So, let's see how we can draw a whole tile of N by N elements in a single drawcall. First, let's make sure that you have all possible element images on a single texture atlas – this will enable us to draw any element without changing textures.

For each tile, create a vertex buffer that will hold N by N quads and initialize their positions so that they make the whole grid. To make an element appear, you just have to set the texture UV coords of the quad to the element image in texture atlas. You never change quad positions.

bufferData = new Float32Array(N*N*6*8);  // 6 vertices == 2 triangles, WebGL doesn't support quads natively
bufferId = gl.createBuffer();

// fill buffer data with coordinates, etc.

// now after each modification call:
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.DYNAMIC_DRAW);  // dynamic will make it optimized for frequent updates

You can reuse the vertex buffer and just update it's content after each change. Now rendering this vertex buffer will draw all the elements in N by N tile onto the framebuffer. There's even no need to cache it in a texture, it is so cheap.

And when it comes to (c): this should not be needed, but let's assume that your elements are mostly static and you would like to only ever draw what's changed. If you render your graphics to a texture, you can later draw to this texture without clearing it, thus updating only the changed parts. However this (not clearing before rendering) could potentially hurt performance on some older mobile platforms. Also, still remember to draw all the updated elements in one single drawcall as shown above.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.