Optimize viewer shaders and camera controls; sample GPU profiles asynchronously

Amp-Thread-ID: https://ampcode.com/threads/T-01a0b382-1e79-712b-abd1-d29201c9bb5e
Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
Amp
2026-09-18 11:52:09 +00:00
co-authored by heaust
parent bc957cf40d
commit eb8aa0e9dc
21 changed files with 762 additions and 174 deletions
+28 -39
View File
@@ -10,26 +10,13 @@ use crate::render_data::RenderData;
use crate::store::{Loadout, Store};
#[wasm_bindgen(inline_js = r#"
const channel = new MessageChannel();
const ready = [];
channel.port1.onmessage = () => ready.shift()?.();
export function nextFrame() {
return new Promise(resolve => requestAnimationFrame(resolve));
}
export function yieldTask() {
return new Promise(resolve => {
ready.push(resolve);
channel.port2.postMessage(0);
});
}
"#)]
extern "C" {
#[wasm_bindgen(js_name = nextFrame)]
async fn next_frame();
#[wasm_bindgen(js_name = yieldTask)]
async fn yield_task();
}
pub struct RenderLoop {
@@ -42,7 +29,8 @@ pub struct RenderLoop {
profiling: Cell<bool>,
profile_pending: Cell<bool>,
profile_after: Cell<f64>,
profile: RefCell<Option<String>>,
profile_callback: RefCell<Option<js_sys::Function>>,
profile_generation: Cell<u32>,
}
impl RenderLoop {
@@ -57,7 +45,8 @@ impl RenderLoop {
profiling: Cell::new(false),
profile_pending: Cell::new(false),
profile_after: Cell::new(0.0),
profile: RefCell::new(None),
profile_callback: RefCell::new(None),
profile_generation: Cell::new(0),
}
}
@@ -81,13 +70,12 @@ impl RenderLoop {
pub fn set_profiling(&self, enabled: bool) {
self.profiling.set(enabled);
self.profile_after.set(0.0);
if !enabled {
self.profile.borrow_mut().take();
}
self.profile_generation
.set(self.profile_generation.get().wrapping_add(1));
}
pub fn take_profile(&self) -> Option<String> {
self.profile.borrow_mut().take()
pub fn on_profile(&self, callback: js_sys::Function) {
*self.profile_callback.borrow_mut() = Some(callback);
}
pub fn start(
@@ -152,15 +140,12 @@ impl RenderLoop {
};
if let Some((profile, adapter, width, height)) = submission {
control.profile_pending.set(true);
control.profile_after.set(started + 250.0);
control.profile_after.set(started + 100.0);
let generation = control.profile_generation.get();
let control = control.clone();
wasm_bindgen_futures::spawn_local(async move {
while profile.state() == 0 {
yield_task().await;
}
if profile.state() == 1 {
let passes = profile
.read()
if let Some(passes) = profile.read().await {
let passes = passes
.into_iter()
.map(|(name, milliseconds)| {
serde_json::json!({
@@ -173,18 +158,22 @@ impl RenderLoop {
.iter()
.filter_map(|pass| pass["milliseconds"].as_f64())
.sum::<f64>();
if control.profiling.get() {
*control.profile.borrow_mut() = Some(
serde_json::json!({
"frame": frame,
"milliseconds": milliseconds,
"readbackMilliseconds": js_sys::Date::now() - started,
"adapter": adapter,
"canvas": { "width": width, "height": height },
"passes": passes,
})
.to_string(),
);
if control.profiling.get()
&& control.profile_generation.get() == generation
{
let sample = serde_json::json!({
"frame": frame,
"milliseconds": milliseconds,
"readbackMilliseconds": js_sys::Date::now() - started,
"adapter": adapter,
"canvas": { "width": width, "height": height },
"passes": passes,
})
.to_string();
if let Some(callback) = control.profile_callback.borrow().as_ref() {
let _ = callback
.call1(&wasm_bindgen::JsValue::NULL, &sample.into());
}
}
}
control.profile_pending.set(false);