Compare commits

...

1 Commits

Author SHA1 Message Date
Alejandro Alonso
79d926fbfc 🎉 Improve huge shapes rendering 2026-01-26 15:25:32 +01:00
3 changed files with 68 additions and 21 deletions

View File

@@ -275,29 +275,46 @@ pub extern "C" fn set_view_end() {
state.render_state.options.set_fast_mode(false);
state.render_state.cancel_animation_frame();
let zoom_changed = state.render_state.zoom_changed();
// let zoom_changed = state.render_state.zoom_changed();
// Update tile_viewbox first so that get_tiles_for_shape uses the correct interest area
// This is critical because we limit tiles to the interest area for optimization
let scale = state.render_state.get_scale();
state.render_state.tile_viewbox.update(state.render_state.viewbox, scale);
// Only rebuild tile indices when zoom has changed.
// During pan-only operations, shapes stay in the same tiles
// because tile_size = 1/scale * TILE_SIZE (depends only on zoom).
if zoom_changed {
let _rebuild_start = performance::begin_timed_log!("rebuild_tiles");
performance::begin_measure!("set_view_end::rebuild_tiles");
if state.render_state.options.is_profile_rebuild_tiles() {
state.rebuild_tiles();
} else {
state.rebuild_tiles_shallow();
}
performance::end_measure!("set_view_end::rebuild_tiles");
performance::end_timed_log!("rebuild_tiles", _rebuild_start);
// if zoom_changed {
// let _rebuild_start = performance::begin_timed_log!("rebuild_tiles");
// performance::begin_measure!("set_view_end::rebuild_tiles");
// if state.render_state.options.is_profile_rebuild_tiles() {
// state.rebuild_tiles();
// } else {
// state.rebuild_tiles_shallow();
// }
// performance::end_measure!("set_view_end::rebuild_tiles");
// performance::end_timed_log!("rebuild_tiles", _rebuild_start);
// } else {
// // During pan, we only clear the tile index without
// // invalidating cached textures, which is more efficient.
// let _clear_start = performance::begin_timed_log!("clear_tile_index");
// performance::begin_measure!("set_view_end::clear_tile_index");
// state.clear_tile_index();
// performance::end_measure!("set_view_end::clear_tile_index");
// performance::end_timed_log!("clear_tile_index", _clear_start);
// }
let _rebuild_start = performance::begin_timed_log!("rebuild_tiles");
performance::begin_measure!("set_view_end::rebuild_tiles");
if state.render_state.options.is_profile_rebuild_tiles() {
state.rebuild_tiles();
} else {
// During pan, we only clear the tile index without
// invalidating cached textures, which is more efficient.
let _clear_start = performance::begin_timed_log!("clear_tile_index");
performance::begin_measure!("set_view_end::clear_tile_index");
state.clear_tile_index();
performance::end_measure!("set_view_end::clear_tile_index");
performance::end_timed_log!("clear_tile_index", _clear_start);
state.rebuild_tiles_shallow();
}
performance::end_measure!("set_view_end::rebuild_tiles");
performance::end_timed_log!("rebuild_tiles", _rebuild_start);
state.render_state.sync_cached_viewbox();
performance::end_measure!("set_view_end");
performance::end_timed_log!("set_view_end", _end_start);

View File

@@ -1164,7 +1164,6 @@ impl RenderState {
let scale = self.get_scale();
self.tile_viewbox.update(self.viewbox, scale);
self.focus_mode.reset();
performance::begin_measure!("render");
@@ -2113,13 +2112,43 @@ impl RenderState {
}
/*
* Given a shape returns the TileRect with the range of tiles that the shape is in
* Given a shape returns the TileRect with the range of tiles that the shape is in.
* This is always limited to the interest area to optimize performance and prevent
* processing unnecessary tiles outside the viewport. The interest area already
* includes a margin (VIEWPORT_INTEREST_AREA_THRESHOLD) calculated via
* get_tiles_for_viewbox_with_interest, ensuring smooth pan/zoom interactions.
*
* When the viewport changes (pan/zoom), the interest area is updated and shapes
* are dynamically added to the tile index via the fallback mechanism in
* render_shape_tree_partial_uncached, ensuring all shapes render correctly.
*/
pub fn get_tiles_for_shape(&mut self, shape: &Shape, tree: ShapesPoolRef) -> TileRect {
let scale = self.get_scale();
let extrect = self.get_cached_extrect(shape, tree, scale);
let tile_size = tiles::get_tile_size(scale);
tiles::get_tiles_for_rect(extrect, tile_size)
let shape_tiles = tiles::get_tiles_for_rect(extrect, tile_size);
println!("shape_tiles: {:?}", shape_tiles);
let interest_rect = &self.tile_viewbox.interest_rect;
println!("interest_rect: {:?}", interest_rect);
// Calculate the intersection of shape_tiles with interest_rect
// This returns only the tiles that are both in the shape and in the interest area
let intersection_x1 = shape_tiles.x1().max(interest_rect.x1());
let intersection_y1 = shape_tiles.y1().max(interest_rect.y1());
let intersection_x2 = shape_tiles.x2().min(interest_rect.x2());
let intersection_y2 = shape_tiles.y2().min(interest_rect.y2());
// Return the intersection if valid (there is overlap), otherwise return empty rect
if intersection_x1 <= intersection_x2 && intersection_y1 <= intersection_y2 {
// Valid intersection: return the tiles that are in both shape_tiles and interest_rect
println!("intersection: {:?}", TileRect(intersection_x1, intersection_y1, intersection_x2, intersection_y2));
TileRect(intersection_x1, intersection_y1, intersection_x2, intersection_y2)
} else {
// No intersection: shape is completely outside interest area
// The shape will be added dynamically via add_shape_tiles when it enters
// the interest area during pan/zoom operations
println!("no intersection");
TileRect(0, 0, -1, -1)
}
}
/*

View File

@@ -84,6 +84,7 @@ impl TileViewbox {
pub fn update(&mut self, viewbox: Viewbox, scale: f32) {
self.visible_rect = get_tiles_for_viewbox(viewbox, scale);
self.interest_rect = get_tiles_for_viewbox_with_interest(viewbox, self.interest, scale);
println!("update interest_rect: {:?}", self.interest_rect);
self.center = get_tile_center_for_viewbox(viewbox, scale);
}