mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Cut over to the graphene execution model
This commit is contained in:
@@ -9,17 +9,16 @@ use crate::texture_cache::TextureCache;
|
||||
use anyhow::Result;
|
||||
use core_types::Color;
|
||||
use core_types::color::SRGBA8;
|
||||
use futures::lock::Mutex;
|
||||
use glam::UVec2;
|
||||
use graphene_application_io::{ApplicationIo, EditorApi};
|
||||
use raster_types::Texture;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use vello::{AaConfig, AaSupport, RenderParams, Renderer, RendererOptions, Scene};
|
||||
use wgpu::{Origin3d, TextureAspect};
|
||||
|
||||
pub use context::Context as WgpuContext;
|
||||
pub use context::ContextBuilder as WgpuContextBuilder;
|
||||
pub use pipeline::AsyncPipeline as AsyncWgpuPipeline;
|
||||
pub use pipeline::Pipeline as WgpuPipeline;
|
||||
pub use pipeline::PipelineCache as WgpuPipelineCache;
|
||||
pub use rendering::RenderContext;
|
||||
@@ -61,6 +60,18 @@ impl std::fmt::Debug for WgpuExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
/// Owned Arc handle carrying the executor as an ordinary wire value.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WgpuExecutorHandle(pub std::sync::Arc<WgpuExecutor>);
|
||||
|
||||
impl std::ops::Deref for WgpuExecutorHandle {
|
||||
type Target = WgpuExecutor;
|
||||
|
||||
fn deref(&self) -> &WgpuExecutor {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ApplicationIo<Executor = WgpuExecutor>> From<&'a EditorApi<T>> for &'a WgpuExecutor {
|
||||
fn from(editor_api: &'a EditorApi<T>) -> Self {
|
||||
editor_api.application_io.as_ref().unwrap().gpu_executor().unwrap()
|
||||
@@ -68,8 +79,8 @@ impl<'a, T: ApplicationIo<Executor = WgpuExecutor>> From<&'a EditorApi<T>> for &
|
||||
}
|
||||
|
||||
impl WgpuExecutor {
|
||||
pub async fn render_vello_scene(&self, scene: &Scene, size: UVec2, context: &RenderContext, background: Option<Color>) -> Result<Texture> {
|
||||
let texture = self.request_texture(size).await;
|
||||
pub fn render_vello_scene(&self, scene: &Scene, size: UVec2, context: &RenderContext, background: Option<Color>) -> Result<Texture> {
|
||||
let texture = self.request_texture(size);
|
||||
|
||||
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
@@ -82,7 +93,7 @@ impl WgpuExecutor {
|
||||
};
|
||||
|
||||
{
|
||||
let mut renderer = self.inner.vello_renderer.lock().await;
|
||||
let mut renderer = self.inner.vello_renderer.lock().unwrap();
|
||||
for (image_brush, texture) in context.resource_overrides.iter() {
|
||||
let texture_view = wgpu::TexelCopyTextureInfoBase {
|
||||
texture: (**texture).clone(),
|
||||
@@ -109,8 +120,8 @@ impl WgpuExecutor {
|
||||
pipeline.init::<P>(self);
|
||||
}
|
||||
|
||||
pub async fn request_texture(&self, size: UVec2) -> Texture {
|
||||
self.inner.texture_cache.lock().await.request_texture(&self.context().device, size)
|
||||
pub fn request_texture(&self, size: UVec2) -> Texture {
|
||||
self.inner.texture_cache.lock().unwrap().request_texture(&self.context().device, size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +1,16 @@
|
||||
use dyn_any::DynAny;
|
||||
use std::any::Any;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use crate::WgpuExecutor;
|
||||
|
||||
pub type PipelineFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
||||
|
||||
pub trait Pipeline: Any + Send + Sync + Sized {
|
||||
type Args<'a>;
|
||||
type Out: Send;
|
||||
|
||||
fn create(executor: &WgpuExecutor) -> Self;
|
||||
|
||||
fn run<'a>(&'a self, executor: &'a WgpuExecutor, args: &'a Self::Args<'_>) -> PipelineFuture<'a, Self::Out>;
|
||||
}
|
||||
|
||||
pub trait AsyncPipeline: Any + Send + Sync + Sized {
|
||||
type Args<'a>;
|
||||
type Out: Send;
|
||||
|
||||
fn create(executor: &WgpuExecutor) -> Self;
|
||||
|
||||
fn run<'a>(&'a self, executor: &'a WgpuExecutor, args: &'a Self::Args<'_>) -> impl Future<Output = Self::Out> + Send + 'a;
|
||||
}
|
||||
|
||||
impl<P: AsyncPipeline> Pipeline for P {
|
||||
type Args<'a> = <P as AsyncPipeline>::Args<'a>;
|
||||
type Out = <P as AsyncPipeline>::Out;
|
||||
|
||||
fn create(executor: &WgpuExecutor) -> Self {
|
||||
<P as AsyncPipeline>::create(executor)
|
||||
}
|
||||
|
||||
fn run<'a>(&'a self, executor: &'a WgpuExecutor, args: &'a Self::Args<'_>) -> PipelineFuture<'a, Self::Out> {
|
||||
Box::pin(<P as AsyncPipeline>::run(self, executor, args))
|
||||
}
|
||||
fn run<'a>(&'a self, executor: &'a WgpuExecutor, args: &'a Self::Args<'_>) -> Self::Out;
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, DynAny)]
|
||||
@@ -51,13 +25,13 @@ impl PipelineCache {
|
||||
self.pipeline.get_or_init(|| Box::new(P::create(executor)));
|
||||
}
|
||||
|
||||
pub async fn run<P: Pipeline>(&self, args: &P::Args<'_>) -> P::Out {
|
||||
pub fn run<P: Pipeline>(&self, args: &P::Args<'_>) -> P::Out {
|
||||
let executor = self.executor.get().expect("PipelineCache not initialized");
|
||||
let entry = self.pipeline.get().expect("PipelineCache not initialized");
|
||||
let pipeline = (&**entry)
|
||||
let pipeline = (**entry)
|
||||
.downcast_ref::<P>()
|
||||
.unwrap_or_else(|| panic!("PipelineCache type mismatch: run::<{}>() but init used a different pipeline type", std::any::type_name::<P>(),));
|
||||
pipeline.run(executor, args).await
|
||||
pipeline.run(executor, args)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ use crate::WgpuContext;
|
||||
use crate::shader_runtime::{FULLSCREEN_VERTEX_SHADER_NAME, ShaderRuntime};
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::shaders::buffer_struct::BufferStruct;
|
||||
use futures::lock::Mutex;
|
||||
use raster_types::{GPU, Raster};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use wgpu::util::{BufferInitDescriptor, DeviceExt};
|
||||
use wgpu::{
|
||||
BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, Buffer, BufferBinding, BufferBindingType, BufferUsages, ColorTargetState, Face,
|
||||
@@ -33,8 +33,8 @@ impl PerPixelAdjustShaderRuntime {
|
||||
}
|
||||
|
||||
impl ShaderRuntime {
|
||||
pub async fn run_per_pixel_adjust<T: BufferStruct>(&self, shaders: &Shaders<'_>, textures: List<Raster<GPU>>, args: Option<&T>) -> List<Raster<GPU>> {
|
||||
let mut cache = self.per_pixel_adjust.pipeline_cache.lock().await;
|
||||
pub fn run_per_pixel_adjust<T: BufferStruct>(&self, shaders: &Shaders<'_>, textures: List<Raster<GPU>>, args: Option<&T>) -> List<Raster<GPU>> {
|
||||
let mut cache = self.per_pixel_adjust.pipeline_cache.lock().unwrap();
|
||||
let pipeline = cache
|
||||
.entry(shaders.fragment_shader_name.to_owned())
|
||||
.or_insert_with(|| PerPixelAdjustGraphicsPipeline::new(&self.context, shaders));
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::WgpuExecutor;
|
||||
use crate::WgpuExecutorHandle;
|
||||
use core_types::Color;
|
||||
use core_types::Ctx;
|
||||
use core_types::color::SRGBA8;
|
||||
use core_types::list::{Item, List};
|
||||
use core_types::ops::Convert;
|
||||
use core_types::ops::{Convert, ConvertAsync};
|
||||
use core_types::runtime::SourceFuture;
|
||||
use core_types::transform::Footprint;
|
||||
use raster_types::Image;
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
@@ -38,6 +39,52 @@ fn upload_to_texture(device: &wgpu::Device, queue: &wgpu::Queue, image: &Raster<
|
||||
)
|
||||
}
|
||||
|
||||
/// Passthrough conversion for GPU `List`s - no conversion needed
|
||||
impl Convert<List<Raster<GPU>>, WgpuExecutorHandle> for List<Raster<GPU>> {
|
||||
fn convert(self, _: Footprint, _converter: WgpuExecutorHandle) -> List<Raster<GPU>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a `List<Raster<CPU>>` to `List<Raster<GPU>>` by uploading each image to a texture
|
||||
impl Convert<List<Raster<GPU>>, WgpuExecutorHandle> for List<Raster<CPU>> {
|
||||
fn convert(self, _: Footprint, executor: WgpuExecutorHandle) -> List<Raster<GPU>> {
|
||||
let device = &executor.context().device;
|
||||
let queue = executor.context().queue.lock();
|
||||
let list = self
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
let (image, attributes) = row.into_parts();
|
||||
let texture = upload_to_texture(device, &queue, &image);
|
||||
|
||||
Item::from_parts(Raster::new_gpu(texture), attributes)
|
||||
})
|
||||
.collect();
|
||||
|
||||
queue.submit([]);
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts single CPU raster to GPU by uploading to texture
|
||||
impl Convert<Raster<GPU>, WgpuExecutorHandle> for Raster<CPU> {
|
||||
fn convert(self, _: Footprint, executor: WgpuExecutorHandle) -> Raster<GPU> {
|
||||
let device = &executor.context().device;
|
||||
let queue = executor.context().queue.lock();
|
||||
let texture = upload_to_texture(device, &queue, &self);
|
||||
|
||||
queue.submit([]);
|
||||
Raster::new_gpu(texture)
|
||||
}
|
||||
}
|
||||
|
||||
/// Passthrough conversion for CPU `List`s - no conversion needed
|
||||
impl Convert<List<Raster<CPU>>, WgpuExecutorHandle> for List<Raster<CPU>> {
|
||||
fn convert(self, _: Footprint, _converter: WgpuExecutorHandle) -> List<Raster<CPU>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Raster<GPU> texture to Raster<CPU> by downloading the underlying texture data.
|
||||
///
|
||||
/// Assumptions:
|
||||
@@ -142,57 +189,11 @@ impl RasterGpuToRasterCpuConverter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Passthrough conversion for GPU `List`s - no conversion needed
|
||||
impl<'i> Convert<List<Raster<GPU>>, &'i WgpuExecutor> for List<Raster<GPU>> {
|
||||
async fn convert(self, _: Footprint, _converter: &'i WgpuExecutor) -> List<Raster<GPU>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a `List<Raster<CPU>>` to `List<Raster<GPU>>` by uploading each image to a texture
|
||||
impl<'i> Convert<List<Raster<GPU>>, &'i WgpuExecutor> for List<Raster<CPU>> {
|
||||
async fn convert(self, _: Footprint, executor: &'i WgpuExecutor) -> List<Raster<GPU>> {
|
||||
let device = &executor.context().device;
|
||||
let queue = executor.context().queue.lock();
|
||||
let list = self
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
let (image, attributes) = row.into_parts();
|
||||
let texture = upload_to_texture(device, &queue, &image);
|
||||
|
||||
Item::from_parts(Raster::new_gpu(texture), attributes)
|
||||
})
|
||||
.collect();
|
||||
|
||||
queue.submit([]);
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts single CPU raster to GPU by uploading to texture
|
||||
impl<'i> Convert<Raster<GPU>, &'i WgpuExecutor> for Raster<CPU> {
|
||||
async fn convert(self, _: Footprint, executor: &'i WgpuExecutor) -> Raster<GPU> {
|
||||
let device = &executor.context().device;
|
||||
let queue = executor.context().queue.lock();
|
||||
let texture = upload_to_texture(device, &queue, &self);
|
||||
|
||||
queue.submit([]);
|
||||
Raster::new_gpu(texture)
|
||||
}
|
||||
}
|
||||
|
||||
/// Passthrough conversion for CPU `List`s - no conversion needed
|
||||
impl<'i> Convert<List<Raster<CPU>>, &'i WgpuExecutor> for List<Raster<CPU>> {
|
||||
async fn convert(self, _: Footprint, _converter: &'i WgpuExecutor) -> List<Raster<CPU>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a `List<Raster<GPU>>` to `List<Raster<CPU>>` by downloading texture data in one go then asynchronously maps all buffers and processes the results.
|
||||
impl<'i> Convert<List<Raster<CPU>>, &'i WgpuExecutor> for List<Raster<GPU>> {
|
||||
async fn convert(self, _: Footprint, executor: &'i WgpuExecutor) -> List<Raster<CPU>> {
|
||||
let device = &executor.context().device;
|
||||
let queue = &executor.context().queue;
|
||||
impl ConvertAsync<List<Raster<CPU>>, WgpuExecutorHandle> for List<Raster<GPU>> {
|
||||
fn convert(self, _: Footprint, executor: WgpuExecutorHandle) -> SourceFuture<List<Raster<CPU>>> {
|
||||
let device = executor.context().device.clone();
|
||||
let queue = executor.context().queue.lock();
|
||||
|
||||
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("batch_texture_download_encoder"),
|
||||
@@ -203,48 +204,50 @@ impl<'i> Convert<List<Raster<CPU>>, &'i WgpuExecutor> for List<Raster<GPU>> {
|
||||
|
||||
for row in self {
|
||||
let (element, attributes) = row.into_parts();
|
||||
converters.push(RasterGpuToRasterCpuConverter::new(device, &mut encoder, element));
|
||||
converters.push(RasterGpuToRasterCpuConverter::new(&device, &mut encoder, element));
|
||||
rows_meta.push(Item::from_parts((), attributes));
|
||||
}
|
||||
|
||||
queue.submit([encoder.finish()]);
|
||||
|
||||
let mut map_futures = Vec::new();
|
||||
for converter in converters {
|
||||
map_futures.push(converter.convert(device));
|
||||
}
|
||||
Box::pin(async move {
|
||||
let mut map_futures = Vec::new();
|
||||
for converter in converters {
|
||||
map_futures.push(converter.convert(&device));
|
||||
}
|
||||
|
||||
let map_results = futures::future::try_join_all(map_futures)
|
||||
.await
|
||||
.map_err(|_| "Failed to receive map result")
|
||||
.expect("Buffer mapping communication failed");
|
||||
let map_results = futures::future::try_join_all(map_futures)
|
||||
.await
|
||||
.map_err(|_| "Failed to receive map result")
|
||||
.expect("Buffer mapping communication failed");
|
||||
|
||||
map_results
|
||||
.into_iter()
|
||||
.zip(rows_meta)
|
||||
.map(|(element, row)| {
|
||||
let (_, attributes) = row.into_parts();
|
||||
Item::from_parts(element, attributes)
|
||||
})
|
||||
.collect()
|
||||
map_results
|
||||
.into_iter()
|
||||
.zip(rows_meta)
|
||||
.map(|(element, row)| {
|
||||
let (_, attributes) = row.into_parts();
|
||||
Item::from_parts(element, attributes)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts single GPU raster to CPU by downloading texture data
|
||||
impl<'i> Convert<Raster<CPU>, &'i WgpuExecutor> for Raster<GPU> {
|
||||
async fn convert(self, _: Footprint, executor: &'i WgpuExecutor) -> Raster<CPU> {
|
||||
let device = &executor.context().device;
|
||||
let queue = &executor.context().queue;
|
||||
impl ConvertAsync<Raster<CPU>, WgpuExecutorHandle> for Raster<GPU> {
|
||||
fn convert(self, _: Footprint, executor: WgpuExecutorHandle) -> SourceFuture<Raster<CPU>> {
|
||||
let device = executor.context().device.clone();
|
||||
let queue = executor.context().queue.lock();
|
||||
|
||||
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("single_texture_download_encoder"),
|
||||
});
|
||||
|
||||
let converter = RasterGpuToRasterCpuConverter::new(device, &mut encoder, self);
|
||||
let converter = RasterGpuToRasterCpuConverter::new(&device, &mut encoder, self);
|
||||
|
||||
queue.submit([encoder.finish()]);
|
||||
|
||||
converter.convert(device).await.expect("Failed to download texture data")
|
||||
Box::pin(async move { converter.convert(&device).await.expect("Failed to download texture data") })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,10 +255,10 @@ impl<'i> Convert<Raster<CPU>, &'i WgpuExecutor> for Raster<GPU> {
|
||||
///
|
||||
/// Accepts either individual raster data or a `List` of raster elements and converts it to the GPU format using the WgpuExecutor's device and queue.
|
||||
#[node_macro::node(category(""))]
|
||||
pub async fn upload_texture<'a: 'n, T: Convert<List<Raster<GPU>>, &'a WgpuExecutor>>(
|
||||
pub fn upload_texture<T: Convert<List<Raster<GPU>>, WgpuExecutorHandle>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(List<Raster<CPU>>, List<Raster<GPU>>)] input: T,
|
||||
executor: &'a WgpuExecutor,
|
||||
executor: WgpuExecutorHandle,
|
||||
) -> List<Raster<GPU>> {
|
||||
input.convert(Footprint::DEFAULT, executor).await
|
||||
input.convert(Footprint::DEFAULT, executor)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user