From 4a85a9ac468d67f5f6585181735f99b318cb8028 Mon Sep 17 00:00:00 2001 From: Aitor Moreno Date: Fri, 27 Jun 2025 13:58:56 +0200 Subject: [PATCH] WIP --- render-wasm/src/emscripten.rs | 55 ++++++++++++++++- render-wasm/src/main.rs | 1 - render-wasm/src/render.rs | 107 +++++++++++++++------------------- render-wasm/src/tiles.rs | 36 ++++++++++++ 4 files changed, 134 insertions(+), 65 deletions(-) diff --git a/render-wasm/src/emscripten.rs b/render-wasm/src/emscripten.rs index 69c4b9cfc8..a4aab1236f 100644 --- a/render-wasm/src/emscripten.rs +++ b/render-wasm/src/emscripten.rs @@ -2,7 +2,7 @@ macro_rules! run_script { ($s:expr) => {{ extern "C" { - pub fn emscripten_run_script(script: *const i8); + fn emscripten_run_script(script: *const i8); } match std::ffi::CString::new($s) { @@ -16,7 +16,7 @@ macro_rules! run_script { macro_rules! run_script_int { ($s:expr) => {{ extern "C" { - pub fn emscripten_run_script_int(script: *const i8) -> i32; + fn emscripten_run_script_int(script: *const i8) -> i32; } match std::ffi::CString::new($s) { @@ -30,7 +30,7 @@ macro_rules! run_script_int { macro_rules! get_now { () => {{ extern "C" { - pub fn emscripten_get_now() -> f64; + fn emscripten_get_now() -> f64; } unsafe { emscripten_get_now() } }}; @@ -54,6 +54,54 @@ macro_rules! init_gl { }}; } +#[macro_export] +macro_rules! debugger { + () => {{ + extern "C" { + fn emscripten_debugger(); + } + unsafe { emscripten_debugger() } + }} +} + +#[repr(u32)] +#[allow(dead_code)] +pub enum Log { + Default = 1, + Warn = 2, + Error = 4, + CStack = 8, + JSStack = 16, + Demangle = 32, + NoPaths = 64, + FuncParams = 128, +} + +#[macro_export] +macro_rules! log { + ($flags:expr, $($arg:tt)*) => {{ + #[cfg(debug_assertions)] + { + extern "C" { + fn emscripten_log(flags: u32, message: *const std::os::raw::c_char); + } + use std::ffi::CString; + let msg = format!($($arg)*); + let c_msg = CString::new(msg).unwrap(); + let flags = $flags as u32; + unsafe { + emscripten_log(flags, c_msg.as_ptr()); + } + } + }} +} + +#[allow(unused_imports)] +pub use log; + +#[allow(unused_imports)] +pub use debugger; + #[allow(unused_imports)] pub use run_script; @@ -65,3 +113,4 @@ pub use get_now; #[allow(unused_imports)] pub use init_gl; + diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 6891614390..2450773ede 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -1,4 +1,3 @@ -#[cfg(target_arch = "wasm32")] mod emscripten; mod math; mod mem; diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 5a610253ff..203e53e81b 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -20,6 +20,7 @@ use gpu_state::GpuState; use options::RenderStateOptions; use surfaces::{SurfaceId, Surfaces}; +use crate::emscripten; use crate::performance; use crate::shapes::{Corners, Fill, Shape, SolidColor, StructureEntry, Type}; use crate::state::ShapesPool; @@ -77,7 +78,7 @@ impl PendingNodes { } pub fn add_root(&mut self) { - println!("add_root"); + emscripten::log!(emscripten::Log::Default, "add_root"); self.list.push(NodeRenderState { id: Uuid::nil(), visited_children: false, @@ -92,7 +93,7 @@ impl PendingNodes { child_id: &Uuid, children_clip_bounds: Option<(Rect, Option, Matrix)>, ) { - println!("add_child"); + emscripten::log!(emscripten::Log::Default, "add_child"); self.list.push(NodeRenderState { id: *child_id, visited_children: false, @@ -103,7 +104,7 @@ impl PendingNodes { } pub fn add_children_safeguard(&mut self, element: &Shape, mask: bool) { - println!("add_children_safeguard"); + emscripten::log!(emscripten::Log::Default, "add_children_safeguard"); // Set the node as visited_children before processing children self.list.push(NodeRenderState { id: element.id, @@ -115,7 +116,7 @@ impl PendingNodes { } pub fn add_mask(&mut self, element: &Shape) { - println!("add_mask"); + emscripten::log!(emscripten::Log::Default, "add_mask"); self.list.push(NodeRenderState { id: element.id, visited_children: true, @@ -773,7 +774,7 @@ impl RenderState { self.render_in_progress = true; self.render_is_full = full; if self.render_is_full { - println!("pending_nodes.add_root"); + emscripten::log!(emscripten::Log::Default, "pending_nodes.add_root"); self.pending_nodes.add_root(); } self.apply_drawing_to_render_canvas(None); @@ -804,14 +805,19 @@ impl RenderState { )?; } + self.flush_and_submit(); + if self.render_in_progress { self.cancel_animation_frame(); self.render_request_id = Some(wapi::request_animation_frame!()); } else { if self.render_is_full { - self.apply_render_to_final_canvas(self.render_area); + // if let Some(next_tile) = self.pending_tiles.pop() { + // self.update_render_context(&next_tile); + emscripten::log!(emscripten::Log::Default, "whatever {}", self.pending_tiles.len()); + let tile_rect = self.get_current_tile_bounds(); + self.apply_render_to_final_canvas(tile_rect); } - self.flush_and_submit(); performance::end_measure!("render"); } } @@ -941,40 +947,19 @@ impl RenderState { self.focus_mode.exit(&element.id); } - pub fn get_current_tile_bounds(&mut self) -> Rect { - let tiles::Tile(tile_x, tile_y) = self.current_tile.unwrap(); - let scale = self.get_scale(); - let offset_x = self.viewbox.area.left * scale; - let offset_y = self.viewbox.area.top * scale; - Rect::from_xywh( - (tile_x as f32 * tiles::TILE_SIZE) - offset_x, - (tile_y as f32 * tiles::TILE_SIZE) - offset_y, - tiles::TILE_SIZE, - tiles::TILE_SIZE, - ) + #[inline] + pub fn get_tile_bounds(&mut self, tile: tiles::Tile) -> Rect { + tiles::get_tile_bounds(self.viewbox, tile, self.get_scale()) } - // Returns the bounds of the current tile relative to the viewbox, - // aligned to the nearest tile grid origin. - // - // Unlike `get_current_tile_bounds`, which calculates bounds using the exact - // scaled offset of the viewbox, this method snaps the origin to the nearest - // lower multiple of `TILE_SIZE`. This ensures the tile bounds are aligned - // with the global tile grid, which is useful for rendering tiles in a - /// consistent and predictable layout. + #[inline] + pub fn get_current_tile_bounds(&mut self) -> Rect { + self.get_tile_bounds(self.current_tile.unwrap()) + } + + #[inline] pub fn get_current_aligned_tile_bounds(&mut self) -> Rect { - let tiles::Tile(tile_x, tile_y) = self.current_tile.unwrap(); - let scale = self.get_scale(); - let start_tile_x = - (self.viewbox.area.left * scale / tiles::TILE_SIZE).floor() * tiles::TILE_SIZE; - let start_tile_y = - (self.viewbox.area.top * scale / tiles::TILE_SIZE).floor() * tiles::TILE_SIZE; - Rect::from_xywh( - (tile_x as f32 * tiles::TILE_SIZE) - start_tile_x, - (tile_y as f32 * tiles::TILE_SIZE) - start_tile_y, - tiles::TILE_SIZE, - tiles::TILE_SIZE, - ) + tiles::get_tile_aligned_bounds(self.viewbox, self.current_tile.unwrap(), self.get_scale()) } pub fn render_shape_tree_full( @@ -985,7 +970,7 @@ impl RenderState { scale_content: &HashMap, timestamp: i32, ) -> Result<(bool, bool), String> { - println!("render_shape_tree_full:begin"); + emscripten::log!(emscripten::Log::Default, "render_shape_tree_full:begin"); let result = self.render_shape_tree_full_uncached( tree, modifiers, @@ -995,7 +980,7 @@ impl RenderState { ); let (is_empty, should_stop_rendering) = result?; - println!("is_empty {} should_stop_rendering {}", is_empty, should_stop_rendering); + emscripten::log!(emscripten::Log::Default, "is_empty {} should_stop_rendering {}", is_empty, should_stop_rendering); if should_stop_rendering { self.render_in_progress = false; self.render_is_full = false; @@ -1006,7 +991,7 @@ impl RenderState { } debug::render_wasm_label(self); - println!("render_shape_tree_full:end"); + emscripten::log!(emscripten::Log::Default, "render_shape_tree_full:end"); Ok((is_empty, should_stop_rendering)) } @@ -1058,7 +1043,7 @@ impl RenderState { ) { // We need to iterate over every tile of the element. for tile in tiles.iter() { - println!( + emscripten::log!(emscripten::Log::Default, "render_shape_tree_full_uncached::for::start {} {}", tile.0, tile.1 ); @@ -1071,7 +1056,7 @@ impl RenderState { scale_content, ); - println!("render_shape_tree_full_uncached::for::end"); + emscripten::log!(emscripten::Log::Default, "render_shape_tree_full_uncached::for::end"); } } @@ -1085,9 +1070,9 @@ impl RenderState { ) -> Result<(bool, bool), String> { let mut iteration = 0; let mut is_empty = true; - println!("render_shape_tree_full_uncached::while::start"); + emscripten::log!(emscripten::Log::Default, "render_shape_tree_full_uncached::while::start"); while let Some(node_render_state) = self.pending_nodes.next() { - println!( + emscripten::log!(emscripten::Log::Default, "render_shape_tree_full_uncached::while::pop {} {}", self.pending_nodes.len(), self.pending_nodes.is_empty() @@ -1106,7 +1091,7 @@ impl RenderState { .to_string(), )?; - println!("a {} {}", element.id, node_render_state.is_root()); + emscripten::log!(emscripten::Log::Default, "a {} {}", element.id, node_render_state.is_root()); if !node_render_state.is_root() { let mut transformed_element: Cow = Cow::Borrowed(element); @@ -1129,17 +1114,17 @@ impl RenderState { } } - println!("b"); + emscripten::log!(emscripten::Log::Default, "b"); // If the shape is not in the tile set, then we update // it. if self.get_tiles_of(node_id).is_none() { - println!("updating tiles for {}", element.id); + emscripten::log!(emscripten::Log::Default, "updating tiles for {}", element.id); self.update_tile_for(element); } - println!("c"); + emscripten::log!(emscripten::Log::Default, "c"); if !element.is_root() { - println!("render_shape_tree_full_uncached_shape_tiles"); + emscripten::log!(emscripten::Log::Default, "render_shape_tree_full_uncached_shape_tiles"); let mut tiles_opt = self.get_tiles_of(element.id); let tiles = tiles_opt.as_mut().unwrap().clone(); self.render_shape_tree_full_uncached_shape_tiles( @@ -1153,7 +1138,7 @@ impl RenderState { } - println!("d {}", element.is_recursive()); + emscripten::log!(emscripten::Log::Default, "d {}", element.is_recursive()); if !visited_children && element.is_recursive() { self.pending_nodes.add_children_safeguard(element, mask); @@ -1171,25 +1156,25 @@ impl RenderState { }); } - println!("children_ids {}", children_ids.len()); + emscripten::log!(emscripten::Log::Default, "children_ids {}", children_ids.len()); for child_id in children_ids.iter() { - println!("children id {}", child_id); + emscripten::log!(emscripten::Log::Default, "children id {}", child_id); self.pending_nodes.add_child(child_id, children_clip_bounds); } } - println!("e"); + emscripten::log!(emscripten::Log::Default, "e"); // We try to avoid doing too many calls to get_time if self.should_stop_rendering(iteration, timestamp) { - println!("should_stop_rendering {} {}", self.pending_nodes.is_empty(), self.pending_nodes.len() == 0); + emscripten::log!(emscripten::Log::Default, "should_stop_rendering {} {}", self.pending_nodes.is_empty(), self.pending_nodes.len() == 0); return Ok((is_empty, self.pending_nodes.is_empty())); } - if (iteration == 100) { + if iteration == 100 { return Ok((false, true)); } iteration += 1; } - println!("render_shape_tree_full_uncached::while::end"); + emscripten::log!(emscripten::Log::Default, "render_shape_tree_full_uncached::while::end"); Ok((is_empty, self.pending_nodes.is_empty())) } @@ -1307,12 +1292,12 @@ impl RenderState { scale_content: &HashMap, timestamp: i32, ) -> Result<(), String> { - println!("render_shape_tree_partial:begin"); + emscripten::log!(emscripten::Log::Default, "render_shape_tree_partial:begin"); let mut should_stop = false; while !should_stop { if let Some(current_tile) = self.current_tile { if self.surfaces.has_cached_tile_surface(current_tile) { - println!("cached"); + emscripten::log!(emscripten::Log::Default, "cached"); performance::begin_measure!("render_shape_tree::cached"); let tile_rect = self.get_current_tile_bounds(); self.surfaces.draw_cached_tile_surface( @@ -1331,7 +1316,7 @@ impl RenderState { ); } } else { - println!("uncached"); + emscripten::log!(emscripten::Log::Default, "uncached"); performance::begin_measure!("render_shape_tree::uncached"); let (is_empty, early_return) = self.render_shape_tree_partial_uncached( tree, @@ -1400,7 +1385,7 @@ impl RenderState { ui::render(self, tree, modifiers, structure); debug::render_wasm_label(self); - println!("render_shape_tree_partial:end"); + emscripten::log!(emscripten::Log::Default, "render_shape_tree_partial:end"); Ok(()) } diff --git a/render-wasm/src/tiles.rs b/render-wasm/src/tiles.rs index ec530ab23d..0f033eefbb 100644 --- a/render-wasm/src/tiles.rs +++ b/render-wasm/src/tiles.rs @@ -112,6 +112,38 @@ pub fn get_tile_rect(tile: Tile, scale: f32) -> skia::Rect { skia::Rect::from_xywh(tx, ty, ts, ts) } +pub fn get_tile_bounds(viewbox: Viewbox, Tile(tile_x, tile_y): Tile, scale: f32) -> skia::Rect { + let offset_x = viewbox.area.left * scale; + let offset_y = viewbox.area.top * scale; + skia::Rect::from_xywh( + (tile_x as f32 * TILE_SIZE) - offset_x, + (tile_y as f32 * TILE_SIZE) - offset_y, + TILE_SIZE, + TILE_SIZE, + ) +} + +// Returns the bounds of the current tile relative to the viewbox, +// aligned to the nearest tile grid origin. +// +// Unlike `get_current_tile_bounds`, which calculates bounds using the exact +// scaled offset of the viewbox, this method snaps the origin to the nearest +// lower multiple of `TILE_SIZE`. This ensures the tile bounds are aligned +// with the global tile grid, which is useful for rendering tiles in a +// consistent and predictable layout. +pub fn get_tile_aligned_bounds(viewbox: Viewbox, Tile(tile_x, tile_y): Tile, scale: f32) -> skia::Rect { + let start_tile_x = + (viewbox.area.left * scale / TILE_SIZE).floor() * TILE_SIZE; + let start_tile_y = + (viewbox.area.top * scale / TILE_SIZE).floor() * TILE_SIZE; + skia::Rect::from_xywh( + (tile_x as f32 * TILE_SIZE) - start_tile_x, + (tile_y as f32 * TILE_SIZE) - start_tile_y, + TILE_SIZE, + TILE_SIZE, + ) +} + // This structure is usseful to keep all the shape uuids by shape id. pub struct TileHashMap { grid: HashMap>, @@ -176,6 +208,10 @@ impl PendingTiles { } } + pub fn len(&self) -> usize { + self.list.len() + } + pub fn update(&mut self, tile_viewbox: &TileViewbox) { self.list.clear();