diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..c192934 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,33 @@ +name: Rust + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + check-rs: + runs-on: ubuntu-latest + env: + RUSTFLAGS: "-D warnings" + steps: + - name: Install Protoc + uses: arduino/setup-protoc@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v3 + - name: Check cargo fmt compliance + run: cargo fmt --all -- --check + - name: Check no rustc warnings + run: cargo check --tests + - name: Check for clippy warnings + run: cargo clippy + - name: Build + run: cargo build --verbose +# # Tests currently fail with `MakeWgpuAdapterError` +# - name: Run tests +# run: cargo test --verbose diff --git a/.gitignore b/.gitignore index 9e5be58..310fdc0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /.idea /vega-wgpu-renderer/tests/output/ +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index ff68652..47e6caa 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,7 @@ image export, and to VegaFusion to support serverside rendering of large marks. To start with, the most valuable contribution of this project is probably the testing infrastructure. By relying on vl-convert, a collection of input Vega specs are rendered to PNG and converted to scene graphs. The GPU rendered PNG images are then compared for similarity to the baselines using structural similarity. See the `gen-test-data` -crate for more information. \ No newline at end of file +crate for more information. + +Note: These tests aren't running on GitHub Actions yet due to a `MakeWgpuAdapterError` error that +needs to be diagnosed. diff --git a/gen-test-data/src/main.rs b/gen-test-data/src/main.rs index 47ae09e..dd15697 100644 --- a/gen-test-data/src/main.rs +++ b/gen-test-data/src/main.rs @@ -82,21 +82,21 @@ fn main() { /// Get the image width from the SVG fn get_svg_width(svg: &str) -> u32 { let width_re = Regex::new("width=\"(\\d+)\"").unwrap(); - let captures = width_re.captures(&svg).expect("Missing width"); + let captures = width_re.captures(svg).expect("Missing width"); captures.get(1).unwrap().as_str().parse().unwrap() } /// Get the image height from the SVG fn get_svg_height(svg: &str) -> u32 { let width_re = Regex::new("height=\"(\\d+)\"").unwrap(); - let captures = width_re.captures(&svg).expect("Missing height"); + let captures = width_re.captures(svg).expect("Missing height"); captures.get(1).unwrap().as_str().parse().unwrap() } /// Get the renderer origin by extracting the first `translate` transform from the SVG fn get_svg_origin(svg: &str) -> (u32, u32) { let width_re = Regex::new("translate\\((\\d+),(\\d+)\\)").unwrap(); - let captures = width_re.captures(&svg).expect("Missing height"); + let captures = width_re.captures(svg).expect("Missing height"); let origin_x: u32 = captures.get(1).unwrap().as_str().parse().unwrap(); let origin_y: u32 = captures.get(2).unwrap().as_str().parse().unwrap(); (origin_x, origin_y) diff --git a/vega-wgpu-renderer/src/lib.rs b/vega-wgpu-renderer/src/lib.rs index eb64bfd..de8ae5c 100644 --- a/vega-wgpu-renderer/src/lib.rs +++ b/vega-wgpu-renderer/src/lib.rs @@ -3,23 +3,16 @@ pub mod renderers; pub mod scene; pub mod specs; -use std::iter; -use wgpu::util::DeviceExt; - -use winit::dpi::{LogicalSize, PhysicalSize, Size}; +use winit::dpi::{PhysicalSize, Size}; use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, - window::{Window, WindowBuilder}, + window::WindowBuilder, }; -use crate::renderers::canvas::{Canvas, PngCanvas, WindowCanvas}; -use crate::scene::rect::RectInstance; +use crate::renderers::canvas::{Canvas, WindowCanvas}; use crate::scene::scene_graph::SceneGraph; -use crate::scene::symbol::SymbolInstance; use crate::specs::dims::SceneGraphDims; -use crate::specs::group::GroupItemSpec; -use crate::specs::mark::{MarkContainerSpec, MarkSpec}; use crate::specs::SceneGraphSpec; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; @@ -81,7 +74,7 @@ pub async fn run() { .expect("Failed to parse scene graph"); // Save to png - let mut canvas = WindowCanvas::new(window, origin).await.unwrap(); + let mut canvas = WindowCanvas::new(window).await.unwrap(); canvas.set_scene(&scene_graph); diff --git a/vega-wgpu-renderer/src/renderers/canvas.rs b/vega-wgpu-renderer/src/renderers/canvas.rs index 6869c6a..9a4733d 100644 --- a/vega-wgpu-renderer/src/renderers/canvas.rs +++ b/vega-wgpu-renderer/src/renderers/canvas.rs @@ -4,14 +4,14 @@ use crate::renderers::rect::RectShader; use crate::renderers::rule::RuleShader; use crate::renderers::symbol::SymbolShader; use crate::renderers::text::TextMarkRenderer; -use crate::scene::rect::{RectInstance, RectMark}; +use crate::scene::rect::RectMark; use crate::scene::rule::RuleMark; use crate::scene::scene_graph::{SceneGraph, SceneGroup, SceneMark}; -use crate::scene::symbol::{SymbolInstance, SymbolMark}; +use crate::scene::symbol::SymbolMark; use crate::scene::text::TextMark; use image::imageops::crop_imm; use wgpu::{ - Adapter, Buffer, BufferAddress, BufferDescriptor, BufferUsages, CommandBuffer, CommandEncoder, + Adapter, Buffer, BufferAddress, BufferDescriptor, BufferUsages, CommandBuffer, CommandEncoderDescriptor, Device, DeviceDescriptor, Extent3d, ImageCopyBuffer, ImageCopyTexture, ImageDataLayout, LoadOp, MapMode, Operations, Origin3d, PowerPreference, Queue, RenderPassColorAttachment, RenderPassDescriptor, RequestAdapterOptions, StoreOp, @@ -49,8 +49,8 @@ pub trait Canvas { fn add_symbol_mark(&mut self, mark: &SymbolMark) { self.add_mark_renderer(MarkRenderer::Geom(GeomMarkRenderer::new( - &self.device(), - self.uniform().clone(), + self.device(), + *self.uniform(), self.texture_format(), self.sample_count(), Box::new(SymbolShader::new(mark.shape)), @@ -60,8 +60,8 @@ pub trait Canvas { fn add_rect_mark(&mut self, mark: &RectMark) { self.add_mark_renderer(MarkRenderer::Geom(GeomMarkRenderer::new( - &self.device(), - self.uniform().clone(), + self.device(), + *self.uniform(), self.texture_format(), self.sample_count(), Box::new(RectShader::new()), @@ -71,8 +71,8 @@ pub trait Canvas { fn add_rule_mark(&mut self, mark: &RuleMark) { self.add_mark_renderer(MarkRenderer::Geom(GeomMarkRenderer::new( - &self.device(), - self.uniform().clone(), + self.device(), + *self.uniform(), self.texture_format(), self.sample_count(), Box::new(RuleShader::new()), @@ -82,9 +82,9 @@ pub trait Canvas { fn add_text_mark(&mut self, mark: &TextMark) { self.add_mark_renderer(MarkRenderer::Text(TextMarkRenderer::new( - &self.device(), - &self.queue(), - self.uniform().clone(), + self.device(), + self.queue(), + *self.uniform(), self.texture_format(), self.sample_count(), mark.instances.clone(), @@ -257,11 +257,10 @@ pub struct WindowCanvas { size: winit::dpi::PhysicalSize, marks: Vec, uniform: CanvasUniform, - origin: [f32; 2], } impl WindowCanvas { - pub async fn new(window: Window, origin: [f32; 2]) -> Result { + pub async fn new(window: Window) -> Result { let size = window.inner_size(); let instance = make_wgpu_instance(); @@ -316,7 +315,6 @@ impl WindowCanvas { window, uniform, marks: Vec::new(), - origin, }) } @@ -328,7 +326,7 @@ impl WindowCanvas { &self.window } - pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + pub fn resize(&mut self, _new_size: winit::dpi::PhysicalSize) { // if new_size.width > 0 && new_size.height > 0 { // self.size = new_size; // self.config.width = new_size.width; @@ -433,7 +431,6 @@ pub struct PngCanvas { uniform: CanvasUniform, width: f32, height: f32, - origin: [f32; 2], pub texture_view: TextureView, pub output_buffer: Buffer, pub texture: Texture, @@ -443,7 +440,7 @@ pub struct PngCanvas { } impl PngCanvas { - pub async fn new(width: f32, height: f32, origin: [f32; 2]) -> Result { + pub async fn new(width: f32, height: f32) -> Result { let instance = make_wgpu_instance(); let adapter = make_wgpu_adapter(&instance, None).await?; let (device, queue) = request_wgpu_device(&adapter).await?; @@ -509,7 +506,6 @@ impl PngCanvas { width, height, uniform, - origin, texture, texture_view, output_buffer, @@ -620,7 +616,7 @@ impl PngCanvas { }; self.output_buffer.unmap(); - Ok((img)) + Ok(img) } } diff --git a/vega-wgpu-renderer/src/renderers/mark.rs b/vega-wgpu-renderer/src/renderers/mark.rs index 4a5b51a..4e90ab4 100644 --- a/vega-wgpu-renderer/src/renderers/mark.rs +++ b/vega-wgpu-renderer/src/renderers/mark.rs @@ -1,6 +1,5 @@ use crate::renderers::canvas::CanvasUniform; use crate::renderers::vertex::Vertex; -use crate::scene::rect::RectInstance; use wgpu::util::DeviceExt; use wgpu::{CommandBuffer, Device, TextureFormat, TextureView}; @@ -17,13 +16,10 @@ pub trait MarkShader { pub struct GeomMarkRenderer { render_pipeline: wgpu::RenderPipeline, vertex_buffer: wgpu::Buffer, - num_vertices: u32, index_buffer: wgpu::Buffer, num_indices: u32, instance_buffer: wgpu::Buffer, num_instances: u32, - uniform_buffer: wgpu::Buffer, - uniform: CanvasUniform, uniform_bind_group: wgpu::BindGroup, } @@ -126,7 +122,6 @@ impl GeomMarkRenderer { contents: bytemuck::cast_slice(mark_shader.verts()), usage: wgpu::BufferUsages::VERTEX, }); - let num_vertices = mark_shader.verts().len() as u32; let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Index Buffer"), @@ -145,13 +140,10 @@ impl GeomMarkRenderer { Self { render_pipeline, vertex_buffer, - num_vertices, index_buffer, num_indices, instance_buffer, num_instances, - uniform_buffer, - uniform, uniform_bind_group, } } @@ -190,6 +182,6 @@ impl GeomMarkRenderer { render_pass.draw_indexed(0..self.num_indices, 0, 0..self.num_instances); } - return mark_encoder.finish(); + mark_encoder.finish() } } diff --git a/vega-wgpu-renderer/src/renderers/rect.rs b/vega-wgpu-renderer/src/renderers/rect.rs index 0295432..3cdf22f 100644 --- a/vega-wgpu-renderer/src/renderers/rect.rs +++ b/vega-wgpu-renderer/src/renderers/rect.rs @@ -10,6 +10,12 @@ pub struct RectShader { fragment_entry_point: String, } +impl Default for RectShader { + fn default() -> Self { + Self::new() + } +} + impl RectShader { pub fn new() -> Self { Self { diff --git a/vega-wgpu-renderer/src/renderers/rule.rs b/vega-wgpu-renderer/src/renderers/rule.rs index adf248c..5fb5690 100644 --- a/vega-wgpu-renderer/src/renderers/rule.rs +++ b/vega-wgpu-renderer/src/renderers/rule.rs @@ -10,6 +10,12 @@ pub struct RuleShader { fragment_entry_point: String, } +impl Default for RuleShader { + fn default() -> Self { + Self::new() + } +} + impl RuleShader { pub fn new() -> Self { Self { diff --git a/vega-wgpu-renderer/src/renderers/symbol.rs b/vega-wgpu-renderer/src/renderers/symbol.rs index f879705..a3dafab 100644 --- a/vega-wgpu-renderer/src/renderers/symbol.rs +++ b/vega-wgpu-renderer/src/renderers/symbol.rs @@ -137,7 +137,7 @@ impl SymbolShader { SymbolShape::Triangle => { let r = 0.5; let h = r * sqrt3 / 2.0; - let o = (h - r * tan30); + let o = h - r * tan30; Self { verts: vec![ Vertex { @@ -279,7 +279,7 @@ impl SymbolShader { SymbolShape::Wedge => { let r = 0.5; let h = r * sqrt3 / 2.0; - let o = (h - r * tan30); + let o = h - r * tan30; let b = r / 4.0; Self { verts: vec![ diff --git a/vega-wgpu-renderer/src/renderers/text.rs b/vega-wgpu-renderer/src/renderers/text.rs index c33f1ed..976c369 100644 --- a/vega-wgpu-renderer/src/renderers/text.rs +++ b/vega-wgpu-renderer/src/renderers/text.rs @@ -1,8 +1,6 @@ use crate::renderers::canvas::CanvasUniform; -use crate::renderers::mark::MarkShader; use crate::scene::text::TextInstance; use crate::specs::text::{FontWeightNameSpec, FontWeightSpec, TextAlignSpec, TextBaselineSpec}; -use glyphon::cosmic_text::Align; use glyphon::{ Attrs, Buffer, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer, Weight, @@ -30,12 +28,12 @@ impl TextMarkRenderer { sample_count: u32, instances: Vec, ) -> Self { - let mut font_system = FontSystem::new(); - let mut cache = SwashCache::new(); + let font_system = FontSystem::new(); + let cache = SwashCache::new(); let mut atlas = TextAtlas::new(device, queue, texture_format); - let mut text_renderer = TextRenderer::new( + let text_renderer = TextRenderer::new( &mut atlas, - &device, + device, MultisampleState { count: sample_count, mask: !0, @@ -106,7 +104,7 @@ impl TextMarkRenderer { .iter() .zip(&self.instances) .map(|(buffer, instance)| { - let (width, height) = measure(&buffer); + let (width, height) = measure(buffer); let left = match instance.align { TextAlignSpec::Left => instance.position[0], @@ -125,7 +123,7 @@ impl TextMarkRenderer { }; TextArea { - buffer: &buffer, + buffer, left, top, scale: 1.0, @@ -146,8 +144,8 @@ impl TextMarkRenderer { self.text_renderer .prepare( - &device, - &queue, + device, + queue, &mut self.font_system, &mut self.atlas, Resolution { @@ -166,7 +164,7 @@ impl TextMarkRenderer { let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { label: None, color_attachments: &[Some(RenderPassColorAttachment { - view: &texture_view, + view: texture_view, resolve_target, ops: Operations { load: wgpu::LoadOp::Load, diff --git a/vega-wgpu-renderer/src/scene/rule.rs b/vega-wgpu-renderer/src/scene/rule.rs index 2da4a3a..5a6ecd5 100644 --- a/vega-wgpu-renderer/src/scene/rule.rs +++ b/vega-wgpu-renderer/src/scene/rule.rs @@ -1,5 +1,4 @@ use crate::error::VegaWgpuError; -use crate::scene::rect::RectInstance; use crate::specs::mark::MarkContainerSpec; use crate::specs::rule::RuleItemSpec; diff --git a/vega-wgpu-renderer/src/scene/scene_graph.rs b/vega-wgpu-renderer/src/scene/scene_graph.rs index b61b929..871a571 100644 --- a/vega-wgpu-renderer/src/scene/scene_graph.rs +++ b/vega-wgpu-renderer/src/scene/scene_graph.rs @@ -5,8 +5,6 @@ use crate::scene::symbol::SymbolMark; use crate::scene::text::TextMark; use crate::specs::group::GroupItemSpec; use crate::specs::mark::{MarkContainerSpec, MarkSpec}; -use crate::specs::rect::RectItemSpec; -use crate::specs::symbol::SymbolItemSpec; pub trait SceneVisitor { type Error; @@ -28,7 +26,6 @@ pub trait SceneVisitor { #[derive(Debug, Clone)] pub struct SceneGraph { pub(crate) groups: Vec, - pub(crate) origin: [f32; 2], pub(crate) width: f32, pub(crate) height: f32, } @@ -57,7 +54,6 @@ impl SceneGraph { ) -> Result { Ok(Self { groups: SceneGroup::from_spec(spec, origin)?, - origin, width, height, }) diff --git a/vega-wgpu-renderer/src/scene/text.rs b/vega-wgpu-renderer/src/scene/text.rs index db77b6c..4900010 100644 --- a/vega-wgpu-renderer/src/scene/text.rs +++ b/vega-wgpu-renderer/src/scene/text.rs @@ -1,6 +1,5 @@ use crate::error::VegaWgpuError; use crate::specs::mark::MarkContainerSpec; -use crate::specs::symbol::{SymbolItemSpec, SymbolShape}; use crate::specs::text::{ FontStyleSpec, FontWeightSpec, TextAlignSpec, TextBaselineSpec, TextItemSpec, }; diff --git a/vega-wgpu-renderer/tests/test_image_baselines.rs b/vega-wgpu-renderer/tests/test_image_baselines.rs index 48b8635..ddd2d0d 100644 --- a/vega-wgpu-renderer/tests/test_image_baselines.rs +++ b/vega-wgpu-renderer/tests/test_image_baselines.rs @@ -1,5 +1,3 @@ -use serde::{Deserialize, Serialize}; - #[cfg(test)] mod test_image_baselines { use dssim::Dssim; @@ -61,7 +59,7 @@ mod test_image_baselines { let scene_graph: SceneGraph = SceneGraph::from_spec(&scene_spec, origin, width, height) .expect("Failed to parse scene graph"); - let mut png_canvas = pollster::block_on(PngCanvas::new(width, height, origin)).unwrap(); + let mut png_canvas = pollster::block_on(PngCanvas::new(width, height)).unwrap(); png_canvas.set_scene(&scene_graph); let img = pollster::block_on(png_canvas.render()).expect("Failed to render PNG image"); let result_path = format!("{output_dir}/{category}-{spec_name}.png"); diff --git a/vega-wgpu-renderer/tests/test_parsing.rs b/vega-wgpu-renderer/tests/test_parsing.rs deleted file mode 100644 index a9acd52..0000000 --- a/vega-wgpu-renderer/tests/test_parsing.rs +++ /dev/null @@ -1,13 +0,0 @@ -use vega_wgpu_renderer::specs::mark::MarkSpec; - -#[test] -fn parse_bar() { - let scene: MarkSpec = serde_json::from_str(include_str!("specs/bar.sg.json")).unwrap(); - println!("{scene:#?}"); -} - -#[test] -fn parse_symbol() { - let scene: MarkSpec = serde_json::from_str(include_str!("specs/circles.sg.json")).unwrap(); - println!("{scene:#?}"); -}