Fix Imaginate by porting its JS roundtrip code to graph-based async execution in Rust (#1250)

* Create asynchronous rust imaginate node

* Make a first imaginate request via rust

* Implement parsing of imaginate API result image

* Stop refresh timer from affecting imaginate progress requests

* Add cargo-about clarification for rustls-webpki

* Delete imaginate.ts and all uses of its functions

* Add imaginate img2img feature

* Fix imaginate random seed button

* Fix imaginate ui inferring non-custom resolutions

* Fix the imaginate progress indicator

* Remove ImaginatePreferences from being compiled into node graph

* Regenerate imaginate only when hitting button

* Add ability to terminate imaginate requests

* Add imaginate server check feature

* Do not compile wasm_bindgen bindings in graphite_editor for tests

* Address some review suggestions

- move wasm futures dependency in editor to the future-executor crate
- guard wasm-bindgen in editor behind a `wasm` feature flag
- dont make seed number input a slider
- remove poll_server_check from process_message function beginning
- guard wasm related code behind `cfg(target_arch = "wasm32")` instead
  of `cfg(test)`
- Call the imaginate idle states "Ready" and "Done" instead of "Nothing
  to do"
- Call the imaginate uploading state "Uploading Image" instead of
  "Uploading Input Image"
- Remove the EvalSyncNode

* Fix imaginate host name being restored between graphite instances

also change the progress status texts a bit.

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
nat-rix
2023-06-09 09:03:15 +02:00
committed by Keavon Chambers
parent a1c70c4d90
commit f76b850b9c
35 changed files with 1500 additions and 1326 deletions

View File

@@ -1,8 +1,11 @@
use dyn_any::{DynAny, StaticType};
use glam::{DAffine2, DVec2};
use graph_craft::imaginate_input::{ImaginateController, ImaginateMaskStartingFill, ImaginateSamplingMethod};
use graph_craft::proto::DynFuture;
use graphene_core::raster::{Alpha, BlendMode, BlendNode, Image, ImageFrame, Linear, LinearChannel, Luminance, Pixel, RGBMut, Raster, RasterMut, RedGreenBlue, Sample};
use graphene_core::transform::Transform;
use crate::wasm_application_io::WasmEditorApi;
use graphene_core::raster::bbox::{AxisAlignedBbox, Bbox};
use graphene_core::value::CopiedNode;
use graphene_core::{Color, Node};
@@ -414,19 +417,74 @@ fn empty_image<_P: Pixel>(transform: DAffine2, color: _P) -> ImageFrame<_P> {
ImageFrame { image, transform }
}
#[derive(Debug, Clone, Copy)]
pub struct ImaginateNode<P, E> {
cached: E,
_p: PhantomData<P>,
macro_rules! generate_imaginate_node {
($($val:ident: $t:ident: $o:ty,)*) => {
pub struct ImaginateNode<P: Pixel, E, C, $($t,)*> {
editor_api: E,
controller: C,
$($val: $t,)*
cache: std::sync::Arc<std::sync::Mutex<Image<P>>>,
}
impl<'e, P: Pixel, E, C, $($t,)*> ImaginateNode<P, E, C, $($t,)*>
where $($t: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, $o>>,)*
E: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, WasmEditorApi<'e>>>,
C: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, ImaginateController>>,
{
pub fn new(editor_api: E, controller: C, $($val: $t,)* cache: std::sync::Arc<std::sync::Mutex<Image<P>>>) -> Self {
Self { editor_api, controller, $($val,)* cache }
}
}
impl<'i, 'e: 'i, P: Pixel + 'i, E: 'i, C: 'i, $($t: 'i,)*> Node<'i, ImageFrame<P>> for ImaginateNode<P, E, C, $($t,)*>
where $($t: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, $o>>,)*
E: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, WasmEditorApi<'e>>>,
C: for<'any_input> Node<'any_input, (), Output = DynFuture<'any_input, ImaginateController>>,
{
type Output = DynFuture<'i, ImageFrame<P>>;
fn eval(&'i self, frame: ImageFrame<P>) -> Self::Output {
let controller = self.controller.eval(());
$(let $val = self.$val.eval(());)*
Box::pin(async move {
let controller: std::pin::Pin<Box<dyn std::future::Future<Output = ImaginateController>>> = controller;
let controller: ImaginateController = controller.await;
if controller.take_regenerate_trigger() {
let editor_api = self.editor_api.eval(());
let image = super::imaginate::imaginate(frame.image, editor_api, controller, $($val,)*).await;
self.cache.lock().unwrap().clone_from(&image);
return ImageFrame {
image,
..frame
}
}
let image = self.cache.lock().unwrap().clone();
ImageFrame {
image,
..frame
}
})
}
}
}
}
#[node_macro::node_fn(ImaginateNode<_P>)]
fn imaginate<_P: Pixel>(image_frame: ImageFrame<_P>, cached: Option<std::sync::Arc<graphene_core::raster::Image<_P>>>) -> ImageFrame<_P> {
let cached_image = cached.map(|mut x| std::sync::Arc::make_mut(&mut x).clone()).unwrap_or(image_frame.image);
ImageFrame {
image: cached_image,
transform: image_frame.transform,
}
generate_imaginate_node! {
seed: Seed: f64,
res: Res: Option<DVec2>,
samples: Samples: u32,
sampling_method: SamplingMethod: ImaginateSamplingMethod,
prompt_guidance: PromptGuidance: f64,
prompt: Prompt: String,
negative_prompt: NegativePrompt: String,
adapt_input_image: AdaptInputImage: bool,
image_creativity: ImageCreativity: f64,
masking_layer: MaskingLayer: Option<Vec<u64>>,
inpaint: Inpaint: bool,
mask_blur: MaskBlur: f64,
mask_starting_fill: MaskStartingFill: ImaginateMaskStartingFill,
improve_faces: ImproveFaces: bool,
tiling: Tiling: bool,
}
#[derive(Debug, Clone, Copy)]