mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 23:38:06 +08:00
merge
This commit is contained in:
@@ -4,11 +4,6 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
profiling = []
|
||||
passthrough = []
|
||||
|
||||
[dependencies]
|
||||
# Local dependencies
|
||||
graphene-core = { workspace = true, features = ["wgpu"] }
|
||||
@@ -25,3 +20,4 @@ futures = { workspace = true }
|
||||
web-sys = { workspace = true }
|
||||
winit = { workspace = true }
|
||||
vello = { workspace = true }
|
||||
bytemuck = { workspace = true }
|
||||
|
||||
@@ -32,24 +32,17 @@ impl Context {
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
// #[cfg(not(feature = "passthrough"))]
|
||||
#[cfg(target_family = "wasm")]
|
||||
required_features: wgpu::Features::empty(),
|
||||
// Currently disabled because not all backend support passthrough.
|
||||
// TODO: reenable only when vulkan adapter is available
|
||||
// #[cfg(feature = "passthrough")]
|
||||
// required_features: wgpu::Features::SPIRV_SHADER_PASSTHROUGH,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
required_features: wgpu::Features::PUSH_CONSTANTS,
|
||||
required_limits,
|
||||
memory_hints: Default::default(),
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
.ok()?;
|
||||
|
||||
let info = adapter.get_info();
|
||||
// skip this on LavaPipe temporarily
|
||||
if info.vendor == 0x10005 {
|
||||
return None;
|
||||
}
|
||||
Some(Self {
|
||||
device: Arc::new(device),
|
||||
queue: Arc::new(queue),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
mod context;
|
||||
pub mod texture_upload;
|
||||
|
||||
use anyhow::Result;
|
||||
pub use context::Context;
|
||||
@@ -41,13 +42,14 @@ pub struct Surface {
|
||||
}
|
||||
|
||||
pub struct TargetTexture {
|
||||
texture: wgpu::Texture,
|
||||
view: wgpu::TextureView,
|
||||
size: UVec2,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(target_family = "wasm")]
|
||||
pub type Window = web_sys::HtmlCanvasElement;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub type Window = Arc<winit::window::Window>;
|
||||
|
||||
unsafe impl StaticType for Surface {
|
||||
@@ -59,7 +61,47 @@ const VELLO_SURFACE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unor
|
||||
impl WgpuExecutor {
|
||||
pub async fn render_vello_scene(&self, scene: &Scene, surface: &WgpuSurface, size: UVec2, context: &RenderContext, background: Color) -> Result<()> {
|
||||
let mut guard = surface.surface.target_texture.lock().await;
|
||||
let target_texture = if let Some(target_texture) = &*guard
|
||||
|
||||
let surface_inner = &surface.surface.inner;
|
||||
let surface_caps = surface_inner.get_capabilities(&self.context.adapter);
|
||||
surface_inner.configure(
|
||||
&self.context.device,
|
||||
&SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::STORAGE_BINDING,
|
||||
format: VELLO_SURFACE_FORMAT,
|
||||
width: size.x,
|
||||
height: size.y,
|
||||
present_mode: surface_caps.present_modes[0],
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Opaque,
|
||||
view_formats: vec![],
|
||||
desired_maximum_frame_latency: 2,
|
||||
},
|
||||
);
|
||||
|
||||
self.render_vello_scene_to_target_texture(scene, size, context, background, &mut guard).await?;
|
||||
|
||||
let surface_texture = surface_inner.get_current_texture()?;
|
||||
let mut encoder = self.context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Surface Blit") });
|
||||
surface.surface.blitter.copy(
|
||||
&self.context.device,
|
||||
&mut encoder,
|
||||
&guard.as_ref().unwrap().view,
|
||||
&surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default()),
|
||||
);
|
||||
self.context.queue.submit([encoder.finish()]);
|
||||
surface_texture.present();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn render_vello_scene_to_texture(&self, scene: &Scene, size: UVec2, context: &RenderContext, background: Color) -> Result<wgpu::Texture> {
|
||||
let mut output = None;
|
||||
self.render_vello_scene_to_target_texture(scene, size, context, background, &mut output).await?;
|
||||
Ok(output.unwrap().texture)
|
||||
}
|
||||
|
||||
async fn render_vello_scene_to_target_texture(&self, scene: &Scene, size: UVec2, context: &RenderContext, background: Color, output: &mut Option<TargetTexture>) -> Result<()> {
|
||||
let target_texture = if let Some(target_texture) = output
|
||||
&& target_texture.size == size
|
||||
{
|
||||
target_texture
|
||||
@@ -79,31 +121,13 @@ impl WgpuExecutor {
|
||||
view_formats: &[],
|
||||
});
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
*guard = Some(TargetTexture { size, view });
|
||||
guard.as_ref().unwrap()
|
||||
*output = Some(TargetTexture { texture, view, size });
|
||||
output.as_mut().unwrap()
|
||||
};
|
||||
|
||||
let surface_inner = &surface.surface.inner;
|
||||
let surface_caps = surface_inner.get_capabilities(&self.context.adapter);
|
||||
surface_inner.configure(
|
||||
&self.context.device,
|
||||
&SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::STORAGE_BINDING,
|
||||
format: VELLO_SURFACE_FORMAT,
|
||||
width: size.x,
|
||||
height: size.y,
|
||||
present_mode: surface_caps.present_modes[0],
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Opaque,
|
||||
view_formats: vec![],
|
||||
desired_maximum_frame_latency: 2,
|
||||
},
|
||||
);
|
||||
|
||||
let [r, g, b, _] = background.to_rgba8_srgb();
|
||||
let [r, g, b, a] = background.to_rgba8_srgb();
|
||||
let render_params = RenderParams {
|
||||
// We are using an explicit opaque color here to eliminate the alpha premultiplication step
|
||||
// which would be required to support a transparent webgpu canvas
|
||||
base_color: vello::peniko::Color::from_rgba8(r, g, b, 0xff),
|
||||
base_color: vello::peniko::Color::from_rgba8(r, g, b, a),
|
||||
width: size.x,
|
||||
height: size.y,
|
||||
antialiasing_method: AaConfig::Msaa16,
|
||||
@@ -111,42 +135,29 @@ impl WgpuExecutor {
|
||||
|
||||
{
|
||||
let mut renderer = self.vello_renderer.lock().await;
|
||||
for (id, texture) in context.resource_overrides.iter() {
|
||||
let texture = texture.clone();
|
||||
for (image, texture) in context.resource_overrides.iter() {
|
||||
let texture_view = wgpu::TexelCopyTextureInfoBase {
|
||||
texture,
|
||||
texture: texture.clone(),
|
||||
mip_level: 0,
|
||||
origin: Origin3d::ZERO,
|
||||
aspect: TextureAspect::All,
|
||||
};
|
||||
renderer.override_image(
|
||||
&vello::peniko::Image::new(vello::peniko::Blob::from_raw_parts(Arc::new(vec![]), *id), vello::peniko::ImageFormat::Rgba8, 0, 0),
|
||||
Some(texture_view),
|
||||
);
|
||||
renderer.override_image(image, Some(texture_view));
|
||||
}
|
||||
renderer.render_to_texture(&self.context.device, &self.context.queue, scene, &target_texture.view, &render_params)?;
|
||||
for (image, _) in context.resource_overrides.iter() {
|
||||
renderer.override_image(image, None);
|
||||
}
|
||||
}
|
||||
|
||||
let surface_texture = surface_inner.get_current_texture()?;
|
||||
let mut encoder = self.context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Surface Blit") });
|
||||
surface.surface.blitter.copy(
|
||||
&self.context.device,
|
||||
&mut encoder,
|
||||
&target_texture.view,
|
||||
&surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default()),
|
||||
);
|
||||
self.context.queue.submit([encoder.finish()]);
|
||||
surface_texture.present();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(target_family = "wasm")]
|
||||
pub fn create_surface(&self, canvas: graphene_application_io::WasmSurfaceHandle) -> Result<SurfaceHandle<Surface>> {
|
||||
let surface = self.context.instance.create_surface(wgpu::SurfaceTarget::Canvas(canvas.surface))?;
|
||||
self.create_surface_inner(surface, canvas.window_id)
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub fn create_surface(&self, window: SurfaceHandle<Window>) -> Result<SurfaceHandle<Surface>> {
|
||||
let surface = self.context.instance.create_surface(wgpu::SurfaceTarget::Window(Box::new(window.surface)))?;
|
||||
self.create_surface_inner(surface, window.window_id)
|
||||
@@ -167,12 +178,13 @@ impl WgpuExecutor {
|
||||
|
||||
impl WgpuExecutor {
|
||||
pub async fn new() -> Option<Self> {
|
||||
let context = Context::new().await?;
|
||||
Self::with_context(Context::new().await?)
|
||||
}
|
||||
|
||||
pub fn with_context(context: Context) -> Option<Self> {
|
||||
let vello_renderer = Renderer::new(
|
||||
&context.device,
|
||||
RendererOptions {
|
||||
// surface_format: Some(wgpu::TextureFormat::Rgba8Unorm),
|
||||
pipeline_cache: None,
|
||||
use_cpu: false,
|
||||
antialiasing_support: AaSupport::all(),
|
||||
|
||||
53
node-graph/wgpu-executor/src/texture_upload.rs
Normal file
53
node-graph/wgpu-executor/src/texture_upload.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use crate::WgpuExecutor;
|
||||
use graphene_core::color::SRGBA8;
|
||||
use graphene_core::raster_types::{CPU, GPU, Raster};
|
||||
use graphene_core::table::{Table, TableRow};
|
||||
use graphene_core::{Ctx, ExtractFootprint};
|
||||
use wgpu::util::{DeviceExt, TextureDataOrder};
|
||||
use wgpu::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages};
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
pub async fn upload_texture<'a: 'n>(_: impl ExtractFootprint + Ctx, input: Table<Raster<CPU>>, executor: &'a WgpuExecutor) -> Table<Raster<GPU>> {
|
||||
let device = &executor.context.device;
|
||||
let queue = &executor.context.queue;
|
||||
let table = input
|
||||
.iter()
|
||||
.map(|row| {
|
||||
let image = row.element;
|
||||
let rgba8_data: Vec<SRGBA8> = image.data.iter().map(|x| (*x).into()).collect();
|
||||
|
||||
let texture = device.create_texture_with_data(
|
||||
queue,
|
||||
&TextureDescriptor {
|
||||
label: Some("upload_texture node texture"),
|
||||
size: Extent3d {
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8UnormSrgb,
|
||||
// I don't know what usages are actually necessary
|
||||
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::COPY_SRC,
|
||||
view_formats: &[],
|
||||
},
|
||||
TextureDataOrder::LayerMajor,
|
||||
bytemuck::cast_slice(rgba8_data.as_slice()),
|
||||
);
|
||||
|
||||
TableRow {
|
||||
element: Raster::new_gpu(texture),
|
||||
mask: row.mask.clone(),
|
||||
transform: *row.transform,
|
||||
alpha_blending: *row.alpha_blending,
|
||||
source_node_id: *row.source_node_id,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
queue.submit([]);
|
||||
|
||||
table
|
||||
}
|
||||
Reference in New Issue
Block a user