Reading Attributes
When to Use
Use this skill when the user asks to read an attribute value, fetch mesh data (points, faceVertexCounts, etc.), inspect a render setting, or sample transforms.
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.
- Reusable ovstage query, interned attribute token, expected USD value type, and scalar versus array read mode.
- Target consumer: CPU NumPy, C DLTensor, GPU-aware DLPack consumer, or metadata/schema inspection.
- Whether the caller needs raw storage, shape/dtype discovery, or values copied out for later use.
- 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
stage-queries first if the user needs to discover prims or attribute schemas before reading values.
Instructions
- Identify the target language, prim paths or prim-list handle, attribute name, scalar/array mode, 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.
- Release each fetched ovstage group and read/query handle according to the referenced examples; 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 reads retain their documented unsupported-type limits for string arrays, asset arrays, and timecode attributes.
- The
OVRTX_SEMANTIC_NONE restriction applies only to deprecated ovrtx compatibility reads.
- The referenced snippets remain the source of truth; update or add tested snippets before documenting new API usage.
Overview
stage.read_attributes reads one or more requested attribute columns through an ovstage query. Fetch each result group, consume its DLPack tensors, then release the group and read handle.
Use ovstage.OrdinalRange.latest(ordinal) to read the latest committed values visible at an ordinal.
For mapping (zero-copy writes into ovstage buffers), see the mapping-attributes skill.
USD Type Lookup
Use this table to answer "how do I read USD type X?" Ovstage uses stage.read_attributes for both scalar and array-valued columns; inspect each group's is_array flag. Deprecated C bindings set binding.binding_desc.attribute_type.is_array = true for array reads. Python exposes shape dimensions; C uses DLDataType::lanes.
| USD type(s) |
Python read result |
C binding dtype |
Notes |
bool / bool[] |
(N,) or (M,), np.bool_ |
{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} |
Authored scalar USD float3 currently populates as zero; prefer role-bearing point3f, normal3f, vector3f, or color3f for values read from USD. |
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 not surfaced as tensor metadata; they still matter for USD population and schema intent. |
quat* and arrays |
numeric dtype by suffix, shape (N, 4) or (M, 4) |
{kDLFloat, bits, 4} |
Runtime component order is (i, j, k, real), while USDA authoring order is (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-specific APIs/snippets may reshape 4x4 xforms to (N, 4, 4). |
extent, _worldExtent |
np.float64, shape (N, 6) |
{kDLFloat, 64, 6} |
extent is local-space; _worldExtent is world-space. |
string |
uint8 byte array, decode as UTF-8 |
{kDLUInt, 8, 1} with is_array=true |
Scalar USD strings are represented as byte arrays. This is not string[]; string arrays are not supported. Use token[] for string-like arrays. |
token / token[] |
raw uint64 token IDs |
{kDLUInt, 64, 1} |
Resolve token IDs with ovstage.PathDictionary.token_to_string(). |
asset |
uint64 token id pairs with AttributeSemantic.ASSET_PATH_ID |
{kDLUInt, 64, 2} with is_array=false |
One (authored, resolved) pair per prim. Resolve each id with ovstage.PathDictionary.token_to_string(). Token id 0 is the empty token, so a resolved id of 0 means the path resolved to nothing. |
relationship |
raw uint64 path IDs |
path IDs / path-list semantics |
Resolve path IDs with ovstage.PathDictionary.path_to_string(). Use relationship-specific skills for schema-specific behavior. |
timecode / timecode[] |
unsupported |
unsupported |
|
Python
Scalar read
Returns a ManagedDLTensor with shape (N,) for N input prims. Convert with np.from_dlpack() for a zero-copy numpy view.
Source: tests/docs/python/test_attribute_read.py snippet doc-read-attribute-scalar
Deprecated compatibility: scalar read into a destination
Pass dest= with a DLPack-compatible tensor (NumPy array, Warp array, etc.). The read writes directly into dest; the returned tensor aliases the same memory. The dest dtype must match how the runtime stores the attribute.
Source: tests/docs/python/test_attribute_read.py snippet doc-read-attribute-dest-tensor
Array read
Fetch the array-valued group, copy or consume its DLPack tensor, and release the group.
Source: tests/docs/python/test_attribute_read.py snippet doc-read-array-attribute
Async read
Wait for the ovstage read handle, fetch groups, then release each group and the read handle.
Source: tests/docs/python/test_attribute_read.py snippet doc-read-attribute-async
Deprecated compatibility: GPU destination (CUDA)
Allocate the destination on the GPU via any DLPack-compatible allocator (e.g. Warp). Pass the CUDA stream handle so the read is stream-ordered with your GPU work.
Source: tests/docs/python/test_attribute_read.py snippet doc-read-attribute-cuda-dest
Authored attribute matrix
Python raw snippets:
| Type/pattern |
Snippet |
bool |
doc-read-usd-bool |
int |
doc-read-usd-int |
float |
doc-read-usd-float |
point3f |
doc-read-usd-point3f |
point3f[] |
doc-read-usd-point3f-array |
normal3f |
doc-read-usd-normal3f |
vector3f |
doc-read-usd-vector3f |
color3f |
doc-read-usd-color3f |
matrix4d |
doc-read-usd-matrix4d |
quatf |
doc-read-usd-quatf |
string |
doc-read-usd-string |
The snippets listed in this table live in tests/docs/python/test_all_attributes.py.
These examples use ovstage.PathDictionary to obtain attribute-name tokens,
read through a reusable ovstage query, copy each returned tensor, and release
its result group.
Local and world-space extents
extent is the authored local-space extent. _worldExtent is populated as the transformed world-space extent.
Source: tests/docs/python/test_all_attributes.py snippet doc-extent-world-extent
C
Scalar read
Source: tests/docs/c/test_attribute_read.cpp snippet doc-read-attribute-scalar-c
Array read
Under ovstage, array-vs-scalar kind is not part of the read call — the read handle returns group.is_array reflecting the column's declared kind. Under the deprecated ovrtx_read_attribute compat shim, set binding.binding_desc.attribute_type.is_array = true explicitly (defaulted to false by ovrtx_make_binding_desc).
Source: tests/docs/c/test_attribute_read.cpp snippet doc-read-array-attribute-c
Supported authored attribute read snippets in C
The snippets below are ovstage-native: ovstage_read_attributes against a DocsQueryAndToken (single-prim query + interned attribute token), one ovstage_fetch_read_next per matched group, ovstage_release_group per group, then ovstage_release_read. Each snippet asserts group.data.tensor_count > 0 before dereferencing group.data.tensors[0].
C raw snippets:
| Type/pattern |
Snippet |
| scalar numeric |
doc-read-usd-float-c |
| lane-3 scalar |
doc-read-usd-point3f-c |
| lane-3 array |
doc-read-usd-point3f-array-c |
| lane-16 matrix |
doc-read-usd-matrix4d-c |
| quaternion |
doc-read-usd-quatf-c |
| token |
doc-read-usd-token-c |
| token array |
doc-read-usd-token-array-c |
| string bytes |
doc-read-usd-string-c |
| scalar asset id pair |
doc-read-usd-asset-c |
The snippets listed in this table live in tests/docs/c/test_all_attributes.cpp.
C token snippets include path-dictionary resolution because the C API exposes ovstage_get_path_dictionary(). As of ovstage 0.2, reading a scalar asset returns ASSET_PATH_ID token id pairs ({kDLUInt, 64, 2}, is_array=false, one (authored, resolved) pair per prim). Resolve both ids through the path dictionary. A resolved id of 0 is the empty token, which means the path resolved to nothing. On ovstage 0.1.x the same read returned {kDLUInt, 64, 2} pairs with semantic NONE, and on 0.1.0 single-prim reads failed with END_OF_ITERATION. The doc-read-usd-asset-c / doc-write-usd-asset-c snippets are runtime-validated by AllAttributesTest.AssetReadWriteSnippets.
Local and world-space extents in C
Source: tests/docs/c/test_all_attributes.cpp snippet doc-extent-world-extent-c
Key Types / Functions
| Python (ovstage) |
C (ovstage) |
C (deprecated ovrtx compat) |
stage.read_attributes(query, attrs, ordinal_range) |
ovstage_read_attributes(stage, query, tokens, count, range, &read_handle) |
ovrtx_read_attribute(renderer, &binding, &read_dest, &read_handle) + fetch/release |
read.fetch_next() / stage.release_group(group) |
ovstage_fetch_read_next(stage, read_handle, timeout, &group) / ovstage_release_group(stage, &group) |
ovrtx_fetch_read_result(...) / ovrtx_release_read_result(...) |
ovstage.OrdinalRange.latest(ordinal) |
ovstage_ordinal_range_t range{}; range.end_ordinal = ordinal; |
stream-ordered compatibility read |
Ovstage C result layout (ovstage_read_group_t):
.is_array — array vs scalar kind, matches the write-side declaration.
.data.tensors / .data.tensor_count — DLTensor array. tensor_count==1 under the packed-uniform layout every snippet uses; always assert tensor_count > 0 before dereferencing tensors[0].
- Each
DLTensor in the group: shape=[prim_count] for scalar reads and shape=[element_count] for array reads, with dtype.lanes carrying the tuple width (matrix4d → {kDLFloat, 64, 16}, point3f → {kDLFloat, 32, 3}, etc.).
.prims.list / .prims.count / .prims.offset / .prims.index_map — enumerate the matched prims via the group's prim list; index_map applies gather/reorder semantics.
.attribute — the token that identifies which of the caller's requested attributes this group corresponds to. Route multi-attribute reads (e.g. doc-extent-world-extent-c) by comparing to the interned tokens. One token can yield more than one group: when the queried prims use the name for several column types and no ovstage write has given the name an identity, each prim is served with the type of its own column, so the groups differ in dtype and is_array. Read the shape off each group rather than off the first one. Such a name cannot be served to a GPU destination, so a device read returns no group for it and serves the rest of the read normally (ovstage >= 0.2).
Deprecated C compat result layout (ovrtx_read_output_t):
- Scalar reads:
buffer_count == 1, single tensor with shape [prim_count].
- Array reads:
buffer_count == prim_count, one tensor per prim (variable length).
- When a caller-supplied
read_dest tensor was passed in: buffer_count == 0 (data landed in your tensor).
Troubleshooting
- Ovstage reads carry the schema semantic; ovrtx compat reads do not.
ovrtx_read_attribute rejects any semantic other than OVRTX_SEMANTIC_NONE. ovstage_read_attributes reads the column at whatever semantic it was created with, so the caller does not pass a semantic on read — the returned DLTensor dtype code/bits/lanes are the authoritative shape.
- Generic authored USD attributes require opt-in population. Root-layer
customLayerData.populateAllAuthoredAttributes = true asks the runtime to populate authored attributes beyond the normal schema set. Use it only when needed: populating everything can dramatically increase memory usage on assets with many unused properties. See loading-usd for the layer metadata tradeoff.
- Schema-owned attributes fix the element type.
omni:rtx:rtpt:maxBounces is stored as uint32 even if you wrote it as int32. When allocating a dest tensor, match the runtime's dtype (np.uint32) — not what you wrote.
- Release fetched ovstage groups explicitly. Call
stage.release_group(group) after consuming or copying each group, and release manually managed read/query handles.
- Ovstage reads carry array-vs-scalar kind in the returned group, not the request. Check
group.is_array after ovstage_fetch_read_next — the read call itself takes no is_array parameter. The deprecated ovrtx_read_attribute compat shim still requires binding.binding_desc.attribute_type.is_array = true on the binding (defaulted to false by ovrtx_make_binding_desc).
- Ovstage queries own their prim-list storage; ovrtx compat bindings borrow.
ovstage_query_from_path_list retains a reference to the passed ovx_primpath_list_t until ovstage_release_query fires — the caller then calls path_dictionary_release_path_list_reference. The deprecated ovrtx_make_binding_desc stores the ovx_string_t* prim path array you pass without copying; keep it alive until the read has been enqueued, waited, fetched, and released.
- Unsupported authored types are tested as absent.
tests/docs/data/all-attributes.usda deliberately authors string[], asset[], custom relationships, timecode, and timecode[]; the all-attributes tests assert they are not populated by the current runtime.
Deprecated Standalone APIs
The renderer attribute-read APIs in this skill are deprecated in 0.4 and retained
for compatibility. New code should read through an ovstage query, consume fetched
groups, and release each group and read handle according to ovstage ownership rules.
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.
Related skills
stage-queries — discover prims and their attribute schemas before reading.
writing-attributes — write values that can then be read back.
mapping-attributes — zero-copy mapping (no fetch step).
async-operations — polling, timeouts, the two-phase Operation/PendingFetch lifecycle.
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: reading-attributes3description: Reading scalar or array attributes from prims into CPU or GPU tensors. Use when user asks to read an attribute value, fetch mesh data (points, faceVertexCounts, etc.), inspect a render setting, or sample transforms.4license: LicenseRef-NvidiaProprietary5---67# Reading Attributes89## When to Use1011Use this skill when the user asks to read an attribute value, fetch mesh data (points, faceVertexCounts, etc.), inspect a render setting, or sample transforms.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- Reusable ovstage query, interned attribute token, expected USD value type, and scalar versus array read mode.19- Target consumer: CPU NumPy, C DLTensor, GPU-aware DLPack consumer, or metadata/schema inspection.20- Whether the caller needs raw storage, shape/dtype discovery, or values copied out for later use.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 `stage-queries` first if the user needs to discover prims or attribute schemas before reading values.2829## Instructions30311. Identify the target language, prim paths or prim-list handle, attribute name, scalar/array mode, memory target, and sync/async requirement.322. Read the matching source snippet and copy its lifecycle pattern rather than inventing equivalent calls.333. Validate dtype, shape, semantic, and ownership rules before proposing or editing code.344. Release each fetched ovstage group and read/query handle according to the referenced examples; preserve C compatibility lifetimes when maintaining C code.355. When changing code, run the narrow docs test or example that owns the snippet whenever practical.3637## Output Format3839- For explanations, cite the relevant API names, source snippets, and caveats.40- For code changes, summarize the files changed, snippets affected, and validation run.4142## Scripts4344This skill has no scripts.4546## Limitations4748- Deprecated ovrtx compatibility reads retain their documented unsupported-type limits for string arrays, asset arrays, and timecode attributes.49- The `OVRTX_SEMANTIC_NONE` restriction applies only to deprecated ovrtx compatibility reads.50- The referenced snippets remain the source of truth; update or add tested snippets before documenting new API usage.5152## Overview5354`stage.read_attributes` reads one or more requested attribute columns through an ovstage query. Fetch each result group, consume its DLPack tensors, then release the group and read handle.5556Use `ovstage.OrdinalRange.latest(ordinal)` to read the latest committed values visible at an ordinal.5758For mapping (zero-copy writes into ovstage buffers), see the `mapping-attributes` skill.5960## USD Type Lookup6162Use this table to answer "how do I read USD type X?" Ovstage uses `stage.read_attributes` for both scalar and array-valued columns; inspect each group's `is_array` flag. Deprecated C bindings set `binding.binding_desc.attribute_type.is_array = true` for array reads. Python exposes shape dimensions; C uses `DLDataType::lanes`.6364| USD type(s) | Python read result | C binding dtype | Notes |65|---|---|---|---|66| `bool` / `bool[]` | `(N,)` or `(M,)`, `np.bool_` | `{kDLBool, 8, 1}` | |67| `uchar` / `uchar[]` | `np.uint8` | `{kDLUInt, 8, 1}` | |68| `int`, `int2`, `int3`, `int4` and arrays | `np.int32`, trailing shape `()`, `(2,)`, `(3,)`, `(4,)` | `{kDLInt, 32, lanes}` | |69| `uint` / `uint[]` | `np.uint32` | `{kDLUInt, 32, 1}` | |70| `int64` / `int64[]` | `np.int64` | `{kDLInt, 64, 1}` | |71| `uint64` / `uint64[]` | `np.uint64` | `{kDLUInt, 64, 1}` | |72| `half`, `half2`, `half3`, `half4` and arrays | `np.float16`, trailing shape by component count | `{kDLFloat, 16, lanes}` | |73| `float`, `float2`, `float3`, `float4` and arrays | `np.float32`, trailing shape by component count | `{kDLFloat, 32, lanes}` | Authored scalar USD `float3` currently populates as zero; prefer role-bearing `point3f`, `normal3f`, `vector3f`, or `color3f` for values read from USD. |74| `double`, `double2`, `double3`, `double4` and arrays | `np.float64`, trailing shape by component count | `{kDLFloat, 64, lanes}` | |75| `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 not surfaced as tensor metadata; they still matter for USD population and schema intent. |76| `quat*` and arrays | numeric dtype by suffix, shape `(N, 4)` or `(M, 4)` | `{kDLFloat, bits, 4}` | Runtime component order is `(i, j, k, real)`, while USDA authoring order is `(real, i, j, k)`. |77| `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-specific APIs/snippets may reshape 4x4 xforms to `(N, 4, 4)`. |78| `extent`, `_worldExtent` | `np.float64`, shape `(N, 6)` | `{kDLFloat, 64, 6}` | `extent` is local-space; `_worldExtent` is world-space. |79| `string` | `uint8` byte array, decode as UTF-8 | `{kDLUInt, 8, 1}` with `is_array=true` | Scalar USD strings are represented as byte arrays. This is not `string[]`; string arrays are not supported. Use `token[]` for string-like arrays. |80| `token` / `token[]` | raw `uint64` token IDs | `{kDLUInt, 64, 1}` | Resolve token IDs with `ovstage.PathDictionary.token_to_string()`. |81| `asset` | `uint64` token id pairs with `AttributeSemantic.ASSET_PATH_ID` | `{kDLUInt, 64, 2}` with `is_array=false` | One `(authored, resolved)` pair per prim. Resolve each id with `ovstage.PathDictionary.token_to_string()`. Token id 0 is the empty token, so a resolved id of 0 means the path resolved to nothing. |82| `relationship` | raw `uint64` path IDs | path IDs / path-list semantics | Resolve path IDs with `ovstage.PathDictionary.path_to_string()`. Use relationship-specific skills for schema-specific behavior. |83| `timecode` / `timecode[]` | unsupported | unsupported | |8485## Python8687### Scalar read8889Returns a `ManagedDLTensor` with shape `(N,)` for N input prims. Convert with `np.from_dlpack()` for a zero-copy numpy view.9091> **Source:** `tests/docs/python/test_attribute_read.py` snippet `doc-read-attribute-scalar`9293### Deprecated compatibility: scalar read into a destination9495Pass `dest=` with a DLPack-compatible tensor (NumPy array, Warp array, etc.). The read writes directly into `dest`; the returned tensor aliases the same memory. The `dest` dtype must match how the runtime stores the attribute.9697> **Source:** `tests/docs/python/test_attribute_read.py` snippet `doc-read-attribute-dest-tensor`9899### Array read100101Fetch the array-valued group, copy or consume its DLPack tensor, and release the group.102103> **Source:** `tests/docs/python/test_attribute_read.py` snippet `doc-read-array-attribute`104105### Async read106107Wait for the ovstage read handle, fetch groups, then release each group and the read handle.108109> **Source:** `tests/docs/python/test_attribute_read.py` snippet `doc-read-attribute-async`110111### Deprecated compatibility: GPU destination (CUDA)112113Allocate the destination on the GPU via any DLPack-compatible allocator (e.g. Warp). Pass the CUDA stream handle so the read is stream-ordered with your GPU work.114115> **Source:** `tests/docs/python/test_attribute_read.py` snippet `doc-read-attribute-cuda-dest`116117### Authored attribute matrix118119Python raw snippets:120121| Type/pattern | Snippet |122|---|---|123| `bool` | `doc-read-usd-bool` |124| `int` | `doc-read-usd-int` |125| `float` | `doc-read-usd-float` |126| `point3f` | `doc-read-usd-point3f` |127| `point3f[]` | `doc-read-usd-point3f-array` |128| `normal3f` | `doc-read-usd-normal3f` |129| `vector3f` | `doc-read-usd-vector3f` |130| `color3f` | `doc-read-usd-color3f` |131| `matrix4d` | `doc-read-usd-matrix4d` |132| `quatf` | `doc-read-usd-quatf` |133| `string` | `doc-read-usd-string` |134135The snippets listed in this table live in `tests/docs/python/test_all_attributes.py`.136137These examples use `ovstage.PathDictionary` to obtain attribute-name tokens,138read through a reusable ovstage query, copy each returned tensor, and release139its result group.140141### Local and world-space extents142143`extent` is the authored local-space extent. `_worldExtent` is populated as the transformed world-space extent.144145> **Source:** `tests/docs/python/test_all_attributes.py` snippet `doc-extent-world-extent`146147## C148149### Scalar read150151> **Source:** `tests/docs/c/test_attribute_read.cpp` snippet `doc-read-attribute-scalar-c`152153### Array read154155Under ovstage, array-vs-scalar kind is not part of the read call — the read handle returns `group.is_array` reflecting the column's declared kind. Under the deprecated `ovrtx_read_attribute` compat shim, set `binding.binding_desc.attribute_type.is_array = true` explicitly (defaulted to `false` by `ovrtx_make_binding_desc`).156157> **Source:** `tests/docs/c/test_attribute_read.cpp` snippet `doc-read-array-attribute-c`158159### Supported authored attribute read snippets in C160161The snippets below are ovstage-native: `ovstage_read_attributes` against a `DocsQueryAndToken` (single-prim query + interned attribute token), one `ovstage_fetch_read_next` per matched group, `ovstage_release_group` per group, then `ovstage_release_read`. Each snippet asserts `group.data.tensor_count > 0` before dereferencing `group.data.tensors[0]`.162163C raw snippets:164165| Type/pattern | Snippet |166|---|---|167| scalar numeric | `doc-read-usd-float-c` |168| lane-3 scalar | `doc-read-usd-point3f-c` |169| lane-3 array | `doc-read-usd-point3f-array-c` |170| lane-16 matrix | `doc-read-usd-matrix4d-c` |171| quaternion | `doc-read-usd-quatf-c` |172| token | `doc-read-usd-token-c` |173| token array | `doc-read-usd-token-array-c` |174| string bytes | `doc-read-usd-string-c` |175| scalar asset id pair | `doc-read-usd-asset-c` |176177The snippets listed in this table live in `tests/docs/c/test_all_attributes.cpp`.178179C token snippets include path-dictionary resolution because the C API exposes `ovstage_get_path_dictionary()`. As of ovstage 0.2, reading a scalar `asset` returns `ASSET_PATH_ID` token id pairs (`{kDLUInt, 64, 2}`, `is_array=false`, one `(authored, resolved)` pair per prim). Resolve both ids through the path dictionary. A resolved id of 0 is the empty token, which means the path resolved to nothing. On ovstage 0.1.x the same read returned `{kDLUInt, 64, 2}` pairs with semantic `NONE`, and on 0.1.0 single-prim reads failed with END_OF_ITERATION. The `doc-read-usd-asset-c` / `doc-write-usd-asset-c` snippets are runtime-validated by `AllAttributesTest.AssetReadWriteSnippets`.180181### Local and world-space extents in C182183> **Source:** `tests/docs/c/test_all_attributes.cpp` snippet `doc-extent-world-extent-c`184185## Key Types / Functions186187| Python (ovstage) | C (ovstage) | C (deprecated ovrtx compat) |188|---|---|---|189| `stage.read_attributes(query, attrs, ordinal_range)` | `ovstage_read_attributes(stage, query, tokens, count, range, &read_handle)` | `ovrtx_read_attribute(renderer, &binding, &read_dest, &read_handle)` + fetch/release |190| `read.fetch_next()` / `stage.release_group(group)` | `ovstage_fetch_read_next(stage, read_handle, timeout, &group)` / `ovstage_release_group(stage, &group)` | `ovrtx_fetch_read_result(...)` / `ovrtx_release_read_result(...)` |191| `ovstage.OrdinalRange.latest(ordinal)` | `ovstage_ordinal_range_t range{}; range.end_ordinal = ordinal;` | stream-ordered compatibility read |192193Ovstage C result layout (`ovstage_read_group_t`):194- `.is_array` — array vs scalar kind, matches the write-side declaration.195- `.data.tensors` / `.data.tensor_count` — DLTensor array. `tensor_count==1` under the packed-uniform layout every snippet uses; always assert `tensor_count > 0` before dereferencing `tensors[0]`.196- Each `DLTensor` in the group: `shape=[prim_count]` for scalar reads and `shape=[element_count]` for array reads, with `dtype.lanes` carrying the tuple width (matrix4d → `{kDLFloat, 64, 16}`, point3f → `{kDLFloat, 32, 3}`, etc.).197- `.prims.list` / `.prims.count` / `.prims.offset` / `.prims.index_map` — enumerate the matched prims via the group's prim list; index_map applies gather/reorder semantics.198- `.attribute` — the token that identifies which of the caller's requested attributes this group corresponds to. Route multi-attribute reads (e.g. `doc-extent-world-extent-c`) by comparing to the interned tokens. One token can yield more than one group: when the queried prims use the name for several column types and no ovstage write has given the name an identity, each prim is served with the type of its own column, so the groups differ in `dtype` and `is_array`. Read the shape off each group rather than off the first one. Such a name cannot be served to a GPU destination, so a device read returns no group for it and serves the rest of the read normally (ovstage >= 0.2).199200Deprecated C compat result layout (`ovrtx_read_output_t`):201- Scalar reads: `buffer_count == 1`, single tensor with shape `[prim_count]`.202- Array reads: `buffer_count == prim_count`, one tensor per prim (variable length).203- When a caller-supplied `read_dest` tensor was passed in: `buffer_count == 0` (data landed in your tensor).204205## Troubleshooting206207- **Ovstage reads carry the schema semantic; ovrtx compat reads do not.** `ovrtx_read_attribute` rejects any semantic other than `OVRTX_SEMANTIC_NONE`. `ovstage_read_attributes` reads the column at whatever semantic it was created with, so the caller does not pass a semantic on read — the returned DLTensor dtype code/bits/lanes are the authoritative shape.208- **Generic authored USD attributes require opt-in population.** Root-layer `customLayerData.populateAllAuthoredAttributes = true` asks the runtime to populate authored attributes beyond the normal schema set. Use it only when needed: populating everything can dramatically increase memory usage on assets with many unused properties. See `loading-usd` for the layer metadata tradeoff.209- **Schema-owned attributes fix the element type.** `omni:rtx:rtpt:maxBounces` is stored as `uint32` even if you wrote it as `int32`. When allocating a `dest` tensor, match the runtime's dtype (`np.uint32`) — not what you wrote.210- **Release fetched ovstage groups explicitly.** Call `stage.release_group(group)` after consuming or copying each group, and release manually managed read/query handles.211- **Ovstage reads carry array-vs-scalar kind in the returned group, not the request.** Check `group.is_array` after `ovstage_fetch_read_next` — the read call itself takes no `is_array` parameter. The deprecated `ovrtx_read_attribute` compat shim still requires `binding.binding_desc.attribute_type.is_array = true` on the binding (defaulted to `false` by `ovrtx_make_binding_desc`).212- **Ovstage queries own their prim-list storage; ovrtx compat bindings borrow.** `ovstage_query_from_path_list` retains a reference to the passed `ovx_primpath_list_t` until `ovstage_release_query` fires — the caller then calls `path_dictionary_release_path_list_reference`. The deprecated `ovrtx_make_binding_desc` stores the `ovx_string_t*` prim path array you pass without copying; keep it alive until the read has been enqueued, waited, fetched, and released.213- **Unsupported authored types are tested as absent.** `tests/docs/data/all-attributes.usda` deliberately authors `string[]`, `asset[]`, custom relationships, `timecode`, and `timecode[]`; the all-attributes tests assert they are not populated by the current runtime.214215## Deprecated Standalone APIs216217The renderer attribute-read APIs in this skill are deprecated in 0.4 and retained218for compatibility. New code should read through an ovstage query, consume fetched219groups, and release each group and read handle according to ovstage ownership rules.220221See `docs/core/ovstage_integration.rst`, `skills/update-0_3-0_4-c/SKILL.md` and `skills/update-0_3-0_4-python/SKILL.md`.222223## Related skills224225- `stage-queries` — discover prims and their attribute schemas before reading.226- `writing-attributes` — write values that can then be read back.227- `mapping-attributes` — zero-copy mapping (no fetch step).228- `async-operations` — polling, timeouts, the two-phase `Operation`/`PendingFetch` lifecycle.229230## References231232- Use the `> **Source:**` directives in this skill to locate tested snippets before reusing API patterns.233- Keep related skills, docs, and snippets synchronized when changing the workflow.