From a40a110788e577417cfede4fb6fb3b130d86b37e Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Tue, 27 Aug 2024 13:55:08 -0700 Subject: [PATCH] Ray-caster: only draw when the scene is changed This is how I typically schedule draw calls in JavaScript applications. The baseline CPU activity for the display prototype is now in line with other pages (though perhaps a bit higher), and the profiler shows little time being spent in draw calls, even when I'm continually moving a slider. The interface feels pretty responsive overall, although the sliders seem to be lagging a bit. --- app-proto/inversive-display/src/main.rs | 130 +++++++++++++----------- 1 file changed, 73 insertions(+), 57 deletions(-) diff --git a/app-proto/inversive-display/src/main.rs b/app-proto/inversive-display/src/main.rs index 5f1987f..15c2518 100644 --- a/app-proto/inversive-display/src/main.rs +++ b/app-proto/inversive-display/src/main.rs @@ -108,6 +108,21 @@ fn main() { // display let display = create_node_ref(); + // change listener + let scene_changed = create_signal(true); + create_effect(move || { + ctrl_x.track(); + ctrl_y.track(); + radius_x.track(); + radius_y.track(); + opacity.track(); + highlight.track(); + layer_threshold.track(); + debug_mode.track(); + + scene_changed.set(true); + }); + on_mount(move || { // list construction elements const SPHERE_MAX: usize = 12; @@ -216,66 +231,67 @@ fn main() { bind_vertex_attrib(&ctx, position_index, 3, &positions); // set up a repainting routine - let (_, start_updating_display, _) = create_raf(move || { - /* INSTRUMENTS */ - // measure frame time - frames_since_last_sample += 1; - if frames_since_last_sample >= SAMPLE_PERIOD { - let frame_moment = performance.now(); - frame_time.set((frame_moment - last_frame_moment) / (SAMPLE_PERIOD as f64)); - last_frame_moment = frame_moment; + let (_, start_animation_loop, _) = create_raf(move || { + if scene_changed.get() { + /* INSTRUMENTS */ + // measure frame time + frames_since_last_sample += 1; + if frames_since_last_sample >= SAMPLE_PERIOD { + let frame_moment = performance.now(); + frame_time.set((frame_moment - last_frame_moment) / (SAMPLE_PERIOD as f64)); + last_frame_moment = frame_moment; + frames_since_last_sample = 0; + } + + // update the construction + sphere_vec.clear(); + sphere_vec.push(engine::sphere(0.5, 0.5, -5.0 + ctrl_x.get(), radius_x.get())); + sphere_vec.push(engine::sphere(-0.5, -0.5, -5.0 + ctrl_y.get(), radius_y.get())); + sphere_vec.push(engine::sphere(-0.5, 0.5, -5.0, 0.75)); + + // set the resolution + let width = canvas.width() as f32; + let height = canvas.height() as f32; + ctx.uniform2f(resolution_loc.as_ref(), width, height); + ctx.uniform1f(shortdim_loc.as_ref(), width.min(height)); + + // pass the construction + ctx.uniform1i(sphere_cnt_loc.as_ref(), sphere_vec.len() as i32); + for n in 0..sphere_vec.len() { + let v = &sphere_vec[n]; + ctx.uniform3f( + sphere_sp_locs[n].as_ref(), + v[0] as f32, v[1] as f32, v[2] as f32 + ); + ctx.uniform2f( + sphere_lt_locs[n].as_ref(), + v[3] as f32, v[4] as f32 + ); + ctx.uniform3fv_with_f32_array( + color_locs[n].as_ref(), + &color_vec[n] + ); + } + + // pass the control parameters + ctx.uniform2f(ctrl_loc.as_ref(), ctrl_x.get() as f32, ctrl_y.get() as f32); /* DEBUG */ + ctx.uniform2f(radius_loc.as_ref(), radius_x.get() as f32, radius_y.get() as f32); /* DEBUG */ + ctx.uniform1f(opacity_loc.as_ref(), opacity.get() as f32); + ctx.uniform1f(highlight_loc.as_ref(), highlight.get() as f32); + ctx.uniform1i(layer_threshold_loc.as_ref(), layer_threshold.get() as i32); + ctx.uniform1i(debug_mode_loc.as_ref(), debug_mode.get() as i32); + + // draw the scene + ctx.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, VERTEX_CNT as i32); + + // clear scene change flag + scene_changed.set(false); + } else { frames_since_last_sample = 0; + frame_time.set(-1.0); } - - // update the construction - sphere_vec.clear(); - sphere_vec.push(engine::sphere(0.5, 0.5, -5.0 + ctrl_x.get(), radius_x.get())); - sphere_vec.push(engine::sphere(-0.5, -0.5, -5.0 + ctrl_y.get(), radius_y.get())); - sphere_vec.push(engine::sphere(-0.5, 0.5, -5.0, 0.75)); - - // set the resolution - let width = canvas.width() as f32; - let height = canvas.height() as f32; - ctx.uniform2f(resolution_loc.as_ref(), width, height); - ctx.uniform1f(shortdim_loc.as_ref(), width.min(height)); - - // pass the construction - ctx.uniform1i(sphere_cnt_loc.as_ref(), sphere_vec.len() as i32); - for n in 0..sphere_vec.len() { - let v = &sphere_vec[n]; - ctx.uniform3f( - sphere_sp_locs[n].as_ref(), - v[0] as f32, v[1] as f32, v[2] as f32 - ); - ctx.uniform2f( - sphere_lt_locs[n].as_ref(), - v[3] as f32, v[4] as f32 - ); - ctx.uniform3fv_with_f32_array( - color_locs[n].as_ref(), - &color_vec[n] - ); - } - - // pass the control parameters - ctx.uniform2f(ctrl_loc.as_ref(), ctrl_x.get() as f32, ctrl_y.get() as f32); /* DEBUG */ - ctx.uniform2f(radius_loc.as_ref(), radius_x.get() as f32, radius_y.get() as f32); /* DEBUG */ - ctx.uniform1f(opacity_loc.as_ref(), opacity.get() as f32); - ctx.uniform1f(highlight_loc.as_ref(), highlight.get() as f32); - ctx.uniform1i(layer_threshold_loc.as_ref(), layer_threshold.get() as i32); - ctx.uniform1i(debug_mode_loc.as_ref(), debug_mode.get() as i32); - - // draw the scene - ctx.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, VERTEX_CNT as i32); }); - - /* - this wastes CPU time by running an animation loop, which updates the - display even when nothing has changed. there should be a way to make - Sycamore do single-frame updates in response to changes, but i - haven't found it yet - */ - start_updating_display(); + start_animation_loop(); }); view! {