diff --git a/.clippy.toml b/.clippy.toml index 198f214bf..0a8bf380e 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -doc-valid-idents = ["WebGPU", ".."] +doc-valid-idents = ["WebGPU", "PostScript", ".."] diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..4d28da71c --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,100 @@ + +# Architecture + +This document should be updated semi-regularly. Feel free to open an issue if it hasn't been updated in more than a year. + +## Goals + +The major goal of Vello is to provide a high quality GPU accelerated renderer suitable for a range of 2D graphics applications, including rendering for GUI applications, creative tools, and scientific visualization. + +Vello emerges from being a research project, which attempts to answer these hypotheses: + +- To what extent is a compute-centered approach better than rasterization ([Direct2D])? +- To what extent do advanced GPU features (subgroups, descriptor arrays, device-scoped barriers) help? +- Can we improve quality and extend the imaging model in useful ways? + +Another goal of the overall project is to explain how the renderer is built, and to advance the state of building applications on GPU compute shaders more generally. +Much of the progress on Vello is documented in blog entries. +See [doc/blogs.md](doc/blogs.md) for pointers to those. + +Ideally, we'd like our documentation to be more structured; we may refactor it in the future (see [#488]). + + +## Roadmap + +The [roadmap for 2023](doc/roadmap_2023.md) is still largely applicable. +The "Semi-stable encoding format" section and most of the "CPU fallback" section can be considered implemented. + +Our current priority is to fill in missing features and to fix rendering artifacts, so that Vello can reach feature parity with other 2D graphics engines. + + +## File structure + +The repository is structured as such: + +- `crates/` + - `encoding/` - Types that represent the data that needs to be rendered. + - `shaders/` - Infrastructure to preprocess and cross-compile shaders at compile time; see "Shader templating". Note that the `vello` crate doesn't currently import this crate (see [#467]). + - `tests/` - Helper code for writing tests; current has a single smoke test and not much else. +- `doc/` - Various documents detailing the vision for Vello as it was developed. This directory should probably be refactored away; adding to it not recommended. +- `examples/` - Example projects using Vello. Each example is its own crate, with its own dependencies. The simplest example is the `shapes` one. +- `integrations/vello_svg` - An SVG rendered based on Vello and usvg. Used in examples. May be moved to `crates/` in the future. +- `shader/` - This is where the magic happens. WGSL shaders that define the compute operations (often variations of prefix sum) that Vello does to render a scene. + - `shared/` - Shared types, functions and constants included in other shaders through non-standard `#import` preprocessor directives (see "Shader templating"). +- `src/` - Code for the main `vello` crate. + - `shaders/` - Same as `crates/shaders/` above. The duplication should eventually be removed (see [#467]). + - `cpu_shader/` - Functions that perform the same work as their equivalently-named WGSL shaders for the CPU fallbacks. The name is a bit loose; they're "shaders" in the sense that they work on resource bindings with the exact same layout as actual GPU shaders. + + +## Shader templating + +WGSL has no meta-programming support, which limits code-sharing. +We use a strategy common to many projects (eg Bevy) which is to implement a limited, simple preprocessor for our shaders. + +This preprocessor implements the following directives: + +1. `import`, which imports from `shader/shared` +2. `ifdef`, `ifndef`, `else` and `endif`, as standard. + These must be at the start of their lines. + Note that there is no support for creating definitions in-shader, these are only specified externally (in `src/shaders.rs`). + Note also that this definitions cannot currently be used in-code (`import`s may be used instead) + +This format is compatible with [`wgsl-analyzer`], which we recommend using. +If you run into any issues, please report them on Zulip ([#gpu > wgsl-analyzer issues](https://xi.zulipchat.com/#narrow/stream/197075-gpu/topic/wgsl-analyzer.20issues)), and/or on the [`wgsl-analyzer`] issue tracker. +Note that new imports must currently be added to `.vscode/settings.json` for this support to work correctly. +`wgsl-analyzer` only supports imports in very few syntactic locations, so we limit their use to these places. + + +## Path encoding + +See [Path segment encoding](./doc/pathseg.md) document. + + +## Intermediary layers + +There are multiple layers of separation between "draw shape in Scene" and "commands are sent to wgpu": + +- First, everything you do in `Scene` appends data to an `Encoding`. +The encoding owns multiple buffers representing compressed path commands, draw commands, transforms, etc. It's a linearized representation of the things you asked the `Scene` to draw. +- From that encoding, we generate a `Recording`, which is an array of commands; each `Command` represents an operation interacting with the GPU (think "upload buffer", "dispatch", "download buffer", etc). +- We then use `WgpuEngine` to send these commands to the actual GPU. + +In principle, other backends could consume a `Recording`, but for now the only implemented wgpu backend is `WgpuEngine`. + + +### CPU rendering + +The code in `cpu_shader/*.rs` and `cpu_dispatch.rs` provides *some* support for CPU-side rendering. It's in an awkward place right now: + +- It's called through WgpuEngine, so the dependency on wgpu is still there. +- Fine rasterization (the part at the end that puts pixels on screen) doesn't work in CPU yet (see [#386]). +- Every single WGSL shader needs a CPU equivalent, which is pretty cumbersome. + +Still, it's useful for testing and debugging. + + +[`wgsl-analyzer`]: https://marketplace.visualstudio.com/items?itemName=wgsl-analyzer.wgsl-analyzer +[direct2d]: https://docs.microsoft.com/en-us/windows/win32/direct2d/direct2d-portal +[#488]: https://github.com/linebender/vello/issues/488 +[#467]: https://github.com/linebender/vello/issues/467 +[#386]: https://github.com/linebender/vello/issues/386 diff --git a/README.md b/README.md index 76aa28eb5..6a9f0b4fa 100644 --- a/README.md +++ b/README.md @@ -14,18 +14,110 @@ -Vello is a 2d graphics rendering engine, using [`wgpu`]. -It efficiently draws large 2d scenes with interactive or near-interactive performance. - - - -It is used as the rendering backend for [Xilem], a UI toolkit. +Vello is an experimental 2D graphics rendering engine written in Rust, with a focus on GPU compute. +It can draw large 2D scenes with interactive or near-interactive performance, using [`wgpu`] for GPU access. Quickstart to run an example program: ```shell cargo run -p with_winit ``` +![image](https://github.com/linebender/vello/assets/8573618/cc2b742e-2135-4b70-8051-c49aeddb5d19) + +It is used as the rendering backend for [Xilem], a Rust GUI toolkit. + +> [!WARNING] +> Vello can currently be considered in an alpha state. In particular, we're still working on the following: +> +> - [Major rendering artifacts when drawing more than 64k objects](https://github.com/linebender/vello/issues/334). +> - [Implementing blur and filter effects](https://github.com/linebender/vello/issues/476). +> - [Properly implementing strokes](https://github.com/linebender/vello/issues/303) and [supporting all SVG stroke caps](https://github.com/linebender/vello/issues/280). +> - [Conflations artifacts](https://github.com/linebender/vello/issues/49). +> - [GPU memory allocation strategy](https://github.com/linebender/vello/issues/366) +> - [Glyph caching](https://github.com/linebender/vello/issues/204) + +## Motivation + +Vello is meant to fill the same place in the graphics stack as other vector graphics renderers like [Skia](https://skia.org/), [Cairo](https://www.cairographics.org/), and its predecessor project [Piet](https://github.com/linebender/piet). +On a basic level, that means it provides tools to render shapes, images, gradients, text, etc, using a PostScript-inspired API, the same that powers SVG files and [the browser `` element](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D). + +Vello's selling point is that it gets better performance than other renderers by better leveraging the GPU. +In traditional PostScript-style renderers, some steps of the render process like sorting and clipping either need to be handled in the CPU or done through the use of intermediary textures. +Vello avoids this by using prefix-sum algorithms to parallelize work that usually needs to happen in sequence, so that work can be offloaded to the GPU with minimal use of temporary buffers. + +This means that Vello needs a GPU with support for compute shaders to run. + + +## Getting started + +Vello is meant to be integrated deep in UI render stacks. +While drawing in a Vello scene is easy, actually rendering that scene to a surface requires setting up a wgpu context, which is a non-trivial task. + +To use Vello as the renderer for your PDF reader / GUI toolkit / etc, your code will have to look roughly like this: + +```rust +// Initialize wgpu and get handles +let (width, height) = ...; +let device: wgpu::Device = ...; +let queue: wgpu::Queue = ...; +let surface: wgpu::Surface<'_> = ...; +let texture_format: wgpu::TextureFormat = ...; +let mut renderer = Renderer::new( + &device, + RendererOptions { + surface_format: Some(texture_format), + use_cpu: false, + antialiasing_support: vello::AaSupport::all(), + num_init_threads: NonZeroUsize::new(1), + }, +).expect("Failed to create renderer"); + +// Create scene and draw stuff in it +let mut scene = vello::Scene::new(); +scene.fill( + vello::peniko::Fill::NonZero, + vello::Affine::IDENTITY, + vello::Color::rgb8(242, 140, 168), + None, + &vello::Circle::new((420.0, 200.0), 120.0), +); + +// Draw more stuff +scene.push_layer(...); +scene.fill(...); +scene.stroke(...); +scene.pop_layer(...); + +// Render to your window/buffer/etc. +let surface_texture = surface.get_current_texture() + .expect("failed to get surface texture"); +renderer + .render_to_surface( + &device, + &queue, + &scene, + &surface_texture, + &vello::RenderParams { + base_color: Color::BLACK, // Background color + width, + height, + antialiasing_method: AaConfig::Msaa16, + }, + ) + .expect("Failed to render to surface"); +surface_texture.present(); +``` + +See the [`examples/`](https://github.com/linebender/vello/tree/main/examples) folder to see how that code integrates with frameworks like winit and bevy. + + +## Performance + +We've observed 177 fps for the paris-30k test scene on an M1 Max, at a resolution of 1600 pixels square, which is excellent performance and represents something of a best case for the engine. + +More formal benchmarks are on their way. + + ## Integrations ### SVG @@ -42,14 +134,14 @@ A separate integration for playing Lottie animations is available through the [` ## Examples -Our examples are provided in separate packages in the [`examples`](examples) folder. +Our examples are provided in separate packages in the [`examples`](https://github.com/linebender/vello/tree/main/examples) folder. This allows them to have independent dependencies and faster builds. Examples must be selected using the `--package` (or `-p`) Cargo flag. ### Winit -Our [winit] example ([examples/with_winit](examples/with_winit)) demonstrates rendering to a [winit] window. -By default, this renders [GhostScript Tiger] all SVG files in [examples/assets/downloads](examples/assets/downloads) directory (using [`vello_svg`](#svg)). +Our [winit] example ([examples/with_winit](https://github.com/linebender/vello/tree/main/examples/with_winit)) demonstrates rendering to a [winit] window. +By default, this renders the [GhostScript Tiger] as well as all SVG files you add in the [examples/assets/downloads/](https://github.com/linebender/vello/tree/main/examples/assets/downloads) directory using [`vello_svg`](#svg). A custom list of SVG file paths (and directories to render all SVG files from) can be provided as arguments instead. It also includes a collection of test scenes showing the capabilities of `vello`, which can be shown with `--test-scenes`. @@ -68,7 +160,7 @@ cargo run -p with_winit -- download ### Bevy -The [Bevy] example ([examples/with_bevy](examples/with_bevy)) demonstrates using Vello within a [Bevy] application. +The [Bevy] example ([examples/with_bevy](https://github.com/linebender/vello/tree/main/examples/with_bevy)) demonstrates using Vello within a [Bevy] application. This currently draws to a [`wgpu`] `Texture` using `vello`, then uses that texture as the faces of a cube. ```shell @@ -127,52 +219,13 @@ keystore_password = "android" ## Community -[![Xi Zulip](https://img.shields.io/badge/Xi%20Zulip-%23gpu-blue?logo=Zulip)](https://xi.zulipchat.com/#narrow/stream/197075-gpu) - -Discussion of Vello development happens in the [Xi Zulip](https://xi.zulipchat.com/), specifically the [#gpu stream](https://xi.zulipchat.com/#narrow/stream/197075-gpu). All public content can be read without logging in - -## Shader templating - -We implement a limited, simple preprocessor for our shaders, as wgsl has insufficient code-sharing for our needs. - -This implements only classes of statements. - -1. `import`, which imports from `shader/shared` -2. `ifdef`, `ifndef`, `else` and `endif`, as standard. - These must be at the start of their lines. - Note that there is no support for creating definitions in-shader, these are only specified externally (in `src/shaders.rs`). - Note also that this definitions cannot currently be used in-code (`import`s may be used instead) +Discussion of Vello development happens in the [Xi Zulip](https://xi.zulipchat.com/), specifically the [#gpu stream](https://xi.zulipchat.com/#narrow/stream/197075-gpu). All public content can be read without logging in. -This format is compatible with [`wgsl-analyzer`], which we recommend using. -If you run into any issues, please report them on Zulip ([#gpu > wgsl-analyzer issues](https://xi.zulipchat.com/#narrow/stream/197075-gpu/topic/wgsl-analyzer.20issues)), and/or on the [`wgsl-analyzer`] issue tracker. -Note that new imports must currently be added to `.vscode/settings.json` for this support to work correctly. -`wgsl-analyzer` only supports imports in very few syntactic locations, so we limit their use to these places. - -## GPU abstraction - -Our rendering code does not directly interact with `wgpu`. -Instead, we generate a `Recording`, a simple value type, then an `Engine` plays that recording to the actual GPU. -The only currently implemented `Engine` uses `wgpu`. - -The idea is that this can abstract easily over multiple GPU back-ends, without either the render logic needing to be polymorphic or having dynamic dispatch at the GPU abstraction. -The goal is to be more agile. - -## Goals - -The major goal of Vello is to provide a high quality GPU accelerated renderer suitable for a range of 2D graphics applications, including rendering for GUI applications, creative tools, and scientific visualization. -The [roadmap for 2023](doc/roadmap_2023.md) explains the goals and plans for the next few months of development - -Vello emerges from being a research project, which attempts to answer these hypotheses: - -- To what extent is a compute-centered approach better than rasterization ([Direct2D])? - -- To what extent do "advanced" GPU features (subgroups, descriptor arrays, device-scoped barriers) help? - -- Can we improve quality and extend the imaging model in useful ways? +Contributions are welcome by pull request. The [Rust code of conduct] applies. -Another goal of the overall project is to explain how the renderer is built, and to advance the state of building applications on GPU compute shaders more generally. -Much of the progress on Vello is documented in blog entries. -See [doc/blogs.md](doc/blogs.md) for pointers to those. +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +licensed as noted in the "License" section, without any additional terms or conditions. ## History @@ -184,7 +237,7 @@ This succeeded the previous prototype, [piet-metal], and included work adapted f The decision to lay down `piet-gpu-hal` in favor of WebGPU is discussed in detail in the blog post [Requiem for piet-gpu-hal]. -A [vision](doc/vision.md) document dated December 2020 explained the longer-term goals of the project, and how we might get there. +A [vision](https://github.com/linebender/vello/tree/main/doc/vision.md) document dated December 2020 explained the longer-term goals of the project, and how we might get there. Many of these items are out-of-date or completed, but it still may provide some useful background. ## Related projects @@ -208,25 +261,16 @@ Licensed under either of at your option. -In addition, all files in the [`shader`](shader) and [`src/cpu_shader`](src/cpu_shader) +In addition, all files in the [`shader`](https://github.com/linebender/vello/tree/main/shader) and [`src/cpu_shader`](https://github.com/linebender/vello/tree/main/src/cpu_shader) directories and subdirectories thereof are alternatively licensed under -the Unlicense ([shader/UNLICENSE](shader/UNLICENSE) or ). +the Unlicense ([shader/UNLICENSE](https://github.com/linebender/vello/tree/main/shader/UNLICENSE) or ). For clarity, these files are also licensed under either of the above licenses. The intent is for this research to be used in as broad a context as possible. -The files in subdirectories of the [`examples/assets`](examples/assets) directory are licensed solely under +The files in subdirectories of the [`examples/assets`](https://github.com/linebender/vello/tree/main/examples/assets) directory are licensed solely under their respective licenses, available in the `LICENSE` file in their directories. -## Contribution - -Contributions are welcome by pull request. The [Rust code of conduct] applies. - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -licensed as above, without any additional terms or conditions. - [piet-metal]: https://github.com/linebender/piet-metal -[direct2d]: https://docs.microsoft.com/en-us/windows/win32/direct2d/direct2d-portal [`wgpu`]: https://wgpu.rs/ [Xilem]: https://github.com/linebender/xilem/ [rust code of conduct]: https://www.rust-lang.org/policies/code-of-conduct @@ -236,5 +280,4 @@ licensed as above, without any additional terms or conditions. [GhostScript tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg [winit]: https://github.com/rust-windowing/winit [Bevy]: https://bevyengine.org/ -[`wgsl-analyzer`]: https://marketplace.visualstudio.com/items?itemName=wgsl-analyzer.wgsl-analyzer [Requiem for piet-gpu-hal]: https://raphlinus.github.io/rust/gpu/2023/01/07/requiem-piet-gpu-hal.html diff --git a/crates/encoding/src/encoding.rs b/crates/encoding/src/encoding.rs index 0ac663b9a..5bf7e7e69 100644 --- a/crates/encoding/src/encoding.rs +++ b/crates/encoding/src/encoding.rs @@ -69,6 +69,9 @@ impl Encoding { self.path_tags.is_empty() } + #[doc(alias = "clear")] + // This is not called "clear" because "clear" has other implications + // in graphics contexts. /// Clears the encoding. pub fn reset(&mut self) { self.transforms.clear(); @@ -472,6 +475,9 @@ pub struct Resources { #[cfg(feature = "full")] impl Resources { + #[doc(alias = "clear")] + // This is not called "clear" because "clear" has other implications + // in graphics contexts. fn reset(&mut self) { self.patches.clear(); self.color_stops.clear(); diff --git a/src/cpu_dispatch.rs b/src/cpu_dispatch.rs index 901f5c127..b0f48df4e 100644 --- a/src/cpu_dispatch.rs +++ b/src/cpu_dispatch.rs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Support for CPU implementations of compute shaders. +//! +//! Note that while this CPU implementation is useful for testing and debugging, +//! a full CPU fallback for targets without wgpu hasn't been implemented yet. use std::{ cell::{Ref, RefCell, RefMut}, diff --git a/src/lib.rs b/src/lib.rs index 398ae5ed6..f96d75d71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,86 @@ #![warn(clippy::doc_markdown, clippy::semicolon_if_nothing_returned)] +//! Vello is an experimental 2d graphics rendering engine written in Rust, using [`wgpu`]. +//! It efficiently draws large 2d scenes with interactive or near-interactive performance. +//! +//! ![image](https://github.com/linebender/vello/assets/8573618/cc2b742e-2135-4b70-8051-c49aeddb5d19) +//! +//! +//! ## Motivation +//! +//! Vello is meant to fill the same place in the graphics stack as other vector graphics renderers like [Skia](https://skia.org/), [Cairo](https://www.cairographics.org/), and its predecessor project [Piet](https://www.cairographics.org/). +//! On a basic level, that means it provides tools to render shapes, images, gradients, texts, etc, using a PostScript-inspired API, the same that powers SVG files and [the browser `` element](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D). +//! +//! Vello's selling point is that it gets better performance than other renderers by better leveraging the GPU. +//! In traditional PostScript-style renderers, some steps of the render process like sorting and clipping either need to be handled in the CPU or done through the use of intermediary textures. +//! Vello avoids this by using prefix-scan algorithms to parallelize work that usually needs to happen in sequence, so that work can be offloaded to the GPU with minimal use of temporary buffers. +//! +//! This means that Vello needs a GPU with support for compute shaders to run. +//! +//! +//! ## Getting started +//! +//! Vello is meant to be integrated deep in UI render stacks. +//! While drawing in a Vello scene is easy, actually rendering that scene to a surface setting up a wgpu context, which is a non-trivial task. +//! +//! To use Vello as the renderer for your PDF reader / GUI toolkit / etc, your code will have to look roughly like this: +//! +//! ```ignore +//! // Initialize wgpu and get handles +//! let (width, height) = ...; +//! let device: wgpu::Device = ...; +//! let queue: wgpu::Queue = ...; +//! let surface: wgpu::Surface<'_> = ...; +//! let texture_format: wgpu::TextureFormat = ...; +//! let mut renderer = Renderer::new( +//! &device, +//! RendererOptions { +//! surface_format: Some(texture_format), +//! use_cpu: false, +//! antialiasing_support: vello::AaSupport::all(), +//! num_init_threads: NonZeroUsize::new(1), +//! }, +//! ).expect("Failed to create renderer"); +//! +//! // Create scene and draw stuff in it +//! let mut scene = vello::Scene::new(); +//! scene.fill( +//! vello::peniko::Fill::NonZero, +//! vello::Affine::IDENTITY, +//! vello::Color::rgb8(242, 140, 168), +//! None, +//! &vello::Circle::new((420.0, 200.0), 120.0), +//! ); +//! +//! // Draw more stuff +//! scene.push_layer(...); +//! scene.fill(...); +//! scene.stroke(...); +//! scene.pop_layer(...); +//! +//! // Render to your window/buffer/etc. +//! let surface_texture = surface.get_current_texture() +//! .expect("failed to get surface texture"); +//! renderer +//! .render_to_surface( +//! &device, +//! &queue, +//! &scene, +//! &surface_texture, +//! &vello::RenderParams { +//! base_color: Color::BLACK, // Background color +//! width, +//! height, +//! antialiasing_method: AaConfig::Msaa16, +//! }, +//! ) +//! .expect("Failed to render to surface"); +//! surface_texture.present(); +//! ``` +//! +//! See the [`examples/`](https://github.com/linebender/vello/tree/main/examples) folder to see how that code integrates with frameworks like winit and bevy. + mod cpu_dispatch; mod cpu_shader; mod recording; diff --git a/src/scene.rs b/src/scene.rs index 136dc8b2c..6fffd358d 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -6,7 +6,14 @@ use peniko::{BlendMode, BrushRef, Color, Fill, Font, Image, StyleRef}; use skrifa::instance::NormalizedCoord; use vello_encoding::{Encoding, Glyph, GlyphRun, Patch, Transform}; -/// Encoded definition of a scene and associated resources. +// TODO - Document invariants and edge cases (#470) +// - What happens when we pass a transform matrix with NaN values to the Scene? +// - What happens if a push_layer isn't matched by a pop_layer? + +/// The main datatype for rendering graphics. +/// +/// A Scene stores a sequence of drawing commands, their context, and the +/// associated resources, which can later be rendered. #[derive(Clone, Default)] pub struct Scene { encoding: Encoding, @@ -28,20 +35,25 @@ impl Scene { &self.encoding } - /// Pushes a new layer bound by the specified shape and composed with + /// Pushes a new layer clipped by the specified shape and composed with /// previous layers using the specified blend mode. + /// + /// Every drawing command after this call will be clipped by the shape + /// until the layer is popped. + /// + /// **However, the transforms are *not* saved or modified by the layer stack.** pub fn push_layer( &mut self, blend: impl Into, alpha: f32, transform: Affine, - shape: &impl Shape, + clip: &impl Shape, ) { let blend = blend.into(); self.encoding .encode_transform(Transform::from_kurbo(&transform)); self.encoding.encode_fill_style(Fill::NonZero); - if !self.encoding.encode_shape(shape, true) { + if !self.encoding.encode_shape(clip, true) { // If the layer shape is invalid, encode a valid empty path. This suppresses // all drawing until the layer is popped. self.encoding @@ -161,7 +173,10 @@ impl Scene { DrawGlyphs::new(&mut self.encoding, font) } - /// Appends a fragment to the scene. + /// Appends a child scene. + /// + /// The given transform is applied to every transform in the child. + /// This is an O(N) operation. pub fn append(&mut self, other: &Scene, transform: Option) { self.encoding.append( &other.encoding, diff --git a/src/shaders/preprocess.rs b/src/shaders/preprocess.rs index a5c41dbc5..142161668 100644 --- a/src/shaders/preprocess.rs +++ b/src/shaders/preprocess.rs @@ -1,6 +1,9 @@ // Copyright 2022 the Vello Authors // SPDX-License-Identifier: Apache-2.0 OR MIT +// TODO (#467) - Remove this file and use vello_shaders crate instead, +// then update ARCHITECTURE.md. + use std::{ collections::{HashMap, HashSet}, fs,