mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Make the Brush node store its cache as internal #[data] state instead of a serialized node input (#4126)
* Make the Brush node store its cache as internal #[data] state instead of a serialized node input * Remove BrushCacheImpl::unique_id
This commit is contained in:
@@ -195,6 +195,7 @@ async fn brush(
|
||||
/// The list of brush stroke paths drawn by the Brush tool, with each including both its coordinates and styles.
|
||||
trace: Table<BrushStroke>,
|
||||
/// Internal cache data used to accelerate rendering of the brush content.
|
||||
#[data]
|
||||
cache: BrushCache,
|
||||
) -> Table<Raster<CPU>> {
|
||||
if background.is_empty() {
|
||||
@@ -419,6 +420,7 @@ mod test {
|
||||
async fn test_brush_output_size() {
|
||||
let image = brush(
|
||||
(),
|
||||
&BrushCache::default(),
|
||||
Table::new_from_element(Raster::new_cpu(Image::<Color>::default())),
|
||||
Table::new_from_element(BrushStroke {
|
||||
trace: vec![crate::brush_stroke::BrushInputSample { position: DVec2::ZERO }],
|
||||
@@ -431,7 +433,6 @@ mod test {
|
||||
blend_mode: BlendMode::Normal,
|
||||
},
|
||||
}),
|
||||
BrushCache::default(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(image.element(0).unwrap().width, 20);
|
||||
|
||||
@@ -3,37 +3,22 @@ use crate::brush_stroke::BrushStyle;
|
||||
use core_types::ATTR_TRANSFORM;
|
||||
use core_types::graphene_hash::CacheHashWrapper;
|
||||
use core_types::table::TableRow;
|
||||
use dyn_any::DynAny;
|
||||
use raster_types::CPU;
|
||||
use raster_types::Raster;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::hash::Hasher;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
// TODO: This is a temporary hack, be sure to not reuse this when the brush system is replaced/rewritten.
|
||||
static NEXT_BRUSH_CACHE_IMPL_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[derive(Clone, Debug, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct BrushCacheImpl {
|
||||
#[cfg_attr(feature = "serde", serde(default = "new_unique_id"))]
|
||||
unique_id: u64,
|
||||
// The full previous input that was cached.
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
prev_input: Vec<BrushStroke>,
|
||||
|
||||
// The strokes that have been fully processed and blended into the background.
|
||||
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "raster_types::image::migrate_image_frame_row"))]
|
||||
background: TableRow<Raster<CPU>>,
|
||||
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "raster_types::image::migrate_image_frame_row"))]
|
||||
blended_image: TableRow<Raster<CPU>>,
|
||||
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "raster_types::image::migrate_image_frame_row"))]
|
||||
last_stroke_texture: TableRow<Raster<CPU>>,
|
||||
|
||||
// A cache for brush textures.
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
brush_texture_cache: HashMap<CacheHashWrapper<BrushStyle>, Raster<CPU>>,
|
||||
}
|
||||
|
||||
@@ -97,35 +82,6 @@ impl BrushCacheImpl {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BrushCacheImpl {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
unique_id: new_unique_id(),
|
||||
prev_input: Vec::new(),
|
||||
background: Default::default(),
|
||||
blended_image: Default::default(),
|
||||
last_stroke_texture: Default::default(),
|
||||
brush_texture_cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BrushCacheImpl {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.unique_id == other.unique_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for BrushCacheImpl {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.unique_id.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
fn new_unique_id() -> u64 {
|
||||
NEXT_BRUSH_CACHE_IMPL_ID.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct BrushPlan {
|
||||
pub strokes: Vec<BrushStroke>,
|
||||
@@ -134,44 +90,9 @@ pub struct BrushPlan {
|
||||
pub first_stroke_point_skip: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct BrushCache(Arc<Mutex<BrushCacheImpl>>);
|
||||
|
||||
// A bit of a cursed implementation to work around the current node system.
|
||||
// The original object is a 'prototype' that when cloned gives you a independent
|
||||
// new object. Any further clones however are all the same underlying cache object.
|
||||
impl Clone for BrushCache {
|
||||
fn clone(&self) -> Self {
|
||||
Self(Arc::new(Mutex::new(self.0.lock().unwrap().clone())))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BrushCache {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if Arc::ptr_eq(&self.0, &other.0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let s = self.0.lock().unwrap();
|
||||
let o = other.0.lock().unwrap();
|
||||
|
||||
*s == *o
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for BrushCache {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.lock().unwrap().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl graphene_hash::CacheHash for BrushCache {
|
||||
fn cache_hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
core::hash::Hash::hash(&self.0.lock().unwrap().unique_id, state);
|
||||
}
|
||||
}
|
||||
|
||||
impl BrushCache {
|
||||
pub fn compute_brush_plan(&self, background: TableRow<Raster<CPU>>, input: &[BrushStroke]) -> BrushPlan {
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod brush;
|
||||
pub mod brush_cache;
|
||||
mod brush_cache;
|
||||
pub mod brush_stroke;
|
||||
|
||||
pub mod migrations {
|
||||
|
||||
Reference in New Issue
Block a user