Graphene CLI + quantization research (#1320)

* Implement skeleton for graphene-cli

* Configure gpu surface on non wasm32 targets

* Create window with full hd size

* Create window using the graphen-cli

* Use window size for surface creation

* Reuse surface configuration

* Reduce window size for native applications to 800x600

* Add compute pipeline test

* Poll wgpu execution externally

* Remove cache node after texture upload

* Add profiling instructions

* Add more debug markers

* Evaluate extract node before flattening the network

* Reenable hue saturation node for compilation

* Make hue saturation node work on the gpu + make f32 default for user inputs

* Add version of test files without caching

* Only dispatch each workgroup not pixel

* ICE

* Add quantization to gpu code

* Fix quantization

* Load images at graph runtime

* Fix quantization calculation

* Feature gate quantization

* Use git version of autoquant

* Add license to `graphene-cli`

* Fix graphene-cli test case

* Ignore tests on non unix platforms

* Fix flattening test
This commit is contained in:
Dennis Kobert
2023-07-04 17:04:09 +02:00
committed by GitHub
parent 19318a948c
commit f2b911b358
57 changed files with 10169 additions and 845 deletions

View File

@@ -99,7 +99,7 @@ impl DocumentNode {
document_node_path: self.path.unwrap_or(Vec::new()),
}
} else {
unreachable!("tried to resolve not flattened node on resolved node");
unreachable!("tried to resolve not flattened node on resolved node {:?}", self);
}
}
@@ -654,10 +654,13 @@ impl NodeNetwork {
/// Recursively dissolve non-primitive document nodes and return a single flattened network of nodes.
pub fn flatten_with_fns(&mut self, node: NodeId, map_ids: impl Fn(NodeId, NodeId) -> NodeId + Copy, gen_id: impl Fn() -> NodeId + Copy) {
let (id, mut node) = self
self.resolve_extract_nodes();
let Some((id, mut node)) = self
.nodes
.remove_entry(&node)
.unwrap_or_else(|| panic!("The node which was supposed to be flattened does not exist in the network, id {} network {:#?}", node, self));
.remove_entry(&node) else {
warn!("The node which was supposed to be flattened does not exist in the network, id {} network {:#?}", node, self);
return;
};
if self.disabled.contains(&id) {
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
@@ -665,11 +668,12 @@ impl NodeNetwork {
self.nodes.insert(id, node);
return;
}
log::debug!("Flattening node {:?}", &node.name);
// replace value inputs with value nodes
for input in &mut node.inputs {
// Skip inputs that are already value nodes
if node.implementation == DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()) {
if node.implementation == DocumentNodeImplementation::Unresolved("graphene_core::value::ClonedNode".into()) {
break;
}
@@ -690,7 +694,7 @@ impl NodeNetwork {
DocumentNode {
name: "Value".into(),
inputs: vec![NodeInput::Value { tagged_value, exposed }],
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ClonedNode".into()),
path,
..Default::default()
},
@@ -706,6 +710,8 @@ impl NodeNetwork {
}
if let DocumentNodeImplementation::Network(mut inner_network) = node.implementation {
// Resolve all extract nodes in the inner network
inner_network.resolve_extract_nodes();
// Connect all network inputs to either the parent network nodes, or newly created value nodes.
inner_network.map_ids(|inner_id| map_ids(id, inner_id));
let new_nodes = inner_network.nodes.keys().cloned().collect::<Vec<_>>();
@@ -717,8 +723,10 @@ impl NodeNetwork {
assert_eq!(
node.inputs.len(),
inner_network.inputs.len(),
"The number of inputs to the node and the inner network must be the same {}",
node.name
"The number of inputs to the node and the inner network must be the same for {}. The node has {:?} inputs, the network has {:?} inputs.",
node.name,
node.inputs,
inner_network.inputs
);
// Match the document node input and the inputs of the inner network
for (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) {
@@ -829,21 +837,30 @@ impl NodeNetwork {
self.nodes.retain(|_, node| !matches!(node.implementation, DocumentNodeImplementation::Extract));
for (_, node) in &mut extraction_nodes {
log::info!("extraction network: {:#?}", &self);
if let DocumentNodeImplementation::Extract = node.implementation {
assert_eq!(node.inputs.len(), 1);
log::debug!("Resolving extract node {:?}", node);
let NodeInput::Node { node_id, output_index, .. } = node.inputs.pop().unwrap() else {
panic!("Extract node has no input");
panic!("Extract node has no input, inputs: {:?}", node.inputs);
};
assert_eq!(output_index, 0);
// TODO: check if we can readd lambda checking
let mut input_node = self.nodes.remove(&node_id).unwrap();
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into());
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::value::ClonedNode".into());
if let Some(input) = input_node.inputs.get_mut(0) {
*input = match &input {
NodeInput::Node { .. } => NodeInput::Network(generic!(T)),
ni => NodeInput::Network(ni.ty()),
};
}
for input in input_node.inputs.iter_mut() {
match input {
NodeInput::Node { .. } | NodeInput::Value { .. } => *input = NodeInput::Network(generic!(T)),
_ => (),
if let NodeInput::Node { .. } = input {
*input = NodeInput::Network(generic!(T))
}
}
log::debug!("Extract node {:?} resolved to {:?}", node, input_node);
node.inputs = vec![NodeInput::value(TaggedValue::DocumentNode(input_node), false)];
}
}
@@ -1108,7 +1125,7 @@ mod test {
tagged_value: TaggedValue::U32(2),
exposed: false,
}],
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ClonedNode".into()),
path: Some(vec![1, 4]),
..Default::default()
},

View File

@@ -55,6 +55,7 @@ pub enum TaggedValue {
ManipulatorGroupIds(Vec<graphene_core::uuid::ManipulatorGroupId>),
Font(graphene_core::text::Font),
BrushStrokes(Vec<graphene_core::vector::brush_stroke::BrushStroke>),
#[cfg_attr(feature = "serde", serde(skip))]
BrushCache(BrushCache),
Segments(Vec<graphene_core::raster::ImageFrame<Color>>),
DocumentNode(DocumentNode),
@@ -193,6 +194,7 @@ impl<'a> TaggedValue {
TaggedValue::F64(x) => x.to_string() + "_f64",
TaggedValue::Bool(x) => x.to_string(),
TaggedValue::BlendMode(blend_mode) => "BlendMode::".to_string() + &blend_mode.to_string(),
TaggedValue::Color(color) => "graphene_core::Color::from_rgbaf32_unchecked(0.,0.,0.,1.)".to_string(),
_ => panic!("Cannot convert to primitive string"),
}
}

View File

@@ -9,13 +9,12 @@ pub struct Compiler {}
impl Compiler {
pub fn compile(&self, mut network: NodeNetwork, resolve_inputs: bool) -> impl Iterator<Item = ProtoNetwork> {
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
println!("flattening");
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
for id in node_ids {
network.flatten(id);
}
network.remove_redundant_id_nodes();
network.resolve_extract_nodes();
network.remove_dead_nodes();
let proto_networks = network.into_proto_networks();
proto_networks.map(move |mut proto_network| {

View File

@@ -20,10 +20,10 @@ pub type LocalFuture<'n, T> = Pin<Box<dyn core::future::Future<Output = T> + 'n>
pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
pub type FutureAny<'n> = DynFuture<'n, Any<'n>>;
pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n;
pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n)>;
pub type TypeErasedRef<'n> = &'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n);
pub type TypeErasedBox<'n> = Box<dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n>;
pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n>>;
pub type TypeErasedPinnedRef<'n> = Pin<&'n TypeErasedNode<'n>>;
pub type TypeErasedRef<'n> = &'n TypeErasedNode<'n>;
pub type TypeErasedBox<'n> = Box<TypeErasedNode<'n>>;
pub type TypeErasedPinned<'n> = Pin<Box<TypeErasedNode<'n>>>;
pub type NodeConstructor = for<'a> fn(Vec<Arc<NodeContainer>>) -> DynFuture<'static, TypeErasedBox<'static>>;
@@ -181,7 +181,7 @@ impl Hash for ConstructionArgs {
impl ConstructionArgs {
pub fn new_function_args(&self) -> Vec<String> {
match self {
ConstructionArgs::Nodes(nodes) => nodes.iter().map(|n| format!("&n{}", n.0)).collect(),
ConstructionArgs::Nodes(nodes) => nodes.iter().map(|n| format!("n{:0x}", n.0)).collect(),
ConstructionArgs::Value(value) => vec![value.to_primitive_string()],
ConstructionArgs::Inline(inline) => vec![inline.expr.clone()],
}
@@ -248,7 +248,7 @@ impl ProtoNode {
pub fn value(value: ConstructionArgs, path: Vec<NodeId>) -> Self {
Self {
identifier: NodeIdentifier::new("graphene_core::value::ValueNode"),
identifier: NodeIdentifier::new("graphene_core::value::ClonedNode"),
construction_args: value,
input: ProtoNodeInput::None,
document_node_path: path,