diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index 65ac296895..4ae8be8b17 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -135,6 +135,14 @@ (wasm.mem/free) text))) +(defn ^:export wasmRenderStats + [] + (let [module wasm/internal-module + f (when module (unchecked-get module "_render_stats"))] + (if (fn? f) + (wasm.h/call module "_render_stats") + (js/console.warn "[debug] render-wasm module not ready or missing _render_stats")))) + (defn ^:export wasmAtlasConsole "Logs the current render-wasm atlas as an image in the JS console (if present)." [] diff --git a/render-wasm/Cargo.toml b/render-wasm/Cargo.toml index a26e798e13..77d43ba94d 100644 --- a/render-wasm/Cargo.toml +++ b/render-wasm/Cargo.toml @@ -10,6 +10,7 @@ build = "build.rs" [features] default = [] +stats = [] profile = ["profile-macros", "profile-raf"] profile-macros = [] profile-raf = [] @@ -36,4 +37,6 @@ thiserror = "2.0.18" uuid = { version = "1.11.0", features = ["v4", "js"] } [profile.release] -opt-level = "s" +opt-level = 3 +lto = "fat" +strip = true diff --git a/render-wasm/_build_env b/render-wasm/_build_env index 1082af1aac..7f9c2e2486 100644 --- a/render-wasm/_build_env +++ b/render-wasm/_build_env @@ -31,8 +31,8 @@ export EM_MALLOC="dlmalloc" export EMCC_CFLAGS="--no-entry \ --js-library src/js/wapi.js \ - -sASSERTIONS=1 \ - -sALLOW_TABLE_GROWTH=1 \ + -sMALLOC=$EM_MALLOC \ + -sALLOW_TABLE_GROWTH=0 \ -sALLOW_MEMORY_GROWTH=1 \ -sINITIAL_HEAP=$EM_INITIAL_HEAP \ -sMEMORY_GROWTH_GEOMETRIC_STEP=$EM_MEMORY_GROWTH_GEOMETRIC_STEP \ @@ -50,13 +50,13 @@ export CARGO_PARAMS="${@:2}"; if [ "$BUILD_MODE" = "release" ]; then export CARGO_PARAMS="--release $CARGO_PARAMS" - export EMCC_CFLAGS="-Os $EMCC_CFLAGS" + export EMCC_CFLAGS="-O3 -sASSERTIONS=0 $EMCC_CFLAGS" else # TODO: Extra parameters that could be good to look into: # -gseparate-dwarf # -gsplit-dwarf # -gsource-map - export EMCC_CFLAGS="-g $EMCC_CFLAGS -sVERBOSE=1 -sMALLOC=$EM_MALLOC" + export EMCC_CFLAGS="-g -sASSERTIONS=1 -sVERBOSE=1 $EMCC_CFLAGS" fi function clean { diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 685afc898a..f0a1ab1381 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -1052,6 +1052,13 @@ pub extern "C" fn render_shape_pixels( }) } +#[no_mangle] +pub extern "C" fn render_stats() { + with_state!(state, { + state.render_state.print_stats(); + }) +} + fn main() { #[cfg(target_arch = "wasm32")] init_gl!(); diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 99c085c42a..8ecd87c5f2 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -15,6 +15,7 @@ mod ui; use skia_safe::{self as skia, Matrix, RRect, Rect}; use std::borrow::Cow; +use std::collections::HashMap; use std::collections::HashSet; use gpu_state::GpuState; @@ -289,9 +290,47 @@ fn sort_z_index(tree: ShapesPoolRef, element: &Shape, children_ids: Vec) - } } +struct RenderStats { + pub counts: HashMap, +} + +#[allow(dead_code)] +impl RenderStats { + pub fn new() -> Self { + Self { + counts: HashMap::new(), + } + } + + fn count(&mut self, id: Uuid) -> i32 { + let counter = self.counts.entry(id).or_insert(0); + *counter += 1; + *counter + } + + fn clear(&mut self) { + self.counts.clear(); + } + + #[allow(dead_code)] + fn get(&self, id: &Uuid) -> Option<&i32> { + self.counts.get(id) + } + + fn print(&self) { + let mut sum: i32 = 0; + for (&id, &count) in self.counts.iter() { + println!("{}: {}", id, count); + sum += count; + } + println!("{}: {}", self.counts.len(), sum); + } +} + pub(crate) struct RenderState { gpu_state: GpuState, pub options: RenderOptions, + stats: RenderStats, pub surfaces: Surfaces, pub fonts: FontStore, pub viewbox: Viewbox, @@ -388,6 +427,7 @@ impl RenderState { Ok(RenderState { gpu_state: gpu_state.clone(), options, + stats: RenderStats::new(), surfaces, fonts, viewbox, @@ -876,6 +916,9 @@ impl RenderState { outset: Option, target_surface: SurfaceId, ) -> Result<()> { + #[cfg(feature = "stats")] + self.stats.count(shape.id); + let surface_ids = fills_surface_id as u32 | strokes_surface_id as u32 | innershadows_surface_id as u32 @@ -1690,6 +1733,9 @@ impl RenderState { timestamp: i32, sync_render: bool, ) -> Result<()> { + #[cfg(feature = "stats")] + self.stats.clear(); + let _start = performance::begin_timed_log!("start_render_loop"); let scale = self.get_scale(); @@ -3443,6 +3489,10 @@ impl RenderState { pub fn set_view(&mut self, zoom: f32, x: f32, y: f32) { self.viewbox.set_all(zoom, x, y); } + + pub fn print_stats(&self) { + self.stats.print(); + } } impl Drop for RenderState { diff --git a/render-wasm/src/render/gpu_state.rs b/render-wasm/src/render/gpu_state.rs index aa62b83817..77f8816435 100644 --- a/render-wasm/src/render/gpu_state.rs +++ b/render-wasm/src/render/gpu_state.rs @@ -1,5 +1,8 @@ use crate::error::{Error, Result}; -use skia_safe::gpu::{self, gl::FramebufferInfo, gl::TextureInfo, DirectContext}; +use skia_safe::gpu::{ + self, ganesh::context_options::Enable, gl::FramebufferInfo, gl::TextureInfo, ContextOptions, + DirectContext, +}; use skia_safe::{self as skia, ISize}; #[derive(Debug, Clone)] @@ -13,7 +16,16 @@ impl GpuState { let interface = gpu::gl::Interface::new_native().ok_or(Error::CriticalError( "Failed to create GL interface".to_string(), ))?; - let context = gpu::direct_contexts::make_gl(interface, None).ok_or( + + // We tweak some options to enhance performance. + let mut context_options = ContextOptions::default(); + // context_options.reduce_ops_task_splitting = Enable::Yes; + context_options.skip_gl_error_checks = Enable::Yes; + // context_options.runtime_program_cache_size = 1024; + // context_options.allow_multiple_glyph_cache_textures = Enable::Yes; + // context_options.allow_path_mask_caching = false; + + let context = gpu::direct_contexts::make_gl(interface, Some(&context_options)).ok_or( Error::CriticalError("Failed to create GL context".to_string()), )?; let framebuffer_info = { diff --git a/render-wasm/src/render/strokes.rs b/render-wasm/src/render/strokes.rs index affd2a168c..3a142b05e6 100644 --- a/render-wasm/src/render/strokes.rs +++ b/render-wasm/src/render/strokes.rs @@ -231,8 +231,8 @@ fn draw_stroke_on_path( if let Some(pt) = path_transform { canvas.concat(pt); } - let skia_path = path.to_skia_path(svg_attrs); + let skia_path = path.to_skia_path(svg_attrs); match stroke.render_kind(is_open) { StrokeKind::Inner => { draw_inner_stroke_path(canvas, &skia_path, &draw_paint, blur, antialias); diff --git a/render-wasm/src/shapes/paths.rs b/render-wasm/src/shapes/paths.rs index 40d0d80067..ab7dc5acbf 100644 --- a/render-wasm/src/shapes/paths.rs +++ b/render-wasm/src/shapes/paths.rs @@ -1,7 +1,6 @@ -use skia_safe::{self as skia, Matrix}; - use crate::math; use crate::shapes::svg_attrs::{FillRule, SvgAttrs}; +use skia_safe::{self as skia, Matrix}; mod subpaths;