Dynamic scene building

Howdy,

I’ve been trying to learn the Optix7 API by building an interactive viewer, mostly based on the Optix7 SDK optixMeshViewer sample. I’ve got a bare bones and probably very naive version up and running at https://github.com/Hurleyworks/Optix7Sandbox

There’s a few things I’m not sure I did the best way.

  1. Every time a new mesh is added, I totally rebuilt the hitgroup section of the SBT and totally rebuilt the top level IAS. Is that the only/best practice for handling dynamically added meshes?

  2. When dynamically deleting a mesh from the scene, I freed the device buffer holding all the geometry data and freed the device buffer holding the compacted GAS but I’m not sure what to do with the OptixTraversableHandle … I didn’t see an api for deleting it. I then totally rebuilt the hitgroup section of the SBT and totally rebuilt the top level IAS again. Is that the correct way to dynamically remove a mesh from the scene?

TIA

Steve

Hi, good questions.

So best practice depends on the features and requirements and design of your app, and the scale of your dynamic updates, I can’t answer that directly, but here are some ways to think about adding & removing things that I hope will help you.

Adding a new instance to the scene will usually require a top level IAS rebuild, so that is typically the only thing you can do. If you know where new objects will go in advance, then there are some advanced techniques, but in general we should assume an IAS rebuild. You can save time if you avoid re-allocating the IAS buffer by padding it, pre-allocating space for extra instances. This would also imply not using compaction for your IAS.

Keep in mind that optixMeshViewer is organized in a particular way meant for ease of understanding, but it doesn’t represent the only way to organize your IAS & SBT. For example, you don’t necessarily need to have a hit group record for each instance. That’s convenient for our SDK sample, but may not be ideal in more complex scenarios. Your hit group record could store pointers to per-instance data.

The main thing to think about is that the contents of any given entry in the SBT only need to be valid for SBT offsets you’re currently using in your acceleration structures. If you’re not referencing a particular SBT entry, then it won’t be used, and it isn’t required to contain valid information. Valid SBT entries also do not need to be contiguous, and you don’t have to use a contiguous set of SBT offsets in your acceleration structures… there can be holes in memory and holes in your numbering scheme. This has implications for how you might both add and remove objects quickly when making dynamic updates.

What you can do, optionally, is pre-allocate your entire SBT if you have a maximum number of hitgroups, or alternatively you could also treat your SBT like a dynamic array: pre-allocate space for several hitgroups, and resize it in batches to amortize the allocation & update work. You could put some dummy hit group records in your SBT in advance, and then update only 1 existing hit group record in-place when you need a new slot.

For removing objects, you don’t necessarily need to touch the SBT. Once you remove it from the IAS, the corresponding SBT entry won’t be accessed, so there’s no need to rebuild the hitgroup table at all unless you want to, you can just keep track of which SBT entries are available for overwrite. You might want to zero out the unused SBT entries just for safety, especially if you’re deleting the shaders they reference.

Another feature at your disposal here is visibility masks. If you aren’t using them for something else, you could designate masks for “valid” and/or “invalid/deleted” objects. If you used visibility masks, then to remove an object from the scene, you could mark the instance with the “invalid” mask. The instance mask is part of the instance array input to optixAccelBuild(), and for only updating the visibility masks (and SBT offsets if you want) but not the transforms, you can use OPTIX_BUILD_OPERATION_UPDATE which is much faster than OPTIX_BUILD_OPERATION_BUILD.

So, you could imagine a scenario where you can get away with very little work when “deleting” objects from your scene, and only rebuild your IAS & update your SBT when you add new objects. Or if you want to support doing both operations in any given frame, you could imagine making yourself a simple host-side class to keep track of available SBT slots and which small blocks of GPU memory need updating when you add & remove things, then only rebuild your SBT & IAS occasionally if you run out of available slots. Or pre-allocate space for everything and never do a full rebuild.

The OptixTraversableHandle is an encoding of the pointer, so you don’t need to destroy a traversable handle, you are done once you delete the associated buffer.

I hope that helps you expand your options and see some interesting possibilities. It’s worth mentioning that managing the OptiX SBT with OptiX 7 is now very similar to what people are doing in Vulkan and Direct-X ray tracing. So for strategies for dynamic scenes, you can take advantage of the larger respository of knowledge online for building games in DXR, for example, and apply those strategies directly in OptiX. If you look around, you’ll find presentations that talk about tricky ways of managing changes to the SBT and minimizing the amount of work that needs to be done.


David.

Hi David,

Thanks for taking the time to explain all these options and possibilities. It’s very helpful!