Skip to content

Scene Runtime

Mikhail Agapov edited this page Aug 10, 2023 · 9 revisions

Concept

image

Scene downloading is initiated from Unity's ECS systems. The downloading itself is performed via the usual UnityWebRequest. ISceneFactory is responsible for doing this in an async manner. ISceneFactory exposes additional overloads to create scenes from files for testing purposes.

Apart from initiating Unity's web requests the scene lifecycle is thread agnostic and, thus, executes in a separate thread. It's a vital constituent of the performance the project is able to achieve:

  • Each instance of SceneEngine is relying on the thread pool
  • When the call to Engine is awaited its continuation is scheduled on the thread pool

⚠️ A single scene does not utilize a single thread. Threads will be changed according to the thread pool after each await. It means a developer can't make any assumptions about thread consistency.

  • API implementations must be thread-agnostic
  • Resources shared between them must be thread-safe

The scene itself is represented by ISceneFacade. It has the following capabilities:

  • StartUpdateLoop
  • SetTargetFPS: the update frequency of JS Scene is controlled from C#
  • DisposeAsync

When the scene is created its life cycle is controlled by ECS. ISceneFacade is added as a component to the entity representing the scene.

The process of scene downloading is described in detail in a separate section.

When the scene code along the modules is loaded SceneRuntimeImpl is responsible for creating a separate instance of the execution engine via ClearSript.

⚠️ There is no such concept as engine pooling: every scene creates a unique instance, and when it goes out of scope the instance is disposed of. It creates a considerable GC pressure but ScriptEngine is not reusable. ClearScript takes care of disposing of unmanaged resources.

Proceed to Systems to familiarize yourself with the ECS systems that manage the scenes' life cycle.

Scene Downloading

TODO insert a principle scheme

CRDT

We have our own custom allocation-free highly-performance implementation of the CRDT protocol.

Core characteristics:

  • The process executes off the main thread
  • PoolableCollections based on ArrayPool<T>.Shared hide the complexity of having individual pools for different collection types and provide thread-safety out of the box
  • No temporary allocations: Messages processing is driven by the implementation of IMemoryOwner<byte> that uses prewarmed pools under the hood. When the message is disposed of the rented buffer returns to the pool. This pool is thread-safe
  • State storing is based on structures that are designed to be as lightweight as possible
  • Messages deserialization is based on ReadOnlyMemory<byte> that is continuously advanced forward to prevent allocations
  • Deserialization uses ByteUtils to slice memory regions into typed structures in an unsafe manner. This process is much faster than the managed one and is close to reinterpret_cast from C

CRDT - ECS Bridge

Messages Reconciliation

SDK components

Synchronization with ECS

Engine API Implementation

Clone this wiki locally