Merge branch 'staging-render' into develop

This commit is contained in:
Andrey Antukh
2025-12-10 10:10:44 +01:00
60 changed files with 16588 additions and 12368 deletions

View File

@@ -163,6 +163,19 @@ pub extern "C" fn render_sync() {
pub extern "C" fn render_sync_shape(a: u32, b: u32, c: u32, d: u32) {
with_state_mut!(state, {
let id = uuid_from_u32_quartet(a, b, c, d);
state.use_shape(id);
// look for an existing root shape, and create it if missing
let mut was_root_missing = false;
if !state.shapes.has(&Uuid::nil()) {
state.shapes.add_shape(Uuid::nil());
was_root_missing = true;
}
if was_root_missing {
state.set_parent_for_current_shape(Uuid::nil());
}
state.rebuild_tiles_from(Some(&id));
state
.render_sync_shape(&id, performance::get_time())
@@ -330,6 +343,10 @@ fn set_children_set(entries: Vec<Uuid>) {
parent_id = Some(shape.id);
(_, deleted) = shape.compute_children_differences(&entries);
shape.children = entries.clone();
for id in entries {
state.touch_shape(id);
}
});
with_state_mut!(state, {
@@ -339,6 +356,7 @@ fn set_children_set(entries: Vec<Uuid>) {
for id in deleted {
state.delete_shape_children(parent_id, id);
state.touch_shape(id);
}
});
}
@@ -650,6 +668,26 @@ pub extern "C" fn set_modifiers() {
});
}
#[no_mangle]
pub extern "C" fn start_temp_objects() {
unsafe {
#[allow(static_mut_refs)]
let mut state = STATE.take().expect("Got an invalid state pointer");
state = Box::new(state.start_temp_objects());
STATE = Some(state);
}
}
#[no_mangle]
pub extern "C" fn end_temp_objects() {
unsafe {
#[allow(static_mut_refs)]
let mut state = STATE.take().expect("Got an invalid state pointer");
state = Box::new(state.end_temp_objects());
STATE = Some(state);
}
}
fn main() {
#[cfg(target_arch = "wasm32")]
init_gl!();

View File

@@ -34,9 +34,9 @@ pub use fonts::*;
pub use images::*;
// This is the extra are used for tile rendering.
const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 1;
const VIEWPORT_INTEREST_AREA_THRESHOLD: i32 = 2;
const MAX_BLOCKING_TIME_MS: i32 = 32;
const NODE_BATCH_THRESHOLD: i32 = 10;
const NODE_BATCH_THRESHOLD: i32 = 3;
type ClipStack = Vec<(Rect, Option<Corners>, Matrix)>;
@@ -141,7 +141,20 @@ impl NodeRenderState {
match &element.shape_type {
Type::Frame(_) => {
let bounds = element.get_selrect_shadow_bounds(shadow);
let mut bounds = element.get_selrect_shadow_bounds(shadow);
let blur_inset = (shadow.blur * 2.).max(0.0);
if blur_inset > 0.0 {
let max_inset_x = (bounds.width() * 0.5).max(0.0);
let max_inset_y = (bounds.height() * 0.5).max(0.0);
// Clamp the inset so we never shrink more than half of the width/height;
// otherwise the rect could end up inverted on small frames.
let inset_x = blur_inset.min(max_inset_x);
let inset_y = blur_inset.min(max_inset_y);
if inset_x > 0.0 || inset_y > 0.0 {
bounds.inset((inset_x, inset_y));
}
}
let mut transform = element.transform;
transform.post_translate(element.center());
transform.pre_translate(-element.center());
@@ -1721,6 +1734,7 @@ impl RenderState {
allow_stop: bool,
) -> Result<(), String> {
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) {
@@ -1794,17 +1808,21 @@ impl RenderState {
if !self.surfaces.has_cached_tile_surface(next_tile) {
if let Some(ids) = self.tiles.get_shapes_at(next_tile) {
let root_ids_map: std::collections::HashMap<Uuid, usize> = root_ids
.iter()
.enumerate()
.map(|(i, id)| (*id, i))
.collect();
// We only need first level shapes
let mut valid_ids: Vec<Uuid> = ids
.iter()
.filter(|id| root_ids.contains(id))
.filter(|id| root_ids_map.contains_key(id))
.copied()
.collect();
// These shapes for the tile should be ordered as they are in the parent node
valid_ids.sort_by_key(|id| {
root_ids.iter().position(|x| x == id).unwrap_or(usize::MAX)
});
valid_ids.sort_by_key(|id| root_ids_map.get(id).unwrap_or(&usize::MAX));
self.pending_nodes.extend(valid_ids.into_iter().map(|id| {
NodeRenderState {
@@ -1821,6 +1839,7 @@ impl RenderState {
should_stop = true;
}
}
self.render_in_progress = false;
self.surfaces.gc();

View File

@@ -8,8 +8,8 @@ use super::{gpu_state::GpuState, tiles::Tile, tiles::TileViewbox, tiles::TILE_SI
use base64::{engine::general_purpose, Engine as _};
use std::collections::{HashMap, HashSet};
const TEXTURES_CACHE_CAPACITY: usize = 512;
const TEXTURES_BATCH_DELETE: usize = 32;
const TEXTURES_CACHE_CAPACITY: usize = 1024;
const TEXTURES_BATCH_DELETE: usize = 256;
// This is the amount of extra space we're going to give to all the surfaces to render shapes.
// If it's too big it could affect performance.
const TILE_SIZE_MULTIPLIER: i32 = 2;

View File

@@ -290,7 +290,7 @@ fn propagate_reflow(
let mut skip_reflow = false;
if shape.is_layout_horizontal_fill() || shape.is_layout_vertical_fill() {
if let Some(parent_id) = shape.parent_id {
if !reflown.contains(&parent_id) {
if parent_id != Uuid::nil() && !reflown.contains(&parent_id) {
// If this is a fill layout but the parent has not been reflown yet
// we wait for the next iteration for reflow
skip_reflow = true;

View File

@@ -53,15 +53,6 @@ struct LayoutAxis {
is_auto_across: bool,
}
impl LayoutAxis {
fn main_space(&self) -> f32 {
self.main_size - self.padding_main_start - self.padding_main_end
}
fn across_space(&self) -> f32 {
self.across_size - self.padding_across_start - self.padding_across_end
}
}
impl LayoutAxis {
fn new(
shape: &Shape,
@@ -101,6 +92,13 @@ impl LayoutAxis {
}
}
}
fn main_space(&self) -> f32 {
self.main_size - self.padding_main_start - self.padding_main_end
}
fn across_space(&self) -> f32 {
self.across_size - self.padding_across_start - self.padding_across_end
}
}
#[derive(Debug, Copy, Clone)]
@@ -624,6 +622,9 @@ pub fn reflow_flex_layout(
}
result.push_back(Modifier::transform_propagate(child.id, transform));
if child.has_layout() {
result.push_back(Modifier::reflow(child.id));
}
shape_anchor = next_anchor(
layout_data,
@@ -654,7 +655,11 @@ pub fn reflow_flex_layout(
.iter()
.map(|track| {
let nshapes = usize::max(track.shapes.len(), 1);
track.shapes.iter().map(|s| s.main_size).sum::<f32>()
track
.shapes
.iter()
.map(|s| s.margin_main_start + s.margin_main_end + s.main_size)
.sum::<f32>()
+ (nshapes as f32 - 1.0) * layout_axis.gap_main
})
.reduce(f32::max)

View File

@@ -792,6 +792,9 @@ pub fn reflow_grid_layout(
}
result.push_back(Modifier::transform_propagate(child.id, transform));
if child.has_layout() {
result.push_back(Modifier::reflow(child.id));
}
}
if shape.is_layout_horizontal_auto() || shape.is_layout_vertical_auto() {

View File

@@ -24,6 +24,7 @@ pub(crate) struct State<'a> {
pub current_id: Option<Uuid>,
pub current_browser: u8,
pub shapes: ShapesPool<'a>,
pub saved_shapes: Option<ShapesPool<'a>>,
}
impl<'a> State<'a> {
@@ -34,9 +35,32 @@ impl<'a> State<'a> {
current_id: None,
current_browser: 0,
shapes: ShapesPool::new(),
// TODO: Maybe this can be moved to a different object
saved_shapes: None,
}
}
// Creates a new temporary shapes pool.
// Will panic if a previous temporary pool exists.
pub fn start_temp_objects(mut self) -> Self {
if self.saved_shapes.is_some() {
panic!("Tried to start a temp objects while the previous have not been restored");
}
self.saved_shapes = Some(self.shapes);
self.shapes = ShapesPool::new();
self
}
// Disposes of the temporary shapes pool restoring the normal pool
// Will panic if a there is no temporary pool.
pub fn end_temp_objects(mut self) -> Self {
self.shapes = self
.saved_shapes
.expect("Tried to end temp objects but not content to be restored is present");
self.saved_shapes = None;
self
}
pub fn resize(&mut self, width: i32, height: i32) {
self.render_state.resize(width, height);
}

View File

@@ -88,6 +88,7 @@ impl TileViewbox {
}
pub fn is_visible(&self, tile: &Tile) -> bool {
// TO CHECK self.interest_rect.contains(tile)
self.visible_rect.contains(tile)
}
}

View File

@@ -59,18 +59,19 @@ pub extern "C" fn set_layout_child_data(
is_absolute: bool,
z_index: i32,
) {
let h_sizing = RawSizing::from(h_sizing);
let v_sizing = RawSizing::from(v_sizing);
let max_h = if has_max_h { Some(max_h) } else { None };
let min_h = if has_min_h { Some(min_h) } else { None };
let max_w = if has_max_w { Some(max_w) } else { None };
let min_w = if has_min_w { Some(min_w) } else { None };
let raw_align_self = align::RawAlignSelf::from(align_self);
let align_self = raw_align_self.try_into().ok();
with_current_shape_mut!(state, |shape: &mut Shape| {
let h_sizing = RawSizing::from(h_sizing);
let v_sizing = RawSizing::from(v_sizing);
let max_h = if has_max_h { Some(max_h) } else { None };
let min_h = if has_min_h { Some(min_h) } else { None };
let max_w = if has_max_w { Some(max_w) } else { None };
let min_w = if has_min_w { Some(min_w) } else { None };
let raw_align_self = align::RawAlignSelf::from(align_self);
let align_self = raw_align_self.try_into().ok();
shape.set_flex_layout_child_data(
margin_top,
margin_right,