Performance¶
Minimizing 2D Work¶
The key to performance is, of course, avoiding unnecessary work. The Roblox UI engine knows this, but we have to play nice with it and help it figure out what work can be avoided.
The Roblox UI engine caches the 2D renders- that is, it renders once and will only re-render if the cache is invalidated. Source
A ScreenGui/SurfaceGui/BillboardGui’s appearance is cached until one of the following events occurs:
- A descendant is added to the Gui.
- A descendant is removed from the Gui.
- A property of a descendant of the Gui changes.
- A property of the Gui changes.
If any of these events occur, the Gui’s appearance will be recomputed the next frame it gets rendered. Shoutout to the Roblox engineers who added this optimization.
We can help this along even further by splitting our app up into many ScreenGuis, so that changing one does not invalidate the others! The sidebar, the popup menus, the lessons, the tutorials, etc. all get their own ScreenGui so that we avoid invalidating the cache of Guis that did not actually change. This means our engine only computes the appearance of Guis when absolutely necessary, just the way we like it.
Removing 3D and Physics Work¶
This project is UI only. There is no physics, no 3D world, no characters to run around. That means it should run fast as hell. However, the Roblox engine has no concept of UI only games- so we have to do our best to minimize the impact of the pointless 3D work that is done in the background.
-
Hello darkness my old friend: We set our lighting to the simplest possible- voxel tech, no shadows, all black color values, and brightness 0.
-
Floating Point: We set gravity to 0, making it so that there are absolutely no forces in play for the physics engine.
-
Empty inside: We do not spawn in characters for the players, and the world has no geometry. There is nothing but sky.
-
Narrow minded: We set our 3D camera FoV to 1, the lowest it supports. This way, we'd render only a tiny sliver of the aforementioned sky.
-
Blindfolded: We set our camera CFrame matrix to all zeros, causing the camera to render pure black as it is distorted down to nothingness.
-
No thank you: Roblox injects default scripts, such as camera controls, character controls, chat, and more. We override these by replacing them with empty directories or (in cases where we can't override) by destroying them on startup.
Benchmarking¶
How much of an improvement do we see from these 3D/Physics changes? Let's find out.
*Note: These are being applied cumulatively, so each one down the table has the previous ones applied already.
Running with Ryzen 9 3900X, RTX 2070 Super, 32GB 3600MHz RAM on the lowest Roblox graphics setting
Project | ~FPS | Frame Time | % Gain | Relative % Gain |
---|---|---|---|---|
Default Baseplate | 1100 | 909µs | 0.00% | 0.00% |
Hello Darkness | 1250 | 800µs | 11.99% | 11.99% |
Floating Point | 1275 | 784µs | 13.75% | 2.00% |
Empty Inside | 1700 | 588µs | 35.31% | 25.00% |
Narrow Minded | 1800 | 555µs | 38.94% | 5.61% |
Blindfolded | 2000 | 500µs | 44.99% | 9.91% |
No Thank You | 2400 | 416µs | 54.24% | 16.8% |
These are rather predictable results. Although some are insignificant, I have done them anyway since they are basically no additional effort on my part.
You may try the benchmark place yourself, as I've turned it into a friendly competition:
https://www.roblox.com/games/9225725156/Flex-Your-FPS
Result¶
Well, how does this all run in an actual game? Like a goddamn champ, that's how!
1200+ FPS even while UI is being animated, 1800 FPS peak at idle.