How to prevent instancer flattening in Hydra 2.0

Hello,

I might be missing something obvious here, but how could we avoid flattening of nested instancers? Right now, we’re getting a bunch of prototype copies like ForInstancer52ed…14c2, which are coming from UsdImagingPiPrototypePropagatingSceneIndex. The problem is that in scene there’s just a couple of prototypes with lots of instances, but in Hydra there’s a lots of such prototypes with just a handful instances. Admittedly, it gives visually correct result, but it’s wasting a lot of memory.

As our renderer can natively deal with nesting (up to certain level) we would like to take advantage of it and limit number of prototypes. As I understand, it would involve customizing PrototypePropagatingSceneIndex, however I’m unsure how to disable/change the default one (without patching code) and/or orchestrate with custom one within third party applications.

I’ve seen that this topic was touched in a couple of places, but I haven’t found any concrete examples. Is there something that I’m missing?

1 Like

Hi Jakub. My understanding is that Hydra 2 does support nested instances, and the HdPrman render delegate makes use of this – for example, instancing a leaf prototype onto a tree prototype, and instancing the tree onto a forest. There are, however, some unresolved questions around the implementation of deep selection within nested instances.

There are two phases that determine the final number of prototypes. First, the USD scenegraph applies its instance-sharing rules to determine how many prototypes to generate to back its native instances. These are what live in the USD scenegraph at paths such as </__Prototype_NNN>. These are serially numbered but not in a deterministic order. In usdview you can see these if you turn on Show > Prototype Prims.

The next phase is that Hydra applies additional rules that may further split prototypes based on inherited state. This is what the “instance aggregation” in UsdImaging is about. You can see a list of the input fields used to split prototypes in _InstanceDataSourceNames() in usdImaging/sceneIndices.cpp: OpenUSD/pxr/usdImaging/usdImaging/sceneIndices.cpp at dev · PixarAnimationStudios/OpenUSD · GitHub

Currently this list includes:

  • material bindings
  • purpose
  • certain model (asset) level schemas, which may provide context for texture path resolution

Perhaps one of these is varying across your prototypes and causing Hydra to split them further, unexpectedly? Also, I know that there has been some discussion / observation that we may need some more flexibility over this policy in the future, in order to better adapt to renderer-specific capabilities. Motivating examples there include (1) some renderers such as RenderMan can vary surface (but not displacement) materials per-instance, (2) some renderers may be able to run UsdSkel vertex deformation internally, allowing crowd agent prototypes to stay shared while letting individual instances pose uniquely.

Hope this helps. More robust handling of instances has definitely been a focus for Hydra 2 but it is a large topic with a lot of converging / diverging needs.

Hi blevin,
Thank you for your response. Indeed, I’ve seen code in HdPrman, however trying to replicate it I’m having hard time to figure out, which HdMesh should be used as actual prototype as render delegate is provided with a number of those prototype copies. Please consider this toy example scene, where there are only two simple mesh cubes, which in turn are instanced a bunch of times. When inspecting Hydra index you should see that there’s a number of separate prototypes instead of expected two.

I’d expect that we could instruct Hydra to request only those two meshes and deal with instancing complexity with custom implementation of HdInstancer. Am I missing some setting / env variable that enables that? I’m using USD-24.3 for my testing.

pointInstancersNested.usda (4.1 KB)

Hi Jakub, thanks for the example file.

I can confirm that I see the result you describe: rather than 2 prototypes (one for each cubeMesh), there are more.

I asked the Hydra team why this happens. They said that the UsdImaging point instancer implementation currently creates re-rooted child copies of the prototypes used by the point instancer. I don’t fully grasp the details, but they say this was motivated by trying to implement USD’s semantics.

The considerations they mentioned include UsdGeomPointInstancer’s ability to both use prototypes that either are / are not rooted under that PointInstancer; and to use prototypes that either are concrete (with a def, meaning it will render on its own in addition to being used as a prototype elsewhere) or abstract (with only an over, which is normally ignored by Hydra, but can be used via PointInstancer).

It sounds like it might be possible to revisit this UsdImaging implementation approach in the future to see if it would be feasible avoid creating re-rooted copies of the prototypes, while still supporting USD semantics. It is unclear how large a project would be, given that the implementors so far believed that re-rooting was necessary.

(Side thought. Setting aside USD and UsdImaging and their approaches to instancing for a moment, it seems to me that design space for instancing approaches is broad, and one of the other approaches is to identify and recover sharing opportunities “late” through content-based hashing approaches. HdPrman already uses this for de-duplicating material networks, and I believe HdSt (Storm) does something similar for de-duplicating primvar buffers. At risk of speculating about things I’m not too familiar with, I wonder if something like that could help a renderer delegate recover prototype sharing opportunities, if it’s infeasible to do that upstream.)

The multiple prototypes conversation seems to be related to this: