mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
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:
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user