RAMSES Documentation  27.0.130
Information for RAMSES users and developers
Scene Referencing and DCSM

The concept

There are two main roles in the DCSM/Ramses ecosystem:

  • content provider (RamsesClient + DcsmProvider)
    • create and offer content
    • modify content based on application logic
  • content consumer (ramses::RamsesRenderer + ramses::DcsmContentControl)
    • accept/assign content from providers
    • control content rendering state based on application logic

If one application implements both of these roles, it has full control over the rendered result. If the provider and consumer are two different applications then each takes care of its own part and thanks to DCSM and Ramses a large number of use cases can be achieved without any additional communication channel.

Now imagine a situation where there are 3 participants, each being a separate application:

  • content provider A
    • provides content not knowing where and how it ends up used (e.g. navigation map)
  • content provider/consumer B
    • accepts content from A, uses it inside its own content and offers the result as new content (e.g. navigation map extended with additional overlay elements)
  • content consumer C
    • accepts the single composited content from B and controls its rendering state

The participant B represents a new role which combines the two basic roles of provider and consumer. The important aspect to realize here is that C does not know anything about A and does not care about its content, it only takes content from B. Whether content from B nests other contents or not is irrelevant for C. Using this content nesting approach allows a nice and clearly defined separation of responsibilities between various components. In theory it can also scale to arbitrary number of nesting layers and arbitrary number of contents nested in each layer.

The concept vs Ramses

This concept works well with DCSM, the provider/consumer layer in the middle simply implements both ramses::DcsmProvider and ramses::DcsmConsumer. However applying this concept on Ramses level reveals a problem - the content to be nested coming from lower layer carries a very limited set of information - content ID, scene ID and maybe some DCSM metadata. Also there is typically just one ramses::RamsesRenderer for given content and it is owned by a consumer which in case of nesting is in the top layer, so it is inaccessible from any other layer.

Scene Referencing

To tackle the problem Ramses provides a scene referencing API which allows any scene to 'reference' another scene just by its scene ID, this establishes a master scene and referenced scene relationship. It is important to realize that this does NOT expose any of the referenced scene's Ramses objects (meshnodes, appearances, ...) to the master scene. Instead the API allows to change the referenced scenes rendering states similar to the ramses::RendererSceneControl API, i.e. rendering state, render order, data linking etc., see the ramses::Scene and ramses::SceneReference API docs for details. Even though limited, this already gives a number of possibilities how to composite/nest scenes together.

Overview of the example above, now with scene referencing:

  • content provider A
    • creates and publishes sceneA (Ramses scene ID) offered in contentA (DCSM content ID) for categoryB (DCSM category ID)
  • content provider/consumer B
    • owns categoryB and accepts contentA with sceneA
    • creates and maintains its own sceneB
    • creates scene reference for sceneA in sceneB (sceneB becomes master of sceneA)
    • maintains sceneA's states using scene referencing API
    • publishes sceneB offered in contentB for categoryC
  • content consumer C
    • owns categoryC and accepts contentB with sceneB
    • maintains contentB's states using content control API

Consumer C knows nothing about provider A on application level which is one of the key goals of the initial concept. However down there, in consumer C's renderer, all the data is available to render sceneA as part of its master sceneB. Scene referencing is essentially a remote control of renderering states limited to the scene being referenced.

In order to make all this technically possible there are several rules, guarantees and limitations:

  • a scene can be referenced by one master only at a time
  • RamsesRenderer guarantees that referenced scene will never have higher rendering state than its master (higher state can however be requested from API and will be just kept back)
  • RamsesRenderer guarantees to show/hide master scene with all its references at once (assuming conditions for each scene involved to be ready/showable are met)
  • referenced scene inherits display mapping and display buffer assignment from its master
  • referenced scene's render order is relative to its master scene's render order

Check out the scene referencing example in Ramses SDK to get the feeling of how to use the API for a basic use case.

Scene Referencing events

The mechanism of events dispatching is used in several Ramses APIs and it is not different with scene referencing. With scene referencing however we deal with two remote participants (referenced scene's provider and consumer with renderer), both contributing to the overall state. This makes it even more important to use the events carefully as means of synchronization and correct sequence of commands.

One event in particular can be helpful to achieve well defined behavior - a versioned flush applied event (ramses::IClientEventHandler::sceneReferenceFlushed). As stated above master scene has no access to referenced scene's internals, it can only request state changes for the scene as a whole on renderer's side. But Ramses scene can be flushed with a version tag, the said event is emitted whenever a referenced scene is flushed with version tag and that flush is fully applied to the scene on renderer's side. The version tag itself is a user defined value, so there can be an agreement how to interprete the value by the application logic (see ramses::Scene::flush).

Understanding READY state

The READY state is probably the most important state and should always be used as synchronization point whenever there is need to show a scene/content 'at once' (with minimal delay), Ramses guarantees to show any given READY scene in the very next frame after the show request. This is essential if a collection of scenes/contents need to appear together, which is probably the case whenever scene referencing is used. There are however different meanings of 'ready' and in the context of scene referencing good understanding might be necessary to properly implement some more advanced use cases.

There are two basic types of READY, one in the DCSM context (ramses::EDcsmState::Ready) and the other in Ramses rendering context (ramses::RenderState::Ready). Even though seemingly independent they need to be considered both together by any content consumer (including the middle layer consumer using scene referencing).

  • DCSM READY (ramses::DcsmProvider) is just a signal between DCSM consumer/provider
    • consumer requests READY, provider decides on its own when it is READY and sends signal back
  • Ramses scene READY (ramses::RendererSceneControl) is more of a technical state
    • a scene is READY when it is mapped to display, assigned to display buffer, its resources are uploaded and it is ready to be shown within a single frame

Then there is the ramses::DcsmContentControl which is already implemented in a way that combines the two for convenience:

  • request READY -> sends DCSM ready request and at the same time scene control ready request for scene associated with the content
  • reports READY <- only when both DCSM content marked as ready by provider and the scene reported as ready from Ramses

Typical example of a desired sequence of state requests/events would then look like this:

Provider Consumer/Provider (master scene) Consumer with renderer
create categoryB create categoryC
prepare sceneA and contentA prepare sceneB and contentB set up renderer
publish sceneA and offer contentA for categoryBpublish sceneB and offer contentB for categoryC
assign contentA assign contentB
create sceneA reference
request contentB READY
request contentA READY (sceneB gets uploaded by Ramses internal logic)
update sceneA (optionally flush with version tag)
mark contentA READY
(optionally wait for version tag)
request sceneA reference READY
... (sceneA gets uploaded by Ramses internal logic)
mark contentB READY
request sceneA reference RENDERED (see note below)
announce contentB will be RENDERED
set contentB to RENDERED
(both sceneA and sceneB are shown together)

The important rule here is always prepare and make all the content you are nesting READY and only then report it as READY. Concretely in this example it means when contentB is requested to be READY via DCSM, make sure that contentB is DCSM READY and also all scenes nested into it were requested to be READY (both DCSM and as scene reference) and reported back as READY (again both DCSM and scene reference state), only then report the contentB as READY to its consumer.

Note regarding requesting the scene reference RENDERED: remember that a referenced scene's rendering state on renderer's side will be kept back to never be higher than master scene's state so it is safe to request RENDERED right away, it can even replace the READY request (state change events will anyway be reported one by one, so there will be event that scene reference is READY even when requested RENDERED right away).