Home -> Threading Model
Threading Model
The Cobalt Renderer API has some thread safety guarantees, however it does not offer complete thread safety for all operations in all circumstances. As a guiding rule, a given object should only be built on one thread, or have its modifications externally synchronized. IE, don't create an ITextureBuffer2D and call SetInitialData on one thread, then call AllocateMemory on another. In this case, you would need to externally synchronize. When following this guidance, operations are thread safe, with the following notable exceptions:
- Binding/unbinding resources. While calls to resource binding methods like BindTexture and BindResourceArray can be made on separate nodes concurrently, it is currently unsafe to call methods which modify the resource binding state on a single node concurrently. This may be changed in the future.
- Modifying state values. While calls to state methods like SetStateValue and ResetStateValue can be made on separate nodes concurrently, it is currently unsafe to call methods which modify state values on a single node concurrently. This may be changed in the future.
- Modifying IRenderableNode state. It is currently unsafe to modify the vertex/index buffer bindings for the same IRenderableNode concurrently from two different threads. As this can only be done before an IRenderableNode is first added to the render tree, this will not typically be an issue, and is treated as a case of building an object. Some updates can be performed after an IRenderableNode has been built and is in use however, such as through SetVertexCount. Torn states may be created if this occurs concurrently from multiple threads, so the modification of an IRenderableNode should be externally synchronized.
Despite the threading rules given above, there is one important synchronization rule within the scope of a single renderer. The StartNewFrame method must be called in complete exclusion to all other calls on that renderer and all objects created from it. No other renderer API call in that object tree should be in progress when StartNewFrame is called, and no other call should begin until it returns. This allows the renderer to latch a coherent frame boundary efficiently. Synchronization helpers to make this rule easier to follow are described below.
Window System Calls
Native window systems often impose their own threading requirements. Calls that bind or resize platform windows, such as BindWindow and NotifyWindowResized, must be made on the UI thread required by the active platform or window framework. The exact UI-thread dispatch mechanism is application-specific.
These calls still belong to the renderer object tree. If they can occur while another thread is advancing frames, they must be synchronized with StartNewFrame in the same way as other renderer calls.
Synchronization Helpers
To assist with synchronization requirements, the ReadWriteMutex helper class provides a reference implementation of a mutex that allows multiple concurrent readers and a single exclusive writer. Pending write lock attempts take priority over later read lock requests, which helps a render thread acquire the exclusive frame-advance lock without being starved by update threads.
Applications are free to use a different locking scheme. If the application design guarantees that no renderer API calls can overlap StartNewFrame, no additional external synchronization is required.
Next page, Allocation Model