♻️ Change how frames are queued

This commit is contained in:
Aitor Moreno 2026-04-29 11:41:07 +02:00
parent e948020886
commit c88015f70e
5 changed files with 38 additions and 74 deletions

View File

@ -317,46 +317,23 @@
(aget buffer 3))
(set! wasm/internal-frame-id nil))))
(defn render-preview!
"Render a lightweight preview without tile caching.
Used during progressive loading for fast feedback."
[]
(when (and wasm/context-initialized? (not @wasm/context-lost?))
(h/call wasm/internal-module "_render_preview")))
(defonce pending-render (atom false))
(defonce shapes-loading? (atom false))
(defonce deferred-render? (atom false))
(defn- register-deferred-render!
[]
(reset! deferred-render? true))
(defn request-render
[_requester]
(when (and wasm/context-initialized? (not @wasm/context-lost?) (not @wasm/disable-request-render?))
(when (and wasm/context-initialized?
(not @wasm/context-lost?)
(not @wasm/disable-request-render?))
(if @shapes-loading?
(register-deferred-render!)
(when-not @pending-render
(reset! pending-render true)
(let [frame-id
(js/requestAnimationFrame
(fn [ts]
(reset! pending-render false)
(set! wasm/internal-frame-id nil)
(render ts)))]
(set! wasm/internal-frame-id frame-id))))))
(reset! deferred-render? true)
(wasm/request-frame render))))
(defn- begin-shapes-loading!
[]
(reset! shapes-loading? true)
(let [frame-id wasm/internal-frame-id
was-pending @pending-render]
(when frame-id
(js/cancelAnimationFrame frame-id)
(set! wasm/internal-frame-id nil))
(reset! pending-render false)
(let [was-pending (wasm/frame-requested?)]
(wasm/cancel-frame)
(reset! deferred-render? was-pending)))
(defn- end-shapes-loading!
@ -1708,12 +1685,9 @@
(set! wasm/context-initialized? false)
;; Cancel any pending animation frame to prevent race conditions
(when wasm/internal-frame-id
(js/cancelAnimationFrame wasm/internal-frame-id)
(set! wasm/internal-frame-id nil))
(wasm/cancel-frame)
;; Reset render flags to prevent new renders from being scheduled
(reset! pending-render false)
(reset! shapes-loading? false)
(reset! deferred-render? false)

View File

@ -10,6 +10,36 @@
(defonce internal-frame-id nil)
(defonce internal-module #js {})
;; Is a frame requested?
(defn frame-requested?
"Returns true if a frame was requested"
[]
(not (nil? internal-frame-id)))
;; Cancels a frame
(defn cancel-frame
"Cancels the current requested frame"
[]
(when-not (nil? internal-frame-id)
(js/cancelAnimationFrame internal-frame-id)
(set! internal-frame-id nil)
true)
false)
(defn create-frame-delegate
"Creates a new frame delegate"
[f]
(fn frame-delegate [timestamp]
(let [frame-id internal-frame-id]
(set! internal-frame-id nil)
(f timestamp frame-id))))
;; Requests a frame
(defn request-frame
"Requests a new frame"
[f]
(set! internal-frame-id (js/requestAnimationFrame (create-frame-delegate f))))
;; Reference to the HTML canvas element.
(defonce canvas nil)
;; Snapshot of the current canvas suitable for `<img src=...>` overlays.
@ -29,7 +59,6 @@
;; When we're rendering in a sync way we want to stop the asynchrous `request-render`
(defonce disable-request-render? (atom false))
(defonce serializers
#js {:blur-type shared/RawBlurType
:blend-mode shared/RawBlendMode

View File

@ -306,15 +306,6 @@ pub extern "C" fn set_preview_mode(enabled: bool) -> Result<()> {
Ok(())
}
#[no_mangle]
#[wasm_error]
pub extern "C" fn render_preview() -> Result<()> {
with_state_mut!(state, {
state.render_preview(performance::get_time());
});
Ok(())
}
/// Enter bulk-loading mode. While active, `state.loading` is `true`.
#[no_mangle]
#[wasm_error]

View File

@ -1864,33 +1864,7 @@ impl RenderState {
performance::end_measure!("render_from_cache");
performance::end_timed_log!("render_from_cache", _start);
}
/// Render a preview of the shapes during loading.
/// This rebuilds tiles for touched shapes and renders synchronously.
pub fn render_preview(&mut self, tree: ShapesPoolRef, timestamp: i32) -> Result<()> {
let _start = performance::begin_timed_log!("render_preview");
performance::begin_measure!("render_preview");
// Enable fast_mode during preview to skip expensive effects (blur, shadows).
// Restore the previous state afterward so the final render is full quality.
let current_fast_mode = self.options.is_fast_mode();
self.options.set_fast_mode(true);
// Skip tile rebuilding during preview - we'll do it at the end
// Just rebuild tiles for touched shapes and render synchronously
self.rebuild_touched_tiles(tree);
// Use the sync render path
self.start_render_loop(None, tree, timestamp, true)?;
self.options.set_fast_mode(current_fast_mode);
performance::end_measure!("render_preview");
performance::end_timed_log!("render_preview", _start);
Ok(())
}
pub fn start_render_loop(
&mut self,

View File

@ -253,10 +253,6 @@ impl State {
self.render_state.rebuild_touched_tiles(&self.shapes);
}
pub fn render_preview(&mut self, timestamp: i32) {
let _ = self.render_state.render_preview(&self.shapes, timestamp);
}
pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) -> Result<()> {
// Index-based storage is safe
self.render_state