Renderer Configuration#
The renderer owns ovrtx’s GPU resources, stream-ordered work queue, and rendering pipeline. In standalone mode it also owns a runtime stage that you load USD into. In attached mode (ovrtx 0.4+) it renders scene state managed by an external ovstage instance instead. Create one renderer before loading USD or stepping RenderProducts.
Creating a Renderer#
# Create the Renderer and attach the stage that owns scene data.
print("Creating renderer. The first run of the application will take some time as shaders are compiled and cached...", file=sys.stderr)
renderer = ovrtx.Renderer()
stage = ovstage.Stage("ovrtx.example.minimal")
renderer.attach_ovstage(stage)
print("Renderer created.", file=sys.stderr)
Configuration entries are passed through RendererConfig:
config = ovrtx.RendererConfig(
sync_mode=True,
log_file_path=str(output_dir / "config-test.log"),
log_level="info",
)
renderer = ovrtx.Renderer(config=config)
assert any(component > 0 for component in renderer.version)
assert renderer.config.sync_mode is True
assert renderer.config.log_level == "info"
assert renderer.config.log_file_path == str(output_dir / "config-test.log")
// Create the renderer, providing configuration settings.
//
// The STATIC ovrtx loader resolves the ${executable_dir} token to the running
// executable's directory, so we hand it the token instead of computing the path
// in client code. ovrtx_setup_runtime() links the package bin beside the exe as
// "ovrtx/".
ovx_string_t ovrtx_package_root = {
OVX_CONFIG_EXECUTABLE_DIR_TOKEN "/ovrtx",
sizeof(OVX_CONFIG_EXECUTABLE_DIR_TOKEN "/ovrtx") - 1};
ovrtx_config_entry_t config_entries[] = {
ovrtx_config_entry_binary_package_root_path(ovrtx_package_root),
};
ovrtx_config_t config {};
config.entries = config_entries;
config.entry_count = sizeof(config_entries) / sizeof(config_entries[0]);
std::cerr << "Creating renderer. The first run of the application will take some time as shaders are compiled and cached..." << std::endl;
result = ovrtx_create_renderer(&config, &renderer);
if (check_and_print_error(result, "create_renderer")) {
return 1;
}
std::cerr << "Renderer created." << std::endl;
Version and config-entry setup use ovrtx_config_t:
uint32_t major = 0;
uint32_t minor = 0;
uint32_t patch = 0;
ovrtx_get_version(&major, &minor, &patch);
ovrtx_config_entry_t entries[] = {
ovrtx_config_entry_log_level(ovx_str("info")),
ovrtx_config_entry_sync_mode(true),
};
ovrtx_config_t config{entries, 2};
ovrtx_renderer_t* configured_renderer = nullptr;
ovrtx_result_t create_result = ovrtx_create_renderer(&config, &configured_renderer);
ASSERT_API_SUCCESS(create_result.status);
ASSERT_NE(configured_renderer, nullptr);
ovrtx_destroy_renderer(configured_renderer);
Configuration Entries#
Common configuration entries include:
|
Description |
|---|---|
|
Write renderer logs to a file. |
|
Set log verbosity. |
|
Point to a custom binary package root. May include
|
|
Keep shared graphics resources alive after the last renderer. |
|
Restrict renderer-level CUDA-visible devices. |
|
Select the Vulkan backend where supported. |
|
Motion BVH mode: |
Config-entry helper |
Description |
|---|---|
Write renderer logs to a file. |
|
Set log verbosity. |
|
Point to a custom binary package root. May include
|
|
Keep shared graphics resources alive after the last renderer. |
|
Restrict renderer-level CUDA-visible devices. |
|
Select the Vulkan backend where supported. |
|
Motion BVH mode: |
Renderer-level active_cuda_gpus must be compatible with any per-RenderProduct
deviceIds allow-list. Refer to RenderProduct Device Pinning.
Runtime Package Layout#
With dynamic linking, ovrtx expects the binary package bin layout to stay
together next to libovrtx-dynamic.so or ovrtx-dynamic.dll. The runtime
package includes directories such as cache, library, libs, mdl,
plugins, rendering-data, and usd_plugins.
Set binary_package_root_path only when static linking ovrtx or when a custom
deployment layout separates the loader library from the package directories.
When the package bin/ directory lives next to your executable, pass
OVX_CONFIG_EXECUTABLE_DIR_TOKEN ("${executable_dir}") instead of
resolving the executable directory in client code.
Multi-Renderer Processes#
For processes that create and destroy multiple renderers, initialize ovrtx once up front when using the C API, then shut it down once all renderers are gone.
// The STATIC ovrtx loader resolves the ${executable_dir} token to the running
// executable's directory; ovrtx_setup_runtime() links the package bin beside
// the exe as "ovrtx/", which we hand the loader as the binary package root.
ovx_string_t ovrtx_package_root = {
OVX_CONFIG_EXECUTABLE_DIR_TOKEN "/ovrtx",
sizeof(OVX_CONFIG_EXECUTABLE_DIR_TOKEN "/ovrtx") - 1};
ovrtx_config_entry_t ovrtx_config_entries[] = {
ovrtx_config_entry_binary_package_root_path(ovrtx_package_root),
ovrtx_config_entry_selection_outline_enabled(true),
ovrtx_config_entry_selection_outline_width(4),
ovrtx_config_entry_selection_fill_mode(OVRTX_SELECTION_FILL_MODE_GROUP_FILL_COLOR),
};
ovrtx_config_t ovrtx_config = {};
ovrtx_config.entries = ovrtx_config_entries;
ovrtx_config.entry_count =
sizeof(ovrtx_config_entries) / sizeof(ovrtx_config_entries[0]);
// Publish OVRTX's USD plugin paths before ovstage initializes the shared USD
// runtime. The process-wide USD schema registry only discovers plugins once.
ovrtx_result_t result = ovrtx_register_schema_paths(&ovrtx_config);
if (check_and_print_error(result, "register_schema_paths")) {
return 1;
}
// The STATIC ovstage loader resolves ${executable_dir} the same way ovrtx does.
// ovstage_setup_runtime() links the package bin beside the exe as "ovstage/"
// (next to "ovrtx/").
ovx_string_t ovstage_package_root = {
OVX_CONFIG_EXECUTABLE_DIR_TOKEN "/ovstage",
sizeof(OVX_CONFIG_EXECUTABLE_DIR_TOKEN "/ovstage") - 1};
ovstage_config_entry_t stage_config_entries[] = {
ovstage_config_entry_binary_package_root_path(ovstage_package_root),
};
ovstage_config_t stage_config {};
stage_config.entries = stage_config_entries;
stage_config.entry_count = sizeof(stage_config_entries) / sizeof(stage_config_entries[0]);
ovstage_api_status_t stage_init_status = ovstage_initialize(&stage_config);
if (stage_init_status != OVSTAGE_OK) {
print_ovstage_error(nullptr, stage_init_status, "initialize");
return 1;
}
result = ovrtx_create_renderer(&ovrtx_config, &renderer);
if (check_and_print_error(result, "create_renderer")) {
ovstage_shutdown();
return 1;
}
auto cleanup = [&](int exit_code) {
int result_code = exit_code;
if (camera_query != OVSTAGE_INVALID_QUERY_HANDLE) {
if (wait_ovstage_op(stage,
ovstage_release_query(stage, camera_query),
"release_query")) {
result_code = 1;
}
camera_query = OVSTAGE_INVALID_QUERY_HANDLE;
}
if (stage_attached) {
result = ovrtx_detach_ovstage(renderer);
if (check_and_print_error(result, "detach_ovstage")) {
result_code = 1;
}
stage_attached = false;
}
if (stage) {
ovstage_api_status_t stage_result = ovstage_destroy_instance(stage);
if (stage_result != OVSTAGE_OK) {
print_ovstage_error(stage, stage_result, "destroy_instance");
result_code = 1;
}
stage = nullptr;
}
if (renderer) {
result = ovrtx_destroy_renderer(renderer);
if (check_and_print_error(result, "destroy_renderer")) {
result_code = 1;
}
renderer = nullptr;
}
// Release the ovstage static loader (unloads ovstage.dll). Safe even if
// ovstage_initialize failed or was never reached.
ovstage_shutdown();
return result_code;
};
// Register the per-group outline/fill style before any picking happens.
if (!configure_selection_style(renderer)) {
return cleanup(1);
}
On Linux headless systems, repeatedly creating and destroying renderers can
force shared graphics resources to unload and reload. If that causes native
driver crashes, configure keep_system_alive=True and initialize ovrtx before
creating renderers. If the issue persists, set
VK_LOADER_DISABLE_DYNAMIC_LIBRARY_UNLOADING=1 in the process environment.
Cleanup#
Python releases renderer resources when the Renderer object is destroyed.
In C, cleanup is explicit:
// Unmap output
ovrtx_cuda_sync_t no_sync = {};
result = ovrtx_unmap_render_var_output(
renderer, rendered_output.map_handle, no_sync);
if (check_and_print_error(result, "unmap_render_var_output")) {
ovrtx_destroy_results(renderer, step_result_handle);
return cleanup(1);
}
// Clean up resources (ovrtx will warn if results are leaked)
result = ovrtx_destroy_results(renderer, step_result_handle);
if (check_and_print_error(result, "destroy_results")) {
return cleanup(1);
}