This commit is contained in:
Aitor Moreno 2026-04-24 09:30:41 +02:00
parent e3981a0cf3
commit 928fdd8bae
9 changed files with 98 additions and 14 deletions

View File

@ -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)."
[]

View File

@ -1035,6 +1035,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!();

View File

@ -16,6 +16,7 @@ mod ui;
use skia_safe::{self as skia, Matrix, RRect, Rect};
use std::borrow::Cow;
use std::collections::HashSet;
use std::collections::HashMap;
use gpu_state::GpuState;
@ -289,9 +290,46 @@ fn sort_z_index(tree: ShapesPoolRef, element: &Shape, children_ids: Vec<Uuid>) -
}
}
struct RenderStats {
pub counts: HashMap<Uuid, i32>,
}
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,
@ -377,6 +415,7 @@ impl RenderState {
Ok(RenderState {
gpu_state: gpu_state.clone(),
options,
stats: RenderStats::new(),
surfaces,
fonts,
viewbox,
@ -851,6 +890,8 @@ impl RenderState {
outset: Option<f32>,
target_surface: SurfaceId,
) -> Result<()> {
self.stats.count(shape.id);
let surface_ids = fills_surface_id as u32
| strokes_surface_id as u32
| innershadows_surface_id as u32
@ -1662,6 +1703,7 @@ impl RenderState {
timestamp: i32,
sync_render: bool,
) -> Result<()> {
self.stats.clear();
let _start = performance::begin_timed_log!("start_render_loop");
let scale = self.get_scale();
@ -3338,6 +3380,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 {

View File

@ -1,5 +1,5 @@
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, DirectContext, ContextOptions};
use skia_safe::{self as skia, ISize};
#[derive(Debug, Clone)]
@ -13,7 +13,18 @@ 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();
println!("context_options {:?}", context_options);
// 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;
println!("context_options {:?}", context_options);
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 = {

View File

@ -7,7 +7,7 @@ const SHOW_WASM_INFO: u32 = 0x08;
// Render performance options
// This is the extra area used for tile rendering (tiles beyond viewport).
// Higher values pre-render more tiles, reducing empty squares during pan but using more memory.
const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 3;
const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 1;
const MAX_BLOCKING_TIME_MS: i32 = 32;
const NODE_BATCH_THRESHOLD: i32 = 3;
const BLUR_DOWNSCALE_THRESHOLD: f32 = 8.0;

View File

@ -10,6 +10,9 @@ use crate::error::{Error, Result};
use crate::render::filters::compose_filters;
use crate::render::{get_dest_rect, get_source_rect};
#[cfg(target_arch = "wasm32")]
use crate::run_script;
#[allow(clippy::too_many_arguments)]
fn draw_stroke_on_rect(
canvas: &skia::Canvas,
@ -229,21 +232,21 @@ fn draw_stroke_on_path(
if let Some(pt) = path_transform {
canvas.concat(pt);
}
let skia_path = path.to_skia_path();
let skia_path = path.to_skia_path();
match stroke.render_kind(is_open) {
StrokeKind::Inner => {
draw_inner_stroke_path(canvas, &skia_path, &draw_paint, blur, antialias);
draw_inner_stroke_path(canvas, skia_path, &draw_paint, blur, antialias);
}
StrokeKind::Center => {
canvas.draw_path(&skia_path, &draw_paint);
canvas.draw_path(skia_path, &draw_paint);
}
StrokeKind::Outer => {
draw_outer_stroke_path(canvas, &skia_path, &draw_paint, blur, antialias);
draw_outer_stroke_path(canvas, skia_path, &draw_paint, blur, antialias);
}
}
handle_stroke_caps(&skia_path, stroke, canvas, is_open, paint, blur, antialias);
handle_stroke_caps(skia_path, stroke, canvas, is_open, paint, blur, antialias);
canvas.restore_to_count(save_count);
}

View File

@ -463,6 +463,7 @@ impl Surfaces {
}
pub fn flush_and_submit(&mut self, gpu_state: &mut GpuState, id: SurfaceId) {
println!("flush_and_submit");
let surface = self.get_mut(id);
gpu_state.context.flush_and_submit_surface(surface, None);
}

View File

@ -2,6 +2,9 @@ use skia_safe::{self as skia};
use indexmap::IndexSet;
#[cfg(target_arch = "wasm32")]
use crate::run_script;
use crate::uuid::Uuid;
use std::borrow::Cow;
use std::cell::{OnceCell, RefCell};
@ -1349,7 +1352,7 @@ impl Shape {
pub fn get_skia_path(&self) -> Option<skia::Path> {
if let Some(path) = self.shape_type.path() {
let mut skia_path = path.to_skia_path();
let mut skia_path = path.to_skia_path().clone();
if let Some(path_transform) = self.to_path_transform() {
skia_path = skia_path.make_transform(&path_transform);
}
@ -1392,6 +1395,8 @@ impl Shape {
if let shape_type @ (Type::Path(_) | Type::Bool(_)) = &mut self.shape_type {
if let Some(path) = shape_type.path_mut() {
#[cfg(target_arch = "wasm32")]
run_script!("console.count('path.transform')");
path.transform(transform);
}
} else if let Type::Text(text) = &mut self.shape_type {

View File

@ -1,7 +1,10 @@
use skia_safe::{self as skia, Matrix};
use std::{ops::Deref, rc::Rc};
use skia_safe::{self as skia, Matrix, wrapper::ValueWrapper};
use crate::math;
#[cfg(target_arch = "wasm32")]
use crate::run_script;
mod subpaths;
type Point = (f32, f32);
@ -19,7 +22,7 @@ impl Segment {}
#[derive(Debug, Clone, PartialEq)]
pub struct Path {
segments: Vec<Segment>,
skia_path: skia::Path,
pub skia_path: skia::Path,
open: bool,
}
@ -217,8 +220,8 @@ impl Path {
Path::new(segments)
}
pub fn to_skia_path(&self) -> skia::Path {
self.skia_path.snapshot()
pub fn to_skia_path(&self) -> &skia::Path {
&self.skia_path
}
pub fn contains(&self, p: skia::Point) -> bool {