mirror of
https://github.com/penpot/penpot.git
synced 2025-05-29 05:36:10 +02:00
🐛 Fix paths performance in new render
This commit is contained in:
parent
8afd217a80
commit
96d44e0631
4 changed files with 183 additions and 22 deletions
|
@ -92,6 +92,74 @@ export function addShapeSolidFill(argb) {
|
|||
Module._add_shape_fill();
|
||||
}
|
||||
|
||||
export function addShapeSolidStroleFill(argb) {
|
||||
const ptr = allocBytes(160);
|
||||
const heap = getHeapU32();
|
||||
const dv = new DataView(heap.buffer);
|
||||
dv.setUint8(ptr, 0x00, true);
|
||||
dv.setUint32(ptr + 4, argb, true);
|
||||
Module._add_shape_stroke_fill();
|
||||
}
|
||||
|
||||
function serializePathAttrs(svgAttrs) {
|
||||
return Object.entries(svgAttrs).reduce((acc, [key, value]) => {
|
||||
return acc + key + '\0' + value + '\0';
|
||||
}, '');
|
||||
}
|
||||
|
||||
export function draw_star(x, y, width, height) {
|
||||
const len = 11; // 1 MOVE + 9 LINE + 1 CLOSE
|
||||
const ptr = allocBytes(len * 28);
|
||||
const heap = getHeapU32();
|
||||
const dv = new DataView(heap.buffer);
|
||||
|
||||
const cx = x + width / 2;
|
||||
const cy = y + height / 2;
|
||||
const outerRadius = Math.min(width, height) / 2;
|
||||
const innerRadius = outerRadius * 0.4;
|
||||
|
||||
const star = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const angle = Math.PI / 5 * i - Math.PI / 2;
|
||||
const r = i % 2 === 0 ? outerRadius : innerRadius;
|
||||
const px = cx + r * Math.cos(angle);
|
||||
const py = cy + r * Math.sin(angle);
|
||||
star.push([px, py]);
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
|
||||
// MOVE to first point
|
||||
dv.setUint16(ptr + offset + 0, 1, true); // MOVE
|
||||
dv.setFloat32(ptr + offset + 20, star[0][0], true);
|
||||
dv.setFloat32(ptr + offset + 24, star[0][1], true);
|
||||
offset += 28;
|
||||
|
||||
// LINE to remaining points
|
||||
for (let i = 1; i < star.length; i++) {
|
||||
dv.setUint16(ptr + offset + 0, 2, true); // LINE
|
||||
dv.setFloat32(ptr + offset + 20, star[i][0], true);
|
||||
dv.setFloat32(ptr + offset + 24, star[i][1], true);
|
||||
offset += 28;
|
||||
}
|
||||
|
||||
// CLOSE the path
|
||||
dv.setUint16(ptr + offset + 0, 4, true); // CLOSE
|
||||
|
||||
Module._set_shape_path_content();
|
||||
|
||||
const str = serializePathAttrs({
|
||||
"fill": "none",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round",
|
||||
});
|
||||
const size = str.length;
|
||||
offset = allocBytes(size);
|
||||
Module.stringToUTF8(str, offset, size);
|
||||
Module._set_shape_path_attrs(3);
|
||||
}
|
||||
|
||||
|
||||
export function setShapeChildren(shapeIds) {
|
||||
const offset = allocBytes(shapeIds.length * 16);
|
||||
const heap = getHeapU32();
|
||||
|
|
83
frontend/resources/wasm-playground/paths.html
Normal file
83
frontend/resources/wasm-playground/paths.html
Normal file
|
@ -0,0 +1,83 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>WASM + WebGL2 Canvas</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background: #111;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script type="module">
|
||||
import initWasmModule from '/js/render_wasm.js';
|
||||
import {
|
||||
init, addShapeSolidFill, assignCanvas, hexToU32ARGB, getRandomInt, getRandomColor,
|
||||
getRandomFloat, useShape, setShapeChildren, setupInteraction, set_parent, draw_star,
|
||||
addShapeSolidStroleFill
|
||||
} from './js/lib.js';
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
const shapes = 1000;
|
||||
|
||||
initWasmModule().then(Module => {
|
||||
init(Module);
|
||||
assignCanvas(canvas);
|
||||
Module._set_canvas_background(hexToU32ARGB("#FABADA", 1));
|
||||
Module._set_view(1, 0, 0);
|
||||
Module._init_shapes_pool(shapes + 1);
|
||||
setupInteraction(canvas);
|
||||
|
||||
const children = [];
|
||||
for (let i = 0; i < shapes; i++) {
|
||||
const uuid = crypto.randomUUID();
|
||||
children.push(uuid);
|
||||
|
||||
useShape(uuid);
|
||||
Module._set_parent(0, 0, 0, 0);
|
||||
Module._set_shape_type(4);
|
||||
const x1 = getRandomInt(0, canvas.width);
|
||||
const y1 = getRandomInt(0, canvas.height);
|
||||
const width = getRandomInt(20, 200);
|
||||
const height = getRandomInt(20, 200);
|
||||
Module._set_shape_selrect(x1, y1, x1 + width, y1 + height);
|
||||
draw_star(x1, y1, width, height);
|
||||
|
||||
const color = getRandomColor();
|
||||
const argb = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||
addShapeSolidFill(argb)
|
||||
|
||||
Module._add_shape_center_stroke(10, 0, 0, 0);
|
||||
const argb2 = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||
addShapeSolidStroleFill(argb2);
|
||||
|
||||
}
|
||||
|
||||
useShape("00000000-0000-0000-0000-000000000000");
|
||||
setShapeChildren(children);
|
||||
|
||||
performance.mark('render:begin');
|
||||
Module._render(Date.now());
|
||||
performance.mark('render:end');
|
||||
const { duration } = performance.measure('render', 'render:begin', 'render:end');
|
||||
// alert(`render time: ${duration.toFixed(2)}ms`);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -26,13 +26,13 @@
|
|||
import initWasmModule from '/js/render_wasm.js';
|
||||
import {
|
||||
init, addShapeSolidFill, assignCanvas, hexToU32ARGB, getRandomInt, getRandomColor,
|
||||
getRandomFloat, useShape, setShapeChildren, setupInteraction
|
||||
getRandomFloat, useShape, setShapeChildren, setupInteraction, addShapeSolidStroleFill
|
||||
} from './js/lib.js';
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
const shapes = 100;
|
||||
const shapes = 1000;
|
||||
|
||||
initWasmModule().then(Module => {
|
||||
init(Module);
|
||||
|
@ -59,6 +59,10 @@
|
|||
const color = getRandomColor();
|
||||
const argb = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||
addShapeSolidFill(argb)
|
||||
|
||||
Module._add_shape_center_stroke(10, 0, 0, 0);
|
||||
const argb2 = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||
addShapeSolidStroleFill(argb2);
|
||||
}
|
||||
|
||||
useShape("00000000-0000-0000-0000-000000000000");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use skia_safe::{self as skia, image, Matrix, RRect, Rect};
|
||||
|
||||
use crate::uuid::Uuid;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::performance;
|
||||
|
@ -339,11 +340,11 @@ impl RenderState {
|
|||
});
|
||||
}
|
||||
|
||||
// Clone so we don't change the value in the global state
|
||||
let mut shape = shape.clone();
|
||||
// We don't want to change the value in the global state
|
||||
let mut shape: Cow<Shape> = Cow::Borrowed(shape);
|
||||
|
||||
if let Some(modifiers) = modifiers {
|
||||
shape.apply_transform(modifiers);
|
||||
shape.to_mut().apply_transform(modifiers);
|
||||
}
|
||||
|
||||
let center = shape.center();
|
||||
|
@ -365,7 +366,7 @@ impl RenderState {
|
|||
match dom_result {
|
||||
Ok(dom) => {
|
||||
dom.render(self.surfaces.canvas(SurfaceId::Fills));
|
||||
shape.set_svg(dom);
|
||||
shape.to_mut().set_svg(dom);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error parsing SVG. Error: {}", e);
|
||||
|
@ -777,19 +778,24 @@ impl RenderState {
|
|||
}
|
||||
|
||||
if !node_render_state.id.is_nil() {
|
||||
// If we didn't visited_children this shape, then we need to do
|
||||
let mut transformed_element = element.clone();
|
||||
let mut transformed_element: Cow<Shape> = Cow::Borrowed(element);
|
||||
|
||||
if let Some(modifier) = modifiers.get(&node_id) {
|
||||
transformed_element.apply_transform(modifier);
|
||||
transformed_element.to_mut().apply_transform(modifier);
|
||||
}
|
||||
if !transformed_element.extrect().intersects(self.render_area)
|
||||
|| transformed_element.hidden()
|
||||
|| transformed_element.visually_insignificant(self.get_scale())
|
||||
{
|
||||
debug::render_debug_shape(self, &transformed_element, false);
|
||||
|
||||
let is_visible = transformed_element
|
||||
.extrect()
|
||||
.intersects(self.render_area)
|
||||
&& !transformed_element.hidden
|
||||
&& !transformed_element.visually_insignificant(self.get_scale());
|
||||
|
||||
if self.options.is_debug_visible() {
|
||||
debug::render_debug_shape(self, &transformed_element, is_visible);
|
||||
}
|
||||
|
||||
if !is_visible {
|
||||
continue;
|
||||
} else {
|
||||
debug::render_debug_shape(self, &transformed_element, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -957,10 +963,10 @@ impl RenderState {
|
|||
let mut nodes = vec![Uuid::nil()];
|
||||
while let Some(shape_id) = nodes.pop() {
|
||||
if let Some(shape) = tree.get_mut(&shape_id) {
|
||||
let mut shape = shape.clone();
|
||||
let mut shape: Cow<Shape> = Cow::Borrowed(shape);
|
||||
if shape_id != Uuid::nil() {
|
||||
if let Some(modifier) = modifiers.get(&shape_id) {
|
||||
shape.apply_transform(modifier);
|
||||
shape.to_mut().apply_transform(modifier);
|
||||
}
|
||||
self.update_tile_for(&shape);
|
||||
} else {
|
||||
|
@ -987,10 +993,10 @@ impl RenderState {
|
|||
let mut nodes = vec![Uuid::nil()];
|
||||
while let Some(shape_id) = nodes.pop() {
|
||||
if let Some(shape) = tree.get_mut(&shape_id) {
|
||||
let mut shape = shape.clone();
|
||||
let mut shape: Cow<Shape> = Cow::Borrowed(shape);
|
||||
if shape_id != Uuid::nil() {
|
||||
if let Some(modifier) = modifiers.get(&shape_id) {
|
||||
shape.apply_transform(modifier);
|
||||
shape.to_mut().apply_transform(modifier);
|
||||
}
|
||||
self.update_tile_for(&shape);
|
||||
}
|
||||
|
@ -1011,8 +1017,8 @@ impl RenderState {
|
|||
) {
|
||||
for (uuid, matrix) in modifiers {
|
||||
if let Some(shape) = tree.get_mut(uuid) {
|
||||
let mut shape: Shape = shape.clone();
|
||||
shape.apply_transform(matrix);
|
||||
let mut shape: Cow<Shape> = Cow::Borrowed(shape);
|
||||
shape.to_mut().apply_transform(matrix);
|
||||
self.update_tile_for(&shape);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue