.stage() / .editableStage() usage

   391   4   1
User Avatar
Member
86 posts
Joined: Nov. 2019
Offline
Hi!

This topic has been brought up in several posts but I am still having some questions for specific workflows, even after watching Mark Tucker's Hive presentation.

What would be the best performing approach for the following example?
Given a user given pattern of thousands of primitives, we have to run some checks accessing to some of its attributes. If the check is successful we create n references per primitive with data fetched from the original primitive.

I would instinctively do as follows;

read_stage = node.inputs()[0].stage()

...

write_stage = node.editableStage()
for path in paths:
# Read attribute per prim from read_stage

# Write: Create References in write_stage

This has never printed a warning and I have never felt a poor performance. However, members of SideFx involved in Solaris have repeatedly told me this is dangerous and potentially worse performance wise, because of how the stage locks work.
What I was told would be something like this:

read_stage = node.inputs()[0].stage()

...

# Read
for path in paths:
# Read attributes
# Store in arrays all needed data.

# Write
write_stage = node.editableStage()
for path in paths:
# Access the arrays for the necessary data
# Create references


My question here is, even when we are dealing with thousands of paths and accesses, is this second approach better in performance? How is the first approach dangerous?


Thanks!
Edited by Cicuta - Dec. 17, 2024 04:36:53
User Avatar
Staff
4525 posts
Joined: July 2005
Offline
The code there will never hit the "trying to lock a stage that is already locked" warning. Because calling node.inputs().stage() doesn't actually "lock" the stage. It returns the stage for the input node, but immediately releases the lock. So when you then call node.editableStage() later, which does hold onto a lock, you're still safe from the "already locked" warning. If you were to call node.inputs().stage() again after calling node.editableStage(), _then_ you'd get the warning and suffer the poor performance. But you aren't doing that.

So is the second approach to the code still the right one? Yes.

Why? Because the first block of code is very misleading. It gives the impression that "read_stage" and "write_stage" are separate things. But they aren't. As explained in the horizon video, the input node and the python node share a single USD stage. So read_stage and write_stage will be the _same stage_. When you modify write_stage, you are also modifying read_stage. Not fatal, and maybe not even problematic in your node, but misleading.

If you know that each of your edits is guaranteed to be completely independent from all the others, it would be totally fine to do:

```
stage = node.editableStage()
for path in paths:
# Read attribute per prim from stage

# Write: Create References in stage
```
Which is actually identical to your first block of code in terms of behavior, but makes it clear that you are reading from and writing to the same stage. But if there is any chance that one edit will affect the "read" of a subsequent edit, then this may not be a good way to go.

I'll also say that the pure python part of reading a bunch of stuff and putting it into a dictionary from which you later read the values to apply the write operations will take an inconsequentially small amount of time. 99% of the time to run your python LOP will be in the bit where you are creating the references, regardless of how you structure the rest of the code (don't believe me on this - measure it, because I may be wrong).

If you really want to make your node faster, figure out how to use the Sdf APIs on the node.editableLayer() instead of using Usd APIs on the write_stage. Especially if you're creating a lot of prims, this will make a huge difference. If you use this approach, you can use the new node.uneditableStage() method (added in 20.5.403) after calling editableLayer(). This will give you access to a readable stage that will be unaffected by the Sdf edits you make to the editableLayer.
User Avatar
Member
86 posts
Joined: Nov. 2019
Offline
Insane! Thank you so much for these tips, makes a lot of sense.
User Avatar
Member
86 posts
Joined: Nov. 2019
Offline
I have noticed that the moment I use node.editableLayer() I cannot use node.editableStage(). Is it possible, when using node.editableLayer() for creating primtives, adding those into a brand new collection? In python collections use the Usd API only and itseems incompatible with working with node.editableLayer(). Is that something that has to be done separately?

Thanks!
User Avatar
Staff
4525 posts
Joined: July 2005
Offline
No, you can only use one or the other.

If you're careful, and know that what you're doing is "safe" (you'll have to do your own research to make such a determination), you can accomplish something very similar. Call editableStage, but then create an Sdf.ChangeBlock, and use Sdf APIs on the "edit target layer" rather than using Usd APIs on the Usd.Stage. Using this approach you could create all your prims inside an Sdf.ChangeBlock, end the change block, then use the Usd APIs to author your collection.

Also, there is nothing in USD that can _only_ be done with USD APIs. Because all Usd APIs boil down to calling Sdf APIs in the C++ code. It just may require some investigation and experimentation to figure out what Sdf APIs you need to call to accomplish what the Usd APIs are doing.
  • Quick Links