Static data
The Rerun SDK allows you to store data as static. Static data belongs to all timelines (existing ones, and ones not yet created) and shadows any temporal data of the same type on the same entity.
That is, any time you log static data to an entity path, all past, present and future temporal data on that same entity path and component is semantically discarded in favor of the static one (which doesn't necessarily mean that it is physically discarded, more on that below).
How to store static data? how-to-store-static-data
Internally, all data in Rerun is stored as chunks of columns. Specifically, each chunk holds zero or more time columns (the indices), and zero or more component columns (the data). Static data is data that lives in a chunk whose set of time columns is the empty set.
The easiest way to create such chunks is by using the log
family of methods, which exposes a static
flag where appropriate:
rr.log("skybox", static=True, generate_skybox_mesh())
The same can be achieved using the send_columns
API by simply leaving the time column set empty:
rr.send_columns("skybox", times=[], components=generate_skybox_mesh())
(Using send_columns
that way is rarely useful in practice, but is just a logical continuation of the data model.)
When should I use static data? when-should-i-use-static-data
There are two broad categories of situations where you'd want to use static data: scene setting and memory savings.
Scene setting scene-setting
Often, you'll want to store data that isn't part of normal data capture, but sets the scene for how it should be shown. For instance, if you are logging cars on a street, perhaps you want to always show a street mesh as part of the scenery, and for that it makes sense for that data to be static.
rr.log("skybox", static=True, generate_skybox_mesh())
The alternative would be to log that data at the beginning of every relevant timeline, which can be very problematic as the set of timelines might not even be known before runtime.
Similarly, coordinate systems or annotation context are typically stored as static.
Memory savings memory-savings
When you store temporal data in Rerun, it is always appended to the existing dataset: there is no such thing as overwriting temporal data. The dataset only grows, it never shrinks. To compensate for that, the Rerun viewer has a garbage collection mechanism that will drop the oldest data from the store when memory becomes scarce.
For example, the following snippet stores 10 images at index 4
on the frame
timeline:
rr.set_time_sequence("frame", 4)
for _ in range(10):
rr.log("camera/image", camera.save_current_frame())
All these images are actually stored, and all of them can be visualized in the viewer independently, even though they share the same index.
Contrary to temporal data, static data is never garbage collected⦠but it can actually be overwritten! Semantically, only a single piece of static data can exist at a given time for a specific component on a specific entity.
In the following snippet, only the data from latest log call (in execution order) will be inspectable in the viewer:
for _ in range(10):
rr.log("camera/image", static=True, camera.save_current_frame())
In practice, the Rerun datastore will rely on these semantics to physically drop the superfluous static data where possible, therefore drastically reducing memory costs. See "Understanding storage costs" for more information.
Understanding storage costs understanding-storage-costs
In "Memory savings", we mentioned that the following snippet semantically stores a single image:
for _ in range(10):
rr.log("camera/image", static=True, camera.save_current_frame())
How these semantics actually translate to physical storage depends on the context.
In recordings in-recordings
Rerun recordings (.rrd
files) are just streams of binary messages: they have no semantics whatsoever, therefore they don't know what static means and can't do anything about it.
If you were to log the snippet above to a file (using e.g. rr.save()
), you'd find that the recording does in fact contains your 10 images.
If you wanted the recording file itself to only contain a single static value, you would need to either:
- Stream the data to the viewer, and then save the recording directly out of the viewer using
Menu > Save recording
(or the equivalent palette command). - Manually recompact your recording using the Rerun CLI so that the data overwrite semantics can get appropriately applied, e.g.:
rerun rrd compact -o compacted.rrd myrecording.rrd
.
In the viewer in-the-viewer
The data store that backs the Rerun viewer natively understands these temporal/garbage-collected vs. static/overwritten semantics.
If you were to log the snippet above directly to the Rerun viewer (using e.g. rr.connect()
), you'd notice that the viewer's memory usage stays constant: the data is automatically being overwritten as new updates come in.
For data where you don't need to keep track of historical values, this effectively to logs its new values indefinitely.
In the following example, you can see our face tracking example indefinitely tracking my face while maintaining constant memory usage by logging all data as static: