Writing Attributes
When to Use
Use this skill when the user asks to write an attribute, set a property, change a material, set a color, or modify mesh data.
Inputs
Resolve inputs in this order: existing repository files and referenced snippets, explicit user request, then broader agent context.
- Target API surface: Python, C/C++, or both.
- Prim paths, attribute name, USD value type, scalar versus array shape, and desired values.
- Data source location, dtype, semantic conversion, ordinal, and write-floor publication.
- Whether the write is one-shot, repeated with a stable target, or needs direct mapped-buffer access.
- Repository source snippets referenced below. Treat these snippets as the API source of truth.
Prerequisites
- Use an ovrtx checkout that contains the referenced examples and docs tests.
- Read the relevant
> **Source:** snippet before writing or explaining API usage.
- Use
attribute-bindings for repeated writes to the same prims/attribute, and mapping-attributes for zero-copy direct writes.
- Use
binding-materials when the request is specifically to set material:binding.
Instructions
- Identify the target language, prim paths, attribute name, value kind, data shape, memory target, and sync/async requirement.
- Read the matching source snippet and copy its lifecycle pattern rather than inventing equivalent calls.
- Validate dtype, shape, semantic, and ownership rules before proposing or editing code.
- Wait for the ovstage write, then advance the write floor before rendering its ordinal; preserve C compatibility lifetimes when maintaining C code.
- When changing code, run the narrow docs test or example that owns the snippet whenever practical.
Output Format
- For explanations, cite the relevant API names, source snippets, and caveats.
- For code changes, summarize the files changed, snippets affected, and validation run.
Scripts
This skill has no scripts.
Limitations
- Deprecated ovrtx compatibility writes retain their documented unsupported-type limits for string arrays, asset arrays, and timecode attributes.
- For ovstage token and path values, intern IDs through
ovstage.PathDictionary and use the matching AttributeSemantic.
- The referenced snippets remain the source of truth; update or add tested snippets before documenting new API usage.
Overview
Beyond transforms, ovstage can write arbitrary attributes to prims: colors, visibility, mesh geometry, string tokens, and more. The DLTensor dtype must exactly match the USD attribute schema.
There are two categories:
- Scalar attributes -- one value per prim (e.g., a color, a transform)
- Array attributes -- variable-length per prim (e.g., mesh points, face vertex counts)
Tensor layout
Ovstage Python and the C compatibility API use lane-based DLTensor attribute layouts.
Python ovstage — lane-based attributes
Scalar values can use NumPy arrays directly. For vectors and matrices, wrap NumPy storage with ovstage.make_dltensor, set the logical element count in shape, and set the component count in dtype.lanes:
| USD type |
NumPy storage |
ovstage DLTensor |
int / float scalar for N prims |
(N,) |
shape=[N], lanes 1 |
float3 / point3f scalar for N prims |
(N, 3) |
shape=[N], lanes 3 |
float4 / color4f scalar for N prims |
(N, 4) |
shape=[N], lanes 4 |
| 4x4 matrix for N prims |
(N, 4, 4) |
shape=[N], lanes 16 |
int[] array with M elements on one prim |
(M,) |
shape=[M], lanes 1 |
float3[] / point3f[] with M elements on one prim |
(M, 3) |
shape=[M], lanes 3 |
Source: tests/docs/python/test_attribute_shapes.py snippets doc-shape-scalar-int32, doc-shape-float3-array, doc-shape-mat4-array.
C — lane-based attributes
The C API uses DLDataType::lanes for multi-component attribute reads and writes. The shape counts logical attribute elements; the lane count holds the vector or matrix component count:
| USD type |
C DLTensor shape |
C DLDataType |
int / float scalar for N prims |
[N] |
{kDLInt/kDLFloat, bits, 1} |
float3 / point3f scalar for N prims |
[N] |
{kDLFloat, 32, 3} |
| 4x4 double matrix for N prims |
[N] |
{kDLFloat, 64, 16} |
int[] array with M elements on one prim |
[M] |
{kDLInt, 32, 1} |
float3[] / point3f[] with M elements on one prim |
[M] |
{kDLFloat, 32, 3} |
Source: tests/docs/c/test_attribute_shapes.cpp snippets doc-shape-scalar-int32-c, doc-shape-float3-array-c, doc-shape-mat4-array-c.
For a C point3f[] attribute with 10 points, write or read one tensor with shape=[10] and dtype={kDLFloat, 32, 3}. Rendered output/AOV tensors are not attribute tensors; in C they use channel-last shapes such as [height, width, channels] with dtype.lanes=1.
USD Type Lookup
Use this table to answer "how do I write USD type X?" For ovstage, pass is_array=True for array-valued attributes. The C compatibility API sets binding.binding_desc.attribute_type.is_array = true.
| USD type(s) |
Python value to write |
C tensor dtype |
Notes |
bool / bool[] |
np.bool_, shape (N,) or per-prim (M,) |
{kDLBool, 8, 1} |
|
uchar / uchar[] |
np.uint8 |
{kDLUInt, 8, 1} |
|
int, int2, int3, int4 and arrays |
np.int32, trailing shape (), (2,), (3,), (4,) |
{kDLInt, 32, lanes} |
|
uint / uint[] |
np.uint32 |
{kDLUInt, 32, 1} |
|
int64 / int64[] |
np.int64 |
{kDLInt, 64, 1} |
|
uint64 / uint64[] |
np.uint64 |
{kDLUInt, 64, 1} |
|
half, half2, half3, half4 and arrays |
np.float16, trailing shape by component count |
{kDLFloat, 16, lanes} |
|
float, float2, float3, float4 and arrays |
np.float32, trailing shape by component count |
{kDLFloat, 32, lanes} |
Direct runtime writes to scalar float3 work, but authored scalar USD float3 population is bugged in the current runtime. Prefer role-bearing point3f, normal3f, vector3f, or color3f for USD-authored data. |
double, double2, double3, double4 and arrays |
np.float64, trailing shape by component count |
{kDLFloat, 64, lanes} |
|
point3*, normal3*, vector3*, color3*, color4*, texCoord2f and arrays |
numeric dtype by suffix (h/f/d), trailing role dimensions |
same numeric dtype/lanes as storage |
Roles are schema intent; tensor storage is numeric. |
quat* and arrays |
numeric dtype by suffix, shape (N, 4) or (M, 4) |
{kDLFloat, bits, 4} |
Write runtime order (i, j, k, real), not USDA order (real, i, j, k). |
matrix2d, matrix3d, matrix4d, frame4d and arrays |
np.float64, flattened trailing shape (4,), (9,), (16,), (16,) |
{kDLFloat, 64, 4/9/16} |
Generic authored matrix attrs are flattened. Transform semantic writes may use (N, 4, 4) Python arrays or {kDLFloat,64,16} C tensors. |
extent, _worldExtent |
np.float64, shape (N, 6) |
{kDLFloat, 64, 6} |
Usually read-only from population; extent is local-space, _worldExtent is world-space. |
string |
UTF-8 np.uint8 byte rows with is_array=True and AttributeSemantic.STRING |
{kDLUInt, 8, 1} with is_array=true |
One byte row represents one USD string value. |
token / token[] |
np.uint64 IDs from PathDictionary.intern_token() with AttributeSemantic.TOKEN_ID |
{kDLUInt, 64, 1} raw IDs with OVRTX_SEMANTIC_TOKEN_ID, or compatibility string helpers |
Set is_array to match the USD attribute kind. |
asset |
np.uint64 id pairs from PathDictionary.intern_token() with AttributeSemantic.ASSET_PATH_ID |
{kDLUInt, 64, 2} with is_array=false |
One (authored, resolved) pair per prim. ovstage does not resolve assets, so intern both paths yourself. Use token id 0 for the resolved half when the path resolves to nothing. Works for both fresh attributes and USD-populated asset columns (ovstage >= 0.2). On 0.1.x a write of this shape created a second same-named column instead of updating the populated one. |
relationship |
np.uint64 IDs from PathDictionary.intern_path() with is_array=True and AttributeSemantic.RELATIONSHIP_PATH_ID |
path string/path ID semantics |
Use relationship-specific skills for schema-specific behavior. |
timecode / timecode[] |
unsupported |
unsupported |
|
Python
Array attribute write (mesh points)
Source: tests/docs/python/test_attribute_shapes.py snippet doc-shape-float3-array
Array attribute write (same pattern for other array schemas)
Source: tests/docs/python/test_attribute_shapes.py snippet doc-shape-float3-array
Prim modes
Source: tests/docs/python/test_attribute_bindings.py snippet doc-bind-attribute-write
Token array attribute
Source: tests/docs/python/test_attribute_bindings.py snippet doc-write-token-array
Path/relationship array attribute
Source: tests/docs/python/test_base.py snippet doc-bind-material
Authored attribute matrix
Python raw snippets:
| Type/pattern |
Snippet |
bool |
doc-write-usd-bool |
int |
doc-write-usd-int |
float |
doc-write-usd-float |
point3f |
doc-write-usd-point3f |
point3f[] |
doc-write-usd-point3f-array |
normal3f |
doc-write-usd-normal3f |
vector3f |
doc-write-usd-vector3f |
color3f |
doc-write-usd-color3f |
matrix4d |
doc-write-usd-matrix4d |
quatf |
doc-write-usd-quatf |
string |
doc-write-usd-string |
token |
doc-write-usd-token |
token[] |
doc-write-usd-token-array |
The snippets listed in this table live in tests/docs/python/test_all_attributes.py.
They write through a reusable ovstage query, wait for each write, and advance
the write floor before reading the updated value. Use ovstage.PathDictionary
to intern token and relationship strings, then write the resulting IDs with
AttributeSemantic.TOKEN_ID, AttributeSemantic.RELATIONSHIP_PATH_ID or
AttributeSemantic.ASSET_PATH_ID. String payloads use byte rows with
AttributeSemantic.STRING.
C
Generic scalar write in C
Source: tests/docs/c/test_attribute_bindings.cpp snippet doc-write-bound-attribute-c
Supported authored attribute write snippets in C
The snippets below are ovstage-native: they issue ovstage_write_attribute against a DocsQueryAndToken (single-prim query handle + interned attribute token) with an ovstage_write_data_t describing the DLTensor payload, is_array, and OVSTAGE_SEMANTIC_*. Each block advances the write floor and bumps the caller's tracked ordinal so a follow-up latest(ordinal) read observes the write. The ovrtx ovrtx_write_attribute + ovrtx_binding_desc_or_handle_t compatibility shim is still present for standalone-renderer callers but is not used by these snippets — the recommended path is attached ovstage.
C raw snippets:
| Type/pattern |
Snippet |
| scalar numeric |
doc-write-usd-float-c |
| lane-3 scalar |
doc-write-usd-point3f-c |
| lane-3 array |
doc-write-usd-point3f-array-c |
| lane-16 matrix |
doc-write-usd-matrix4d-c |
| quaternion |
doc-write-usd-quatf-c |
| token |
doc-write-usd-token-c |
| token array |
doc-write-usd-token-array-c |
| string bytes |
doc-write-usd-string-c |
| scalar asset id pair |
doc-write-usd-asset-c |
The snippets listed in this table live in tests/docs/c/test_all_attributes.cpp. As of ovstage 0.2, writing ASSET_PATH_ID id pairs to a USD-populated scalar asset attribute is supported and updates the existing column in place. On 0.1.x a write of this shape created a second same-named column of a conflicting type instead. The scalar-asset snippets are runtime-validated by AllAttributesTest.AssetReadWriteSnippets. Intern both paths through the path dictionary before the write, and use token id 0 for the resolved half when the path resolves to nothing.
Key Types / Functions
| Python (ovstage) |
C (ovstage) |
C (deprecated ovrtx compat) |
stage.write_attribute(query, attr, ordinal=..., tensors=..., is_array=...) |
ovstage_write_attribute(stage, query_handle, attr_ref, ordinal, write_data, prim_mode) |
ovrtx_write_attribute(renderer, &binding, &buffer, access) |
stage.advance_write_floor(ordinal, ovstage.Scope.ALL) |
ovstage_advance_write_floor(stage, &desc) with desc.ordinal=N, desc.scope=OVSTAGE_SCOPE_ALL |
(renderer implicitly seals per step) |
Ovstage C write payload (ovstage_write_data_t):
.tensors / .tensor_count — DLTensor array. tensor_count==1 for the packed-uniform layout used by every snippet.
.is_array — sole authority for fixed (false) vs. array (true) storage. Does NOT auto-derive from the DLTensor's shape.
.semantic — ovstage_attribute_semantic_t, tag the write with the same geometric role the column was created under, or OVSTAGE_SEMANTIC_NONE to preserve an existing column's authored role.
Semantics (Python: ovstage.AttributeSemantic; C ovstage: ovstage_attribute_semantic_t; C ovrtx compat: ovrtx_attribute_semantic_t):
AttributeSemantic.NONE / OVSTAGE_SEMANTIC_NONE / OVRTX_SEMANTIC_NONE -- generic data.
AttributeSemantic.MATRIX / OVSTAGE_SEMANTIC_MATRIX / OVRTX_SEMANTIC_XFORM_MAT4x4 -- matrix data.
OVRTX_SEMANTIC_XFORM_POS3d_ROT4f_SCALE3f -- decomposed transform (C ovrtx compat only).
OVRTX_SEMANTIC_XFORM_POS3d_ROT3x3f -- decomposed transform (C ovrtx compat only).
AttributeSemantic.RELATIONSHIP_PATH_ID / OVSTAGE_SEMANTIC_RELATIONSHIP_PATH_ID -- interned relationship path IDs (write is_array=true).
AttributeSemantic.TOKEN_ID / OVSTAGE_SEMANTIC_TOKEN_ID / OVRTX_SEMANTIC_TOKEN_ID -- interned token IDs.
AttributeSemantic.STRING / OVSTAGE_SEMANTIC_STRING -- UTF-8 USD string bytes (write is_array=true, dtype {kDLUInt, 8, 1}).
AttributeSemantic.ASSET_PATH_ID / OVSTAGE_SEMANTIC_ASSET_PATH_ID -- interned (authored, resolved) asset token ids (write is_array=false, dtype {kDLUInt, 64, 2}).
In C, ovstage_write_attribute returns ovstage_enqueue_result_t which contains both .status (check for OVSTAGE_OK) and .op_index (for async tracking via ovstage_wait_op). Every doc-test snippet uses the shared docs_wait_ovstage_no_errors(stage, op_index) helper from tests/docs/c/helpers.h to wait + assert on op errors.
The deprecated ovrtx_write_attribute still returns ovrtx_enqueue_result_t and tracks via ovrtx_wait_op for standalone-mode callers.
Deprecated renderer data access modes (Python: from ovrtx import DataAccess):
DataAccess.SYNC -- copies data during the call, safe to free after return
DataAccess.ASYNC -- data accessed later during stream execution, must keep alive; pass cuda_stream= or cuda_event= for GPU synchronization. Not allowed with string data.
Troubleshooting
- Array attribute dtype must exactly match the USD schema. Using numpy's default
float64 for a float3[] attribute (which expects float32) will cause errors.
- In the current runtime, authored scalar USD
float3 values may be created but populated as zero by populateAllAuthoredAttributes. If a value needs to come from USD, author it as a role-bearing type such as vector3f, point3f, normal3f, or color3f. Direct runtime writes to scalar float3 still work.
- Quaternion tensors use ovrtx runtime lane order
(i, j, k, real). USDA quat* values are authored as (real, i, j, k), so reading quatd, quatf, or quath attributes reorders the components into (i, j, k, real), and writes should use that runtime tensor order.
- Deprecated renderer string writes using
Semantic.PATH_STRING or Semantic.TOKEN_STRING require DataAccess.SYNC.
- Deprecated renderer compatibility writes do not support string arrays, asset arrays, or timecode attributes.
- Custom relationships are not populated by the generic authored-attribute path. Specific relationships used by supported schemas, such as
material:binding and shader connections, are handled by their schema/population code paths; arbitrary custom relationships are ignored today.
- Unsupported authored types are covered by negative tests in
tests/docs/python/test_all_attributes.py and tests/docs/c/test_all_attributes.cpp; if one starts populating, keep this documentation and those tests in sync.
- For array attributes in Python, pass a list of tensors (one per prim), not a single tensor. NumPy arrays, Warp arrays, and any
__dlpack__-compatible objects are accepted directly.
PrimMode.UPSERT creates absent prims and updates existing prims. PrimMode.INSERT is create-only. When a write creates a column whose authored interpretation is not generic, pass the matching AttributeSemantic.
- For ovstage token and relationship writes, intern strings with
PathDictionary and write the resulting IDs with TOKEN_ID or RELATIONSHIP_PATH_ID semantics.
- The ovstage-native "binding" is the pair (
ovstage_query_handle_t, ovx_token_t attr_token) reserved with ovstage_query_from_path_list + path_dictionary_create_tokens_from_strings. Both are stable across writes/reads until released via ovstage_release_query and path_dictionary_release_path_list_reference. tests/docs/c/helpers.h factors the setup into DocsQueryAndToken + docs_make_query_and_token / docs_release_query_and_token — every C attribute doc-test uses those helpers to keep the boilerplate out of [snippet:] blocks. The deprecated ovrtx_make_binding_desc still exists for standalone-mode callers and borrows its input ovx_string_t prim path array; keep it alive until the write completes.
dirty_bits is a bitvector with 1 bit per prim -- the byte array size must be (prim_count + 7) / 8.
- In C,
dirty_bits support three combination modes via ovrtx_write_bits_t in the ovrtx_input_buffer_t.dirty_bits_mode field: OVRTX_DIRTY_MASK_REPLACE (default -- replace existing mask), OVRTX_DIRTY_MASK_OR (merge with existing), OVRTX_DIRTY_MASK_AND (intersect with existing).
C convenience helpers for string attributes (#include <ovrtx/ovrtx_attributes.h>):
ovrtx_set_path_attributes(renderer, paths, count, attr_name, path_values) -- write path/relationship attributes. Each prim gets a single-element array (relationships are always arrays in USD).
ovrtx_set_token_attributes(renderer, paths, count, attr_name, token_values) -- write token string attributes (one per prim).
Source: tests/docs/c/test_attribute_helpers.cpp snippet doc-set-token-attributes-c
Deprecated Standalone APIs
The renderer attribute-write APIs in this skill are deprecated in 0.4 and retained
for compatibility. New code should write through ovstage with an application-owned
ordinal, wait for completion, and advance the write floor before rendering.
See docs/core/ovstage_integration.rst, skills/update-0_3-0_4-c/SKILL.md and skills/update-0_3-0_4-python/SKILL.md.
References
- Use the
> **Source:** directives in this skill to locate tested snippets before reusing API patterns.
- Keep related skills, docs, and snippets synchronized when changing the workflow.
1---2name: writing-attributes3description: Writing scalar and array attribute data to prims. Use when user asks to write an attribute, set a property, change a material, set a color, or modify mesh data.4license: LicenseRef-NvidiaProprietary5---67# Writing Attributes89## When to Use1011Use this skill when the user asks to write an attribute, set a property, change a material, set a color, or modify mesh data.1213## Inputs1415Resolve inputs in this order: existing repository files and referenced snippets, explicit user request, then broader agent context.1617- Target API surface: Python, C/C++, or both.18- Prim paths, attribute name, USD value type, scalar versus array shape, and desired values.19- Data source location, dtype, semantic conversion, ordinal, and write-floor publication.20- Whether the write is one-shot, repeated with a stable target, or needs direct mapped-buffer access.21- Repository source snippets referenced below. Treat these snippets as the API source of truth.2223## Prerequisites2425- Use an ovrtx checkout that contains the referenced examples and docs tests.26- Read the relevant `> **Source:**` snippet before writing or explaining API usage.27- Use `attribute-bindings` for repeated writes to the same prims/attribute, and `mapping-attributes` for zero-copy direct writes.28- Use `binding-materials` when the request is specifically to set `material:binding`.2930## Instructions31321. Identify the target language, prim paths, attribute name, value kind, data shape, memory target, and sync/async requirement.332. Read the matching source snippet and copy its lifecycle pattern rather than inventing equivalent calls.343. Validate dtype, shape, semantic, and ownership rules before proposing or editing code.354. Wait for the ovstage write, then advance the write floor before rendering its ordinal; preserve C compatibility lifetimes when maintaining C code.365. When changing code, run the narrow docs test or example that owns the snippet whenever practical.3738## Output Format3940- For explanations, cite the relevant API names, source snippets, and caveats.41- For code changes, summarize the files changed, snippets affected, and validation run.4243## Scripts4445This skill has no scripts.4647## Limitations4849- Deprecated ovrtx compatibility writes retain their documented unsupported-type limits for string arrays, asset arrays, and timecode attributes.50- For ovstage token and path values, intern IDs through `ovstage.PathDictionary` and use the matching `AttributeSemantic`.51- The referenced snippets remain the source of truth; update or add tested snippets before documenting new API usage.5253## Overview5455Beyond transforms, ovstage can write arbitrary attributes to prims: colors, visibility, mesh geometry, string tokens, and more. The DLTensor dtype must exactly match the USD attribute schema.5657There are two categories:58- **Scalar attributes** -- one value per prim (e.g., a color, a transform)59- **Array attributes** -- variable-length per prim (e.g., mesh points, face vertex counts)6061## Tensor layout6263Ovstage Python and the C compatibility API use lane-based DLTensor attribute layouts.6465### Python ovstage — lane-based attributes6667Scalar values can use NumPy arrays directly. For vectors and matrices, wrap NumPy storage with `ovstage.make_dltensor`, set the logical element count in `shape`, and set the component count in `dtype.lanes`:6869| USD type | NumPy storage | ovstage DLTensor |70|---|---|---|71| `int` / `float` scalar for N prims | `(N,)` | `shape=[N]`, lanes 1 |72| `float3` / `point3f` scalar for N prims | `(N, 3)` | `shape=[N]`, lanes 3 |73| `float4` / `color4f` scalar for N prims | `(N, 4)` | `shape=[N]`, lanes 4 |74| 4x4 matrix for N prims | `(N, 4, 4)` | `shape=[N]`, lanes 16 |75| `int[]` array with M elements on one prim | `(M,)` | `shape=[M]`, lanes 1 |76| `float3[]` / `point3f[]` with M elements on one prim | `(M, 3)` | `shape=[M]`, lanes 3 |7778> **Source:** `tests/docs/python/test_attribute_shapes.py` snippets `doc-shape-scalar-int32`, `doc-shape-float3-array`, `doc-shape-mat4-array`.7980### C — lane-based attributes8182The C API uses `DLDataType::lanes` for multi-component attribute reads and writes. The shape counts logical attribute elements; the lane count holds the vector or matrix component count:8384| USD type | C DLTensor shape | C `DLDataType` |85|---|---|---|86| `int` / `float` scalar for N prims | `[N]` | `{kDLInt/kDLFloat, bits, 1}` |87| `float3` / `point3f` scalar for N prims | `[N]` | `{kDLFloat, 32, 3}` |88| 4x4 double matrix for N prims | `[N]` | `{kDLFloat, 64, 16}` |89| `int[]` array with M elements on one prim | `[M]` | `{kDLInt, 32, 1}` |90| `float3[]` / `point3f[]` with M elements on one prim | `[M]` | `{kDLFloat, 32, 3}` |9192> **Source:** `tests/docs/c/test_attribute_shapes.cpp` snippets `doc-shape-scalar-int32-c`, `doc-shape-float3-array-c`, `doc-shape-mat4-array-c`.9394For a C `point3f[]` attribute with 10 points, write or read one tensor with `shape=[10]` and `dtype={kDLFloat, 32, 3}`. Rendered output/AOV tensors are not attribute tensors; in C they use channel-last shapes such as `[height, width, channels]` with `dtype.lanes=1`.9596## USD Type Lookup9798Use this table to answer "how do I write USD type X?" For ovstage, pass `is_array=True` for array-valued attributes. The C compatibility API sets `binding.binding_desc.attribute_type.is_array = true`.99100| USD type(s) | Python value to write | C tensor dtype | Notes |101|---|---|---|---|102| `bool` / `bool[]` | `np.bool_`, shape `(N,)` or per-prim `(M,)` | `{kDLBool, 8, 1}` | |103| `uchar` / `uchar[]` | `np.uint8` | `{kDLUInt, 8, 1}` | |104| `int`, `int2`, `int3`, `int4` and arrays | `np.int32`, trailing shape `()`, `(2,)`, `(3,)`, `(4,)` | `{kDLInt, 32, lanes}` | |105| `uint` / `uint[]` | `np.uint32` | `{kDLUInt, 32, 1}` | |106| `int64` / `int64[]` | `np.int64` | `{kDLInt, 64, 1}` | |107| `uint64` / `uint64[]` | `np.uint64` | `{kDLUInt, 64, 1}` | |108| `half`, `half2`, `half3`, `half4` and arrays | `np.float16`, trailing shape by component count | `{kDLFloat, 16, lanes}` | |109| `float`, `float2`, `float3`, `float4` and arrays | `np.float32`, trailing shape by component count | `{kDLFloat, 32, lanes}` | Direct runtime writes to scalar `float3` work, but authored scalar USD `float3` population is bugged in the current runtime. Prefer role-bearing `point3f`, `normal3f`, `vector3f`, or `color3f` for USD-authored data. |110| `double`, `double2`, `double3`, `double4` and arrays | `np.float64`, trailing shape by component count | `{kDLFloat, 64, lanes}` | |111| `point3*`, `normal3*`, `vector3*`, `color3*`, `color4*`, `texCoord2f` and arrays | numeric dtype by suffix (`h/f/d`), trailing role dimensions | same numeric dtype/lanes as storage | Roles are schema intent; tensor storage is numeric. |112| `quat*` and arrays | numeric dtype by suffix, shape `(N, 4)` or `(M, 4)` | `{kDLFloat, bits, 4}` | Write runtime order `(i, j, k, real)`, not USDA order `(real, i, j, k)`. |113| `matrix2d`, `matrix3d`, `matrix4d`, `frame4d` and arrays | `np.float64`, flattened trailing shape `(4,)`, `(9,)`, `(16,)`, `(16,)` | `{kDLFloat, 64, 4/9/16}` | Generic authored matrix attrs are flattened. Transform semantic writes may use `(N, 4, 4)` Python arrays or `{kDLFloat,64,16}` C tensors. |114| `extent`, `_worldExtent` | `np.float64`, shape `(N, 6)` | `{kDLFloat, 64, 6}` | Usually read-only from population; `extent` is local-space, `_worldExtent` is world-space. |115| `string` | UTF-8 `np.uint8` byte rows with `is_array=True` and `AttributeSemantic.STRING` | `{kDLUInt, 8, 1}` with `is_array=true` | One byte row represents one USD string value. |116| `token` / `token[]` | `np.uint64` IDs from `PathDictionary.intern_token()` with `AttributeSemantic.TOKEN_ID` | `{kDLUInt, 64, 1}` raw IDs with `OVRTX_SEMANTIC_TOKEN_ID`, or compatibility string helpers | Set `is_array` to match the USD attribute kind. |117| `asset` | `np.uint64` id pairs from `PathDictionary.intern_token()` with `AttributeSemantic.ASSET_PATH_ID` | `{kDLUInt, 64, 2}` with `is_array=false` | One `(authored, resolved)` pair per prim. ovstage does not resolve assets, so intern both paths yourself. Use token id 0 for the resolved half when the path resolves to nothing. Works for both fresh attributes and USD-populated asset columns (ovstage >= 0.2). On 0.1.x a write of this shape created a second same-named column instead of updating the populated one. |118| `relationship` | `np.uint64` IDs from `PathDictionary.intern_path()` with `is_array=True` and `AttributeSemantic.RELATIONSHIP_PATH_ID` | path string/path ID semantics | Use relationship-specific skills for schema-specific behavior. |119| `timecode` / `timecode[]` | unsupported | unsupported | |120121## Python122123### Array attribute write (mesh points)124125> **Source:** `tests/docs/python/test_attribute_shapes.py` snippet `doc-shape-float3-array`126127### Array attribute write (same pattern for other array schemas)128129> **Source:** `tests/docs/python/test_attribute_shapes.py` snippet `doc-shape-float3-array`130131### Prim modes132133> **Source:** `tests/docs/python/test_attribute_bindings.py` snippet `doc-bind-attribute-write`134135### Token array attribute136137> **Source:** `tests/docs/python/test_attribute_bindings.py` snippet `doc-write-token-array`138139### Path/relationship array attribute140141> **Source:** `tests/docs/python/test_base.py` snippet `doc-bind-material`142143### Authored attribute matrix144145Python raw snippets:146147| Type/pattern | Snippet |148|---|---|149| `bool` | `doc-write-usd-bool` |150| `int` | `doc-write-usd-int` |151| `float` | `doc-write-usd-float` |152| `point3f` | `doc-write-usd-point3f` |153| `point3f[]` | `doc-write-usd-point3f-array` |154| `normal3f` | `doc-write-usd-normal3f` |155| `vector3f` | `doc-write-usd-vector3f` |156| `color3f` | `doc-write-usd-color3f` |157| `matrix4d` | `doc-write-usd-matrix4d` |158| `quatf` | `doc-write-usd-quatf` |159| `string` | `doc-write-usd-string` |160| `token` | `doc-write-usd-token` |161| `token[]` | `doc-write-usd-token-array` |162163The snippets listed in this table live in `tests/docs/python/test_all_attributes.py`.164165They write through a reusable ovstage query, wait for each write, and advance166the write floor before reading the updated value. Use `ovstage.PathDictionary`167to intern token and relationship strings, then write the resulting IDs with168`AttributeSemantic.TOKEN_ID`, `AttributeSemantic.RELATIONSHIP_PATH_ID` or169`AttributeSemantic.ASSET_PATH_ID`. String payloads use byte rows with170`AttributeSemantic.STRING`.171172## C173174### Generic scalar write in C175176> **Source:** `tests/docs/c/test_attribute_bindings.cpp` snippet `doc-write-bound-attribute-c`177178### Supported authored attribute write snippets in C179180The snippets below are ovstage-native: they issue `ovstage_write_attribute` against a `DocsQueryAndToken` (single-prim query handle + interned attribute token) with an `ovstage_write_data_t` describing the DLTensor payload, `is_array`, and `OVSTAGE_SEMANTIC_*`. Each block advances the write floor and bumps the caller's tracked ordinal so a follow-up `latest(ordinal)` read observes the write. The ovrtx `ovrtx_write_attribute` + `ovrtx_binding_desc_or_handle_t` compatibility shim is still present for standalone-renderer callers but is not used by these snippets — the recommended path is attached ovstage.181182C raw snippets:183184| Type/pattern | Snippet |185|---|---|186| scalar numeric | `doc-write-usd-float-c` |187| lane-3 scalar | `doc-write-usd-point3f-c` |188| lane-3 array | `doc-write-usd-point3f-array-c` |189| lane-16 matrix | `doc-write-usd-matrix4d-c` |190| quaternion | `doc-write-usd-quatf-c` |191| token | `doc-write-usd-token-c` |192| token array | `doc-write-usd-token-array-c` |193| string bytes | `doc-write-usd-string-c` |194| scalar asset id pair | `doc-write-usd-asset-c` |195196The snippets listed in this table live in `tests/docs/c/test_all_attributes.cpp`. As of ovstage 0.2, writing `ASSET_PATH_ID` id pairs to a USD-populated scalar `asset` attribute is supported and updates the existing column in place. On 0.1.x a write of this shape created a second same-named column of a conflicting type instead. The scalar-asset snippets are runtime-validated by `AllAttributesTest.AssetReadWriteSnippets`. Intern both paths through the path dictionary before the write, and use token id 0 for the resolved half when the path resolves to nothing.197198## Key Types / Functions199200| Python (ovstage) | C (ovstage) | C (deprecated ovrtx compat) |201|---|---|---|202| `stage.write_attribute(query, attr, ordinal=..., tensors=..., is_array=...)` | `ovstage_write_attribute(stage, query_handle, attr_ref, ordinal, write_data, prim_mode)` | `ovrtx_write_attribute(renderer, &binding, &buffer, access)` |203| `stage.advance_write_floor(ordinal, ovstage.Scope.ALL)` | `ovstage_advance_write_floor(stage, &desc)` with `desc.ordinal=N, desc.scope=OVSTAGE_SCOPE_ALL` | (renderer implicitly seals per step) |204205Ovstage C write payload (`ovstage_write_data_t`):206- `.tensors` / `.tensor_count` — DLTensor array. `tensor_count==1` for the packed-uniform layout used by every snippet.207- `.is_array` — sole authority for fixed (`false`) vs. array (`true`) storage. Does NOT auto-derive from the DLTensor's shape.208- `.semantic` — `ovstage_attribute_semantic_t`, tag the write with the same geometric role the column was created under, or `OVSTAGE_SEMANTIC_NONE` to preserve an existing column's authored role.209210Semantics (Python: `ovstage.AttributeSemantic`; C ovstage: `ovstage_attribute_semantic_t`; C ovrtx compat: `ovrtx_attribute_semantic_t`):211- `AttributeSemantic.NONE` / `OVSTAGE_SEMANTIC_NONE` / `OVRTX_SEMANTIC_NONE` -- generic data.212- `AttributeSemantic.MATRIX` / `OVSTAGE_SEMANTIC_MATRIX` / `OVRTX_SEMANTIC_XFORM_MAT4x4` -- matrix data.213- `OVRTX_SEMANTIC_XFORM_POS3d_ROT4f_SCALE3f` -- decomposed transform (C ovrtx compat only).214- `OVRTX_SEMANTIC_XFORM_POS3d_ROT3x3f` -- decomposed transform (C ovrtx compat only).215- `AttributeSemantic.RELATIONSHIP_PATH_ID` / `OVSTAGE_SEMANTIC_RELATIONSHIP_PATH_ID` -- interned relationship path IDs (write `is_array=true`).216- `AttributeSemantic.TOKEN_ID` / `OVSTAGE_SEMANTIC_TOKEN_ID` / `OVRTX_SEMANTIC_TOKEN_ID` -- interned token IDs.217- `AttributeSemantic.STRING` / `OVSTAGE_SEMANTIC_STRING` -- UTF-8 USD string bytes (write `is_array=true`, dtype `{kDLUInt, 8, 1}`).218- `AttributeSemantic.ASSET_PATH_ID` / `OVSTAGE_SEMANTIC_ASSET_PATH_ID` -- interned `(authored, resolved)` asset token ids (write `is_array=false`, dtype `{kDLUInt, 64, 2}`).219220In C, `ovstage_write_attribute` returns `ovstage_enqueue_result_t` which contains both `.status` (check for `OVSTAGE_OK`) and `.op_index` (for async tracking via `ovstage_wait_op`). Every doc-test snippet uses the shared `docs_wait_ovstage_no_errors(stage, op_index)` helper from `tests/docs/c/helpers.h` to wait + assert on op errors.221222The deprecated `ovrtx_write_attribute` still returns `ovrtx_enqueue_result_t` and tracks via `ovrtx_wait_op` for standalone-mode callers.223224Deprecated renderer data access modes (Python: `from ovrtx import DataAccess`):225- `DataAccess.SYNC` -- copies data during the call, safe to free after return226- `DataAccess.ASYNC` -- data accessed later during stream execution, must keep alive; pass `cuda_stream=` or `cuda_event=` for GPU synchronization. Not allowed with string data.227228## Troubleshooting229230- Array attribute dtype must exactly match the USD schema. Using numpy's default `float64` for a `float3[]` attribute (which expects `float32`) will cause errors.231- In the current runtime, authored scalar USD `float3` values may be created but populated as zero by `populateAllAuthoredAttributes`. If a value needs to come from USD, author it as a role-bearing type such as `vector3f`, `point3f`, `normal3f`, or `color3f`. Direct runtime writes to scalar `float3` still work.232- Quaternion tensors use ovrtx runtime lane order `(i, j, k, real)`. USDA `quat*` values are authored as `(real, i, j, k)`, so reading `quatd`, `quatf`, or `quath` attributes reorders the components into `(i, j, k, real)`, and writes should use that runtime tensor order.233- Deprecated renderer string writes using `Semantic.PATH_STRING` or `Semantic.TOKEN_STRING` require `DataAccess.SYNC`.234- Deprecated renderer compatibility writes do not support string arrays, asset arrays, or timecode attributes.235- Custom relationships are not populated by the generic authored-attribute path. Specific relationships used by supported schemas, such as `material:binding` and shader connections, are handled by their schema/population code paths; arbitrary custom relationships are ignored today.236- Unsupported authored types are covered by negative tests in `tests/docs/python/test_all_attributes.py` and `tests/docs/c/test_all_attributes.cpp`; if one starts populating, keep this documentation and those tests in sync.237- For array attributes in Python, pass a list of tensors (one per prim), not a single tensor. NumPy arrays, Warp arrays, and any `__dlpack__`-compatible objects are accepted directly.238- `PrimMode.UPSERT` creates absent prims and updates existing prims. `PrimMode.INSERT` is create-only. When a write creates a column whose authored interpretation is not generic, pass the matching `AttributeSemantic`.239- For ovstage token and relationship writes, intern strings with `PathDictionary` and write the resulting IDs with `TOKEN_ID` or `RELATIONSHIP_PATH_ID` semantics.240- The ovstage-native "binding" is the pair (`ovstage_query_handle_t`, `ovx_token_t attr_token`) reserved with `ovstage_query_from_path_list` + `path_dictionary_create_tokens_from_strings`. Both are stable across writes/reads until released via `ovstage_release_query` and `path_dictionary_release_path_list_reference`. `tests/docs/c/helpers.h` factors the setup into `DocsQueryAndToken` + `docs_make_query_and_token` / `docs_release_query_and_token` — every C attribute doc-test uses those helpers to keep the boilerplate out of `[snippet:]` blocks. The deprecated `ovrtx_make_binding_desc` still exists for standalone-mode callers and borrows its input `ovx_string_t` prim path array; keep it alive until the write completes.241- `dirty_bits` is a bitvector with 1 bit per prim -- the byte array size must be `(prim_count + 7) / 8`.242- In C, `dirty_bits` support three combination modes via `ovrtx_write_bits_t` in the `ovrtx_input_buffer_t.dirty_bits_mode` field: `OVRTX_DIRTY_MASK_REPLACE` (default -- replace existing mask), `OVRTX_DIRTY_MASK_OR` (merge with existing), `OVRTX_DIRTY_MASK_AND` (intersect with existing).243244C convenience helpers for string attributes (`#include <ovrtx/ovrtx_attributes.h>`):245- `ovrtx_set_path_attributes(renderer, paths, count, attr_name, path_values)` -- write path/relationship attributes. Each prim gets a single-element array (relationships are always arrays in USD).246- `ovrtx_set_token_attributes(renderer, paths, count, attr_name, token_values)` -- write token string attributes (one per prim).247248> **Source:** `tests/docs/c/test_attribute_helpers.cpp` snippet `doc-set-token-attributes-c`249250## Deprecated Standalone APIs251252The renderer attribute-write APIs in this skill are deprecated in 0.4 and retained253for compatibility. New code should write through ovstage with an application-owned254ordinal, wait for completion, and advance the write floor before rendering.255256See `docs/core/ovstage_integration.rst`, `skills/update-0_3-0_4-c/SKILL.md` and `skills/update-0_3-0_4-python/SKILL.md`.257258## References259260- Use the `> **Source:**` directives in this skill to locate tested snippets before reusing API patterns.261- Keep related skills, docs, and snippets synchronized when changing the workflow.