mirror of
https://github.com/penpot/penpot.git
synced 2025-02-22 14:57:01 -05:00
wip
This commit is contained in:
parent
067825aaed
commit
425d49aeb3
5 changed files with 194 additions and 72 deletions
|
@ -74,7 +74,8 @@ pub extern "C" fn set_canvas_background(raw_color: u32) {
|
|||
#[no_mangle]
|
||||
pub extern "C" fn render(timestamp: i32) {
|
||||
let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer");
|
||||
state.start_render_loop(timestamp).expect("Error rendering");
|
||||
// state.start_render_loop(timestamp).expect("Error rendering");
|
||||
state.start_render_loop_tiles(timestamp).expect("Error rendering tiles");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -108,9 +109,13 @@ pub extern "C" fn set_view(zoom: f32, x: f32, y: f32) {
|
|||
let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer");
|
||||
let render_state = state.render_state();
|
||||
render_state.invalidate_cache_if_needed();
|
||||
if zoom != render_state.viewbox.zoom {
|
||||
render_state.invalidate_tiles();
|
||||
}
|
||||
render_state.viewbox.set_all(zoom, x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_view_zoom(zoom: f32) {
|
||||
let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer");
|
||||
|
@ -124,6 +129,7 @@ pub extern "C" fn set_view_xy(x: f32, y: f32) {
|
|||
let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer");
|
||||
state.render_state().viewbox.set_pan_xy(x, y);
|
||||
}
|
||||
*/
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn use_shape(a: u32, b: u32, c: u32, d: u32) {
|
||||
|
|
|
@ -28,8 +28,8 @@ pub use images::*;
|
|||
const DEFAULT_FONT_BYTES: &[u8] =
|
||||
include_bytes!("../../frontend/resources/fonts/RobotoMono-Regular.ttf");
|
||||
|
||||
const MAX_BLOCKING_TIME_MS: i32 = 32;
|
||||
const NODE_BATCH_THRESHOLD: i32 = 10;
|
||||
const MAX_BLOCKING_TIME_MS: i32 = 320000;
|
||||
const NODE_BATCH_THRESHOLD: i32 = 100000;
|
||||
|
||||
extern "C" {
|
||||
fn emscripten_run_script(script: *const i8);
|
||||
|
@ -72,6 +72,7 @@ pub(crate) struct RenderState {
|
|||
pub pending_nodes: Vec<NodeRenderState>,
|
||||
pub current_tile: tiles::Tile,
|
||||
pub render_complete: bool,
|
||||
pub render_area: Rect,
|
||||
pub tiles: tiles::Tiles,
|
||||
}
|
||||
|
||||
|
@ -79,7 +80,7 @@ impl RenderState {
|
|||
pub fn new(width: i32, height: i32) -> RenderState {
|
||||
// This needs to be done once per WebGL context.
|
||||
let mut gpu_state = GpuState::new();
|
||||
let surfaces = Surfaces::new(&mut gpu_state, (width, height));
|
||||
let mut surfaces = Surfaces::new(&mut gpu_state, (width, height));
|
||||
let mut font_provider = skia::textlayout::TypefaceFontProvider::new();
|
||||
let default_font = skia::FontMgr::default()
|
||||
.new_from_data(DEFAULT_FONT_BYTES, None)
|
||||
|
@ -97,7 +98,7 @@ impl RenderState {
|
|||
|
||||
let debug_font = skia::Font::new(debug_typeface, 10.0);
|
||||
|
||||
let surface_pool = surfaces::SurfacePool::new(&mut final_surface, tiles::get_tile_dimensions());
|
||||
let surface_pool = surfaces::SurfacePool::new(&mut surfaces.target, tiles::get_tile_dimensions());
|
||||
let tiles = tiles::Tiles::new(surface_pool);
|
||||
|
||||
RenderState {
|
||||
|
@ -113,8 +114,9 @@ impl RenderState {
|
|||
background_color: skia::Color::TRANSPARENT,
|
||||
render_request_id: None,
|
||||
render_in_progress: false,
|
||||
pending_nodes: vec![],
|
||||
render_complete: true,
|
||||
render_area: Rect::new_empty(),
|
||||
pending_nodes: vec![],
|
||||
current_tile: (0, 0),
|
||||
tiles
|
||||
}
|
||||
|
@ -247,7 +249,10 @@ impl RenderState {
|
|||
.canvas()
|
||||
.clear(skia::Color::TRANSPARENT);
|
||||
|
||||
self.surfaces.shape.canvas().clear(skia::Color::TRANSPARENT);
|
||||
self.surfaces
|
||||
.shape
|
||||
.canvas()
|
||||
.clear(skia::Color::TRANSPARENT);
|
||||
}
|
||||
|
||||
pub fn invalidate_cache_if_needed(&mut self) {
|
||||
|
@ -391,6 +396,7 @@ impl RenderState {
|
|||
mask: false,
|
||||
}];
|
||||
|
||||
self.render_area = self.viewbox.area;
|
||||
self.render_in_progress = true;
|
||||
self.process_animation_frame(tree, modifiers, timestamp)?;
|
||||
self.render_complete = true;
|
||||
|
@ -417,45 +423,47 @@ impl RenderState {
|
|||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
timestamp: i32,
|
||||
) -> Result<(), String> {
|
||||
let area = self.render_area;
|
||||
if self.render_in_progress {
|
||||
self.render_shape_tree(tree, modifiers, timestamp)?;
|
||||
if self.render_in_progress {
|
||||
if let Some(frame_id) = self.render_request_id {
|
||||
self.cancel_animation_frame(frame_id);
|
||||
}
|
||||
self.render_request_id = Some(self.request_animation_frame());
|
||||
}
|
||||
self.render_shape_tree(tree, modifiers, area, timestamp)?;
|
||||
// if self.render_in_progress {
|
||||
// if let Some(frame_id) = self.render_request_id {
|
||||
// self.cancel_animation_frame(frame_id);
|
||||
// }
|
||||
// self.render_request_id = Some(self.request_animation_frame());
|
||||
// }
|
||||
}
|
||||
|
||||
// self.render_in_progress can have changed
|
||||
if self.render_in_progress {
|
||||
if self.cached_surface_image.is_some() {
|
||||
self.render_from_cache()?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// if self.render_in_progress {
|
||||
// if self.cached_surface_image.is_some() {
|
||||
// self.render_from_cache()?;
|
||||
// }
|
||||
// return Ok(());
|
||||
// }
|
||||
|
||||
// Chech if cached_surface_image is not set or is invalid
|
||||
if self
|
||||
.cached_surface_image
|
||||
.as_ref()
|
||||
.map_or(true, |img| img.invalid)
|
||||
{
|
||||
self.cached_surface_image = Some(CachedSurfaceImage {
|
||||
image: self.surfaces.current.image_snapshot(),
|
||||
viewbox: self.viewbox,
|
||||
invalid: false,
|
||||
has_all_shapes: self.render_complete,
|
||||
});
|
||||
}
|
||||
// if self
|
||||
// .cached_surface_image
|
||||
// .as_ref()
|
||||
// .map_or(true, |img| img.invalid)
|
||||
// {
|
||||
// self.cached_surface_image = Some(CachedSurfaceImage {
|
||||
// image: self.surfaces.current.image_snapshot(),
|
||||
// viewbox: self.viewbox,
|
||||
// invalid: false,
|
||||
// has_all_shapes: self.render_complete,
|
||||
// });
|
||||
// }
|
||||
|
||||
if self.options.is_debug_visible() {
|
||||
debug::render(self);
|
||||
}
|
||||
// NOTE: Esto lo he sacado al start_render_loop_tiles
|
||||
// if self.options.is_debug_visible() {
|
||||
// debug::render(self);
|
||||
// }
|
||||
|
||||
debug::render_wasm_label(self);
|
||||
self.apply_render_to_final_canvas();
|
||||
self.flush();
|
||||
// debug::render_wasm_label(self);
|
||||
// self.apply_render_to_final_canvas();
|
||||
// self.flush();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -500,61 +508,144 @@ impl RenderState {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_render_loop_tiles(
|
||||
&mut self,
|
||||
tree: &mut HashMap<Uuid, Shape>,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
timestamp: i32
|
||||
) -> Result<(), String> {
|
||||
self.reset_canvas();
|
||||
self.surfaces.shape.canvas().scale((
|
||||
self.viewbox.zoom * self.options.dpr(),
|
||||
self.viewbox.zoom * self.options.dpr(),
|
||||
));
|
||||
self.surfaces.shape
|
||||
.canvas()
|
||||
.translate((self.viewbox.pan_x, self.viewbox.pan_y));
|
||||
|
||||
let (sx, sy, ex, ey) = tiles::get_tiles_for_viewbox(self.viewbox);
|
||||
/*
|
||||
// TODO: Instead of rendering only the visible area
|
||||
// we could apply an offset to the viewbox to render
|
||||
// more tiles.
|
||||
sx - interest_delta
|
||||
sy - interest_delta
|
||||
ex + interest_delta
|
||||
ey + interest_delta
|
||||
*/
|
||||
for y in sy..=ey {
|
||||
for x in sx..=ex {
|
||||
let tile = (x, y);
|
||||
self.render_in_progress = true;
|
||||
let _ = self.start_render_loop_tile(tree, modifiers, tile, timestamp);
|
||||
}
|
||||
}
|
||||
self.render_complete = true;
|
||||
if self.options.is_debug_visible() {
|
||||
debug::render(self);
|
||||
}
|
||||
|
||||
debug::render_wasm_label(self);
|
||||
self.apply_render_to_final_canvas();
|
||||
self.flush();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_render_loop_tile(
|
||||
&mut self,
|
||||
tree: &mut HashMap<Uuid, Shape>,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
timestamp: i32,
|
||||
tile: (i32, i32)
|
||||
tile: (i32, i32),
|
||||
timestamp: i32
|
||||
) -> Result<(), String> {
|
||||
self.current_tile = tile;
|
||||
|
||||
if self.render_in_progress {
|
||||
if let Some(frame_id) = self.render_request_id {
|
||||
self.cancel_animation_frame(frame_id);
|
||||
}
|
||||
}
|
||||
println!("Renderizando tile {}:{} {}", tile.0, tile.1, self.render_in_progress);
|
||||
println!("Area {:?}", self.render_area);
|
||||
|
||||
self.reset_canvas();
|
||||
self.drawing_surface.canvas().scale((
|
||||
self.viewbox.zoom * self.options.dpr(),
|
||||
self.viewbox.zoom * self.options.dpr(),
|
||||
));
|
||||
self.drawing_surface
|
||||
.canvas()
|
||||
.translate((self.viewbox.pan_x, self.viewbox.pan_y));
|
||||
// NOTE: No debería afectar
|
||||
// if self.render_in_progress {
|
||||
// if let Some(frame_id) = self.render_request_id {
|
||||
// self.cancel_animation_frame(frame_id);
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: Check if this should use `self.pending_nodes.push` or the
|
||||
// assignment.
|
||||
self.pending_nodes = vec![NodeRenderState {
|
||||
id: Uuid::nil(),
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
}];
|
||||
|
||||
self.render_in_progress = true;
|
||||
self.process_animation_frame(tree, modifiers, timestamp)?;
|
||||
self.render_complete = true;
|
||||
// NOTE: I've moved this to start_render_loop_tiles
|
||||
// self.reset_canvas();
|
||||
// self.surfaces.shape.canvas().scale((
|
||||
// self.viewbox.zoom * self.options.dpr(),
|
||||
// self.viewbox.zoom * self.options.dpr(),
|
||||
// ));
|
||||
// self.surfaces.shape
|
||||
// .canvas()
|
||||
// .translate((self.viewbox.pan_x, self.viewbox.pan_y));
|
||||
|
||||
// If the tile is empty or it doesn't exists
|
||||
if !self.tiles.has_tile_at(tile) {
|
||||
println!("Salimos porque el tile está vacío {}:{}", tile.0, tile.1);
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// TODO: Check if this should use `self.pending_nodes.push` or the
|
||||
// assignment.
|
||||
// self.pending_nodes = vec![NodeRenderState {
|
||||
// id: Uuid::nil(),
|
||||
// visited_children: false,
|
||||
// clip_bounds: None,
|
||||
// visited_mask: false,
|
||||
// mask: false,
|
||||
// }];
|
||||
if let Some(shapes) = self.tiles.get_tile_at(tile) {
|
||||
self.pending_nodes = vec![NodeRenderState {
|
||||
id: Uuid::nil(),
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
}];
|
||||
/*
|
||||
for shape_id in shapes.iter() {
|
||||
let element = tree.get_mut(&shape_id).ok_or(
|
||||
"Error: Element with root_id {node_render_state.id} not found in the tree."
|
||||
.to_string(),
|
||||
)?;
|
||||
|
||||
let children_clip_bounds = if element.is_recursive() {
|
||||
(!element.id.is_nil() & element.clip()).then(|| {
|
||||
let bounds = element.selrect();
|
||||
let mut transform = element.transform;
|
||||
transform.post_translate(bounds.center());
|
||||
transform.pre_translate(-bounds.center());
|
||||
if let Some(modifiers) = modifiers.get(&element.id) {
|
||||
transform.post_concat(&modifiers);
|
||||
}
|
||||
let corners = match element.kind {
|
||||
Kind::Rect(_, corners) => corners,
|
||||
_ => None,
|
||||
};
|
||||
(bounds, corners, transform)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: *shape_id,
|
||||
visited_children: false,
|
||||
clip_bounds: None,
|
||||
clip_bounds: children_clip_bounds,
|
||||
visited_mask: false,
|
||||
mask: false,
|
||||
})
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
self.render_area = tiles::get_tile_rect(self.viewbox, tile);
|
||||
self.render_shape_tree(tree, modifiers, self.render_area, timestamp)?;
|
||||
// self.render_in_progress = true;
|
||||
// self.process_animation_frame(tree, modifiers, timestamp)?;
|
||||
// self.render_complete = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -617,6 +708,7 @@ impl RenderState {
|
|||
&mut self,
|
||||
tree: &mut HashMap<Uuid, Shape>,
|
||||
modifiers: &HashMap<Uuid, Matrix>,
|
||||
area: Rect,
|
||||
timestamp: i32,
|
||||
) -> Result<(), String> {
|
||||
if !self.render_in_progress {
|
||||
|
@ -640,7 +732,7 @@ impl RenderState {
|
|||
// FIXME: I think this name is ambiguous because render_in_progress indicates that the
|
||||
// render is still in progress but render_complete indicates that every element in the
|
||||
// shape tree is rendered. Maybe could this be called render_full or is_full_render?
|
||||
let render_complete = self.viewbox.area.contains(element.selrect());
|
||||
let render_complete = area.contains(element.selrect());
|
||||
if visited_children {
|
||||
if !visited_mask {
|
||||
match element.kind {
|
||||
|
@ -678,7 +770,7 @@ impl RenderState {
|
|||
|
||||
// If we didn't visited_children this shape, then we need to do
|
||||
if !node_render_state.id.is_nil() {
|
||||
if !element.selrect().intersects(self.viewbox.area) || element.hidden() {
|
||||
if !element.selrect().intersects(area) || element.hidden() {
|
||||
debug::render_debug_shape(self, element, false);
|
||||
self.render_complete = render_complete;
|
||||
continue;
|
||||
|
@ -745,10 +837,12 @@ impl RenderState {
|
|||
}
|
||||
|
||||
pub fn update_tile_for(&mut self, shape: &Shape) {
|
||||
println!("update_tile_for {}", shape.id);
|
||||
self.tiles.update_tile_for(self.viewbox, &shape);
|
||||
}
|
||||
|
||||
pub fn invalidate_tiles(&mut self) {
|
||||
println!("invalidate_tiles");
|
||||
self.tiles.invalidate_tiles();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ pub fn render_debug_shape(render_state: &mut RenderState, element: &Shape, inter
|
|||
|
||||
// Renders the tiles in the viewbox
|
||||
pub fn render_debug_viewbox_tiles(render_state: &mut RenderState) {
|
||||
let canvas = render_state.debug_surface.canvas();
|
||||
let canvas = render_state.surfaces.debug.canvas();
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_style(skia::PaintStyle::Stroke);
|
||||
paint.set_color(skia::Color::from_rgb(255, 0, 127));
|
||||
|
@ -78,7 +78,7 @@ pub fn render_debug_viewbox_tiles(render_state: &mut RenderState) {
|
|||
}
|
||||
|
||||
pub fn render_debug_tiles(render_state: &mut RenderState) {
|
||||
let canvas = render_state.debug_surface.canvas();
|
||||
let canvas = render_state.surfaces.debug.canvas();
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_style(skia::PaintStyle::Stroke);
|
||||
paint.set_color(skia::Color::from_rgb(127, 0, 255));
|
||||
|
|
|
@ -24,10 +24,27 @@ pub fn get_tiles_for_rect(rect: skia::Rect, tile_size: f32) -> (i32, i32, i32, i
|
|||
(sx, sy, ex, ey)
|
||||
}
|
||||
|
||||
pub fn get_tiles_for_viewbox(viewbox: Viewbox) -> (i32, i32, i32, i32) {
|
||||
let tile_size = get_tile_size(viewbox);
|
||||
get_tiles_for_rect(viewbox.area, tile_size)
|
||||
}
|
||||
|
||||
pub fn get_tile_pos(viewbox: Viewbox, (x, y): Tile) -> (f32, f32) {
|
||||
(x as f32 * get_tile_size(viewbox), y as f32 * get_tile_size(viewbox))
|
||||
}
|
||||
|
||||
pub fn get_tile_size(viewbox: Viewbox) -> f32 {
|
||||
1. / viewbox.zoom * TILE_SIZE
|
||||
}
|
||||
|
||||
pub fn get_tile_rect(viewbox: Viewbox, tile: Tile) -> skia::Rect {
|
||||
let (tx, ty) = get_tile_pos(viewbox, tile);
|
||||
let ts = get_tile_size(viewbox);
|
||||
skia::Rect::from_xywh(
|
||||
tx, ty, ts, ts
|
||||
)
|
||||
}
|
||||
|
||||
pub struct TileSurfaceCache {
|
||||
pool: SurfacePool,
|
||||
grid: HashMap<Tile, skia::Surface>
|
||||
|
@ -49,7 +66,7 @@ impl TileSurfaceCache {
|
|||
let surface = self.pool.allocate()?;
|
||||
self.grid.insert(tile, surface.clone());
|
||||
Ok(surface)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, tile: Tile, surface: skia::Surface) {
|
||||
self.grid.insert(tile, surface);
|
||||
|
|
|
@ -44,6 +44,11 @@ impl<'a> State<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_render_loop_tiles(&mut self, timestamp: i32) -> Result<(), String> {
|
||||
self.render_state.start_render_loop_tiles(&mut self.shapes, &self.modifiers, timestamp)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<(), String> {
|
||||
self.render_state
|
||||
.process_animation_frame(&mut self.shapes, &self.modifiers, timestamp)?;
|
||||
|
|
Loading…
Add table
Reference in a new issue