Skip to content

Instantly share code, notes, and snippets.

@Nnubes256
Last active September 2, 2022 11:45
Show Gist options
  • Save Nnubes256/19b382ec19abf41b8d29749d5910251e to your computer and use it in GitHub Desktop.
Save Nnubes256/19b382ec19abf41b8d29749d5910251e to your computer and use it in GitHub Desktop.
learn-wgpu macOS M1 memory leak fix, CVDisplayLink variant
# Add this to Cargo.toml
[target.'cfg(target_os = "macos")'.dependencies]
display-link = "0.2"
// ...
#[cfg_attr(target_arch="wasm32", wasm_bindgen(start))]
pub async fn run() {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Warn).expect("Could't initialize logger");
} else {
env_logger::init();
}
}
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
{
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(&window).await;
// !!! NEW !!!
#[cfg(target_os = "macos")]
let (_dl, semaphore) = {
use std::sync::{Arc, Mutex, Condvar};
// Create a makeshift semaphore from a Mutex and a Condvar.
// This technique is discussed in the Rust standard library docs here:
// https://doc.rust-lang.org/std/sync/struct.Condvar.html
let pair = Arc::new((Mutex::new(false), Condvar::new()));
// Setup CVDisplayLink
let pair2 = Arc::clone(&pair);
let mut dl = display_link::DisplayLink::new(move |_ts| {
// This will be called on every vsync.
// TODO: _ts contains an absolute timestamp which can be used
// to calculate a delta time for updates, could be interesting
// to figure out how to pass it over?
let (lock, cvar) = &*pair2;
let mut do_redraw = lock.lock().unwrap();
*do_redraw = true;
// Notify the Winit runloop if it's sleeping.
cvar.notify_one();
}).unwrap();
// Start the CVDisplayLink
dl.resume().unwrap();
// CVDisplayLink needs to be hoisted out of here, otherwise it
// doesn't seem to work.
(_dl, pair)
};
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == window.id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == window.id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
// !!! NEW !!!
#[cfg(target_os = "macos")]
{
// Wait until CVDisplayLink tells us we can request a redraw.
let (lock, cvar) = &*semaphore;
let mut do_redraw = lock.lock().unwrap();
// As long as the value inside the `Mutex<bool>` is `false`,
// we wait.
while !*do_redraw {
do_redraw = cvar.wait(do_redraw).unwrap();
}
// Reset the value
*do_redraw = false;
}
window.request_redraw();
}
_ => {}
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment