♻️ Refactor GpuState and RenderState

* ♻️ Refactor GpuState

* ♻️ Refactor RenderState

* 🔧 Tweak some _build_env options
This commit is contained in:
Aitor Moreno 2026-05-08 11:10:14 +02:00 committed by GitHub
parent cccd7bc6de
commit 4e98dfb99f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 232 additions and 269 deletions

View File

@ -42,6 +42,8 @@ export EMCC_CFLAGS="--no-entry \
-sEXPORTED_RUNTIME_METHODS=GL,UTF8ToString,stringToUTF8,HEAPU8,HEAP32,HEAPU32,HEAPF32 \ -sEXPORTED_RUNTIME_METHODS=GL,UTF8ToString,stringToUTF8,HEAPU8,HEAP32,HEAPU32,HEAPF32 \
-sENVIRONMENT=web \ -sENVIRONMENT=web \
-sMODULARIZE=1 \ -sMODULARIZE=1 \
-sDISABLE_EXCEPTION_CATCHING=1 \
-sFILESYSTEM=0 \
-sEXPORT_ES6=1"; -sEXPORT_ES6=1";
export EM_CACHE="/tmp/emsdk_cache"; export EM_CACHE="/tmp/emsdk_cache";

View File

@ -22,6 +22,7 @@ use crate::state::TextEditorState;
use macros::wasm_error; use macros::wasm_error;
use math::{Bounds, Matrix}; use math::{Bounds, Matrix};
use mem::SerializableResult; use mem::SerializableResult;
use render::{gpu_state::GpuState, RenderState};
use shapes::{StructureEntry, StructureEntryType, TransformEntry}; use shapes::{StructureEntry, StructureEntryType, TransformEntry};
use skia_safe as skia; use skia_safe as skia;
use state::State; use state::State;
@ -39,6 +40,28 @@ pub fn get_text_editor_state() -> &'static mut TextEditorState {
} }
} }
/// GPU State.
static mut GPU_STATE: *mut GpuState = std::ptr::null_mut();
#[inline(always)]
pub(crate) fn get_gpu_state() -> &'static mut GpuState {
unsafe {
debug_assert!(!GPU_STATE.is_null(), "GPU State is null");
&mut *GPU_STATE
}
}
/// Render State.
static mut RENDER_STATE: *mut RenderState = std::ptr::null_mut();
#[inline(always)]
pub(crate) fn get_render_state() -> &'static mut RenderState {
unsafe {
debug_assert!(!RENDER_STATE.is_null(), "Render State is null");
&mut *RENDER_STATE
}
}
// FIXME: These with_state* macros should be using our CriticalError instead of expect. // FIXME: These with_state* macros should be using our CriticalError instead of expect.
// But to do that, we need to not use them at domain-level (i.e. in business logic), just // But to do that, we need to not use them at domain-level (i.e. in business logic), just
// in the context of the wasm call. // in the context of the wasm call.
@ -111,11 +134,30 @@ macro_rules! with_state_mut_current_shape {
}; };
} }
/// Initializes GPU.
fn gpu_init() {
unsafe {
let gpu_state = GpuState::try_new().expect("Cannot initialize GPU State");
GPU_STATE = Box::into_raw(Box::new(gpu_state));
}
}
/// Initializes RenderState.
fn render_init(width: i32, height: i32) {
unsafe {
let render_state =
RenderState::try_new(width, height).expect("Cannot intialize RenderState");
RENDER_STATE = Box::into_raw(Box::new(render_state));
}
}
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn init(width: i32, height: i32) -> Result<()> { pub extern "C" fn init(width: i32, height: i32) -> Result<()> {
let state_box = Box::new(State::try_new(width, height)?); gpu_init();
render_init(width, height);
unsafe { unsafe {
let state_box = Box::new(State::new());
STATE = Some(state_box); STATE = Some(state_box);
TEXT_EDITOR_STATE = Box::into_raw(Box::new(TextEditorState::new())); TEXT_EDITOR_STATE = Box::into_raw(Box::new(TextEditorState::new()));
} }
@ -134,12 +176,10 @@ pub extern "C" fn set_browser(browser: u8) -> Result<()> {
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn clean_up() -> Result<()> { pub extern "C" fn clean_up() -> Result<()> {
with_state_mut!(state, { // Cancel the current animation frame if it exists so
// Cancel the current animation frame if it exists so // it won't try to render without context
// it won't try to render without context let render_state = get_render_state();
let render_state = state.render_state_mut(); render_state.cancel_animation_frame();
render_state.cancel_animation_frame();
});
unsafe { STATE = None } unsafe { STATE = None }
mem::free_bytes()?; mem::free_bytes()?;
Ok(()) Ok(())
@ -148,11 +188,9 @@ pub extern "C" fn clean_up() -> Result<()> {
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_render_options(debug: u32, dpr: f32) -> Result<()> { pub extern "C" fn set_render_options(debug: u32, dpr: f32) -> Result<()> {
with_state_mut!(state, { let render_state = get_render_state();
let render_state = state.render_state_mut(); render_state.set_debug_flags(debug);
render_state.set_debug_flags(debug); render_state.set_dpr(dpr)?;
render_state.set_dpr(dpr)?;
});
Ok(()) Ok(())
} }
@ -161,61 +199,48 @@ pub extern "C" fn set_render_options(debug: u32, dpr: f32) -> Result<()> {
pub extern "C" fn set_viewport_interest_area_threshold( pub extern "C" fn set_viewport_interest_area_threshold(
viewport_interest_area_threshold: i32, viewport_interest_area_threshold: i32,
) -> Result<()> { ) -> Result<()> {
with_state_mut!(state, { let render_state = get_render_state();
let render_state = state.render_state_mut(); render_state.set_viewport_interest_area_threshold(viewport_interest_area_threshold);
render_state.set_viewport_interest_area_threshold(viewport_interest_area_threshold);
});
Ok(()) Ok(())
} }
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_max_blocking_time_ms(max_blocking_time_ms: i32) -> Result<()> { pub extern "C" fn set_max_blocking_time_ms(max_blocking_time_ms: i32) -> Result<()> {
with_state_mut!(state, { let render_state = get_render_state();
let render_state = state.render_state_mut(); render_state.set_max_blocking_time_ms(max_blocking_time_ms);
render_state.set_max_blocking_time_ms(max_blocking_time_ms);
});
Ok(()) Ok(())
} }
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_node_batch_threshold(node_batch_threshold: i32) -> Result<()> { pub extern "C" fn set_node_batch_threshold(node_batch_threshold: i32) -> Result<()> {
with_state_mut!(state, { let render_state = get_render_state();
let render_state = state.render_state_mut(); render_state.set_node_batch_threshold(node_batch_threshold);
render_state.set_node_batch_threshold(node_batch_threshold);
});
Ok(()) Ok(())
} }
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_blur_downscale_threshold(blur_downscale_threshold: f32) -> Result<()> { pub extern "C" fn set_blur_downscale_threshold(blur_downscale_threshold: f32) -> Result<()> {
with_state_mut!(state, { let render_state = get_render_state();
let render_state = state.render_state_mut(); render_state.set_blur_downscale_threshold(blur_downscale_threshold);
render_state.set_blur_downscale_threshold(blur_downscale_threshold);
});
Ok(()) Ok(())
} }
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_antialias_threshold(threshold: f32) -> Result<()> { pub extern "C" fn set_antialias_threshold(threshold: f32) -> Result<()> {
with_state_mut!(state, { get_render_state().set_antialias_threshold(threshold);
state.render_state_mut().set_antialias_threshold(threshold);
});
Ok(()) Ok(())
} }
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_max_atlas_texture_size(max_px: i32) -> Result<()> { pub extern "C" fn set_max_atlas_texture_size(max_px: i32) -> Result<()> {
with_state_mut!(state, { get_render_state()
state .surfaces
.render_state_mut() .set_max_atlas_texture_size(max_px);
.surfaces
.set_max_atlas_texture_size(max_px);
});
Ok(()) Ok(())
} }
@ -241,7 +266,7 @@ pub extern "C" fn render(timestamp: i32) -> Result<()> {
// interactive_transform; we do it once here, with the current // interactive_transform; we do it once here, with the current
// modifier set, so the cost is paid once per rAF rather than // modifier set, so the cost is paid once per rAF rather than
// once per pointer move. // once per pointer move.
if state.render_state.options.is_interactive_transform() { if get_render_state().options.is_interactive_transform() {
let ids = state.shapes.modifier_ids(); let ids = state.shapes.modifier_ids();
if !ids.is_empty() { if !ids.is_empty() {
state.rebuild_modifier_tiles(ids)?; state.rebuild_modifier_tiles(ids)?;
@ -311,9 +336,7 @@ pub extern "C" fn render_from_cache(_: i32) -> Result<()> {
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_preview_mode(enabled: bool) -> Result<()> { pub extern "C" fn set_preview_mode(enabled: bool) -> Result<()> {
with_state_mut!(state, { get_render_state().set_preview_mode(enabled);
state.render_state.set_preview_mode(enabled);
});
Ok(()) Ok(())
} }
@ -356,9 +379,7 @@ pub extern "C" fn end_loading() -> Result<()> {
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn render_loading_overlay() -> Result<()> { pub extern "C" fn render_loading_overlay() -> Result<()> {
with_state_mut!(state, { get_render_state().render_loading_overlay();
state.render_state.render_loading_overlay();
});
Ok(()) Ok(())
} }
@ -375,30 +396,24 @@ pub extern "C" fn process_animation_frame(timestamp: i32) -> Result<()> {
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn reset_canvas() -> Result<()> { pub extern "C" fn reset_canvas() -> Result<()> {
with_state_mut!(state, { get_render_state().reset_canvas();
state.render_state_mut().reset_canvas();
});
Ok(()) Ok(())
} }
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn resize_viewbox(width: i32, height: i32) -> Result<()> { pub extern "C" fn resize_viewbox(width: i32, height: i32) -> Result<()> {
with_state_mut!(state, { get_render_state().resize(width, height)?;
state.resize(width, height)?;
});
Ok(()) Ok(())
} }
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_view(zoom: f32, x: f32, y: f32) -> Result<()> { pub extern "C" fn set_view(zoom: f32, x: f32, y: f32) -> Result<()> {
with_state_mut!(state, { performance::begin_measure!("set_view");
performance::begin_measure!("set_view"); let render_state = get_render_state();
let render_state = state.render_state_mut(); render_state.set_view(zoom, x, y);
render_state.set_view(zoom, x, y); performance::end_measure!("set_view");
performance::end_measure!("set_view");
});
Ok(()) Ok(())
} }
@ -408,15 +423,13 @@ static mut VIEW_INTERACTION_START: i32 = 0;
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_view_start() -> Result<()> { pub extern "C" fn set_view_start() -> Result<()> {
with_state_mut!(state, { #[cfg(feature = "profile-macros")]
#[cfg(feature = "profile-macros")] unsafe {
unsafe { VIEW_INTERACTION_START = performance::get_time();
VIEW_INTERACTION_START = performance::get_time(); }
} performance::begin_measure!("set_view_start");
performance::begin_measure!("set_view_start"); get_render_state().options.set_fast_mode(true);
state.render_state.options.set_fast_mode(true); performance::end_measure!("set_view_start");
performance::end_measure!("set_view_start");
});
Ok(()) Ok(())
} }
@ -430,32 +443,32 @@ pub extern "C" fn set_view_start() -> Result<()> {
pub extern "C" fn set_view_end() -> Result<()> { pub extern "C" fn set_view_end() -> Result<()> {
with_state_mut!(state, { with_state_mut!(state, {
performance::begin_measure!("set_view_end"); performance::begin_measure!("set_view_end");
state.render_state.options.set_fast_mode(false); let render_state = get_render_state();
state.render_state.cancel_animation_frame(); render_state.options.set_fast_mode(false);
render_state.cancel_animation_frame();
let scale = state.render_state.get_scale(); let scale = render_state.get_scale();
state render_state
.render_state
.tile_viewbox .tile_viewbox
.update(state.render_state.viewbox, scale); .update(render_state.viewbox, scale);
if state.render_state.options.is_profile_rebuild_tiles() { if render_state.options.is_profile_rebuild_tiles() {
state.rebuild_tiles(); state.rebuild_tiles();
} else if state.render_state.zoom_changed() { } else if render_state.zoom_changed() {
// Zoom changed: tile sizes differ so all cached tile // Zoom changed: tile sizes differ so all cached tile
// textures are invalid (wrong scale). Rebuild the tile // textures are invalid (wrong scale). Rebuild the tile
// index and clear the tile texture cache, but *preserve* // index and clear the tile texture cache, but *preserve*
// the cache canvas so render_from_cache can show a scaled // the cache canvas so render_from_cache can show a scaled
// preview of the old content while new tiles render. // preview of the old content while new tiles render.
state.render_state.rebuild_tile_index(&state.shapes); render_state.rebuild_tile_index(&state.shapes);
state.render_state.surfaces.invalidate_tile_cache(); render_state.surfaces.invalidate_tile_cache();
} else { } else {
// Pure pan at the same zoom level: tile contents have not // Pure pan at the same zoom level: tile contents have not
// changed — only the viewport position moved. Update the // changed — only the viewport position moved. Update the
// tile index (which tiles are in the interest area) but // tile index (which tiles are in the interest area) but
// keep cached tile textures so the render can blit them // keep cached tile textures so the render can blit them
// instead of re-drawing every visible tile from scratch. // instead of re-drawing every visible tile from scratch.
state.render_state.rebuild_tile_index(&state.shapes); render_state.rebuild_tile_index(&state.shapes);
} }
performance::end_measure!("set_view_end"); performance::end_measure!("set_view_end");
}); });
@ -470,12 +483,11 @@ pub extern "C" fn set_view_end() -> Result<()> {
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_modifiers_start() -> Result<()> { pub extern "C" fn set_modifiers_start() -> Result<()> {
with_state_mut!(state, { performance::begin_measure!("set_modifiers_start");
performance::begin_measure!("set_modifiers_start"); let render_state = get_render_state();
state.render_state.options.set_fast_mode(true); render_state.options.set_fast_mode(true);
state.render_state.options.set_interactive_transform(true); render_state.options.set_interactive_transform(true);
performance::end_measure!("set_modifiers_start"); performance::end_measure!("set_modifiers_start");
});
Ok(()) Ok(())
} }
@ -486,13 +498,12 @@ pub extern "C" fn set_modifiers_start() -> Result<()> {
#[no_mangle] #[no_mangle]
#[wasm_error] #[wasm_error]
pub extern "C" fn set_modifiers_end() -> Result<()> { pub extern "C" fn set_modifiers_end() -> Result<()> {
with_state_mut!(state, { performance::begin_measure!("set_modifiers_end");
performance::begin_measure!("set_modifiers_end"); let render_state = get_render_state();
state.render_state.options.set_fast_mode(false); render_state.options.set_fast_mode(false);
state.render_state.options.set_interactive_transform(false); render_state.options.set_interactive_transform(false);
state.render_state.cancel_animation_frame(); render_state.cancel_animation_frame();
performance::end_measure!("set_modifiers_end"); performance::end_measure!("set_modifiers_end");
});
Ok(()) Ok(())
} }
@ -808,11 +819,9 @@ pub extern "C" fn is_image_cached(
d: u32, d: u32,
is_thumbnail: bool, is_thumbnail: bool,
) -> Result<bool> { ) -> Result<bool> {
with_state_mut!(state, { let id = uuid_from_u32_quartet(a, b, c, d);
let id = uuid_from_u32_quartet(a, b, c, d); let result = get_render_state().has_image(&id, is_thumbnail);
let result = state.render_state().has_image(&id, is_thumbnail); Ok(result)
Ok(result)
})
} }
#[no_mangle] #[no_mangle]
@ -961,15 +970,14 @@ pub extern "C" fn set_structure_modifiers() -> Result<()> {
#[wasm_error] #[wasm_error]
pub extern "C" fn clean_modifiers() -> Result<()> { pub extern "C" fn clean_modifiers() -> Result<()> {
with_state_mut!(state, { with_state_mut!(state, {
let render_state = get_render_state();
let prev_modifier_ids = state.shapes.clean_all(); let prev_modifier_ids = state.shapes.clean_all();
// Skip the tile-cache cleanup during interactive transform: the // Skip the tile-cache cleanup during interactive transform: the
// per-rAF `rebuild_modifier_tiles` in `render()` already evicts // per-rAF `rebuild_modifier_tiles` in `render()` already evicts
// the same tiles for the active modifier set, so the eviction // the same tiles for the active modifier set, so the eviction
// here is redundant and doubles the per-emission cost. // here is redundant and doubles the per-emission cost.
if !prev_modifier_ids.is_empty() && !state.render_state.options.is_interactive_transform() { if !prev_modifier_ids.is_empty() && !render_state.options.is_interactive_transform() {
state render_state.update_tiles_shapes(&prev_modifier_ids, &mut state.shapes)?;
.render_state
.update_tiles_shapes(&prev_modifier_ids, &mut state.shapes)?;
} }
}); });
Ok(()) Ok(())
@ -995,7 +1003,7 @@ pub extern "C" fn set_modifiers() -> Result<()> {
with_state_mut!(state, { with_state_mut!(state, {
state.set_modifiers(modifiers); state.set_modifiers(modifiers);
// TO CHECK // TO CHECK
if !state.render_state.options.is_interactive_transform() { if !get_render_state().options.is_interactive_transform() {
state.rebuild_modifier_tiles(ids)?; state.rebuild_modifier_tiles(ids)?;
} }
}); });
@ -1061,9 +1069,7 @@ pub extern "C" fn render_shape_pixels(
#[no_mangle] #[no_mangle]
pub extern "C" fn render_stats() { pub extern "C" fn render_stats() {
with_state!(state, { get_render_state().print_stats();
state.render_state.print_stats();
})
} }
fn main() { fn main() {

View File

@ -2,7 +2,7 @@ mod debug;
mod fills; mod fills;
pub mod filters; pub mod filters;
mod fonts; mod fonts;
mod gpu_state; pub mod gpu_state;
pub mod grid_layout; pub mod grid_layout;
mod images; mod images;
mod options; mod options;
@ -17,13 +17,10 @@ use skia_safe::{self as skia, Matrix, RRect, Rect};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use gpu_state::GpuState;
use options::RenderOptions; use options::RenderOptions;
pub use surfaces::{SurfaceId, Surfaces}; pub use surfaces::{SurfaceId, Surfaces};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::performance;
use crate::shapes::{ use crate::shapes::{
all_with_ancestors, radius_to_sigma, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, all_with_ancestors, radius_to_sigma, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor,
Stroke, StrokeKind, TextContent, Type, Stroke, StrokeKind, TextContent, Type,
@ -33,6 +30,7 @@ use crate::tiles::{self, PendingTiles, TileRect};
use crate::uuid::Uuid; use crate::uuid::Uuid;
use crate::view::Viewbox; use crate::view::Viewbox;
use crate::wapi; use crate::wapi;
use crate::{get_gpu_state, performance};
pub use fonts::*; pub use fonts::*;
pub use images::*; pub use images::*;
@ -327,7 +325,6 @@ impl RenderStats {
} }
pub(crate) struct RenderState { pub(crate) struct RenderState {
gpu_state: GpuState,
pub options: RenderOptions, pub options: RenderOptions,
stats: RenderStats, stats: RenderStats,
pub surfaces: Surfaces, pub surfaces: Surfaces,
@ -492,13 +489,11 @@ impl RenderState {
pub fn try_new(width: i32, height: i32) -> Result<RenderState> { pub fn try_new(width: i32, height: i32) -> Result<RenderState> {
// This needs to be done once per WebGL context. // This needs to be done once per WebGL context.
let mut gpu_state = GpuState::try_new()?;
let sampling_options = let sampling_options =
skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest); skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest);
let fonts = FontStore::try_new()?; let fonts = FontStore::try_new()?;
let surfaces = Surfaces::try_new( let surfaces = Surfaces::try_new(
&mut gpu_state,
(width, height), (width, height),
sampling_options, sampling_options,
tiles::get_tile_dimensions(), tiles::get_tile_dimensions(),
@ -511,15 +506,14 @@ impl RenderState {
let tiles = tiles::TileHashMap::new(); let tiles = tiles::TileHashMap::new();
let options = RenderOptions::default(); let options = RenderOptions::default();
Ok(RenderState { Ok(Self {
gpu_state: gpu_state.clone(),
options, options,
stats: RenderStats::new(), stats: RenderStats::new(),
surfaces, surfaces,
fonts, fonts,
viewbox, viewbox,
cached_viewbox: Viewbox::new(0., 0.), cached_viewbox: Viewbox::new(0., 0.),
images: ImageStore::new(gpu_state.context.clone()), images: ImageStore::new(),
background_color: skia::Color::TRANSPARENT, background_color: skia::Color::TRANSPARENT,
render_request_id: None, render_request_id: None,
render_in_progress: false, render_in_progress: false,
@ -801,8 +795,7 @@ impl RenderState {
pub fn resize(&mut self, width: i32, height: i32) -> Result<()> { pub fn resize(&mut self, width: i32, height: i32) -> Result<()> {
let dpr_width = (width as f32 * self.options.dpr).floor() as i32; let dpr_width = (width as f32 * self.options.dpr).floor() as i32;
let dpr_height = (height as f32 * self.options.dpr).floor() as i32; let dpr_height = (height as f32 * self.options.dpr).floor() as i32;
self.surfaces self.surfaces.resize(dpr_width, dpr_height)?;
.resize(&mut self.gpu_state, dpr_width, dpr_height)?;
self.viewbox.set_wh(width as f32, height as f32); self.viewbox.set_wh(width as f32, height as f32);
self.tile_viewbox.update(self.viewbox, self.get_scale()); self.tile_viewbox.update(self.viewbox, self.get_scale());
@ -810,8 +803,7 @@ impl RenderState {
} }
pub fn flush_and_submit(&mut self) { pub fn flush_and_submit(&mut self) {
self.surfaces self.surfaces.flush_and_submit(SurfaceId::Target);
.flush_and_submit(&mut self.gpu_state, SurfaceId::Target);
} }
pub fn reset_canvas(&mut self) { pub fn reset_canvas(&mut self) {
@ -890,7 +882,6 @@ impl RenderState {
.as_ref() .as_ref()
.ok_or(Error::CriticalError("Current tile not found".to_string()))?; .ok_or(Error::CriticalError("Current tile not found".to_string()))?;
self.surfaces.cache_current_tile_texture( self.surfaces.cache_current_tile_texture(
&mut self.gpu_state,
&self.tile_viewbox, &self.tile_viewbox,
&current_tile, &current_tile,
&tile_rect, &tile_rect,
@ -2203,13 +2194,12 @@ impl RenderState {
// Clear export context so get_scale() returns to workspace zoom. // Clear export context so get_scale() returns to workspace zoom.
self.export_context = None; self.export_context = None;
self.surfaces self.surfaces.flush_and_submit(target_surface);
.flush_and_submit(&mut self.gpu_state, target_surface);
let image = self.surfaces.snapshot(target_surface); let image = self.surfaces.snapshot(target_surface);
let data = image let data = image
.encode( .encode(
&mut self.gpu_state.context, Some(&mut get_gpu_state().context),
skia::EncodedImageFormat::PNG, skia::EncodedImageFormat::PNG,
100, 100,
) )
@ -3590,8 +3580,7 @@ impl RenderState {
} }
pub fn remove_cached_tile(&mut self, tile: tiles::Tile) { pub fn remove_cached_tile(&mut self, tile: tiles::Tile) {
self.surfaces self.surfaces.remove_cached_tile_surface(tile);
.remove_cached_tile_surface(&mut self.gpu_state, tile);
} }
/// Rebuild the tile index (shape→tile mapping) for all top-level shapes. /// Rebuild the tile index (shape→tile mapping) for all top-level shapes.
@ -3790,6 +3779,6 @@ impl RenderState {
impl Drop for RenderState { impl Drop for RenderState {
fn drop(&mut self) { fn drop(&mut self) {
self.gpu_state.context.free_gpu_resources(); get_gpu_state().context.free_gpu_resources();
} }
} }

View File

@ -1,7 +1,11 @@
use super::{tiles, RenderState, SurfaceId}; use super::{tiles, RenderState, SurfaceId};
use crate::with_state_mut;
use crate::STATE; #[cfg(target_arch = "wasm32")]
use macros::wasm_error; use macros::wasm_error;
#[cfg(target_arch = "wasm32")]
use crate::get_render_state;
use skia_safe::{self as skia, Rect}; use skia_safe::{self as skia, Rect};
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@ -227,9 +231,7 @@ pub fn console_debug_surface_rect(render_state: &mut RenderState, id: SurfaceId,
#[wasm_error] #[wasm_error]
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub extern "C" fn debug_cache_console() -> Result<()> { pub extern "C" fn debug_cache_console() -> Result<()> {
with_state_mut!(state, { console_debug_surface(get_render_state(), SurfaceId::Cache);
console_debug_surface(state.render_state_mut(), SurfaceId::Cache);
});
Ok(()) Ok(())
} }
@ -237,9 +239,7 @@ pub extern "C" fn debug_cache_console() -> Result<()> {
#[wasm_error] #[wasm_error]
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub extern "C" fn debug_cache_base64() -> Result<()> { pub extern "C" fn debug_cache_base64() -> Result<()> {
with_state_mut!(state, { console_debug_surface_base64(get_render_state(), SurfaceId::Cache);
console_debug_surface_base64(state.render_state_mut(), SurfaceId::Cache);
});
Ok(()) Ok(())
} }
@ -247,9 +247,7 @@ pub extern "C" fn debug_cache_base64() -> Result<()> {
#[wasm_error] #[wasm_error]
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub extern "C" fn debug_atlas_console() -> Result<()> { pub extern "C" fn debug_atlas_console() -> Result<()> {
with_state_mut!(state, { console_debug_surface(get_render_state(), SurfaceId::Atlas);
console_debug_surface(state.render_state_mut(), SurfaceId::Atlas);
});
Ok(()) Ok(())
} }
@ -257,8 +255,6 @@ pub extern "C" fn debug_atlas_console() -> Result<()> {
#[wasm_error] #[wasm_error]
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub extern "C" fn debug_atlas_base64() -> Result<()> { pub extern "C" fn debug_atlas_base64() -> Result<()> {
with_state_mut!(state, { console_debug_surface_base64(get_render_state(), SurfaceId::Atlas);
console_debug_surface_base64(state.render_state_mut(), SurfaceId::Atlas);
});
Ok(()) Ok(())
} }

View File

@ -41,7 +41,7 @@ impl GpuState {
} }
}; };
Ok(GpuState { Ok(Self {
context, context,
framebuffer_info, framebuffer_info,
}) })

View File

@ -3,6 +3,7 @@ use crate::shapes::ImageFill;
use crate::uuid::Uuid; use crate::uuid::Uuid;
use crate::error::Result; use crate::error::Result;
use crate::get_gpu_state;
use skia_safe::gpu::{surfaces, Budgeted, DirectContext}; use skia_safe::gpu::{surfaces, Budgeted, DirectContext};
use skia_safe::{self as skia, Codec, ISize}; use skia_safe::{self as skia, Codec, ISize};
use std::collections::HashMap; use std::collections::HashMap;
@ -143,10 +144,12 @@ fn decode_image(context: &mut Box<DirectContext>, raw_data: &[u8]) -> Option<Ima
} }
impl ImageStore { impl ImageStore {
pub fn new(context: DirectContext) -> Self { pub fn new() -> Self {
let gpu_state = get_gpu_state();
let context = &gpu_state.context;
Self { Self {
images: HashMap::with_capacity(2048), images: HashMap::with_capacity(2048),
context: Box::new(context), context: Box::new(context.clone()),
} }
} }

View File

@ -1,7 +1,7 @@
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::performance;
use crate::shapes::Shape; use crate::shapes::Shape;
use crate::view::Viewbox; use crate::view::Viewbox;
use crate::{get_gpu_state, performance};
use skia_safe::{self as skia, IRect, Paint, RRect}; use skia_safe::{self as skia, IRect, Paint, RRect};
@ -101,11 +101,12 @@ pub struct Surfaces {
#[allow(dead_code)] #[allow(dead_code)]
impl Surfaces { impl Surfaces {
pub fn try_new( pub fn try_new(
gpu_state: &mut GpuState,
(width, height): (i32, i32), (width, height): (i32, i32),
sampling_options: skia::SamplingOptions, sampling_options: skia::SamplingOptions,
tile_dims: skia::ISize, tile_dims: skia::ISize,
) -> Result<Self> { ) -> Result<Self> {
let gpu_state = get_gpu_state();
let extra_tile_dims = skia::ISize::new( let extra_tile_dims = skia::ISize::new(
tile_dims.width * TILE_SIZE_MULTIPLIER, tile_dims.width * TILE_SIZE_MULTIPLIER,
tile_dims.height * TILE_SIZE_MULTIPLIER, tile_dims.height * TILE_SIZE_MULTIPLIER,
@ -140,7 +141,7 @@ impl Surfaces {
atlas.canvas().clear(skia::Color::TRANSPARENT); atlas.canvas().clear(skia::Color::TRANSPARENT);
let tiles = TileTextureCache::new(); let tiles = TileTextureCache::new();
Ok(Surfaces { Ok(Self {
target, target,
filter, filter,
cache, cache,
@ -445,12 +446,9 @@ impl Surfaces {
self.margins self.margins
} }
pub fn resize( pub fn resize(&mut self, new_width: i32, new_height: i32) -> Result<()> {
&mut self, let gpu_state = get_gpu_state();
gpu_state: &mut GpuState,
new_width: i32,
new_height: i32,
) -> Result<()> {
self.reset_from_target(gpu_state.create_target_surface(new_width, new_height)?)?; self.reset_from_target(gpu_state.create_target_surface(new_width, new_height)?)?;
Ok(()) Ok(())
} }
@ -547,7 +545,8 @@ impl Surfaces {
self.dirty_surfaces = 0; self.dirty_surfaces = 0;
} }
pub fn flush_and_submit(&mut self, gpu_state: &mut GpuState, id: SurfaceId) { pub fn flush_and_submit(&mut self, id: SurfaceId) {
let gpu_state = get_gpu_state();
let surface = self.get_mut(id); let surface = self.get_mut(id);
gpu_state.context.flush_and_submit_surface(surface, None); gpu_state.context.flush_and_submit_surface(surface, None);
} }
@ -946,13 +945,13 @@ impl Surfaces {
pub fn cache_current_tile_texture( pub fn cache_current_tile_texture(
&mut self, &mut self,
gpu_state: &mut GpuState,
tile_viewbox: &TileViewbox, tile_viewbox: &TileViewbox,
tile: &Tile, tile: &Tile,
tile_rect: &skia::Rect, tile_rect: &skia::Rect,
skip_cache_surface: bool, skip_cache_surface: bool,
tile_doc_rect: skia::Rect, tile_doc_rect: skia::Rect,
) { ) {
let gpu_state = get_gpu_state();
let rect = IRect::from_xywh( let rect = IRect::from_xywh(
self.margins.width, self.margins.width,
self.margins.height, self.margins.height,
@ -985,7 +984,8 @@ impl Surfaces {
self.tiles.has(tile) self.tiles.has(tile)
} }
pub fn remove_cached_tile_surface(&mut self, gpu_state: &mut GpuState, tile: Tile) { pub fn remove_cached_tile_surface(&mut self, tile: Tile) {
let gpu_state = get_gpu_state();
// Mark tile as invalid // Mark tile as invalid
// Old content stays visible until new tile overwrites it atomically, // Old content stays visible until new tile overwrites it atomically,
// preventing flickering during tile re-renders. // preventing flickering during tile re-renders.

View File

@ -1,11 +1,10 @@
use crate::get_render_state;
use crate::shapes::text::TextContent; use crate::shapes::text::TextContent;
use skia_safe::{ use skia_safe::{
self as skia, textlayout::Paragraph as SkiaParagraph, FontMetrics, Point, Rect, TextBlob, self as skia, textlayout::Paragraph as SkiaParagraph, FontMetrics, Point, Rect, TextBlob,
}; };
use std::ops::Deref; use std::ops::Deref;
use crate::{with_state_mut, STATE};
pub struct TextPaths(TextContent); pub struct TextPaths(TextContent);
// Note: This class is not being currently used. // Note: This class is not being currently used.
@ -173,20 +172,18 @@ impl TextPaths {
blob_offset_x: f32, blob_offset_x: f32,
blob_offset_y: f32, blob_offset_y: f32,
) -> Option<(skia::Path, skia::Rect)> { ) -> Option<(skia::Path, skia::Rect)> {
with_state_mut!(state, { let utf16_text = span_text.encode_utf16().collect::<Vec<u16>>();
let utf16_text = span_text.encode_utf16().collect::<Vec<u16>>(); let text = unsafe { skia_safe::as_utf16_unchecked(&utf16_text) };
let text = unsafe { skia_safe::as_utf16_unchecked(&utf16_text) }; let emoji_font = get_render_state().fonts().get_emoji_font(font.size());
let emoji_font = state.render_state.fonts().get_emoji_font(font.size()); let use_font = emoji_font.as_ref().unwrap_or(font);
let use_font = emoji_font.as_ref().unwrap_or(font);
if let Some(mut text_blob) = TextBlob::from_text(text, use_font) { if let Some(mut text_blob) = TextBlob::from_text(text, use_font) {
let path = SkiaParagraph::get_path(&mut text_blob); let path = SkiaParagraph::get_path(&mut text_blob);
let d = Point::new(blob_offset_x, blob_offset_y); let d = Point::new(blob_offset_x, blob_offset_y);
let offset_path = path.with_offset(d); let offset_path = path.with_offset(d);
let bounds = text_blob.bounds(); let bounds = text_blob.bounds();
return Some((offset_path, *bounds)); return Some((offset_path, *bounds));
} }
});
None None
} }
} }

View File

@ -7,10 +7,9 @@ pub use shapes_pool::{ShapesPool, ShapesPoolMutRef, ShapesPoolRef};
pub use text_editor::*; pub use text_editor::*;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::render::RenderState; use crate::shapes::{grid_layout::grid_cell_data, Shape};
use crate::shapes::{modifiers::grid_layout::grid_cell_data, Shape};
use crate::tiles;
use crate::uuid::Uuid; use crate::uuid::Uuid;
use crate::{get_render_state, tiles};
/// This struct holds the state of the Rust application between JS calls. /// This struct holds the state of the Rust application between JS calls.
/// ///
@ -18,7 +17,6 @@ use crate::uuid::Uuid;
/// Note that rust-skia data structures are not thread safe, so a state /// Note that rust-skia data structures are not thread safe, so a state
/// must not be shared between different Web Workers. /// must not be shared between different Web Workers.
pub(crate) struct State { pub(crate) struct State {
pub render_state: RenderState,
pub current_id: Option<Uuid>, pub current_id: Option<Uuid>,
pub current_browser: u8, pub current_browser: u8,
pub shapes: ShapesPool, pub shapes: ShapesPool,
@ -28,16 +26,14 @@ pub(crate) struct State {
} }
impl State { impl State {
pub fn try_new(width: i32, height: i32) -> Result<Self> { pub fn new() -> Self {
Ok(State { Self {
render_state: RenderState::try_new(width, height)?,
// text_editor_state: TextEditorState::new(),
current_id: None, current_id: None,
current_browser: 0, current_browser: 0,
shapes: ShapesPool::new(), shapes: ShapesPool::new(),
saved_shapes: None, saved_shapes: None,
loading: false, loading: false,
}) }
} }
// Creates a new temporary shapes pool. // Creates a new temporary shapes pool.
@ -64,30 +60,16 @@ impl State {
Ok(self) Ok(self)
} }
pub fn resize(&mut self, width: i32, height: i32) -> Result<()> {
self.render_state.resize(width, height)
}
pub fn render_state_mut(&mut self) -> &mut RenderState {
&mut self.render_state
}
pub fn render_state(&self) -> &RenderState {
&self.render_state
}
pub fn render_from_cache(&mut self) { pub fn render_from_cache(&mut self) {
self.render_state.render_from_cache(&self.shapes); get_render_state().render_from_cache(&self.shapes);
} }
pub fn render_sync(&mut self, timestamp: i32) -> Result<()> { pub fn render_sync(&mut self, timestamp: i32) -> Result<()> {
self.render_state get_render_state().start_render_loop(None, &self.shapes, timestamp, true)
.start_render_loop(None, &self.shapes, timestamp, true)
} }
pub fn render_sync_shape(&mut self, id: &Uuid, timestamp: i32) -> Result<()> { pub fn render_sync_shape(&mut self, id: &Uuid, timestamp: i32) -> Result<()> {
self.render_state get_render_state().start_render_loop(Some(id), &self.shapes, timestamp, true)
.start_render_loop(Some(id), &self.shapes, timestamp, true)
} }
pub fn render_shape_pixels( pub fn render_shape_pixels(
@ -96,36 +78,34 @@ impl State {
scale: f32, scale: f32,
timestamp: i32, timestamp: i32,
) -> Result<(Vec<u8>, i32, i32)> { ) -> Result<(Vec<u8>, i32, i32)> {
self.render_state get_render_state().render_shape_pixels(id, &self.shapes, scale, timestamp)
.render_shape_pixels(id, &self.shapes, scale, timestamp)
} }
pub fn start_render_loop(&mut self, timestamp: i32) -> Result<()> { pub fn start_render_loop(&mut self, timestamp: i32) -> Result<()> {
let render_state = get_render_state();
// If zoom changed (e.g. interrupted zoom render followed by pan), the // If zoom changed (e.g. interrupted zoom render followed by pan), the
// tile index may be stale for the new viewport position. Rebuild the // tile index may be stale for the new viewport position. Rebuild the
// index so shapes are mapped to the correct tiles. We use // index so shapes are mapped to the correct tiles. We use
// rebuild_tile_index (NOT rebuild_tiles_shallow) to preserve the tile // rebuild_tile_index (NOT rebuild_tiles_shallow) to preserve the tile
// texture cache — otherwise cached tiles with shadows/blur would be // texture cache — otherwise cached tiles with shadows/blur would be
// cleared and re-rendered in fast mode without effects. // cleared and re-rendered in fast mode without effects.
if self.render_state.zoom_changed() { if render_state.zoom_changed() {
self.render_state.rebuild_tile_index(&self.shapes); render_state.rebuild_tile_index(&self.shapes);
} }
self.render_state render_state.start_render_loop(None, &self.shapes, timestamp, false)
.start_render_loop(None, &self.shapes, timestamp, false)
} }
pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<()> { pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<()> {
self.render_state get_render_state().process_animation_frame(None, &self.shapes, timestamp)
.process_animation_frame(None, &self.shapes, timestamp)
} }
pub fn clear_focus_mode(&mut self) { pub fn clear_focus_mode(&mut self) {
self.render_state.clear_focus_mode(); get_render_state().clear_focus_mode();
} }
pub fn set_focus_mode(&mut self, shapes: Vec<Uuid>) { pub fn set_focus_mode(&mut self, shapes: Vec<Uuid>) {
self.render_state.set_focus_mode(shapes); get_render_state().set_focus_mode(shapes);
} }
pub fn init_shapes_pool(&mut self, capacity: usize) { pub fn init_shapes_pool(&mut self, capacity: usize) {
@ -140,6 +120,8 @@ impl State {
} }
pub fn delete_shape_children(&mut self, parent_id: Uuid, id: Uuid) { pub fn delete_shape_children(&mut self, parent_id: Uuid, id: Uuid) {
let render_state = get_render_state();
// We don't really do a self.shapes.remove so that redo/undo keep working // We don't really do a self.shapes.remove so that redo/undo keep working
let Some(shape) = self.shapes.get(&id) else { let Some(shape) = self.shapes.get(&id) else {
return; return;
@ -155,16 +137,15 @@ impl State {
// //
// Instead, remove the shape from *all* tiles where it was indexed, and // Instead, remove the shape from *all* tiles where it was indexed, and
// drop cached tiles for those entries. // drop cached tiles for those entries.
let indexed_tiles: Vec<tiles::Tile> = self let indexed_tiles: Vec<tiles::Tile> = render_state
.render_state
.tiles .tiles
.get_tiles_of(shape.id) .get_tiles_of(shape.id)
.map(|t| t.iter().copied().collect()) .map(|t| t.iter().copied().collect())
.unwrap_or_default(); .unwrap_or_default();
for tile in indexed_tiles { for tile in indexed_tiles {
self.render_state.remove_cached_tile(tile); render_state.remove_cached_tile(tile);
self.render_state.tiles.remove_shape_at(tile, shape.id); render_state.tiles.remove_shape_at(tile, shape.id);
} }
if let Some(shape_to_delete) = self.shapes.get(&id) { if let Some(shape_to_delete) = self.shapes.get(&id) {
@ -173,8 +154,8 @@ impl State {
if let Some(shape_to_delete) = self.shapes.get_mut(&shape_id) { if let Some(shape_to_delete) = self.shapes.get_mut(&shape_id) {
shape_to_delete.set_deleted(true); shape_to_delete.set_deleted(true);
} }
if self.render_state.show_grid == Some(shape_id) { if render_state.show_grid == Some(shape_id) {
self.render_state.show_grid = None; render_state.show_grid = None;
} }
} }
} }
@ -190,7 +171,7 @@ impl State {
} }
pub fn set_background_color(&mut self, color: skia::Color) { pub fn set_background_color(&mut self, color: skia::Color) {
self.render_state.set_background_color(color); get_render_state().set_background_color(color);
} }
pub fn set_browser(&mut self, browser: u8) { pub fn set_browser(&mut self, browser: u8) {
@ -225,33 +206,32 @@ impl State {
} }
pub fn rebuild_tiles_shallow(&mut self) { pub fn rebuild_tiles_shallow(&mut self) {
self.render_state.rebuild_tiles_shallow(&self.shapes); get_render_state().rebuild_tiles_shallow(&self.shapes);
} }
pub fn rebuild_tiles(&mut self) { pub fn rebuild_tiles(&mut self) {
self.render_state.rebuild_tiles_from(&self.shapes, None); get_render_state().rebuild_tiles_from(&self.shapes, None);
} }
pub fn rebuild_tiles_from(&mut self, base_id: Option<&Uuid>) { pub fn rebuild_tiles_from(&mut self, base_id: Option<&Uuid>) {
self.render_state.rebuild_tiles_from(&self.shapes, base_id); get_render_state().rebuild_tiles_from(&self.shapes, base_id);
} }
pub fn rebuild_touched_tiles(&mut self) { pub fn rebuild_touched_tiles(&mut self) {
self.render_state.rebuild_touched_tiles(&self.shapes); get_render_state().rebuild_touched_tiles(&self.shapes);
} }
pub fn render_preview(&mut self, timestamp: i32) { pub fn render_preview(&mut self, timestamp: i32) {
let _ = self.render_state.render_preview(&self.shapes, timestamp); let _ = get_render_state().render_preview(&self.shapes, timestamp);
} }
pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) -> Result<()> { pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) -> Result<()> {
// Index-based storage is safe // Index-based storage is safe
self.render_state get_render_state().rebuild_modifier_tiles(&mut self.shapes, ids)
.rebuild_modifier_tiles(&mut self.shapes, ids)
} }
pub fn font_collection(&self) -> &FontCollection { pub fn font_collection(&self) -> &FontCollection {
self.render_state.fonts().font_collection() get_render_state().fonts().font_collection()
} }
pub fn get_grid_coords(&self, pos_x: f32, pos_y: f32) -> Option<(i32, i32)> { pub fn get_grid_coords(&self, pos_x: f32, pos_y: f32) -> Option<(i32, i32)> {
@ -284,16 +264,18 @@ impl State {
} }
pub fn touch_current(&mut self) { pub fn touch_current(&mut self) {
let render_state = get_render_state();
if !self.loading { if !self.loading {
if let Some(current_id) = self.current_id { if let Some(current_id) = self.current_id {
self.render_state.mark_touched(current_id); render_state.mark_touched(current_id);
} }
} }
} }
pub fn touch_shape(&mut self, id: Uuid) { pub fn touch_shape(&mut self, id: Uuid) {
let render_state = get_render_state();
if !self.loading { if !self.loading {
self.render_state.mark_touched(id); render_state.mark_touched(id);
} }
} }
} }

View File

@ -1,3 +1,4 @@
use crate::get_render_state;
use crate::skia::textlayout::FontCollection; use crate::skia::textlayout::FontCollection;
use crate::skia::Image; use crate::skia::Image;
use crate::uuid::Uuid; use crate::uuid::Uuid;
@ -25,12 +26,12 @@ pub fn uuid_from_u32(id: [u32; 4]) -> Uuid {
} }
pub fn get_image(image_id: &Uuid) -> Option<&Image> { pub fn get_image(image_id: &Uuid) -> Option<&Image> {
with_state_mut!(state, { state.render_state_mut().images.get(image_id) }) get_render_state().images.get(image_id)
} }
// FIXME: move to a different place ? // FIXME: move to a different place ?
pub fn get_fallback_fonts() -> &'static HashSet<String> { pub fn get_fallback_fonts() -> &'static HashSet<String> {
with_state_mut!(state, { state.render_state().fonts().get_fallback() }) get_render_state().fonts().get_fallback()
} }
pub fn get_font_collection() -> &'static FontCollection { pub fn get_font_collection() -> &'static FontCollection {

View File

@ -1,4 +1,5 @@
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::get_render_state;
use crate::mem; use crate::mem;
use crate::shapes::Fill; use crate::shapes::Fill;
use crate::state::State; use crate::state::State;
@ -106,11 +107,7 @@ pub extern "C" fn store_image() -> Result<()> {
let image_bytes = &bytes[IMAGE_HEADER_SIZE..]; let image_bytes = &bytes[IMAGE_HEADER_SIZE..];
with_state_mut!(state, { with_state_mut!(state, {
if let Err(msg) = if let Err(msg) = get_render_state().add_image(ids.image_id, is_thumbnail, image_bytes) {
state
.render_state_mut()
.add_image(ids.image_id, is_thumbnail, image_bytes)
{
eprintln!("{}", msg); eprintln!("{}", msg);
} }
touch_shapes_with_image(state, ids.image_id); touch_shapes_with_image(state, ids.image_id);
@ -180,7 +177,7 @@ pub extern "C" fn store_image_from_texture() -> Result<()> {
); );
with_state_mut!(state, { with_state_mut!(state, {
if let Err(msg) = state.render_state_mut().add_image_from_gl_texture( if let Err(msg) = get_render_state().add_image_from_gl_texture(
ids.image_id, ids.image_id,
is_thumbnail, is_thumbnail,
texture_id, texture_id,

View File

@ -1,10 +1,9 @@
use macros::{wasm_error, ToJs}; use macros::{wasm_error, ToJs};
use crate::get_render_state;
use crate::mem; use crate::mem;
use crate::shapes::{FontFamily, FontStyle}; use crate::shapes::{FontFamily, FontStyle};
use crate::utils::uuid_from_u32_quartet; use crate::utils::uuid_from_u32_quartet;
use crate::with_state_mut;
use crate::STATE;
#[derive(Debug, PartialEq, Clone, Copy, ToJs)] #[derive(Debug, PartialEq, Clone, Copy, ToJs)]
#[repr(u8)] #[repr(u8)]
@ -41,20 +40,16 @@ pub extern "C" fn store_font(
is_emoji: bool, is_emoji: bool,
is_fallback: bool, is_fallback: bool,
) -> Result<()> { ) -> Result<()> {
with_state_mut!(state, { let id = uuid_from_u32_quartet(a, b, c, d);
let id = uuid_from_u32_quartet(a, b, c, d); let font_bytes = mem::bytes();
let font_bytes = mem::bytes(); let font_style = RawFontStyle::from(style);
let font_style = RawFontStyle::from(style);
let family = FontFamily::new(id, weight, font_style.into()); let family = FontFamily::new(id, weight, font_style.into());
let _ = let _ = get_render_state()
state .fonts_mut()
.render_state_mut() .add(family, &font_bytes, is_emoji, is_fallback);
.fonts_mut()
.add(family, &font_bytes, is_emoji, is_fallback);
mem::free_bytes()?; mem::free_bytes()?;
});
Ok(()) Ok(())
} }
@ -68,12 +63,10 @@ pub extern "C" fn is_font_uploaded(
style: u8, style: u8,
is_emoji: bool, is_emoji: bool,
) -> bool { ) -> bool {
with_state_mut!(state, { let id = uuid_from_u32_quartet(a, b, c, d);
let id = uuid_from_u32_quartet(a, b, c, d); let font_style = RawFontStyle::from(style);
let font_style = RawFontStyle::from(style); let family = FontFamily::new(id, weight, font_style.into());
let family = FontFamily::new(id, weight, font_style.into()); let res = get_render_state().fonts().has_family(&family, is_emoji);
let res = state.render_state().fonts().has_family(&family, is_emoji);
res res
})
} }

View File

@ -1,9 +1,10 @@
use macros::{wasm_error, ToJs}; use macros::{wasm_error, ToJs};
use crate::get_render_state;
use crate::mem; use crate::mem;
use crate::shapes::{GridCell, GridDirection, GridTrack, GridTrackType}; use crate::shapes::{GridCell, GridDirection, GridTrack, GridTrackType};
use crate::uuid::Uuid; use crate::uuid::Uuid;
use crate::{uuid_from_u32_quartet, with_current_shape_mut, with_state, with_state_mut, STATE}; use crate::{uuid_from_u32_quartet, with_current_shape_mut, with_state, STATE};
use super::align; use super::align;
@ -241,17 +242,13 @@ pub extern "C" fn set_grid_cells() -> Result<()> {
#[no_mangle] #[no_mangle]
pub extern "C" fn show_grid(a: u32, b: u32, c: u32, d: u32) { pub extern "C" fn show_grid(a: u32, b: u32, c: u32, d: u32) {
with_state_mut!(state, { let id = uuid_from_u32_quartet(a, b, c, d);
let id = uuid_from_u32_quartet(a, b, c, d); get_render_state().show_grid = Some(id);
state.render_state.show_grid = Some(id);
});
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn hide_grid() { pub extern "C" fn hide_grid() {
with_state_mut!(state, { get_render_state().show_grid = None;
state.render_state.show_grid = None;
});
} }
#[no_mangle] #[no_mangle]

View File

@ -2,7 +2,6 @@ use macros::{wasm_error, ToJs};
use crate::get_text_editor_state; use crate::get_text_editor_state;
use crate::math::{Matrix, Point, Rect}; use crate::math::{Matrix, Point, Rect};
use crate::mem;
use crate::render::text_editor as text_editor_render; use crate::render::text_editor as text_editor_render;
use crate::render::SurfaceId; use crate::render::SurfaceId;
use crate::shapes::{Shape, TextAlign, TextContent, TextPositionWithAffinity, Type, VerticalAlign}; use crate::shapes::{Shape, TextAlign, TextContent, TextPositionWithAffinity, Type, VerticalAlign};
@ -13,6 +12,7 @@ use crate::wasm::fills::RawFillData;
use crate::wasm::text::{ use crate::wasm::text::{
helpers as text_helpers, RawTextAlign, RawTextDecoration, RawTextDirection, RawTextTransform, helpers as text_helpers, RawTextAlign, RawTextDecoration, RawTextDirection, RawTextTransform,
}; };
use crate::{get_render_state, mem};
use crate::{with_state, with_state_mut, STATE}; use crate::{with_state, with_state_mut, STATE};
use skia_safe::Color; use skia_safe::Color;
@ -287,7 +287,7 @@ pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
return; return;
} }
let view_matrix: Matrix = state.render_state.viewbox.get_matrix(); let view_matrix: Matrix = get_render_state().viewbox.get_matrix();
let point = Point::new(x, y); let point = Point::new(x, y);
let Some(shape_id) = get_text_editor_state().active_shape_id else { let Some(shape_id) = get_text_editor_state().active_shape_id else {
return; return;
@ -368,7 +368,7 @@ pub extern "C" fn text_editor_composition_end() -> Result<()> {
get_text_editor_state().push_event(crate::state::TextEditorEvent::ContentChanged); get_text_editor_state().push_event(crate::state::TextEditorEvent::ContentChanged);
get_text_editor_state().push_event(crate::state::TextEditorEvent::NeedsLayout); get_text_editor_state().push_event(crate::state::TextEditorEvent::NeedsLayout);
state.render_state.mark_touched(shape_id); get_render_state().mark_touched(shape_id);
get_text_editor_state().composition.end(); get_text_editor_state().composition.end();
}); });
@ -420,7 +420,7 @@ pub extern "C" fn text_editor_composition_update() -> Result<()> {
get_text_editor_state().push_event(crate::state::TextEditorEvent::ContentChanged); get_text_editor_state().push_event(crate::state::TextEditorEvent::ContentChanged);
get_text_editor_state().push_event(crate::state::TextEditorEvent::NeedsLayout); get_text_editor_state().push_event(crate::state::TextEditorEvent::NeedsLayout);
state.render_state.mark_touched(shape_id); get_render_state().mark_touched(shape_id);
}); });
crate::mem::free_bytes()?; crate::mem::free_bytes()?;
@ -489,7 +489,7 @@ pub extern "C" fn text_editor_insert_text() -> Result<()> {
get_text_editor_state().push_event(TextEditorEvent::ContentChanged); get_text_editor_state().push_event(TextEditorEvent::ContentChanged);
get_text_editor_state().push_event(TextEditorEvent::NeedsLayout); get_text_editor_state().push_event(TextEditorEvent::NeedsLayout);
state.render_state.mark_touched(shape_id); get_render_state().mark_touched(shape_id);
}); });
crate::mem::free_bytes()?; crate::mem::free_bytes()?;
@ -516,7 +516,7 @@ pub extern "C" fn text_editor_delete_backward(word_boundary: bool) {
}; };
get_text_editor_state().delete_backward(text_content, word_boundary); get_text_editor_state().delete_backward(text_content, word_boundary);
state.render_state.mark_touched(shape_id); get_render_state().mark_touched(shape_id);
}); });
} }
@ -540,7 +540,7 @@ pub extern "C" fn text_editor_delete_forward(word_boundary: bool) {
}; };
get_text_editor_state().delete_forward(text_content, word_boundary); get_text_editor_state().delete_forward(text_content, word_boundary);
state.render_state.mark_touched(shape_id); get_render_state().mark_touched(shape_id);
}); });
} }
@ -564,7 +564,7 @@ pub extern "C" fn text_editor_insert_paragraph() {
}; };
get_text_editor_state().insert_paragraph(text_content); get_text_editor_state().insert_paragraph(text_content);
state.render_state.mark_touched(shape_id); get_render_state().mark_touched(shape_id);
}); });
} }
@ -880,16 +880,16 @@ pub extern "C" fn text_editor_render_overlay() {
return; return;
}; };
let canvas = state.render_state.surfaces.canvas(SurfaceId::Target); let canvas = get_render_state().surfaces.canvas(SurfaceId::Target);
let viewbox = state.render_state.viewbox; let viewbox = get_render_state().viewbox;
text_editor_render::render_overlay( text_editor_render::render_overlay(
canvas, canvas,
&viewbox, &viewbox,
&state.render_state.options, &get_render_state().options,
get_text_editor_state(), get_text_editor_state(),
shape, shape,
); );
state.render_state.flush_and_submit(); get_render_state().flush_and_submit();
}); });
} }