mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Recreate introspection data from the monitor's context snapshot
This commit is contained in:
@@ -1004,6 +1004,54 @@ impl CtxSnapshot {
|
||||
pub fn generations(&self) -> &[(SourceId, u64)] {
|
||||
&self.generations
|
||||
}
|
||||
|
||||
/// Rebuilds the captured context against `scope`, allocating the borrowed
|
||||
/// chains in the scope's arena; `None` reports arena exhaustion. The
|
||||
/// scope carries the serving generation, so the recreated evaluation reads
|
||||
/// current source data under the captured addressing.
|
||||
pub fn rehydrate<'e>(&'e self, scope: &'e EvalScope<'e>) -> Option<ContextImpl<'e>> {
|
||||
let arena = scope.arena();
|
||||
let mut ctx = ContextImpl::root(scope);
|
||||
if let Some(footprint) = self.footprint {
|
||||
let (footprint, _) = arena.alloc(footprint)?;
|
||||
ctx = ctx.with_footprint(footprint);
|
||||
}
|
||||
if let Some(levels) = &self.index {
|
||||
let mut outer = None;
|
||||
for &index in levels.iter().skip(1).rev() {
|
||||
let (link, _) = arena.alloc(IndexLink { index: index as u64, outer })?;
|
||||
outer = Some(link);
|
||||
}
|
||||
ctx.index = IndexLink {
|
||||
index: levels.first().copied().unwrap_or_default() as u64,
|
||||
outer,
|
||||
};
|
||||
}
|
||||
if let Some(positions) = &self.positions {
|
||||
let mut head = None;
|
||||
for &position in positions.iter().rev() {
|
||||
let (link, _) = arena.alloc(PositionLink { position, outer: head })?;
|
||||
head = Some(link);
|
||||
}
|
||||
if let Some(head) = head {
|
||||
ctx = ctx.with_position(head);
|
||||
}
|
||||
}
|
||||
let mut varargs = None;
|
||||
for args in self.varargs.iter().rev() {
|
||||
let slots: Vec<DynSlot<'e>> = args.iter().map(|slot| &**slot as DynSlot<'e>).collect();
|
||||
let slots = arena.alloc_slice_copy(&slots)?;
|
||||
let (link, _) = arena.alloc(VarArgLink {
|
||||
args: VarArgSlots::Slice(slots),
|
||||
outer: varargs,
|
||||
})?;
|
||||
varargs = Some(link);
|
||||
}
|
||||
if let Some(varargs) = varargs {
|
||||
ctx = ctx.with_varargs(varargs);
|
||||
}
|
||||
Some(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtractFootprint for CtxSnapshot {
|
||||
|
||||
@@ -1554,96 +1554,6 @@ pub unsafe fn interrupt_frame(entry: usize, layout: &Layout) {
|
||||
claim_frame(layout);
|
||||
}
|
||||
|
||||
/// A captured record: the layout plus a generation-checked handle to the
|
||||
/// arena copy, materialized by the introspection holder, which owns the
|
||||
/// arena. A dead generation materializes to `None`, never to a stale read.
|
||||
#[derive(Clone)]
|
||||
pub struct RecordCapture {
|
||||
layout: Layout,
|
||||
lanes: usize,
|
||||
bytes: crate::arena::ArenaWeak<Box<[u8]>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RecordCapture {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("RecordCapture(..)")
|
||||
}
|
||||
}
|
||||
|
||||
impl RecordCapture {
|
||||
/// # Safety
|
||||
/// `rec` must be a live record of `layout`.
|
||||
pub unsafe fn capture(layout: &Layout, rec: Rec, arena: &crate::arena::Arena) -> Option<RecordCapture> {
|
||||
let bytes = unsafe { copy_record_bytes(layout, rec) };
|
||||
arena.alloc(bytes).map(|(_, weak)| RecordCapture {
|
||||
layout: layout.clone(),
|
||||
lanes: 1,
|
||||
bytes: weak,
|
||||
})
|
||||
}
|
||||
|
||||
/// Captures every lane of a leveled wire's batch.
|
||||
///
|
||||
/// # Safety
|
||||
/// `batch` must hold live records of `layout`.
|
||||
pub unsafe fn capture_level(layout: &Layout, batch: crate::node::RecordBatch<'_>, arena: &crate::arena::Arena) -> Option<RecordCapture> {
|
||||
let stride = layout.lane_stride();
|
||||
let mut bytes = vec![0u8; batch.len() * stride].into_boxed_slice();
|
||||
for lane in 0..batch.len() {
|
||||
// SAFETY: both sides hold `len` lanes at the shared layout's stride.
|
||||
unsafe { std::ptr::copy_nonoverlapping(batch.get(lane).rec().ptr(), bytes.as_mut_ptr().add(lane * stride), stride) };
|
||||
}
|
||||
arena.alloc(bytes).map(|(_, weak)| RecordCapture {
|
||||
layout: layout.clone(),
|
||||
lanes: batch.len(),
|
||||
bytes: weak,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn layout(&self) -> &Layout {
|
||||
&self.layout
|
||||
}
|
||||
|
||||
/// The captured lane count: one for a rank-0 capture, the whole extent
|
||||
/// for a level capture.
|
||||
pub fn lanes(&self) -> usize {
|
||||
self.lanes
|
||||
}
|
||||
|
||||
/// A batch view over the captured records, alive while the arena holds
|
||||
/// the capture's generation.
|
||||
pub fn batch<'a>(&'a self, arena: &'a crate::arena::Arena) -> Option<crate::node::RecordBatch<'a>> {
|
||||
let bytes = self.bytes.upgrade(arena)?;
|
||||
// SAFETY: the constructors store `lanes` records of `layout`.
|
||||
Some(unsafe { crate::node::RecordBatch::new(bytes.as_ptr(), self.lanes, &self.layout) })
|
||||
}
|
||||
|
||||
/// The captured element of the first lane, cloned out through the
|
||||
/// layout's erased glue.
|
||||
pub fn materialize_element(&self, arena: &crate::arena::Arena) -> Option<Box<dyn std::any::Any + Send + Sync>> {
|
||||
let bytes = self.bytes.upgrade(arena)?;
|
||||
match self.lanes {
|
||||
0 => None,
|
||||
_ => Some(unsafe { (self.layout.element.clone_out)(bytes.as_ptr()) }),
|
||||
}
|
||||
}
|
||||
|
||||
/// The first lane's attributes, read out through the layout's erased glue.
|
||||
pub fn materialize(&self, arena: &crate::arena::Arena) -> Option<Vec<(&'static str, Box<dyn crate::list::AnyAttributeValue>)>> {
|
||||
let bytes = self.bytes.upgrade(arena)?;
|
||||
if self.lanes == 0 {
|
||||
return Some(Vec::new());
|
||||
}
|
||||
Some(
|
||||
self.layout
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| (field.name, unsafe { (field.read_erased)(bytes.as_ptr().add(field.offset)) }))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A record deep-copied out of its evaluation: the packed bytes plus owned
|
||||
/// clones of every parked payload, replayable into a later evaluation's
|
||||
/// storage through the layout's erased glue. The layout stays with the
|
||||
@@ -2088,6 +1998,13 @@ impl<'a, T: 'static> crate::lane::LaneSource for RunView<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: crate::render_complexity::RenderComplexity + 'static> crate::render_complexity::RenderComplexity for RunView<'_, T> {
|
||||
fn render_complexity(&self) -> usize {
|
||||
use crate::lane::LaneSource;
|
||||
(0..self.lane_count()).filter_map(|lane| self.element(lane)).map(crate::render_complexity::RenderComplexity::render_complexity).sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: crate::bounds::BoundingBox + 'static> crate::bounds::BoundingBox for RunView<'_, T> {
|
||||
fn bounding_box(&self, transform: glam::DAffine2, include_stroke: bool) -> crate::bounds::RenderBoundingBox {
|
||||
crate::bounds::lane_bounding_box(self, transform, include_stroke)
|
||||
|
||||
@@ -303,6 +303,12 @@ impl EdgeHandle {
|
||||
self.downcast_erased(record_edge_type::<T>())
|
||||
}
|
||||
|
||||
/// The erased record edge, for callers that dispatch on the layout rather
|
||||
/// than a static element type. `None` for a plain (non-record) edge.
|
||||
pub fn record_edge(self) -> Option<SharedEdge<ErasedRecordNode>> {
|
||||
self.node.downcast::<SharedEdge<ErasedRecordNode>>().ok().map(|edge| *edge)
|
||||
}
|
||||
|
||||
pub fn downcast_erased<N: ?Sized + 'static>(self, expected: Type) -> Result<SharedEdge<N>, ConstructionError> {
|
||||
let found = self.ty;
|
||||
self.node.downcast::<SharedEdge<N>>().map(|edge| *edge).map_err(|_| ConstructionError::Type {
|
||||
|
||||
@@ -10,7 +10,7 @@ use core_types::arena::Arena;
|
||||
use core_types::context::InjectIndex;
|
||||
use core_types::gpoll::{Finality, GraphError};
|
||||
use core_types::node::Node;
|
||||
use core_types::record::{Group, GroupContent, GroupItem, LevelStatus, RecordCapture, RecordValue, materialize_level};
|
||||
use core_types::record::{Group, GroupContent, GroupItem, LevelStatus, RecordValue, materialize_level};
|
||||
use core_types::uuid::NodeId;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use vector_types::GradientStops;
|
||||
@@ -46,36 +46,35 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// The captured wire as the legacy value the editor's downcasts expect: a
|
||||
/// rank-0 capture is its element, a leveled `Graphic` capture becomes its
|
||||
/// legacy list through the group bridge, and another element type becomes a
|
||||
/// legacy list of that element. `None` for an element type outside the
|
||||
/// legacy vocabulary or a capture whose arena generation has passed.
|
||||
pub fn capture_to_legacy(capture: &RecordCapture, arena: &Arena) -> Option<Box<dyn std::any::Any + Send + Sync>> {
|
||||
if capture.layout().depth == 0 {
|
||||
// The group-carrying types legacy-convert while the capture is still
|
||||
/// The resident batch as the legacy value the editor's downcasts expect: a
|
||||
/// rank-0 wire is its element, a leveled `Graphic` wire becomes its legacy
|
||||
/// list through the group bridge, and another element type becomes a legacy
|
||||
/// list of that element. `None` for an element type outside the legacy
|
||||
/// vocabulary or an empty rank-0 batch.
|
||||
pub fn batch_to_legacy(layout: &core_types::record::Layout, batch: core_types::node::RecordBatch<'_>, _arena: &Arena) -> Option<Box<dyn std::any::Any + Send + Sync>> {
|
||||
if layout.depth == 0 {
|
||||
// The group-carrying types legacy-convert while the batch is
|
||||
// resident; the deep clone-out would hand back the unreadable owned
|
||||
// form.
|
||||
if capture.lanes() > 0 {
|
||||
let element = &capture.layout().element;
|
||||
if element.type_id == std::any::TypeId::of::<Graphic>() {
|
||||
let batch = capture.batch(arena)?;
|
||||
// SAFETY: the layout records the element type, and a parked
|
||||
// element stores its reference at offset 0.
|
||||
let graphic = unsafe { core_types::record::borrow_element::<Graphic>(batch.get(0).rec()) };
|
||||
return Some(Box::new(crate::graphic::map_groups_to_legacy(graphic)));
|
||||
}
|
||||
if element.type_id == std::any::TypeId::of::<Artboard>() {
|
||||
let batch = capture.batch(arena)?;
|
||||
// SAFETY: as for the graphic arm.
|
||||
let artboard = unsafe { core_types::record::borrow_element::<Artboard>(batch.get(0).rec()) };
|
||||
return Some(Box::new(artboard.with_legacy_groups()));
|
||||
}
|
||||
if batch.is_empty() {
|
||||
return None;
|
||||
}
|
||||
return capture.materialize_element(arena);
|
||||
let element = &layout.element;
|
||||
if element.type_id == std::any::TypeId::of::<Graphic>() {
|
||||
// SAFETY: the layout records the element type, and a parked
|
||||
// element stores its reference at offset 0.
|
||||
let graphic = unsafe { core_types::record::borrow_element::<Graphic>(batch.get(0).rec()) };
|
||||
return Some(Box::new(crate::graphic::map_groups_to_legacy(graphic)));
|
||||
}
|
||||
if element.type_id == std::any::TypeId::of::<Artboard>() {
|
||||
// SAFETY: as for the graphic arm.
|
||||
let artboard = unsafe { core_types::record::borrow_element::<Artboard>(batch.get(0).rec()) };
|
||||
return Some(Box::new(artboard.with_legacy_groups()));
|
||||
}
|
||||
// SAFETY: lane 0 is a live record of `layout`.
|
||||
return Some(unsafe { (element.clone_out)(batch.get(0).rec().ptr()) });
|
||||
}
|
||||
let batch = capture.batch(arena)?;
|
||||
// SAFETY: the captured bytes live in the arena for the capture's generation.
|
||||
// SAFETY: the caller's batch is resident for the read.
|
||||
let item = unsafe { GroupItem::from_resident(batch) };
|
||||
fn typed<T: Clone + Send + Sync + 'static>(item: &GroupItem) -> Option<Box<dyn std::any::Any + Send + Sync>> {
|
||||
run_to_legacy_list::<T>(item).map(|list| Box::new(list) as Box<dyn std::any::Any + Send + Sync>)
|
||||
|
||||
@@ -2966,6 +2966,46 @@ impl Render for List<String> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for RunView<'_, Artboard> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
render_artboard_svg(self, render, render_params)
|
||||
}
|
||||
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||
render_artboard_vello(self, scene, transform, context, render_params)
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option<NodeId>) {
|
||||
collect_artboard_metadata(self, metadata, footprint)
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
add_artboard_upstream_click_targets(self, click_targets)
|
||||
}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
self.lane_count() > 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for RunView<'_, String> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
render_text_svg(self, render, render_params)
|
||||
}
|
||||
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
||||
render_text_vello(self, scene, transform, render_params)
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
||||
collect_text_metadata(self, metadata, footprint, caller_element_id)
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
add_text_upstream_click_targets(self, click_targets)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SvgSegment {
|
||||
Slice(&'static str),
|
||||
|
||||
Reference in New Issue
Block a user