Implement dynamic table attributes to generalize the graphic-specific Table type (#4050)

* Feature-gate serde derives behind cfg_attr in all runtime node graph type crates

* Refactor Table to move its hard-coded fields into an attributes field

* Encapsulate TableRow/TableRowRef/TableRowMut attribute fields behind accessor methods

* Remove TaggedValue::GraphicUnused

* Refactor Table<T> to use dynamic attributes instead fixed names

* Fix code review soundness concerns

* Add todo work

* Replace row-oriented Table<T> API with column-oriented access

* Fix attribute propagation bugs

---------
This commit is contained in:
Keavon Chambers
2026-04-28 02:10:24 -07:00
parent 324b9e664c
commit 76938eb69a
75 changed files with 2330 additions and 1349 deletions

View File

@@ -14,24 +14,25 @@ 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, serde::Serialize, serde::Deserialize)]
#[derive(Clone, Debug, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
struct BrushCacheImpl {
#[serde(default = "new_unique_id")]
#[cfg_attr(feature = "serde", serde(default = "new_unique_id"))]
unique_id: u64,
// The full previous input that was cached.
#[serde(default)]
#[cfg_attr(feature = "serde", serde(default))]
prev_input: Vec<BrushStroke>,
// The strokes that have been fully processed and blended into the background.
#[serde(default, deserialize_with = "raster_types::image::migrate_image_frame_row")]
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "raster_types::image::migrate_image_frame_row"))]
background: TableRow<Raster<CPU>>,
#[serde(default, deserialize_with = "raster_types::image::migrate_image_frame_row")]
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "raster_types::image::migrate_image_frame_row"))]
blended_image: TableRow<Raster<CPU>>,
#[serde(default, deserialize_with = "raster_types::image::migrate_image_frame_row")]
#[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.
#[serde(skip)]
#[cfg_attr(feature = "serde", serde(skip))]
brush_texture_cache: HashMap<CacheHashWrapper<BrushStyle>, Raster<CPU>>,
}
@@ -63,11 +64,10 @@ impl BrushCacheImpl {
background = std::mem::take(&mut self.blended_image);
// Check if the first non-blended stroke is an extension of the last one.
let mut first_stroke_texture = TableRow {
element: Raster::<CPU>::default(),
transform: glam::DAffine2::ZERO,
..Default::default()
};
let mut first_stroke_texture = TableRow::new_from_element(Raster::<CPU>::default())
.with_attribute("transform", glam::DAffine2::ZERO)
.with_attribute("alpha_blending", core_types::AlphaBlending::default())
.with_attribute("source_node_id", None::<core_types::uuid::NodeId>);
let mut first_stroke_point_skip = 0;
let strokes = input[num_blended_strokes..].to_vec();
if !strokes.is_empty() && self.prev_input.len() > num_blended_strokes {
@@ -135,7 +135,8 @@ pub struct BrushPlan {
pub first_stroke_point_skip: usize,
}
#[derive(Debug, Default, DynAny, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Default, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BrushCache(Arc<Mutex<BrushCacheImpl>>);
// A bit of a cursed implementation to work around the current node system.