OGN Reference Guide

This is a detailed guide to the syntax of the .ogn file. All of the keywords supported are described in detail, and a simplified JSON schema file is provided for reference.

Each of the described elements contains an example of its use in a .ogn file to illustrate the syntax. For a more detailed guide on how each of the elements are used see the OGN User Guide.

Node Level Keywords

Attribute Level Keywords

description

description

exclude

default

icon

deprecated

memoryType

memoryType

cudaPointers

metadata

metadata

maximum

singleton

minimum

tags

optional

tokens

type

uiName

uiName

version

uiType

language

unvalidated

scheduling

categories


Basic Structure

See the Naming Conventions for guidance on how to name your files, nodes, and attributes.

{
    "NodeName": {
        "NODE_PROPERTY": "NODE_PROPERTY_VALUE",
        "inputs": "ATTRIBUTE_DICTIONARY",
        "outputs": "ATTRIBUTE_DICTIONARY",
        "state": "ATTRIBUTE_DICTIONARY",
        "tests": "TEST_DATA"
    }
}

The NODE_PROPERTY values are keywords recognized at the node level. The values in NODE_PROPERTY_VALUE will vary based on the specific keyword to which they pertain.

The ATTRIBUTE_DICTIONARY sections contain all of the information required to define the subset of attributes, each containing a set of Attribute Property Keywords that describe the attribute.

Lastly the TEST_DATA section contains information required to construct one or more Python tests that exercise the basic node operation.

Comments

JSON files do not have a syntax for adding comments, however in order to allow for adding descriptions or disabled values to a .ogn file the leading character “$” will treat the key in any key/value pair as a comment. So while "description":"Hello" will be treated as a value to be added to the node definition, "$description":"Hello" will be ignored and not parsed.

Comments can appear pretty much anywhere in your file. They are used extensively in the Walkthrough Tutorial Nodes to describe the file contents.

{
    "Node": {
        "$comment": "This node is like a box of chocolates - you never know what you're gonna get",
        "description": [

Node Property Keywords

These are the elements that can appear in the NODE_PROPERTY section. The values they describe pertain to the node type as a whole.

description

The description key value is required on all nodes and will be used in the generated documentation of the node. You can embed reStructuredText code in the string to be rendered in the final node documentation, though it will appear as-is in internal documentation such as Python docstrings.

The value can be a string or a list of strings. If it is a list, they will be concatenated as appropriate in the locations they are used. (Linefeeds preserved in Python docstrings, turned into a single space for text documentation, prepended with comment directives in code…)

Tip

This mandatory string should inform users exactly what function the node performs, as concisely as possible.

{
    "Node": {
        "$comment": "This node is like a box of chocolates - you never know what you're gonna get",
        "description": [
            "This node is part of the OmniGraph node writing examples.",
            "It is structured to include node and attribute information illustrating the .ogn format"
        ],
        "version": 1,

version

The integer value version defines the version number of the current node definition. It is up to the node writer how to manage the encoding of version levels in the integer value. (For example a node might encode a major version of 3, a minor version of 6, and a patch version of 12 in two digit groups as the integer 30612, or it might simply use monotonic increasing values for versions 1, 2, 3…)

Tip

This mandatory value can be anything but by convention should start at 1.

        "description": [
            "This node is part of the OmniGraph node writing examples.",
            "It is structured to include node and attribute information illustrating the .ogn format"
        ],
        "version": 1,
        "exclude": ["c++", "docs", "icon", "python", "template", "tests", "usd"],

exclude

Some node types will not be interested in all generated files, e.g. if the node is a Python node it will not need the C++ interface. Any of the generated files can be skipped by including it in a list of strings whose key is exclude. Here is a node which excludes all generated output, something you might do if you are developing the description of a new node and just want the node syntax to validate without generating code.

Legal values to include in the exclusion list are “c++”, “docs”, “icon”, “python”, “template”, “tests”, or “usd”, in any combination.

Note

C++ is automatically excluded when the implementation language is Python, however when the implementation language is C++ there will still be a Python interface class generated for convenience. It will have less functionality than for nodes implemented in Python and is mainly intended to provide an easy interface to the node from Python scripts.

        "version": 1,
        "exclude": ["c++", "docs", "icon", "python", "template", "tests", "usd"],
        "language": "python",

icon

A string value that represents the path, relative to the .ogn file, of the icon file that represents the node. This icon should be a square SVG file. If not specified then it will default to the file with the same name as the .ogn file with the .svg extension (e.g. OgnMyNode.ogn looks for the file OgnMyNode.svg). When no icon file exists the UI can choose a default for it. The icon will be installed into the extension’s generated ogn/ directory

        "exclude": ["c++", "docs", "icon", "python", "template", "tests", "usd"],
        "icon": "icons/CompanyLogo.svg",
        "language": "python"

The extended syntax for the icon description adds the ability to specify custom coloring. Instead of just a string path, the icon is represented by a dictionary of icon properties. Allowed values are “path”, the icon location as with the simple syntax, “color”, a color representation for the draw part of the icon’s shape, “backgroundColor”, a color representation for the part of the icon not containing its shape, and “borderColor”, a color representation for the outline of the icon.

Colors are represented in one of two ways - as hexadecimal in the form #AABBGGRR, or as a decimal list of [R, G, B, A], both using the value range [0, 255].

        "exclude": ["c++", "docs", "icon", "python", "template", "tests", "usd"],
        "icon": {
            "path": "icons/CompanyLogo.svg",
            "color": "#FF123456",
            "backgroundColor": [86, 52, 18, 255],
            "borderColor": "#FF3E3E3E"
        },
        "language": "python"

Note

Unspecified colors will use the defaults. An unspecified path will look for the icon in the default location.

language

A string value that represents the language of implementation. The default when not specified is “c++”. The other legal value is “python”. This value indicates the language in which the node compute algorithm is written.

        "exclude": ["c++", "docs", "icon", "python", "template", "tests", "usd"],
        "language": "python",
        "memoryType": "cuda",

memoryType

Nodes can be written to work on the CPU, GPU via CUDA, or both. For each case the data access has to take this into account so that the data comes from the correct memory store. The valid values for the memoryType property are cpu, cuda, and any. The first two mean that by default all attribute values on the node are exclusively in either CPU or CUDA-specific GPU memory. any means that the node could run on either CPU or GPU, where the decision of which to use happens at runtime. The default value is cpu.

        "language": "python",
        "memoryType": "cuda",
        "metadata": {

categories

Categories provide a way to group similar node types, mostly so that they can be managed easier in the UI.

        "description": "This is some kind of math array conversion node",
        "categories": "math:array,math:conversion"

For a more detailed example see the Node Categories “how-to”.

cudaPointers

Usually when the memory type is set to cuda or any the CUDA memory pointers for array types are returned as a GPU pointer to GPU data, so when passing the data to CUDA code you have to pass pointers-to-pointers, since the CPU code cannot dereference them. Sometimes it is more efficient to just pass the GPU pointer directly though, pointed at by a CPU pointer. (It’s still a pointer to allow for null values.) You can do this by specifying “cpu” as your cudaPointers property.

        "metadata": {
            "author": "Bertram P. Knowedrighter"
        },
        "cudaPointers": "cpu",
        "uiName": "OmniGraph Example Node"

Note

You can also specify “cuda” for this value, although as it is the default this has no effect.

metadata

Node types can have key/value style metadata attached to them by adding a dictionary of them using the metadata property. The key and value are any arbitrary string, though it’s a good idea to avoid keywords starting with with underscore (_) as they may have special meaning to the graph. Lists of strings can also be used as metadata values, though they will be transformed into a single comma-separated string.

A simple example of useful metadata is a human readable format for your node type name. UI code can then read the consistently named metadata to provide a better name in any interface requiring node type selection. In the example the keyword author is used.

        "memoryType": "cuda",
        "metadata": {
            "author": "Bertram P. Knowedrighter"
        },
        "tokens": "apple",

Tip

There are several hardcoded metadata values, described in this guide. The keywords under which these are stored are available as constants for consistency, and can be found in Python in the og.MetadataKeys object and in C++ in the file omni/graph/core/ogn/Database.h.

scheduling

A string or list of string values that represent information for the scheduler on how nodes of this type may be safely scheduled. The string values are fixed, and say specific things about the kind of data the node access when computing.

        "version": 1,
        "scheduling": ["global-write", "usd"],
        "language": "python"
        "version": 1,
        "scheduling": "global-write,usd",
        "language": "python"

The strings accepted as values in the .ogn file are described below (extracted directly from the code)

class SchedulingHints:
    """Class managing the scheduling hints.
    The keywords are case-independent during parsing, specified in lower case here for easy checking.

    When there is a -read and -write variant only one of them should be specified at a time:
        no suffix: The item in question is accessed for both read and write
        -read suffix: The item in question is accessed only for reading
        -write suffix: The item in question is accessed only for writing

    These class static values list the possible values for the "scheduling" lists in the .ogn file.

    # Set when the node accesses other global data, i.e. data stored outside of the node, including the data
    # on other nodes.
    GLOBAL_DATA = "global"
    GLOBAL_DATA_READ = "global-read"
    GLOBAL_DATA_WRITE = "global-write"

    # Set when a node accesses static data, i.e. data shared among all nodes of the same type
    STATIC_DATA = "static"
    STATIC_DATA_READ = "static-read"
    STATIC_DATA_WRITE = "static-write"

    # Set when the node is a threadsafe function, i.e. it can be scheduled in parallel with any other nodes, including
    # nodes of the same type. This flag is not compatible with the topology hints that aren't read-only.
    THREADSAFE = "threadsafe"

    # Set when the node accesses the graph topology, e.g. connections, attributes, or nodes
    TOPOLOGY = "topology"
    TOPOLOGY_READ = "topology-read"
    TOPOLOGY_WRITE = "topology-write"

    # Set when the node accesses the USD stage data (for read-only, write-only, or both read and write)
    USD = "usd"
    USD_READ = "usd-read"
    USD_WRITE = "usd-write"

    # Set when the scheduling of the node compute may be modified from the evaluator default.
    COMPUTERULE_DEFAULT = "compute-default"
    COMPUTERULE_ON_REQUEST = "compute-on-request"

    # Set when the node author wishes to specify the purity of the computations that a node does.
    # A "pure" node is one that has no side effects in its initialize, compute, and/or release
    # methods (no mutation of data that is shared/can be accessed outside of the node scope, no
    # dependencies on external data apart from its inputs that could influence execution results).
    # In other words, pure nodes are deterministic in that they will always produce the same output
    # attribute values for a given set of input attribute values, and do not access, rely on, or
    # otherwise mutate data external to the node's scope.
    PURE = "pure"
    """

singleton

singleton is metadata with special meaning to the node type, so as a shortcut it can also be specified as its own keyword at the node level. The meaning is the same; associate a piece of metadata with the node type. This piece of metadata indicates the quality of the node type of only being able to instantiate a single node of that type in a graph or its child graphs. The value is specified as a boolean, though it is stored as the string “1”. (If the boolean is false then nothing is stored, as that is the default.)

        "language": "python",
        "singleton": true,
        "memoryType": "cuda"

tags

tags is a very common piece of metadata, so as a shortcut it can also be specified as its own keyword at the node level. The meaning is the same; associate a piece of metadata with the node type. This piece of metadata can be used by the UI to better organize sets of nodes into common groups.

        "tokens": "apple",
        "tags": "fruit,example,chocolate",
        "uiName": "OmniGraph Example Node",

Tip

Tags can be either a single string, a comma-separated string, or a list of strings. They will all be represented as a comma-separated string in the metadata.

tokens

Token types are more efficient than string types for comparison, and are fairly common. For that reason the .ogn file provides this shortcut to predefine some tokens for use in your node implementation code.

The simplest method of adding tokens is to add a single token string.

        "metadata": {
            "author": "Bertram P. Knowedrighter"
        },
        "tokens": "apple",
        "tags": "fruit,example,chocolate",

If you have multiple tokens then you can instead specify a list:

        "metadata": {
            "author": "Bertram P. Knowedrighter"
        },
        "tokens": ["apple", "pear", "orange"],
        "uiName": "OmniGraph Example Node"

The lookup is the same:

Lastly, if the token value contains illegal names for C++ or Python variables you can specify tokens in a dictionary, where the key is the name through which it will be accessed and the value is the actual token string:

        "metadata": {
            "author": "Bertram P. Knowedrighter"
        },
        "tokens": { "apple": "Granny Smith", "pear": "Bosc Pear", "orange": "Florida Navel Orange"},
        "uiName": "OmniGraph Example Node"

See the OGN User Guide for information on how to access the different sets of token in your code.

uiName

uiName is a very common piece of metadata, so as a shortcut it can also be specified as its own keyword at the node level. The meaning is the same; associate a piece of metadata with the node type. This piece of metadata can be used by the UI to present a more human-readable name for the node type.

        "tags": "fruit,example,chocolate",
        "uiName": "OmniGraph Example Node",

Tip

Unlike the actual name, the uiName has no formatting or uniqueness requirements. Choose a name that will make its function obvious to a user selecting it from a list.

Attribute Dictionaries

Each of the three attribute sections, denoted by the keywords inputs, outputs, and state, contain a list of attributes of each respective location and their properties.

inputs

Attributes that are read-only within the node’s compute function. These form the collection of data used to run the node’s computation algorithm.

outputs

Attributes whose values are generated as part of the computation algorithm. Until the node computes their values they will be undefined. This data is passed on to other nodes in the graph, or made available for inspection.

state

Attributes that persist between one evaluation and the next. They are both readable and writable. The primary difference between state attributes and output attributes is that when you set the value on a state attribute that value is guaranteed to be there the next time the node computes. Its data is entirely owned by the node.

        "uiName": "OmniGraph Example Node",
        "inputs": {
        },
        "outputs": {
        },
        "state": {
        }

Note

If there are no attributes of a specific location then that section can simply be omitted.

Attribute Property Keywords

The top level keyword of the attribute is always the unique name. It is always namespaced within the section it resides and only need be unique within that section. For example, the attribute mesh can appear in both the inputs and outputs sections, where it will be named inputs:mesh and outputs:mesh respectively.

Attribute Properties

Like the outer node level, each of the attributes has a set of mandatory and optional attributes.

description

As with the node, the description field is a multi-line description of the attribute, optionally with reStructuredText formatting. The description should contain enough information for the user to know how that attribute will be used (as an input), computed (as an output), or updated (as state).

Tip

This mandatory string should inform users exactly what data the attribute contains, as concisely as possible.

        "inputs": {
            "numberOfLimbs": {
                "description": "The number of limbs present in the generated character",
                "type": "int",

type

The type property is one of several hard-coded values that specify what type of data the attribute contains. As we ramp up not all type combinations are supported; run generate_node.py –help to see the currently supported list of attribute types. For a full list of supported types and the data types they generate see Attribute Data Types.

Tip

This field is mandatory, and will help determine what type of interface is generated for the node.

            "numberOfLimbs": {
                "description": "The number of limbs present in the generated character",
                "type": "int",
                "default": 4,

default

The default property on inputs contains the value of the attribute that will be used when the user has not explicitly set a value or provided an incoming connection to it. For outputs the default value is optional and will only be used when the node compute method cannot be run.

The value type of the default property will be the JSON version of the type of data, shown in Attribute Data Types.

                "type": "int",
                "default": 4,
                "optional": true,

Tip

Although input attributes should all have a default, concrete data types need not have a default set if the intent is for them to have their natural default. It will be assigned to them automatically. e.g. 0 for “int”, [0.0, 0.0, 0.0] for “float[3]”, false for “bool”, and “[]” for any array types.

Warning

Some attribute types, such as “any” and “bundle”, have no well-defined data types and cannot have a default set.

deprecated

The deprecated property is used to indicate that the attribute is being phased out and should no longer be used. The value of the property is a string or array of strings providing users with information on how they should change their graphs to accommodate the eventual removal of the attribute.

        "inputs": {
            "offset": {
                "description": "Value to be added to the result, after 'scale' has been applied.",
                "type": "float",
                "default": 0.0,
                "optional": true,
                "deprecated": "Use 'minValue' instead."
            },
            "scale": {
                "description": "Value to multiply the result by, before 'offset' has been applied.",
                "type": "float",
                "default": 1.0,
                "optional": true,
                "deprecated": [
                    "Use 'maxValue' instead.",
                    "To reproduce the same behavior as before, set 'maxValue' to 'scale' + 'offset'."
                ]
            }

optional

The optional property is used to tell the node whether the attribute’s value needs to be present in order for the compute function to run. If it is set to true then the value is not checked before calling compute. The default value false will not call the compute function if the attribute does not have a valid value.

                "default": 4,
                "optional": true,
                "memoryType": "cpu",

memoryType

By default every attribute in a node will use the memoryType defined at the node level. It’s possible for attributes to override that choice by adding that same keyword in the attribute properties.

Here’s an example of an attribute that overrides the node level memory type to force the attribute onto the CPU. You might do this to keep cheap POD values on the CPU while the expensive data arrays go directly to the GPU.

                "optional": true,
                "memoryType": "cpu",
                "minimum": 2,

minimum/maximum

When specified, these properties represent the minimum and maximum allowable value for the attribute. For arrays the values are applicable to every array element. For tuples the values will themselves be tuples with the same size.

                "memoryType": "cpu",
                "minimum": 2,
                "maximum": 8,
                "metadata": {

Note

These properties are only valid for the numeric attribute types, including tuples and arrays. At present they are not applied at runtime, only for validating test and default values within the .ogn file, however in the future they may be saved so it is always a good idea to specify values here when applicable.

metadata

Attributes can also have key/value style metadata attached to them by adding a dictionary of them using the metadata property. The key and value are any arbitrary string, though it’s a good idea to avoid keywords starting with underscore (_) as they may have special meaning to the graph. Lists of strings can also be used as metadata values, though they will be transformed into a single comma-separated string.

                "maximum": 8,
                "metadata": {
                    "disclaimer": "There is no distinction between 8-limbed spiders and 8-limbed Octopi"
                },
                "uiName": "Number Of Limbs"

There are a number of attribute metadata keys with special meanings:

allowedTokens

Used only for attributes of type token and contains a list of the values that token is allowed to take.

        "inputs": {
            "operator": {
                "type": "token",
                "description": "The mathematical operator to apply",
                "metadata": {
                    "allowedTokens": ["lt", "gt", "ne"]
                }
            }
        }

Sometimes you may wish to have special characters in the list of allowed tokens. The generated code uses the token name for easy access to its values so in these cases you will have to also supply a corresponding safe name for the token value through which the generated code will access it.

        "inputs": {
            "operator": {
                "type": "token",
                "description": "The mathematical operator to apply",
                "metadata": {
                    "allowedTokens": {
                        "lt": "<",
                        "gt": ">",
                        "ne": "!="
                    }
                }
            }
        }

In both cases you would access the token values through the database members db.tokens.lt, db.tokens.gt, and db.tokens.ne.

When you have safe names specified you can also choose to set the default value using the safe name rather than the literal value of the token. You can also specify the allowedTokens metadata at the main keyword level.

        "inputs": {
            "operator": {
                "type": "token",
                "description": "The mathematical operator to apply",
                "default": "<",
                "allowedTokens": {
                    "lt": "<",
                    "gt": ">",
                    "ne": "!="
                }
            },
            "operator2": {
                "type": "token",
                "description": "The mathematical operator to apply, defaulted by name",
                "default": "lt",
                "allowedTokens": {
                    "lt": "<",
                    "gt": ">",
                    "ne": "!="
                }
            }
        }

allowMultiInputs

Used to allow a node attribute to take more than one input. By default it is disabled and dragging a connection to a node input in the action graph will replace the connection. When enabled by setting allowMultiInputs to 1, the connection will be added instead of replaced and the property editor will support adding multiple inputs to the attribute. This is currently only supported on target and bundle types.

        "inputs": {
            "primsBundle": {
                "type": "bundle",
                "description": [
                    "The bundle(s) of multiple prims to be written back."
                ],
                "metadata": {
                    "allowMultiInputs": "1"
                }
            }
        }

hidden

This is a hint to the application that the attribute should be hidden from the user.

        "inputs": {
            "appearanceHint": {
                "description": "Hint to shader writer of how the limbs should be colored.",
                "type": "string",
                "optional": true,
                "metadata": {
                    "hidden": "true"
                }
            }
        }

Less commonly used attributes are often hidden to declutter the UI and the application may provide a mechanism to allow the user to display them on request. For example, in the Create application hidden attributes are not displayed in its Graph windows but do appear in its Property window from where their hidden state can be toggled off and on.

internal

Marks an attribute which is for internal use only. An application would not normally display the attribute to users or allow them to interact with it through its UI.

        "inputs": {
            "debugFlags": {
                "description": "Debugging flags. For internal use only.",
                "type": "int",
                "optional": true,
                "metadata": {
                    "internal": "true"
                }
            }
        }

literalOnly

Indicates that the value of the attribute can only be set to a literal. In other words, if an attribute has literalOnly set to 1, then it cannot be connected to other attributes, so the only way to modify the value of the attribute is to set its value to a literal. A typical use case is the input attributes of event source nodes. Since the action evaluator does not evaluate nodes upstream of an event source node, the input attributes of event source nodes should not be allowed to connect to upstream nodes, so they should be declared as literalOnly.

        "inputs": {
            "keyIn": {
                "type": "token",
                "description": "The key to trigger the downstream execution",
                "metadata": {
                    "literalOnly": "1"
                }
            }
        }

outputOnly

Used with an input attribute which can be the source of output connections but should not be the target of input connections. Typically an application will allow input attributes to take their value from an incoming connection or to be set by the user through the UI if they don’t have an incoming connection. The application may also disallow outbound connections. Setting outputOnly to 1 is a hint to the application that it should continue to allow the user to set the attribute’s value through its UI but disallow incoming connections and enable outgoing connections. A typical use for this is with a “constant” node which allows the user to enter a constant value which can then be passed on to other nodes via output connections.

        "inputs": {
            "defaultSpeed": {
                "type": "double",
                "description": [ "Default speed for interpolations. By connecting all 'Interpolate To' nodes to",
                                "this the user can set it in just one place." ],
                "metadata": {
                    "outputOnly": "1"
                }
            }
        }

uiName

uiName is a very common piece of metadata, so as a shortcut it can also be specified as its own keyword at the attribute level. The meaning is the same; associate a piece of metadata with the attribute. This piece of metadata can be used by the UI to present a more human-readable name for the attribute.

                    "disclaimer": "There is no distinction between 8-limbed spiders and 8-limbed Octopi"
                },
                "uiName": "Number Of Limbs"
            }

Tip

Unlike the actual name, the uiName has no formatting or uniqueness requirements. Choose a name that will make its function obvious to a user selecting it from a list. The UI may or may not include the namespace when displaying it so if that distinction is critical, include it in the uiName.

uiType

uiType is used to provide a hint to the property panel as to how the property should bre displayed. When “filePath” is specified, string and token fields will create file browser widgets in the property panel. When “color” is specified, 3- and 4-component tuples will use a color picker widget in the property panel.

                "metadata": {
                    "uiType": "color"
                }

unvalidated

unvalidated is similar to the optional keyword, in that it is used to tag attributes that may not take part in a compute(). The difference is that these attributes will always exists, they just may not have valid data when the compute is invoked. For such attributes the onus is on the node writer to check validity of such attributes if they do end up being used for the compute.

        "inputs": {
            "useInput1": {
                "type": "bool",
                "description": "If true then output the first input"
            },
            "firstInput": {
                "type": "any",
                "description": "First attribute to be checked",
                "unvalidated": true
            },
            "secondInput": {
                "type": "any",
                "description": "Second attribute to be checked",
                "unvalidated": true
            }
        }

Test Definitions

The node generator is also capable of automatically generating some unit tests on the operation of the node’s algorithm through the tests property. This property contains a list of dictionaries, where each entry in the list is a dictionary of either attribute name : attribute value (if a test file is not being utilized) or path to node in test scene : dictionary of attribute values key-value pairs.

The test runs by either setting all of the input attributes to their corresponding values in the dictionary or leveraging a user-specified test scene in which all input connections/values have already been set, executing the node’s compute algorithm, and then comparing the computed values of the outputs against their corresponding values in the dictionary.

Note

When input attributes do not have a value in the tests their default is used. When output attributes do not have a value in the test they are not checked against the computed result.

There are a few methods of specifying test data, each of which revolve around whether or not a test scene is to be used for a given test run. Between each format there are equivalencies in the specifics so you can use the one(s) that makes the most sense for your particular test cases. They can coexist if you have different types of test data.

The first method is using a single dictionary to specify any non-default attribute values (without an external test scene).

        "tests": [
            { "inputs:firstAddend": 1, "inputs:secondAddend" : 2, "outputs:sum": 3 },
            { "inputs:secondAddend": 5, "outputs:sum": 5 },
            {

This example shows test cases to exercise a simple node that adds two integers together. The first test says if the node has inputs 1 and 2 the output should be 3 and the second one says if the node has an input of 5 and a default valued input the output should be 5 (the defaults have been set to 0).

For a more complex text you can specify the data involved in the test by location instead of all in a single dictionary. Here’s a similar example for an 8 dimensional polynomial solver.

            { "inputs:secondAddend": 5, "outputs:sum": 5 },
            {
                "inputs": {
                    "t0": 40320,
                    "t1": -109584,
                    "t2": 118124,
                    "t3": -67284,
                    "t4": 22449,
                    "t5": -4536,
                    "t6": 546,
                    "t7": -36,
                    "t8": 1
                },
                "outputs": { "roots": [1, 2, 3, 4, 5, 6, 7, 8] }
            },
            {

CPU/GPU Data Switch

There is one special piece of test configuration that applies when you have output data that switches at runtime between CPU and GPU (usually through some sort of input information, such as a boolean switch, or a limit on data size). This information tells the test generator that an output should be expected on the GPU instead of the default CPU location.

            {
                "inputs:isGpu": true,
                "inputs:points": [[1.0, 2.0, 3.0], [2.0, 3.0, 4.0]],
                "inputs:multiplier": [2.0, 3.0, 4.0],
                "outputs:points": [[2.0, 6.0, 12.0], [4.0, 9.0, 16.0]],
                "gpu": ["outputs:points"]
            },

This example illustrates testing of an output on GPU, where the GPU location is selected at runtime based on the attribute inputs:isGpu.

Extended Type Test Data

If your node has extended-type attributes, you will have to specify which type you want to use for your test. This is necessary to distinguish between types which aren’t support by json. For example double, float and half.

            {
                "inputs:multiplier": 2.0,
                "inputs:element": {"type": "float", "value": 6.0},
                "outputs:tuple": {"type:": "float[3]", "value": [12.0, 12.0, 12.0]}
            }

This example has one input “multiplier” which is a specific decimal type, input “element” and output “tuple” which are extended-types. “element” and “tuple” are related by the logic of the node in that their base types must match, so the test is specifying the same type for those attributes.

For tests that deal with extended data types you must also specify the data type with the value to be set so that it can properly resolve. (The other way of resolving data types is by connection. These simple tests only involve a single node so resolution can only be done by setting values directly.)

        "tests": [
            { "inputs:firstAddend": {"int": 1}, "inputs:secondAddend" : {"int": 2}, "outputs:sum": 3 },
            { "inputs:firstAddend": {"float": 1.5}, "inputs:secondAddend" : {"float": 2.5}, "outputs:sum": 4.0 }
        ]

State Test Data

In the special case of tests that need to exercise state data extra syntax is added to differentiate between values that should be set on state attributes before the test starts and values that are checked on them after the test is completed. The special suffix _set is appended to the state namespace to signify that a value is to be initialized before a test runs. You may optionally add the suffix _get to the state namespace to clarify which values are to be checked after the test runs but that is the default so it is not necessary.

        "tests": [
            { "state_set:counter": 9, "state_get:counter": 10 },
            { "state_set:counter": 5, "state:counter": 6 },
            {
                "state_set": {
                    "counter": 9
                },
                "state_get": {
                    "counter": 10
                }
            },
            {
                "state_set": {
                    "counter": 5
                },
                "state": {
                    "counter": 6
                }
            }
        ]

Test Graph Setup

For more complex situations you may need more than just a single node to test code paths properly. For these situations there is a pre-test setup section you can add, in the form of the Controller.edit function parameters. Only the creation directives are accepted, not the destructive ones such as disconnections.

These creation directives are all executed by the test script before it starts to run, providing you with a known starting graph configuration consisting of any nodes, prims, and connections needed to run the test.

            "tests": [
                {
                    "inputs": {
                        "firstAddend": 1
                    },
                    "outputs": {
                        "sum": 3
                    },
                    "setup": {
                        "create_nodes": [
                            ["TestNode", "omni.graph.examples.Node"]
                        ],
                        "create_prims": [
                            [["Prim1", {"attrInt": ["int", 2]}]]
                        ],
                        "connect": [
                            ["Prim1", "attrInt", "TestNode", "inputs:secondAddend"]
                        ]
                    }
                },
                {
                    "inputs:firstAddend": 10, "outputs:sum": 12
                }
            ]

Note

If you define the graph in this way then the first node in your “nodes” directives must refer to the node being tested. If there is no graph specified then a single node of the type being tested will be the sole contents of the stage when running the test.

External Test File

Some nodes may require large amounts of data and/or graph set-up before they can be properly tested, which can be time- consuming to set up via the test construct code alone. For such situations it can be more efficient to simply load an external file (.usd or .usda format) that already describes the entire test scene.

            "tests": [
                {
                    "file": "TestFileForMyNode.usda",
                    "/World/MyGraph/MyNode": {
                        "outputs": {
                            "outputAttr0": "ExpectedValue0",
                            "outputAttr1": {"type": "token", "value": "ExpectedValue1"}
                        },
                        "outputs:outputAttr2": "ExpectedValue2",
                        "state": {
                            "stateAttr0": "ExpectedState0",
                            "stateAttr1": "ExpectedState1"
                        },
                        "state:stateAttr2": "ExpectedState2"
                    },
                    "/World/MyGraph/AnotherNodeInTheSameTestScene.outputs": {
                        "result": 5,
                        "intermediateResult": {"type": "float", "value": 2.5}
                    },
                    "/World/MyGraph/AnotherNodeInTheSameTestScene.state": {
                        "intermediateState": 1.5
                    },
                    "/World/MyGraph/MyNode.outputs:outputAttr3": "ExpectedValue2",
                    "/World/MyGraph/MyNode.outputs:outputAttr4": {"type": "token", "value": "ExpectedValue3"}
                }
            ]

The test file locations can be specified either as an absolute path or as a path relative to the .ogn file. Note that the formatting in this test is different from the previous examples; because the test file in question may contain many different nodes whose output conditions we would like to check after evaluation, we need to add an extra layer of wrapping to relate the nodes being tested to the attributes that need to be checked. More specifically, there are four general allowed key-value patterns for creating per-node test constructs when utilizing an external test file:

  1. The key is the path to the node in the test scene, and the value is a dictionary of dictionaries with attribute namespace : attribute name : expected value key-value pairs containing all attributes that we wish to test for the given node.

                "tests": [
                    {
                        "file": "TestFileForMyNode.usda",
                        "/World/MyGraph/MyNode": {
                            "outputs": {
                                "outputAttr0": "ExpectedValue0",
                                "outputAttr1": {"type": "token", "value": "ExpectedValue1"}
                            },
                            "outputs:outputAttr2": "ExpectedValue2",
                            "state": {
                                "stateAttr0": "ExpectedState0",
                                "stateAttr1": "ExpectedState1"
                            },
                            "state:stateAttr2": "ExpectedState2"
                        },
                        "/World/MyGraph/AnotherNodeInTheSameTestScene.outputs": {
                            "result": 5,
                            "intermediateResult": {"type": "float", "value": 2.5}
                        },
                        "/World/MyGraph/AnotherNodeInTheSameTestScene.state": {
                            "intermediateState": 1.5
                        },
                        "/World/MyGraph/MyNode.outputs:outputAttr3": "ExpectedValue2",
                        "/World/MyGraph/MyNode.outputs:outputAttr4": {"type": "token", "value": "ExpectedValue3"}
                    }
                ]
    
  2. The key is the path to the node in the test scene, and the value is the short-hand form of the attributes (attribute namespace:attribute name : expected value) requiring a post-compute check:

                "tests": [
                    {
                        "file": "TestFileForMyNode.usda",
                        "/World/MyGraph/MyNode": {
                            "outputs": {
                                "outputAttr0": "ExpectedValue0",
                                "outputAttr1": {"type": "token", "value": "ExpectedValue1"}
                            },
                            "outputs:outputAttr2": "ExpectedValue2",
                            "state": {
                                "stateAttr0": "ExpectedState0",
                                "stateAttr1": "ExpectedState1"
                            },
                            "state:stateAttr2": "ExpectedState2"
                        },
                        "/World/MyGraph/AnotherNodeInTheSameTestScene.outputs": {
                            "result": 5,
                            "intermediateResult": {"type": "float", "value": 2.5}
                        },
                        "/World/MyGraph/AnotherNodeInTheSameTestScene.state": {
                            "intermediateState": 1.5
                        },
                        "/World/MyGraph/MyNode.outputs:outputAttr3": "ExpectedValue2",
                        "/World/MyGraph/MyNode.outputs:outputAttr4": {"type": "token", "value": "ExpectedValue3"}
                    }
                ]
    
  3. The key is a combination of the path to the node in the test scene and attribute namespace, while the value is a key-value pair of attribute name : expected value for all attributes in the namespace that require verification:

                "tests": [
                    {
                        "file": "TestFileForMyNode.usda",
                        "/World/MyGraph/MyNode": {
                            "outputs": {
                                "outputAttr0": "ExpectedValue0",
                                "outputAttr1": {"type": "token", "value": "ExpectedValue1"}
                            },
                            "outputs:outputAttr2": "ExpectedValue2",
                            "state": {
                                "stateAttr0": "ExpectedState0",
                                "stateAttr1": "ExpectedState1"
                            },
                            "state:stateAttr2": "ExpectedState2"
                        },
                        "/World/MyGraph/AnotherNodeInTheSameTestScene.outputs": {
                            "result": 5,
                            "intermediateResult": {"type": "float", "value": 2.5}
                        },
                        "/World/MyGraph/AnotherNodeInTheSameTestScene.state": {
                            "intermediateState": 1.5
                        },
                        "/World/MyGraph/MyNode.outputs:outputAttr3": "ExpectedValue2",
                        "/World/MyGraph/MyNode.outputs:outputAttr4": {"type": "token", "value": "ExpectedValue3"}
                    }
                ]
    
  4. The key is a combination of the path to the node in the test scene, attribute namespace, and attribute name that we want to test, while the value is the expected attribute value after graph evaluation(s).

                "tests": [
                    {
                        "file": "TestFileForMyNode.usda",
                        "/World/MyGraph/MyNode": {
                            "outputs": {
                                "outputAttr0": "ExpectedValue0",
                                "outputAttr1": {"type": "token", "value": "ExpectedValue1"}
                            },
                            "outputs:outputAttr2": "ExpectedValue2",
                            "state": {
                                "stateAttr0": "ExpectedState0",
                                "stateAttr1": "ExpectedState1"
                            },
                            "state:stateAttr2": "ExpectedState2"
                        },
                        "/World/MyGraph/AnotherNodeInTheSameTestScene.outputs": {
                            "result": 5,
                            "intermediateResult": {"type": "float", "value": 2.5}
                        },
                        "/World/MyGraph/AnotherNodeInTheSameTestScene.state": {
                            "intermediateState": 1.5
                        },
                        "/World/MyGraph/MyNode.outputs:outputAttr3": "ExpectedValue2",
                        "/World/MyGraph/MyNode.outputs:outputAttr4": {"type": "token", "value": "ExpectedValue3"}
                    }
                ]
    

A few important aspects to point out for this specific test configuration include:

  • Tests that use an outside scene are allowed to ask for attribute checks to be made for nodes that are not defined by the .ogn file that they reside in (as long as they actually exist in the scene). This is to allow for further flexibility when designing node unit tests, since there can be situations where it is more convenient to check that another node in the graph where the native .ogn-defined node also resides in produces a correct set of outputs rather than explicitly performing similar checks against the native .ogn-defined node.

  • The setting of inputs and state attributes on nodes prior to the test run is not allowed in this context; it is assumed that the test scene in question handles this already. In other words, the only allowed node attribute namespaces are outputs and state (or state_get), and the specified values will serve as the expected values that the test checks against after running the test scene.

  • The setup construct is not allowed in tests that use an external scene, since it is expected that the file already handles all pre-test configurations. Inserting tests that do utilize a setup dictionary before and after a test with an external file is allowed, however; the two constructs are orthogonal and will not affect one another in any fashion. This is demonstrated in the following listing, where the first and third test use a setup dictionary to create their test scenes, while the second test leverages an outside file, all without issue:

                "tests": [
                    {
                        "inputs": {
                            "firstAddend": 1
                        },
                        "outputs": {
                            "sum": 3
                        },
                        "setup": {
                            "create_nodes": [
                                ["TestNode", "omni.graph.examples.Node"]
                            ],
                            "create_prims": [
                                [["Prim1", {"attrInt": ["int", 2]}]]
                            ],
                            "connect": [
                                ["Prim1", "attrInt", "TestNode", "inputs:secondAddend"]
                            ]
                        }
                    },
                    {
                        "file": "../../relative/path/to/TestFileName.usda",
                        "/World/PushGraph/TestNode": {
                            "outputs:sum": 5
                        },
                        "/World/PushGraph/intToString.outputs:outString": "5"
                    },
                    {
                        "file": "C:/absolute/path/to/AnotherTestFileName.usda",
                        "/World/PushGraph/TestNode": {
                            "outputs:sum": 7
                        },
                        "/World/PushGraph/intToString.outputs:outString": "3"
                    },
                    {
                        "inputs:firstAddend": 10, "outputs:sum": 12
                    }
                ]
    
  • All node attribute test specifications remain compatible with extended attribute types.

Simplified Schema

This schema outlines the relationships between all of the values in the .ogn file. It is simplified in that it does not include schema information that validates data types in default, minimum, maximum, and test section fields. (It’s possible to include such information in a schema, it just makes it very difficult to follow.)

  1{
  2    "$schema": "http://json-schema.org/schema#",
  3    "type": "object",
  4    "title": "OmniGraph Compute Node Interface Description",
  5    "description": "Contains a description of the interfaces available on one or more OmniGraph Compute Nodes.",
  6
  7    "$comments": "If any dictionary keyword begins with a '$' it will be treated as a comment",
  8
  9    "definitions": {
 10        "commentType": {
 11            "description": "Pattern to allow $comment to be used with any data to annotate the file",
 12            "type": ["array", "boolean", "integer", "number", "object", "string"]
 13        },
 14
 15        "languageType": {
 16            "description": "Values recognized as valid node language types",
 17            "pattern": "^(cpp|c\\+\\+|cu|cuda|py|python)$"
 18        },
 19
 20        "tags": {
 21            "description": "Single value or list of value to use as tags on the node",
 22            "type": ["array", "string"]
 23        },
 24
 25        "schedulingHints": {
 26            "description": "Values recognized as valid scheduling hints",
 27            "pattern": "^(global|global-read|global-write|static|static-read|static-write|threadsafe|topology|topology-read|topology-write|usd|usd-read|usd-write|compute-default|compute-on-request|pure)$"
 28        },
 29
 30        "color": {
 31            "description": "RGBA Color specification",
 32            "oneOf": [
 33                { "pattern": "^#[0-9a-fA-F]{8}$" },
 34                { "type": "array", "items": { "type": "integer", "minItems": 4, "maxItems": 4 } }
 35            ]
 36        },
 37
 38        "iconDictionary": {
 39            "description": "Long form specifying icon properties by keyword",
 40            "type": "object",
 41            "properties": {
 42                "path": { "type": "string" },
 43                "color": { "$ref": "#/definitions/color" },
 44                "backgroundColor": { "$ref": "#/definitions/color" },
 45                "borderColor": { "$ref": "#/definitions/color" }
 46            }
 47        },
 48        "icon": {
 49            "description": "Single string path or dictionary of detailed information to override icon appearance",
 50            "oneOf": [
 51                { "type" : "string" },
 52                { "$ref": "#/definitions/iconDictionary" }
 53            ]
 54        },
 55
 56        "attributeValue": {
 57            "description": "Simplified match for attribute values specified in the file (no type validation)",
 58            "type": ["array", "boolean", "integer", "number", "string"]
 59        },
 60
 61        "attributeValueType": {
 62            "description": "Types of data recognized as valid attribute data in JSON form",
 63            "type": ["array", "boolean", "integer", "number", "string"]
 64        },
 65
 66        "typedAttributeValue": {
 67            "description": "An attribute value that includes type information",
 68            "type": "object",
 69            "properties": {
 70                "type": { "$ref": "#/definitions/attributePattern" },
 71                "value": { "$ref": "#/definitions/attributeValueType" }
 72            }
 73        },
 74
 75        "maybeTypedAttributeValue": {
 76            "description": "An attribute value that may be typed",
 77            "oneOf": [
 78                { "$ref": "#/definitions/attributeValueType" },
 79                { "$ref": "#/definitions/typedAttributeValue" }
 80            ]
 81        },
 82
 83        "$subtypes": {"what":"Patterns for matching the attribute type declaration"},
 84        "simpleType": {
 85            "description": "Matches an attribute type pattern with no component counts or arrays",
 86            "pattern": "^[^\\[\\]]+$"
 87        },
 88        "componentType": {
 89            "description": "Matches an attribute type pattern with a simple value with a component count",
 90            "pattern": "^[^\\[\\]]+\\[[0-9]{1,3}\\]$"
 91        },
 92        "arrayType": {
 93            "description": "Matches an attribute type pattern for an array of simple values",
 94            "pattern": "^[^\\[\\]]+\\[\\]$"
 95        },
 96        "arrayOfArraysType": {
 97            "description": "Matches an attribute type pattern for an array of arrays of simple values",
 98            "pattern": "^[^\\[\\]]+\\[\\]\\[\\]$"
 99        },
100        "componentArrayType": {
101            "description": "Matches an attribute type pattern for an array of components",
102            "pattern": "^[^\\[\\]]+\\[[0-9]{1,3}\\]\\[\\]$"
103        },
104        "componentArrayOfArraysType": {
105            "description": "Matches an attribute type pattern for an array of arrays of components",
106            "pattern": "^[^\\[\\]]+\\[[0-9]{1,3}\\]\\[\\]\\[\\]$"
107        },
108        "attributeTypeName": {
109            "description": "Simple attribute type name portion of the pattern",
110            "pattern": "^(any|bool|bundle|double|execution|float|half|int|int64|path|string|target|timecode|token|uchar|uint|uint64)"
111        },
112        "attributeTypeNameWithRoles": {
113            "description": "Simple attribute type name portion of the pattern including role-based attributes",
114            "pattern": "^(any|bool|bundle|double|execution|float|half|int|int64|objectId|string|target|timecode|token|uchar|uint|uint64|(color|normal|point|quat|path|texcoord|vector|(d|f|h))|matrixd|frame|transform)"
115        },
116        "numericAttributeTypeName": {
117            "description": "Numeric attribute types supporting min/max values",
118            "pattern": "^(double|float|half|int|int64|timecode|uchar|uint|uint64)"
119        },
120        "numericAttributeTypeNameWithRoles": {
121            "description": "Numeric and role-based attribute types supporting min/max values",
122            "pattern": "^(double|float|half|int|int64|timecode|uchar|uint|uint64|(color|normal|point|quat|texcoord|vector|(d|f|h))|(matrix(d|f))|frame|transform)"
123        },
124
125        "$allTypes": {"what":"Patterns for matching all valid attribute types and their component or array extensions"},
126        "attributePatternSimple": {
127            "description": "Simple attribute types, no components or arrays",
128            "allOf": [
129                { "$ref": "#/definitions/attributeTypeName" },
130                { "$ref": "#/definitions/simpleType" }
131            ]
132        },
133        "attributePatternComponent": {
134            "description": "Simple attribute types with a non-zero component count, no arrays",
135            "allOf": [
136                { "$ref": "#/definitions/attributeTypeNameWithRoles" },
137                { "$ref": "#/definitions/componentType" }
138            ]
139        },
140        "attributePatternArray": {
141            "description": "Array attribute types with no components",
142            "allOf": [
143                { "$ref": "#/definitions/attributeTypeName" },
144                { "$ref": "#/definitions/arrayType" }
145            ]
146        },
147        "attributePatternArrayOfArrays": {
148            "description": "Array of arrays of attribute types with no components",
149            "allOf": [
150                { "$ref": "#/definitions/attributeTypeName" },
151                { "$ref": "#/definitions/arrayOfArraysType" }
152            ]
153        },
154        "attributePatternComponentArray": {
155            "description": "Array attribute types with a non-zero component count",
156            "allOf": [
157                { "$ref": "#/definitions/attributeTypeNameWithRoles" },
158                { "$ref": "#/definitions/componentArrayType" }
159            ]
160        },
161        "attributePatternComponentArrayOfArrays": {
162            "description": "Array of arrays of attribute types with a non-zero component count",
163            "allOf": [
164                { "$ref": "#/definitions/attributeTypeNameWithRoles" },
165                { "$ref": "#/definitions/componentArrayOfArraysType" }
166            ]
167        },
168        "attributePattern": {
169            "description": "Match all of the simple types, plus an optional component count, and optional array type",
170            "oneOf": [
171                { "$ref": "#/definitions/attributePatternSimple" },
172                { "$ref": "#/definitions/attributePatternComponent" },
173                { "$ref": "#/definitions/attributePatternArray" },
174                { "$ref": "#/definitions/attributePatternArrayOfArrays" },
175                { "$ref": "#/definitions/attributePatternComponentArray" },
176                { "$ref": "#/definitions/attributePatternComponentArrayOfArrays" }
177            ]
178        },
179
180
181
182        "$numericTypes": {"what":"Patterns for recognizing the numeric types, for special handling"},
183        "numericAttributePatternSimple": {
184            "description": "Numeric attribute types, no components or arrays",
185            "allOf": [
186                { "$ref": "#/definitions/numericAttributeTypeName" },
187                { "$ref": "#/definitions/simpleType" }
188            ]
189        },
190        "numericAttributePatternComponent": {
191            "description": "Numeric attribute types with a non-zero component count, no arrays",
192            "allOf": [
193                { "$ref": "#/definitions/numericAttributeTypeNameWithRoles" },
194                { "$ref": "#/definitions/componentType" }
195            ]
196        },
197        "numericAttributePatternArray": {
198            "description": "Array of numeric attribute types with no components",
199            "allOf": [
200                { "$ref": "#/definitions/numericAttributeTypeName" },
201                { "$ref": "#/definitions/arrayType" }
202            ]
203        },
204        "numericAttributePatternArrayOfArrays": {
205            "description": "Array of arrays of numeric attribute types with no components",
206            "allOf": [
207                { "$ref": "#/definitions/numericAttributeTypeName" },
208                { "$ref": "#/definitions/arrayOfArraysType" }
209            ]
210        },
211        "numericAttributePatternComponentArray": {
212            "description": "Array of numeric attribute types with a non-zero component count",
213            "allOf": [
214                { "$ref": "#/definitions/numericAttributeTypeNameWithRoles" },
215                { "$ref": "#/definitions/componentArrayType" }
216            ]
217        },
218        "numericAttributePatternComponentArrayOfArrays": {
219            "description": "Array of arrays of numeric attribute types with a non-zero component count",
220            "allOf": [
221                { "$ref": "#/definitions/numericAttributeTypeNameWithRoles" },
222                { "$ref": "#/definitions/componentArrayOfArraysType" }
223            ]
224        },
225        "numericAttributePattern": {
226            "description": "Match all of the numeric types, plus an optional component count, and optional array type",
227            "oneOf": [
228                { "$ref": "#/definitions/numericAttributePatternSimple" },
229                { "$ref": "#/definitions/numericAttributePatternComponent" },
230                { "$ref": "#/definitions/numericAttributePatternArray" },
231                { "$ref": "#/definitions/numericAttributePatternArrayOfArrays" },
232                { "$ref": "#/definitions/numericAttributePatternComponentArray" },
233                { "$ref": "#/definitions/numericAttributePatternComponentArrayOfArrays" }
234            ]
235        },
236
237        "memoryTypeName": {
238          "description": "Node or attribute-level specification of the memory location",
239          "type": "string",
240          "pattern": "^(cpu|cuda|any)$"
241        },
242
243        "metadata": {
244          "description": "Key/Value pairs to be stored with the node or attribute type definition",
245          "type": "object",
246          "additionalProperties": false,
247          "patternProperties": {
248              "^\\$": { "$ref": "#/definitions/commentType" },
249              "^[^\\$].*": { "type" : "string" }
250            }
251        },
252
253        "attribute": {
254            "type": "object",
255            "description": "A single attribute on a node",
256            "required": ["description", "type"],
257            "properties": {
258                "description": { "type": ["string", "array"], "items": { "type": "string" } },
259                "array": { "type": "boolean", "default": false },
260                "optional": { "type": "boolean", "default": false },
261                "unvalidated": { "type": "boolean", "default": false },
262                "memoryType": { "$ref": "#/definitions/memoryTypeName" },
263                "metadata": { "$ref": "#/definitions/metadata" },
264                "type": { "$ref": "#/definitions/attributePattern" },
265                "uiName": { "type": "string" }
266            },
267            "patternProperties": {
268                "^\\$": { "$ref": "#/definitions/commentType" }
269            },
270
271            "$extraProperties": "Collection of properties that are conditional on attribute types or other values",
272            "allOf": [
273                {
274                    "$attributeDefaults": "Define attribute default types, where omission is okay if attribute is optional or an output",
275                    "if" : {
276                        "required": ["default"]
277                    },
278                    "then": {
279                        "$comment": "If the default is provided then validate its type",
280                        "$ref": "#/definitions/attributeValue"
281                    }
282                },
283                {
284                    "description": "A minimum value is only supported on certain attribute types",
285                    "if": {
286                        "required": ["minimum"]
287                    },
288                    "then": {
289                        "$comment": "If the minimum is provided then validate its type",
290                        "$ref": "#/definitions/attributeValue"
291                    }
292                },
293                {
294                    "description": "A maximum value is only supported on certain attribute types",
295                    "if": {
296                        "required": ["maximum"]
297                    },
298                    "then": {
299                        "$comment": "If the maximum is provided then validate its type",
300                        "$ref": "#/definitions/attributeValue"
301                    }
302                }
303            ]
304        },
305        "attributes": {
306            "type": "object",
307            "default": {},
308            "description": "A subset of attributes on a node",
309            "$comment": "Attribute names are alphanumeric with underscores, not starting with a number, allowing periods and colons as separators",
310            "additionalProperties": false,
311            "patternProperties": {
312                "^[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/attribute" },
313                "^\\$" : { "$ref": "#/definitions/commentType" }
314            }
315        },
316        "tests": {
317            "description": "Tests consist of a list of objects containing either direct values for input and output attributes, or a file path to an external test scene followed by a list of objects containing the expected output and state values to check for a user-specified node (as long as it exists in the test scene)",
318            "type": "array",
319            "items": {
320                "description": "Attribute values can either be namespaces as inputs:X/outputs:X/state:X or in objects with keys inputs/outputs/state. When a file string is specified, attribute values can be correlated to a path to a node in the scene concatenated with the attribute as nodePath.outputs:X/nodePath.state:X, objects with keys nodePath.outputs whose values are the attribute name + value, and/or objects with keys nodePath whose values are objects with keys outputs/state (similar to the previous case without test files)",
321                "type": "object",
322                "patternProperties": {
323                    "description": { "$ref": "#/definitions/commentType" },
324                    "^inputs$": {
325                        "type": "object",
326                        "description": "Alternative way of specifying inputs without namespacing; not compatible with file strings located in the same test",
327                        "patternProperties": {
328                            "^[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" }
329                        }
330                    },
331                    "^outputs$": {
332                        "type": "object",
333                        "description": "Alternative way of specifying outputs without namespacing",
334                        "patternProperties": {
335                        "^[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" }
336                        }
337                    },
338                    "^state$": {
339                        "type": "object",
340                        "description": "Alternative way of specifying state without namespacing",
341                        "patternProperties": {
342                            "^[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" }
343                        }
344                    },
345                    "^setup$": {
346                        "type": "object",
347                        "description": "Detailed graph setup prior to a test; not compatible with file strings located in the same test",
348                        "properties": {
349                            "nodes": { "type": "array" },
350                            "connections": { "type": "array" },
351                            "prims": { "type": "array" },
352                            "values": { "type": "object" }
353                        }
354                    },
355                    "^inputs:[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" },
356                    "^outputs:[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" },
357                    "^state:[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" },
358                    "^\\$" : { "$ref": "#/definitions/commentType" },
359                    "^file$": {
360                        "type": "string",
361                        "description": "Name of the (optional) test file to use; not compatible with inputs and/or setup objects in the same test"
362                    },
363                    "^[A-Za-z_][A-Za-z0-9_\\\/._]$": {
364                        "type": "object",
365                        "description": "Path to an arbitrary node in the test scene specified by the file name. Only allowed if the file property is defined for the given test",
366                        "patternProperties": {
367                            "^outputs$": { "$ref": "#/definitions/tests/items/patternProperties/^outputs$" },
368                            "^state$": { "$ref": "#/definitions/tests/items/patternProperties/^state$" },
369                            "^outputs:[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" },
370                            "^state:[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" }
371                        }
372                    },
373                    "^[A-Za-z_][A-Za-z0-9_\\\/._].outputs$": {
374                        "type": "object",
375                        "description": "Path to an arbitrary node in the test scene concatenated with the \"outputs\" attribute namespace. Only allowed if the file property is defined for the given test",
376                        "patternProperties": {
377                            "^[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" }
378                        }
379                    },
380                    "^[A-Za-z_][A-Za-z0-9_\\\/._].state$": {
381                        "type": "object",
382                        "description": "Path to an arbitrary node in the test scene concatenated with the \"state\" attribute namespace. Only allowed if the file property is defined for the given test",
383                        "patternProperties": {
384                            "^[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" }
385                        }
386                    },
387                    "^[A-Za-z_][A-Za-z0-9_\\\/._].outputs:[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" },
388                    "^[A-Za-z_][A-Za-z0-9_\\\/._].state:[A-Za-z_][A-Za-z0-9_.:]*$": { "$ref": "#/definitions/maybeTypedAttributeValue" }
389                }
390            }
391        },
392        "node": {
393            "type": "object",
394            "description": "Referenced schema for describing an OmniGraph Compute Node",
395            "required": ["description"],
396            "additionalProperties": false,
397            "patternProperties": {
398                "categories": { "$ref": "#/definitions/categoriesName" },
399                "categoryType": { "$ref": "#/definitions/categoryTypeName" },
400                "description": { "type": ["string", "array"], "items": { "type": "string" } },
401                "exclude": { "type": ["string", "array"], "items": { "type": "string" } },
402                "language": { "$ref": "#/definitions/languageType" },
403                "inputs": { "$ref": "#/definitions/attributes" },
404                "outputs": { "$ref": "#/definitions/attributes" },
405                "state": { "$ref": "#/definitions/attributes" },
406                "tests": { "$ref": "#/definitions/tests" },
407                "version": { "type": "integer", "default": 1 },
408                "metadata": { "$ref": "#/definitions/metadata" },
409                "memoryType": { "$ref": "#/definitions/memoryTypeName" },
410                "scheduling": {
411                    "oneOf": [
412                        { "type" : "string" },
413                        { "$ref": "#/definitions/schedulingHints" }
414                    ]
415                },
416                "uiName": { "type": "string" },
417                "icon": { "$ref": "#/definitions/icon" },
418                "tags": { "$ref": "#/definitions/tags" },
419                "^\\$" : { "$ref": "#/definitions/commentType" }
420            }
421        }
422    },
423
424    "$limitation": "A valid file must have at least one node interface definition",
425    "minProperties": 1,
426
427    "additionalProperties": false,
428    "patternProperties": {
429        "^[A-Za-z_][A-Za-z0-9_]*$": { "$ref": "#/definitions/node" },
430        "^\\$" : { "$ref": "#/definitions/commentType" }
431    }
432}