Attribute Mapping#
Note
Python stage attribute mapping uses ovstage query and map-group APIs.
Renderer map/unmap methods are deprecated compatibility APIs. Refer to
skills/update-0_3-0_4-python/SKILL.md.
Mapping an attribute gives application code direct access to its stage buffer. This avoids a copy when code writes directly into the buffer.
The lifecycle is:
Map the attribute.
Write into the mapped tensor while the mapping is active.
Unmap the attribute, passing CUDA stream or event synchronization when GPU work wrote the data.
CPU Mapping#
with stage.map_attribute(query, attribute, ordinal=2) as mapping:
mapping.wait()
group = mapping.fetch_next()
matrices = np.from_dlpack(group.dlpack(0)).reshape(1, 4, 4)
matrices[0, 3, 0] = 10.0
stage.advance_write_floor(2, ovstage.Scope.ALL).wait()
// Zero-copy write via map/unmap: map_attribute hands back writable
// storage for each matched prim; the caller fills it, then unmap_attribute
// commits and releases the map session.
ovx_string_or_token_t attr_ref{};
attr_ref.token = attr_token;
ovstage_map_desc_t map_desc{};
map_desc.attribute = attr_ref;
map_desc.dtype = {kDLFloat, 64, 16};
map_desc.semantic = OVSTAGE_SEMANTIC_MATRIX;
map_desc.prim_mode = OVSTAGE_PRIM_MODE_UPSERT;
ovstage_map_handle_t map_handle = OVSTAGE_INVALID_MAP_HANDLE;
ovstage_enqueue_result_t mq = ovstage_map_attribute(
stage_, query_handle, &map_desc, /*ordinal=*/2,
/*element_sizes=*/nullptr, /*element_count=*/0, &map_handle);
ASSERT_EQ(mq.status, OVSTAGE_OK) << format_ovstage_last_error();
docs_wait_ovstage_no_errors(stage_, mq.op_index);
ovstage_map_group_t map_group{};
ASSERT_EQ(ovstage_fetch_map_next(stage_, map_handle, OVSTAGE_TIMEOUT_INFINITE, &map_group),
OVSTAGE_OK);
double* matrix = static_cast<double*>(map_group.data.tensors[0].data);
matrix[0] = 1.0;
matrix[5] = 1.0;
matrix[10] = 1.0;
matrix[15] = 1.0;
matrix[12] = 15.0; // translation.x under USD row-vector convention
// Commit all pending groups and release the map handle.
ovstage_cuda_sync_t no_sync{};
ovstage_enqueue_result_t uq = ovstage_unmap_attribute(stage_, map_handle, no_sync);
ASSERT_EQ(uq.status, OVSTAGE_OK) << format_ovstage_last_error();
docs_wait_ovstage_no_errors(stage_, uq.op_index);
docs_ovstage_advance_write_floor(stage_, 2);
CUDA Mapping#
The deprecated renderer wrapper can map attributes to CUDA memory for GPU-side writes. The example below documents its stream-synchronization requirements.
mapping = renderer.map_attribute(
["/World/Plane"],
"omni:xform",
dtype="float64",
shape=(4, 4),
device=ovrtx.Device.CUDA,
)
tensor = wp.from_dlpack(mapping.tensor, dtype=wp.mat44d)
stream = wp.Stream(device=tensor.device)
wp.launch(_set_xform_translation_x, dim=1, inputs=[tensor, wp.float64(6.0)], stream=stream)
mapping.unmap(stream=stream.cuda_stream)
Explicit Unmap#
Context managers are preferred in Python, but explicit async unmap is available when the application needs to coordinate mapping lifetime manually.
mapping = stage.map_attribute(query, attribute, ordinal=2)
mapping.wait()
group = mapping.fetch_next()
matrices = np.from_dlpack(group.dlpack(0)).reshape(1, 4, 4)
matrices[0, 3, 0] = 9.0
op = mapping.unmap()
op.wait()
stage.advance_write_floor(2, ovstage.Scope.ALL).wait()
Limits and Lifetime#
Ovstage maps ragged array attributes when
element_sizessupplies one element count per queried prim. Omitelement_sizesfor fixed-size attributes.The tensor returned by a mapping is valid only until unmap. Copy data if it must outlive the mapping.
For deprecated renderer CUDA mappings, pass a stream or event on unmap so ovrtx knows when GPU writes are complete.
Do not pass CUDA sync objects for CPU mappings.
Multiple mappings can be outstanding; effects are applied in unmap order.