Skip to main content

Attribute Management

Overview layers' public API were designed to follow a Reactive programming paradigm.

The challenge is that in the "reactive" model, every change to application state causes everything to be re-rendered. This is not an issue for UI rendering for general purpose apps, but for high performance, graphics intensive and sometime 3D contents to be rendered by the GPU, huge memory buffers (so called "vertex attributes", or just "attributes" for short) must be prepared and transferred to the GPUs before any draw calls got executed on the GPUs.

GPU Performance Challenges

Creating and transferring new GPU buffers before every draw call would result in unacceptable performance even for moderately complex models. Just like in React (which "renders" to the browser's slow-updating DOM), the challenge becomes to detect which part of the visualization is changed to limit both attributes recalculation and re-rendering to the minimum.

Since the length of attributes are usually proportional of to the number of data elements being visualized (hundreds of thousands or even multiple millions of elements are not uncommon in big data visualizations), efficient attribute updates is critical. alleviates the burden of layer developers by providing an AttributeManager class to manage the lifecycle of those GPU attributes. Note that it is completely possible for a layer to use custom code to manage attribute updates, however most layers rely on the AttributeManager class to handle GPU buffer management for them.

Automatic Attribute Generation

Automated attribute generation and management is suitable when a set of vertex shader attributes are generated by iteration over a data array, and updates to these attributes are needed either when the data itself changes, or when other data relevant to the calculations change.

  • First the application registers descriptions of its dynamic vertex attributes using AttributeManager.add().
  • Then, when any change that affects attributes is detected by the application, the app will call AttributeManager.invalidate().
  • Finally before it renders, it calls AttributeManager.update() to ensure that attributes are automatically rebuilt if anything has been invalidated.

The application provided update functions describe how attributes should be updated from a data array and are expected to traverse that data array (or iterable) and fill in the attribute's typed array.

Note that the attribute manager intentionally does not do advanced change detection, but instead makes it easy to build such detection by offering the ability to "invalidate" each attribute separately.

Accessors, Shallow Comparisons and updateTriggers

The layer will expect each object to provide a number of "attributes" that it can use to set the GL buffers. By default, the layer will look for these attributes to be available as fields directly on the objects during iteration over the supplied data set. To gain more control of attribute access and/or to do on-the-fly calculation of attributes.

Note: A layer only renders when a property change is detected. For performance reasons, property change detection uses shallow compare, which means that mutating an element inside a buffer or a mutable data array does not register as a property change, and thus does not trigger a re-render.

To force trigger a render after mutating buffers, simply increment the renderCount property. To force trigger a buffer update after mutating data, increment the updateCount property.

Advanced Topics

Manual Buffer Management

While most apps rely on their layers to automatically generate appropriate GPU buffers from their props, it is possible for applications to take control of buffer generation and supply the buffers as properties.

While this allows for ultimate performance and control of updates, as well as potential sharing of buffers between layers, the application will need to generate attributes in exactly the format that the layer shaders expect, creating a strong coupling between the application and the layer.

Note: The application can provide some buffers and let others be managed by the layer. As an example management of the instancePickingColors buffer is normally left to the layer.

More information

Introduction to Vertex Attributes layers use WebGL2/WebGPU to render visualization elements. In this programming model, all geometry elements are made of a set of vertices, and each vertex has multiple attributes to determine how it will be rendered to the screen. These attributes are called vertex attributes and are provided by users using JavaScript typed arrays (e.g. Float32Array or Uint8Array).

During rendering, these vertex attributes will become available in vertex shaders executing on the GPU.

Part of designing a new layer is creating an elegant mapping from a set of data properties in JavaScript to a set of GPU vertex attributes, and then implementing the code that generates the typed arrays that represent the geometry so that GPU calls can be performed efficiently later when drawing the layer.

Introduction to Instanced Vertex Attributes

Even for GPU-accelerated rendering, setting up draw calls and dispatching them to the GPU can quickly become a performance bottleneck for visualizing big data. Therefore, each layer aspires to use as few GPU draw calls as possible to draw everything contained in the data.

Here the Instanced Rendering comes into play. While some vertex attributes will still describe the geometry of each object (or instance), other vertex attributes will describe what is different between each object or instance. The latter kind of attributes are called instanced attributes.

Learning More

While you can certainly start consulting detailed WebGL2/WebGPU resources to learn more about vertex attributes, be aware that many available resources can get quite technical, involving more concepts than you may need at this point.

If you are new to these concepts, we have found that a great way to learn more is simply to copy an existing layer and start extending/modifying its functionality.