Spotting Verge3D Performance Bottlenecks

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:

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:

Verge3D comes with the built-in FPS counter. You can activate it by:

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:

Verge3D FPS counter window

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:

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:

Verge3D performance profile

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.

Verge3D performance issues

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. Profiling materials and shaders

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:

Shadows

Complex shadow algorithms with hight shadow map sizes can also negatively affect your rendering speed.

  1. If you have multiple lights on your scene, disable shadows for the weakest (with low intensity) ones.
  2. In most cases, point light shadows require more calculations than spot or sun/directional shadows.
  3. Reduce shadow map size (see Blender, 3ds Max, Maya).
  4. 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:

  1. Reduce environment cube map tile size (see Blender, 3ds Max, Maya). We recommend using 256 pixel cube maps for most cases.
  2. 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.

Profiling network activity in Verge3D

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:

  1. 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.
  2. 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:

  1. Downscale your textures by reducing their dimensions by factor 2 (e.g 2048x2048 to 1024x1024 or even 512x512).
  2. 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.

Profiling textures in Verge3D

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.

Profiling render buffers in Verge3D

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!