Instance tables refactor part 1: wrap graphical data in the new Instances<T> struct (#2230)

* Port VectorData to Instances<VectorData>

* Port ImageFrame<P> and TextureFrame to Instances<ImageFrame<P>> and Instances<TextureFrame>

* Avoid mutation with the TransformMut trait

* Port GraphicGroup to Instances<GraphicGroup>

* It compiles!

* Organize debugging

* Document upgrading

* Fix Brush node

* Restore TransformMut in lieu of TransformSet trait

* Fix tests

* Final code review
This commit is contained in:
Keavon Chambers
2025-01-28 23:51:12 -08:00
committed by GitHub
parent 408f9bffa1
commit eb0ff20d3c
43 changed files with 1855 additions and 1221 deletions

View File

@@ -4,7 +4,8 @@ use graph_craft::document::value::TaggedValue;
use graph_craft::document::*;
use graph_craft::proto::*;
use graphene_core::application_io::ApplicationIo;
use graphene_core::raster::*;
use graphene_core::raster::image::{ImageFrame, ImageFrameTable};
use graphene_core::raster::{BlendMode, Image, Pixel};
use graphene_core::*;
use wgpu_executor::{Bindgroup, PipelineLayout, Shader, ShaderIO, ShaderInput, WgpuExecutor, WgpuShaderInput};
@@ -61,7 +62,10 @@ impl Clone for ComputePass {
}
#[node_macro::old_node_impl(MapGpuNode)]
async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, editor_api: &'a graphene_core::application_io::EditorApi<WasmApplicationIo>) -> ImageFrame<Color> {
async fn map_gpu<'a: 'input>(image: ImageFrameTable<Color>, node: DocumentNode, editor_api: &'a graphene_core::application_io::EditorApi<WasmApplicationIo>) -> ImageFrameTable<Color> {
let image_frame_table = &image;
let image = image.one_item();
log::debug!("Executing gpu node");
let executor = &editor_api.application_io.as_ref().and_then(|io| io.gpu_executor()).unwrap();
@@ -75,9 +79,9 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
self.cache.lock().as_ref().unwrap().get("placeholder").unwrap().clone()
} else {
let name = "placeholder".to_string();
let Ok(compute_pass_descriptor) = create_compute_pass_descriptor(node, &image, executor).await else {
let Ok(compute_pass_descriptor) = create_compute_pass_descriptor(node, image_frame_table, executor).await else {
log::error!("Error creating compute pass descriptor in 'map_gpu()");
return ImageFrame::empty();
return ImageFrameTable::default();
};
self.cache.lock().as_mut().unwrap().insert(name, compute_pass_descriptor.clone());
log::error!("created compute pass");
@@ -105,7 +109,7 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
#[cfg(feature = "image-compare")]
log::debug!("score: {:?}", score.score);
ImageFrame {
let result = ImageFrame {
image: Image {
data: colors,
width: image.image.width,
@@ -114,7 +118,9 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
},
transform: image.transform,
alpha_blending: image.alpha_blending,
}
};
ImageFrameTable::new(result)
}
impl<Node, EditorApi> MapGpuNode<Node, EditorApi> {
@@ -127,7 +133,13 @@ impl<Node, EditorApi> MapGpuNode<Node, EditorApi> {
}
}
async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(node: DocumentNode, image: &ImageFrame<T>, executor: &&WgpuExecutor) -> Result<ComputePass, String> {
async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(node: DocumentNode, image: &ImageFrameTable<T>, executor: &&WgpuExecutor) -> Result<ComputePass, String>
where
GraphicElement: From<ImageFrame<T>>,
T::Static: Pixel,
{
let image = image.one_item();
let compiler = graph_craft::graphene_compiler::Compiler {};
let inner_network = NodeNetwork::value_network(node);
@@ -145,40 +157,37 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(node
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into()),
..Default::default()
},
/*DocumentNode {
name: "Index".into(),
// inputs: vec![NodeInput::Network(concrete!(UVec3))],
inputs: vec![NodeInput::Inline(InlineRust::new("i1.x as usize".into(), concrete![u32]))],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::value::CopiedNode".into()),
..Default::default()
},*/
/*
DocumentNode {
name: "Get Node".into(),
inputs: vec![NodeInput::node(NodeId(1), 0), NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::storage::GetNode".into()),
..Default::default()
},*/
// DocumentNode {
// name: "Index".into(),
// // inputs: vec![NodeInput::Network(concrete!(UVec3))],
// inputs: vec![NodeInput::Inline(InlineRust::new("i1.x as usize".into(), concrete![u32]))],
// implementation: DocumentNodeImplementation::ProtoNode("graphene_core::value::CopiedNode".into()),
// ..Default::default()
// },
// DocumentNode {
// name: "Get Node".into(),
// inputs: vec![NodeInput::node(NodeId(1), 0), NodeInput::node(NodeId(0), 0)],
// implementation: DocumentNodeImplementation::ProtoNode("graphene_core::storage::GetNode".into()),
// ..Default::default()
// },
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Network(inner_network),
..Default::default()
},
/*
DocumentNode {
name: "Save Node".into(),
inputs: vec![
NodeInput::node(NodeId(5), 0),
NodeInput::Inline(InlineRust::new(
"|x| o0[(_global_index.y * i1 + _global_index.x) as usize] = x".into(),
// "|x|()".into(),
Type::Fn(Box::new(concrete!(PackedPixel)), Box::new(concrete!(()))),
)),
],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::generic::FnMutNode".into()),
..Default::default()
},
*/
// DocumentNode {
// name: "Save Node".into(),
// inputs: vec![
// NodeInput::node(NodeId(5), 0),
// NodeInput::Inline(InlineRust::new(
// "|x| o0[(_global_index.y * i1 + _global_index.x) as usize] = x".into(),
// // "|x|()".into(),
// Type::Fn(Box::new(concrete!(PackedPixel)), Box::new(concrete!(()))),
// )),
// ],
// implementation: DocumentNodeImplementation::ProtoNode("graphene_core::generic::FnMutNode".into()),
// ..Default::default()
// },
]
.into_iter()
.enumerate()
@@ -204,7 +213,7 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(node
)
.await
.unwrap();
// return ImageFrame::empty();
let len: usize = image.image.data.len();
let storage_buffer = executor
@@ -218,21 +227,22 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(node
},
)
.unwrap();
/*
let canvas = editor_api.application_io.create_surface();
let surface = unsafe { executor.create_surface(canvas) }.unwrap();
let surface_id = surface.surface_id;
// let canvas = editor_api.application_io.create_surface();
let texture = executor.create_texture_buffer(image.image.clone(), TextureBufferOptions::Texture).unwrap();
// let surface = unsafe { executor.create_surface(canvas) }.unwrap();
// let surface_id = surface.surface_id;
// executor.create_render_pass(texture, surface).unwrap();
// let texture = executor.create_texture_buffer(image.image.clone(), TextureBufferOptions::Texture).unwrap();
// // executor.create_render_pass(texture, surface).unwrap();
// let frame = SurfaceFrame {
// surface_id,
// transform: image.transform,
// };
// return frame;
let frame = SurfaceFrame {
surface_id,
transform: image.transform,
};
return frame;*/
log::debug!("creating buffer");
let width_uniform = executor.create_uniform_buffer(image.image.width).unwrap();
@@ -269,9 +279,13 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(node
}
#[node_macro::node(category("Debug: GPU"))]
async fn blend_gpu_image(_: (), foreground: ImageFrame<Color>, background: ImageFrame<Color>, blend_mode: BlendMode, opacity: f64) -> ImageFrame<Color> {
async fn blend_gpu_image(_: (), foreground: ImageFrameTable<Color>, background: ImageFrameTable<Color>, blend_mode: BlendMode, opacity: f64) -> ImageFrameTable<Color> {
let foreground = foreground.one_item();
let background = background.one_item();
let foreground_size = DVec2::new(foreground.image.width as f64, foreground.image.height as f64);
let background_size = DVec2::new(background.image.width as f64, background.image.height as f64);
// Transforms a point from the background image to the foreground image
let bg_to_fg = DAffine2::from_scale(foreground_size) * foreground.transform.inverse() * background.transform * DAffine2::from_scale(1. / background_size);
@@ -320,7 +334,7 @@ async fn blend_gpu_image(_: (), foreground: ImageFrame<Color>, background: Image
let proto_networks: Result<Vec<_>, _> = compiler.compile(network.clone()).collect();
let Ok(proto_networks_result) = proto_networks else {
log::error!("Error compiling network in 'blend_gpu_image()");
return ImageFrame::empty();
return ImageFrameTable::default();
};
let proto_networks = proto_networks_result;
log::debug!("compiling shader");
@@ -430,7 +444,7 @@ async fn blend_gpu_image(_: (), foreground: ImageFrame<Color>, background: Image
let result = executor.read_output_buffer(readback_buffer).await.unwrap();
let colors = bytemuck::pod_collect_to_vec::<u8, Color>(result.as_slice());
ImageFrame {
let result = ImageFrame {
image: Image {
data: colors,
width: background.image.width,
@@ -439,5 +453,7 @@ async fn blend_gpu_image(_: (), foreground: ImageFrame<Color>, background: Image
},
transform: background.transform,
alpha_blending: background.alpha_blending,
}
};
ImageFrameTable::new(result)
}