From 2e8781fbb661f7ef5065fdadae61090bdf2094d5 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Thu, 11 Aug 2022 15:29:15 -0400 Subject: [PATCH 1/8] Remove piet API & replace w/ fragments Removes the dependency on the piet crate and replaces all uses with the scene crate. Also does some cleanup of the scene API, renaming some types and moving them all to the crate root for better ergonomics. --- Cargo.lock | 93 +---- pgpu-render/src/lib.rs | 22 +- pgpu-render/src/render.rs | 38 +- piet-gpu/Cargo.toml | 7 +- piet-gpu/bin/android.rs | 40 +- piet-gpu/bin/cli.rs | 16 +- piet-gpu/bin/winit.rs | 107 +++-- piet-gpu/src/blend.rs | 103 ----- piet-gpu/src/encoder.rs | 301 -------------- piet-gpu/src/glyph_render.rs | 87 ----- piet-gpu/src/gradient.rs | 249 ------------ piet-gpu/src/lib.rs | 122 +++--- piet-gpu/src/pico_svg.rs | 48 +-- piet-gpu/src/render_ctx.rs | 452 ---------------------- piet-gpu/src/render_driver.rs | 23 +- piet-gpu/src/samples.rs | 368 ++++++++++++++++++ piet-gpu/src/simple_text.rs | 82 ++++ piet-gpu/src/stages/transform.rs | 21 - piet-gpu/src/test_scenes.rs | 332 ---------------- piet-gpu/src/text.rs | 271 ------------- piet-scene/src/brush/color.rs | 1 + piet-scene/src/brush/gradient.rs | 38 +- piet-scene/src/brush/image.rs | 46 +-- piet-scene/src/brush/mod.rs | 31 +- piet-scene/src/geometry.rs | 8 + piet-scene/src/{glyph/mod.rs => glyph.rs} | 68 ++-- piet-scene/src/lib.rs | 44 ++- piet-scene/src/path.rs | 30 +- piet-scene/src/resource/gradient.rs | 12 +- piet-scene/src/resource/mod.rs | 28 +- piet-scene/src/scene/blend.rs | 10 +- piet-scene/src/scene/builder.rs | 172 ++++---- piet-scene/src/scene/mod.rs | 27 +- tests/src/draw.rs | 2 +- 34 files changed, 910 insertions(+), 2389 deletions(-) delete mode 100644 piet-gpu/src/blend.rs delete mode 100644 piet-gpu/src/encoder.rs delete mode 100644 piet-gpu/src/glyph_render.rs delete mode 100644 piet-gpu/src/gradient.rs delete mode 100644 piet-gpu/src/render_ctx.rs create mode 100644 piet-gpu/src/samples.rs create mode 100644 piet-gpu/src/simple_text.rs delete mode 100644 piet-gpu/src/test_scenes.rs delete mode 100644 piet-gpu/src/text.rs rename piet-scene/src/{glyph/mod.rs => glyph.rs} (85%) diff --git a/Cargo.lock b/Cargo.lock index 0eab498..947b09a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -589,12 +589,6 @@ dependencies = [ "libc", ] -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "memchr" version = "2.5.0" @@ -873,33 +867,23 @@ dependencies = [ "piet-scene", ] -[[package]] -name = "piet" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00543608fb5ee6063f5ff1259246ae23073c1a5e413e643d0469da3d4b7b4de" -dependencies = [ - "kurbo 0.7.1", - "unic-bidi", -] - [[package]] name = "piet-gpu" version = "0.1.0" dependencies = [ "bytemuck", "clap", + "kurbo 0.8.3", "ndk 0.3.0", "ndk-glue 0.3.0", "ndk-sys", - "piet", "piet-gpu-hal", "piet-gpu-types", + "piet-scene", "png", "rand", "raw-window-handle 0.3.4", "roxmltree", - "swash", "winit", ] @@ -1219,16 +1203,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "swash" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bdb2004b76b5f5f3afe722d70b1aea160d2362cb7e40716a7106197ee7f82d" -dependencies = [ - "yazi", - "zeno", -] - [[package]] name = "syn" version = "1.0.98" @@ -1292,57 +1266,6 @@ dependencies = [ "serde", ] -[[package]] -name = "unic-bidi" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1356b759fb6a82050666f11dce4b6fe3571781f1449f3ef78074e408d468ec09" -dependencies = [ - "matches", - "unic-ucd-bidi", -] - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-bidi" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - [[package]] name = "unicode-ident" version = "1.0.1" @@ -1654,15 +1577,3 @@ name = "xmlparser" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" - -[[package]] -name = "yazi" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03b3e19c937b5b9bd8e52b1c88f30cce5c0d33d676cf174866175bb794ff658" - -[[package]] -name = "zeno" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c110ba09c9b3a43edd4803d570df0da2414fed6e822e22b976a4e3ef50860701" diff --git a/pgpu-render/src/lib.rs b/pgpu-render/src/lib.rs index 7763e58..8d137ea 100644 --- a/pgpu-render/src/lib.rs +++ b/pgpu-render/src/lib.rs @@ -26,9 +26,7 @@ mod render; -use piet_scene::brush::{Brush, Color}; -use piet_scene::path::Element; -use piet_scene::scene::Fill; +use piet_scene::{Brush, Color, Fill, PathElement}; use render::*; use std::ffi::c_void; use std::mem::transmute; @@ -199,7 +197,7 @@ pub struct PgpuTransform { pub dy: f32, } -impl From for PgpuAffine { +impl From for piet_scene::Affine { fn from(xform: PgpuTransform) -> Self { Self { xx: xform.xx, @@ -212,8 +210,6 @@ impl From for PgpuAffine { } } -pub type PgpuAffine = piet_scene::geometry::Affine; - /// Creates a new builder for filling a piet-gpu scene. The specified scene /// should not be accessed while the builder is live. #[no_mangle] @@ -243,7 +239,7 @@ pub unsafe extern "C" fn pgpu_scene_builder_add_glyph( } impl Iterator for PgpuPathIter { - type Item = piet_scene::path::Element; + type Item = PathElement; fn next(&mut self) -> Option { let mut el = PgpuPathElement { @@ -253,17 +249,17 @@ impl Iterator for PgpuPathIter { if (self.next_element)(self.context, &mut el as _) { let p = &el.points; Some(match el.verb { - PgpuPathVerb::MoveTo => Element::MoveTo((p[0].x, p[0].y).into()), - PgpuPathVerb::LineTo => Element::LineTo((p[0].x, p[0].y).into()), + PgpuPathVerb::MoveTo => PathElement::MoveTo((p[0].x, p[0].y).into()), + PgpuPathVerb::LineTo => PathElement::LineTo((p[0].x, p[0].y).into()), PgpuPathVerb::QuadTo => { - Element::QuadTo((p[0].x, p[0].y).into(), (p[1].x, p[1].y).into()) + PathElement::QuadTo((p[0].x, p[0].y).into(), (p[1].x, p[1].y).into()) } - PgpuPathVerb::CurveTo => Element::CurveTo( + PgpuPathVerb::CurveTo => PathElement::CurveTo( (p[0].x, p[0].y).into(), (p[1].x, p[1].y).into(), (p[2].x, p[2].y).into(), ), - PgpuPathVerb::Close => Element::Close, + PgpuPathVerb::Close => PathElement::Close, }) } else { None @@ -445,7 +441,7 @@ pub unsafe extern "C" fn pgpu_glyph_bbox( glyph: *const PgpuGlyph, transform: &[f32; 6], ) -> PgpuRect { - let transform = piet_scene::geometry::Affine::new(transform); + let transform = piet_scene::Affine::new(transform); let rect = (*glyph).bbox(Some(transform)); PgpuRect { x0: rect.min.x, diff --git a/pgpu-render/src/render.rs b/pgpu-render/src/render.rs index 16b112a..bbea802 100644 --- a/pgpu-render/src/render.rs +++ b/pgpu-render/src/render.rs @@ -14,13 +14,11 @@ // // Also licensed under MIT license, at your choice. -use piet_gpu::{EncodedSceneRef, PixelFormat, RenderConfig}; +use piet_gpu::{PixelFormat, RenderConfig}; use piet_gpu_hal::{QueryPool, Session}; -use piet_scene::geometry::{Affine, Rect}; use piet_scene::glyph::pinot::{types::Tag, FontDataRef}; use piet_scene::glyph::{GlyphContext, GlyphProvider}; -use piet_scene::resource::ResourceContext; -use piet_scene::scene::{Fragment, Scene}; +use piet_scene::{Affine, Rect, ResourceContext, Scene, SceneFragment}; /// State and resources for rendering a scene. pub struct PgpuRenderer { @@ -120,47 +118,31 @@ impl PgpuScene { pub fn builder(&mut self) -> PgpuSceneBuilder { self.rcx.advance(); - PgpuSceneBuilder(piet_scene::scene::build_scene( + PgpuSceneBuilder(piet_scene::SceneBuilder::for_scene( &mut self.scene, &mut self.rcx, )) } - - fn encoded_scene<'a>(&'a self) -> EncodedSceneRef<'a, piet_scene::geometry::Affine> { - let d = self.scene.data(); - EncodedSceneRef { - transform_stream: &d.transform_stream, - tag_stream: &d.tag_stream, - pathseg_stream: &d.pathseg_stream, - linewidth_stream: &d.linewidth_stream, - drawtag_stream: &d.drawtag_stream, - drawdata_stream: &d.drawdata_stream, - n_path: d.n_path, - n_pathseg: d.n_pathseg, - n_clip: d.n_clip, - ramp_data: self.rcx.ramp_data(), - } - } } /// Encoded streams and resources describing a vector graphics scene fragment. -pub struct PgpuSceneFragment(pub Fragment); +pub struct PgpuSceneFragment(pub SceneFragment); impl PgpuSceneFragment { pub fn new() -> Self { - Self(Fragment::default()) + Self(SceneFragment::default()) } pub fn builder(&mut self) -> PgpuSceneBuilder { - PgpuSceneBuilder(piet_scene::scene::build_fragment(&mut self.0)) + PgpuSceneBuilder(piet_scene::SceneBuilder::for_fragment(&mut self.0)) } } /// Builder for constructing an encoded scene. -pub struct PgpuSceneBuilder<'a>(pub piet_scene::scene::Builder<'a>); +pub struct PgpuSceneBuilder<'a>(pub piet_scene::SceneBuilder<'a>); impl<'a> PgpuSceneBuilder<'a> { - pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::geometry::Affine) { + pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::Affine) { self.0.append(&glyph.fragment, Some(*transform)); } @@ -216,7 +198,7 @@ pub struct PgpuGlyphProvider<'a>(GlyphProvider<'a>); impl<'a> PgpuGlyphProvider<'a> { pub fn get(&mut self, gid: u16) -> Option { - let fragment = self.0.get(gid)?; + let fragment = self.0.get(gid, None)?; Some(PgpuGlyph { fragment }) } @@ -228,7 +210,7 @@ impl<'a> PgpuGlyphProvider<'a> { /// Encoded (possibly color) outline for a glyph. pub struct PgpuGlyph { - fragment: Fragment, + fragment: SceneFragment, } impl PgpuGlyph { diff --git a/piet-gpu/Cargo.toml b/piet-gpu/Cargo.toml index e459fc4..9240093 100644 --- a/piet-gpu/Cargo.toml +++ b/piet-gpu/Cargo.toml @@ -26,15 +26,18 @@ path = "../piet-gpu-hal" [dependencies.piet-gpu-types] path = "../piet-gpu-types" +[dependencies.piet-scene] +path = "../piet-scene" +features = ["kurbo"] + [dependencies] -piet = "0.2.0" png = "0.16.2" rand = "0.7.3" roxmltree = "0.13" winit = "0.26.1" clap = "2.33" -swash = "0.1.4" bytemuck = { version = "1.7.2", features = ["derive"] } +kurbo = "0.8.3" [target.'cfg(target_os = "android")'.dependencies] ndk = "0.3" diff --git a/piet-gpu/bin/android.rs b/piet-gpu/bin/android.rs index 8f3fa18..cf5db9d 100644 --- a/piet-gpu/bin/android.rs +++ b/piet-gpu/bin/android.rs @@ -17,10 +17,8 @@ use piet_gpu_hal::{ SubmittedCmdBuf, Surface, Swapchain, }; -use piet::kurbo::Point; -use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder}; - -use piet_gpu::{test_scenes, PietGpuRenderContext, RenderDriver, Renderer}; +use piet_gpu::{samples, RenderDriver, Renderer, SimpleText}; +use piet_scene::{ResourceContext, Scene, SceneBuilder}; #[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))] fn main() { @@ -134,14 +132,15 @@ impl GfxState { info_string = stats.short_summary(); println!("{}", info_string); } - let mut ctx = PietGpuRenderContext::new(); - test_scenes::render_anim_frame(&mut ctx, self.current_frame); - //test_scenes::render_tiger(&mut ctx); - render_info_string(&mut ctx, &info_string); - if let Err(e) = self - .render_driver - .upload_render_ctx(&self.session, &mut ctx) - { + let mut text = SimpleText::new(); + let mut scene = Scene::default(); + let mut rcx = ResourceContext::default(); + let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx); + samples::render_anim_frame(&mut builder, self.current_frame); + //samples::render_tiger(&mut builder, false); + render_info(&mut text, &mut builder, &info_string); + builder.finish(); + if let Err(e) = self.render_driver.upload_scene(&self.session, &scene, &rcx) { println!("error in uploading: {}", e); } let (image_idx, acquisition_semaphore) = self.swapchain.next().unwrap(); @@ -173,12 +172,13 @@ impl GfxState { } } -fn render_info_string(rc: &mut impl RenderContext, info: &str) { - let layout = rc - .text() - .new_text_layout(info.to_string()) - .default_attribute(TextAttribute::FontSize(60.0)) - .build() - .unwrap(); - rc.draw_text(&layout, Point::new(110.0, 120.0)); +fn render_info(simple_text: &mut SimpleText, sb: &mut SceneBuilder, info: &str) { + simple_text.add( + sb, + None, + 60.0, + None, + piet_scene::Affine::translate(110.0, 120.0), + info, + ); } diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 6257ebf..632fa74 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -6,7 +6,8 @@ use clap::{App, Arg}; use piet_gpu_hal::{BufferUsage, Error, Instance, InstanceFlags, Session}; -use piet_gpu::{test_scenes, PicoSvg, PietGpuRenderContext, RenderDriver, Renderer}; +use piet_gpu::{samples, PicoSvg, RenderDriver, Renderer}; +use piet_scene::{ResourceContext, Scene, SceneBuilder}; const WIDTH: usize = 2048; const HEIGHT: usize = 1536; @@ -227,11 +228,13 @@ fn main() -> Result<(), Error> { ) .get_matches(); let instance = Instance::new(InstanceFlags::default())?; + let mut scene = Scene::default(); + let mut rcx = ResourceContext::default(); unsafe { let device = instance.device()?; let session = Session::new(device); - - let mut ctx = PietGpuRenderContext::new(); + rcx.advance(); + let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx); if let Some(input) = matches.value_of("INPUT") { let mut scale = matches .value_of("scale") @@ -244,16 +247,17 @@ fn main() -> Result<(), Error> { let start = std::time::Instant::now(); let svg = PicoSvg::load(&xml_str, scale).unwrap(); println!("parsing time: {:?}", start.elapsed()); - test_scenes::render_svg(&mut ctx, &svg); + samples::render_svg(&mut builder, &svg, true); } else { //test_scenes::render_scene(&mut ctx); - test_scenes::render_blend_grid(&mut ctx); + samples::render_blend_grid(&mut builder); } + builder.finish(); let renderer = Renderer::new(&session, WIDTH, HEIGHT, 1)?; let mut render_driver = RenderDriver::new(&session, 1, renderer); let start = std::time::Instant::now(); - render_driver.upload_render_ctx(&session, &mut ctx)?; + render_driver.upload_scene(&session, &scene, &rcx)?; let image_usage = BufferUsage::MAP_READ | BufferUsage::COPY_DST; let image_buf = session.create_buffer((WIDTH * HEIGHT * 4) as u64, image_usage)?; diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index 7d4126e..92fc35d 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -1,8 +1,6 @@ -use piet::kurbo::Point; -use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder}; +use piet_gpu::{samples, PicoSvg, RenderDriver, Renderer, SimpleText}; use piet_gpu_hal::{Error, ImageLayout, Instance, InstanceFlags, Session}; - -use piet_gpu::{test_scenes, PicoSvg, PietGpuRenderContext, RenderDriver, Renderer}; +use piet_scene::{ResourceContext, Scene, SceneBuilder}; use clap::{App, Arg}; @@ -59,6 +57,9 @@ fn main() -> Result<(), Error> { let instance = Instance::new(InstanceFlags::default())?; let mut info_string = "info".to_string(); + let mut scene = Scene::default(); + let mut rcx = ResourceContext::default(); + let mut simple_text = piet_gpu::SimpleText::new(); unsafe { let surface = instance.surface(&window)?; let device = instance.device()?; @@ -72,7 +73,7 @@ fn main() -> Result<(), Error> { let renderer = Renderer::new(&session, WIDTH, HEIGHT, NUM_FRAMES)?; let mut render_driver = RenderDriver::new(&session, NUM_FRAMES, renderer); - let mut mode = 0usize; + let mut sample_index = 0usize; event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll; // `ControlFlow::Wait` if only re-render on event @@ -87,8 +88,12 @@ fn main() -> Result<(), Error> { WindowEvent::KeyboardInput { input, .. } => { if input.state == ElementState::Pressed { match input.virtual_keycode { - Some(VirtualKeyCode::Left) => mode = mode.wrapping_sub(1), - Some(VirtualKeyCode::Right) => mode = mode.wrapping_add(1), + Some(VirtualKeyCode::Left) => { + sample_index = sample_index.saturating_sub(1) + } + Some(VirtualKeyCode::Right) => { + sample_index = sample_index.saturating_add(1) + } _ => {} } } @@ -107,52 +112,35 @@ fn main() -> Result<(), Error> { info_string = stats.short_summary(); } - let mut ctx = PietGpuRenderContext::new(); - let test_blend = false; if let Some(svg) = &svg { - test_scenes::render_svg(&mut ctx, svg); - } else if test_blend { - use piet_gpu::{Blend, BlendMode::*, CompositionMode::*}; - let blends = [ - Blend::new(Normal, SrcOver), - Blend::new(Multiply, SrcOver), - Blend::new(Screen, SrcOver), - Blend::new(Overlay, SrcOver), - Blend::new(Darken, SrcOver), - Blend::new(Lighten, SrcOver), - Blend::new(ColorDodge, SrcOver), - Blend::new(ColorBurn, SrcOver), - Blend::new(HardLight, SrcOver), - Blend::new(SoftLight, SrcOver), - Blend::new(Difference, SrcOver), - Blend::new(Exclusion, SrcOver), - Blend::new(Hue, SrcOver), - Blend::new(Saturation, SrcOver), - Blend::new(Color, SrcOver), - Blend::new(Luminosity, SrcOver), - Blend::new(Normal, Clear), - Blend::new(Normal, Copy), - Blend::new(Normal, Dest), - Blend::new(Normal, SrcOver), - Blend::new(Normal, DestOver), - Blend::new(Normal, SrcIn), - Blend::new(Normal, DestIn), - Blend::new(Normal, SrcOut), - Blend::new(Normal, DestOut), - Blend::new(Normal, SrcAtop), - Blend::new(Normal, DestAtop), - Blend::new(Normal, Xor), - Blend::new(Normal, Plus), - ]; - let blend = blends[mode % blends.len()]; - test_scenes::render_blend_test(&mut ctx, current_frame, blend); - info_string = format!("{:?}", blend); + rcx.advance(); + let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx); + samples::render_svg(&mut builder, svg, false); + render_info(&mut simple_text, &mut builder, &info_string); + builder.finish(); + if let Err(e) = render_driver.upload_scene(&session, &scene, &rcx) { + println!("error in uploading: {}", e); + } } else { - test_scenes::render_anim_frame(&mut ctx, current_frame); - } - render_info_string(&mut ctx, &info_string); - if let Err(e) = render_driver.upload_render_ctx(&session, &mut ctx) { - println!("error in uploading: {}", e); + rcx.advance(); + let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx); + + const N_SAMPLES: usize = 4; + match sample_index % N_SAMPLES { + 0 => samples::render_anim_frame( + &mut builder, + &mut simple_text, + current_frame, + ), + 1 => samples::render_blend_grid(&mut builder), + 2 => samples::render_tiger(&mut builder, false), + _ => samples::render_scene(&mut builder), + } + render_info(&mut simple_text, &mut builder, &info_string); + builder.finish(); + if let Err(e) = render_driver.upload_scene(&session, &scene, &rcx) { + println!("error in uploading: {}", e); + } } let (image_idx, acquisition_semaphore) = swapchain.next().unwrap(); @@ -193,12 +181,13 @@ fn main() -> Result<(), Error> { } } -fn render_info_string(rc: &mut impl RenderContext, info: &str) { - let layout = rc - .text() - .new_text_layout(info.to_string()) - .default_attribute(TextAttribute::FontSize(40.0)) - .build() - .unwrap(); - rc.draw_text(&layout, Point::new(110.0, 50.0)); +fn render_info(simple_text: &mut SimpleText, sb: &mut SceneBuilder, info: &str) { + simple_text.add( + sb, + None, + 40.0, + None, + piet_scene::Affine::translate(110.0, 50.0), + info, + ); } diff --git a/piet-gpu/src/blend.rs b/piet-gpu/src/blend.rs deleted file mode 100644 index f0ca002..0000000 --- a/piet-gpu/src/blend.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2022 The piet-gpu authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[repr(C)] -pub enum BlendMode { - Normal = 0, - Multiply = 1, - Screen = 2, - Overlay = 3, - Darken = 4, - Lighten = 5, - ColorDodge = 6, - ColorBurn = 7, - HardLight = 8, - SoftLight = 9, - Difference = 10, - Exclusion = 11, - Hue = 12, - Saturation = 13, - Color = 14, - Luminosity = 15, - // Clip is the same as normal, but doesn't always push a blend group. - Clip = 128, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[repr(C)] -pub enum CompositionMode { - Clear = 0, - Copy = 1, - Dest = 2, - SrcOver = 3, - DestOver = 4, - SrcIn = 5, - DestIn = 6, - SrcOut = 7, - DestOut = 8, - SrcAtop = 9, - DestAtop = 10, - Xor = 11, - Plus = 12, - PlusLighter = 13, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Blend { - pub mode: BlendMode, - pub composition_mode: CompositionMode, -} - -impl Blend { - pub fn new(mode: BlendMode, composition_mode: CompositionMode) -> Self { - Self { - mode, - composition_mode, - } - } - - pub(crate) fn pack(&self) -> u32 { - (self.mode as u32) << 8 | self.composition_mode as u32 - } -} - -impl Default for Blend { - fn default() -> Self { - Self { - mode: BlendMode::Clip, - composition_mode: CompositionMode::SrcOver, - } - } -} - -impl From for Blend { - fn from(mode: BlendMode) -> Self { - Self { - mode, - composition_mode: CompositionMode::SrcOver, - } - } -} - -impl From for Blend { - fn from(mode: CompositionMode) -> Self { - Self { - mode: BlendMode::Normal, - composition_mode: mode, - } - } -} diff --git a/piet-gpu/src/encoder.rs b/piet-gpu/src/encoder.rs deleted file mode 100644 index 2d7c23a..0000000 --- a/piet-gpu/src/encoder.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2021 The piet-gpu authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. - -//! Low-level scene encoding. - -use crate::{Blend, SceneStats, DRAWTAG_SIZE, TRANSFORM_SIZE}; -use bytemuck::{Pod, Zeroable}; -use piet_gpu_hal::BufWrite; - -use crate::stages::{self, PathEncoder, Transform, DRAW_PART_SIZE, PATHSEG_PART_SIZE}; - -pub struct Encoder { - transform_stream: Vec, - tag_stream: Vec, - pathseg_stream: Vec, - linewidth_stream: Vec, - drawtag_stream: Vec, - drawdata_stream: Vec, - n_path: u32, - n_pathseg: u32, - n_clip: u32, -} - -#[derive(Copy, Clone, Debug)] -pub struct EncodedSceneRef<'a, T: Copy + Pod> { - pub transform_stream: &'a [T], - pub tag_stream: &'a [u8], - pub pathseg_stream: &'a [u8], - pub linewidth_stream: &'a [f32], - pub drawtag_stream: &'a [u32], - pub drawdata_stream: &'a [u8], - pub n_path: u32, - pub n_pathseg: u32, - pub n_clip: u32, - pub ramp_data: &'a [u32], -} - -impl<'a, T: Copy + Pod> EncodedSceneRef<'a, T> { - pub(crate) fn stats(&self) -> SceneStats { - SceneStats { - n_drawobj: self.drawtag_stream.len(), - drawdata_len: self.drawdata_stream.len(), - n_transform: self.transform_stream.len(), - linewidth_len: std::mem::size_of_val(self.linewidth_stream), - pathseg_len: self.pathseg_stream.len(), - n_pathtag: self.tag_stream.len(), - - n_path: self.n_path, - n_pathseg: self.n_pathseg, - n_clip: self.n_clip, - } - } - - pub fn write_scene(&self, buf: &mut BufWrite) { - buf.extend_slice(&self.drawtag_stream); - let n_drawobj = self.drawtag_stream.len(); - buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE); - buf.extend_slice(&self.drawdata_stream); - buf.extend_slice(&self.transform_stream); - buf.extend_slice(&self.linewidth_stream); - buf.extend_slice(&self.tag_stream); - let n_pathtag = self.tag_stream.len(); - buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize)); - buf.extend_slice(&self.pathseg_stream); - } -} - -/// A scene fragment encoding a glyph. -/// -/// This is a reduced version of the full encoder. -#[derive(Default)] -pub struct GlyphEncoder { - tag_stream: Vec, - pathseg_stream: Vec, - drawtag_stream: Vec, - drawdata_stream: Vec, - n_path: u32, - n_pathseg: u32, -} - -// Tags for draw objects. See shader/drawtag.h for the authoritative source. -const DRAWTAG_FILLCOLOR: u32 = 0x44; -const DRAWTAG_FILLLINGRADIENT: u32 = 0x114; -const DRAWTAG_FILLRADGRADIENT: u32 = 0x2dc; -const DRAWTAG_BEGINCLIP: u32 = 0x05; -const DRAWTAG_ENDCLIP: u32 = 0x25; - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] -pub struct FillColor { - rgba_color: u32, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] -pub struct FillLinGradient { - index: u32, - p0: [f32; 2], - p1: [f32; 2], -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] -pub struct FillRadGradient { - index: u32, - p0: [f32; 2], - p1: [f32; 2], - r0: f32, - r1: f32, -} - -#[allow(unused)] -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] -pub struct FillImage { - index: u32, - // [i16; 2] - offset: u32, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] -pub struct Clip { - blend: u32, -} - -impl Encoder { - pub fn new() -> Encoder { - Encoder { - transform_stream: vec![Transform::IDENTITY], - tag_stream: Vec::new(), - pathseg_stream: Vec::new(), - linewidth_stream: vec![-1.0], - drawtag_stream: Vec::new(), - drawdata_stream: Vec::new(), - n_path: 0, - n_pathseg: 0, - n_clip: 0, - } - } - - pub fn path_encoder(&mut self) -> PathEncoder { - PathEncoder::new(&mut self.tag_stream, &mut self.pathseg_stream) - } - - pub fn finish_path(&mut self, n_pathseg: u32) { - self.n_path += 1; - self.n_pathseg += n_pathseg; - } - - pub fn transform(&mut self, transform: Transform) { - self.tag_stream.push(0x20); - self.transform_stream.push(transform); - } - - // Swap the last two tags in the tag stream; used for transformed - // gradients. - pub fn swap_last_tags(&mut self) { - let len = self.tag_stream.len(); - self.tag_stream.swap(len - 1, len - 2); - } - - // -1.0 means "fill" - pub fn linewidth(&mut self, linewidth: f32) { - self.tag_stream.push(0x40); - self.linewidth_stream.push(linewidth); - } - - /// Encode a fill color draw object. - /// - /// This should be encoded after a path. - pub fn fill_color(&mut self, rgba_color: u32) { - self.drawtag_stream.push(DRAWTAG_FILLCOLOR); - let element = FillColor { rgba_color }; - self.drawdata_stream.extend(bytemuck::bytes_of(&element)); - } - - /// Encode a fill linear gradient draw object. - /// - /// This should be encoded after a path. - pub fn fill_lin_gradient(&mut self, index: u32, p0: [f32; 2], p1: [f32; 2]) { - self.drawtag_stream.push(DRAWTAG_FILLLINGRADIENT); - let element = FillLinGradient { index, p0, p1 }; - self.drawdata_stream.extend(bytemuck::bytes_of(&element)); - } - - /// Encode a fill radial gradient draw object. - /// - /// This should be encoded after a path. - pub fn fill_rad_gradient(&mut self, index: u32, p0: [f32; 2], p1: [f32; 2], r0: f32, r1: f32) { - self.drawtag_stream.push(DRAWTAG_FILLRADGRADIENT); - let element = FillRadGradient { - index, - p0, - p1, - r0, - r1, - }; - self.drawdata_stream.extend(bytemuck::bytes_of(&element)); - } - - /// Start a clip. - pub fn begin_clip(&mut self, blend: Option) { - self.drawtag_stream.push(DRAWTAG_BEGINCLIP); - let element = Clip { - blend: blend.unwrap_or(Blend::default()).pack(), - }; - self.drawdata_stream.extend(bytemuck::bytes_of(&element)); - self.n_clip += 1; - } - - pub fn end_clip(&mut self, blend: Option) { - self.drawtag_stream.push(DRAWTAG_ENDCLIP); - let element = Clip { - blend: blend.unwrap_or(Blend::default()).pack(), - }; - self.drawdata_stream.extend(bytemuck::bytes_of(&element)); - // This is a dummy path, and will go away with the new clip impl. - self.tag_stream.push(0x10); - self.n_path += 1; - self.n_clip += 1; - } - - pub fn write_scene(&self, buf: &mut BufWrite) { - buf.extend_slice(&self.drawtag_stream); - let n_drawobj = self.drawtag_stream.len(); - buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE); - buf.extend_slice(&self.drawdata_stream); - buf.extend_slice(&self.transform_stream); - buf.extend_slice(&self.linewidth_stream); - buf.extend_slice(&self.tag_stream); - let n_pathtag = self.tag_stream.len(); - buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize)); - buf.extend_slice(&self.pathseg_stream); - } - - pub(crate) fn stats(&self) -> SceneStats { - SceneStats { - n_drawobj: self.drawtag_stream.len(), - drawdata_len: self.drawdata_stream.len(), - n_transform: self.transform_stream.len(), - linewidth_len: std::mem::size_of_val(&*self.linewidth_stream), - n_pathtag: self.tag_stream.len(), - pathseg_len: self.pathseg_stream.len(), - - n_path: self.n_path, - n_pathseg: self.n_pathseg, - n_clip: self.n_clip, - } - } - - pub(crate) fn encode_glyph(&mut self, glyph: &GlyphEncoder) { - self.tag_stream.extend(&glyph.tag_stream); - self.pathseg_stream.extend(&glyph.pathseg_stream); - self.drawtag_stream.extend(&glyph.drawtag_stream); - self.drawdata_stream.extend(&glyph.drawdata_stream); - self.n_path += glyph.n_path; - self.n_pathseg += glyph.n_pathseg; - } -} - -fn padding(x: usize, align: usize) -> usize { - x.wrapping_neg() & (align - 1) -} - -impl GlyphEncoder { - pub(crate) fn path_encoder(&mut self) -> PathEncoder { - PathEncoder::new(&mut self.tag_stream, &mut self.pathseg_stream) - } - - pub(crate) fn finish_path(&mut self, n_pathseg: u32) { - self.n_path += 1; - self.n_pathseg += n_pathseg; - } - - /// Encode a fill color draw object. - /// - /// This should be encoded after a path. - pub(crate) fn fill_color(&mut self, rgba_color: u32) { - self.drawtag_stream.push(DRAWTAG_FILLCOLOR); - let element = FillColor { rgba_color }; - self.drawdata_stream.extend(bytemuck::bytes_of(&element)); - } - - pub(crate) fn is_color(&self) -> bool { - !self.drawtag_stream.is_empty() - } -} diff --git a/piet-gpu/src/glyph_render.rs b/piet-gpu/src/glyph_render.rs deleted file mode 100644 index 8f4c626..0000000 --- a/piet-gpu/src/glyph_render.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2022 The piet-gpu authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. - -//! An experimental API for glyph rendering. - -use piet::{kurbo::Affine, RenderContext}; -use swash::{scale::ScaleContext, CacheKey, FontDataRef}; - -use crate::{encoder::GlyphEncoder, PietGpuRenderContext}; - -pub struct GlyphRenderer { - pub render_ctx: PietGpuRenderContext, - scale_context: ScaleContext, -} - -#[repr(transparent)] -pub struct FontId(CacheKey); - -impl GlyphRenderer { - pub fn new() -> GlyphRenderer { - let render_ctx = PietGpuRenderContext::new(); - let scale_context = ScaleContext::new(); - GlyphRenderer { - render_ctx, - scale_context, - } - } - - pub unsafe fn add_glyph( - &mut self, - font_data: &[u8], - font_id: u64, - glyph_id: u16, - transform: [f32; 6], - ) { - // This transmute is dodgy because the definition in swash isn't repr(transparent). - // I think the best solution is to have a from_u64 method, but we'll work that out - // later. - let font_id = FontId(std::mem::transmute(font_id)); - let encoder = self.make_glyph(font_data, font_id, glyph_id); - const DEFAULT_UPEM: u16 = 2048; - let affine = Affine::new([ - transform[0] as f64, - transform[1] as f64, - transform[2] as f64, - transform[3] as f64, - transform[4] as f64, - transform[5] as f64, - ]) * Affine::scale(1.0 / DEFAULT_UPEM as f64); - self.render_ctx.transform(affine); - self.render_ctx.encode_glyph(&encoder); - // TODO: don't fill glyph if RGBA - self.render_ctx.fill_glyph(0xff_ff_ff_ff); - self.render_ctx.transform(affine.inverse()); - } - - pub fn reset(&mut self) { - self.render_ctx = PietGpuRenderContext::new(); - } - - fn make_glyph(&mut self, font_data: &[u8], font_id: FontId, glyph_id: u16) -> GlyphEncoder { - let mut encoder = GlyphEncoder::default(); - let font_data = FontDataRef::new(font_data).expect("invalid font"); - let mut font_ref = font_data.get(0).expect("invalid font index"); - font_ref.key = font_id.0; - let mut scaler = self.scale_context.builder(font_ref).size(2048.).build(); - if let Some(outline) = scaler.scale_outline(glyph_id) { - crate::text::append_outline(&mut encoder, outline.verbs(), outline.points()); - } else { - println!("failed to scale"); - } - encoder - } -} diff --git a/piet-gpu/src/gradient.rs b/piet-gpu/src/gradient.rs deleted file mode 100644 index 443eaec..0000000 --- a/piet-gpu/src/gradient.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2021 The piet-gpu authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Also licensed under MIT license, at your choice. - -//! Implementation of gradients. - -use std::collections::hash_map::{Entry, HashMap}; - -use piet::kurbo::Point; -use piet::{Color, FixedLinearGradient, FixedRadialGradient, GradientStop}; - -/// Radial gradient compatible with COLRv1 spec -#[derive(Debug, Clone)] -pub struct Colrv1RadialGradient { - /// The center of the iner circle. - pub center0: Point, - /// The offset of the origin relative to the center. - pub center1: Point, - /// The radius of the inner circle. - pub radius0: f64, - /// The radius of the outer circle. - pub radius1: f64, - /// The stops. - pub stops: Vec, -} - -#[derive(Clone)] -pub struct BakedGradient { - ramp: Vec, -} - -#[derive(Clone)] -pub struct LinearGradient { - pub(crate) start: [f32; 2], - pub(crate) end: [f32; 2], - pub(crate) ramp_id: u32, -} - -#[derive(Clone)] -pub struct RadialGradient { - pub(crate) start: [f32; 2], - pub(crate) end: [f32; 2], - pub(crate) r0: f32, - pub(crate) r1: f32, - pub(crate) ramp_id: u32, -} - -#[derive(Default)] -pub struct RampCache { - ramps: Vec, - map: HashMap, -} - -#[derive(Clone, Hash, PartialEq, Eq)] -struct GradientRamp(Vec); - -pub const N_SAMPLES: usize = 512; -// TODO: make this dynamic -pub const N_GRADIENTS: usize = 256; - -#[derive(Clone, Copy)] -struct PremulRgba([f64; 4]); - -impl PremulRgba { - fn from_color(c: &Color) -> PremulRgba { - let rgba = c.as_rgba(); - let a = rgba.3; - // TODO: sRGB nonlinearity? This is complicated. - PremulRgba([rgba.0 * a, rgba.1 * a, rgba.2 * a, a]) - } - - fn to_u32(&self) -> u32 { - let z = self.0; - let r = (z[0].max(0.0).min(1.0) * 255.0).round() as u32; - let g = (z[1].max(0.0).min(1.0) * 255.0).round() as u32; - let b = (z[2].max(0.0).min(1.0) * 255.0).round() as u32; - let a = (z[3].max(0.0).min(1.0) * 255.0).round() as u32; - r | (g << 8) | (b << 16) | (a << 24) - } - - fn lerp(&self, other: PremulRgba, t: f64) -> PremulRgba { - fn l(a: f64, b: f64, t: f64) -> f64 { - a * (1.0 - t) + b * t - } - let a = self.0; - let b = other.0; - PremulRgba([ - l(a[0], b[0], t), - l(a[1], b[1], t), - l(a[2], b[2], t), - l(a[3], b[3], t), - ]) - } -} - -impl GradientRamp { - fn from_stops(stops: &[GradientStop]) -> GradientRamp { - let mut last_u = 0.0; - let mut last_c = PremulRgba::from_color(&stops[0].color); - let mut this_u = last_u; - let mut this_c = last_c; - let mut j = 0; - let v = (0..N_SAMPLES) - .map(|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.pos as f64; - this_c = PremulRgba::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_u32() - }) - .collect(); - GradientRamp(v) - } - - /// For debugging/development. - pub(crate) fn dump(&self) { - for val in &self.0 { - println!("{:x}", val); - } - } -} - -impl RampCache { - /// Add a gradient ramp to the cache. - /// - /// Currently there is no eviction, so if the gradient is animating, there may - /// be resource leaks. In order to support lifetime management, the signature - /// should probably change so it returns a ref-counted handle, so that eviction - /// is deferred until the last handle is dropped. - /// - /// This function is pretty expensive, but the result is lightweight. - fn add_ramp(&mut self, ramp: &[GradientStop]) -> usize { - let ramp = GradientRamp::from_stops(ramp); - match self.map.entry(ramp) { - Entry::Occupied(o) => *o.get(), - Entry::Vacant(v) => { - let idx = self.ramps.len(); - self.ramps.push(v.key().clone()); - v.insert(idx); - idx - } - } - } - - pub fn add_linear_gradient(&mut self, lin: &FixedLinearGradient) -> LinearGradient { - let ramp_id = self.add_ramp(&lin.stops); - LinearGradient { - ramp_id: ramp_id as u32, - start: crate::render_ctx::to_f32_2(lin.start), - end: crate::render_ctx::to_f32_2(lin.end), - } - } - - pub fn add_radial_gradient(&mut self, rad: &FixedRadialGradient) -> RadialGradient { - let ramp_id = self.add_ramp(&rad.stops); - RadialGradient { - ramp_id: ramp_id as u32, - start: crate::render_ctx::to_f32_2(rad.center + rad.origin_offset), - end: crate::render_ctx::to_f32_2(rad.center), - r0: 0.0, - r1: rad.radius as f32, - } - } - - pub fn add_radial_gradient_colrv1(&mut self, rad: &Colrv1RadialGradient) -> RadialGradient { - let ramp_id = self.add_ramp(&rad.stops); - RadialGradient { - ramp_id: ramp_id as u32, - start: crate::render_ctx::to_f32_2(rad.center0), - end: crate::render_ctx::to_f32_2(rad.center1), - r0: rad.radius0 as f32, - r1: rad.radius1 as f32, - } - } - - /// Dump the contents of a gradient. This is for debugging. - #[allow(unused)] - pub(crate) fn dump_gradient(&self, lin: &LinearGradient) { - println!("id = {}", lin.ramp_id); - self.ramps[lin.ramp_id as usize].dump(); - } - - /// Get the ramp data. - /// - /// This concatenates all the ramps; we'll want a more sophisticated approach to - /// incremental update. - pub fn get_ramp_data(&self) -> Vec { - let mut result = Vec::with_capacity(N_SAMPLES * self.ramps.len()); - for ramp in &self.ramps { - result.extend(&ramp.0); - } - result - } -} - -#[cfg(test)] -mod test { - use super::RampCache; - use piet::kurbo::Point; - use piet::{Color, FixedLinearGradient, GradientStop}; - - #[test] - fn simple_ramp() { - let stops = vec![ - GradientStop { - color: Color::WHITE, - pos: 0.0, - }, - GradientStop { - color: Color::BLACK, - pos: 1.0, - }, - ]; - let mut cache = RampCache::default(); - let lin = FixedLinearGradient { - start: Point::new(0.0, 0.0), - end: Point::new(0.0, 1.0), - stops, - }; - let our_lin = cache.add_linear_gradient(&lin); - cache.dump_gradient(&our_lin); - } -} diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 71ce7d4..a3d86b2 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -1,31 +1,24 @@ -mod blend; -mod encoder; -pub mod glyph_render; -mod gradient; mod pico_svg; -mod render_ctx; mod render_driver; +pub mod samples; +mod simple_text; pub mod stages; -pub mod test_scenes; -mod text; + +pub use piet_scene as scene; use bytemuck::{Pod, Zeroable}; use std::convert::TryInto; -pub use blend::{Blend, BlendMode, CompositionMode}; -pub use encoder::EncodedSceneRef; -pub use gradient::Colrv1RadialGradient; -pub use render_ctx::PietGpuRenderContext; pub use render_driver::RenderDriver; - -use piet::kurbo::Vec2; -use piet::{ImageFormat, RenderContext}; +pub use simple_text::SimpleText; use piet_gpu_hal::{ - include_shader, BindType, Buffer, BufferUsage, CmdBuf, ComputePassDescriptor, DescriptorSet, - Error, Image, ImageLayout, Pipeline, QueryPool, Session, + include_shader, BindType, BufWrite, Buffer, BufferUsage, CmdBuf, ComputePassDescriptor, + DescriptorSet, Error, Image, ImageLayout, Pipeline, QueryPool, Session, }; +use piet_scene::{ResourceContext, Scene}; + pub use pico_svg::PicoSvg; use stages::{ClipBinding, ElementBinding, ElementCode, DRAW_PART_SIZE, PATHSEG_PART_SIZE}; @@ -36,6 +29,10 @@ const TILE_H: usize = 16; const PTCL_INITIAL_ALLOC: usize = 1024; +const N_GRADIENT_SAMPLES: usize = 512; +// TODO: make this dynamic +const N_GRADIENTS: usize = 256; + #[allow(unused)] fn dump_scene(buf: &[u8]) { for i in 0..(buf.len() / 4) { @@ -333,8 +330,8 @@ impl Renderer { .collect::, _>>()?; let bg_image = Self::make_test_bg_image(&session); - const GRADIENT_BUF_SIZE: usize = - crate::gradient::N_GRADIENTS * crate::gradient::N_SAMPLES * 4; + const GRADIENT_BUF_SIZE: usize = N_GRADIENTS * N_GRADIENT_SAMPLES * 4; + let gradient_bufs = (0..n_bufs) .map(|_| { session @@ -409,59 +406,29 @@ impl Renderer { }) } - /// Convert the scene in the render context to GPU resources. - /// - /// At present, this requires that any command buffer submission has completed. - /// A future evolution will handle staging of the next frame's scene while the - /// rendering of the current frame is in flight. - pub fn upload_render_ctx( + pub fn upload_scene( &mut self, - render_ctx: &mut PietGpuRenderContext, + scene: &Scene, + rcx: &ResourceContext, buf_ix: usize, ) -> Result<(), Error> { - self.scene_stats = render_ctx.stats(); + self.scene_stats = SceneStats::from_scene(scene); unsafe { self.upload_config(buf_ix)?; { let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?; - render_ctx.write_scene(&mut mapped_scene); + write_scene(scene, &mut mapped_scene); } // Upload gradient data. - let ramp_data = render_ctx.get_ramp_data(); + let ramp_data = rcx.ramp_data(); if !ramp_data.is_empty() { assert!( self.gradient_bufs[buf_ix].size() as usize >= std::mem::size_of_val(&*ramp_data) ); - self.gradient_bufs[buf_ix].write(&ramp_data)?; - } - } - Ok(()) - } - - pub fn upload_scene( - &mut self, - scene: &EncodedSceneRef, - buf_ix: usize, - ) -> Result<(), Error> { - self.scene_stats = scene.stats(); - - unsafe { - self.upload_config(buf_ix)?; - { - let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?; - scene.write_scene(&mut mapped_scene); - } - - // Upload gradient data. - if !scene.ramp_data.is_empty() { - assert!( - self.gradient_bufs[buf_ix].size() as usize - >= std::mem::size_of_val(&*scene.ramp_data) - ); - self.gradient_bufs[buf_ix].write(scene.ramp_data)?; + self.gradient_bufs[buf_ix].write(ramp_data)?; } } Ok(()) @@ -643,12 +610,8 @@ impl Renderer { width: usize, height: usize, buf: &[u8], - format: ImageFormat, ) -> Result { unsafe { - if format != ImageFormat::RgbaPremul { - return Err("unsupported image format".into()); - } let buffer = session.create_buffer_init(&buf, BufferUsage::COPY_SRC)?; const RGBA: piet_gpu_hal::ImageFormat = piet_gpu_hal::ImageFormat::Rgba8; let image = session.create_image2d(width.try_into()?, height.try_into()?, RGBA)?; @@ -682,18 +645,14 @@ impl Renderer { buf[(y * WIDTH + x) * 4 + 2] = b; } } - Self::make_image(session, WIDTH, HEIGHT, &buf, ImageFormat::RgbaPremul).unwrap() + Self::make_image(session, WIDTH, HEIGHT, &buf).unwrap() } fn make_gradient_image(session: &Session) -> Image { unsafe { const RGBA: piet_gpu_hal::ImageFormat = piet_gpu_hal::ImageFormat::Rgba8; session - .create_image2d( - gradient::N_SAMPLES as u32, - gradient::N_GRADIENTS as u32, - RGBA, - ) + .create_image2d(N_GRADIENT_SAMPLES as u32, N_GRADIENTS as u32, RGBA) .unwrap() } } @@ -792,6 +751,21 @@ const DRAWTAG_SIZE: usize = 4; const ANNOTATED_SIZE: usize = 40; impl SceneStats { + pub fn from_scene(scene: &piet_scene::Scene) -> Self { + let data = scene.data(); + Self { + n_drawobj: data.drawtag_stream.len(), + drawdata_len: data.drawdata_stream.len(), + n_transform: data.transform_stream.len(), + linewidth_len: std::mem::size_of_val(&*data.linewidth_stream), + pathseg_len: data.pathseg_stream.len(), + n_pathtag: data.tag_stream.len(), + n_path: data.n_path, + n_pathseg: data.n_pathseg, + n_clip: data.n_clip, + } + } + pub(crate) fn scene_size(&self) -> usize { align_up(self.n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE + self.drawdata_len @@ -896,6 +870,24 @@ impl SceneStats { } } +fn write_scene(scene: &Scene, buf: &mut BufWrite) { + let data = scene.data(); + buf.extend_slice(&data.drawtag_stream); + let n_drawobj = data.drawtag_stream.len(); + buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE); + buf.extend_slice(&data.drawdata_stream); + buf.extend_slice(&data.transform_stream); + buf.extend_slice(&data.linewidth_stream); + buf.extend_slice(&data.tag_stream); + let n_pathtag = data.tag_stream.len(); + buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize)); + buf.extend_slice(&data.pathseg_stream); +} + +fn padding(x: usize, align: usize) -> usize { + x.wrapping_neg() & (align - 1) +} + fn align_up(x: usize, align: usize) -> usize { debug_assert!(align.is_power_of_two()); (x + align - 1) & !(align - 1) diff --git a/piet-gpu/src/pico_svg.rs b/piet-gpu/src/pico_svg.rs index 140c42d..eebe3ec 100644 --- a/piet-gpu/src/pico_svg.rs +++ b/piet-gpu/src/pico_svg.rs @@ -4,12 +4,12 @@ use std::str::FromStr; use roxmltree::{Document, Node}; -use piet::kurbo::{Affine, BezPath}; +use kurbo::{Affine, BezPath}; -use piet::{Color, RenderContext}; +use piet_scene::Color; pub struct PicoSvg { - items: Vec, + pub items: Vec, } pub enum Item { @@ -18,14 +18,14 @@ pub enum Item { } pub struct StrokeItem { - width: f64, - color: Color, - path: BezPath, + pub width: f64, + pub color: Color, + pub path: BezPath, } pub struct FillItem { - color: Color, - path: BezPath, + pub color: Color, + pub path: BezPath, } struct Parser<'a> { @@ -44,20 +44,6 @@ impl PicoSvg { } Ok(PicoSvg { items }) } - - pub fn render(&self, rc: &mut impl RenderContext) { - for item in &self.items { - match item { - Item::Fill(fill_item) => { - rc.fill(&fill_item.path, &fill_item.color); - //rc.stroke(&fill_item.path, &fill_item.color, 1.0); - } - Item::Stroke(stroke_item) => { - rc.stroke(&stroke_item.path, &stroke_item.color, stroke_item.width); - } - } - } - } } impl<'a> Parser<'a> { @@ -119,7 +105,14 @@ fn parse_color(color: &str) -> Color { if color.len() == 4 { hex = (hex >> 8) * 0x110000 + ((hex >> 4) & 0xf) * 0x1100 + (hex & 0xf) * 0x11; } - Color::from_rgba32_u32((hex << 8) + 0xff) + let rgba = (hex << 8) + 0xff; + let (r, g, b, a) = ( + (rgba >> 24 & 255) as u8, + ((rgba >> 16) & 255) as u8, + ((rgba >> 8) & 255) as u8, + (rgba & 255) as u8, + ); + Color::rgba8(r, g, b, a) } else if color.starts_with("rgb(") { let mut iter = color[4..color.len() - 1].split(','); let r = u8::from_str(iter.next().unwrap()).unwrap(); @@ -127,19 +120,20 @@ fn parse_color(color: &str) -> Color { let b = u8::from_str(iter.next().unwrap()).unwrap(); Color::rgb8(r, g, b) } else { - Color::from_rgba32_u32(0xff00ff80) + Color::rgba8(255, 0, 255, 0x80) } } -fn modify_opacity(color: Color, attr_name: &str, node: Node) -> Color { +fn modify_opacity(mut color: Color, attr_name: &str, node: Node) -> Color { if let Some(opacity) = node.attribute(attr_name) { let alpha = if opacity.ends_with("%") { let pctg = opacity[..opacity.len() - 1].parse().unwrap_or(100.0); pctg * 0.01 } else { opacity.parse().unwrap_or(1.0) - }; - color.with_alpha(alpha) + } as f64; + color.a = (alpha.min(1.0).max(0.0) * 255.0).round() as u8; + color } else { color } diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs deleted file mode 100644 index a283507..0000000 --- a/piet-gpu/src/render_ctx.rs +++ /dev/null @@ -1,452 +0,0 @@ -// This should match the value in kernel4.comp for correct rendering. -const DO_SRGB_CONVERSION: bool = false; - -use std::borrow::Cow; - -use crate::encoder::GlyphEncoder; -use crate::stages::Transform; -use piet::kurbo::{Affine, PathEl, Point, Rect, Shape}; -use piet::{ - Color, Error, FixedGradient, ImageFormat, InterpolationMode, IntoBrush, RenderContext, - StrokeStyle, -}; - -use piet_gpu_hal::BufWrite; -use piet_gpu_types::encoder::{Encode, Encoder}; -use piet_gpu_types::scene::Element; - -use crate::gradient::{Colrv1RadialGradient, LinearGradient, RadialGradient, RampCache}; -use crate::text::Font; -pub use crate::text::{PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder}; -use crate::{Blend, SceneStats}; - -pub struct PietGpuImage; - -pub struct PietGpuRenderContext { - encoder: Encoder, - elements: Vec, - // Will probably need direct accesss to hal Device to create images etc. - inner_text: PietGpuText, - stroke_width: f32, - // We're tallying these cpu-side for expedience, but will probably - // move this to some kind of readback from element processing. - /// The count of elements that make it through to coarse rasterization. - path_count: usize, - /// The count of path segment elements. - pathseg_count: usize, - /// The count of transform elements. - trans_count: usize, - - cur_transform: Affine, - state_stack: Vec, - clip_stack: Vec, - - ramp_cache: RampCache, - - // Fields for new element processing pipeline below - // TODO: delete old encoder, rename - new_encoder: crate::encoder::Encoder, -} - -#[derive(Clone)] -pub enum PietGpuBrush { - Solid(u32), - LinGradient(LinearGradient), - RadGradient(RadialGradient), -} - -#[derive(Default)] -struct State { - /// The transform at the parent state. - transform: Affine, - n_clip: usize, -} - -struct ClipElement { - blend: Option, -} - -const TOLERANCE: f64 = 0.25; - -impl PietGpuRenderContext { - pub fn new() -> PietGpuRenderContext { - let encoder = Encoder::new(); - let elements = Vec::new(); - let font = Font::new(); - let inner_text = PietGpuText::new(font); - let stroke_width = -1.0; - PietGpuRenderContext { - encoder, - elements, - inner_text, - stroke_width, - path_count: 0, - pathseg_count: 0, - trans_count: 0, - cur_transform: Affine::default(), - state_stack: Vec::new(), - clip_stack: Vec::new(), - ramp_cache: RampCache::default(), - new_encoder: crate::encoder::Encoder::new(), - } - } - - pub(crate) fn stats(&self) -> SceneStats { - self.new_encoder.stats() - } - - pub fn write_scene(&self, buf: &mut BufWrite) { - self.new_encoder.write_scene(buf); - } - - // TODO: delete - pub fn get_scene_buf(&mut self) -> &[u8] { - const ALIGN: usize = 128; - let padded_size = (self.elements.len() + (ALIGN - 1)) & ALIGN.wrapping_neg(); - self.elements.resize(padded_size, Element::Nop()); - self.elements.encode(&mut self.encoder); - self.encoder.buf() - } - - pub fn path_count(&self) -> usize { - self.path_count - } - - pub fn pathseg_count(&self) -> usize { - self.pathseg_count - } - - pub fn trans_count(&self) -> usize { - self.trans_count - } - - pub fn get_ramp_data(&self) -> Vec { - self.ramp_cache.get_ramp_data() - } -} - -impl RenderContext for PietGpuRenderContext { - type Brush = PietGpuBrush; - type Image = PietGpuImage; - type Text = PietGpuText; - type TextLayout = PietGpuTextLayout; - - fn status(&mut self) -> Result<(), Error> { - Ok(()) - } - - fn solid_brush(&mut self, color: Color) -> Self::Brush { - // kernel4 expects colors encoded in alpha-premultiplied sRGB: - // - // [α,sRGB(α⋅R),sRGB(α⋅G),sRGB(α⋅B)] - // - // See also http://ssp.impulsetrain.com/gamma-premult.html. - let (r, g, b, a) = color.as_rgba(); - let premul = Color::rgba( - to_srgb(from_srgb(r) * a), - to_srgb(from_srgb(g) * a), - to_srgb(from_srgb(b) * a), - a, - ); - PietGpuBrush::Solid(premul.as_rgba_u32()) - } - - fn gradient(&mut self, gradient: impl Into) -> Result { - match gradient.into() { - FixedGradient::Linear(lin) => { - let lin = self.ramp_cache.add_linear_gradient(&lin); - Ok(PietGpuBrush::LinGradient(lin)) - } - FixedGradient::Radial(rad) => { - let rad = self.ramp_cache.add_radial_gradient(&rad); - Ok(PietGpuBrush::RadGradient(rad)) - } - } - } - - fn clear(&mut self, _color: Color) {} - - fn stroke(&mut self, shape: impl Shape, brush: &impl IntoBrush, width: f64) { - self.encode_linewidth(width.abs() as f32); - let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); - let path = shape.path_elements(TOLERANCE); - self.encode_path(path, false); - self.encode_brush(&brush); - } - - fn stroke_styled( - &mut self, - _shape: impl Shape, - _brush: &impl IntoBrush, - _width: f64, - _style: &StrokeStyle, - ) { - } - - fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush) { - let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); - let path = shape.path_elements(TOLERANCE); - self.encode_linewidth(-1.0); - self.encode_path(path, true); - self.encode_brush(&brush); - } - - fn fill_even_odd(&mut self, _shape: impl Shape, _brush: &impl IntoBrush) {} - - fn clip(&mut self, shape: impl Shape) { - self.encode_linewidth(-1.0); - let path = shape.path_elements(TOLERANCE); - self.encode_path(path, true); - self.new_encoder.begin_clip(None); - self.clip_stack.push(ClipElement { blend: None }); - if let Some(tos) = self.state_stack.last_mut() { - tos.n_clip += 1; - } - } - - fn text(&mut self) -> &mut Self::Text { - &mut self.inner_text - } - - fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into) { - self.encode_linewidth(-1.0); - layout.draw_text(self, pos.into()); - } - - fn save(&mut self) -> Result<(), Error> { - self.state_stack.push(State { - transform: self.cur_transform, - n_clip: 0, - }); - Ok(()) - } - - fn restore(&mut self) -> Result<(), Error> { - if let Some(state) = self.state_stack.pop() { - self.encode_transform(Transform::from_kurbo(state.transform)); - self.cur_transform = state.transform; - for _ in 0..state.n_clip { - self.pop_clip(); - } - Ok(()) - } else { - Err(Error::StackUnbalance) - } - } - - fn finish(&mut self) -> Result<(), Error> { - for _ in 0..self.clip_stack.len() { - self.pop_clip(); - } - Ok(()) - } - - fn transform(&mut self, transform: Affine) { - self.cur_transform *= transform; - self.encode_transform(Transform::from_kurbo(self.cur_transform)); - } - - fn make_image( - &mut self, - _width: usize, - _height: usize, - _buf: &[u8], - _format: ImageFormat, - ) -> Result { - Ok(PietGpuImage) - } - - fn draw_image( - &mut self, - _image: &Self::Image, - _rect: impl Into, - _interp: InterpolationMode, - ) { - } - - fn draw_image_area( - &mut self, - _image: &Self::Image, - _src_rect: impl Into, - _dst_rect: impl Into, - _interp: InterpolationMode, - ) { - } - - fn blurred_rect(&mut self, _rect: Rect, _blur_radius: f64, _brush: &impl IntoBrush) {} - - fn current_transform(&self) -> Affine { - self.cur_transform - } - - fn with_save(&mut self, f: impl FnOnce(&mut Self) -> Result<(), Error>) -> Result<(), Error> { - self.save()?; - // Always try to restore the stack, even if `f` errored. - f(self).and(self.restore()) - } -} - -impl PietGpuRenderContext { - pub fn blend(&mut self, shape: impl Shape, blend: Blend) { - self.encode_linewidth(-1.0); - let path = shape.path_elements(TOLERANCE); - self.encode_path(path, true); - self.new_encoder.begin_clip(Some(blend)); - self.clip_stack.push(ClipElement { blend: Some(blend) }); - if let Some(tos) = self.state_stack.last_mut() { - tos.n_clip += 1; - } - } - - pub fn radial_gradient_colrv1(&mut self, rad: &Colrv1RadialGradient) -> PietGpuBrush { - PietGpuBrush::RadGradient(self.ramp_cache.add_radial_gradient_colrv1(rad)) - } - - pub fn fill_transform(&mut self, shape: impl Shape, brush: &PietGpuBrush, transform: Affine) { - let path = shape.path_elements(TOLERANCE); - self.encode_linewidth(-1.0); - self.encode_path(path, true); - self.encode_transform(Transform::from_kurbo(transform)); - self.new_encoder.swap_last_tags(); - self.encode_brush(&brush); - self.encode_transform(Transform::from_kurbo(transform.inverse())); - } - - fn encode_path(&mut self, path: impl Iterator, is_fill: bool) { - if is_fill { - self.encode_path_inner( - path.flat_map(|el| { - match el { - PathEl::MoveTo(..) => Some(PathEl::ClosePath), - _ => None, - } - .into_iter() - .chain(Some(el)) - }) - .chain(Some(PathEl::ClosePath)), - ) - } else { - self.encode_path_inner(path) - } - } - - fn encode_path_inner(&mut self, path: impl Iterator) { - let mut pe = self.new_encoder.path_encoder(); - for el in path { - match el { - PathEl::MoveTo(p) => { - let p = to_f32_2(p); - pe.move_to(p[0], p[1]); - } - PathEl::LineTo(p) => { - let p = to_f32_2(p); - pe.line_to(p[0], p[1]); - } - PathEl::QuadTo(p1, p2) => { - let p1 = to_f32_2(p1); - let p2 = to_f32_2(p2); - pe.quad_to(p1[0], p1[1], p2[0], p2[1]); - } - PathEl::CurveTo(p1, p2, p3) => { - let p1 = to_f32_2(p1); - let p2 = to_f32_2(p2); - let p3 = to_f32_2(p3); - pe.cubic_to(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]); - } - PathEl::ClosePath => pe.close_path(), - } - } - pe.path(); - let n_pathseg = pe.n_pathseg(); - self.new_encoder.finish_path(n_pathseg); - } - - fn pop_clip(&mut self) { - let tos = self.clip_stack.pop().unwrap(); - self.new_encoder.end_clip(tos.blend); - } - - pub(crate) fn encode_glyph(&mut self, glyph: &GlyphEncoder) { - self.new_encoder.encode_glyph(glyph); - } - - pub(crate) fn fill_glyph(&mut self, rgba_color: u32) { - self.new_encoder.fill_color(rgba_color); - } - - pub(crate) fn encode_transform(&mut self, transform: Transform) { - self.new_encoder.transform(transform); - } - - fn encode_linewidth(&mut self, linewidth: f32) { - if self.stroke_width != linewidth { - self.new_encoder.linewidth(linewidth); - self.stroke_width = linewidth; - } - } - - fn encode_brush(&mut self, brush: &PietGpuBrush) { - match brush { - PietGpuBrush::Solid(rgba_color) => { - self.new_encoder.fill_color(*rgba_color); - } - PietGpuBrush::LinGradient(lin) => { - self.new_encoder - .fill_lin_gradient(lin.ramp_id, lin.start, lin.end); - } - PietGpuBrush::RadGradient(rad) => { - self.new_encoder - .fill_rad_gradient(rad.ramp_id, rad.start, rad.end, rad.r0, rad.r1); - } - } - } -} - -impl IntoBrush for PietGpuBrush { - fn make_brush<'b>( - &'b self, - _piet: &mut PietGpuRenderContext, - _bbox: impl FnOnce() -> Rect, - ) -> std::borrow::Cow<'b, PietGpuBrush> { - Cow::Borrowed(self) - } -} - -pub(crate) fn to_f32_2(point: Point) -> [f32; 2] { - [point.x as f32, point.y as f32] -} - -fn rect_to_f32_4(rect: Rect) -> [f32; 4] { - [ - rect.x0 as f32, - rect.y0 as f32, - rect.x1 as f32, - rect.y1 as f32, - ] -} - -fn to_srgb(f: f64) -> f64 { - if DO_SRGB_CONVERSION { - if f <= 0.0031308 { - f * 12.92 - } else { - let a = 0.055; - (1. + a) * f64::powf(f, f64::recip(2.4)) - a - } - } else { - f - } -} - -fn from_srgb(f: f64) -> f64 { - if DO_SRGB_CONVERSION { - if f <= 0.04045 { - f / 12.92 - } else { - let a = 0.055; - f64::powf((f + a) * f64::recip(1. + a), 2.4) - } - } else { - f - } -} diff --git a/piet-gpu/src/render_driver.rs b/piet-gpu/src/render_driver.rs index 98dff0c..afeec5e 100644 --- a/piet-gpu/src/render_driver.rs +++ b/piet-gpu/src/render_driver.rs @@ -14,10 +14,10 @@ // // Also licensed under MIT license, at your choice. -use bytemuck::Pod; use piet_gpu_hal::{CmdBuf, Error, Image, QueryPool, Semaphore, Session, SubmittedCmdBuf}; +use piet_scene::{ResourceContext, Scene}; -use crate::{EncodedSceneRef, MemoryHeader, PietGpuRenderContext, Renderer, SceneStats}; +use crate::{MemoryHeader, Renderer, SceneStats}; /// Additional logic for sequencing rendering operations, specifically /// for handling failure and reallocation. @@ -86,24 +86,15 @@ impl RenderDriver { } } - pub fn upload_render_ctx( + pub fn upload_scene( &mut self, session: &Session, - render_ctx: &mut PietGpuRenderContext, + scene: &Scene, + rcx: &ResourceContext, ) -> Result<(), Error> { - let stats = render_ctx.stats(); + let stats = SceneStats::from_scene(scene); self.ensure_scene_buffers(session, &stats)?; - self.renderer.upload_render_ctx(render_ctx, self.buf_ix) - } - - pub fn upload_scene( - &mut self, - session: &Session, - scene: &EncodedSceneRef, - ) -> Result<(), Error> { - let stats = scene.stats(); - self.ensure_scene_buffers(session, &stats)?; - self.renderer.upload_scene(scene, self.buf_ix) + self.renderer.upload_scene(scene, rcx, self.buf_ix) } fn ensure_scene_buffers(&mut self, session: &Session, stats: &SceneStats) -> Result<(), Error> { diff --git a/piet-gpu/src/samples.rs b/piet-gpu/src/samples.rs new file mode 100644 index 0000000..b3b4b8b --- /dev/null +++ b/piet-gpu/src/samples.rs @@ -0,0 +1,368 @@ +use crate::PicoSvg; +use kurbo::BezPath; +use piet_scene::*; + +use crate::SimpleText; + +#[allow(unused)] +const N_CIRCLES: usize = 0; + +#[allow(unused)] +pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) { + use crate::pico_svg::*; + let start = std::time::Instant::now(); + for item in &svg.items { + match item { + Item::Fill(fill) => { + sb.fill( + Fill::NonZero, + &fill.color.into(), + None, + convert_bez_path(&fill.path), + ); + } + Item::Stroke(stroke) => { + sb.stroke( + &simple_stroke(stroke.width as f32), + &stroke.color.into(), + None, + convert_bez_path(&stroke.path), + ); + } + } + } + if print_stats { + println!("flattening and encoding time: {:?}", start.elapsed()); + } +} + +#[allow(unused)] +pub fn render_tiger(sb: &mut SceneBuilder, print_stats: bool) { + use super::pico_svg::*; + let xml_str = std::str::from_utf8(include_bytes!("../Ghostscript_Tiger.svg")).unwrap(); + let start = std::time::Instant::now(); + let svg = PicoSvg::load(xml_str, 8.0).unwrap(); + if print_stats { + println!("parsing time: {:?}", start.elapsed()); + } + render_svg(sb, &svg, print_stats); +} + +pub fn render_scene(sb: &mut SceneBuilder) { + render_cardioid(sb); + render_clip_test(sb); + render_alpha_test(sb); + //render_tiger(sb, false); +} + +#[allow(unused)] +fn render_cardioid(sb: &mut SceneBuilder) { + let n = 601; + let dth = std::f32::consts::PI * 2.0 / (n as f32); + let center = Point::new(1024.0, 768.0); + let r = 750.0; + let mut path = vec![]; + for i in 1..n { + let mut p0 = center; + let a0 = i as f32 * dth; + p0.x += a0.cos() * r; + p0.y += a0.sin() * r; + let mut p1 = center; + let a1 = ((i * 2) % n) as f32 * dth; + p1.x += a1.cos() * r; + p1.y += a1.sin() * r; + path.push(PathElement::MoveTo(p0)); + path.push(PathElement::LineTo(p1)); + } + sb.stroke( + &simple_stroke(2.0), + &Brush::Solid(Color::rgb8(0, 0, 0)), + None, + &path, + ); +} + +#[allow(unused)] +fn render_clip_test(sb: &mut SceneBuilder) { + const N: usize = 16; + const X0: f32 = 50.0; + const Y0: f32 = 450.0; + // Note: if it gets much larger, it will exceed the 1MB scratch buffer. + // But this is a pretty demanding test. + const X1: f32 = 550.0; + const Y1: f32 = 950.0; + let step = 1.0 / ((N + 1) as f32); + for i in 0..N { + let t = ((i + 1) as f32) * step; + let path = &[ + PathElement::MoveTo((X0, Y0).into()), + PathElement::LineTo((X1, Y0).into()), + PathElement::LineTo((X1, Y0 + t * (Y1 - Y0)).into()), + PathElement::LineTo((X1 + t * (X0 - X1), Y1).into()), + PathElement::LineTo((X0, Y1).into()), + PathElement::Close, + ]; + sb.push_layer(Mix::Clip.into(), path); + } + let rect = Rect { + min: Point::new(X0, Y0), + max: Point::new(X1, Y1), + }; + sb.fill( + Fill::NonZero, + &Brush::Solid(Color::rgb8(0, 0, 0)), + None, + rect.elements(), + ); + for _ in 0..N { + sb.pop_layer(); + } +} + +#[allow(unused)] +fn render_alpha_test(sb: &mut SceneBuilder) { + // Alpha compositing tests. + sb.fill( + Fill::NonZero, + &Color::rgb8(255, 0, 0).into(), + None, + make_diamond(Point::new(1024.0, 100.0)), + ); + sb.fill( + Fill::NonZero, + &Color::rgba8(0, 255, 0, 0x80).into(), + None, + make_diamond(Point::new(1024.0, 125.0)), + ); + sb.push_layer(Mix::Clip.into(), make_diamond(Point::new(1024.0, 150.0))); + sb.fill( + Fill::NonZero, + &Color::rgba8(0, 0, 255, 0x80).into(), + None, + make_diamond(Point::new(1024.0, 175.0)), + ); + sb.pop_layer(); +} + +#[allow(unused)] +pub fn render_blend_grid(sb: &mut SceneBuilder) { + const BLEND_MODES: &[Mix] = &[ + Mix::Normal, + Mix::Multiply, + Mix::Darken, + Mix::Screen, + Mix::Lighten, + Mix::Overlay, + Mix::ColorDodge, + Mix::ColorBurn, + Mix::HardLight, + Mix::SoftLight, + Mix::Difference, + Mix::Exclusion, + Mix::Hue, + Mix::Saturation, + Mix::Color, + Mix::Luminosity, + ]; + for (ix, &blend) in BLEND_MODES.iter().enumerate() { + let i = ix % 4; + let j = ix / 4; + let transform = Affine::translate(i as f32 * 225., j as f32 * 225.); + render_blend_square(sb, blend.into(), transform); + } +} + +#[allow(unused)] +fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affine) { + // Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + sb.transform(transform); + let rect = Rect::from_origin_size(Point::new(0., 0.), 200., 200.); + let stops = &[ + GradientStop { + color: Color::rgb8(0, 0, 0), + offset: 0.0, + }, + GradientStop { + color: Color::rgb8(255, 255, 255), + offset: 1.0, + }, + ][..]; + let linear = Brush::LinearGradient(LinearGradient { + start: Point::new(0.0, 0.0), + end: Point::new(200.0, 0.0), + stops: stops.into(), + extend: ExtendMode::Pad, + }); + sb.fill(Fill::NonZero, &linear, None, rect.elements()); + const GRADIENTS: &[(f32, f32, Color)] = &[ + (150., 0., Color::rgb8(64, 240, 255)), + (175., 100., Color::rgb8(240, 96, 255)), + (125., 200., Color::rgb8(255, 192, 64)), + ]; + for (x, y, c) in GRADIENTS { + let mut color2 = c.clone(); + color2.a = 0; + let stops = &[ + GradientStop { + color: c.clone(), + offset: 0.0, + }, + GradientStop { + color: color2, + offset: 1.0, + }, + ][..]; + let rad = Brush::RadialGradient(RadialGradient { + center0: Point::new(*x, *y), + center1: Point::new(*x, *y), + radius0: 0.0, + radius1: 100.0, + stops: stops.into(), + extend: ExtendMode::Pad, + }); + sb.fill(Fill::NonZero, &rad, None, rect.elements()); + } + const COLORS: &[Color] = &[ + Color::rgb8(0, 0, 255), + Color::rgb8(0, 255, 0), + Color::rgb8(255, 0, 0), + ]; + sb.push_layer(Mix::Normal.into(), rect.elements()); + for (i, c) in COLORS.iter().enumerate() { + let stops = &[ + GradientStop { + color: Color::rgb8(255, 255, 255), + offset: 0.0, + }, + GradientStop { + color: c.clone(), + offset: 1.0, + }, + ][..]; + let linear = Brush::LinearGradient(LinearGradient { + start: Point::new(0.0, 0.0), + end: Point::new(0.0, 200.0), + stops: stops.into(), + extend: ExtendMode::Pad, + }); + sb.transform(transform); + sb.push_layer(blend, rect.elements()); + // squash the ellipse + let a = transform + * Affine::translate(100., 100.) + * Affine::rotate(std::f32::consts::FRAC_PI_3 * (i * 2 + 1) as f32) + * Affine::scale(1.0, 0.357) + * Affine::translate(-100., -100.); + sb.transform(a); + sb.fill( + Fill::NonZero, + &linear, + None, + make_ellipse(100., 100., 90., 90.), + ); + sb.pop_layer(); + } + sb.pop_layer(); +} + +#[allow(unused)] +pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) { + sb.fill( + Fill::NonZero, + &Brush::Solid(Color::rgb8(128, 128, 128)), + None, + Rect::from_origin_size(Point::new(0.0, 0.0), 1000.0, 1000.0).elements(), + ); + let text_size = 60.0 + 40.0 * (0.01 * i as f32).sin(); + let s = "\u{1f600}hello piet-gpu text!"; + text.add( + sb, + None, + text_size, + None, + Affine::translate(110.0, 600.0), + s, + ); + text.add( + sb, + None, + text_size, + None, + Affine::translate(110.0, 700.0), + s, + ); + sb.transform(Affine::IDENTITY); + let th = (std::f32::consts::PI / 180.0) * (i as f32); + let center = Point::new(500.0, 500.0); + let mut p1 = center; + p1.x += 400.0 * th.cos(); + p1.y += 400.0 * th.sin(); + sb.stroke( + &simple_stroke(5.0), + &Brush::Solid(Color::rgb8(128, 0, 0)), + None, + &[PathElement::MoveTo(center), PathElement::LineTo(p1)], + ); +} + +fn convert_bez_path<'a>(path: &'a BezPath) -> impl Iterator + 'a + Clone { + path.elements() + .iter() + .map(|el| PathElement::from_kurbo(*el)) +} + +fn make_ellipse(cx: f32, cy: f32, rx: f32, ry: f32) -> impl Iterator + Clone { + let a = 0.551915024494; + let arx = a * rx; + let ary = a * ry; + let elements = [ + PathElement::MoveTo(Point::new(cx + rx, cy)), + PathElement::CurveTo( + Point::new(cx + rx, cy + ary), + Point::new(cx + arx, cy + ry), + Point::new(cx, cy + ry), + ), + PathElement::CurveTo( + Point::new(cx - arx, cy + ry), + Point::new(cx - rx, cy + ary), + Point::new(cx - rx, cy), + ), + PathElement::CurveTo( + Point::new(cx - rx, cy - ary), + Point::new(cx - arx, cy - ry), + Point::new(cx, cy - ry), + ), + PathElement::CurveTo( + Point::new(cx + arx, cy - ry), + Point::new(cx + rx, cy - ary), + Point::new(cx + rx, cy), + ), + PathElement::Close, + ]; + (0..elements.len()).map(move |i| elements[i]) +} + +fn make_diamond(origin: Point) -> impl Iterator + Clone { + const SIZE: f32 = 50.0; + let elements = [ + PathElement::MoveTo(Point::new(origin.x, origin.y - SIZE)), + PathElement::LineTo(Point::new(origin.x + SIZE, origin.y)), + PathElement::LineTo(Point::new(origin.x, origin.y + SIZE)), + PathElement::LineTo(Point::new(origin.x - SIZE, origin.y)), + PathElement::Close, + ]; + (0..elements.len()).map(move |i| elements[i]) +} + +fn simple_stroke(width: f32) -> Stroke<[f32; 0]> { + Stroke { + width, + join: Join::Round, + miter_limit: 1.4, + start_cap: Cap::Round, + end_cap: Cap::Round, + dash_pattern: [], + dash_offset: 0.0, + scale: true, + } +} diff --git a/piet-gpu/src/simple_text.rs b/piet-gpu/src/simple_text.rs new file mode 100644 index 0000000..0068b70 --- /dev/null +++ b/piet-gpu/src/simple_text.rs @@ -0,0 +1,82 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +use piet_scene::glyph::{pinot, pinot::TableProvider, GlyphContext}; +use piet_scene::{Affine, Brush, SceneBuilder}; + +pub use pinot::FontRef; + +// This is very much a hack to get things working. +// On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji +const FONT_DATA: &[u8] = include_bytes!("../third-party/Roboto-Regular.ttf"); + +pub struct SimpleText { + gcx: GlyphContext, +} + +impl SimpleText { + pub fn new() -> Self { + Self { + gcx: GlyphContext::new(), + } + } + + pub fn add( + &mut self, + builder: &mut SceneBuilder, + font: Option<&FontRef>, + size: f32, + brush: Option<&Brush>, + transform: Affine, + text: &str, + ) { + let font = font.unwrap_or(&FontRef { + data: FONT_DATA, + offset: 0, + }); + if let Some(cmap) = font.cmap() { + if let Some(hmtx) = font.hmtx() { + let upem = font.head().map(|head| head.units_per_em()).unwrap_or(1000) as f32; + let scale = size / upem; + let vars: [(pinot::types::Tag, f32); 0] = []; + let mut provider = self.gcx.new_provider(font, None, size, false, vars); + let hmetrics = hmtx.hmetrics(); + let default_advance = hmetrics + .get(hmetrics.len().saturating_sub(1)) + .map(|h| h.advance_width) + .unwrap_or(0); + let mut pen_x = 0f32; + for ch in text.chars() { + let gid = cmap.map(ch as u32).unwrap_or(0); + let advance = hmetrics + .get(gid as usize) + .map(|h| h.advance_width) + .unwrap_or(default_advance) as f32 + * scale; + if let Some(glyph) = provider.get(gid, brush) { + if !glyph.is_empty() { + let xform = transform + * Affine::translate(pen_x, 0.0) + * Affine::scale(1.0, -1.0); + builder.append(&glyph, Some(xform)); + } + } + pen_x += advance; + } + } + } + } +} diff --git a/piet-gpu/src/stages/transform.rs b/piet-gpu/src/stages/transform.rs index 0e0a3f6..8e237ba 100644 --- a/piet-gpu/src/stages/transform.rs +++ b/piet-gpu/src/stages/transform.rs @@ -18,8 +18,6 @@ use bytemuck::{Pod, Zeroable}; -use piet::kurbo::Affine; - /// An affine transform. // This is equivalent to the version in piet-gpu-types, but the bytemuck // representation will likely be faster. @@ -35,23 +33,4 @@ impl Transform { mat: [1.0, 0.0, 0.0, 1.0], translate: [0.0, 0.0], }; - - pub fn from_kurbo(a: Affine) -> Transform { - let c = a.as_coeffs(); - Transform { - mat: [c[0] as f32, c[1] as f32, c[2] as f32, c[3] as f32], - translate: [c[4] as f32, c[5] as f32], - } - } - - pub fn to_kurbo(self) -> Affine { - Affine::new([ - self.mat[0] as f64, - self.mat[1] as f64, - self.mat[2] as f64, - self.mat[3] as f64, - self.translate[0] as f64, - self.translate[1] as f64, - ]) - } } diff --git a/piet-gpu/src/test_scenes.rs b/piet-gpu/src/test_scenes.rs deleted file mode 100644 index e3aeaba..0000000 --- a/piet-gpu/src/test_scenes.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! Various synthetic scenes for exercising the renderer. - -use rand::{Rng, RngCore}; - -use crate::{Blend, BlendMode, Colrv1RadialGradient, CompositionMode, PietGpuRenderContext}; -use piet::kurbo::{Affine, BezPath, Circle, Line, Point, Rect, Shape}; -use piet::{ - Color, GradientStop, LinearGradient, Text, TextAttribute, TextLayoutBuilder, UnitPoint, -}; - -use crate::{PicoSvg, RenderContext, Vec2}; - -const N_CIRCLES: usize = 0; - -pub fn render_blend_test(rc: &mut PietGpuRenderContext, i: usize, blend: Blend) { - rc.fill(Rect::new(400., 400., 800., 800.), &Color::rgb8(0, 0, 200)); - rc.save().unwrap(); - rc.blend(Rect::new(0., 0., 1000., 1000.), blend); - rc.transform(Affine::translate(Vec2::new(600., 600.)) * Affine::rotate(0.01 * i as f64)); - rc.fill(Rect::new(0., 0., 400., 400.), &Color::rgba8(255, 0, 0, 255)); - rc.restore().unwrap(); -} - -pub fn render_svg(rc: &mut impl RenderContext, svg: &PicoSvg) { - let start = std::time::Instant::now(); - svg.render(rc); - println!("flattening and encoding time: {:?}", start.elapsed()); -} - -pub fn render_scene(rc: &mut PietGpuRenderContext) { - const WIDTH: usize = 2048; - const HEIGHT: usize = 1536; - let mut rng = rand::thread_rng(); - for _ in 0..N_CIRCLES { - let color = Color::from_rgba32_u32(rng.next_u32()); - let center = Point::new( - rng.gen_range(0.0, WIDTH as f64), - rng.gen_range(0.0, HEIGHT as f64), - ); - let radius = rng.gen_range(0.0, 50.0); - let circle = Circle::new(center, radius); - rc.fill(circle, &color); - } - let _ = rc.save(); - let mut path = BezPath::new(); - path.move_to((200.0, 150.0)); - path.line_to((100.0, 200.0)); - path.line_to((150.0, 250.0)); - path.close_path(); - rc.clip(path); - - let mut path = BezPath::new(); - path.move_to((100.0, 150.0)); - path.line_to((200.0, 200.0)); - path.line_to((150.0, 250.0)); - path.close_path(); - rc.fill(path, &Color::rgb8(128, 0, 128)); - let _ = rc.restore(); - rc.stroke( - piet::kurbo::Line::new((100.0, 100.0), (200.0, 150.0)), - &Color::WHITE, - 5.0, - ); - //render_cardioid(rc); - render_clip_test(rc); - render_alpha_test(rc); - render_gradient_test(rc); - render_text_test(rc); - //render_tiger(rc); -} - -#[allow(unused)] -fn render_cardioid(rc: &mut impl RenderContext) { - let n = 601; - let dth = std::f64::consts::PI * 2.0 / (n as f64); - let center = Point::new(1024.0, 768.0); - let r = 750.0; - let mut path = BezPath::new(); - for i in 1..n { - let p0 = center + Vec2::from_angle(i as f64 * dth) * r; - let p1 = center + Vec2::from_angle(((i * 2) % n) as f64 * dth) * r; - //rc.fill(&Circle::new(p0, 8.0), &Color::WHITE); - path.move_to(p0); - path.line_to(p1); - //rc.stroke(Line::new(p0, p1), &Color::BLACK, 2.0); - } - rc.stroke(&path, &Color::BLACK, 2.0); -} - -#[allow(unused)] -fn render_clip_test(rc: &mut impl RenderContext) { - const N: usize = 16; - const X0: f64 = 50.0; - const Y0: f64 = 450.0; - // Note: if it gets much larger, it will exceed the 1MB scratch buffer. - // But this is a pretty demanding test. - const X1: f64 = 550.0; - const Y1: f64 = 950.0; - let step = 1.0 / ((N + 1) as f64); - for i in 0..N { - let t = ((i + 1) as f64) * step; - rc.save(); - let mut path = BezPath::new(); - path.move_to((X0, Y0)); - path.line_to((X1, Y0)); - path.line_to((X1, Y0 + t * (Y1 - Y0))); - path.line_to((X1 + t * (X0 - X1), Y1)); - path.line_to((X0, Y1)); - path.close_path(); - rc.clip(path); - } - let rect = piet::kurbo::Rect::new(X0, Y0, X1, Y1); - rc.fill(rect, &Color::BLACK); - for _ in 0..N { - rc.restore(); - } -} - -#[allow(unused)] -fn render_alpha_test(rc: &mut impl RenderContext) { - // Alpha compositing tests. - rc.fill( - diamond(Point::new(1024.0, 100.0)), - &Color::Rgba32(0xff0000ff), - ); - rc.fill( - diamond(Point::new(1024.0, 125.0)), - &Color::Rgba32(0x00ff0080), - ); - rc.save(); - rc.clip(diamond(Point::new(1024.0, 150.0))); - rc.fill( - diamond(Point::new(1024.0, 175.0)), - &Color::Rgba32(0x0000ff80), - ); - rc.restore(); -} - -#[allow(unused)] -fn render_gradient_test(rc: &mut PietGpuRenderContext) { - let stops = vec![ - GradientStop { - color: Color::rgb8(0, 255, 0), - pos: 0.0, - }, - GradientStop { - color: Color::BLACK, - pos: 1.0, - }, - ]; - let rad = Colrv1RadialGradient { - center0: Point::new(200.0, 200.0), - center1: Point::new(250.0, 200.0), - radius0: 50.0, - radius1: 100.0, - stops, - }; - let brush = rc.radial_gradient_colrv1(&rad); - //let brush = FixedGradient::Radial(rad); - //let brush = Color::rgb8(0, 128, 0); - let transform = Affine::new([1.0, 0.0, 0.0, 0.5, 0.0, 100.0]); - rc.fill_transform(Rect::new(100.0, 100.0, 300.0, 300.0), &brush, transform); -} - -fn diamond(origin: Point) -> impl Shape { - let mut path = BezPath::new(); - const SIZE: f64 = 50.0; - path.move_to((origin.x, origin.y - SIZE)); - path.line_to((origin.x + SIZE, origin.y)); - path.line_to((origin.x, origin.y + SIZE)); - path.line_to((origin.x - SIZE, origin.y)); - path.close_path(); - return path; -} - -#[allow(unused)] -fn render_text_test(rc: &mut impl RenderContext) { - rc.save(); - //rc.transform(Affine::new([0.2, 0.0, 0.0, -0.2, 200.0, 800.0])); - let layout = rc - .text() - .new_text_layout("\u{1f600}hello piet-gpu text!") - .default_attribute(TextAttribute::FontSize(100.0)) - .build() - .unwrap(); - rc.draw_text(&layout, Point::new(110.0, 600.0)); - rc.draw_text(&layout, Point::new(110.0, 700.0)); - rc.restore(); -} - -#[allow(unused)] -fn render_tiger(rc: &mut impl RenderContext) { - let xml_str = std::str::from_utf8(include_bytes!("../Ghostscript_Tiger.svg")).unwrap(); - let start = std::time::Instant::now(); - let svg = PicoSvg::load(xml_str, 8.0).unwrap(); - println!("parsing time: {:?}", start.elapsed()); - - let start = std::time::Instant::now(); - svg.render(rc); - println!("flattening and encoding time: {:?}", start.elapsed()); -} - -pub fn render_blend_square(rc: &mut PietGpuRenderContext, blend: Blend) { - // Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - let rect = Rect::new(0., 0., 200., 200.); - let stops = vec![ - GradientStop { - color: Color::BLACK, - pos: 0.0, - }, - GradientStop { - color: Color::WHITE, - pos: 1.0, - }, - ]; - let linear = LinearGradient::new(UnitPoint::LEFT, UnitPoint::RIGHT, stops); - rc.fill(rect, &linear); - const GRADIENTS: &[(f64, f64, Color)] = &[ - (150., 0., Color::rgb8(255, 240, 64)), - (175., 100., Color::rgb8(255, 96, 240)), - (125., 200., Color::rgb8(64, 192, 255)), - ]; - for (x, y, c) in GRADIENTS { - let stops = vec![ - GradientStop { - color: c.clone(), - pos: 0.0, - }, - GradientStop { - color: Color::rgba8(0, 0, 0, 0), - pos: 1.0, - }, - ]; - let rad = Colrv1RadialGradient { - center0: Point::new(*x, *y), - center1: Point::new(*x, *y), - radius0: 0.0, - radius1: 100.0, - stops, - }; - let brush = rc.radial_gradient_colrv1(&rad); - rc.fill(Rect::new(0., 0., 200., 200.), &brush); - } - const COLORS: &[Color] = &[ - Color::rgb8(255, 0, 0), - Color::rgb8(0, 255, 0), - Color::rgb8(0, 0, 255), - ]; - let _ = rc.with_save(|rc| { - // Isolation (this can be removed for non-isolated version) - rc.blend(rect, BlendMode::Normal.into()); - for (i, c) in COLORS.iter().enumerate() { - let stops = vec![ - GradientStop { - color: Color::WHITE, - pos: 0.0, - }, - GradientStop { - color: c.clone(), - pos: 1.0, - }, - ]; - // squash the ellipse - let a = Affine::translate((100., 100.)) - * Affine::rotate(std::f64::consts::FRAC_PI_3 * (i * 2 + 1) as f64) - * Affine::scale_non_uniform(1.0, 0.357) - * Affine::translate((-100., -100.)); - let linear = LinearGradient::new(UnitPoint::TOP, UnitPoint::BOTTOM, stops); - let _ = rc.with_save(|rc| { - rc.blend(rect, blend); - rc.transform(a); - rc.fill(Circle::new((100., 100.), 90.), &linear); - Ok(()) - }); - } - Ok(()) - }); -} - -pub fn render_blend_grid(rc: &mut PietGpuRenderContext) { - const BLEND_MODES: &[BlendMode] = &[ - BlendMode::Normal, - BlendMode::Multiply, - BlendMode::Darken, - BlendMode::Screen, - BlendMode::Lighten, - BlendMode::Overlay, - BlendMode::ColorDodge, - BlendMode::ColorBurn, - BlendMode::HardLight, - BlendMode::SoftLight, - BlendMode::Difference, - BlendMode::Exclusion, - BlendMode::Hue, - BlendMode::Saturation, - BlendMode::Color, - BlendMode::Luminosity, - ]; - for (ix, &blend) in BLEND_MODES.iter().enumerate() { - let _ = rc.with_save(|rc| { - let i = ix % 4; - let j = ix / 4; - rc.transform(Affine::translate((i as f64 * 225., j as f64 * 225.))); - render_blend_square(rc, blend.into()); - Ok(()) - }); - } -} - -pub fn render_anim_frame(rc: &mut impl RenderContext, i: usize) { - rc.fill( - Rect::new(0.0, 0.0, 1000.0, 1000.0), - &Color::rgb8(128, 128, 128), - ); - let text_size = 60.0 + 40.0 * (0.01 * i as f64).sin(); - rc.save().unwrap(); - //rc.transform(Affine::new([0.2, 0.0, 0.0, -0.2, 200.0, 800.0])); - let layout = rc - .text() - .new_text_layout("\u{1f600}hello piet-gpu text!") - .default_attribute(TextAttribute::FontSize(text_size)) - .build() - .unwrap(); - rc.draw_text(&layout, Point::new(110.0, 600.0)); - rc.draw_text(&layout, Point::new(110.0, 700.0)); - rc.restore().unwrap(); - let th = (std::f64::consts::PI / 180.0) * (i as f64); - let center = Point::new(500.0, 500.0); - let p1 = center + 400.0 * Vec2::from_angle(th); - let line = Line::new(center, p1); - rc.stroke(line, &Color::rgb8(128, 0, 0), 5.0); -} diff --git a/piet-gpu/src/text.rs b/piet-gpu/src/text.rs deleted file mode 100644 index da8b86e..0000000 --- a/piet-gpu/src/text.rs +++ /dev/null @@ -1,271 +0,0 @@ -use std::ops::RangeBounds; - -use swash::scale::{ScaleContext, Scaler}; -use swash::zeno::{Vector, Verb}; -use swash::{FontRef, GlyphId}; - -use piet::kurbo::{Point, Rect, Size}; -use piet::{ - Error, FontFamily, HitTestPoint, HitTestPosition, LineMetric, RenderContext, Text, - TextAttribute, TextLayout, TextLayoutBuilder, TextStorage, -}; - -use crate::encoder::GlyphEncoder; -use crate::render_ctx; -use crate::stages::Transform; -use crate::PietGpuRenderContext; - -// This is very much a hack to get things working. -// On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji -const FONT_DATA: &[u8] = include_bytes!("../third-party/Roboto-Regular.ttf"); - -#[derive(Clone)] -pub struct Font { - // Storing the font_ref is ok for static font data, but the better way to do - // this is to store the CacheKey. - font_ref: FontRef<'static>, -} - -#[derive(Clone)] -pub struct PietGpuText { - font: Font, -} - -#[derive(Clone)] -pub struct PietGpuTextLayout { - font: Font, - size: f64, - glyphs: Vec, -} - -pub struct PietGpuTextLayoutBuilder { - font: Font, - text: String, - size: f64, -} - -#[derive(Clone, Debug)] -struct Glyph { - glyph_id: GlyphId, - x: f32, - y: f32, -} - -struct TextRenderCtx<'a> { - scaler: Scaler<'a>, -} - -impl PietGpuText { - pub(crate) fn new(font: Font) -> PietGpuText { - PietGpuText { font } - } -} - -impl Text for PietGpuText { - type TextLayout = PietGpuTextLayout; - type TextLayoutBuilder = PietGpuTextLayoutBuilder; - - fn load_font(&mut self, _data: &[u8]) -> Result { - Ok(FontFamily::default()) - } - - fn new_text_layout(&mut self, text: impl TextStorage) -> Self::TextLayoutBuilder { - PietGpuTextLayoutBuilder::new(&self.font, &text.as_str()) - } - - fn font_family(&mut self, _family_name: &str) -> Option { - Some(FontFamily::default()) - } -} - -impl TextLayout for PietGpuTextLayout { - fn size(&self) -> Size { - Size::ZERO - } - - fn image_bounds(&self) -> Rect { - Rect::ZERO - } - - fn line_text(&self, _line_number: usize) -> Option<&str> { - None - } - - fn line_metric(&self, _line_number: usize) -> Option { - None - } - - fn line_count(&self) -> usize { - 0 - } - - fn hit_test_point(&self, _point: Point) -> HitTestPoint { - HitTestPoint::default() - } - - fn hit_test_text_position(&self, _text_position: usize) -> HitTestPosition { - HitTestPosition::default() - } - - fn text(&self) -> &str { - "" - } -} - -impl Font { - pub fn new() -> Font { - let font_ref = FontRef::from_index(FONT_DATA, 0).expect("error parsing font"); - Font { font_ref } - } - - fn make_path<'a>(&self, glyph_id: GlyphId, tc: &mut TextRenderCtx<'a>) -> GlyphEncoder { - let mut encoder = GlyphEncoder::default(); - if tc.scaler.has_color_outlines() { - if let Some(outline) = tc.scaler.scale_color_outline(glyph_id) { - // TODO: be more sophisticated choosing a palette - let palette = self.font_ref.color_palettes().next().unwrap(); - let mut i = 0; - while let Some(layer) = outline.get(i) { - if let Some(color_ix) = layer.color_index() { - let color = palette.get(color_ix); - append_outline(&mut encoder, layer.verbs(), layer.points()); - encoder.fill_color(*bytemuck::from_bytes(&color)); - } - i += 1; - } - return encoder; - } - } - if let Some(outline) = tc.scaler.scale_outline(glyph_id) { - append_outline(&mut encoder, outline.verbs(), outline.points()); - } - encoder - } -} - -impl PietGpuTextLayout { - pub(crate) fn make_layout(font: &Font, text: &str, size: f64) -> PietGpuTextLayout { - let mut glyphs = Vec::new(); - let mut x = 0.0; - let y = 0.0; - for c in text.chars() { - let glyph_id = font.font_ref.charmap().map(c); - let glyph = Glyph { glyph_id, x, y }; - glyphs.push(glyph); - let adv = font.font_ref.glyph_metrics(&[]).advance_width(glyph_id); - x += adv; - } - PietGpuTextLayout { - glyphs, - font: font.clone(), - size, - } - } - - pub(crate) fn draw_text(&self, ctx: &mut PietGpuRenderContext, pos: Point) { - let mut scale_ctx = ScaleContext::new(); - let scaler = scale_ctx.builder(self.font.font_ref).size(2048.).build(); - let mut tc = TextRenderCtx { scaler }; - // Should we use ppem from font, or let swash scale? - const DEFAULT_UPEM: u16 = 2048; - let scale = self.size as f32 / DEFAULT_UPEM as f32; - ctx.save().unwrap(); - // TODO: handle y offsets also - for glyph in &self.glyphs { - let tpos = render_ctx::to_f32_2(pos); - let transform = Transform { - mat: [scale, 0.0, 0.0, -scale], - translate: [tpos[0] + scale * glyph.x, tpos[1]], - }; - //println!("{:?}, {:?}", transform.mat, transform.translate); - ctx.encode_transform(transform); - let glyph = self.font.make_path(glyph.glyph_id, &mut tc); - ctx.encode_glyph(&glyph); - if !glyph.is_color() { - ctx.fill_glyph(0xff_ff_ff_ff); - } - } - ctx.restore().unwrap(); - } -} - -impl PietGpuTextLayoutBuilder { - pub(crate) fn new(font: &Font, text: &str) -> PietGpuTextLayoutBuilder { - PietGpuTextLayoutBuilder { - font: font.clone(), - text: text.to_owned(), - size: 12.0, - } - } -} - -impl TextLayoutBuilder for PietGpuTextLayoutBuilder { - type Out = PietGpuTextLayout; - - fn max_width(self, _width: f64) -> Self { - self - } - - fn alignment(self, _alignment: piet::TextAlignment) -> Self { - self - } - - fn default_attribute(mut self, attribute: impl Into) -> Self { - let attribute = attribute.into(); - match attribute { - TextAttribute::FontSize(size) => self.size = size, - _ => (), - } - self - } - - fn range_attribute( - self, - _range: impl RangeBounds, - _attribute: impl Into, - ) -> Self { - self - } - - fn build(self) -> Result { - Ok(PietGpuTextLayout::make_layout( - &self.font, &self.text, self.size, - )) - } -} - -pub(crate) fn append_outline(encoder: &mut GlyphEncoder, verbs: &[Verb], points: &[Vector]) { - let mut path_encoder = encoder.path_encoder(); - let mut i = 0; - for verb in verbs { - match verb { - Verb::MoveTo => { - let p = points[i]; - path_encoder.move_to(p.x, p.y); - i += 1; - } - Verb::LineTo => { - let p = points[i]; - path_encoder.line_to(p.x, p.y); - i += 1; - } - Verb::QuadTo => { - let p1 = points[i]; - let p2 = points[i + 1]; - path_encoder.quad_to(p1.x, p1.y, p2.x, p2.y); - i += 2; - } - Verb::CurveTo => { - let p1 = points[i]; - let p2 = points[i + 1]; - let p3 = points[i + 2]; - path_encoder.cubic_to(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); - i += 3; - } - Verb::Close => path_encoder.close_path(), - } - } - path_encoder.path(); - let n_pathseg = path_encoder.n_pathseg(); - encoder.finish_path(n_pathseg); -} diff --git a/piet-scene/src/brush/color.rs b/piet-scene/src/brush/color.rs index a377888..59ffae5 100644 --- a/piet-scene/src/brush/color.rs +++ b/piet-scene/src/brush/color.rs @@ -14,6 +14,7 @@ // // Also licensed under MIT license, at your choice. +/// 32-bit RGBA color. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] pub struct Color { pub r: u8, diff --git a/piet-scene/src/brush/gradient.rs b/piet-scene/src/brush/gradient.rs index 161604b..a010ad5 100644 --- a/piet-scene/src/brush/gradient.rs +++ b/piet-scene/src/brush/gradient.rs @@ -15,17 +15,19 @@ // Also licensed under MIT license, at your choice. use super::color::Color; +use super::ExtendMode; use crate::geometry::Point; use smallvec::SmallVec; use std::hash::{Hash, Hasher}; +/// Offset and color of a transition point in a gradient. #[derive(Copy, Clone, PartialOrd, Default, Debug)] -pub struct Stop { +pub struct GradientStop { pub offset: f32, pub color: Color, } -impl Hash for Stop { +impl Hash for GradientStop { fn hash(&self, state: &mut H) { self.offset.to_bits().hash(state); self.color.hash(state); @@ -33,46 +35,46 @@ impl Hash for Stop { } // Override PartialEq to use to_bits for the offset to match with the Hash impl -impl std::cmp::PartialEq for Stop { +impl std::cmp::PartialEq for GradientStop { fn eq(&self, other: &Self) -> bool { self.offset.to_bits() == other.offset.to_bits() && self.color == other.color } } -impl std::cmp::Eq for Stop {} +impl std::cmp::Eq for GradientStop {} -pub type StopVec = SmallVec<[Stop; 4]>; - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum Extend { - Pad, - Repeat, - Reflect, -} +/// Collection of gradient stops. +pub type GradientStops = SmallVec<[GradientStop; 4]>; +/// Definition of a gradient that transitions between two or more colors along +/// a line. #[derive(Clone, Debug)] pub struct LinearGradient { pub start: Point, pub end: Point, - pub stops: StopVec, - pub extend: Extend, + pub stops: GradientStops, + pub extend: ExtendMode, } +/// Definition of a gradient that transitions between two or more colors that +/// radiate from an origin. #[derive(Clone, Debug)] pub struct RadialGradient { pub center0: Point, pub radius0: f32, pub center1: Point, pub radius1: f32, - pub stops: StopVec, - pub extend: Extend, + pub stops: GradientStops, + pub extend: ExtendMode, } +/// Definition gradient that transitions between two or more colors that rotate +/// around a center point. #[derive(Clone, Debug)] pub struct SweepGradient { pub center: Point, pub start_angle: f32, pub end_angle: f32, - pub stops: StopVec, - pub extend: Extend, + pub stops: GradientStops, + pub extend: ExtendMode, } diff --git a/piet-scene/src/brush/image.rs b/piet-scene/src/brush/image.rs index 07157e7..737ca94 100644 --- a/piet-scene/src/brush/image.rs +++ b/piet-scene/src/brush/image.rs @@ -18,53 +18,35 @@ use std::result::Result; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Format { - A8, - Rgba8, -} - -impl Format { - pub fn data_size(self, width: u32, height: u32) -> Option { - (width as usize) - .checked_mul(height as usize) - .and_then(|size| { - size.checked_mul(match self { - Self::A8 => 1, - Self::Rgba8 => 4, - }) - }) - } -} - +/// Image data resource. #[derive(Clone, Debug)] pub struct Image(Arc); #[derive(Clone, Debug)] struct Inner { id: u64, - format: Format, width: u32, height: u32, - data: Vec, + data: Arc<[u8]>, } impl Image { pub fn new( - format: Format, width: u32, height: u32, - mut data: Vec, - ) -> Result { - let data_size = format.data_size(width, height).ok_or(DataSizeError)?; + data: impl Into>, + ) -> Result { + let data_size = width + .checked_mul(height) + .and_then(|x| x.checked_mul(4)) + .ok_or(ImageDataSizeError)? as usize; + let data = data.into(); if data.len() < data_size { - return Err(DataSizeError); + return Err(ImageDataSizeError); } - data.truncate(data_size); static ID: AtomicU64 = AtomicU64::new(1); Ok(Self(Arc::new(Inner { id: ID.fetch_add(1, Ordering::Relaxed), - format, width, height, data, @@ -75,10 +57,6 @@ impl Image { self.0.id } - pub fn format(&self) -> Format { - self.0.format - } - pub fn width(&self) -> u32 { self.0.width } @@ -92,5 +70,7 @@ impl Image { } } +/// Error returned when image data size is not sufficient for the specified +/// dimensions. #[derive(Clone, Debug)] -pub struct DataSizeError; +pub struct ImageDataSizeError; diff --git a/piet-scene/src/brush/mod.rs b/piet-scene/src/brush/mod.rs index 9cde1fb..1d7a674 100644 --- a/piet-scene/src/brush/mod.rs +++ b/piet-scene/src/brush/mod.rs @@ -22,8 +22,7 @@ pub use color::Color; pub use gradient::*; pub use image::*; -use crate::resource::PersistentBrush; - +/// Describes the content of a filled or stroked shape. #[derive(Clone, Debug)] pub enum Brush { Solid(Color), @@ -31,5 +30,31 @@ pub enum Brush { RadialGradient(RadialGradient), SweepGradient(SweepGradient), Image(Image), - Persistent(PersistentBrush), +} + +/// Defines how a brush is extended when the content does not +/// completely fill a shape. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum ExtendMode { + Pad, + Repeat, + Reflect, +} + +impl From for Brush { + fn from(c: Color) -> Self { + Self::Solid(c) + } +} + +impl From for Brush { + fn from(g: LinearGradient) -> Self { + Self::LinearGradient(g) + } +} + +impl From for Brush { + fn from(g: RadialGradient) -> Self { + Self::RadialGradient(g) + } } diff --git a/piet-scene/src/geometry.rs b/piet-scene/src/geometry.rs index 2df7f83..dc76fe8 100644 --- a/piet-scene/src/geometry.rs +++ b/piet-scene/src/geometry.rs @@ -201,6 +201,14 @@ impl Rect { } } + /// Creates a new rectangle from an origin point and dimensions. + pub fn from_origin_size(origin: Point, width: f32, height: f32) -> Self { + Self { + min: origin, + max: Point::new(origin.x + width, origin.y + height), + } + } + /// Returns the width of the rectangle. pub fn width(&self) -> f32 { self.max.x - self.min.x diff --git a/piet-scene/src/glyph/mod.rs b/piet-scene/src/glyph.rs similarity index 85% rename from piet-scene/src/glyph/mod.rs rename to piet-scene/src/glyph.rs index f6ebf14..8eb0627 100644 --- a/piet-scene/src/glyph/mod.rs +++ b/piet-scene/src/glyph.rs @@ -14,12 +14,14 @@ // // Also licensed under MIT license, at your choice. +//! Support for glyph rendering. + pub use moscato::pinot; use crate::brush::{Brush, Color}; use crate::geometry::Affine; -use crate::path::Element; -use crate::scene::{build_fragment, Fill, Fragment}; +use crate::path::PathElement; +use crate::scene::{Fill, SceneBuilder, SceneFragment}; use moscato::{Context, Scaler}; use pinot::{types::Tag, FontRef}; @@ -81,28 +83,29 @@ pub struct GlyphProvider<'a> { impl<'a> GlyphProvider<'a> { /// Returns a scene fragment containing the commands to render the /// specified glyph. - pub fn get(&mut self, gid: u16) -> Option { + pub fn get(&mut self, gid: u16, brush: Option<&Brush>) -> Option { let glyph = self.scaler.glyph(gid)?; let path = glyph.path(0)?; - let mut fragment = Fragment::default(); - let mut builder = build_fragment(&mut fragment); + let mut fragment = SceneFragment::default(); + let mut builder = SceneBuilder::for_fragment(&mut fragment); builder.fill( Fill::NonZero, - &Brush::Solid(Color::rgb8(255, 255, 255)), + brush.unwrap_or(&Brush::Solid(Color::rgb8(255, 255, 255))), None, convert_path(path.elements()), ); + builder.finish(); Some(fragment) } /// Returns a scene fragment containing the commands and resources to /// render the specified color glyph. - pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option { + pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option { use crate::geometry::*; use moscato::Command; let glyph = self.scaler.color_glyph(palette_index, gid)?; - let mut fragment = Fragment::default(); - let mut builder = build_fragment(&mut fragment); + let mut fragment = SceneFragment::default(); + let mut builder = SceneBuilder::for_fragment(&mut fragment); let mut xform_stack: SmallVec<[Affine; 8]> = SmallVec::new(); for command in glyph.commands() { match command { @@ -171,59 +174,64 @@ impl<'a> GlyphProvider<'a> { ); } } - Command::Fill(brush, brush_xform) => { + Command::Fill(_brush, _brush_xform) => { // TODO: this needs to compute a bounding box for // the parent clips } } } + builder.finish(); Some(fragment) } } fn convert_path( path: impl Iterator + Clone, -) -> impl Iterator + Clone { +) -> impl Iterator + Clone { use crate::geometry::Point; path.map(|el| match el { - moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y)), - moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y)), + moscato::Element::MoveTo(p0) => PathElement::MoveTo(Point::new(p0.x, p0.y)), + moscato::Element::LineTo(p0) => PathElement::LineTo(Point::new(p0.x, p0.y)), moscato::Element::QuadTo(p0, p1) => { - Element::QuadTo(Point::new(p0.x, p0.y), Point::new(p1.x, p1.y)) + PathElement::QuadTo(Point::new(p0.x, p0.y), Point::new(p1.x, p1.y)) } - moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo( + moscato::Element::CurveTo(p0, p1, p2) => PathElement::CurveTo( Point::new(p0.x, p0.y), Point::new(p1.x, p1.y), Point::new(p2.x, p2.y), ), - moscato::Element::Close => Element::Close, + moscato::Element::Close => PathElement::Close, }) } fn convert_transformed_path( path: impl Iterator + Clone, xform: &Affine, -) -> impl Iterator + Clone { +) -> impl Iterator + Clone { use crate::geometry::Point; let xform = *xform; path.map(move |el| match el { - moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y).transform(&xform)), - moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y).transform(&xform)), - moscato::Element::QuadTo(p0, p1) => Element::QuadTo( + moscato::Element::MoveTo(p0) => { + PathElement::MoveTo(Point::new(p0.x, p0.y).transform(&xform)) + } + moscato::Element::LineTo(p0) => { + PathElement::LineTo(Point::new(p0.x, p0.y).transform(&xform)) + } + moscato::Element::QuadTo(p0, p1) => PathElement::QuadTo( Point::new(p0.x, p0.y).transform(&xform), Point::new(p1.x, p1.y).transform(&xform), ), - moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo( + moscato::Element::CurveTo(p0, p1, p2) => PathElement::CurveTo( Point::new(p0.x, p0.y).transform(&xform), Point::new(p1.x, p1.y).transform(&xform), Point::new(p2.x, p2.y).transform(&xform), ), - moscato::Element::Close => Element::Close, + moscato::Element::Close => PathElement::Close, }) } -fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::Blend { - use crate::scene::{Blend, Compose, Mix}; +fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::BlendMode { + use crate::scene::{BlendMode, Compose, Mix}; use moscato::CompositeMode; let mut mix = Mix::Normal; let mut compose = Compose::SrcOver; @@ -257,7 +265,7 @@ fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::Blend { CompositeMode::HslColor => mix = Mix::Color, CompositeMode::HslLuminosity => mix = Mix::Luminosity, } - Blend { mix, compose } + BlendMode { mix, compose } } fn convert_transform(xform: &moscato::Transform) -> crate::geometry::Affine { @@ -298,11 +306,11 @@ fn convert_brush(brush: &moscato::Brush) -> crate::brush::Brush { } } -fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::StopVec { - use crate::brush::Stop; +fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::GradientStops { + use crate::brush::GradientStop; stops .iter() - .map(|stop| Stop { + .map(|stop| GradientStop { offset: stop.offset, color: Color { r: stop.color.r, @@ -314,8 +322,8 @@ fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::StopVec { .collect() } -fn convert_extend(extend: moscato::ExtendMode) -> crate::brush::Extend { - use crate::brush::Extend::*; +fn convert_extend(extend: moscato::ExtendMode) -> crate::brush::ExtendMode { + use crate::brush::ExtendMode::*; match extend { moscato::ExtendMode::Pad => Pad, moscato::ExtendMode::Repeat => Repeat, diff --git a/piet-scene/src/lib.rs b/piet-scene/src/lib.rs index 8f436b2..4e0729b 100644 --- a/piet-scene/src/lib.rs +++ b/piet-scene/src/lib.rs @@ -14,19 +14,26 @@ // // Also licensed under MIT license, at your choice. -pub mod brush; -pub mod geometry; +mod brush; +mod geometry; +mod path; +mod resource; +mod scene; + pub mod glyph; -pub mod path; -pub mod resource; -pub mod scene; + +pub use brush::*; +pub use geometry::*; +pub use path::*; +pub use resource::*; +pub use scene::*; /// Implement conversions to and from Kurbo types when the `kurbo` feature is /// enabled. #[cfg(feature = "kurbo")] mod kurbo_conv { use super::geometry::{Affine, Point, Rect}; - use super::path::Element; + use super::path::PathElement; impl Point { /// Creates a new point from the equivalent kurbo type. @@ -90,26 +97,27 @@ mod kurbo_conv { } } - impl Element { + impl PathElement { /// Creates a new path element from the equivalent kurbo type. pub fn from_kurbo(el: kurbo::PathEl) -> Self { use kurbo::PathEl::*; - use Point::from_kurbo; - match e { - MoveTo(p0) => Self::MoveTo(from_kurbo(p0)), - LineTo(p0) => Self::LineTo(from_kurbo(p0)), - QuadTo(p0, p1) => Self::QuadTo(from_kurbo(p0), from_kurbo(p1)), - CurveTo(p0, p1, p2) => { - Self::CurveTo(from_kurbo(p0), from_kurbo(p1), from_kurbo(p2)) - } + match el { + MoveTo(p0) => Self::MoveTo(Point::from_kurbo(p0)), + LineTo(p0) => Self::LineTo(Point::from_kurbo(p0)), + QuadTo(p0, p1) => Self::QuadTo(Point::from_kurbo(p0), Point::from_kurbo(p1)), + CurveTo(p0, p1, p2) => Self::CurveTo( + Point::from_kurbo(p0), + Point::from_kurbo(p1), + Point::from_kurbo(p2), + ), ClosePath => Self::Close, } } } - impl From for kurbo::PathEl { - fn from(e: Element) -> Self { - use Element::*; + impl From for kurbo::PathEl { + fn from(e: PathElement) -> Self { + use PathElement::*; match e { MoveTo(p0) => Self::MoveTo(p0.into()), LineTo(p0) => Self::LineTo(p0.into()), diff --git a/piet-scene/src/path.rs b/piet-scene/src/path.rs index 3ad0ac1..bccf656 100644 --- a/piet-scene/src/path.rs +++ b/piet-scene/src/path.rs @@ -18,7 +18,7 @@ use super::geometry::{Point, Rect}; /// Action of a path element. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Verb { +pub enum PathVerb { MoveTo, LineTo, QuadTo, @@ -28,7 +28,7 @@ pub enum Verb { /// Element of a path represented by a verb and its associated points. #[derive(Copy, Clone, PartialEq, Debug)] -pub enum Element { +pub enum PathElement { MoveTo(Point), LineTo(Point), QuadTo(Point, Point), @@ -36,27 +36,27 @@ pub enum Element { Close, } -impl Element { +impl PathElement { /// Returns the verb that describes the action of the path element. - pub fn verb(&self) -> Verb { + pub fn verb(&self) -> PathVerb { match self { - Self::MoveTo(..) => Verb::MoveTo, - Self::LineTo(..) => Verb::LineTo, - Self::QuadTo(..) => Verb::QuadTo, - Self::CurveTo(..) => Verb::CurveTo, - Self::Close => Verb::Close, + Self::MoveTo(..) => PathVerb::MoveTo, + Self::LineTo(..) => PathVerb::LineTo, + Self::QuadTo(..) => PathVerb::QuadTo, + Self::CurveTo(..) => PathVerb::CurveTo, + Self::Close => PathVerb::Close, } } } impl Rect { - pub fn elements(&self) -> impl Iterator + Clone { + pub fn elements(&self) -> impl Iterator + Clone { let elements = [ - Element::MoveTo((self.min.x, self.min.y).into()), - Element::LineTo((self.max.x, self.min.y).into()), - Element::LineTo((self.max.x, self.max.y).into()), - Element::LineTo((self.min.x, self.max.y).into()), - Element::Close, + PathElement::MoveTo((self.min.x, self.min.y).into()), + PathElement::LineTo((self.max.x, self.min.y).into()), + PathElement::LineTo((self.max.x, self.max.y).into()), + PathElement::LineTo((self.min.x, self.max.y).into()), + PathElement::Close, ]; (0..5).map(move |i| elements[i]) } diff --git a/piet-scene/src/resource/gradient.rs b/piet-scene/src/resource/gradient.rs index 39b7f40..8a23216 100644 --- a/piet-scene/src/resource/gradient.rs +++ b/piet-scene/src/resource/gradient.rs @@ -1,4 +1,4 @@ -use crate::brush::{Color, Stop, StopVec}; +use crate::brush::{Color, GradientStop, GradientStops}; use std::collections::HashMap; const N_SAMPLES: usize = 512; @@ -7,15 +7,11 @@ const RETAINED_COUNT: usize = 64; #[derive(Default)] pub struct RampCache { epoch: u64, - map: HashMap, + map: HashMap, data: Vec, } impl RampCache { - pub fn new() -> Self { - Self::default() - } - pub fn advance(&mut self) { self.epoch += 1; if self.map.len() > RETAINED_COUNT { @@ -31,7 +27,7 @@ impl RampCache { self.data.clear(); } - pub fn add(&mut self, stops: &[Stop]) -> u32 { + pub fn add(&mut self, stops: &[GradientStop]) -> u32 { if let Some(entry) = self.map.get_mut(stops) { entry.1 = self.epoch; entry.0 @@ -73,7 +69,7 @@ impl RampCache { } } -fn make_ramp<'a>(stops: &'a [Stop]) -> impl Iterator + 'a { +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; diff --git a/piet-scene/src/resource/mod.rs b/piet-scene/src/resource/mod.rs index a1ea58b..2e6b2d6 100644 --- a/piet-scene/src/resource/mod.rs +++ b/piet-scene/src/resource/mod.rs @@ -1,14 +1,12 @@ mod gradient; -use crate::brush::{Brush, Stop}; +use crate::brush::GradientStop; use gradient::RampCache; -use std::collections::HashMap; /// Context for caching resources across rendering operations. #[derive(Default)] pub struct ResourceContext { ramps: RampCache, - persistent_map: HashMap, } impl ResourceContext { @@ -22,35 +20,13 @@ impl ResourceContext { pub fn clear(&mut self) { self.ramps.clear(); - self.persistent_map.clear(); } - pub fn add_ramp(&mut self, stops: &[Stop]) -> u32 { + pub fn add_ramp(&mut self, stops: &[GradientStop]) -> u32 { self.ramps.add(stops) } - pub fn create_brush(&mut self, brush: &Brush) -> PersistentBrush { - match brush { - Brush::Persistent(dup) => return *dup, - _ => {} - } - PersistentBrush { kind: 0, id: 0 } - } - - pub fn destroy_brush(&mut self, brush: PersistentBrush) {} - pub fn ramp_data(&self) -> &[u32] { &self.ramps.data() } } - -/// Handle for a brush that is managed by the resource context. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct PersistentBrush { - kind: u8, - id: u64, -} - -struct PersistentBrushData { - brush: Brush, -} diff --git a/piet-scene/src/scene/blend.rs b/piet-scene/src/scene/blend.rs index d6aa080..43ecfa7 100644 --- a/piet-scene/src/scene/blend.rs +++ b/piet-scene/src/scene/blend.rs @@ -60,12 +60,12 @@ pub enum Compose { /// Blend mode consisting of mixing and composition functions. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct Blend { +pub struct BlendMode { pub mix: Mix, pub compose: Compose, } -impl Blend { +impl BlendMode { pub fn new(mix: Mix, compose: Compose) -> Self { Self { mix, compose } } @@ -75,7 +75,7 @@ impl Blend { } } -impl Default for Blend { +impl Default for BlendMode { fn default() -> Self { Self { mix: Mix::Clip, @@ -84,7 +84,7 @@ impl Default for Blend { } } -impl From for Blend { +impl From for BlendMode { fn from(mix: Mix) -> Self { Self { mix, @@ -93,7 +93,7 @@ impl From for Blend { } } -impl From for Blend { +impl From for BlendMode { fn from(compose: Compose) -> Self { Self { mix: Mix::Normal, diff --git a/piet-scene/src/scene/builder.rs b/piet-scene/src/scene/builder.rs index 5394f88..37befac 100644 --- a/piet-scene/src/scene/builder.rs +++ b/piet-scene/src/scene/builder.rs @@ -15,37 +15,39 @@ // Also licensed under MIT license, at your choice. use super::style::{Fill, Stroke}; -use super::{Affine, Blend, Element, Fragment, FragmentResources, ResourcePatch, Scene, SceneData}; +use super::{ + Affine, BlendMode, FragmentResources, PathElement, ResourcePatch, Scene, SceneData, + SceneFragment, +}; use crate::brush::*; use crate::resource::ResourceContext; use bytemuck::{Pod, Zeroable}; use core::borrow::Borrow; - -const MAX_BLEND_STACK: usize = 256; - -/// Creates a new builder for filling a scene. Any current content in the scene -/// will be cleared. -pub fn build_scene<'a>(scene: &'a mut Scene, rcx: &'a mut ResourceContext) -> Builder<'a> { - Builder::new(&mut scene.data, ResourceData::Scene(rcx)) -} - -/// Creates a new builder for filling a scene fragment. Any current content in -/// the fragment will be cleared. -pub fn build_fragment<'a>(fragment: &'a mut Fragment) -> Builder<'a> { - Builder::new( - &mut fragment.data, - ResourceData::Fragment(&mut fragment.resources), - ) -} +use smallvec::SmallVec; /// Builder for constructing a scene or scene fragment. -pub struct Builder<'a> { +pub struct SceneBuilder<'a> { scene: &'a mut SceneData, resources: ResourceData<'a>, - layers: Vec, + layers: SmallVec<[BlendMode; 8]>, } -impl<'a> Builder<'a> { +impl<'a> SceneBuilder<'a> { + /// Creates a new builder for filling a scene. Any current content in the scene + /// will be cleared. + pub fn for_scene(scene: &'a mut Scene, rcx: &'a mut ResourceContext) -> Self { + Self::new(&mut scene.data, ResourceData::Scene(rcx)) + } + + /// Creates a new builder for filling a scene fragment. Any current content in + /// the fragment will be cleared. + pub fn for_fragment(fragment: &'a mut SceneFragment) -> Self { + Self::new( + &mut fragment.data, + ResourceData::Fragment(&mut fragment.resources), + ) + } + /// Creates a new builder for constructing a scene. fn new(scene: &'a mut SceneData, mut resources: ResourceData<'a>) -> Self { let is_fragment = match resources { @@ -57,30 +59,29 @@ impl<'a> Builder<'a> { Self { scene, resources, - layers: vec![], + layers: Default::default(), } } /// Sets the current transformation. pub fn transform(&mut self, transform: Affine) { - self.encode_transform(transform); + if self.scene.transform_stream.last() != Some(&transform) { + self.encode_transform(transform); + } } /// Pushes a new layer bound by the specifed shape and composed with /// previous layers using the specified blend mode. - pub fn push_layer<'s, E>(&mut self, blend: Blend, elements: E) + pub fn push_layer<'s, E>(&mut self, blend: BlendMode, elements: E) where E: IntoIterator, E::IntoIter: Clone, - E::Item: Borrow, + E::Item: Borrow, { self.linewidth(-1.0); let elements = elements.into_iter(); self.encode_path(elements, true); self.begin_clip(Some(blend)); - if self.layers.len() >= MAX_BLEND_STACK { - panic!("Maximum clip/blend stack size {} exceeded", MAX_BLEND_STACK); - } self.layers.push(blend); } @@ -94,32 +95,33 @@ impl<'a> Builder<'a> { /// Fills a shape using the specified style and brush. pub fn fill<'s, E>( &mut self, - style: Fill, + _style: Fill, brush: &Brush, brush_transform: Option, elements: E, ) where E: IntoIterator, E::IntoIter: Clone, - E::Item: Borrow, + E::Item: Borrow, { self.linewidth(-1.0); let elements = elements.into_iter(); - self.encode_path(elements, true); - if let Some(brush_transform) = brush_transform { - if let Some(last_transform) = self.scene.transform_stream.last().copied() { - self.encode_transform(brush_transform * last_transform); - self.swap_last_tags(); - self.encode_brush(brush); - self.encode_transform(last_transform); + if self.encode_path(elements, true) { + if let Some(brush_transform) = brush_transform { + if let Some(last_transform) = self.scene.transform_stream.last().copied() { + self.encode_transform(brush_transform * last_transform); + self.swap_last_tags(); + self.encode_brush(brush); + self.encode_transform(last_transform); + } else { + self.encode_transform(brush_transform); + self.swap_last_tags(); + self.encode_brush(brush); + self.encode_transform(Affine::IDENTITY); + } } else { - self.encode_transform(brush_transform); - self.swap_last_tags(); self.encode_brush(brush); - self.encode_transform(Affine::IDENTITY); } - } else { - self.encode_brush(brush); } } @@ -134,37 +136,38 @@ impl<'a> Builder<'a> { D: Borrow<[f32]>, E: IntoIterator, E::IntoIter: Clone, - E::Item: Borrow, + E::Item: Borrow, { self.linewidth(style.width); let elements = elements.into_iter(); - self.encode_path(elements, false); - if let Some(brush_transform) = brush_transform { - if let Some(last_transform) = self.scene.transform_stream.last().copied() { - self.encode_transform(brush_transform * last_transform); - self.swap_last_tags(); - self.encode_brush(brush); - self.encode_transform(last_transform); + if self.encode_path(elements, false) { + if let Some(brush_transform) = brush_transform { + if let Some(last_transform) = self.scene.transform_stream.last().copied() { + self.encode_transform(brush_transform * last_transform); + self.swap_last_tags(); + self.encode_brush(brush); + self.encode_transform(last_transform); + } else { + self.encode_transform(brush_transform); + self.swap_last_tags(); + self.encode_brush(brush); + self.encode_transform(Affine::IDENTITY); + } } else { - self.encode_transform(brush_transform); - self.swap_last_tags(); self.encode_brush(brush); - self.encode_transform(Affine::IDENTITY); } - } else { - self.encode_brush(brush); } } /// Appends a fragment to the scene. - pub fn append(&mut self, fragment: &Fragment, transform: Option) { + pub fn append(&mut self, fragment: &SceneFragment, transform: Option) { let drawdata_base = self.scene.drawdata_stream.len(); let mut cur_transform = self.scene.transform_stream.last().copied(); if let Some(transform) = transform { if cur_transform.is_none() { cur_transform = Some(Affine::IDENTITY); } - self.encode_transform(transform); + self.transform(transform); } else if cur_transform != Some(Affine::IDENTITY) { self.encode_transform(Affine::IDENTITY); } @@ -204,7 +207,7 @@ impl<'a> Builder<'a> { } // Prevent fragments from affecting transform state. Should we allow this? if let Some(transform) = cur_transform { - self.encode_transform(transform); + self.transform(transform); } } @@ -216,11 +219,11 @@ impl<'a> Builder<'a> { } } -impl<'a> Builder<'a> { - fn encode_path(&mut self, elements: E, is_fill: bool) +impl<'a> SceneBuilder<'a> { + fn encode_path(&mut self, elements: E, is_fill: bool) -> bool where E: Iterator, - E::Item: Borrow, + E::Item: Borrow, { if is_fill { self.encode_path_inner( @@ -228,34 +231,39 @@ impl<'a> Builder<'a> { .map(|el| *el.borrow()) .flat_map(|el| { match el { - Element::MoveTo(..) => Some(Element::Close), + PathElement::MoveTo(..) => Some(PathElement::Close), _ => None, } .into_iter() .chain(Some(el)) }) - .chain(Some(Element::Close)), + .chain(Some(PathElement::Close)), ) } else { self.encode_path_inner(elements.map(|el| *el.borrow())) } } - fn encode_path_inner(&mut self, elements: impl Iterator) { + fn encode_path_inner(&mut self, elements: impl Iterator) -> bool { let mut b = PathBuilder::new(&mut self.scene.tag_stream, &mut self.scene.pathseg_stream); + let mut has_els = false; for el in elements { match el { - Element::MoveTo(p0) => b.move_to(p0.x, p0.y), - Element::LineTo(p0) => b.line_to(p0.x, p0.y), - Element::QuadTo(p0, p1) => b.quad_to(p0.x, p0.y, p1.x, p1.y), - Element::CurveTo(p0, p1, p2) => b.cubic_to(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y), - Element::Close => b.close_path(), + PathElement::MoveTo(p0) => b.move_to(p0.x, p0.y), + PathElement::LineTo(p0) => b.line_to(p0.x, p0.y), + PathElement::QuadTo(p0, p1) => b.quad_to(p0.x, p0.y, p1.x, p1.y), + PathElement::CurveTo(p0, p1, p2) => b.cubic_to(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y), + PathElement::Close => b.close_path(), } + has_els = true; } - b.path(); - let n_pathseg = b.n_pathseg(); - self.scene.n_path += 1; - self.scene.n_pathseg += n_pathseg; + if has_els { + b.path(); + let n_pathseg = b.n_pathseg(); + self.scene.n_path += 1; + self.scene.n_pathseg += n_pathseg; + } + has_els } fn encode_transform(&mut self, transform: Affine) { @@ -272,8 +280,10 @@ impl<'a> Builder<'a> { // -1.0 means "fill" fn linewidth(&mut self, linewidth: f32) { - self.scene.tag_stream.push(0x40); - self.scene.linewidth_stream.push(linewidth); + if self.scene.linewidth_stream.last() != Some(&linewidth) { + self.scene.tag_stream.push(0x40); + self.scene.linewidth_stream.push(linewidth); + } } fn encode_brush(&mut self, brush: &Brush) { @@ -311,11 +321,10 @@ impl<'a> Builder<'a> { } Brush::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"), Brush::Image(_image) => todo!("images aren't done yet!"), - Brush::Persistent(_) => todo!("persistent brushes aren't done yet!"), } } - fn add_ramp(&mut self, stops: &[Stop]) -> u32 { + fn add_ramp(&mut self, stops: &[GradientStop]) -> u32 { match &mut self.resources { ResourceData::Scene(res) => res.add_ramp(stops), ResourceData::Fragment(res) => { @@ -332,10 +341,10 @@ impl<'a> Builder<'a> { } /// Start a clip. - fn begin_clip(&mut self, blend: Option) { + fn begin_clip(&mut self, blend: Option) { self.scene.drawtag_stream.push(DRAWTAG_BEGINCLIP); let element = Clip { - blend: blend.unwrap_or(Blend::default()).pack(), + blend: blend.unwrap_or(BlendMode::default()).pack(), }; self.scene .drawdata_stream @@ -343,10 +352,10 @@ impl<'a> Builder<'a> { self.scene.n_clip += 1; } - fn end_clip(&mut self, blend: Option) { + fn end_clip(&mut self, blend: Option) { self.scene.drawtag_stream.push(DRAWTAG_ENDCLIP); let element = Clip { - blend: blend.unwrap_or(Blend::default()).pack(), + blend: blend.unwrap_or(BlendMode::default()).pack(), }; self.scene .drawdata_stream @@ -357,7 +366,6 @@ impl<'a> Builder<'a> { self.scene.n_clip += 1; } } - enum ResourceData<'a> { Fragment(&'a mut FragmentResources), Scene(&'a mut ResourceContext), diff --git a/piet-scene/src/scene/mod.rs b/piet-scene/src/scene/mod.rs index 5f0e77f..33abe57 100644 --- a/piet-scene/src/scene/mod.rs +++ b/piet-scene/src/scene/mod.rs @@ -18,13 +18,13 @@ mod blend; mod builder; mod style; -pub use blend::{Blend, Compose, Mix}; -pub use builder::{build_fragment, build_scene, Builder}; +pub use blend::{BlendMode, Compose, Mix}; +pub use builder::SceneBuilder; pub use style::*; use super::brush::*; use super::geometry::{Affine, Point}; -use super::path::Element; +use super::path::PathElement; use core::ops::Range; @@ -43,6 +43,10 @@ pub struct SceneData { } impl SceneData { + fn is_empty(&self) -> bool { + self.pathseg_stream.is_empty() + } + fn reset(&mut self, is_fragment: bool) { self.transform_stream.clear(); self.tag_stream.clear(); @@ -97,23 +101,32 @@ impl Scene { /// Encoded definition of a scene fragment and associated resources. #[derive(Default)] -pub struct Fragment { +pub struct SceneFragment { data: SceneData, resources: FragmentResources, } -impl Fragment { +impl SceneFragment { + /// Returns true if the fragment does not contain any paths. + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + /// Returns the underlying stream of points that defined all encoded path /// segments. pub fn points(&self) -> &[Point] { - bytemuck::cast_slice(&self.data.pathseg_stream) + if self.is_empty() { + &[] + } else { + bytemuck::cast_slice(&self.data.pathseg_stream) + } } } #[derive(Default)] struct FragmentResources { patches: Vec, - stops: Vec, + stops: Vec, } enum ResourcePatch { diff --git a/tests/src/draw.rs b/tests/src/draw.rs index dc82572..907fadc 100644 --- a/tests/src/draw.rs +++ b/tests/src/draw.rs @@ -17,7 +17,7 @@ //! Tests for the piet-gpu draw object stage. use piet_gpu_hal::{BufWrite, BufferUsage}; -use rand::{seq::SliceRandom, Rng}; +use rand::seq::SliceRandom; use crate::{Config, Runner, TestResult}; From cd25528abd4a92372d13e883c54f29cf296f1e1a Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Thu, 11 Aug 2022 18:00:53 -0400 Subject: [PATCH 2/8] make gradient ramps late bound Removes ResourceContext type. This makes scenes and fragments nearly identical. Should they be merged? --- pgpu-render/src/render.rs | 10 +- piet-gpu/bin/android.rs | 7 +- piet-gpu/bin/cli.rs | 8 +- piet-gpu/bin/winit.rs | 13 +-- piet-gpu/src/lib.rs | 56 +++++++--- .../gradient.rs => piet-gpu/src/ramp.rs | 9 +- piet-gpu/src/render_driver.rs | 11 +- piet-gpu/src/samples.rs | 100 +++++++++++++++++- piet-scene/src/resource.rs | 31 ++++++ piet-scene/src/resource/mod.rs | 32 ------ piet-scene/src/scene/builder.rs | 98 +++-------------- piet-scene/src/scene/mod.rs | 38 +++---- 12 files changed, 227 insertions(+), 186 deletions(-) rename piet-scene/src/resource/gradient.rs => piet-gpu/src/ramp.rs (95%) create mode 100644 piet-scene/src/resource.rs delete mode 100644 piet-scene/src/resource/mod.rs diff --git a/pgpu-render/src/render.rs b/pgpu-render/src/render.rs index bbea802..591fdeb 100644 --- a/pgpu-render/src/render.rs +++ b/pgpu-render/src/render.rs @@ -18,7 +18,7 @@ use piet_gpu::{PixelFormat, RenderConfig}; use piet_gpu_hal::{QueryPool, Session}; use piet_scene::glyph::pinot::{types::Tag, FontDataRef}; use piet_scene::glyph::{GlyphContext, GlyphProvider}; -use piet_scene::{Affine, Rect, ResourceContext, Scene, SceneFragment}; +use piet_scene::{Affine, Rect, Scene, SceneFragment}; /// State and resources for rendering a scene. pub struct PgpuRenderer { @@ -105,23 +105,17 @@ impl PgpuRenderer { /// Encoded streams and resources describing a vector graphics scene. pub struct PgpuScene { scene: Scene, - rcx: ResourceContext, } impl PgpuScene { pub fn new() -> Self { Self { scene: Scene::default(), - rcx: ResourceContext::new(), } } pub fn builder(&mut self) -> PgpuSceneBuilder { - self.rcx.advance(); - PgpuSceneBuilder(piet_scene::SceneBuilder::for_scene( - &mut self.scene, - &mut self.rcx, - )) + PgpuSceneBuilder(piet_scene::SceneBuilder::for_scene(&mut self.scene)) } } diff --git a/piet-gpu/bin/android.rs b/piet-gpu/bin/android.rs index cf5db9d..d94b77f 100644 --- a/piet-gpu/bin/android.rs +++ b/piet-gpu/bin/android.rs @@ -18,7 +18,7 @@ use piet_gpu_hal::{ }; use piet_gpu::{samples, RenderDriver, Renderer, SimpleText}; -use piet_scene::{ResourceContext, Scene, SceneBuilder}; +use piet_scene::{Scene, SceneBuilder}; #[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))] fn main() { @@ -134,13 +134,12 @@ impl GfxState { } let mut text = SimpleText::new(); let mut scene = Scene::default(); - let mut rcx = ResourceContext::default(); - let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx); + let mut builder = SceneBuilder::for_scene(&mut scene); samples::render_anim_frame(&mut builder, self.current_frame); //samples::render_tiger(&mut builder, false); render_info(&mut text, &mut builder, &info_string); builder.finish(); - if let Err(e) = self.render_driver.upload_scene(&self.session, &scene, &rcx) { + if let Err(e) = self.render_driver.upload_scene(&self.session, &scene) { println!("error in uploading: {}", e); } let (image_idx, acquisition_semaphore) = self.swapchain.next().unwrap(); diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 632fa74..4e52542 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -7,7 +7,7 @@ use clap::{App, Arg}; use piet_gpu_hal::{BufferUsage, Error, Instance, InstanceFlags, Session}; use piet_gpu::{samples, PicoSvg, RenderDriver, Renderer}; -use piet_scene::{ResourceContext, Scene, SceneBuilder}; +use piet_scene::{Scene, SceneBuilder}; const WIDTH: usize = 2048; const HEIGHT: usize = 1536; @@ -229,12 +229,10 @@ fn main() -> Result<(), Error> { .get_matches(); let instance = Instance::new(InstanceFlags::default())?; let mut scene = Scene::default(); - let mut rcx = ResourceContext::default(); unsafe { let device = instance.device()?; let session = Session::new(device); - rcx.advance(); - let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx); + let mut builder = SceneBuilder::for_scene(&mut scene); if let Some(input) = matches.value_of("INPUT") { let mut scale = matches .value_of("scale") @@ -257,7 +255,7 @@ fn main() -> Result<(), Error> { let renderer = Renderer::new(&session, WIDTH, HEIGHT, 1)?; let mut render_driver = RenderDriver::new(&session, 1, renderer); let start = std::time::Instant::now(); - render_driver.upload_scene(&session, &scene, &rcx)?; + render_driver.upload_scene(&session, &scene)?; let image_usage = BufferUsage::MAP_READ | BufferUsage::COPY_DST; let image_buf = session.create_buffer((WIDTH * HEIGHT * 4) as u64, image_usage)?; diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index 92fc35d..94a2237 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -1,6 +1,6 @@ use piet_gpu::{samples, PicoSvg, RenderDriver, Renderer, SimpleText}; use piet_gpu_hal::{Error, ImageLayout, Instance, InstanceFlags, Session}; -use piet_scene::{ResourceContext, Scene, SceneBuilder}; +use piet_scene::{Scene, SceneBuilder}; use clap::{App, Arg}; @@ -58,7 +58,6 @@ fn main() -> Result<(), Error> { let instance = Instance::new(InstanceFlags::default())?; let mut info_string = "info".to_string(); let mut scene = Scene::default(); - let mut rcx = ResourceContext::default(); let mut simple_text = piet_gpu::SimpleText::new(); unsafe { let surface = instance.surface(&window)?; @@ -113,17 +112,15 @@ fn main() -> Result<(), Error> { } if let Some(svg) = &svg { - rcx.advance(); - let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx); + let mut builder = SceneBuilder::for_scene(&mut scene); samples::render_svg(&mut builder, svg, false); render_info(&mut simple_text, &mut builder, &info_string); builder.finish(); - if let Err(e) = render_driver.upload_scene(&session, &scene, &rcx) { + if let Err(e) = render_driver.upload_scene(&session, &scene) { println!("error in uploading: {}", e); } } else { - rcx.advance(); - let mut builder = SceneBuilder::for_scene(&mut scene, &mut rcx); + let mut builder = SceneBuilder::for_scene(&mut scene); const N_SAMPLES: usize = 4; match sample_index % N_SAMPLES { @@ -138,7 +135,7 @@ fn main() -> Result<(), Error> { } render_info(&mut simple_text, &mut builder, &info_string); builder.finish(); - if let Err(e) = render_driver.upload_scene(&session, &scene, &rcx) { + if let Err(e) = render_driver.upload_scene(&session, &scene) { println!("error in uploading: {}", e); } } diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index a3d86b2..b2dfffb 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -1,4 +1,5 @@ mod pico_svg; +mod ramp; mod render_driver; pub mod samples; mod simple_text; @@ -7,6 +8,7 @@ pub mod stages; pub use piet_scene as scene; use bytemuck::{Pod, Zeroable}; +use scene::ResourcePatch; use std::convert::TryInto; pub use render_driver::RenderDriver; @@ -17,7 +19,7 @@ use piet_gpu_hal::{ DescriptorSet, Error, Image, ImageLayout, Pipeline, QueryPool, Session, }; -use piet_scene::{ResourceContext, Scene}; +use piet_scene::Scene; pub use pico_svg::PicoSvg; use stages::{ClipBinding, ElementBinding, ElementCode, DRAW_PART_SIZE, PATHSEG_PART_SIZE}; @@ -154,6 +156,9 @@ pub struct Renderer { gradient_bufs: Vec, gradients: Image, + + ramps: ramp::RampCache, + drawdata_patches: Vec<(usize, u32)>, } impl RenderConfig { @@ -364,6 +369,9 @@ impl Renderer { .build(&session, &k4_pipeline)?; let scene_stats = Default::default(); + let ramps = ramp::RampCache::default(); + let drawdata_patches = vec![]; + Ok(Renderer { width, height, @@ -403,26 +411,34 @@ impl Renderer { _bg_image: bg_image, gradient_bufs, gradients, + ramps, + drawdata_patches, }) } - pub fn upload_scene( - &mut self, - scene: &Scene, - rcx: &ResourceContext, - buf_ix: usize, - ) -> Result<(), Error> { + pub fn upload_scene(&mut self, scene: &Scene, buf_ix: usize) -> Result<(), Error> { + self.drawdata_patches.clear(); self.scene_stats = SceneStats::from_scene(scene); - + self.ramps.advance(); + let data = scene.data(); + let stop_data = &data.resources.stops; + for patch in &data.resources.patches { + match patch { + ResourcePatch::Ramp { offset, stops } => { + let ramp_id = self.ramps.add(&stop_data[stops.clone()]); + self.drawdata_patches.push((*offset, ramp_id)); + } + } + } unsafe { self.upload_config(buf_ix)?; { let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?; - write_scene(scene, &mut mapped_scene); + write_scene(scene, &self.drawdata_patches, &mut mapped_scene); } // Upload gradient data. - let ramp_data = rcx.ramp_data(); + let ramp_data = self.ramps.data(); if !ramp_data.is_empty() { assert!( self.gradient_bufs[buf_ix].size() as usize @@ -870,12 +886,28 @@ impl SceneStats { } } -fn write_scene(scene: &Scene, buf: &mut BufWrite) { +fn write_scene(scene: &Scene, drawdata_patches: &[(usize, u32)], buf: &mut BufWrite) { let data = scene.data(); buf.extend_slice(&data.drawtag_stream); let n_drawobj = data.drawtag_stream.len(); buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE); - buf.extend_slice(&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 { + buf.extend_slice(&data.drawdata_stream[pos..offset]); + } + buf.push(value); + pos = offset + 4; + } + if pos < data.drawdata_stream.len() { + buf.extend_slice(&data.drawdata_stream[pos..]) + } + } else { + buf.extend_slice(&data.drawdata_stream); + } buf.extend_slice(&data.transform_stream); buf.extend_slice(&data.linewidth_stream); buf.extend_slice(&data.tag_stream); diff --git a/piet-scene/src/resource/gradient.rs b/piet-gpu/src/ramp.rs similarity index 95% rename from piet-scene/src/resource/gradient.rs rename to piet-gpu/src/ramp.rs index 8a23216..8e4ca19 100644 --- a/piet-scene/src/resource/gradient.rs +++ b/piet-gpu/src/ramp.rs @@ -1,4 +1,5 @@ -use crate::brush::{Color, GradientStop, GradientStops}; +use piet_scene::{Color, GradientStop, GradientStops}; + use std::collections::HashMap; const N_SAMPLES: usize = 512; @@ -21,12 +22,6 @@ impl RampCache { } } - pub fn clear(&mut self) { - self.epoch = 0; - self.map.clear(); - self.data.clear(); - } - pub fn add(&mut self, stops: &[GradientStop]) -> u32 { if let Some(entry) = self.map.get_mut(stops) { entry.1 = self.epoch; diff --git a/piet-gpu/src/render_driver.rs b/piet-gpu/src/render_driver.rs index afeec5e..c4d8043 100644 --- a/piet-gpu/src/render_driver.rs +++ b/piet-gpu/src/render_driver.rs @@ -15,7 +15,7 @@ // Also licensed under MIT license, at your choice. use piet_gpu_hal::{CmdBuf, Error, Image, QueryPool, Semaphore, Session, SubmittedCmdBuf}; -use piet_scene::{ResourceContext, Scene}; +use piet_scene::Scene; use crate::{MemoryHeader, Renderer, SceneStats}; @@ -86,15 +86,10 @@ impl RenderDriver { } } - pub fn upload_scene( - &mut self, - session: &Session, - scene: &Scene, - rcx: &ResourceContext, - ) -> Result<(), Error> { + pub fn upload_scene(&mut self, session: &Session, scene: &Scene) -> Result<(), Error> { let stats = SceneStats::from_scene(scene); self.ensure_scene_buffers(session, &stats)?; - self.renderer.upload_scene(scene, rcx, self.buf_ix) + self.renderer.upload_scene(scene, self.buf_ix) } fn ensure_scene_buffers(&mut self, session: &Session, stats: &SceneStats) -> Result<(), Error> { diff --git a/piet-gpu/src/samples.rs b/piet-gpu/src/samples.rs index b3b4b8b..a04f95b 100644 --- a/piet-gpu/src/samples.rs +++ b/piet-gpu/src/samples.rs @@ -168,7 +168,10 @@ pub fn render_blend_grid(sb: &mut SceneBuilder) { let i = ix % 4; let j = ix / 4; let transform = Affine::translate(i as f32 * 225., j as f32 * 225.); - render_blend_square(sb, blend.into(), transform); + let square = blend_square(blend.into()); + sb.append(&square, Some(transform)); + // sb.append(&square, Some(transform)); + // render_blend_square(sb, blend.into(), transform); } } @@ -265,6 +268,101 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin sb.pop_layer(); } +#[allow(unused)] +fn blend_square(blend: BlendMode) -> SceneFragment { + let mut fragment = SceneFragment::default(); + let mut sb = SceneBuilder::for_fragment(&mut fragment); + // Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + let rect = Rect::from_origin_size(Point::new(0., 0.), 200., 200.); + let stops = &[ + GradientStop { + color: Color::rgb8(0, 0, 0), + offset: 0.0, + }, + GradientStop { + color: Color::rgb8(255, 255, 255), + offset: 1.0, + }, + ][..]; + let linear = Brush::LinearGradient(LinearGradient { + start: Point::new(0.0, 0.0), + end: Point::new(200.0, 0.0), + stops: stops.into(), + extend: ExtendMode::Pad, + }); + sb.fill(Fill::NonZero, &linear, None, rect.elements()); + const GRADIENTS: &[(f32, f32, Color)] = &[ + (150., 0., Color::rgb8(64, 240, 255)), + (175., 100., Color::rgb8(240, 96, 255)), + (125., 200., Color::rgb8(255, 192, 64)), + ]; + for (x, y, c) in GRADIENTS { + let mut color2 = c.clone(); + color2.a = 0; + let stops = &[ + GradientStop { + color: c.clone(), + offset: 0.0, + }, + GradientStop { + color: color2, + offset: 1.0, + }, + ][..]; + let rad = Brush::RadialGradient(RadialGradient { + center0: Point::new(*x, *y), + center1: Point::new(*x, *y), + radius0: 0.0, + radius1: 100.0, + stops: stops.into(), + extend: ExtendMode::Pad, + }); + sb.fill(Fill::NonZero, &rad, None, rect.elements()); + } + const COLORS: &[Color] = &[ + Color::rgb8(0, 0, 255), + Color::rgb8(0, 255, 0), + Color::rgb8(255, 0, 0), + ]; + sb.push_layer(Mix::Normal.into(), rect.elements()); + for (i, c) in COLORS.iter().enumerate() { + let stops = &[ + GradientStop { + color: Color::rgb8(255, 255, 255), + offset: 0.0, + }, + GradientStop { + color: c.clone(), + offset: 1.0, + }, + ][..]; + let linear = Brush::LinearGradient(LinearGradient { + start: Point::new(0.0, 0.0), + end: Point::new(0.0, 200.0), + stops: stops.into(), + extend: ExtendMode::Pad, + }); + sb.transform(Affine::IDENTITY); + sb.push_layer(blend, rect.elements()); + // squash the ellipse + let a = Affine::translate(100., 100.) + * Affine::rotate(std::f32::consts::FRAC_PI_3 * (i * 2 + 1) as f32) + * Affine::scale(1.0, 0.357) + * Affine::translate(-100., -100.); + sb.transform(a); + sb.fill( + Fill::NonZero, + &linear, + None, + make_ellipse(100., 100., 90., 90.), + ); + sb.pop_layer(); + } + sb.pop_layer(); + sb.finish(); + fragment +} + #[allow(unused)] pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) { sb.fill( diff --git a/piet-scene/src/resource.rs b/piet-scene/src/resource.rs new file mode 100644 index 0000000..7069304 --- /dev/null +++ b/piet-scene/src/resource.rs @@ -0,0 +1,31 @@ +use crate::brush::GradientStop; +use core::ops::Range; + +#[derive(Default)] +/// Collection of late bound resources for a scene or scene fragment. +pub struct ResourceBundle { + /// Sequence of resource patches. + pub patches: Vec, + /// Cache of gradient stops, referenced by range from the patches. + pub stops: Vec, +} + +impl ResourceBundle { + /// Clears the resource set. + pub(crate) fn clear(&mut self) { + self.patches.clear(); + self.stops.clear(); + } +} + +#[derive(Clone)] +/// Description of a late bound resource. +pub enum ResourcePatch { + /// Gradient ramp resource. + Ramp { + /// Byte offset to the ramp id in the draw data stream. + offset: usize, + /// Range of the gradient stops in the resource set. + stops: Range, + }, +} diff --git a/piet-scene/src/resource/mod.rs b/piet-scene/src/resource/mod.rs deleted file mode 100644 index 2e6b2d6..0000000 --- a/piet-scene/src/resource/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -mod gradient; - -use crate::brush::GradientStop; -use gradient::RampCache; - -/// Context for caching resources across rendering operations. -#[derive(Default)] -pub struct ResourceContext { - ramps: RampCache, -} - -impl ResourceContext { - pub fn new() -> Self { - Self::default() - } - - pub fn advance(&mut self) { - self.ramps.advance(); - } - - pub fn clear(&mut self) { - self.ramps.clear(); - } - - pub fn add_ramp(&mut self, stops: &[GradientStop]) -> u32 { - self.ramps.add(stops) - } - - pub fn ramp_data(&self) -> &[u32] { - &self.ramps.data() - } -} diff --git a/piet-scene/src/scene/builder.rs b/piet-scene/src/scene/builder.rs index 37befac..d7c8b03 100644 --- a/piet-scene/src/scene/builder.rs +++ b/piet-scene/src/scene/builder.rs @@ -15,12 +15,8 @@ // Also licensed under MIT license, at your choice. use super::style::{Fill, Stroke}; -use super::{ - Affine, BlendMode, FragmentResources, PathElement, ResourcePatch, Scene, SceneData, - SceneFragment, -}; -use crate::brush::*; -use crate::resource::ResourceContext; +use super::{Affine, BlendMode, PathElement, Scene, SceneData, SceneFragment}; +use crate::{brush::*, ResourcePatch}; use bytemuck::{Pod, Zeroable}; use core::borrow::Borrow; use smallvec::SmallVec; @@ -28,37 +24,27 @@ use smallvec::SmallVec; /// Builder for constructing a scene or scene fragment. pub struct SceneBuilder<'a> { scene: &'a mut SceneData, - resources: ResourceData<'a>, layers: SmallVec<[BlendMode; 8]>, } impl<'a> SceneBuilder<'a> { /// Creates a new builder for filling a scene. Any current content in the scene /// will be cleared. - pub fn for_scene(scene: &'a mut Scene, rcx: &'a mut ResourceContext) -> Self { - Self::new(&mut scene.data, ResourceData::Scene(rcx)) + pub fn for_scene(scene: &'a mut Scene) -> Self { + Self::new(&mut scene.data, false) } /// Creates a new builder for filling a scene fragment. Any current content in /// the fragment will be cleared. pub fn for_fragment(fragment: &'a mut SceneFragment) -> Self { - Self::new( - &mut fragment.data, - ResourceData::Fragment(&mut fragment.resources), - ) + Self::new(&mut fragment.data, true) } /// Creates a new builder for constructing a scene. - fn new(scene: &'a mut SceneData, mut resources: ResourceData<'a>) -> Self { - let is_fragment = match resources { - ResourceData::Fragment(_) => true, - _ => false, - }; + fn new(scene: &'a mut SceneData, is_fragment: bool) -> Self { scene.reset(is_fragment); - resources.clear(); Self { scene, - resources, layers: Default::default(), } } @@ -161,7 +147,6 @@ impl<'a> SceneBuilder<'a> { /// Appends a fragment to the scene. pub fn append(&mut self, fragment: &SceneFragment, transform: Option) { - let drawdata_base = self.scene.drawdata_stream.len(); let mut cur_transform = self.scene.transform_stream.last().copied(); if let Some(transform) = transform { if cur_transform.is_none() { @@ -172,39 +157,6 @@ impl<'a> SceneBuilder<'a> { self.encode_transform(Affine::IDENTITY); } self.scene.append(&fragment.data, &transform); - match &mut self.resources { - ResourceData::Scene(res) => { - for patch in &fragment.resources.patches { - match patch { - ResourcePatch::Ramp { - drawdata_offset, - stops, - } => { - let stops = &fragment.resources.stops[stops.clone()]; - let ramp_id = res.add_ramp(stops); - let patch_base = *drawdata_offset + drawdata_base; - (&mut self.scene.drawdata_stream[patch_base..patch_base + 4]) - .copy_from_slice(bytemuck::bytes_of(&ramp_id)); - } - } - } - } - ResourceData::Fragment(res) => { - let stops_base = res.stops.len(); - res.stops.extend_from_slice(&fragment.resources.stops); - res.patches.extend(fragment.resources.patches.iter().map( - |pending| match pending { - ResourcePatch::Ramp { - drawdata_offset, - stops, - } => ResourcePatch::Ramp { - drawdata_offset: drawdata_offset + drawdata_base, - stops: stops.start + stops_base..stops.end + stops_base, - }, - }, - )); - } - } // Prevent fragments from affecting transform state. Should we allow this? if let Some(transform) = cur_transform { self.transform(transform); @@ -325,19 +277,15 @@ impl<'a> SceneBuilder<'a> { } fn add_ramp(&mut self, stops: &[GradientStop]) -> u32 { - match &mut self.resources { - ResourceData::Scene(res) => res.add_ramp(stops), - ResourceData::Fragment(res) => { - let stops_start = res.stops.len(); - res.stops.extend_from_slice(stops); - let id = res.patches.len() as u32; - res.patches.push(ResourcePatch::Ramp { - drawdata_offset: self.scene.drawdata_stream.len(), - stops: stops_start..stops_start + stops.len(), - }); - id - } - } + let offset = self.scene.drawdata_stream.len(); + let resources = &mut self.scene.resources; + let stops_start = resources.stops.len(); + resources.stops.extend_from_slice(stops); + resources.patches.push(ResourcePatch::Ramp { + offset, + stops: stops_start..stops_start + stops.len(), + }); + 0 } /// Start a clip. @@ -366,22 +314,6 @@ impl<'a> SceneBuilder<'a> { self.scene.n_clip += 1; } } -enum ResourceData<'a> { - Fragment(&'a mut FragmentResources), - Scene(&'a mut ResourceContext), -} - -impl ResourceData<'_> { - fn clear(&mut self) { - match self { - Self::Fragment(res) => { - res.patches.clear(); - res.stops.clear(); - } - _ => {} - } - } -} // Tags for draw objects. See shader/drawtag.h for the authoritative source. const DRAWTAG_FILLCOLOR: u32 = 0x44; diff --git a/piet-scene/src/scene/mod.rs b/piet-scene/src/scene/mod.rs index 33abe57..91fd14b 100644 --- a/piet-scene/src/scene/mod.rs +++ b/piet-scene/src/scene/mod.rs @@ -22,11 +22,9 @@ pub use blend::{BlendMode, Compose, Mix}; pub use builder::SceneBuilder; pub use style::*; -use super::brush::*; use super::geometry::{Affine, Point}; use super::path::PathElement; - -use core::ops::Range; +use super::resource::{ResourceBundle, ResourcePatch}; /// Raw data streams describing an encoded scene. #[derive(Default)] @@ -40,6 +38,7 @@ pub struct SceneData { pub n_path: u32, pub n_pathseg: u32, pub n_clip: u32, + pub resources: ResourceBundle, } impl SceneData { @@ -57,6 +56,7 @@ impl SceneData { self.n_path = 0; self.n_pathseg = 0; self.n_clip = 0; + self.resources.clear(); if !is_fragment { self.transform_stream .push(Affine::new(&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0])); @@ -65,9 +65,11 @@ impl SceneData { } fn append(&mut self, other: &SceneData, transform: &Option) { + let stops_base = self.resources.stops.len(); + let drawdata_base = self.drawdata_stream.len(); if let Some(transform) = *transform { self.transform_stream - .extend(other.transform_stream.iter().map(|x| *x * transform)); + .extend(other.transform_stream.iter().map(|x| transform * *x)); } else { self.transform_stream .extend_from_slice(&other.transform_stream); @@ -82,6 +84,20 @@ impl SceneData { self.n_path += other.n_path; self.n_pathseg += other.n_pathseg; self.n_clip += other.n_clip; + self.resources + .stops + .extend_from_slice(&other.resources.stops); + self.resources + .patches + .extend(other.resources.patches.iter().map(|patch| match patch { + ResourcePatch::Ramp { offset, stops } => { + let stops = stops.start + stops_base..stops.end + stops_base; + ResourcePatch::Ramp { + offset: drawdata_base + offset, + stops, + } + } + })); } } @@ -103,7 +119,6 @@ impl Scene { #[derive(Default)] pub struct SceneFragment { data: SceneData, - resources: FragmentResources, } impl SceneFragment { @@ -122,16 +137,3 @@ impl SceneFragment { } } } - -#[derive(Default)] -struct FragmentResources { - patches: Vec, - stops: Vec, -} - -enum ResourcePatch { - Ramp { - drawdata_offset: usize, - stops: Range, - }, -} From 7fe022228a33fe0a0cbc683b602d9fcb4251f043 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 16 Aug 2022 14:52:04 -0400 Subject: [PATCH 3/8] Make transforms stateless Removes the transform state mutator from SceneBuilder and adds transform parameters to push_layer, fill and stroke methods. --- pgpu-render/src/lib.rs | 16 +-- pgpu-render/src/render.rs | 27 +++-- piet-gpu/bin/winit.rs | 3 +- piet-gpu/src/samples.rs | 177 +++++++++++++------------------- piet-scene/src/glyph.rs | 14 ++- piet-scene/src/scene/builder.rs | 61 ++++------- 6 files changed, 127 insertions(+), 171 deletions(-) diff --git a/pgpu-render/src/lib.rs b/pgpu-render/src/lib.rs index 8d137ea..43ff438 100644 --- a/pgpu-render/src/lib.rs +++ b/pgpu-render/src/lib.rs @@ -273,8 +273,8 @@ pub unsafe extern "C" fn pgpu_scene_builder_transform( builder: *mut PgpuSceneBuilder<'static>, transform: *const PgpuTransform, ) { - if !transform.is_null() { - (*builder).0.transform((*transform).into()) + if let Some(transform) = transform.as_ref() { + (*builder).transform = (*transform).into(); } } @@ -308,9 +308,13 @@ pub unsafe extern "C" fn pgpu_scene_builder_fill_path( } else { Some((*brush_transform).into()) }; - (*builder) - .0 - .fill(fill, &brush, brush_transform, (*path).clone()); + (*builder).builder.fill( + fill, + (*builder).transform, + &brush, + brush_transform, + (*path).clone(), + ); } /// Appends a scene fragment to the underlying scene or fragment. The @@ -329,7 +333,7 @@ pub unsafe extern "C" fn pgpu_scene_builder_append_fragment( } else { Some((*transform).into()) }; - (*builder).0.append(&(*fragment).0, transform); + (*builder).builder.append(&(*fragment).0, transform); } /// Finalizes the scene builder, making the underlying scene ready for diff --git a/pgpu-render/src/render.rs b/pgpu-render/src/render.rs index 591fdeb..4cf9563 100644 --- a/pgpu-render/src/render.rs +++ b/pgpu-render/src/render.rs @@ -103,19 +103,18 @@ impl PgpuRenderer { } /// Encoded streams and resources describing a vector graphics scene. -pub struct PgpuScene { - scene: Scene, -} +pub struct PgpuScene(pub Scene); impl PgpuScene { pub fn new() -> Self { - Self { - scene: Scene::default(), - } + Self(Scene::default()) } pub fn builder(&mut self) -> PgpuSceneBuilder { - PgpuSceneBuilder(piet_scene::SceneBuilder::for_scene(&mut self.scene)) + PgpuSceneBuilder { + builder: piet_scene::SceneBuilder::for_scene(&mut self.0), + transform: Affine::IDENTITY, + } } } @@ -128,20 +127,26 @@ impl PgpuSceneFragment { } pub fn builder(&mut self) -> PgpuSceneBuilder { - PgpuSceneBuilder(piet_scene::SceneBuilder::for_fragment(&mut self.0)) + PgpuSceneBuilder { + builder: piet_scene::SceneBuilder::for_fragment(&mut self.0), + transform: Affine::IDENTITY, + } } } /// Builder for constructing an encoded scene. -pub struct PgpuSceneBuilder<'a>(pub piet_scene::SceneBuilder<'a>); +pub struct PgpuSceneBuilder<'a> { + pub builder: piet_scene::SceneBuilder<'a>, + pub transform: Affine, +} impl<'a> PgpuSceneBuilder<'a> { pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::Affine) { - self.0.append(&glyph.fragment, Some(*transform)); + self.builder.append(&glyph.fragment, Some(*transform)); } pub fn finish(self) { - self.0.finish(); + self.builder.finish(); } } diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index 94a2237..870efe0 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -122,7 +122,7 @@ fn main() -> Result<(), Error> { } else { let mut builder = SceneBuilder::for_scene(&mut scene); - const N_SAMPLES: usize = 4; + const N_SAMPLES: usize = 5; match sample_index % N_SAMPLES { 0 => samples::render_anim_frame( &mut builder, @@ -131,6 +131,7 @@ fn main() -> Result<(), Error> { ), 1 => samples::render_blend_grid(&mut builder), 2 => samples::render_tiger(&mut builder, false), + 3 => samples::render_brush_transform(&mut builder, current_frame), _ => samples::render_scene(&mut builder), } render_info(&mut simple_text, &mut builder, &info_string); diff --git a/piet-gpu/src/samples.rs b/piet-gpu/src/samples.rs index a04f95b..51e49cd 100644 --- a/piet-gpu/src/samples.rs +++ b/piet-gpu/src/samples.rs @@ -16,6 +16,7 @@ pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) { Item::Fill(fill) => { sb.fill( Fill::NonZero, + Affine::IDENTITY, &fill.color.into(), None, convert_bez_path(&fill.path), @@ -24,6 +25,7 @@ pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) { Item::Stroke(stroke) => { sb.stroke( &simple_stroke(stroke.width as f32), + Affine::IDENTITY, &stroke.color.into(), None, convert_bez_path(&stroke.path), @@ -76,6 +78,7 @@ fn render_cardioid(sb: &mut SceneBuilder) { } sb.stroke( &simple_stroke(2.0), + Affine::IDENTITY, &Brush::Solid(Color::rgb8(0, 0, 0)), None, &path, @@ -102,7 +105,7 @@ fn render_clip_test(sb: &mut SceneBuilder) { PathElement::LineTo((X0, Y1).into()), PathElement::Close, ]; - sb.push_layer(Mix::Clip.into(), path); + sb.push_layer(Mix::Clip.into(), Affine::IDENTITY, path); } let rect = Rect { min: Point::new(X0, Y0), @@ -110,6 +113,7 @@ fn render_clip_test(sb: &mut SceneBuilder) { }; sb.fill( Fill::NonZero, + Affine::IDENTITY, &Brush::Solid(Color::rgb8(0, 0, 0)), None, rect.elements(), @@ -124,22 +128,29 @@ fn render_alpha_test(sb: &mut SceneBuilder) { // Alpha compositing tests. sb.fill( Fill::NonZero, + Affine::IDENTITY, &Color::rgb8(255, 0, 0).into(), None, - make_diamond(Point::new(1024.0, 100.0)), + make_diamond(1024.0, 100.0), ); sb.fill( Fill::NonZero, + Affine::IDENTITY, &Color::rgba8(0, 255, 0, 0x80).into(), None, - make_diamond(Point::new(1024.0, 125.0)), + make_diamond(1024.0, 125.0), + ); + sb.push_layer( + Mix::Clip.into(), + Affine::IDENTITY, + make_diamond(1024.0, 150.0), ); - sb.push_layer(Mix::Clip.into(), make_diamond(Point::new(1024.0, 150.0))); sb.fill( Fill::NonZero, + Affine::IDENTITY, &Color::rgba8(0, 0, 255, 0x80).into(), None, - make_diamond(Point::new(1024.0, 175.0)), + make_diamond(1024.0, 175.0), ); sb.pop_layer(); } @@ -170,15 +181,12 @@ pub fn render_blend_grid(sb: &mut SceneBuilder) { let transform = Affine::translate(i as f32 * 225., j as f32 * 225.); let square = blend_square(blend.into()); sb.append(&square, Some(transform)); - // sb.append(&square, Some(transform)); - // render_blend_square(sb, blend.into(), transform); } } #[allow(unused)] fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affine) { // Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - sb.transform(transform); let rect = Rect::from_origin_size(Point::new(0., 0.), 200., 200.); let stops = &[ GradientStop { @@ -196,7 +204,7 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin stops: stops.into(), extend: ExtendMode::Pad, }); - sb.fill(Fill::NonZero, &linear, None, rect.elements()); + sb.fill(Fill::NonZero, transform, &linear, None, rect.elements()); const GRADIENTS: &[(f32, f32, Color)] = &[ (150., 0., Color::rgb8(64, 240, 255)), (175., 100., Color::rgb8(240, 96, 255)), @@ -223,14 +231,14 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin stops: stops.into(), extend: ExtendMode::Pad, }); - sb.fill(Fill::NonZero, &rad, None, rect.elements()); + sb.fill(Fill::NonZero, transform, &rad, None, rect.elements()); } const COLORS: &[Color] = &[ Color::rgb8(0, 0, 255), Color::rgb8(0, 255, 0), Color::rgb8(255, 0, 0), ]; - sb.push_layer(Mix::Normal.into(), rect.elements()); + sb.push_layer(Mix::Normal.into(), transform, rect.elements()); for (i, c) in COLORS.iter().enumerate() { let stops = &[ GradientStop { @@ -248,17 +256,16 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin stops: stops.into(), extend: ExtendMode::Pad, }); - sb.transform(transform); - sb.push_layer(blend, rect.elements()); + sb.push_layer(blend, transform, rect.elements()); // squash the ellipse let a = transform * Affine::translate(100., 100.) * Affine::rotate(std::f32::consts::FRAC_PI_3 * (i * 2 + 1) as f32) * Affine::scale(1.0, 0.357) * Affine::translate(-100., -100.); - sb.transform(a); sb.fill( Fill::NonZero, + a, &linear, None, make_ellipse(100., 100., 90., 90.), @@ -272,93 +279,7 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin fn blend_square(blend: BlendMode) -> SceneFragment { let mut fragment = SceneFragment::default(); let mut sb = SceneBuilder::for_fragment(&mut fragment); - // Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - let rect = Rect::from_origin_size(Point::new(0., 0.), 200., 200.); - let stops = &[ - GradientStop { - color: Color::rgb8(0, 0, 0), - offset: 0.0, - }, - GradientStop { - color: Color::rgb8(255, 255, 255), - offset: 1.0, - }, - ][..]; - let linear = Brush::LinearGradient(LinearGradient { - start: Point::new(0.0, 0.0), - end: Point::new(200.0, 0.0), - stops: stops.into(), - extend: ExtendMode::Pad, - }); - sb.fill(Fill::NonZero, &linear, None, rect.elements()); - const GRADIENTS: &[(f32, f32, Color)] = &[ - (150., 0., Color::rgb8(64, 240, 255)), - (175., 100., Color::rgb8(240, 96, 255)), - (125., 200., Color::rgb8(255, 192, 64)), - ]; - for (x, y, c) in GRADIENTS { - let mut color2 = c.clone(); - color2.a = 0; - let stops = &[ - GradientStop { - color: c.clone(), - offset: 0.0, - }, - GradientStop { - color: color2, - offset: 1.0, - }, - ][..]; - let rad = Brush::RadialGradient(RadialGradient { - center0: Point::new(*x, *y), - center1: Point::new(*x, *y), - radius0: 0.0, - radius1: 100.0, - stops: stops.into(), - extend: ExtendMode::Pad, - }); - sb.fill(Fill::NonZero, &rad, None, rect.elements()); - } - const COLORS: &[Color] = &[ - Color::rgb8(0, 0, 255), - Color::rgb8(0, 255, 0), - Color::rgb8(255, 0, 0), - ]; - sb.push_layer(Mix::Normal.into(), rect.elements()); - for (i, c) in COLORS.iter().enumerate() { - let stops = &[ - GradientStop { - color: Color::rgb8(255, 255, 255), - offset: 0.0, - }, - GradientStop { - color: c.clone(), - offset: 1.0, - }, - ][..]; - let linear = Brush::LinearGradient(LinearGradient { - start: Point::new(0.0, 0.0), - end: Point::new(0.0, 200.0), - stops: stops.into(), - extend: ExtendMode::Pad, - }); - sb.transform(Affine::IDENTITY); - sb.push_layer(blend, rect.elements()); - // squash the ellipse - let a = Affine::translate(100., 100.) - * Affine::rotate(std::f32::consts::FRAC_PI_3 * (i * 2 + 1) as f32) - * Affine::scale(1.0, 0.357) - * Affine::translate(-100., -100.); - sb.transform(a); - sb.fill( - Fill::NonZero, - &linear, - None, - make_ellipse(100., 100., 90., 90.), - ); - sb.pop_layer(); - } - sb.pop_layer(); + render_blend_square(&mut sb, blend, Affine::IDENTITY); sb.finish(); fragment } @@ -367,6 +288,7 @@ fn blend_square(blend: BlendMode) -> SceneFragment { pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) { sb.fill( Fill::NonZero, + Affine::IDENTITY, &Brush::Solid(Color::rgb8(128, 128, 128)), None, Rect::from_origin_size(Point::new(0.0, 0.0), 1000.0, 1000.0).elements(), @@ -389,7 +311,6 @@ pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) Affine::translate(110.0, 700.0), s, ); - sb.transform(Affine::IDENTITY); let th = (std::f32::consts::PI / 180.0) * (i as f32); let center = Point::new(500.0, 500.0); let mut p1 = center; @@ -397,12 +318,54 @@ pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) p1.y += 400.0 * th.sin(); sb.stroke( &simple_stroke(5.0), + Affine::IDENTITY, &Brush::Solid(Color::rgb8(128, 0, 0)), None, &[PathElement::MoveTo(center), PathElement::LineTo(p1)], ); } +#[allow(unused)] +pub fn render_brush_transform(sb: &mut SceneBuilder, i: usize) { + let th = (std::f32::consts::PI / 180.0) * (i as f32); + let stops = &[ + GradientStop { + color: Color::rgb8(255, 0, 0), + offset: 0.0, + }, + GradientStop { + color: Color::rgb8(0, 255, 0), + offset: 0.5, + }, + GradientStop { + color: Color::rgb8(0, 0, 255), + offset: 1.0, + }, + ][..]; + let linear = LinearGradient { + start: Point::new(0.0, 0.0), + end: Point::new(0.0, 200.0), + stops: stops.into(), + extend: ExtendMode::Pad, + } + .into(); + sb.fill( + Fill::NonZero, + Affine::translate(200.0, 200.0), + &linear, + Some(Affine::rotate(th).around_center(200.0, 100.0)), + Rect::from_origin_size(Point::default(), 400.0, 200.0).elements(), + ); + sb.stroke( + &simple_stroke(40.0), + Affine::translate(800.0, 200.0), + &linear, + Some(Affine::rotate(th).around_center(200.0, 100.0)), + Rect::from_origin_size(Point::default(), 400.0, 200.0).elements(), + ); + +} + fn convert_bez_path<'a>(path: &'a BezPath) -> impl Iterator + 'a + Clone { path.elements() .iter() @@ -440,13 +403,13 @@ fn make_ellipse(cx: f32, cy: f32, rx: f32, ry: f32) -> impl Iterator impl Iterator + Clone { +fn make_diamond(cx: f32, cy: f32) -> impl Iterator + Clone { const SIZE: f32 = 50.0; let elements = [ - PathElement::MoveTo(Point::new(origin.x, origin.y - SIZE)), - PathElement::LineTo(Point::new(origin.x + SIZE, origin.y)), - PathElement::LineTo(Point::new(origin.x, origin.y + SIZE)), - PathElement::LineTo(Point::new(origin.x - SIZE, origin.y)), + PathElement::MoveTo(Point::new(cx, cy - SIZE)), + PathElement::LineTo(Point::new(cx + SIZE, cy)), + PathElement::LineTo(Point::new(cx, cy + SIZE)), + PathElement::LineTo(Point::new(cx - SIZE, cy)), PathElement::Close, ]; (0..elements.len()).map(move |i| elements[i]) diff --git a/piet-scene/src/glyph.rs b/piet-scene/src/glyph.rs index 8eb0627..6f752b7 100644 --- a/piet-scene/src/glyph.rs +++ b/piet-scene/src/glyph.rs @@ -90,6 +90,7 @@ impl<'a> GlyphProvider<'a> { let mut builder = SceneBuilder::for_fragment(&mut fragment); builder.fill( Fill::NonZero, + Affine::IDENTITY, brush.unwrap_or(&Brush::Solid(Color::rgb8(255, 255, 255))), None, convert_path(path.elements()), @@ -125,10 +126,15 @@ impl<'a> GlyphProvider<'a> { if let Some(xform) = xform_stack.last() { builder.push_layer( Default::default(), + Affine::IDENTITY, convert_transformed_path(path.elements(), xform), ); } else { - builder.push_layer(Default::default(), convert_path(path.elements())); + builder.push_layer( + Default::default(), + Affine::IDENTITY, + convert_path(path.elements()), + ); } } Command::PopClip => builder.pop_layer(), @@ -140,7 +146,7 @@ impl<'a> GlyphProvider<'a> { if let Some(xform) = xform_stack.last() { rect = rect.transform(xform); } - builder.push_layer(Default::default(), rect.elements()); + builder.push_layer(Default::default(), Affine::IDENTITY, rect.elements()); } Command::PopLayer => builder.pop_layer(), Command::BeginBlend(bounds, mode) => { @@ -151,7 +157,7 @@ impl<'a> GlyphProvider<'a> { if let Some(xform) = xform_stack.last() { rect = rect.transform(xform); } - builder.push_layer(convert_blend(*mode), rect.elements()) + builder.push_layer(convert_blend(*mode), Affine::IDENTITY, rect.elements()) } Command::EndBlend => builder.pop_layer(), Command::SimpleFill(path_index, brush, brush_xform) => { @@ -161,6 +167,7 @@ impl<'a> GlyphProvider<'a> { if let Some(xform) = xform_stack.last() { builder.fill( Fill::NonZero, + Affine::IDENTITY, &brush, brush_xform.map(|x| x * *xform), convert_transformed_path(path.elements(), xform), @@ -168,6 +175,7 @@ impl<'a> GlyphProvider<'a> { } else { builder.fill( Fill::NonZero, + Affine::IDENTITY, &brush, brush_xform, convert_path(path.elements()), diff --git a/piet-scene/src/scene/builder.rs b/piet-scene/src/scene/builder.rs index d7c8b03..56f9d7f 100644 --- a/piet-scene/src/scene/builder.rs +++ b/piet-scene/src/scene/builder.rs @@ -49,21 +49,15 @@ impl<'a> SceneBuilder<'a> { } } - /// Sets the current transformation. - pub fn transform(&mut self, transform: Affine) { - if self.scene.transform_stream.last() != Some(&transform) { - self.encode_transform(transform); - } - } - /// Pushes a new layer bound by the specifed shape and composed with /// previous layers using the specified blend mode. - pub fn push_layer<'s, E>(&mut self, blend: BlendMode, elements: E) + pub fn push_layer<'s, E>(&mut self, blend: BlendMode, transform: Affine, elements: E) where E: IntoIterator, E::IntoIter: Clone, E::Item: Borrow, { + self.maybe_encode_transform(transform); self.linewidth(-1.0); let elements = elements.into_iter(); self.encode_path(elements, true); @@ -82,6 +76,7 @@ impl<'a> SceneBuilder<'a> { pub fn fill<'s, E>( &mut self, _style: Fill, + transform: Affine, brush: &Brush, brush_transform: Option, elements: E, @@ -90,21 +85,14 @@ impl<'a> SceneBuilder<'a> { E::IntoIter: Clone, E::Item: Borrow, { + self.maybe_encode_transform(transform); self.linewidth(-1.0); let elements = elements.into_iter(); if self.encode_path(elements, true) { if let Some(brush_transform) = brush_transform { - if let Some(last_transform) = self.scene.transform_stream.last().copied() { - self.encode_transform(brush_transform * last_transform); - self.swap_last_tags(); - self.encode_brush(brush); - self.encode_transform(last_transform); - } else { - self.encode_transform(brush_transform); - self.swap_last_tags(); - self.encode_brush(brush); - self.encode_transform(Affine::IDENTITY); - } + self.encode_transform(transform * brush_transform); + self.swap_last_tags(); + self.encode_brush(brush); } else { self.encode_brush(brush); } @@ -115,6 +103,7 @@ impl<'a> SceneBuilder<'a> { pub fn stroke<'s, D, E>( &mut self, style: &Stroke, + transform: Affine, brush: &Brush, brush_transform: Option, elements: E, @@ -124,21 +113,14 @@ impl<'a> SceneBuilder<'a> { E::IntoIter: Clone, E::Item: Borrow, { + self.maybe_encode_transform(transform); self.linewidth(style.width); let elements = elements.into_iter(); if self.encode_path(elements, false) { if let Some(brush_transform) = brush_transform { - if let Some(last_transform) = self.scene.transform_stream.last().copied() { - self.encode_transform(brush_transform * last_transform); - self.swap_last_tags(); - self.encode_brush(brush); - self.encode_transform(last_transform); - } else { - self.encode_transform(brush_transform); - self.swap_last_tags(); - self.encode_brush(brush); - self.encode_transform(Affine::IDENTITY); - } + self.encode_transform(transform * brush_transform); + self.swap_last_tags(); + self.encode_brush(brush); } else { self.encode_brush(brush); } @@ -147,20 +129,7 @@ impl<'a> SceneBuilder<'a> { /// Appends a fragment to the scene. pub fn append(&mut self, fragment: &SceneFragment, transform: Option) { - let mut cur_transform = self.scene.transform_stream.last().copied(); - if let Some(transform) = transform { - if cur_transform.is_none() { - cur_transform = Some(Affine::IDENTITY); - } - self.transform(transform); - } else if cur_transform != Some(Affine::IDENTITY) { - self.encode_transform(Affine::IDENTITY); - } self.scene.append(&fragment.data, &transform); - // Prevent fragments from affecting transform state. Should we allow this? - if let Some(transform) = cur_transform { - self.transform(transform); - } } /// Completes construction and finalizes the underlying scene. @@ -218,6 +187,12 @@ impl<'a> SceneBuilder<'a> { has_els } + fn maybe_encode_transform(&mut self, transform: Affine) { + if self.scene.transform_stream.last() != Some(&transform) { + self.encode_transform(transform); + } + } + fn encode_transform(&mut self, transform: Affine) { self.scene.tag_stream.push(0x20); self.scene.transform_stream.push(transform); From 8ee317a92237bbb57a007cc6aafafc946b2abbf5 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 16 Aug 2022 14:55:59 -0400 Subject: [PATCH 4/8] format :( --- piet-gpu/src/samples.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/piet-gpu/src/samples.rs b/piet-gpu/src/samples.rs index 51e49cd..9a4f094 100644 --- a/piet-gpu/src/samples.rs +++ b/piet-gpu/src/samples.rs @@ -363,7 +363,6 @@ pub fn render_brush_transform(sb: &mut SceneBuilder, i: usize) { Some(Affine::rotate(th).around_center(200.0, 100.0)), Rect::from_origin_size(Point::default(), 400.0, 200.0).elements(), ); - } fn convert_bez_path<'a>(path: &'a BezPath) -> impl Iterator + 'a + Clone { From a6597af52af4b29b162f5ea1703857906d2a0561 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 17 Aug 2022 16:06:48 -0400 Subject: [PATCH 5/8] macOS fixes * Fix call to removed encoded_scene method in pgpu-render * Add new ImageFormat::Surface variant to select a pixel format that matches the platform specific surface format. This makes gradients consistent across platforms. --- pgpu-render/src/render.rs | 2 +- piet-gpu-hal/src/dx12.rs | 2 +- piet-gpu-hal/src/lib.rs | 2 ++ piet-gpu-hal/src/metal.rs | 3 ++- piet-gpu-hal/src/vulkan.rs | 2 +- piet-gpu/src/lib.rs | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pgpu-render/src/render.rs b/pgpu-render/src/render.rs index 4cf9563..6c59997 100644 --- a/pgpu-render/src/render.rs +++ b/pgpu-render/src/render.rs @@ -87,7 +87,7 @@ impl PgpuRenderer { .session .image_from_raw_mtl(target, self.width, self.height); if let Some(renderer) = &mut self.pgpu_renderer { - renderer.upload_scene(&scene.encoded_scene(), 0).unwrap(); + renderer.upload_scene(&scene.0, 0).unwrap(); renderer.record(&mut cmd_buf, &self.query_pool, 0); // TODO later: we can bind the destination image and avoid the copy. cmd_buf.blit_image(&renderer.image_dev, &dst_image); diff --git a/piet-gpu-hal/src/dx12.rs b/piet-gpu-hal/src/dx12.rs index 8d6820b..6cfd8c8 100644 --- a/piet-gpu-hal/src/dx12.rs +++ b/piet-gpu-hal/src/dx12.rs @@ -330,7 +330,7 @@ impl crate::backend::Device for Dx12Device { ) -> Result { let format = match format { ImageFormat::A8 => winapi::shared::dxgiformat::DXGI_FORMAT_R8_UNORM, - ImageFormat::Rgba8 => winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM, + ImageFormat::Rgba8 | ImageFormat::Surface => winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM, }; let resource = self .device diff --git a/piet-gpu-hal/src/lib.rs b/piet-gpu-hal/src/lib.rs index a1073f4..1e3fa32 100644 --- a/piet-gpu-hal/src/lib.rs +++ b/piet-gpu-hal/src/lib.rs @@ -98,6 +98,8 @@ pub enum ImageFormat { A8, // 8 bit per pixel RGBA Rgba8, + // Match default surface format + Surface, } bitflags! { diff --git a/piet-gpu-hal/src/metal.rs b/piet-gpu-hal/src/metal.rs index 7471d19..a17f448 100644 --- a/piet-gpu-hal/src/metal.rs +++ b/piet-gpu-hal/src/metal.rs @@ -349,7 +349,8 @@ impl crate::backend::Device for MtlDevice { //desc.set_mipmap_level_count(1); let mtl_format = match format { ImageFormat::A8 => metal::MTLPixelFormat::R8Unorm, - ImageFormat::Rgba8 => metal::MTLPixelFormat::BGRA8Unorm, + ImageFormat::Rgba8 => metal::MTLPixelFormat::RGBA8Unorm, + ImageFormat::Surface => metal::MTLPixelFormat::BGRA8Unorm, }; desc.set_pixel_format(mtl_format); desc.set_usage(metal::MTLTextureUsage::ShaderRead | metal::MTLTextureUsage::ShaderWrite); diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 6a790ef..1ce698b 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -578,7 +578,7 @@ impl crate::backend::Device for VkDevice { | vk::ImageUsageFlags::TRANSFER_DST; let vk_format = match format { ImageFormat::A8 => vk::Format::R8_UNORM, - ImageFormat::Rgba8 => vk::Format::R8G8B8A8_UNORM, + ImageFormat::Rgba8 | ImageFormat::Surface => vk::Format::R8G8B8A8_UNORM, }; let image = device.create_image( &vk::ImageCreateInfo::builder() diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index b2dfffb..8030234 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -224,7 +224,7 @@ impl Renderer { let image_format = match config.format { PixelFormat::A8 => piet_gpu_hal::ImageFormat::A8, - PixelFormat::Rgba8 => piet_gpu_hal::ImageFormat::Rgba8, + PixelFormat::Rgba8 => piet_gpu_hal::ImageFormat::Surface, }; let image_dev = session.create_image2d(width as u32, height as u32, image_format)?; From f7b1bca18be9e05b07c03e3b687b05d1c09520a4 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 19 Oct 2022 14:29:40 -0400 Subject: [PATCH 6/8] fix gradient ramp color packing --- piet-gpu/src/ramp.rs | 2 +- piet-gpu/src/samples.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/piet-gpu/src/ramp.rs b/piet-gpu/src/ramp.rs index 8e4ca19..1c834f7 100644 --- a/piet-gpu/src/ramp.rs +++ b/piet-gpu/src/ramp.rs @@ -124,6 +124,6 @@ impl ColorF64 { 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; - b | (g << 8) | (r << 16) | (a << 24) + r | (g << 8) | (b << 16) | (a << 24) } } diff --git a/piet-gpu/src/samples.rs b/piet-gpu/src/samples.rs index 9a4f094..091f4ef 100644 --- a/piet-gpu/src/samples.rs +++ b/piet-gpu/src/samples.rs @@ -206,9 +206,9 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin }); sb.fill(Fill::NonZero, transform, &linear, None, rect.elements()); const GRADIENTS: &[(f32, f32, Color)] = &[ - (150., 0., Color::rgb8(64, 240, 255)), - (175., 100., Color::rgb8(240, 96, 255)), - (125., 200., Color::rgb8(255, 192, 64)), + (150., 0., Color::rgb8(255, 240, 64)), + (175., 100., Color::rgb8(255, 96, 240)), + (125., 200., Color::rgb8(64, 192, 255)), ]; for (x, y, c) in GRADIENTS { let mut color2 = c.clone(); @@ -234,9 +234,9 @@ fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affin sb.fill(Fill::NonZero, transform, &rad, None, rect.elements()); } const COLORS: &[Color] = &[ - Color::rgb8(0, 0, 255), - Color::rgb8(0, 255, 0), Color::rgb8(255, 0, 0), + Color::rgb8(0, 255, 0), + Color::rgb8(0, 0, 255), ]; sb.push_layer(Mix::Normal.into(), transform, rect.elements()); for (i, c) in COLORS.iter().enumerate() { From 14247770aa7b9122e512aee4f28f5872be36ca89 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 19 Oct 2022 14:58:54 -0400 Subject: [PATCH 7/8] update dependencies --- Cargo.lock | 496 ++++++++++++++++++++++----------- piet-gpu-hal/Cargo.toml | 18 +- piet-gpu-hal/src/dx12.rs | 7 +- piet-gpu-hal/src/lib.rs | 2 +- piet-gpu-hal/src/metal.rs | 10 +- piet-gpu-hal/src/metal/util.rs | 2 +- piet-gpu-hal/src/mux.rs | 11 +- piet-gpu-hal/src/vulkan.rs | 29 +- piet-gpu/Cargo.toml | 9 +- piet-gpu/bin/android.rs | 36 +-- piet-gpu/bin/cli.rs | 6 +- piet-gpu/bin/winit.rs | 10 +- 12 files changed, 403 insertions(+), 233 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 947b09a..a8a9b7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,10 +3,10 @@ version = 3 [[package]] -name = "adler32" -version = "1.2.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ansi_term" @@ -31,21 +31,21 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "ash" -version = "0.33.3+1.2.191" +version = "0.37.0+1.3.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219" +checksum = "006ca68e0f2b03f22d6fa9f2860f85aed430d257fec20f8879b2145e7c7ae1a6" dependencies = [ "libloading", ] [[package]] name = "ash-window" -version = "0.7.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f91ce4c6be1a2ba99d3d6cd57d5bae9ac6d6f903b5ae53d6b1dee2edf872af" +checksum = "b912285a7c29f3a8f87ca6f55afc48768624e5e33ec17dbd2f2075903f5e35ab" dependencies = [ "ash", - "raw-window-handle 0.3.4", + "raw-window-handle 0.5.0", "raw-window-metal", ] @@ -86,38 +86,35 @@ checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "bytemuck" -version = "1.10.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e" +checksum = "1b9e1f5fa78f69496407a27ae9ed989e3c3b072310286f5ef385525e4cbc24a9" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - [[package]] name = "calloop" -version = "0.9.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" +checksum = "a22a6a8f622f797120d452c630b0ab12e1331a1a753e2039ce7868d4ac77b4ee" dependencies = [ "log", - "nix", + "nix 0.24.2", + "slotmap", + "thiserror", + "vec_map", ] [[package]] @@ -126,7 +123,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc" dependencies = [ - "clap", + "clap 2.34.0", "heck", "indexmap", "log", @@ -145,12 +142,6 @@ version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -167,11 +158,35 @@ dependencies = [ "atty", "bitflags", "strsim 0.8.0", - "textwrap", + "textwrap 0.11.0", "unicode-width", "vec_map", ] +[[package]] +name = "clap" +version = "3.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +dependencies = [ + "atty", + "bitflags", + "clap_lex", + "indexmap", + "strsim 0.10.0", + "termcolor", + "textwrap 0.15.1", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cocoa" version = "0.24.0" @@ -181,8 +196,8 @@ dependencies = [ "bitflags", "block", "cocoa-foundation", - "core-foundation 0.9.3", - "core-graphics 0.22.3", + "core-foundation", + "core-graphics", "foreign-types", "libc", "objc", @@ -196,57 +211,29 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" dependencies = [ "bitflags", "block", - "core-foundation 0.9.3", + "core-foundation", "core-graphics-types", "foreign-types", "libc", "objc", ] -[[package]] -name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -dependencies = [ - "core-foundation-sys 0.7.0", - "libc", -] - [[package]] name = "core-foundation" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ - "core-foundation-sys 0.8.3", + "core-foundation-sys", "libc", ] -[[package]] -name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" - [[package]] name = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" -[[package]] -name = "core-graphics" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" -dependencies = [ - "bitflags", - "core-foundation 0.7.0", - "foreign-types", - "libc", -] - [[package]] name = "core-graphics" version = "0.22.3" @@ -254,7 +241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ "bitflags", - "core-foundation 0.9.3", + "core-foundation", "core-graphics-types", "foreign-types", "libc", @@ -267,31 +254,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ "bitflags", - "core-foundation 0.9.3", + "core-foundation", "foreign-types", "libc", ] -[[package]] -name = "core-video-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" -dependencies = [ - "cfg-if 0.1.10", - "core-foundation-sys 0.7.0", - "core-graphics 0.19.2", - "libc", - "objc", -] - [[package]] name = "crc32fast" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -370,16 +344,6 @@ dependencies = [ "syn", ] -[[package]] -name = "deflate" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" -dependencies = [ - "adler32", - "byteorder", -] - [[package]] name = "dispatch" version = "0.2.0" @@ -410,6 +374,16 @@ dependencies = [ "instant", ] +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -437,11 +411,22 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "half" version = "1.8.2" @@ -494,7 +479,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -557,7 +542,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "winapi", ] @@ -577,7 +562,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -597,9 +582,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.3.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" +checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" dependencies = [ "libc", ] @@ -627,6 +612,20 @@ dependencies = [ "objc", ] +[[package]] +name = "metal" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060" +dependencies = [ + "bitflags", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -635,11 +634,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.3.7" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ - "adler32", + "adler", ] [[package]] @@ -651,7 +650,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -669,21 +668,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" dependencies = [ "jni-sys", - "ndk-sys", + "ndk-sys 0.2.2", "num_enum", "thiserror", ] [[package]] name = "ndk" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" dependencies = [ "bitflags", "jni-sys", - "ndk-sys", + "ndk-sys 0.4.0", "num_enum", + "raw-window-handle 0.5.0", "thiserror", ] @@ -704,22 +704,23 @@ dependencies = [ "log", "ndk 0.3.0", "ndk-macro 0.2.0", - "ndk-sys", + "ndk-sys 0.2.2", ] [[package]] name = "ndk-glue" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71bee8ea72d685477e28bd004cfe1bf99c754d688cd78cad139eae4089484d4" +checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" dependencies = [ - "lazy_static", "libc", "log", - "ndk 0.5.0", + "ndk 0.7.0", "ndk-context", "ndk-macro 0.3.0", - "ndk-sys", + "ndk-sys 0.4.0", + "once_cell", + "parking_lot", ] [[package]] @@ -754,6 +755,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" +[[package]] +name = "ndk-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" +dependencies = [ + "jni-sys", +] + [[package]] name = "nix" version = "0.22.3" @@ -762,7 +772,19 @@ checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" dependencies = [ "bitflags", "cc", - "cfg-if 1.0.0", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +dependencies = [ + "bitflags", + "cfg-if", "libc", "memoffset", ] @@ -824,28 +846,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] -name = "parking_lot" -version = "0.11.2" +name = "os_str_bytes" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" dependencies = [ - "cfg-if 1.0.0", - "instant", + "cfg-if", "libc", "redox_syscall", "smallvec", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -860,7 +886,7 @@ version = "0.1.0" dependencies = [ "cbindgen", "cocoa", - "metal", + "metal 0.22.0", "objc", "piet-gpu", "piet-gpu-hal", @@ -872,17 +898,18 @@ name = "piet-gpu" version = "0.1.0" dependencies = [ "bytemuck", - "clap", + "clap 3.2.22", "kurbo 0.8.3", "ndk 0.3.0", "ndk-glue 0.3.0", - "ndk-sys", + "ndk-sys 0.2.2", "piet-gpu-hal", "piet-gpu-types", "piet-scene", "png", - "rand", + "rand 0.8.5", "raw-window-handle 0.3.4", + "raw-window-handle 0.5.0", "roxmltree", "winit", ] @@ -906,10 +933,11 @@ dependencies = [ "block", "bytemuck", "cocoa-foundation", + "core-graphics-types", "foreign-types", - "metal", + "metal 0.24.0", "objc", - "raw-window-handle 0.3.4", + "raw-window-handle 0.5.0", "smallvec", "winapi", "wio", @@ -920,11 +948,11 @@ name = "piet-gpu-tests" version = "0.1.0" dependencies = [ "bytemuck", - "clap", + "clap 2.34.0", "kurbo 0.7.1", "piet-gpu", "piet-gpu-hal", - "rand", + "rand 0.7.3", ] [[package]] @@ -959,13 +987,13 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "png" -version = "0.16.8" +version = "0.17.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c" dependencies = [ "bitflags", "crc32fast", - "deflate", + "flate2", "miniz_oxide", ] @@ -996,9 +1024,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -1018,13 +1046,24 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.2.2", + "rand_core 0.5.1", "rand_hc", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -1032,7 +1071,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", ] [[package]] @@ -1041,7 +1090,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.7", ] [[package]] @@ -1050,7 +1108,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", ] [[package]] @@ -1073,15 +1131,24 @@ dependencies = [ ] [[package]] -name = "raw-window-metal" -version = "0.1.2" +name = "raw-window-handle" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd21ed1cdef7f1b1579b972148ba6058b5b545959a14d91ea83c4f0ea9f289b" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" +dependencies = [ + "cty", +] + +[[package]] +name = "raw-window-metal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d18241d631f19847a5f4cc0a3f81d978202c375573ab7d90ab14dcf0a9262ec" dependencies = [ "cocoa", - "core-graphics 0.22.3", + "core-graphics", "objc", - "raw-window-handle 0.3.4", + "raw-window-handle 0.5.0", ] [[package]] @@ -1160,6 +1227,15 @@ dependencies = [ "serde", ] +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.9.0" @@ -1168,9 +1244,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smithay-client-toolkit" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3" +checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" dependencies = [ "bitflags", "calloop", @@ -1178,7 +1254,7 @@ dependencies = [ "lazy_static", "log", "memmap2", - "nix", + "nix 0.24.2", "pkg-config", "wayland-client", "wayland-cursor", @@ -1205,9 +1281,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.98" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", @@ -1220,7 +1296,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", "libc", "redox_syscall", @@ -1228,6 +1304,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -1237,6 +1322,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" + [[package]] name = "thiserror" version = "1.0.31" @@ -1290,6 +1381,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1308,7 +1405,7 @@ version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -1365,7 +1462,7 @@ dependencies = [ "bitflags", "downcast-rs", "libc", - "nix", + "nix 0.22.3", "scoped-tls", "wayland-commons", "wayland-scanner", @@ -1378,7 +1475,7 @@ version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" dependencies = [ - "nix", + "nix 0.22.3", "once_cell", "smallvec", "wayland-sys", @@ -1390,7 +1487,7 @@ version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" dependencies = [ - "nix", + "nix 0.22.3", "wayland-client", "xcursor", ] @@ -1455,6 +1552,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1467,37 +1573,88 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -1505,35 +1662,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] -name = "winit" -version = "0.26.1" +name = "windows_x86_64_msvc" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "winit" +version = "0.27.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37f64802920c4c35d12a53dad5e0c55bbc3004d8dc4f2e4dd64ad02c5665d7aa" dependencies = [ "bitflags", "cocoa", - "core-foundation 0.9.3", - "core-graphics 0.22.3", - "core-video-sys", + "core-foundation", + "core-graphics", "dispatch", "instant", - "lazy_static", "libc", "log", "mio", - "ndk 0.5.0", - "ndk-glue 0.5.2", - "ndk-sys", + "ndk 0.7.0", + "ndk-glue 0.7.0", "objc", + "once_cell", "parking_lot", "percent-encoding", "raw-window-handle 0.4.3", + "raw-window-handle 0.5.0", "smithay-client-toolkit", "wasm-bindgen", "wayland-client", "wayland-protocols", "web-sys", - "winapi", + "windows-sys 0.36.1", "x11-dl", ] diff --git a/piet-gpu-hal/Cargo.toml b/piet-gpu-hal/Cargo.toml index f9b844a..110fc7f 100644 --- a/piet-gpu-hal/Cargo.toml +++ b/piet-gpu-hal/Cargo.toml @@ -7,12 +7,12 @@ license = "MIT/Apache-2.0" edition = "2018" [dependencies] -ash = "0.33" -ash-window = "0.7" -raw-window-handle = "0.3" -bitflags = "1.2.1" -smallvec = "1.6.1" -bytemuck = "1.7.2" +ash = { version = "0.37", features = ["loaded"] } +ash-window = "0.12" +raw-window-handle = "0.5" +bitflags = "1.3.2" +smallvec = "1.9" +bytemuck = "1.12.1" [target.'cfg(target_os="windows")'.dependencies] winapi = { version = "0.3.9", features = [ @@ -24,8 +24,10 @@ winapi = { version = "0.3.9", features = [ wio = "0.2.2" [target.'cfg(target_os="macos")'.dependencies] -metal = "0.22" -objc = "0.2.5" +metal = "0.24" +objc = "0.2.7" block = "0.1.6" cocoa-foundation = "0.1" +# Note: foreign-types is up to 0.5 but metal hasn't upgraded to it foreign-types = "0.3.2" +core-graphics-types = "0.1.1" diff --git a/piet-gpu-hal/src/dx12.rs b/piet-gpu-hal/src/dx12.rs index 6cfd8c8..f7edb24 100644 --- a/piet-gpu-hal/src/dx12.rs +++ b/piet-gpu-hal/src/dx12.rs @@ -17,7 +17,7 @@ use winapi::shared::minwindef::TRUE; use winapi::shared::{dxgi, dxgi1_2, dxgitype}; use winapi::um::d3d12; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use smallvec::SmallVec; @@ -153,9 +153,10 @@ impl Dx12Instance { /// Create a surface for the specified window handle. pub fn surface( &self, - window_handle: &dyn HasRawWindowHandle, + _display_handle: RawDisplayHandle, + window_handle: RawWindowHandle, ) -> Result { - if let RawWindowHandle::Windows(w) = window_handle.raw_window_handle() { + if let RawWindowHandle::Win32(w) = window_handle { let hwnd = w.hwnd as *mut _; Ok(Dx12Surface { hwnd }) } else { diff --git a/piet-gpu-hal/src/lib.rs b/piet-gpu-hal/src/lib.rs index 1e3fa32..0ec95c7 100644 --- a/piet-gpu-hal/src/lib.rs +++ b/piet-gpu-hal/src/lib.rs @@ -98,7 +98,7 @@ pub enum ImageFormat { A8, // 8 bit per pixel RGBA Rgba8, - // Match default surface format + // Format that matches the target surface Surface, } diff --git a/piet-gpu-hal/src/metal.rs b/piet-gpu-hal/src/metal.rs index a17f448..754c089 100644 --- a/piet-gpu-hal/src/metal.rs +++ b/piet-gpu-hal/src/metal.rs @@ -29,9 +29,10 @@ use objc::rc::autoreleasepool; use objc::runtime::{Object, BOOL, YES}; use objc::{class, msg_send, sel, sel_impl}; -use metal::{CGFloat, CommandBufferRef, MTLFeatureSet}; +use core_graphics_types::base::CGFloat; +use metal::{CommandBufferRef, MTLFeatureSet}; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use crate::{ BufferUsage, ComputePassDescriptor, Error, GpuInfo, ImageFormat, MapMode, WorkgroupLimits, @@ -139,9 +140,10 @@ impl MtlInstance { pub unsafe fn surface( &self, - window_handle: &dyn HasRawWindowHandle, + _display_handle: RawDisplayHandle, + window_handle: RawWindowHandle, ) -> Result { - if let RawWindowHandle::MacOS(handle) = window_handle.raw_window_handle() { + if let RawWindowHandle::AppKit(handle) = window_handle { Ok(Self::make_surface(handle.ns_view as id, handle.ns_window as id).unwrap()) } else { Err("can't create surface for window handle".into()) diff --git a/piet-gpu-hal/src/metal/util.rs b/piet-gpu-hal/src/metal/util.rs index 2b9b362..869f0a8 100644 --- a/piet-gpu-hal/src/metal/util.rs +++ b/piet-gpu-hal/src/metal/util.rs @@ -16,7 +16,7 @@ //! Utilities and types for Metal integration -use metal::{CGFloat, CGSize}; +use core_graphics_types::{base::CGFloat, geometry::CGSize}; #[link(name = "QuartzCore", kind = "framework")] extern "C" { diff --git a/piet-gpu-hal/src/mux.rs b/piet-gpu-hal/src/mux.rs index c4149d1..6f0731c 100644 --- a/piet-gpu-hal/src/mux.rs +++ b/piet-gpu-hal/src/mux.rs @@ -16,6 +16,8 @@ //! A multiplexer module that selects a back-end at runtime. +use raw_window_handle::RawDisplayHandle; +use raw_window_handle::RawWindowHandle; use smallvec::SmallVec; mux_cfg! { @@ -163,12 +165,13 @@ impl Instance { /// Create a surface from the specified window handle. pub unsafe fn surface( &self, - window_handle: &dyn raw_window_handle::HasRawWindowHandle, + display_handle: RawDisplayHandle, + window_handle: RawWindowHandle, ) -> Result { mux_match! { self; - Instance::Vk(i) => i.surface(window_handle).map(Surface::Vk), - Instance::Dx12(i) => i.surface(window_handle).map(Surface::Dx12), - Instance::Mtl(i) => i.surface(window_handle).map(Surface::Mtl), + Instance::Vk(i) => i.surface(display_handle, window_handle).map(Surface::Vk), + Instance::Dx12(i) => i.surface(display_handle, window_handle).map(Surface::Dx12), + Instance::Mtl(i) => i.surface(display_handle, window_handle).map(Surface::Mtl), } } diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 1ce698b..7189b8d 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -10,6 +10,7 @@ use ash::extensions::{ext::DebugUtils, khr}; use ash::vk::DebugUtilsLabelEXT; use ash::{vk, Device, Entry, Instance}; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use smallvec::SmallVec; use crate::backend::Device as DeviceTrait; @@ -157,7 +158,7 @@ impl VkInstance { pub fn new() -> Result { unsafe { let app_name = CString::new("VkToy").unwrap(); - let entry = Entry::new()?; + let entry = Entry::load()?; let mut layers = Layers::new(entry.enumerate_instance_layer_properties()?); if cfg!(debug_assertions) { @@ -165,7 +166,7 @@ impl VkInstance { .try_add(CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap()); } - let mut exts = Extensions::new(entry.enumerate_instance_extension_properties()?); + let mut exts = Extensions::new(entry.enumerate_instance_extension_properties(None)?); let mut has_debug_ext = false; if cfg!(debug_assertions) { has_debug_ext = exts.try_add(DebugUtils::name()); @@ -221,12 +222,15 @@ impl VkInstance { )?; let (dbg_loader, _dbg_callbk) = if has_debug_ext { + let flags = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION; let dbg_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() .message_severity( vk::DebugUtilsMessageSeverityFlagsEXT::ERROR | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING, ) - .message_type(vk::DebugUtilsMessageTypeFlagsEXT::all()) + .message_type(flags) .pfn_user_callback(Some(vulkan_debug_callback)); let dbg_loader = DebugUtils::new(&entry, &instance); let dbg_callbk = dbg_loader @@ -256,10 +260,17 @@ impl VkInstance { /// The caller is responsible for making sure that the instance outlives the surface. pub unsafe fn surface( &self, - window_handle: &dyn raw_window_handle::HasRawWindowHandle, + display_handle: RawDisplayHandle, + window_handle: RawWindowHandle, ) -> Result { Ok(VkSurface { - surface: ash_window::create_surface(&self.entry, &self.instance, window_handle, None)?, + surface: ash_window::create_surface( + &self.entry, + &self.instance, + display_handle, + window_handle, + None, + )?, surface_fn: khr::Surface::new(&self.entry, &self.instance), }) } @@ -273,8 +284,7 @@ impl VkInstance { /// but for now keep things simple. pub unsafe fn device(&self) -> Result { let devices = self.instance.enumerate_physical_devices()?; - let (pdevice, qfi) = - choose_device(&self.instance, &devices).ok_or("no suitable device")?; + let (pdevice, qfi) = choose_device(&self.instance, &devices).ok_or("no suitable device")?; let mut has_descriptor_indexing = false; let vk1_1 = self.vk_version >= vk::make_api_version(0, 1, 1, 0); @@ -1456,7 +1466,10 @@ unsafe fn choose_device( // both Metal and DX12 which do not require such validation. It might be worth // exposing this to the user in a future device enumeration API, which would // also allow selection between discrete and integrated devices. - if info.queue_flags.contains(vk::QueueFlags::COMPUTE | vk::QueueFlags::GRAPHICS) { + if info + .queue_flags + .contains(vk::QueueFlags::COMPUTE | vk::QueueFlags::GRAPHICS) + { return Some((*pdevice, ix as u32)); } } diff --git a/piet-gpu/Cargo.toml b/piet-gpu/Cargo.toml index 9240093..7b09e59 100644 --- a/piet-gpu/Cargo.toml +++ b/piet-gpu/Cargo.toml @@ -31,11 +31,12 @@ path = "../piet-scene" features = ["kurbo"] [dependencies] -png = "0.16.2" -rand = "0.7.3" +png = "0.17.6" +rand = "0.8.5" roxmltree = "0.13" -winit = "0.26.1" -clap = "2.33" +winit = {version = "0.27.3", default-features = false, features = ["x11", "wayland", "wayland-dlopen"]} +raw-window-handle = "0.5" +clap = "3.2.22" bytemuck = { version = "1.7.2", features = ["derive"] } kurbo = "0.8.3" diff --git a/piet-gpu/bin/android.rs b/piet-gpu/bin/android.rs index d94b77f..f41fa0b 100644 --- a/piet-gpu/bin/android.rs +++ b/piet-gpu/bin/android.rs @@ -6,10 +6,10 @@ //! Requires the [cargo-apk] tool. //! [cargo-apk]: https://crates.io/crates/cargo-apk -use raw_window_handle::android::AndroidHandle; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::{ + AndroidDisplayHandle, AndroidNdkWindowHandle, RawDisplayHandle, RawWindowHandle, +}; -use ndk::native_window::NativeWindow; use ndk_glue::Event; use piet_gpu_hal::{ @@ -25,10 +25,6 @@ fn main() { my_main().unwrap(); } -struct MyHandle { - handle: AndroidHandle, -} - // State required to render and present the contents struct GfxState { session: Session, @@ -51,9 +47,13 @@ fn my_main() -> Result<(), Error> { if let Some(window) = &*window { let width = window.width() as usize; let height = window.height() as usize; - let handle = get_handle(window); let instance = Instance::new(InstanceFlags::default())?; - let surface = unsafe { instance.surface(&handle)? }; + let mut android_handle = AndroidNdkWindowHandle::empty(); + android_handle.a_native_window = window.ptr().as_ptr() as *mut _; + let window_handle = RawWindowHandle::AndroidNdk(android_handle); + let display_handle = + RawDisplayHandle::Android(AndroidDisplayHandle::empty()); + let surface = unsafe { instance.surface(display_handle, window_handle)? }; gfx_state = Some(GfxState::new(&instance, Some(&surface), width, height)?); } else { println!("native window is sadly none"); @@ -72,24 +72,6 @@ fn my_main() -> Result<(), Error> { } } -fn get_handle(window: &NativeWindow) -> MyHandle { - println!( - "window = {:?}, {}x{}", - window.ptr(), - window.width(), - window.height() - ); - let mut handle = AndroidHandle::empty(); - handle.a_native_window = window.ptr().as_ptr() as *mut std::ffi::c_void; - MyHandle { handle } -} - -unsafe impl HasRawWindowHandle for MyHandle { - fn raw_window_handle(&self) -> RawWindowHandle { - RawWindowHandle::Android(self.handle) - } -} - impl GfxState { fn new( instance: &Instance, diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 4e52542..457dab5 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -219,10 +219,10 @@ fn trace_ptcl(buf: &[u32]) { fn main() -> Result<(), Error> { let matches = App::new("piet-gpu test") .arg(Arg::with_name("INPUT").index(1)) - .arg(Arg::with_name("flip").short("f").long("flip")) + .arg(Arg::with_name("flip").short('f').long("flip")) .arg( Arg::with_name("scale") - .short("s") + .short('s') .long("scale") .takes_value(true), ) @@ -280,7 +280,7 @@ fn main() -> Result<(), Error> { let ref mut w = BufWriter::new(file); let mut encoder = png::Encoder::new(w, WIDTH as u32, HEIGHT as u32); - encoder.set_color(png::ColorType::RGBA); + encoder.set_color(png::ColorType::Rgba); encoder.set_depth(png::BitDepth::Eight); let mut writer = encoder.write_header().unwrap(); diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index 870efe0..c97651d 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -4,6 +4,8 @@ use piet_scene::{Scene, SceneBuilder}; use clap::{App, Arg}; +use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; + use winit::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -18,10 +20,10 @@ const HEIGHT: usize = 1536; fn main() -> Result<(), Error> { let matches = App::new("piet-gpu test") .arg(Arg::with_name("INPUT").index(1)) - .arg(Arg::with_name("flip").short("f").long("flip")) + .arg(Arg::with_name("flip").short('f').long("flip")) .arg( Arg::with_name("scale") - .short("s") + .short('s') .long("scale") .takes_value(true), ) @@ -60,7 +62,9 @@ fn main() -> Result<(), Error> { let mut scene = Scene::default(); let mut simple_text = piet_gpu::SimpleText::new(); unsafe { - let surface = instance.surface(&window)?; + let display_handle = window.raw_display_handle(); + let window_handle = window.raw_window_handle(); + let surface = instance.surface(display_handle, window_handle)?; let device = instance.device()?; let mut swapchain = instance.swapchain(WIDTH / 2, HEIGHT / 2, &device, &surface)?; let session = Session::new(device); From 73833eb7c3d4bd2163623e896499d677f4806ef0 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 19 Oct 2022 15:05:23 -0400 Subject: [PATCH 8/8] update tests --- Cargo.lock | 2 +- tests/Cargo.toml | 2 +- tests/src/main.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8a9b7e..5cfbf46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -948,7 +948,7 @@ name = "piet-gpu-tests" version = "0.1.0" dependencies = [ "bytemuck", - "clap 2.34.0", + "clap 3.2.22", "kurbo 0.7.1", "piet-gpu", "piet-gpu-hal", diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 1f0760a..ea320b9 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" default = ["piet-gpu"] [dependencies] -clap = "2.33" +clap = "3.2.22" bytemuck = "1.7.2" kurbo = "0.7.1" rand = "0.7.3" diff --git a/tests/src/main.rs b/tests/src/main.rs index 96504f1..5f72708 100644 --- a/tests/src/main.rs +++ b/tests/src/main.rs @@ -42,27 +42,27 @@ fn main() { let matches = App::new("piet-gpu-tests") .arg( Arg::with_name("verbose") - .short("v") + .short('v') .long("verbose") .help("Verbose reporting of results"), ) .arg( Arg::with_name("groups") - .short("g") + .short('g') .long("groups") .help("Groups to run") .takes_value(true), ) .arg( Arg::with_name("size") - .short("s") + .short('s') .long("size") .help("Size of tests") .takes_value(true), ) .arg( Arg::with_name("n_iter") - .short("n") + .short('n') .long("n_iter") .help("Number of iterations") .takes_value(true),