Hey!
I'm writing a simple renderer for my game (I'm not using an engine). The whole update logic takes place on the main thread, while the rendering logic (and some other related calculations) are done on a separate thread. The main thread is supposed to periodically send data to the render thread. This way, the render thread can render a frame while the main thread starts computing the next one.
Currently, I'm using a channel from the standard library. The main thread simply sends data through that channel and the render threads waits on it everytime it wants to render a new frame. I want to optimize this process.
The idea
- Use two
RenderData
instances (probably allocated on the heap). While the main thread can access the first instance, the render thread can access the other one. Always using the same two instances allows reusing buffer allocations. - When a thread finishes its work, it indicates to the system it is ready and starts blocking.
- Once the other thread finishes its work, it indicates to the system it is ready.
- Both threads swap the pointer to
RenderData
they are using with the other.
Before a thread sets itself as finished, it has exclusive access to its own RenderData
instance.
Here is a pseudo-implementation of what I'm talking about, so that you can understand better how I want it to work.
struct Shared<T> {
a: UnsafeCell<T>,
b: UnsafeCell<T>,
}
struct Swap<T> {
shared: *mut Shared<T>,
value: *mut T,
}
impl<T> Swap<T> {
fn wait_and_swap(this: &mut Self) {
// block until the other thread is ready.
if this.value == this.shared.a.get() {
this.value = this.shared.b.get();
} else {
this.value = this.shared.a.get();
}
// I don't think we need to wait on the other thread
// because the `wait_and_swap` function won't return
// until pointers are swapped anyway.
}
}
impl Deref for Swap<T> {}
impl DerefMut for Swap<T> {}
Actual questions
Is this actually a good idea? I added a lot of context because I want to avoid the x-y problem, so if you have a better idea, please share it!
First, is there a crate that does this or something close enough?
If not, is there a way to correctly do this using simple syncronisation primitives? The part I'm struggling with is the "both threads needs to wait for one another". It's like a mutex lock, but the first thread locks and the second thread unlocks. How error prone would it be to implement it myself? Is there useful resources on the subject?