Async Operations, Status, and Errors#
ovrtx enqueue operations are internally asynchronous and stream-ordered. Python
offers blocking convenience methods plus *_async variants. C enqueue
functions return operation ids that must be waited before fetching results or
assuming side effects are visible.
Python Operations#
Operation.wait(timeout_ns=0) polls. Operation.wait() blocks indefinitely.
Operations that produce data use a two-phase lifecycle: wait returns a pending
fetch object, then fetch() retrieves the result.
op = renderer.open_usd_async(TEST_BASE_PATH)
# TODO: Restore the assertion once the packaged open_usd_async wrapper returns _VOID_RESULT.
op.wait()
assert "/World/Plane" in renderer.query_prims(attribute_filter_mode=ovrtx.AttributeFilterMode.NONE)
# step_async() returns an Operation. wait() resolves to a PendingFetch,
# whose fetch() produces the RenderProductSetOutputs that step() would
# have returned synchronously.
op = renderer.step_async(
render_products={"/Render/Camera"},
delta_time=1.0 / 60,
)
pending = op.wait()
products = pending.fetch()
C Waits#
In C, check the immediate enqueue status first, then wait on op_index. Some
errors, such as a missing USD file during load, are reported only when the
operation is waited.
// Load a USD layer into the renderer.
//
// As well as just passing a URI to an existing layer, we could pass a USDA
// string in order to compose a Stage at runtime. This can be very useful
// for dynamically creating the RenderProducts etc. that define the render
// output rather than editing the original layer to add them.
//
// A real application might want to load the USD layer and traverse it to
// find either existing RenderProducts, and/or Cameras and allow the user to
// select which one to render, and which RenderVars to output.
char const* usd_url = "https://omniverse-content-production.s3.us-west-2.amazonaws.com/Samples/Robot-OVRTX/robot-ovrtx.usda";
std::cerr << "Adding " << usd_url << " at root..." << std::endl;
ovrtx_enqueue_result_t enqueue_result =
ovrtx_open_usd_from_file(renderer, {usd_url, strlen(usd_url)});
// This operation is asynchronous as loading the USD may take a long time.
// We'll just poll every 100ms till it's done.
ovrtx_op_wait_result_t wait_result;
result = ovrtx_wait_op(
renderer, enqueue_result.op_index, ovrtx_timeout_t{0}, &wait_result);
if (check_and_print_error(result, "wait_op")) {
ovrtx_destroy_renderer(renderer);
return 1;
}
while (ovrtx_wait_op(renderer, enqueue_result.op_index, ovrtx_timeout_t{0},
&wait_result).status == OVRTX_API_TIMEOUT) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cerr << "USD loaded." << std::endl;
Status Queries#
Status queries take a point-in-time snapshot of a pending operation. They are useful for progress bars and logs while waiting for long-running work such as USD loading, shader compilation, stage queries, or render steps.
def wait_with_status(op: ovrtx.Operation, label: str):
result = op.wait(timeout_ns=1_000_000_000)
while result is None:
print_operation_status(label, op.query_status())
result = op.wait(timeout_ns=1_000_000_000)
return result
static bool wait_with_status(ovrtx_renderer_t* renderer,
ovrtx_op_id_t op_id,
std::string_view label) {
ovrtx_op_wait_result_t wait_result {};
ovrtx_result_t result =
ovrtx_wait_op(renderer, op_id, ovrtx_timeout_t {1000000000}, &wait_result);
while (result.status == OVRTX_API_TIMEOUT) {
if (print_operation_status(renderer, op_id, label)) {
return true;
}
result = ovrtx_wait_op(renderer, op_id, ovrtx_timeout_t {1000000000}, &wait_result);
}
if (check_and_print_error(result, "wait_op")) {
return true;
}
return print_wait_errors(wait_result);
}
Python Operation.query_status() is available only before wait() consumes
the operation context. In C, each successful ovrtx_query_op_status()
call must be paired with ovrtx_release_op_status().
Error Handling#
Python methods raise RuntimeError on API failures or async operation
failures. In C, every API result must be checked. Error strings returned by
ovrtx_get_last_error() are valid only until the next API call on the
same thread.
The minimal C example uses a helper that works with both synchronous and enqueue results:
template <typename ResultT>
static bool check_and_print_error(ResultT const& result,
std::string_view operation) {
if (result.status == OVRTX_API_ERROR) {
ovx_string_t error = ovrtx_get_last_error();
if (error.ptr && error.length > 0) {
std::cerr << "ovrtx " << operation << " failed: "
<< std::string_view(error.ptr, error.length) << std::endl;
} else {
std::cerr << "ovrtx " << operation << " failed" << std::endl;
}
return true;
}
return false;
}
C Log Callback#
The C API can install a process-global log callback. Use it for application logging, CI diagnostics, or routing ovrtx messages into another logging system.
// First pass: receive all messages (NULL channel filter).
g_message_count.store(0);
ovrtx_result_t r = ovrtx_set_log_callback(OVRTX_LOG_INFO,
nullptr, // NULL = all channels
&count_messages,
&g_message_count);
ASSERT_API_SUCCESS(r.status);
// Reset first so the renderer is in a clean state.
ovrtx_enqueue_result_t eq = ovrtx_reset_stage(renderer_);
ASSERT_API_SUCCESS(eq.status);
ovrtx_op_wait_result_t wait_result;
ASSERT_API_SUCCESS(ovrtx_wait_op(renderer_, eq.op_index, ovrtx_timeout_infinite, &wait_result).status);
do_open_usd();
// Ensure pending log messages have been delivered through the callback.
ovrtx_timeout_t flush_timeout{5'000'000'000ull};
ovrtx_flush_log(flush_timeout);
int observed_any_channel = g_message_count.load();
// Second pass: install a high default threshold and an explicit low
// threshold for a channel prefix that cannot match any real channel.
g_message_count.store(0);
std::string bogus = "this.channel.does.not.exist.42=info";
ovx_string_t filter{bogus.c_str(), bogus.size()};
r = ovrtx_set_log_callback(OVRTX_LOG_FATAL, &filter, &count_messages, &g_message_count);
ASSERT_API_SUCCESS(r.status);
// Re-run the same work and flush — the callback should not fire.
eq = ovrtx_reset_stage(renderer_);
ASSERT_API_SUCCESS(eq.status);
ASSERT_API_SUCCESS(ovrtx_wait_op(renderer_, eq.op_index, ovrtx_timeout_infinite, &wait_result).status);
do_open_usd();
ovrtx_flush_log(flush_timeout);
int observed_bogus_filter = g_message_count.load();
// Disable the callback before the renderer is torn down.
ovrtx_set_log_callback(OVRTX_LOG_INFO, nullptr, nullptr, nullptr);
The channel filter is a comma-separated list of channel_prefix=level rules.
Accepted levels are verbose/debug, info, warn/warning,
error, and fatal. The longest matching channel prefix wins.