Release 0.1.1
Pre-releasePatch release. The C ABI is unchanged apart from one enumerator value: no
exported symbol was added, removed, or renamed, and no vtable slot or struct
layout moved, but the ovstage_config_uint64_t count sentinel advances from
0 to 1 now that the enum has a key. The headers add
OVSTAGE_ERROR_OUT_OF_RANGE, the runtime-default hierarchy computation model
selector, its configuration key, and two static inline configuration-entry
helpers; Python adds StageConfig, ErrorCode.OUT_OF_RANGE, and
HierarchyComputationModel.RUNTIME_DEFAULT. Some read and write validations
became stricter, others became less restrictive, and several now report a
different code and message, so callers that branch on status codes should read
the notes below.
Added
- Process configuration can select the runtime-default hierarchy computation
model, which automatic transform updates use and which callers can request
explicitly withOVSTAGE_HIERARCHY_COMPUTATION_MODEL_RUNTIME_DEFAULT. C
callers build the entry with either of two new helpers,
ovstage_config_entry_runtime_default_hierarchy_computation_model()or the
genericovstage_config_entry_uint64(), and pass it toovstage_initialize;
Python callers set it throughStageConfigwhen creating aStage. The
setting is process-scoped and defaults to
OVSTAGE_HIERARCHY_COMPUTATION_MODEL_CPU_INCREMENTAL. - Python:
make_dltensoraccepts adtypelayout override for a non-NumPy
DLPack producer, folding complete trailing dimensions intodtype.lanes. A
Warpvec3fbuffer exported as(N, 3)with one lane can be re-described as
(N,)with 3 lanes without copying the producer buffer. A fold that consumes
every producer axis normalizes to a one-element shape, so a component-only
producer shaped(3,)with one lane becomes(1,)with 3 lanes. 0.1.0
rejected every override on this path withValueError.
Changed
- Reads now enforce the sealing rule the API has always specified: an ordinal
must be sealed before data written at it can be read back. Ordinal-range reads
were not gated at all in 0.1.0 and returned data from ordinals that were still
open; they now validate the changes a range selects. Both read kinds also now
validate while the write floor is still at its initial value of0, a case
0.1.0 skipped, so a write at a positive ordinal is not readable until the
floor advances to cover it. Either failure reports
OVSTAGE_ERROR_WRITE_FLOOR_VIOLATION. Snapshot validation is at the same time
narrowed from the whole attribute column to the paths a query selects, so an
unsealed write to one prim no longer vetoes a read of untouched prims and
reads that 0.1.0 rejected can now succeed. Pending overlaps continue to report
OVSTAGE_ERROR_OP_FAILED, and a range that selects no change still returns
zero groups. - An ordinal-range read can now fail with the new
OVSTAGE_ERROR_OUT_OF_RANGE
status. The current implementation stores only the latest payload per key, so
when a selected(attribute, path)changed again after the range's end, the
only stored value is newer than the range and the interval cannot be
materialized, whether or not that later change is sealed. Latest-snapshot
reads never report this status: theirend_ordinalis not a historical
payload bound, so the current value is exactly what they ask for. ovstage_initializenow validates process configuration instead of ignoring
it. A malformed, duplicate, or unknown entry, or a runtime setting that
conflicts with one already active in the process, returns
OVSTAGE_ERROR_INVALID_ARGUMENT.- When ovstage starts its own runtime, it supplies a fixed internal argument
list, so host process arguments are no longer parsed as runtime options.
Configuration arrives throughOVSTAGE_*environment variables and
ovstage_initialize. A runtime the host started stays host-owned and keeps
whatever configuration the host gave it. - Clone retargets relationship targets, scalar and array path values, and USD
attribute connections that point inside the source subtree; paths outside it
are copied unchanged, so clones keep referencing shared materials and other
shared resources. Cloned attribute values, including relationship targets, are
ordinal-change-tracked. Scene hierarchy changes, such as parent child lists,
still are not. - Fixed-size reads and maps expose a canonical lane-based layout:
ndim == 1, a
leading dimension equal to the transported data-row count, and the complete
per-row tuple width indtype.lanes. Convenience write inputs such as
(N, 3)or(N, 4, 4)are still accepted, but their trailing shape is folded
into lanes and is no longer echoed back on read. A fixed-size write without
index_mapmust now haveshape[0]equal to the logical element count; a
flat(N * L,)one-lane tensor is not inferred asNrows of widthL. - Python: a
ManagedDLTensorreleases what it retains when the capsule is
destroyed, not when the consumer releases the tensor — for a consumed capsule,
as soon as the consumer takes ownership, which is generally earlier.
np.from_dlpack(group.dlpack(0))remains valid while the owning read/map
operation is alive; only a custom producer whosemanager_ctxsolely owns the
backing memory must keep theManagedDLTensoralive as long as the consumer's
view is used. - The headers and guides now specify the
maskbuffer contract (elementiis
biti % 64of wordi / 64; a non-NULL mask must address at least
ceil(count / 64)uint64_twords, because exactly that many are read), the
distinct roles ofcount,index_map, andmask, and that array writes do
not fold trailing dimensions intodtype.lanesthe way fixed-size writes do.
Seeovstage_write_data_tand the writing-attributes guide. - Documented the read representation for scalar
assetattributes populated
through the RENDERING population domain: each prim's value is one fixed
{kDLUInt, 64, 2}element carrying the
{authored-path token, resolved-path token}pair (resolved token0when
unresolved), with attribute semanticNONE. Decode tokens through the shared
path dictionary (path_dictionary_get_strings_from_tokens, Python
PathDictionary.token_to_string). This is transitional and is planned to
change toOVSTAGE_SEMANTIC_ASSET_STRINGbyte rows in a future release.
Fixed
write_attributederived a write's transported row count fromindex_map
rather than from the payload, so a map entry past the payload's rows invented
extra rows and the resulting row width was reported as though the caller had
declared it: a 4-bytefloat32scalar was rejected as
OVSTAGE_ERROR_NOT_SUPPORTEDwith "dtype code=2 bits=32 lanes=1 and 2
byte(s)". Fixed-size writes now take their row count from the tensor's
shape[0]and per-row array writes fromtensor_count, and both range-check
index_mapagainst it. Packed array transport declares no row count, so there
the map still defines the partition; a partition the payload cannot support is
nowOVSTAGE_ERROR_INVALID_ARGUMENTwith a message naming the map.- Most write payload rejections no longer return a bare error code with an empty
message. Missingcount, mutually exclusiveindex_map/mask, an oversized
count, a malformed single source tensor, an out-of-range row selection, and a
payload that does not divide evenly across its rows each now carry a
diagnostic naming the offending input. Per-row (tensor_count > 1) tensor
validation still reports a bareOVSTAGE_ERROR_INVALID_ARGUMENT. - A row width that is not a whole number of
dtypeelements is now
OVSTAGE_ERROR_INVALID_ARGUMENTrather thanOVSTAGE_ERROR_NOT_SUPPORTED: it
reflects a payload cut into the wrong number of rows, not a capability the
build lacks. Genuinely unrepresentable dtypes and over-wide fixed rows remain
OVSTAGE_ERROR_NOT_SUPPORTED. - A write that declares
OVSTAGE_SEMANTIC_MATRIXis no longer restricted to
non-array values with the fixed{kDLFloat, 64, 16}layout. The matrix role
is represented independently of the numeric layout, somatrix3f,matrix4f,
and matrix-valued arrays write and read back with the layout the caller
declared. 0.1.0 rejected them withOVSTAGE_ERROR_OP_FAILED. read_attributeson a USD-populated scalarassetattribute returned
OVSTAGE_ERROR_END_OF_ITERATIONwith no groups when the read covered a single
prim, even though the attribute was discoverable with anOVSTAGE_FILTER_OP_HAS
query and the same read succeeded across more than one prim. Single-prim reads
now return the representation multi-prim reads already produced.- A write whose source tensor lives in CUDA device memory reached storage but
did not mark the written elements as changed, so downstream consumers such as
a renderer never observed the new values; a CUDA-sourcedomni:xformupdate
left the rendered scene unchanged. Such writes now flag the elements they
touch, and wait for their device-side copy to complete before the operation
reports done, where previously it could report completion with the copy still
in flight. - Population no longer drops a render settings
camerarelationship whose
camera prim does not exist yet, so that relationship survives population and
cloning. Other relationships still require their targets to exist.
Relationship targets are now also taken as authored rather than forwarded
through relationship chains. - USD scene-graph instance proxies now carry the reset-transform-stack state of
the prim they stand in for, so a proxy whose source prim resets its transform
stack is populated with the correct transform. - ovstage initializes reliably however the host process was launched. Some
command lines, such as an inline multi-linepython -ccommand or an argument
containing a newline, could abort initialization. - Python:
write_attribute/write_attributesnow validatecount,
index_map, andmasktogether and raiseValueErrorrather than reaching
the runtime. Acountlarger thanlen(index_map), or larger than themask
could index, made the runtime read past the caller's buffer; acountof0
computed from an empty selection silently widened a write to the whole query
instead of writing nothing; a negativecountwrapped through theuint32
field to roughly2^32; and amaskwithout an explicit non-zerocount
produced a payload the runtime rejected with an empty message. To write more
of the query, lengthenindex_maprather than raisingcount, or omit
countto write the whole query. - Python:
wait,fetch, andflush_logtimeouts are validated instead of
being wrapped through theuint64field, where-1happened to become
TIMEOUT_INFINITE. A non-integer now raisesTypeError, and a negative or
oversized value raisesValueError, before the pending operation and its
input keepalives are consumed. - Python: a rejected enqueue captures its diagnostic when the operation is
created rather than when it is waited on, so
op1 = write(...); op2 = write(...); op1.wait()no longer reportsop2's
message or none at all. - Python:
numpy_to_dldatatypenow accepts every dtype spelling numpy itself
accepts. A numpy scalar type (np.float32), a character code ("f4"), and a
builtin (float) were all rejected withValueError: Unsupported numpy dtype for DLPack; only an exact dtype-name string or annp.dtypeinstance
resolved. Inputs are now normalized throughnp.dtypefirst. Dtypes DLPack
has no mapping for still raiseValueError, as doesNone. - Python: invalid DLPack lane counts are rejected instead of producing a
malformed layout.numpy_to_dldatatyperaises forlanesoutside
[1, 65535]rather than wrapping negative values through theuint16field,
anddltensor_to_numpyrejects a zero lane count and refuses to build a view
larger than the numpy array amake_dltensortensor wraps. A non-numpy
make_dltensorlayout override likewise requires positive source and
requested bit widths. Tensors from other producers still get no size check;
see the limitation below. - Python: writing into a read-only DLPack view no longer raises
SystemError: error return without exception setand no longer leaks the
tensor's retained resources. This affected any exception propagating while an
exported tensor was released, not only read-only writes. - Python:
Stage.destroy()reports a failed native destruction by raising
OvstageErrorinstead of ignoring the status. When instance destruction
fails it keeps its native handle and process reference, so the caller can
retry without losing ownership state. - Python: on Windows, a bundled plugin could fail to load because its own
runtime dependency sits in a siblingplugins/<name>.libdirectory that was
not on the DLL search path. Every immediateplugins/*subdirectory is now
registered with the Windows loader when the library is loaded. - The package ships its
LICENSE, with corrected wheel metadata.
Limitations
The 0.1.0 limitations still apply, except where superseded above.
New in 0.1.1
- An ordinal-range read that selects a key with a retained change after the
range's end returnsOVSTAGE_ERROR_OUT_OF_RANGE: the payload for that
interval cannot be materialized, so the read yields neither change membership
nor data.
Not new — pre-existing in 0.1.0, documented here for the first time
- A latest-snapshot read returns the current committed value rather than the
value as of the requestedend_ordinal, so it can return a payload written at
a higher ordinal, and nothing in the result marks that this happened. - Python:
dltensor_to_numpysizes its view from the tensor's ownshapeand
dtype.lanes, and can check that against the real buffer only for a tensor
make_dltensorwrapped around a numpy array. For any other producer (a
non-numpy DLPack export, or a hand-builtDLTensor), a descriptor that
overstates its payload yields a view extending past the buffer, and reading
it is out of bounds. Attribute data ovstage itself stores cannot carry an
out-of-range lane count, since the write path caps lanes at 255. - Writing to a USD-populated scalar
assetattribute is not supported:
OVSTAGE_SEMANTIC_ASSET_STRINGbyte-row payloads are rejected with a type
mismatch, and raw token-pair writes are not validated end-to-end. asset[]attributes are not populated by generic authored-attribute
population and are not readable.