Change Values and Rewire#

Goal. Retune or rewire a graph without reloading the scene.

Before you start. Types and Values, Chain Nodes.

The Shape#

A value-input is addressable on the Shader prim by its USD name. Write it, publish the edit by advancing the write floor, then step:

main.py, from the runnable blur example#
def set_radius(stage, ordinal: int, radius: int) -> None:
    """Write inputs:radius on both blur nodes and publish the edit.

    The attribute is connected to nothing, so the value on the prim is the value
    the node reads. Each node is written separately because each carries its own
    copy of the attribute.
    """
    value = np.array([radius], dtype=np.int32)
    tensor = ovstage.make_dltensor(
        value, dtype=ovstage.numpy_to_dldatatype(value.dtype, lanes=1), shape=[1], ndim=1
    )
    with ovstage.PathDictionary(stage) as paths:
        attribute = paths.intern_token("inputs:radius")
        for node in BLUR_NODES:
            path_list = paths.create_path_list_from_strings([node])
            with stage.query_from_path_list(path_list) as query:
                stage.write_attribute(
                    query, attribute, ordinal=ordinal, tensors=tensor, is_array=False
                ).wait()
            paths.destroy_path_list(path_list)
    stage.advance_write_floor(ordinal, ovstage.Scope.ALL).wait()

How It Works#

An unconnected input takes effect on the next step. The blur example changes its radius this way and renders again:

main.py, from the runnable blur example#
    # inputs:radius is unconnected, so a write takes effect on the next step
    # with no reset. At 0 the weight table is {1.0} and the blur is the
    # identity, so the published AOV matches the frame's LdrColor exactly.
    ordinal += 1
    products = render_at_radius(stage, renderer, ordinal, 0)
    frame = products[RENDER_PRODUCT].frames[0]
    save_render_var(frame, LDR_COLOR_PATH, OUTPUT_DIR / "radius-0-input.png")
    save_render_var(frame, BLURRED_PATH, OUTPUT_DIR / "radius-0.png")
    del frame, products

A connected input ignores a direct write. If inputs:X is connected to another prim, the connection is the source of the value, so writing inputs:X succeeds and changes nothing. Write the source attribute instead; the node reads it on the next step.

Each node carries its own copy. Two nodes referencing the same shader definition each have their own inputs:X, so retuning both means writing both.

A changed value can cost more than the value. Anything the launch script derives from it is derived again, and a cached computation keyed on it runs again. This is how a new radius reaches a new weight table.

Rewiring changes what runs. Execution order comes from connections, so changing a connection can make a node that was idle start running, or stop one that was running. Refer to Order comes from connections.

Outputs can be connected while the scene runs. Connecting a RenderVar to an existing node’s output rebuilds that node’s connected component and publishes the output on the next step. Adding or removing a binding or shader input also rebuilds only the affected component; writing an existing input’s value does not rebuild it.

Verify It Worked#

Choose a value with an exact effect and check it byte for byte, before and after the change, against the same input AOV. A picture that changes but not in the way the value predicts means the write landed somewhere other than the input you meant.

The blur example sets the radius to zero, which makes the node the identity, and saves that frame’s input and output, which match pixel for pixel. A write that missed leaves the previous radius in place, and the output differs from the input.

When It Goes Wrong#

  • The write reported success and nothing changed: the input is connected, so the connection is winning. Write the source attribute instead.

  • The attribute would not bind: ovrtx’s default binding mode skips attributes it cannot find rather than reporting them, so a misspelled name is silent. Bind with the mode that requires the attribute to exist while you are debugging.

  • A node stopped running after a rewire: it now feeds nothing that is requested. Refer to Nothing Appears.

  • A node stopped by a Lua sandbox limit stays stopped with the original error: change one of its value-inputs to retry without rebuilding the graph. Writing an equal value or editing another node does not clear the stop.