0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-19 21:22:31 -05:00

Merge pull request #5529 from penpot/azazeln28-refactor-panning-perf

♻️ Refactor panning perf
This commit is contained in:
Belén Albeza 2025-01-14 15:26:03 +01:00 committed by GitHub
commit 4d4bf1edd1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 70 additions and 23 deletions

View file

@ -292,19 +292,23 @@
(when @canvas-init?
(wasm.api/resize-viewbox (:width vport) (:height vport))))
(mf/with-effect [base-objects @canvas-init?]
(mf/with-effect [@canvas-init? base-objects]
(when @canvas-init?
(wasm.api/set-objects base-objects)))
(mf/with-effect [preview-blend @canvas-init?]
(mf/with-effect [@canvas-init? preview-blend]
(when (and @canvas-init? preview-blend)
(wasm.api/request-render)))
(wasm.api/request-render "with-effect")))
(mf/with-effect [vbox @canvas-init?]
(mf/with-effect [@canvas-init? vbox]
(when @canvas-init?
(wasm.api/set-view zoom vbox)))
(wasm.api/set-view-zoom zoom vbox)))
(mf/with-effect [background]
(mf/with-effect [@canvas-init? vbox]
(when @canvas-init?
(wasm.api/set-view-box zoom vbox)))
(mf/with-effect [@canvas-init? background]
(when @canvas-init?
(wasm.api/set-canvas-background background)))

View file

@ -61,15 +61,16 @@
[r g b a]))
(defn cancel-render
[]
[_]
(when internal-frame-id
(js/cancelAnimationFrame internal-frame-id)
(set! internal-frame-id nil)))
(defn request-render
[]
(when internal-frame-id (cancel-render))
(set! internal-frame-id (js/requestAnimationFrame render)))
[requester]
(when internal-frame-id (cancel-render requester))
(let [frame-id (js/requestAnimationFrame render)]
(set! internal-frame-id frame-id)))
(defn use-shape
[id]
@ -386,10 +387,15 @@
(def debounce-render-without-cache (fns/debounce render-without-cache 100))
(defn set-view
(defn set-view-box
[zoom vbox]
(h/call internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
(h/call internal-module "_navigate")
(h/call internal-module "_pan"))
(defn set-view-zoom
[zoom vbox]
(h/call internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
(h/call internal-module "_zoom")
(debounce-render-without-cache))
(defn set-objects
@ -441,7 +447,7 @@
(let [pending' (concat (set-shape-fills fills) (set-shape-strokes strokes))]
(recur (inc index) (into pending pending'))))
pending))]
(request-render)
(request-render "set-objects")
(when-let [pending (seq pending)]
(->> (rx/from pending)
(rx/mapcat identity)

View file

@ -130,7 +130,7 @@
;; when something synced with wasm
;; is modified, we need to request
;; a new render.
(api/request-render)))
(api/request-render "set-wasm-attrs")))
(defn- impl-assoc
[self k v]

View file

@ -70,9 +70,16 @@ pub unsafe extern "C" fn render_without_cache() {
}
#[no_mangle]
pub unsafe extern "C" fn navigate() {
pub unsafe extern "C" fn zoom() {
let state: &mut Box<State<'_>> =
unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
state.zoom();
}
#[no_mangle]
pub unsafe extern "C" fn pan() {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
state.navigate();
state.pan();
}
#[no_mangle]

View file

@ -36,9 +36,13 @@ pub(crate) struct CachedSurfaceImage {
}
impl CachedSurfaceImage {
fn is_dirty(&self, viewbox: &Viewbox) -> bool {
fn is_dirty_for_zooming(&mut self, viewbox: &Viewbox) -> bool {
!self.has_all_shapes && !self.viewbox.area.contains(viewbox.area)
}
fn is_dirty_for_panning(&mut self, _viewbox: &Viewbox) -> bool {
!self.has_all_shapes
}
}
pub(crate) struct RenderState {
@ -168,9 +172,23 @@ impl RenderState {
.clear(skia::Color::TRANSPARENT);
}
pub fn navigate(&mut self, tree: &HashMap<Uuid, impl Renderable>) -> Result<(), String> {
if let Some(cached_surface_image) = self.cached_surface_image.as_ref() {
if cached_surface_image.is_dirty(&self.viewbox) {
pub fn zoom(&mut self, tree: &HashMap<Uuid, impl Renderable>) -> Result<(), String> {
if let Some(cached_surface_image) = self.cached_surface_image.as_mut() {
let is_dirty = cached_surface_image.is_dirty_for_zooming(&self.viewbox);
if is_dirty {
self.render_all(tree, true);
} else {
self.render_all_from_cache()?;
}
}
Ok(())
}
pub fn pan(&mut self, tree: &HashMap<Uuid, impl Renderable>) -> Result<(), String> {
if let Some(cached_surface_image) = self.cached_surface_image.as_mut() {
let is_dirty = cached_surface_image.is_dirty_for_panning(&self.viewbox);
if is_dirty {
self.render_all(tree, true);
} else {
self.render_all_from_cache()?;
@ -192,6 +210,7 @@ impl RenderState {
);
self.translate(self.viewbox.pan_x, self.viewbox.pan_y);
// Reset shape tree
let is_complete = self.render_shape_tree(&Uuid::nil(), tree);
if generate_cached_surface_image || self.cached_surface_image.is_none() {
self.cached_surface_image = Some(CachedSurfaceImage {
@ -299,7 +318,7 @@ impl RenderState {
if !root_id.is_nil() {
if !element.bounds().intersects(self.viewbox.area) || element.hidden() {
self.render_debug_element(element, false);
// TODO: This means that not all the shapes are renderer so we
// TODO: This means that not all the shapes are rendered so we
// need to call a render_all on the zoom out.
return is_complete; // TODO return is_complete or return false??
} else {

View file

@ -0,0 +1,6 @@
use crate::math::Point;
#[derive(Debug, Clone, PartialEq)]
pub struct Rect {
}

View file

@ -36,9 +36,14 @@ impl<'a> State<'a> {
&mut self.render_state
}
pub fn navigate(&mut self) {
pub fn pan(&mut self) {
// TODO: propagate error to main fn
let _ = self.render_state.navigate(&self.shapes).unwrap();
let _ = self.render_state.pan(&self.shapes).unwrap();
}
pub fn zoom(&mut self) {
// TODO: propagate error to main fn
let _ = self.render_state.zoom(&self.shapes).unwrap();
}
pub fn render_all(&mut self, generate_cached_surface_image: bool) {