Hey guys, I have a question about lots of GUIs in TextureMode and best practices on how to keep things optimized
In my multiplayer game, I spawn ~100 text labels in the same areas (using them as nameplates) above the characters, I made a super quick playground to check things out and I don’t see any big performance troubles so far, they do tank my fps but not too much and really 100 text labels in the same area is more of a “worst case” situation than anything else, my best guess is 30-40 at the same time
However, I’m kinda worried it might hit low-end machines (5-6 years old or integrated GPUs) so… Is there anything I should be aware of? Is there any way to improve the performance?
In regular dev, I’d use instancing and draw most if not all in just 1 draw call but I’m pretty new to BabylonJS and WebGL in general so feeling kinda lost, maybe instancing is a viable option too just have to find out how to work with shaders, camera, etc.
Thank you very much!
EDIT looks like adding isHitTestVisible = false helps quite a bit which makes sense!
Hey @nazmouse22 , welcome to the Babylon community We’re super glad to have you here!
I love that you’re accounting for performance on low end machines! I don’t have a ton of advice to give you at the moment since I’m fairly new to the GUI system myself, but you seem like you’re on the right track. I’ll note that there are a couple of different things involved in GUI:
layout calculations, probably not too big of an issue especially since in this PG is just a child of root. also, you’re not moving the controls around or changing the hierarchy of containers.
pointer testing. you’ve already discovered this, but it can be very expensive as every time a pointermove event is triggered, we need to check if the pointer is within the bounds of every single control. there might be some ways to optimize this if you really need a ton of clickable controls - for ex. you could enable and disable this based on what quadrant of the screen the pointer is in
rendering the controls to a 2D canvas - this step shouldn’t theoretically be too bad as the canvas is hardware accelerated, but could become a problem with large number of controls
I’m going to tag @RaananW who is a little more familiar with the GUI system than me, in case he has anything to add.
Looking at your demo, I have a few quick optimizations for the playground that might help a bit with performance.
The first is the usage of setInterval in the playground. It is not an issue with your scene, but with the playground itself. If you run your scene a few times you will notice that it doesn’t run so smoothly after a while. This is because the intervals are not cleared every time you run the demo. The scene is disposed, but the old interval is still running.
The second is reusing the plane’s geometry instead of recreating it every time. You talked about instancing, but instancing won’t work here because you require a dynamic texture. what you can do is use cloning, which will reuse the geometry but will allow you to have a different material per mesh. The amount of draw calls is not reduced in that case, but memory is managed better.
Both of those suggestions were integrated here - Texture mode Babylon.GUI | Babylon.js Playground (babylonjs.com)
I think most of your issues will not be rendering related, but CPU-bound. pointer events are CPU-related, for example. Reducing the amount of listeners for pointermove (as you found out) will increase performance.
Thanks a lot!
Glad to be a part of a community, Babylon is amazing compared to other alternatives and inspector UI turned out to be super helpful too! Feels like I never left Unity/Unreal , also the native BabylonJS idea is amazing, very curious about it, I can definitely see it using myself for some small prototypes, would be interesting to compare it to BabylonCpp implementation
Now speaking of the rest of your points:
- Yeah layout calculations won’t happen, it’s one-time spawn and parent sort of thing
Pointer testing is totally irrelevant right, I turned it off as soon as I found out it’s possible to do that
Rendering the controls to a 2D canvas, I think the only concern here is the memory allocated for each texture? I’ll cull some things out but the worst case leaves me with 100 of 512x512 maps (size can probably be reduced)
Thanks for your answer!
Oh damn! Sneaky setInterval, my bad, I thought every “run” playground cleans everything up but it makes sense now, I’m used to refresh page a lot so it wasn’t a very big deal for me to be honest but definitely something to keep an eye on
Reusing plane’s geometry sounds interesting, wasn’t aware of it until now, can I use geometry cloning for skinned meshes (skeleton with animations), or is it for static only? How does it save the memory tho? Does it prevent creating some dynamic collections as the size and the properties are known or something like that?
Speaking of instancing - you’re absolutely right, when I was typing it, in my mind I was thinking about the world space mesh text with some sprites behind it that all share the same material
Thanks a lot for your answers, very helpful!
I’ll try a few things out and see how it all goes
It does it by reusing the geometry in different meshes. it only creates one vertex buffer object, which prevents holding the vertex data arrays in each mesh.
yes and no. this is a bit more complex as you need to clone those elements as well. If you use the asset container it is being done for you automatically, otherwise you will need to make sure you clone everything
Hello @nazmouse22 just checking in, do you need any further help?
Uh oh I’m sorry I was super busy recently, thanks a lot for checking! No need for help anymore, I got nameplates drawing and everything is working well with a decent fps on older machines