mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 07:28:12 +08:00
Convert create_context to a sync derivation kernel over the bare-root contract
This commit is contained in:
Generated
+1
@@ -2038,6 +2038,7 @@ dependencies = [
|
|||||||
"core-types",
|
"core-types",
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
|
"graphene-hash",
|
||||||
"graphene-resource",
|
"graphene-resource",
|
||||||
"log",
|
"log",
|
||||||
"raster-types",
|
"raster-types",
|
||||||
|
|||||||
@@ -84,12 +84,12 @@ pub fn wrap_network_in_scope(network: NodeNetwork, editor_api: Arc<PlatformEdito
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
call_argument: concrete!(graphene_std::application_io::RenderConfig),
|
call_argument: concrete!(Context),
|
||||||
inputs: vec![NodeInput::node(NodeId(4), 0)],
|
inputs: vec![NodeInput::node(NodeId(4), 0)],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphene_std::render_node::create_context::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(graphene_std::render_node::create_context::IDENTIFIER),
|
||||||
context_features: graphene_std::ContextDependencies {
|
context_features: graphene_std::ContextDependencies {
|
||||||
// We add the extract index annotation here to force the compiler to add a context nullification node before this node so the render context is properly nullified so the render cache node can do its's work
|
// We add the extract index annotation here to force the compiler to add a context nullification node before this node so the render context is properly nullified so the render cache node can do its's work
|
||||||
extract: ContextFeatures::INDEX,
|
extract: ContextFeatures::INDEX | ContextFeatures::VARARGS,
|
||||||
inject: ContextFeatures::REAL_TIME | ContextFeatures::ANIMATION_TIME | ContextFeatures::POINTER_POSITION | ContextFeatures::FOOTPRINT | ContextFeatures::VARARGS,
|
inject: ContextFeatures::REAL_TIME | ContextFeatures::ANIMATION_TIME | ContextFeatures::POINTER_POSITION | ContextFeatures::FOOTPRINT | ContextFeatures::VARARGS,
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ wgpu = ["dep:raster-types", "raster-types/wgpu"]
|
|||||||
# Local dependencies
|
# Local dependencies
|
||||||
dyn-any = { workspace = true }
|
dyn-any = { workspace = true }
|
||||||
core-types = { workspace = true }
|
core-types = { workspace = true }
|
||||||
|
graphene-hash = { workspace = true }
|
||||||
vector-types = { workspace = true }
|
vector-types = { workspace = true }
|
||||||
text-nodes = { workspace = true }
|
text-nodes = { workspace = true }
|
||||||
graphene-resource = { workspace = true }
|
graphene-resource = { workspace = true }
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ pub trait GetEditorPreferences {
|
|||||||
fn max_render_region_area(&self) -> u32;
|
fn max_render_region_area(&self) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, graphene_hash::CacheHash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub enum ExportFormat {
|
pub enum ExportFormat {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -69,14 +69,14 @@ pub enum ExportFormat {
|
|||||||
Raster,
|
Raster,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, graphene_hash::CacheHash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct TimingInformation {
|
pub struct TimingInformation {
|
||||||
pub time: f64,
|
pub time: f64,
|
||||||
pub animation_time: Duration,
|
pub animation_time: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, graphene_hash::CacheHash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct RenderConfig {
|
pub struct RenderConfig {
|
||||||
pub viewport: Footprint,
|
pub viewport: Footprint,
|
||||||
|
|||||||
@@ -770,6 +770,12 @@ impl<'a> EvalScope<'a> {
|
|||||||
scope
|
scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_pointer_position(&self, pointer_position: Option<DVec2>) -> EvalScope<'a> {
|
||||||
|
let mut scope = EvalScope { pointer_position, ..*self };
|
||||||
|
scope.hash = scope.compute_hash(None);
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
|
||||||
pub fn nullified(&self, keep: ContextFeatures) -> EvalScope<'a> {
|
pub fn nullified(&self, keep: ContextFeatures) -> EvalScope<'a> {
|
||||||
let mut scope = EvalScope {
|
let mut scope = EvalScope {
|
||||||
real_time: self.real_time.filter(|_| keep.contains(ContextFeatures::REAL_TIME)),
|
real_time: self.real_time.filter(|_| keep.contains(ContextFeatures::REAL_TIME)),
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
|
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
|
||||||
assert_eq!(runtime.drain(), vec![], "an interrupted prologue must not spawn or claim the slot");
|
assert_eq!(runtime.drain(), Vec::<SourceId>::new(), "an interrupted prologue must not spawn or claim the slot");
|
||||||
gate.store(true, Ordering::Relaxed);
|
gate.store(true, Ordering::Relaxed);
|
||||||
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
|
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
|
||||||
assert_eq!(runtime.drain(), vec![9]);
|
assert_eq!(runtime.drain(), vec![9]);
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ impl_via_hash! {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl_via_hash! {
|
impl_via_hash! {
|
||||||
String,
|
String,
|
||||||
|
core::time::Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CacheHash for std::borrow::Cow<'a, str> {
|
impl<'a> CacheHash for std::borrow::Cow<'a, str> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use core_types::gpoll::Interrupt;
|
use core_types::gpoll::Interrupt;
|
||||||
use core_types::list::List;
|
use core_types::list::List;
|
||||||
use core_types::transform::{Footprint, Transform};
|
use core_types::transform::{Footprint, Transform};
|
||||||
use core_types::{Color, Context, Ctx, DeriveCtx, ExtractFootprint, ExtractVarArgs, OwnedContextImpl, WasmNotSend};
|
use core_types::{Color, Context, Ctx, DeriveCtx, ExtractFootprint, ExtractVarArgs, VarArgLink, VarArgSlots, WasmNotSend};
|
||||||
use graph_craft::document::value::{RenderOutput, RenderOutputType};
|
use graph_craft::document::value::{RenderOutput, RenderOutputType};
|
||||||
use graphene_application_io::{ExportFormat, RenderConfig};
|
use graphene_application_io::{ExportFormat, RenderConfig};
|
||||||
use graphic_types::raster_types::{CPU, Raster};
|
use graphic_types::raster_types::{CPU, Raster};
|
||||||
@@ -141,11 +141,13 @@ fn render<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(category(""))]
|
||||||
fn create_context<'a>(
|
fn create_context(ctx: impl Ctx + ExtractVarArgs + DeriveCtx, data: impl Node<Context<'_>, Output = RenderOutput>) -> Result<RenderOutput, Interrupt> {
|
||||||
// Context injections are defined in the wrap_network_in_scope function
|
let render_config = *ctx
|
||||||
render_config: RenderConfig,
|
.vararg(0)
|
||||||
data: impl Node<Context<'_>, Output = RenderOutput>,
|
.expect("Did not find var args")
|
||||||
) -> RenderOutput {
|
.downcast_ref::<RenderConfig>()
|
||||||
|
.expect("Downcasting render config yielded invalid type");
|
||||||
|
|
||||||
let render_output_type = match render_config.export_format {
|
let render_output_type = match render_config.export_format {
|
||||||
ExportFormat::Svg => RenderOutputTypeRequest::Svg,
|
ExportFormat::Svg => RenderOutputTypeRequest::Svg,
|
||||||
ExportFormat::Raster => RenderOutputTypeRequest::Vello,
|
ExportFormat::Raster => RenderOutputTypeRequest::Vello,
|
||||||
@@ -166,16 +168,78 @@ fn create_context<'a>(
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let ctx = OwnedContextImpl::default()
|
let scope = ctx
|
||||||
.with_footprint(footprint)
|
.scope()
|
||||||
.with_real_time(render_config.time.time)
|
.with_real_time(Some(render_config.time.time))
|
||||||
.with_animation_time(render_config.time.animation_time.as_secs_f64())
|
.with_animation_time(Some(render_config.time.animation_time.as_secs_f64()))
|
||||||
.with_pointer_position(render_config.pointer)
|
.with_pointer_position(Some(render_config.pointer));
|
||||||
.with_vararg(Box::new(render_params))
|
let varargs = VarArgLink {
|
||||||
.into_context();
|
args: VarArgSlots::Single(&render_params),
|
||||||
|
outer: None,
|
||||||
let mut result = data.eval(ctx).await;
|
};
|
||||||
|
let scoped = ctx.with_scope(&scope);
|
||||||
|
let with_params = scoped.with_varargs(&varargs);
|
||||||
|
let mut result = data.eval(&with_params.with_footprint(&footprint))?;
|
||||||
|
|
||||||
result.metadata.apply_transform(glam::DAffine2::from_scale(glam::DVec2::splat(1. / render_config.scale)));
|
result.metadata.apply_transform(glam::DAffine2::from_scale(glam::DVec2::splat(1. / render_config.scale)));
|
||||||
result
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use core_types::arena::Arena;
|
||||||
|
use core_types::context::{ContextImpl, EvalScope, VarArgsResult};
|
||||||
|
use core_types::gnode::GNode;
|
||||||
|
use core_types::gpoll::GPoll;
|
||||||
|
use core_types::{ExtractAnimationTime, ExtractPointerPosition, ExtractRealTime};
|
||||||
|
use graphene_application_io::TimingInformation;
|
||||||
|
|
||||||
|
struct ProbeNode;
|
||||||
|
|
||||||
|
impl<'a> GNode<ContextImpl<'a>> for ProbeNode {
|
||||||
|
type Output = RenderOutput;
|
||||||
|
|
||||||
|
fn eval(&self, ctx: &ContextImpl<'a>) -> GPoll<RenderOutput> {
|
||||||
|
let render_params = ctx.vararg(0).unwrap().downcast_ref::<RenderParams>().expect("the vararg chain must start with RenderParams");
|
||||||
|
assert_eq!(render_params.scale, 2.0);
|
||||||
|
assert!(matches!(ctx.vararg(1), Err(VarArgsResult::IndexOutOfBounds)), "the RenderConfig must not leak downstream");
|
||||||
|
assert_eq!(ctx.footprint().transform, glam::DAffine2::from_scale(glam::DVec2::splat(2.0)) * Footprint::DEFAULT.transform);
|
||||||
|
assert_eq!(ctx.try_real_time(), Some(1.5));
|
||||||
|
assert_eq!(ctx.try_animation_time(), Some(2.0));
|
||||||
|
assert_eq!(ctx.try_pointer_position(), Some(glam::DVec2::new(3.0, 4.0)));
|
||||||
|
GPoll::Final(RenderOutput {
|
||||||
|
data: RenderOutputType::Buffer { data: Vec::new(), width: 0, height: 0 },
|
||||||
|
metadata: RenderMetadata::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_context_builds_the_render_context_from_the_root_vararg() {
|
||||||
|
let arena = Arena::new(256);
|
||||||
|
let generations = [];
|
||||||
|
let scope = EvalScope::new(None, None, None, &generations, &arena);
|
||||||
|
let root = ContextImpl::root(&scope);
|
||||||
|
let render_config = RenderConfig {
|
||||||
|
scale: 2.0,
|
||||||
|
time: TimingInformation {
|
||||||
|
time: 1.5,
|
||||||
|
animation_time: std::time::Duration::from_secs(2),
|
||||||
|
},
|
||||||
|
pointer: glam::DVec2::new(3.0, 4.0),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let varargs = VarArgLink {
|
||||||
|
args: VarArgSlots::Single(&render_config),
|
||||||
|
outer: None,
|
||||||
|
};
|
||||||
|
let ctx = root.with_varargs(&varargs);
|
||||||
|
|
||||||
|
let graph = CreateContextNode::new(ProbeNode);
|
||||||
|
let GPoll::Final(result) = <CreateContextNode<ProbeNode> as GNode<ContextImpl>>::eval(&graph, &ctx) else {
|
||||||
|
panic!("create_context must complete synchronously");
|
||||||
|
};
|
||||||
|
assert_eq!(result.data, RenderOutputType::Buffer { data: Vec::new(), width: 0, height: 0 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user