freeciv-web icon indicating copy to clipboard operation
freeciv-web copied to clipboard

Run at 60 fps !

Open lmoureaux opened this issue 8 years ago • 20 comments

The WebGL renderer is slow. This ticket is about improving its performance. I will try to keep this post updated myself, but feel free to modify it if needed.

Findings

  • The renderer is CPU-bound, even on old IGPs (Intel HD 4000)
  • Profiling showed that ~40% of time is spent in pure WebGL calls
  • The scene contains a large number of small objects (I got up to 5,000), mostly units, cities and labels
  • Many of these objects have several children (at least units, don't know for cities, labels are clean)
  • With 50,000 objects in the scene, we have less than 1 µs/object on the average to render at 60 fps
  • Three.js will sort objects to limit state changes (source)
  • Three.js won't batch geometries
  • maprenderer.info has some useful info

Discussion

Since Three.js doesn't do any batching, it issues a draw call for every visible object. It will also bind different uniforms values for every object (model and view matrices). Issuing many draw calls is bad for performance, hence the low framerate.

Solution

TL;DR : batch-render everything.

  • Batch units. This is difficult because each unit has several meshes
    • Reduce the complexity of unit models (#49) : merge groups, but geometries are also unnecessarily complex (load time)
  • Batch flags and unit labels (#47)
  • Batch cities
  • Batch specials
  • Batch roads, rivers, ...

lmoureaux avatar Jan 22 '17 00:01 lmoureaux

Using BufferGeometry instead of Geometry will also improve performance. Geometry is currently used on the terrain, fog-of-war/darkness and imported collada models (units, cities). https://threejs.org/docs/api/core/BufferGeometry.html

ghost avatar Jan 22 '17 07:01 ghost

I have added a benchmark button, which can be found in the pregame webgl configuration dialog. This will measure the frames per second while playing 30 turns

I get 24 fps at 3840x2160 on my desktop computer, on the high quality setting. I get 17 fps on my Android mobile phone, on the low quality setting.

ghost avatar Jan 22 '17 13:01 ghost

Intel HD 4000, i7-3770S, 1280x1024:

             HQ  MQ  LQ
Firefox 50    7  10  16
Chromium 55  19  25  39

Antialiasing doesn't make a difference. The server is running locally ('cause network latency affects the results dramatically)

I ran the benchmark with intel-gpu-overlay on, and I can see the GPU load decrease as time goes on and more Warriors come to life: spectacle z32730

Here's my "benchmark": set

/set revealmap start
/set fogofwar disabled
/set dispersion 10
/set startunits dddddddddddddddddddd

You get a scene with N warriors * P players. Wait for the framerate to stabilize, write down the result. Here's the data (on Chromium, same computer): spectacle hz8452

The tendency is clear: the framerate goes down as the number of units increases.

lmoureaux avatar Jan 22 '17 20:01 lmoureaux

I have just fixed a bug where special tile resources would be added multiple times to the scene. This change has improved the framerate of the benchmark to 38fps, from 24fps previously, on my desktop. So this was a significant improvement in performance!

https://github.com/freeciv/freeciv-web/commit/2b86741d33ca4b802c68b14679a41498704ec1ae

ghost avatar Jan 23 '17 19:01 ghost

Your fix works great on Chromium, but Firefox is still very slow.

I found you can get some debug info from Three.js:

maprenderer.info

The number of draw calls is available there (draw.calls IIRC). The frame time is proportional to it, even if the scene doesn't change (moving around to see more or less units).

lmoureaux avatar Jan 26 '17 19:01 lmoureaux

Here are some more ideas and feedback we could implement in Freeciv WebGL: https://news.ycombinator.com/item?id=13489881

For example, glTF, STandard material model (GGX + Metallic + Roughness) in Three.JS, and more.

ghost avatar Jan 27 '17 09:01 ghost

I have switched from using 3d models in Collada format to the Three.js binary format. I think framerate, memory usage and download speed should be improved now.

ghost avatar Feb 01 '17 18:02 ghost

I've been playing with hardware instancing, but I'm not sure my code actually draws anything...

Le 1 février 2017 19:49:52 GMT+01:00, "Andreas Røsdal" [email protected] a écrit :

I have switched from using 3d models in Collada format to the Three.js binary format. I think framerate, memory usage and download speed should be improved now.

-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/freeciv/freeciv-web/issues/56#issuecomment-276744982

lmoureaux avatar Feb 02 '17 07:02 lmoureaux

I've been playing with hardware instancing, but I'm not sure my code actually draws anything...

WebGL 2 supports instancing: https://www.saschawillems.de/?p=1852

ghost avatar Feb 02 '17 07:02 ghost

95% of all WebGL 1 devices have support for it through an extension, and Three.js has some support too. WebGL 2 isn't widely supported yet (just landed in mainline Firefox).

Le 2 février 2017 08:30:24 GMT+01:00, "Andreas Røsdal" [email protected] a écrit :

I've been playing with hardware instancing, but I'm not sure my code actually draws anything...

WebGL 2 supports instancing: https://www.saschawillems.de/?p=1852

-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/freeciv/freeciv-web/issues/56#issuecomment-276887086

lmoureaux avatar Feb 02 '17 19:02 lmoureaux

95% of all WebGL 1 devices have support for it through an extension, and Three.js has some support too. WebGL 2 isn't widely supported yet (just landed in mainline Firefox).

Cool, this sounds very promising!

ghost avatar Feb 02 '17 19:02 ghost

It would be interesting to know that kind of performance you get in Freeciv WebGL now. I have made several changes which should have improved performance. I haven't managed to get hardware instancing working yet though.

ghost avatar May 01 '17 20:05 ghost

It would be interesting to know that king of performance you get in Freeciv WebGL now.

Give me two weeks to finish my master thesis. I'll have more time once it's done.

I haven't managed to get hardware instancing working yet though.

Working at all or in Freeciv-web ? I had some code that ran without throwing, but I never saw anything drawn on screen. Since it was quite generic, I planned to test it in a well-controlled environment, then integrate it into Freeciv progressively. But my studies got in the way.

Don't you think it would be useful to have an easier-to-work-with testing environment? Minifying the source makes debugging impossible, and I had to reconnect after every change. I remember missing the simplicity of just typing 'make'...

lmoureaux avatar May 01 '17 20:05 lmoureaux

Don't you think it would be useful to have an easier-to-work-with testing environment? Minifying the source makes debugging impossible, and I had to reconnect after every change. I remember missing the simplicity of just typing 'make'...

I agree, and I am very interested in improving this. Based on your comment above, I have changed so that the not the minified JavaScript files are loaded when running Freeciv-web locally. Source maps are generated when the Javascript is minified, so it should be possible to use that also. Perhaps there are other things to improve there also.

Working at all or in Freeciv-web ?

I haven't gotten it working at all. I think perhaps one way is to work with the Three.js developers in implementing proper support for instancing in Three.js. Here are two relevant links: https://github.com/mrdoob/three.js/pull/10093 https://github.com/Benjamin-Dobell/three.js/tree/instancing-builds

ghost avatar May 02 '17 17:05 ghost

I currently get 48 fps in the benchmark, compared to 38fps previously and 24fps when we first started benchmarking. I hope we can find more ways to improve the framerate.

ghost avatar May 21 '17 18:05 ghost

Performance I got 11 fps on Firefox and another system (still Intel IGP and CPU-bound, default settings). The average in-game frame rate is about 15 fps. Both the benchmark and the game run at 24 fps in Chromium. I don't know that the bottleneck on FF is, but I suspect it is still related to the high object count.

Instancing Instancing a bunch of triangles and managing each of them individually isn't difficult. I built an API close to https://github.com/mrdoob/three.js/pull/10750 (except that my code supports a variable instance count) and got good results. I can post if if you're interested. The worst problem is materials, and that's where Three.js built-in code would help. I don't think I know GL well enough to contribute to Three.js, but I'm open to some kind of collaboration.

Coding environment

  • Updating the code to the latest version can be hard (running the right scripts etc). I had to give up on my Arch setup because I couldn't find where things broke. Now using Vagrant on Ubuntu, no problem so far.
  • I'd like to have a "testing game" with many cities, units and extras. Today I spent 2 hours playing just to get a testing savegame. A scenario with a few nations at the beginning of the Age of Steam would be interesting for players too!
  • I built a simple HTML page using the Freeciv-web JS, filling data structures from JSON dumps. I got to a point where everything loads without throwing, but nothing gets rendered (problems loading assets). Once working, such a page could make graphics hacking much easier.

lmoureaux avatar May 22 '17 02:05 lmoureaux

If variable instance count means that you are resizing the buffers dynamically that should not be very performant. I think it might be better to pre allocate a fixed size, and then use some logic to clip a number of them.

Huge civ fan, i'd like to get involved with this. Stuck at BTS though, briefly played 5, never 6.

pailhead avatar Jun 01 '17 01:06 pailhead

If variable instance count means that you are resizing the buffers dynamically that should not be very performant.

This can be mitigated by resizing the buffers only when needed, using a logic similar to what std::vector does (e.g. grow by 10 when needed, shrink by 10 when there are 12 free slots). The count parameter of glDrawElementsInstanced can be used to limit rendering to the required number of elements.

There is no need to change count at 60 fps, the frequency is more likely to be under 1/s. Does reallocating the buffer every ~10s look feasible ?

Huge civ fan, i'd like to get involved with this. Stuck at BTS though, briefly played 5, never 6.

AFAIK, the Freeciv engine can completely emulate the Civ 3 rules. Nobody cared of checking Civ 4 yet, but the engine becomes more and more flexible over years.

lmoureaux avatar Jun 01 '17 10:06 lmoureaux

Thanks for the ideas @pailhead. If you have more details about how to improve Freeciv WebGL, that would be very welcome. Perhaps you would like to submit some code improvements also. Please let me know if you need any help getting Freeciv-web running locally on your development computer. Thanks!

ghost avatar Jun 01 '17 19:06 ghost

In the benchmark in the most recent version of Freeciv WebGL, I now get 51 fps in Chrome, 50 fps in Firefox, and 34 fps in Microsoft Edge. Memory usage is also much better now. So the performance is gradually improving! Do any of you have any suggestions about how to improve the performance further now?

ghost avatar Aug 31 '17 19:08 ghost