Update graphene-cli and fix no-std compilation for graphene-core (#1428)

* Initialize wgpu executor from graphene cli

* Make `graphene-core` `no-std` complient again

* Implemnt image similarity calculation

* Add nan checks

* Make image comparison optional

* Feature gate quantization to reduce the number of warnings
This commit is contained in:
Dennis Kobert 2023-09-30 11:20:17 +02:00 committed by GitHub
parent b9027883a8
commit 7e3469fa3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 571 additions and 406 deletions

View file

@ -4,6 +4,7 @@ pub struct LogToConsoleNode;
#[node_macro::node_fn(LogToConsoleNode)]
fn log_to_console<T: core::fmt::Debug>(value: T) -> T {
#[cfg(not(target_arch = "spirv"))]
debug!("{:#?}", value);
value
}

View file

@ -3,6 +3,9 @@ use core::marker::PhantomData;
use core::ops::{Add, Div, Mul, Rem, Sub};
use num_traits::Pow;
#[cfg(target_arch = "spirv")]
use spirv_std::num_traits::float::Float;
// Add
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct AddNode;

View file

@ -150,13 +150,27 @@ fn dequantize_fn<'a>(color: PackedPixel, quantization: [Quantization; 4]) -> Col
pub fn dequantize_color(color: PackedPixel, quant: [Quantization; 4]) -> Color {
let mut offset = 0;
let r = decode(color.0, offset, quant[0]);
let mut r = decode(color.0, offset, quant[0]);
offset += quant[0].bits();
let g = decode(color.0, offset, quant[1]);
let mut g = decode(color.0, offset, quant[1]);
offset += quant[1].bits();
let b = decode(color.0, offset, quant[2]);
let mut b = decode(color.0, offset, quant[2]);
offset += quant[2].bits();
let a = decode(color.0, offset, quant[3]);
let mut a = decode(color.0, offset, quant[3]);
if a.is_nan() {
a = 0.;
}
if r.is_nan() {
r = 0.;
}
if g.is_nan() {
g = 0.;
}
if b.is_nan() {
b = 0.;
}
Color::from_rgbaf32_unchecked(r, g, b, a)
}

View file

@ -7,6 +7,9 @@ use glam::DVec2;
pub use self::color::{Color, Luma, SRGBA8};
#[cfg(target_arch = "spirv")]
use spirv_std::num_traits::float::Float;
pub mod adjustments;
pub mod bbox;
#[cfg(not(target_arch = "spirv"))]
@ -14,6 +17,7 @@ pub mod brightness_contrast;
#[cfg(not(target_arch = "spirv"))]
pub mod brush_cache;
pub mod color;
#[cfg(not(target_arch = "spirv"))]
pub mod curve;
pub mod discrete_srgb;
pub use adjustments::*;

View file

@ -1,9 +1,12 @@
#![allow(clippy::too_many_arguments)]
#[cfg(feature = "alloc")]
use super::curve::{Curve, CurveManipulatorGroup, ValueMapperNode};
use super::{Channel, Color, ImageFrame, Node, RGBMut};
use super::{Channel, Color, Node, RGBMut};
#[cfg(feature = "alloc")]
use super::ImageFrame;
use bezier_rs::{Bezier, TValue};
use dyn_any::{DynAny, StaticType};
use core::fmt::Debug;
@ -890,14 +893,17 @@ fn exposure(color: Color, exposure: f32, offset: f32, gamma_correction: f32) ->
const WINDOW_SIZE: usize = 1024;
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, Copy)]
pub struct GenerateCurvesNode<OutputChannel, Curve> {
curve: Curve,
_channel: core::marker::PhantomData<OutputChannel>,
}
#[cfg(feature = "alloc")]
#[node_macro::node_fn(GenerateCurvesNode<_Channel>)]
fn generate_curves<_Channel: Channel + super::Linear>(_primary: (), curve: Curve) -> ValueMapperNode<_Channel> {
use bezier_rs::{Bezier, TValue};
let [mut pos, mut param]: [[f32; 2]; 2] = [[0.; 2], curve.first_handle];
let mut lut = vec![_Channel::from_f64(0.); WINDOW_SIZE];
let end = CurveManipulatorGroup {
@ -934,11 +940,13 @@ fn generate_curves<_Channel: Channel + super::Linear>(_primary: (), curve: Curve
ValueMapperNode::new(lut)
}
#[cfg(feature = "alloc")]
#[derive(Debug, Clone)]
pub struct ColorFillNode<C> {
color: C,
}
#[cfg(feature = "alloc")]
#[node_macro::node_fn(ColorFillNode)]
pub fn color_fill_node(mut image_frame: ImageFrame<Color>, color: Color) -> ImageFrame<Color> {
for pixel in &mut image_frame.image.data {
@ -951,12 +959,14 @@ pub fn color_fill_node(mut image_frame: ImageFrame<Color>, color: Color) -> Imag
image_frame
}
#[cfg(feature = "alloc")]
pub struct ColorOverlayNode<Color, BlendMode, Opacity> {
color: Color,
blend_mode: BlendMode,
opacity: Opacity,
}
#[cfg(feature = "alloc")]
#[node_macro::node_fn(ColorOverlayNode)]
pub fn color_overlay_node(mut image: ImageFrame<Color>, color: Color, blend_mode: BlendMode, opacity: f32) -> ImageFrame<Color> {
let opacity = (opacity / 100.).clamp(0., 1.);

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
[toolchain]
channel = "nightly-2023-03-04"
channel = "nightly-2023-05-27"
components = [
"rust-src",
"rustc-dev",

View file

@ -13,7 +13,7 @@ crate-type = ["dylib", "lib"]
libm = { git = "https://github.com/rust-lang/libm", tag = "0.2.5" }
[dependencies]
spirv-std = { version = "0.8" }
spirv-std = { version = "0.9" }
graphene-core = { path = "{{gcore_path}}", default-features = false, features = [
"gpu",
] }

View file

@ -1,5 +1,5 @@
[toolchain]
channel = "nightly-2023-03-04"
channel = "nightly-2023-05-27"
components = [
"rust-src",
"rustc-dev",

View file

@ -21,10 +21,14 @@ wgpu = ["gpu", "wgpu-executor"]
quantization = ["autoquant"]
wasm = ["wasm-bindgen", "web-sys", "js-sys"]
imaginate = ["image/png", "base64", "js-sys", "web-sys", "wasm-bindgen-futures"]
image-compare = ["dep:image-compare"]
wayland = []
[dependencies]
rand = { version = "0.8.5", features = ["alloc", "small_rng"], default-features = false}
rand = { version = "0.8.5", features = [
"alloc",
"small_rng",
], default-features = false }
rand_chacha = { version = "0.3.1", default-features = false }
autoquant = { git = "https://github.com/truedoctor/autoquant", optional = true, features = [
"fitting",
@ -44,7 +48,10 @@ gpu-compiler-bin-wrapper = { path = "../gpu-compiler/gpu-compiler-bin-wrapper",
compilation-client = { path = "../compilation-client", optional = true }
bytemuck = { version = "1.13" }
tempfile = "3"
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
image = { version = "0.24", default-features = false, features = [
"png",
"jpeg",
] }
base64 = { version = "0.21", optional = true }
dyn-clone = "1.0"
@ -64,6 +71,7 @@ wasm-bindgen-futures = { version = "0.4.36", optional = true }
winit = "0.28.6"
url = "2.4.0"
tokio = { version = "1.29.0", optional = true, features = ["fs", "io-std"] }
image-compare = { version = "0.3.0", optional = true }
[dependencies.serde]
version = "1.0"

View file

@ -10,6 +10,9 @@ use graphene_core::raster::*;
use graphene_core::*;
use wgpu_executor::WgpuExecutor;
#[cfg(feature = "quantization")]
use graphene_core::quantization::PackedPixel;
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
@ -74,6 +77,11 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
let quantization = QuantizationChannels::default();
log::debug!("quantization: {:?}", quantization);
#[cfg(feature = "image-compare")]
let img: image::DynamicImage = image::Rgba32FImage::from_raw(image.image.width, image.image.height, bytemuck::cast_vec(image.image.data.clone()))
.unwrap()
.into();
#[cfg(feature = "quantization")]
let image = ImageFrame {
image: Image {
@ -83,6 +91,7 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
},
transform: image.transform,
};
// TODO: The cache should be based on the network topology not the node name
let compute_pass_descriptor = if self.cache.borrow().contains_key(&node.name) {
self.cache.borrow().get(&node.name).unwrap().clone()
@ -114,6 +123,14 @@ async fn map_gpu<'a: 'input>(image: ImageFrame<Color>, node: DocumentNode, edito
#[cfg(not(feature = "quantization"))]
let colors = bytemuck::pod_collect_to_vec::<u8, Color>(result.as_slice());
log::debug!("first color: {:?}", colors[0]);
#[cfg(feature = "image-compare")]
let img2: image::DynamicImage = image::Rgba32FImage::from_raw(image.image.width, image.image.height, bytemuck::cast_vec(colors.clone())).unwrap().into();
#[cfg(feature = "image-compare")]
let score = image_compare::rgb_hybrid_compare(&img.into_rgb8(), &img2.into_rgb8()).unwrap();
#[cfg(feature = "image-compare")]
log::debug!("score: {:?}", score.score);
ImageFrame {
image: Image {
data: colors,
@ -279,6 +296,10 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
return frame;*/
log::debug!("creating buffer");
let width_uniform = executor.create_uniform_buffer(image.image.width).unwrap();
#[cfg(not(feature = "quantization"))]
core::hint::black_box(quantization);
#[cfg(feature = "quantization")]
let quantization_uniform = executor.create_uniform_buffer(quantization).unwrap();
let storage_buffer = executor
.create_storage_buffer(
@ -292,7 +313,8 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
)
.unwrap();
let width_uniform = Arc::new(width_uniform);
let _quantization_uniform = Arc::new(quantization_uniform);
#[cfg(feature = "quantization")]
let quantization_uniform = Arc::new(quantization_uniform);
let storage_buffer = Arc::new(storage_buffer);
let output_buffer = executor.create_output_buffer(len, concrete!(Color), false).unwrap();
let output_buffer = Arc::new(output_buffer);

View file

@ -48,7 +48,7 @@ fn generate_quantization(data: Vec<f64>, samples: usize) -> [Quantization; 4] {
let merged: ErrorFunction<30> = autoquant::packing::merge_error_functions(&merged, &blue_error);
let merged: ErrorFunction<40> = autoquant::packing::merge_error_functions(&merged, &alpha_error);
let bin_size = 32;
let bin_size = 8;
let mut distributions = [red, green, blue, alpha].into_iter();
let bits = &merged.bits[bin_size];

View file

@ -56,7 +56,7 @@ impl WasmApplicationIo {
None
};
#[cfg(all(feature = "wgpu", not(target_arch = "wasm32")))]
let executor = None;
let executor = WgpuExecutor::new().await;
let mut io = Self {
#[cfg(target_arch = "wasm32")]
ids: RefCell::new(0),