Skip to content

Specification Proposal : "Functional" Node Definitions #2355

@kwokcb

Description

@kwokcb

Proposal for "Functional" Node Definition

Proposal

Introduce a syntax to reduce the complexity of specifying and maintaining a definition and it's corresponding implementation graph.

  • This makes packaging of definitions simpler, including for the case of embedding definitions within working documents.
  • This is additive and can be done incrementally for either new or existing definitions.
  • This could be considered for any proposal to introduce definitions into UsdShade for OpenUSD.
  • This can also be considered as an useful "consolidation" step for any future proposal for type-less definitions.

We will use the term functional node definition for this definition variant.

Scope

This only affects definitions using node graph implementations. All other implementation variants are not affected.

Affected areas would include:

  • Validation
  • Implementation lookup
  • Code generation
  • Definition organization

Background Complexity

Currently nodegraph (functional graph) is associated nodedef (definition declaration).

This separation is useful in the case where here can be multiple implementations for a given definition, but is not required
when there is only one implementation (a).

The following are the details of implementation logic which a user must handle properly:

  • This creates a implementation split for a logically atomic entity into two parts, where the following validation logic must be adhered to:

    1. The definition has the interface inputs and outputs without connections as there is no implementation
    2. The graph has the node graph implementation, without interface inputs and outputs with connections to nodes in the graph implementation.
      • Connections to internal node inputs from definition inputs are allowed but cannot be defined on the graph.
      • The output on the graph and the definition must match by name, and type. Outputs can specify a default input which can only be defined on the definition.
    3. Specifies the association "backwards" from the graph to the corresponding definition via a nodedef meta-data attribute on the graph.
  • The concept of a compound nodegraph and functional nodegraph must be handled by the user due to the above logic rules.

    • A specific check is required to see if a nodedef meta-data attribute is defined on the nodegraph.
    • This is required to allow different actions to be performed on a compound graph versus a functional graph. For example, the former is mutable but the latter is not.
    • The interface for the functional graph must be manually kept in sync with it's definition. If the graph is changed without modifying it's corresponding definition then this can make the entire definition invalid.
    • This check is also required to avoid mixing definitions and non-definitions within the same "working" document. For example it is not recommended to embe the standard library definitions within a working document.
  • By convention, the standard library separates the functional nodegraph and definition into two files making it harder to match one with the other. When publishing (creating) definitions this splits an otherwise "atomic" grouping into 2 places.

(a) It is still possible to have implementations per target, but the node graph implementation is considered the "default" one when no such overrides are defined. Note that this mechanism would still be supported with the current syntax and implementation logic.

Proposal

The proposal is allow the implementation of a functional graph to reside directly within the definition (nodedef) making this an "atomic" entity.

Changes:

  • Outputs would specify connections to internal node outputs for data routing. If specified then the graph should reside within the definition.
  • Outputs can still specify default values or inputs.
  • Additional functional node graphs are only allowed if they are for a specific target. (Note this may not be currently allowed by the spec.)

Example: The following is an example of one of the safepower definitions:

Current Specificaition

  • Definition
  <nodedef name="ND_safepower_float" node="safepower" nodegroup="math">
    <input name="in1" type="float" value="0.0" />
    <input name="in2" type="float" value="1.0" />
    <output name="out" type="float" defaultinput="in1" />
  </nodedef>
  • Functional Graph
 <nodegraph name="NG_safepower_float" nodedef="ND_safepower_float">
    <sign name="sign_in1" type="float">
      <input name="in" type="float" interfacename="in1" />
    </sign>
    <absval name="abs_in1" type="float">
      <input name="in" type="float" interfacename="in1" />
    </absval>
    <power name="power" type="float">
      <input name="in1" type="float" nodename="abs_in1" />
      <input name="in2" type="float" interfacename="in2" />
    </power>
    <multiply name="safepower" type="float">
      <input name="in1" type="float" nodename="sign_in1" />
      <input name="in2" type="float" nodename="power" />
    </multiply>
    <output name="out" type="float" nodename="safepower" />
  </nodegraph>

New Specification

  <nodedef name="ND_safepower_float" node="safepower" nodegroup="math">
    <input name="in1" type="float" value="0.0" />
    <input name="in2" type="float" value="1.0" />
    <output name="out" type="float"  defaultinput="in1" /> <!-- defaults stay here -->

     <nodegraph name="NG_safe_power_float"> <!-- back reference to nodedef removed -->
        <sign name="sign_in1" type="float">
          <input name="in" type="float" interfacename="in1" />
        </sign>
        <absval name="abs_in1" type="float">
          <input name="in" type="float" interfacename="in1" />
        </absval>
        <power name="power" type="float">
          <input name="in1" type="float" nodename="abs_in1" />
          <input name="in2" type="float" interfacename="in2" />
        </power>
        <multiply name="safepower" type="float">
          <input name="in1" type="float" nodename="sign_in1" />
          <input name="in2" type="float" nodename="power" />
        </multiply>
        <output name="out" type="float" nodename="safepower"  /> <!-- output routing stay's here -->
     </nodegraph>
</nodedef>
flowchart LR
    subgraph Current
        ND[Nodedef: ND_safepower_float]
        NG[Nodegraph: NG_safepower_float]
        ND -->|Referenced by nodedef attribute| NG
        ND_inputs[Inputs/Outputs] --> ND
        NG_nodes[Internal Nodes & Routing] --> NG
    end

    subgraph Functional Node Definition
        FND[Nodedef with embedded Nodegraph]
        FND_inputs[Inputs/Outputs] --> FND
        FND_nodes[Internal Nodes & Routing] --> FND
    end

    style ND fill:#151,stroke:#111,stroke-width:2px
    style NG fill:#131,stroke:#111,stroke-width:2px
    style FND fill:#151,stroke:#111,stroke-width:2px

Loading

With the new specification

  • No search is required to find if and where the implementation reside as all content is specified by a single entity.
  • Interfaces and graph node references reside within the same context.
  • No additional functional graph is required to be specified and maintained.
  • Validation checking would be simplified especially for connectivity checking.

Implementation Logic

  • Accessors to get the definition from an nodegraph / implementation would search for a parent definition first,. All existing logic can remain and thus have secondary priority.
  • The cache used to keep definition -> list of implementations would need to scan for implementaiton children parented under a definition. These entries would be added before other existing logic and thus be given higher priority.
    • Note that the existing cache building allows for duplicate implementations already as it does not check for implementation name nor target usage. The existing search logic finds the first suitable implementation. This would not change.
  • There is a convenience function to create definitions from nodegraphs. This can add in logic to create definitions with child implementations. The option could either default to child implementation or not. For backwards compatibility it can be to no create children and be switch to create children in a later release where behaviour is allowed to change.

These are relatively small additive changes in isolated areas of the code. API callers would not see any difference (except for the creator convenience function).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions