Spotting Verge3D Performance Bottlenecks
- Intro
- Check your FPS
- Performance Profile
- Common Rendering Performance Bottlenecks
- Scene Loading Time
- GPU Memory Usage
- See Also
Intro
Since a broad internet audience can use your applications, your Verge3D-based apps must show good performance. For example, some classes of WebGL-capable devices may work much worse than you expect:
- Typical low-end or middle-end laptops with integrated GPUs (Intel, AMD APU graphics).
- Old smartphones or tablet computers.
- Mobile VR devices, such as Oculus Go or Oculus Quest. To prevent motion sickness symptoms, it's mandatory that these devices render with appropriate frame rate (60, 72, or even 90 FPS).
- Devices with battery. Enormous GPU utilization quickly drains out the battery, powering mobile devices.
To optimize efficiently, e.g., without degrading your apps' quality, you should understand basic asset optimization techniques and be able to spot places in your app that cause slow loading and rendering. These are called performance bottlenecks.
Check your FPS
FPS (frames per second, or frame rate) is a crucial quantity representing the rendering time and user experience of your app:
- < 20 — your app is really slow, almost impossible to use.
- 20-40 — slow, but acceptable.
- 40-60 — good enough.
- 60 — ideal frame rate on desktop and mobile browsers.
- > 60 — frame rate you should target on VR headsets.
Verge3D comes with the built-in FPS counter. You can activate it by:
- Quickly pressing *~*, *~*, *~* (triple tilda key), then F from the Service Tools menu.
- Using JavaScript's showFPS() API method.
Once activated, you can see the current FPS value (along with its range and history plot) on the small panel in the left top corner of your app's canvas:
To get legitimate values, you should measure FPS on the slowest / oldest devices you have. Also, most browsers do not allow frame rate be greater than 60 Hz (this improves frame syncing, saves your battery life, and reduces fan noise).
Performance Profile
Verge3D comes with a feature to generate performance profiles in real time. You can get a quick insight into what's happening with performance in your application and what you should do to increase it. To produce such profiles, you may use one of the following methods:
- Quickly press *~*, *~*, *~* (triple tilda key), then P from the Service Tools menu (Verge3D 4.7+).
- Use the print performance info puzzle.
- Use JavaScript's printPerformanceInfo() API method.
Wait at least 1 second while Verge3D collects profile data, then open the browser console. Check out the string in the opened console window:
--- Verge3D Performance Profile (1s) ---
Below it, the lines with collected performance information:
Here is what you can inquire from the generated performance profile:
- Scene Loading Time
- Time in seconds it took to load the application's main scene. See below on how to reduce it.
- Asset Compression
- Shows whether asset compression is enabled or not.
- FPS
- Rendering frame rate. This is the same value reported by the FPS counter.
- Render Calls
- Amount of render calls per frame. An important value that shows how many draw operations are performed during one frame. This value represents the number of different materials on your scene + various supplementary render calls such as shadows, post-processing, etc.
- Triangles Rendered
- How many triangles were rendered per frame. See more info about this value below.
- Geometry Buffers
- How many geometry buffers your app uses.
- HDR Rendering
- Whether HDR rendering switched on or off.
- Viewport Resolution
- Rendering viewport resolution.
- Pixel Ratio
- Current (used by the renderer) and device (native) pixel ratios.
- Image-Based Lighting
- Type of IBL lighting and IBL cube map tile size.
- Lights
- The number of lights in your scene.
- Reflection Probes
- Amount of cubemap and plane reflection probes in your scene.
- Post-Processing
- Scene post-processing passes (in rendering order).
- Shadow Map
- Shadow map parameters: filtering type; the number of rendered shadow maps; shadow map resolution.
- Materials and Shaders
- The list of rendered materials and shaders. Each list item contains the following info: material name (or names if they share the same shader), shader name, and rendering time per frame (measured in milliseconds).
- Total Render Time
- The total amount of time (in milliseconds) it took to render your scene.
- Textures & Render Buffers
- Textures and render buffers stats (resolutions). Textures are printed on the top of the list (prefixed with Texture), while render buffers printed at the bottom of the list (prefixed with RenderTarget). For textures, there is format info: RGBA for uncompressed textures, RGBA_[METHOD] for compressed textures.
To help you spot performance issues, lines which you should pay attention to first, are marked in red. For example, on the screenshot below, the shadow map appears to be too big (4K) which may result in slower rendering.
Common Rendering Performance Bottlenecks
Here are the most frequent bottlenecks limiting your frame rate.
Complex Materials
Graphics hardware can render a limited amount of pixels per second. In most scenarios, you can't render more because your materials use lengthy and sophisticated pixel (aka fragment) shaders, which in turn require too much processing power from the GPU.
You can easily detect the situation with limited pixel performance by reducing or increasing the browser window size. If your frame rate (FPS) grows substantially when you reduce the size, you have such a situation.
The most straightforward way to increase performance is to simplify your materials. Pay attention at the top of the Materials and Shaders list in the performance profile.
In many cases, the issues with material performance happen due to slow shading nodes. See more in the Blender, 3ds Max or Maya material performance subsections.
You can also get rid of the shader-limited performance problem, by setting the pixel ratio < 1 with the set screen scale puzzle. This can severely impact the quality of your app, so consider it a last-hope solution.
Post-processing
Several post-processing effects can substantially reduce the performance of your apps:
- Screen-space refraction and reflection (see puzzle).
- Ambient occlusion (see Blender / Max / Maya manual pages).
- Depth-of-field (see puzzle).
Shadows
Complex shadow algorithms with hight shadow map sizes can also negatively affect your rendering speed.
- If you have multiple lights on your scene, disable shadows for the weakest (with low intensity) ones.
- In most cases, point light shadows require more calculations than spot or sun/directional shadows.
- Reduce shadow map size (see Blender, 3ds Max, Maya).
- Switch to less performance-hungry shadow filtering algorithm (see Blender, 3ds Max, Maya).
Too Much Geometry
Too complex geometry can also affect your rendering performance. We recommend using no more than 100K triangles per model or 1M triangles per entire scene.
Slow Image-Based Lighting
In Verge3D you can do several performance tweaks for your environment lighting:
- Reduce environment cube map tile size (see Blender, 3ds Max, Maya). We recommend using 256 pixel cube maps for most cases.
- Switch to more performance-efficient environment mode (see Blender, 3ds Max, Maya).
Too Many Lights
Excessive light calculations increase the complexity of your shaders. Try to reduce their amount or switch to IBL-only lighting.
Too Many Render Calls
Hundreds of render calls significantly load your CPU. Hence you should keep that value as low as possible. In most situations, we recommend you to keep the amount of render calls below 100.
If you render many static objects with the same material, you can significantly increase peformance by using the batch geometry puzzle.
Scene Loading Time
Another thing that you need to pay attention to is the scene loading time. Users really don't like to wait too much for your application to load.
Here are the most frequently occurring bottlenecks that slow down your app loading.
Large Binary Data or Textures
Since your app assets are transferred over the network, you should minimize their size. To help you with asset optimization, there is a network inspecting tool accessible from the browser console. Simply click on the Network tab and reload your page.
Check the browser documentation on how to use this feature: Chrome, Firefox, Safari.
Sort the resources by size to get the ones affecting your loading time the most. In most cases, these assets can be divided into two categories:
- Verge3D Binary (*.bin or *.bin.xz extensions) — contains geometry, morphing, and animation of your scene. Asset compression can significantly reduce the size of this file, always enable it for your apps.
- Textures (*.jpg, *.png, or *.hdr extensions) — see here for more info on how to optimize them.
Also, there are two dirty but highly effective methods to optimize your textures:
- Downscale your textures by reducing their dimensions by factor 2 (e.g 2048x2048 to 1024x1024 or even 512x512).
- Convert all PNG textures to JPEG (even normal maps).
Too Many or Too Complex Materials
With some exceptions, each material on your scene requires compiling a pair of special programs called shaders: vertex shader and fragment (aka pixel) shader. A shader is a special program executed on your GPU to calculate various rendering entities like vertex positions, textures, lighting, reflections, refractions, etc.
Shader compilation is a computation-intensitive task that is performed each time your app is being loaded. If you have a lot of different materials on your scene, shader compilation can significantly increase total loading time.
So the less shaders your application uses, the faster is loading. Also, shaders are cached by WebGL, so if your shader is used by multiple objects it is compiled only once.
Therefore for fast loading, it is important to use as few unique shaders as possible. One of possible way to achieve this is to enable the glTF compatibility flag, which a) simplifies the shaders a lot so that they are compiled and work faster and b) standardizes them to enable for more effective caching.
The obvious drawback of using glTF-compatible shaders is that they may be too simplistic for implementing desired graphics effects. So as it is always in real-time graphics, you need to balance between quality and performance.
GPU Memory Usage
Excessive GPU memory usage can considerably reduce performance or even crash your app. The following things consume a lot of memory.
Large Textures
For example, a texture of size 4096x4096 alone occupies almost 90 Mb of video memory. To get all textures in your app, check out the Textures & Render Buffers section in the performance profile.
See here for more info on how to calculate texture memory usage.
See texture compression section to find out how to reduce texture memory consumption.
Too Many Post-Processing Passes
Most post-processing effects require additional rendering buffers which consume GPU memory. Check out the bottom part of the Textures & Render Buffers list in the performance profile to see all render buffers.
Large Geometry Buffers
Meshes with many triangles require large geometry buffers and hence consume a lot of video memory to store them.
See Also
Check out the Optimization Techniques section for more asset optimization methods.
Got Questions?
Feel free to ask on the forums!