freeciv / freeciv-web

Freeciv-web is an Open Source strategy game implemented in HTML5 and WebGL, which can be played online against other players, or in single player mode against AI opponents.
Other
2k stars 331 forks source link

Run at 60 fps ! #56

Open lmoureaux opened 7 years ago

lmoureaux commented 7 years ago

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

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.

ghost commented 7 years ago

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 commented 7 years ago

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.

lmoureaux commented 7 years ago

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.

ghost commented 7 years ago

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

lmoureaux commented 7 years ago

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).

ghost commented 7 years ago

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 commented 7 years ago

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.

lmoureaux commented 7 years ago

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" notifications@github.com 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

ghost commented 7 years ago

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

lmoureaux commented 7 years ago

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" notifications@github.com 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

ghost commented 7 years ago

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 commented 7 years ago

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.

lmoureaux commented 7 years ago

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'...

ghost commented 7 years ago

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 commented 7 years ago

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.

lmoureaux commented 7 years ago

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

pailhead commented 7 years ago

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.

lmoureaux commented 7 years ago

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.

ghost commented 7 years ago

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 commented 7 years ago

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?