Runtime

Runtime is used to access and stream scenes from the Umbra cloud. Scenes have to be successfully imported before they can be streamed. Scenes are identified with a locator, which is generated from the import process. We have made couple sample scenes available for testing purposes.

Runtime is implemented in low-level native code and can run in most environments. Runtime does not require a dedicated graphics hardware or a graphics API in order to function. Runtime does require an active internet connection to work.

Runtime objects

Creation

To get started, you must create a runtime object. To create a runtime object, you must call UmbraRuntimeCreate. A single runtime object supports multiple scenes and views, so you never need to create more than one runtime object. The runtime object must be kept alive for the entire duration of the application.

Platform services

The runtime object uses various platform services such as memory allocator during its lifetime. The default platform services are:

  • The default memory allocator uses malloc and free.
  • The default HTTP implementation uses curl.
  • The default logger logs to stdout and stderr.

You can replace the default implementations by calling UmbraSetAllocator, UmbraSetHttp, UmbraSetLogger, respectively.

Specifying supported texture formats

For optimal rendering performance, the runtime object must know which texture formats are supported by the underlying graphics hardware. You must specify the supported formats in the UmbraEnvironmentInfo structure.

If the application does not render anything, you may use UmbraTextureSupportFlags_All.

Local caching

The runtime object can optionally save streamed assets to a local cache. Caching assets can reduce network traffic and improve streaming performance. The location and the size of the local cache are specified by the user in the UmbraEnvironmentInfo structure. Platforms with access to a local file system, we recommend setting up the local cache. The local cache directory must be readable and writable for the duration of the runtime object.

Updating

The runtime object keeps track of various internal state, including but not limited to, updating LODs, updating streaming and computing visibility. To update the internal state of the runtime object, you must periodically call UmbraRuntimeUpdate. We recommend calling UmbraRuntimeUpdate at least once per frame update.

Destruction

To destroy the runtime object, call UmbraRuntimeDestroy. You must destroy all instances of UmbraScene, UmbraView and UmbraSceneCopy before calling UmbraRuntimeDestroy.

Scene objects

A scene object is a handle to an optimized output scene.

Creation and connection

To connect to an imported scene, you need to create a scene object by calling UmbraSceneCreate. Connecting to a scene requires an API key and the locator to the imported scene.

In order to know if the scene connection was successful, in progress or failed, you can call UmbraSceneGetConnectionStatus to find out.

Once the scene is connected, you may use functions such as UmbraSceneGetInfo to get scene specific information, such as the bounding box of the input scene.

Multiple scenes

You may connect to any number of scenes simultaneously, using the same API. If the scenes are not in the same coordinate system, you can transform the scenes yourself with UmbraSceneSetTransform.

Destruction

To destroy the scene object, call UmbraSceneDestroy.

View objects

A view object is used for querying assets from connected scenes. You can use view objects either for rendering or filtering. Query results are accessed with the asset streaming API.

Creation

To create a view object, you must call UmbraViewCreate.

Update for rendering

To use the view object for rendering, you must call UmbraViewUpdateRendering. We recommend calling UmbraViewUpdateRendering once per frame update, or at least when the camera parameters have changed.

Quality

The quality parameter in UmbraViewUpdateRendering should be between 0.0 (lowest possible quality) and 1.0 (highest possible quality). We recommend starting at 0.5 before experimenting with other values.

You may dynamically adjust the quality during runtime. This may be useful for dynamically adjusting the rendering quality based on available memory in the graphics hardware.

Update for filtering

To use the view object for filtering, you must call UmbraViewUpdateFilter. Filtering may be used for optimizing collision detection around a specific region.

Getting the list of renderables

Once the update has been called, to get the list of UmbraRenderable, you must call UmbraViewNextRenderables. This list may be empty or incomplete if the asset streaming has not started or finished yet.

Inside the UmbraRenderable structure, UmbraRenderable::mesh is the pointer to the mesh that you should render. This is the same pointer you have assigned during asset loading.

Multiple views

You may create multiple views using the same API. This may be useful in multi-camera rendering scenarios, such as split-screen local multiplayer.

Destruction

To destroy the view object, call UmbraViewDestroy.

Asset streaming

Once the application has created the runtime objects, the scene objects, and the view objects, the runtime object can begin streaming assets from the Umbra cloud. If the application has setup local caching, the runtime object may also stream assets from the cache.

Since the runtime object does not interact with graphics hardware, it is the applications responsibility to perform all asset loads and unloads as they are made available by the runtime object. Doing so eventually achieves the optimal end result, for example the best visual quality for a given quality parameter. Doing so also prevents the memory usage from exceeding the limits set the environment, especially over longer periods of time.

The runtime object maintains queues for asset loads and asset unloads. The loads and unloads may be added, removed, reordered every time the runtime object is updated. As an example, the loads may be prioritized such that the assets closest to the camera are loaded first.

To get the next asset load or unload, you must call UmbraRuntimeNextAssetLoad or UmbraRuntimeNextAssetUnload, respectively. Both functions may return NULL, if the queues are empty.

Asset loads

To load an asset, you must do the following:

As an example, an unique identifier could be a pointer to the applications mesh object.

Parallelizing asset loads

While the calls to UmbraAssetLoadPrepare and UmbraAssetLoadFinish must happen in that order, it is possible to delay the call to UmbraAssetLoadFinish to some later time. This property allows the application to perform asset loads out-of-order and most importantly, in parallel.

As an example, you can prepare an asset and submit it to your own work queue, which is processed by your worker threads. Once the work is done, the main thread is notified and the asset is finished.

Mesh loads

To get information about the mesh asset, you must call UmbraMeshLoadGetInfo. You may use the returned information to allocate enough memory in order to store it.

To load a mesh, you have two choices: either you load the entire mesh with a single call to UmbraMeshLoadGetData or the mesh in multiple parts using the mesh streaming API (see UmbraMeshStreamSetBuffers, UmbraMeshStreamDone, and UmbraMeshStreamNext).

If the environment supports multithreading, we recommend using UmbraMeshLoadGetData in conjunction with the parallel asset load API. We do not recommend this function in applications with real-time constraints, since decoding the whole mesh may take longer than a single frame.

If the environment does not support multithreading, you should use the streaming API to satisfy real-time constraints.

In either case you must declare the destination buffers for the mesh decoder. See the notes at UmbraMeshLoadGetData how the buffers must be declared.

If the buffers are located in a special kind of memory that does not support write combining (e.g. mapped from graphics hardware), you should use UmbraBufferFlags_UncachedMemory to avoid poor decoding performance.

Meshes contain a reference to a prepared material asset. You can use the reference to link the assets together in your application.

Texture loads

To get information about the texture asset, you must call UmbraTextureLoadGetInfo. You may use the returned information to allocate enough memory in order to store it.

To load a texture, you must call UmbraTextureLoadGetData.

We recommend decoding textures with the parallel asset load API, if the environment supports it.

If the buffers are located in a special kind of memory that does not support write combining (e.g. mapped from graphics hardware), you should use UmbraBufferFlags_UncachedMemory to avoid poor decoding performance.

The decoded textures will be in the formats specified by Specifying supported texture formats.

Materials

To get information about the material asset, you must call UmbraMaterialLoadGetInfo.

Materials contain references to other prepared texture assets. You can use the references to link the assets together in your application.

Asset unloads

An asset may be unloaded due to the following reasons:

  • The view object parameters have changed such that the asset is no longer required.
  • The scene object containing the asset has been destroyed.

To determine which asset needs to be unloaded from the application, you can use UmbraAssetUnloadGetType and UmbraAssetUnloadGetUserPointer.

Once the asset is removed from the application, you must call UmbraAssetUnloadFinish to declare to the runtime object that the asset is no longer available.