From 20f7b68514758dc1743597c1600f9b99770a415c Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 16 Nov 2022 10:49:38 -0500 Subject: [PATCH] finish gradient support --- piet-wgsl/src/engine.rs | 217 +++++++++++++++++++++++++++++++----- piet-wgsl/src/main.rs | 2 +- piet-wgsl/src/ramp.rs | 137 +++++++++++++++++++++++ piet-wgsl/src/render.rs | 96 ++++++++++++---- piet-wgsl/src/shaders.rs | 1 + piet-wgsl/src/test_scene.rs | 82 ++++++++++---- 6 files changed, 459 insertions(+), 76 deletions(-) create mode 100644 piet-wgsl/src/ramp.rs diff --git a/piet-wgsl/src/engine.rs b/piet-wgsl/src/engine.rs index 7d1c854..5e89905 100644 --- a/piet-wgsl/src/engine.rs +++ b/piet-wgsl/src/engine.rs @@ -17,7 +17,7 @@ use std::{ borrow::Cow, collections::{hash_map::Entry, HashMap}, - num::NonZeroU64, + num::{NonZeroU32, NonZeroU64}, sync::atomic::{AtomicU64, Ordering}, }; @@ -25,7 +25,8 @@ use futures_intrusive::channel::shared::GenericOneshotReceiver; use parking_lot::RawMutex; use wgpu::{ util::DeviceExt, BindGroup, BindGroupLayout, Buffer, BufferAsyncError, BufferSlice, BufferView, - ComputePipeline, Device, Queue, + ComputePipeline, Device, Queue, Texture, TextureAspect, TextureFormat, TextureUsages, + TextureView, TextureViewDimension, }; pub type Error = Box; @@ -58,12 +59,27 @@ pub struct BufProxy { id: Id, } +#[derive(Clone, Copy)] +pub struct ImageProxy { + width: u32, + height: u32, + // TODO: format + id: Id, +} + +#[derive(Clone, Copy)] +pub enum ResourceProxy { + Buf(BufProxy), + Image(ImageProxy), +} + pub enum Command { Upload(BufProxy, Vec), + UploadImage(ImageProxy, Vec), // Discussion question: third argument is vec of resources? // Maybe use tricks to make more ergonomic? // Alternative: provide bufs & images as separate sequences, like piet-gpu. - Dispatch(ShaderId, (u32, u32, u32), Vec), + Dispatch(ShaderId, (u32, u32, u32), Vec), Download(BufProxy), Clear(BufProxy, u64, Option), } @@ -92,6 +108,7 @@ pub enum BindType { #[derive(Default)] struct BindMap { buf_map: HashMap, + image_map: HashMap, } impl Engine { @@ -132,6 +149,16 @@ impl Engine { }, count: None, }, + BindType::ImageRead => wgpu::BindGroupLayoutEntry { + binding: i as u32, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, _ => todo!(), }) .collect::>(); @@ -182,6 +209,58 @@ impl Engine { }); bind_map.insert_buf(buf_proxy.id, buf); } + Command::UploadImage(image_proxy, bytes) => { + let buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: &bytes, + usage: wgpu::BufferUsages::COPY_SRC, + }); + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: image_proxy.width, + height: image_proxy.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, + format: TextureFormat::Rgba8Unorm, + }); + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor { + label: None, + dimension: Some(TextureViewDimension::D2), + aspect: TextureAspect::All, + mip_level_count: None, + base_mip_level: 0, + base_array_layer: 0, + array_layer_count: None, + format: Some(TextureFormat::Rgba8Unorm), + }); + encoder.copy_buffer_to_texture( + wgpu::ImageCopyBuffer { + buffer: &buf, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: NonZeroU32::new(image_proxy.width * 4), + rows_per_image: None, + }, + }, + wgpu::ImageCopyTexture { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, + aspect: TextureAspect::All, + }, + wgpu::Extent3d { + width: image_proxy.width, + height: image_proxy.height, + depth_or_array_layers: 1, + }, + ); + bind_map.insert_image(image_proxy.id, texture, texture_view) + } Command::Dispatch(shader_id, wg_size, bindings) => { println!("dispatching {:?} with {} bindings", wg_size, bindings.len()); let shader = &self.shaders[shader_id.0]; @@ -226,13 +305,23 @@ impl Recording { buf_proxy } - pub fn dispatch( - &mut self, - shader: ShaderId, - wg_size: (u32, u32, u32), - resources: impl Into>, - ) { - self.push(Command::Dispatch(shader, wg_size, resources.into())); + pub fn upload_image(&mut self, width: u32, height: u32, data: impl Into>) -> ImageProxy { + let data = data.into(); + let image_proxy = ImageProxy::new(width, height); + self.push(Command::UploadImage(image_proxy, data)); + image_proxy + } + + pub fn dispatch(&mut self, shader: ShaderId, wg_size: (u32, u32, u32), resources: R) + where + R: IntoIterator, + R::Item: Into, + { + self.push(Command::Dispatch( + shader, + wg_size, + resources.into_iter().map(|r| r.into()).collect(), + )); } pub fn download(&mut self, buf: BufProxy) { @@ -251,6 +340,35 @@ impl BufProxy { } } +impl ImageProxy { + pub fn new(width: u32, height: u32) -> Self { + let id = Id::next(); + ImageProxy { width, height, id } + } +} + +impl ResourceProxy { + pub fn new_buf(size: u64) -> Self { + Self::Buf(BufProxy::new(size)) + } + + pub fn new_image(width: u32, height: u32) -> Self { + Self::Image(ImageProxy::new(width, height)) + } +} + +impl From for ResourceProxy { + fn from(value: BufProxy) -> Self { + Self::Buf(value) + } +} + +impl From for ResourceProxy { + fn from(value: ImageProxy) -> Self { + Self::Image(value) + } +} + impl Id { pub fn next() -> Id { let val = ID_COUNTER.fetch_add(1, Ordering::Relaxed); @@ -264,34 +382,79 @@ impl BindMap { self.buf_map.insert(id, buf); } + fn insert_image(&mut self, id: Id, image: Texture, image_view: TextureView) { + self.image_map.insert(id, (image, image_view)); + } + fn create_bind_group( &mut self, device: &Device, layout: &BindGroupLayout, - bindings: &[BufProxy], + bindings: &[ResourceProxy], ) -> Result { for proxy in bindings { - if let Entry::Vacant(v) = self.buf_map.entry(proxy.id) { - let buf = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: proxy.size, - usage: wgpu::BufferUsages::STORAGE - | wgpu::BufferUsages::COPY_DST - | wgpu::BufferUsages::COPY_SRC, - mapped_at_creation: false, - }); - v.insert(buf); + match proxy { + ResourceProxy::Buf(proxy) => { + if let Entry::Vacant(v) = self.buf_map.entry(proxy.id) { + let buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: proxy.size, + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_DST + | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + v.insert(buf); + } + } + ResourceProxy::Image(proxy) => { + if let Entry::Vacant(v) = self.image_map.entry(proxy.id) { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: proxy.width, + height: proxy.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, + format: TextureFormat::Rgba8Unorm, + }); + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor { + label: None, + dimension: Some(TextureViewDimension::D2), + aspect: TextureAspect::All, + mip_level_count: None, + base_mip_level: 0, + base_array_layer: 0, + array_layer_count: None, + format: Some(TextureFormat::Rgba8Unorm), + }); + v.insert((texture, texture_view)); + } + } } } let entries = bindings .iter() .enumerate() - .map(|(i, proxy)| { - let buf = self.buf_map.get(&proxy.id).unwrap(); - Ok(wgpu::BindGroupEntry { - binding: i as u32, - resource: buf.as_entire_binding(), - }) + .map(|(i, proxy)| match proxy { + ResourceProxy::Buf(proxy) => { + let buf = self.buf_map.get(&proxy.id).unwrap(); + Ok(wgpu::BindGroupEntry { + binding: i as u32, + resource: buf.as_entire_binding(), + }) + } + ResourceProxy::Image(proxy) => { + let texture = self.image_map.get(&proxy.id).unwrap(); + Ok(wgpu::BindGroupEntry { + binding: i as u32, + resource: wgpu::BindingResource::TextureView(&texture.1), + }) + } }) .collect::, Error>>()?; let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { diff --git a/piet-wgsl/src/main.rs b/piet-wgsl/src/main.rs index 38645db..2f1d885 100644 --- a/piet-wgsl/src/main.rs +++ b/piet-wgsl/src/main.rs @@ -24,6 +24,7 @@ use wgpu::{Device, Limits, Queue}; mod engine; mod pico_svg; +mod ramp; mod render; mod shaders; mod test_scene; @@ -58,7 +59,6 @@ fn dump_buf(buf: &[u32]) { println!("{}: {:x} {}", i, val, f32::from_bits(*val)); } else { println!("{}: {:x}", i, val); - } } } diff --git a/piet-wgsl/src/ramp.rs b/piet-wgsl/src/ramp.rs new file mode 100644 index 0000000..a26c3d9 --- /dev/null +++ b/piet-wgsl/src/ramp.rs @@ -0,0 +1,137 @@ +use piet_scene::{Color, GradientStop, GradientStops}; + +use std::collections::HashMap; + +const N_SAMPLES: usize = 512; +const RETAINED_COUNT: usize = 64; + +#[derive(Default)] +pub struct RampCache { + epoch: u64, + map: HashMap, + data: Vec, +} + +impl RampCache { + pub fn advance(&mut self) { + self.epoch += 1; + if self.map.len() > RETAINED_COUNT { + self.map + .retain(|_key, value| value.0 < RETAINED_COUNT as u32); + self.data.truncate(RETAINED_COUNT * N_SAMPLES); + } + } + + pub fn add(&mut self, stops: &[GradientStop]) -> u32 { + if let Some(entry) = self.map.get_mut(stops) { + entry.1 = self.epoch; + entry.0 + } else if self.map.len() < RETAINED_COUNT { + let id = (self.data.len() / N_SAMPLES) as u32; + self.data.extend(make_ramp(stops)); + self.map.insert(stops.into(), (id, self.epoch)); + id + } else { + let mut reuse = None; + for (stops, (id, epoch)) in &self.map { + if *epoch + 2 < self.epoch { + reuse = Some((stops.to_owned(), *id)); + break; + } + } + if let Some((old_stops, id)) = reuse { + self.map.remove(&old_stops); + let start = id as usize * N_SAMPLES; + for (dst, src) in self.data[start..start + N_SAMPLES] + .iter_mut() + .zip(make_ramp(stops)) + { + *dst = src; + } + self.map.insert(stops.into(), (id, self.epoch)); + id + } else { + let id = (self.data.len() / N_SAMPLES) as u32; + self.data.extend(make_ramp(stops)); + self.map.insert(stops.into(), (id, self.epoch)); + id + } + } + } + + pub fn data(&self) -> &[u32] { + &self.data + } + + pub fn width(&self) -> u32 { + N_SAMPLES as u32 + } + + pub fn height(&self) -> u32 { + (self.data.len() / N_SAMPLES) as u32 + } +} + +fn make_ramp<'a>(stops: &'a [GradientStop]) -> impl Iterator + 'a { + let mut last_u = 0.0; + let mut last_c = ColorF64::from_color(stops[0].color); + let mut this_u = last_u; + let mut this_c = last_c; + let mut j = 0; + (0..N_SAMPLES).map(move |i| { + let u = (i as f64) / (N_SAMPLES - 1) as f64; + while u > this_u { + last_u = this_u; + last_c = this_c; + if let Some(s) = stops.get(j + 1) { + this_u = s.offset as f64; + this_c = ColorF64::from_color(s.color); + j += 1; + } else { + break; + } + } + let du = this_u - last_u; + let c = if du < 1e-9 { + this_c + } else { + last_c.lerp(&this_c, (u - last_u) / du) + }; + c.to_premul_u32() + }) +} + +#[derive(Copy, Clone, Debug)] +struct ColorF64([f64; 4]); + +impl ColorF64 { + fn from_color(color: Color) -> Self { + Self([ + color.r as f64 / 255.0, + color.g as f64 / 255.0, + color.b as f64 / 255.0, + color.a as f64 / 255.0, + ]) + } + + fn lerp(&self, other: &Self, a: f64) -> Self { + fn l(x: f64, y: f64, a: f64) -> f64 { + x * (1.0 - a) + y * a + } + Self([ + l(self.0[0], other.0[0], a), + l(self.0[1], other.0[1], a), + l(self.0[2], other.0[2], a), + l(self.0[3], other.0[3], a), + ]) + } + + fn to_premul_u32(&self) -> u32 { + let a = self.0[3].min(1.0).max(0.0); + let r = ((self.0[0] * a).min(1.0).max(0.0) * 255.0) as u32; + let g = ((self.0[1] * a).min(1.0).max(0.0) * 255.0) as u32; + let b = ((self.0[2] * a).min(1.0).max(0.0) * 255.0) as u32; + let a = (a * 255.0) as u32; + r | (g << 8) | (b << 16) | (a << 24) + } +} diff --git a/piet-wgsl/src/render.rs b/piet-wgsl/src/render.rs index 7d052cc..4cc63e3 100644 --- a/piet-wgsl/src/render.rs +++ b/piet-wgsl/src/render.rs @@ -4,7 +4,7 @@ use bytemuck::{Pod, Zeroable}; use piet_scene::Scene; use crate::{ - engine::{BufProxy, Recording}, + engine::{BufProxy, Recording, ResourceProxy}, shaders::{self, FullShaders, Shaders}, }; @@ -87,7 +87,8 @@ pub fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) { [config_buf, scene_buf, reduced_buf, tagmonoid_buf], ); - let path_coarse_wgs = (n_pathtag as u32 + shaders::PATH_COARSE_WG - 1) / shaders::PATH_COARSE_WG; + let path_coarse_wgs = + (n_pathtag as u32 + shaders::PATH_COARSE_WG - 1) / shaders::PATH_COARSE_WG; // TODO: more principled size calc let tiles_buf = BufProxy::new(4097 * 8); let segments_buf = BufProxy::new(256 * 24); @@ -122,7 +123,29 @@ pub fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) { pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy) { let mut recording = Recording::default(); + let mut ramps = crate::ramp::RampCache::default(); + let mut drawdata_patches: Vec<(usize, u32)> = vec![]; let data = scene.data(); + let stop_data = &data.resources.stops; + for patch in &data.resources.patches { + use piet_scene::ResourcePatch; + match patch { + ResourcePatch::Ramp { offset, stops } => { + let ramp_id = ramps.add(&stop_data[stops.clone()]); + drawdata_patches.push((*offset, ramp_id)); + } + } + } + let gradient_image = if drawdata_patches.is_empty() { + ResourceProxy::new_image(1, 1) + } else { + let data = ramps.data(); + let width = ramps.width(); + let height = ramps.height(); + let data: &[u8] = bytemuck::cast_slice(data); + println!("gradient image: {}x{} ({} bytes)", width, height, data.len()); + ResourceProxy::Image(recording.upload_image(width, height, data)) + }; let n_pathtag = data.tag_stream.len(); let pathtag_padded = align_up(n_pathtag, 4 * shaders::PATHTAG_REDUCE_WG); // TODO: can compute size accurately, avoid reallocation @@ -135,12 +158,27 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy let drawtag_base = size_to_words(scene.len()); scene.extend(bytemuck::cast_slice(&data.drawtag_stream)); let drawdata_base = size_to_words(scene.len()); - scene.extend(&data.drawdata_stream); + if !drawdata_patches.is_empty() { + let mut pos = 0; + for patch in drawdata_patches { + let offset = patch.0; + let value = patch.1; + if pos < offset { + scene.extend_from_slice(&data.drawdata_stream[pos..offset]); + } + scene.extend_from_slice(bytemuck::bytes_of(&value)); + pos = offset + 4; + } + if pos < data.drawdata_stream.len() { + scene.extend_from_slice(&data.drawdata_stream[pos..]) + } + } else { + scene.extend(&data.drawdata_stream); + } let transform_base = size_to_words(scene.len()); scene.extend(bytemuck::cast_slice(&data.transform_stream)); let linewidth_base = size_to_words(scene.len()); scene.extend(bytemuck::cast_slice(&data.linewidth_stream)); - let n_path = data.n_path; // TODO: calculate for real when we do rectangles let n_drawobj = n_path; @@ -159,11 +197,11 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy linewidth_base, }; println!("{:?}", config); - let scene_buf = recording.upload(scene); - let config_buf = recording.upload(bytemuck::bytes_of(&config).to_owned()); + let scene_buf = ResourceProxy::Buf(recording.upload(scene)); + let config_buf = ResourceProxy::Buf(recording.upload(bytemuck::bytes_of(&config).to_owned())); let pathtag_wgs = pathtag_padded / (4 * shaders::PATHTAG_REDUCE_WG as usize); - let reduced_buf = BufProxy::new(pathtag_wgs as u64 * TAG_MONOID_FULL_SIZE); + let reduced_buf = ResourceProxy::new_buf(pathtag_wgs as u64 * TAG_MONOID_FULL_SIZE); // TODO: really only need pathtag_wgs - 1 recording.dispatch( shaders.pathtag_reduce, @@ -171,22 +209,24 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy [config_buf, scene_buf, reduced_buf], ); - let tagmonoid_buf = - BufProxy::new(pathtag_wgs as u64 * shaders::PATHTAG_REDUCE_WG as u64 * TAG_MONOID_FULL_SIZE); + let tagmonoid_buf = ResourceProxy::new_buf( + pathtag_wgs as u64 * shaders::PATHTAG_REDUCE_WG as u64 * TAG_MONOID_FULL_SIZE, + ); recording.dispatch( shaders.pathtag_scan, (pathtag_wgs as u32, 1, 1), [config_buf, scene_buf, reduced_buf, tagmonoid_buf], ); let drawobj_wgs = (n_drawobj + shaders::PATH_BBOX_WG - 1) / shaders::PATH_BBOX_WG; - let path_bbox_buf = BufProxy::new(n_path as u64 * PATH_BBOX_SIZE); + let path_bbox_buf = ResourceProxy::new_buf(n_path as u64 * PATH_BBOX_SIZE); recording.dispatch( shaders.bbox_clear, (drawobj_wgs, 1, 1), [config_buf, path_bbox_buf], ); - let cubic_buf = BufProxy::new(n_pathtag as u64 * CUBIC_SIZE); - let path_coarse_wgs = (n_pathtag as u32 + shaders::PATH_COARSE_WG - 1) / shaders::PATH_COARSE_WG; + let cubic_buf = ResourceProxy::new_buf(n_pathtag as u64 * CUBIC_SIZE); + let path_coarse_wgs = + (n_pathtag as u32 + shaders::PATH_COARSE_WG - 1) / shaders::PATH_COARSE_WG; recording.dispatch( shaders.pathseg, (path_coarse_wgs, 1, 1), @@ -198,14 +238,14 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy cubic_buf, ], ); - let draw_reduced_buf = BufProxy::new(drawobj_wgs as u64 * DRAWMONOID_SIZE); + let draw_reduced_buf = ResourceProxy::new_buf(drawobj_wgs as u64 * DRAWMONOID_SIZE); recording.dispatch( shaders.draw_reduce, (drawobj_wgs, 1, 1), [config_buf, scene_buf, draw_reduced_buf], ); - let draw_monoid_buf = BufProxy::new(n_drawobj as u64 * DRAWMONOID_SIZE); - let info_buf = BufProxy::new(n_drawobj as u64 * MAX_DRAWINFO_SIZE); + let draw_monoid_buf = ResourceProxy::new_buf(n_drawobj as u64 * DRAWMONOID_SIZE); + let info_buf = ResourceProxy::new_buf(n_drawobj as u64 * MAX_DRAWINFO_SIZE); recording.dispatch( shaders.draw_leaf, (drawobj_wgs, 1, 1), @@ -218,16 +258,17 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy info_buf, ], ); - let draw_bbox_buf = BufProxy::new(n_path as u64 * DRAW_BBOX_SIZE); + let draw_bbox_buf = ResourceProxy::new_buf(n_path as u64 * DRAW_BBOX_SIZE); let bump_buf = BufProxy::new(BUMP_SIZE); // Not actually used yet. - let clip_bbox_buf = BufProxy::new(1024); - let bin_data_buf = BufProxy::new(1 << 20); + let clip_bbox_buf = ResourceProxy::new_buf(1024); + let bin_data_buf = ResourceProxy::new_buf(1 << 20); let width_in_bins = (config.width_in_tiles + 15) / 16; let height_in_bins = (config.height_in_tiles + 15) / 16; let n_bins = width_in_bins * height_in_bins; - let bin_header_buf = BufProxy::new((n_bins * drawobj_wgs) as u64 * BIN_HEADER_SIZE); + let bin_header_buf = ResourceProxy::new_buf((n_bins * drawobj_wgs) as u64 * BIN_HEADER_SIZE); recording.clear_all(bump_buf); + let bump_buf = ResourceProxy::Buf(bump_buf); recording.dispatch( shaders.binning, (drawobj_wgs, 1, 1), @@ -242,8 +283,8 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy bin_header_buf, ], ); - let path_buf = BufProxy::new(n_path as u64 * PATH_SIZE); - let tile_buf = BufProxy::new(1 << 20); + let path_buf = ResourceProxy::new_buf(n_path as u64 * PATH_SIZE); + let tile_buf = ResourceProxy::new_buf(1 << 20); let path_wgs = (n_path + shaders::PATH_BBOX_WG - 1) / shaders::PATH_BBOX_WG; recording.dispatch( shaders.tile_alloc, @@ -258,7 +299,7 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy ], ); - let segments_buf = BufProxy::new(1 << 24); + let segments_buf = ResourceProxy::new_buf(1 << 24); recording.dispatch( shaders.path_coarse, (path_coarse_wgs, 1, 1), @@ -278,7 +319,7 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy (path_wgs, 1, 1), [config_buf, path_buf, tile_buf], ); - let ptcl_buf = BufProxy::new(1 << 24); + let ptcl_buf = ResourceProxy::new_buf(1 << 24); recording.dispatch( shaders.coarse, (width_in_bins, height_in_bins, 1), @@ -300,7 +341,14 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy recording.dispatch( shaders.fine, (config.width_in_tiles, config.height_in_tiles, 1), - [config_buf, tile_buf, segments_buf, out_buf, ptcl_buf], + [ + config_buf, + tile_buf, + segments_buf, + ResourceProxy::Buf(out_buf), + ptcl_buf, + gradient_image, + ], ); let download_buf = out_buf; diff --git a/piet-wgsl/src/shaders.rs b/piet-wgsl/src/shaders.rs index b659170..8e6e89f 100644 --- a/piet-wgsl/src/shaders.rs +++ b/piet-wgsl/src/shaders.rs @@ -255,6 +255,7 @@ pub fn full_shaders(device: &Device, engine: &mut Engine) -> Result Scene { let mut scene = Scene::default(); let mut builder = SceneBuilder::for_scene(&mut scene); - if false { - let path = [ - PathElement::MoveTo(Point::new(100.0, 100.0)), - PathElement::LineTo(Point::new(500.0, 120.0)), - PathElement::LineTo(Point::new(300.0, 150.0)), - PathElement::LineTo(Point::new(200.0, 260.0)), - PathElement::LineTo(Point::new(150.0, 210.0)), - PathElement::Close, - ]; - let brush = Brush::Solid(Color::rgb8(0x40, 0x40, 0xff)); - builder.fill(Fill::NonZero, Affine::IDENTITY, &brush, None, &path); - let transform = Affine::translate(50.0, 50.0); - let brush = Brush::Solid(Color::rgba8(0xff, 0xff, 0x00, 0x80)); - builder.fill(Fill::NonZero, transform, &brush, None, &path); - let transform = Affine::translate(100.0, 100.0); - let style = simple_stroke(1.0); - let brush = Brush::Solid(Color::rgb8(0xa0, 0x00, 0x00)); - builder.stroke(&style, transform, &brush, None, &path); - } else { - let xml_str = std::str::from_utf8(include_bytes!("../../piet-gpu/Ghostscript_Tiger.svg")).unwrap(); - let svg = PicoSvg::load(xml_str, 6.0).unwrap(); - render_svg(&mut builder, &svg, false); + let scene_ix = 1; + match scene_ix { + 0 => { + let path = [ + PathElement::MoveTo(Point::new(100.0, 100.0)), + PathElement::LineTo(Point::new(500.0, 120.0)), + PathElement::LineTo(Point::new(300.0, 150.0)), + PathElement::LineTo(Point::new(200.0, 260.0)), + PathElement::LineTo(Point::new(150.0, 210.0)), + PathElement::Close, + ]; + let brush = Brush::Solid(Color::rgb8(0x40, 0x40, 0xff)); + builder.fill(Fill::NonZero, Affine::IDENTITY, &brush, None, &path); + let transform = Affine::translate(50.0, 50.0); + let brush = Brush::Solid(Color::rgba8(0xff, 0xff, 0x00, 0x80)); + builder.fill(Fill::NonZero, transform, &brush, None, &path); + let transform = Affine::translate(100.0, 100.0); + let style = simple_stroke(1.0); + let brush = Brush::Solid(Color::rgb8(0xa0, 0x00, 0x00)); + builder.stroke(&style, transform, &brush, None, &path); + } + 1 => { + let path = [ + PathElement::MoveTo(Point::new(100.0, 100.0)), + PathElement::LineTo(Point::new(300.0, 100.0)), + PathElement::LineTo(Point::new(300.0, 300.0)), + PathElement::LineTo(Point::new(100.0, 300.0)), + PathElement::Close, + ]; + let gradient = Brush::LinearGradient(LinearGradient { + start: Point::new(100.0, 100.0), + end: Point::new(300.0, 300.0), + extend: piet_scene::ExtendMode::Pad, + stops: vec![ + GradientStop { + offset: 0.0, + color: Color::rgb8(255, 0, 0), + }, + GradientStop { + offset: 0.5, + color: Color::rgb8(0, 255, 0), + }, + GradientStop { + offset: 1.0, + color: Color::rgb8(0, 0, 255), + }, + ].into() + }); + builder.fill(Fill::NonZero, Affine::scale(3.0, 3.0), &gradient, None, &path); + } + _ => { + let xml_str = + std::str::from_utf8(include_bytes!("../../piet-gpu/Ghostscript_Tiger.svg")) + .unwrap(); + let svg = PicoSvg::load(xml_str, 6.0).unwrap(); + render_svg(&mut builder, &svg, false); + } } scene } @@ -94,7 +129,6 @@ fn convert_bez_path<'a>(path: &'a BezPath) -> impl Iterator .map(|el| PathElement::from_kurbo(*el)) } - fn simple_stroke(width: f32) -> Stroke<[f32; 0]> { Stroke { width,