Skip to content

Conversation

@anwang2009
Copy link

Description of Proposal

We'd like to propose more structured categorization of SdrShaderNodes for clarity, discoverability, and transferability between DCCs.

Link to Rendered Proposal

@anwang2009 anwang2009 changed the title Sdr categorization SdrShaderNode categorization Oct 23, 2025
@jesschimein jesschimein moved this from Draft to Finalizing in OpenUSD Proposals Status Oct 23, 2025
@JamesPedFoundry
Copy link

Shader Terminals

There is one piece of information I see missing here which is crucial for shading, and that is how these shaders link up to the RenderDelegates and Material Terminal namespaces.

Typically the render delegates specify the materialContext's they support (mtlx, prman, arnold, nsi, glslfx), and thus which terminals they are interested in, with a specified order. However, currently, and in the proposal, there is no link on the shader as to which terminal namespaces are expected (not enforced).

To me, this makes sense(if sticking with the current terms) as the "target". Currently the proposal describes MaterialX specifying its target as "MaterialX". I would like to suggest that becomes mtlx, and similarly prman, arnold, nsi, glsfx etc...

This does cause some confusion for UsdPreviewSurface in particular; which tends to be supported by all. Typically defined with a terminal namespace/materialContext of glslfx or no target at all. I guess here UsdPreviewSurface could be specified with no target, and thus would appear relevant for all.

It may be that we don't try to shoehorn this into "targets" and have a separate "preferredMaterialContext" or similar field which describes the preferred terminal namespace to have it understood by a renderer as a priority.

Collections

I am also interested to learn more about "collections", the single sentence not quite fully describing their behaviours/design ideas? Is this how a renderer would specify which shaders should appear to an artist when they want to see shaders related to their renderer?

Registry method request

Additionally it would make me happy if there are methods added to the SdrRegistry to "getAllBySubDomain(const TfToken subDomain)" which could then filter (and likely cached for performance reasons) all the shaders related to a certain context; since I imagine this being used frequently to populate user interfaces. (We can then group further by "target")

API changes

I am also not super keen on the change in behaviour for GetShaderNodeIdentifiers/GetShadeNodeNames. We typically use this to get all the shaders and it works well for returning us a list of all the shaders without filtering applied. These method names make sense for their action, and with the new proposal for family to be more utilised, I see this being cumbersome to have to request all the possible different families of shaders/lights? I may well be misunderstanding the change, but from our usage, we use these methods to get all the shaders, and then we loop through thouse shaders and look at the actual shader items in the registry and retain the ones we care about.
For a more concrete example (In pseudo code), we can:

GetLights():
lights = {}
 for shader in GetShaderNodeIdentifiers():
    if shader.getContext() == "light":  
      lights[shaderName] = shader

If anything I'd prefer the removal of the optional filter altogether from GetShaderNodeIdentifiers, and retaining the more specialised method of GetShaderNodesByFamily if filtering by Family is necessary.

@anwang2009
Copy link
Author

anwang2009 commented Nov 7, 2025

Hey @JamesPedFoundry , thanks for the feedback!

Re: Shader Terminals

Reconsidering target in context of terminal namespaces is a great point, and I've been exploring this more. The rabbit hole goes pretty deep -- this is far from final but here's what we're thinking so far:

Here are the concepts at play.

  • source container is the raw shading asset's form that gets consumed, often associated with some file format.
  • target system is something like MaterialX, OSL, glslfx that usually has its own standard and shading language
  • target renderer is a renderer like Renderman or Arnold that often has support for multiple shading systems
  • terminal namespace is a convention adopted by USD where shaders that are terminal nodes in a network define properties with names of the form “outputs:namespace:propName”. Render delegates in Hydra use this convention to find specific terminal properties in priority order of renderContexts as terminal namespaces. Hydra render delegates can also use renderContext as non-terminal namespaces.

It's becoming clear to me that target as a term is not constrained enough, so we're re-evaluating the term itself as well as its semantics relative to the above concepts.

  • target renderer isn't a useful item to have on every node, since it's better for the "source of truth" as to whether a node is renderable be centralized to the renderer itself, to minimize updates to shader definitions and code when the renderer changes what shading ecosystems it supports. If two different renderers have support for the same OSL node, it would be tragic to have to register two different nodes in Sdr, one for each renderer. So the best use for target renderer seems to be to exclude nodes from consideration with other renderers.

We're also re-evaluating sourceType, as currently it sounds like source container, but the term target system may be more accurate to the node's consumers (this is subject to change, I'm still spelunking through codebases).

More thoughts soon to come on this.

Re: Collections

Collections aren't meant to be renderer-based -- you could use them to specify renderers but I think "renderer" deserves more first-class treatment as discussed above.

  • Collections are meant to be more generic -- they should be specified at node parse time, populated through Sdr parser plugins. Metadata on shader definitions themselves might need to be modified to specify what collections they belong to, or one might programmatically deduce what collections a node should belong to in the parser plugin itself.
  • This is intended to enable categorizations of nodes that wouldn't fit in the proposed structured hierarchy or existing metadata. Perhaps usd's "previewSurface"-related nodes would be grouped into a "UsdPreview" collection. Maybe you'd want to categorize your shaders in terms of computational heaviness, like "perfLight" or "perfHeavy" -- something like that.

Re: Registry method request

We recently landed a SdrShaderNode Query API. An additional CustomFilter method is in the works that takes a custom function with signature bool (SdrShaderNode) so folks can provide their own filters. For now, no caching. I'm also planning a utility function to help with displaying nested groupings of nodes, motivated largely by one of the asks in this issue you filed. This query API isn't as convenient as getAllBySubDomain would be, but should be more flexible.

Re: API changes

If anything I'd prefer the removal of the optional filter altogether from GetShaderNodeIdentifiers, and retaining the more specialised method of GetShaderNodesByFamily if filtering by Family is necessary.

Thanks for the usage example.

  • Now I'm leaning towards just leaving GetShaderNodeIdentifiers and GetShaderNodeNames alone, since they'd be the only registry methods that allowing filtering on family before nodes are parsed.
  • The behavior of GetShaderNodesByFamily is covered by the query API addition and the proposed SdrShaderNodePtrVec ParseAndGetAll() registry addition. I don't think that getting all the nodes for a specific family is a popular operation, so I'd deprecate it in favor of the alternate suggestions, but I could be wrong. The safest alternative is to just let GetShaderNodesByFamily exist as convenience API but require it to specify a concrete family. The latter's fair and I'll prefer it since there's more contentious items otherwise in the proposal :)

@anwang2009
Copy link
Author

@JamesPedFoundry and anyone else interested -- I've updated the proposal with a few key modifications

  • sourceType is deprecated in favor of shadingSystem. This comes with a large swath of related API method deprecations. This renaming clarifies intended usage.
  • role is no longer deprecated. Instead, family is deprecated in favor of function, to align semantically with coding-style "function overloads".
  • target is renamed to targetRenderer for clarity

What are your thoughts?

@rafalSFX
Copy link

rafalSFX commented Jan 8, 2026

I really like and support the new term shadingSystem. It emphasizes the shading language aspect of it rather than a neutral-meaning "source", overloaded with other meanings. And I also like the clarified targetRenderer.

Overall, I think it's a nice refresh of the SDR conventions.

Also, I thought of another example where collections categorization might be useful. Namely, it could be used for grouping of shader nodes that read geometry properties. Each language has own shader name for it : MaterialX has geompropvalue, the USD preview shader set has UsdPrimvarReader, etc. Yet a DCC may be interested in all these shaders to provide a convenient way to author them and pre-link them with geometry, etc.

@JamesPedFoundry
Copy link

Hi @anwang2009,

I think these changes are looking great and agree the clarifications made here will significantly improve the SdrRegistry and our ability to use it as a source of "ground truth" as to what plug-ins, shaders or otherwise are available; as well as being able to produce more user friendly UI's due to standardising the categorisation.

I did have a couple of last clarification I wanted to check.

The first is that I see the addition of ParseAll to the API, is this something we'd have to run when inspecting the registry? Or is this done by the plug-ins when items are added to the registry? Not 100% sure on the functionality of that method itself; or would this allow for runtime level re-parsing of defined search paths?

In your examples of data in the registry, example_2 is a math node. It doesn't have the materialX ND_ prefix so am I correct in thinking this would be a separate definition from the ND_Add_float2 and that the ND_ prefixed node would be more like example 4 below?

field example 1 example 2 example 3 example 4
domain rendering general rendering rendering
subdomain filtering math shading shading
context sampleFilter pattern
role geometric math
function add position add
name add_float ND_add_vector4
identifier BackgroundSampleFilter add_float_3 ND_position_vector3 ND_add_vector4

This would be necessary for the case where we would utilise the registry to discover all rendering material shaders.

Cheers,
James

@anwang2009
Copy link
Author

SdrRegistry has two stages: discovery and parse. Discovery finds shaders in the filesystem and Parse constructs SdrShaderNode from those discovery results. @JamesPedFoundry -- SdrRegistry::ParseAll is a helper method to parse all discovery results to SdrShaderNodes using their affiliated SdrParserPlugins. Normally, shader nodes are only parsed when specifically requested via methods like SdrRegistry::GetShaderNodeByIdentifier. So ParseAll and ParseAndGetAll are useful if you want to batch process nodes based on their fields and metadata (which are only fully accessible post-parse). No re-parsing is done if a node is already parsed. If being invoked for the first time, these parse methods are potentially expensive depending on the parser plugins in your system and number of nodes.

Yes, the math node I put in for example 2 doesn't exist currently, it's just there for illustrative purposes.

I checked the SdrRegistry for the ND_add_vector4 example you added. Under this proposed system, we'd see context=pattern and role=math. In general, we'd prefer filling out "context" before "role". From the proposal,

subdomain has no analogues in MaterialX and will remain empty unless additional annotation of data at Sdr parse time is reasonable, or additional metadata is added to MaterialX nodes themselves.

So subdomain would stay empty when the proposal is implemented. It sounds like you'd find it useful for subdomain to be filled out for all mtlx nodes? If so, when this proposal lands, I'll file a followup issue in OpenUSD. It'd be a natural followup that the community can discuss.

@JamesPedFoundry
Copy link

JamesPedFoundry commented Jan 12, 2026

Thank you for the clarifications, the addition of ParseAll and ParseAndGetAll does sound like a great addition and thank you for the explanation there.

I did wonder what the context would be as I couldn't determine from looking around the MaterialX code base what the context was. I was merely trying to estimate based on the data in the nodedefs.mtlx file, so my example could be taken with a grain of salt.

However, saying that I wouldn't be adverse to having the function or name specified to group up similar nodes with different input/output types (since there are many "Add" nodes). I wouldn't necessarily block any current progress for this though, as I imagine this is something that can be extended in the future; as you mention in a follow up issue.

Thank you again for the proposal descriptions, and answering my questions, I don't think I have any further questions and look forward to this coming into the release line!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Finalizing

Development

Successfully merging this pull request may close these issues.

3 participants