Storing global weak mutable references for web assembly

I am writing a WebGL renderer using wasm_bindgen and it has a function responsible for the main loop:

#[wasm_bindgen]
pub fn render() {
  let f = Rc::new(RefCell:new(None));
  let g = Rc::clone(&f);

  let camera = Camera::new();

  *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
    // Use the camera
    request_animation_frame(f.borrow().as_ref().unwrap());
  }) as Box<dyn FnMut()>));
  request_animation_frame(g.borrow().as_ref().unwrap());
}

Now I want that camera to be editable from the TypeScript side through something like this:

#[wasm_bindgen]
pub fn move_camera(x: f32, y: f32, z: f32) {
  let mut camera = <???>;

  camera.move_to(x, y, z);
  camera.update();
}

And the issue I'm facing is how to make the camera accessible outside the render function. The call to move_camera will come from outside Rust, so it needs to be accessible globally.

From my research it seems like I need an Arc<RwLock<Camera>> stored inside a struct and then create a static ref to that struct using lazy_ref. But I can't start a RwLock with None and the camera does not exist before the render function gets called.

So what I am looking for is a thread-safe way to store a weak mutable reference to a value that starts empty and can be assigned at runtime.

I think Arc<RwLock<Option<Camera>>> fits the bill...

Perhaps you could use the once_cell crate to create a static OnceCell<RwLock<Camera>>. It can be initialized once, then read from and written to thereafter.

You don't need a global object if you can have an initialization function that returns something.

struct RendererApi {
    state: Rc<RefCell<State>>,
}
struct State {
    camera: Camera,
    // probably other fields eventually...
}

Your initialization function would construct and return a RendererApi, and you would export methods on RendererApi (with &self as receiver) that access the mutable State internally.

once_cell::sync::OnceCell<RwLock<Camera>> is an available option if you do decide that it should be global, instead.

2 Likes

Ah, tunnel vision was the issue.
You are correct, I can have an initializer return a struct exposing a public API and keep the interior mutability on the state only.

Thank you! That solves it.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.