WebGPU Internals - Bind Group Cache

This class implements a cache of GPU bind groups to avoid recreating them each frame.

Cache implementation

The cache is a node tree:

class WebGPUBindGroupCacheNode {
public values: { [id: number]: WebGPUBindGroupCacheNode };
public bindGroups: GPUBindGroup[];
constructor() {
this.values = {};
}
}
export class WebGPUCacheBindGroups {
private static _Cache: WebGPUBindGroupCacheNode = new WebGPUBindGroupCacheNode();
...
}

The id key in the values object is the id of a bind group resource: a uniform/storage buffer, a sampler or a texture. The id value for the uniform/storage buffer and texture is simply the uniqueId property of the corresponding class (DataBuffer.uniqueId and InternalTexture.uniqueId / ExternalTexture.uniqueId respectively). For the sampler, it is the sampler hash code (computed by WebGPUCacheSampler.GetSamplerHashCode()). The cache is traversed/built by looping over all the buffers/samplers/textures used by a shader (which is encapsulated in a WebGPUPipelineContext), in this order.

Limits of the implementation

The location of a resource (group and binding indices in the [[group(G), binding(B)]] syntax) is not factored in the id and the ids are not globally unique (because they are not from the same pool: there's a separate pool for the buffer and texture uniqueId property), so theoritically some collisions could occur where two sets of resources point to the same cache entry. In practice it will likely never occur. Making the cache foolproof would mean making it even slower and the WebGPU implementation already suffers a lot from having to handle a cache for some objects (bind groups and render pipelines mainly)...

Note also that all uniform buffers have an offset of 0 in Babylon and we don't have a use case where we would have the same buffer used with different capacity values: that means we don't need to take into account the offset/size of the buffer in the cache, only the id.

Optimization

There is an optimization of the cache where we simply return the existing bind groups if the draw and material contexts did not change since the last cache query. Indeed, the draw context holds the list of the uniform/storage buffers and the material context the list of the textures and samplers used by the shader: if those lists did not change the previously created bind groups are still valid.

Monitoring the performances

Performance of the cache can be assessed by looking at these properties (the property should be prefixed by BABYLON.WebGPUCacheBindGroups.):

propertydescription
property
NumBindGroupsCreatedTotal
description
Total of bind groups created since the start of the program
property
NumBindGroupsCreatedLastFrame
description
Number of bind groups created during the last frame - for best cache usage this value should be 0 on average
property
NumBindGroupsLookupLastFrame
description
Number of bind groups retrieved by traversing the cache
property
NumBindGroupsNoLookupLastFrame
description
Number of bind groups retrieved without traversing the cache because no changes in buffers/textures/samplers occurred since the last cache query for this shader