diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcfb4c496..82f8e5eed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ env: RUST_MIN_VER_PKGS: "-p vello -p vello_encoding -p vello_shaders" # List of packages that can not target wasm. # `vello_tests` uses `nv-flip`, which doesn't support wasm. - NO_WASM_PKGS: "--exclude vello_tests" + NO_WASM_PKGS: "--exclude vello_tests --exclude simple_sdl2" diff --git a/Cargo.lock b/Cargo.lock index 5c7d5cf06..a93f968ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,6 +408,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -2029,6 +2038,31 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "sdl2" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380" +dependencies = [ + "bitflags 1.3.2", + "lazy_static", + "libc", + "raw-window-handle", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3" +dependencies = [ + "cfg-if", + "cmake", + "libc", + "version-compare", +] + [[package]] name = "serde" version = "1.0.203" @@ -2085,6 +2119,15 @@ dependencies = [ "winit", ] +[[package]] +name = "simple_sdl2" +version = "0.0.0" +dependencies = [ + "pollster", + "sdl2", + "vello", +] + [[package]] name = "skrifa" version = "0.19.3" @@ -2462,6 +2505,12 @@ dependencies = [ "vello", ] +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + [[package]] name = "version_check" version = "0.9.4" @@ -3361,3 +3410,11 @@ checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" dependencies = [ "zune-core", ] + +[[patch.unused]] +name = "bevy" +version = "0.15.0-dev" + +[[patch.unused]] +name = "bevy_vello" +version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index ff7b509f9..a881b64bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "examples/run_wasm", "examples/scenes", "examples/simple", + "examples/simple_sdl2", ] [workspace.package] diff --git a/examples/simple_sdl2/Cargo.toml b/examples/simple_sdl2/Cargo.toml new file mode 100644 index 000000000..6e4d4f077 --- /dev/null +++ b/examples/simple_sdl2/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "simple_sdl2" +edition.workspace = true +license.workspace = true +repository.workspace = true +publish = false + +[lints] +workspace = true + +# The dependencies here are independent from the workspace versions +[dependencies] +# When using this example outside of the original Vello workspace, +# remove the path property of the following Vello dependency requirement. +vello = { version = "0.2.0", path = "../../vello" } +pollster = "0.3.0" +sdl2 = { version = "0.37.0", features = ["raw-window-handle", "bundled"] } diff --git a/examples/simple_sdl2/src/main.rs b/examples/simple_sdl2/src/main.rs new file mode 100644 index 000000000..5adacf929 --- /dev/null +++ b/examples/simple_sdl2/src/main.rs @@ -0,0 +1,153 @@ +// Copyright 2024 the Vello Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Vello can also be used with non-Winit crates which provide a `RawWindowHandle`. +//! This example uses it with [`sdl2`]. +//! +//! Vello however is primarily designed for Xilem, which uses Winit, and so support for non-Winit crates +//! is on a tier-2 basis, i.e. it is checked in CI, but is not rarely manually validated. +extern crate sdl2; + +use sdl2::event::Event; +use sdl2::keyboard::Keycode; + +use std::num::NonZeroUsize; + +use vello::kurbo::{Affine, Circle, Ellipse, Line, RoundedRect, Stroke}; +use vello::peniko::Color; +use vello::util::{RenderContext, RenderSurface}; +use vello::{AaConfig, DebugLayers, Renderer, RendererOptions, Scene}; + +use vello::wgpu; + +pub fn main() { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + + let width: u32 = 800; + let height: u32 = 600; + + let window = video_subsystem + .window("Vello SDL2 Demo", width, height) + .position_centered() + .metal_view() + .build() + .unwrap(); + + let mut context = RenderContext::new(); + + let surface_future = unsafe { + context.create_render_surface( + context + .instance + .create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(&window).unwrap()) + .unwrap(), + width, + height, + wgpu::PresentMode::AutoVsync, + ) + }; + + let surface = pollster::block_on(surface_future).expect("Error creating surface."); + + let mut renderers: Vec> = vec![]; + + renderers.resize_with(context.devices.len(), || None); + let _ = renderers[surface.dev_id].insert(create_vello_renderer(&context, &surface)); + + let mut scene = Scene::new(); + + let mut event_pump = sdl_context.event_pump().unwrap(); + + 'running: loop { + scene.reset(); + + add_shapes_to_scene(&mut scene); + + let device_handle = &context.devices[surface.dev_id]; + + let surface_texture = surface + .surface + .get_current_texture() + .expect("failed to get surface texture"); + + renderers[surface.dev_id] + .as_mut() + .unwrap() + .render_to_surface( + &device_handle.device, + &device_handle.queue, + &scene, + &surface_texture, + &vello::RenderParams { + base_color: Color::BLACK, // Background color + width, + height, + antialiasing_method: AaConfig::Msaa16, + debug: DebugLayers::none(), + }, + ) + .expect("failed to render to surface"); + + for event in event_pump.poll_iter() { + match event { + Event::Quit { .. } + | Event::KeyDown { + keycode: Some(Keycode::Escape), + .. + } => break 'running, + _ => {} + } + } + + surface_texture.present(); + } +} + +fn create_vello_renderer(render_cx: &RenderContext, surface: &RenderSurface) -> Renderer { + Renderer::new( + &render_cx.devices[surface.dev_id].device, + RendererOptions { + surface_format: Some(surface.format), + use_cpu: false, + antialiasing_support: vello::AaSupport::all(), + num_init_threads: NonZeroUsize::new(1), + }, + ) + .expect("Couldn't create renderer") +} + +fn add_shapes_to_scene(scene: &mut Scene) { + // Draw an outlined rectangle + let stroke = Stroke::new(6.0); + let rect = RoundedRect::new(10.0, 10.0, 240.0, 240.0, 20.0); + let rect_stroke_color = Color::rgb(0.9804, 0.702, 0.5294); + scene.stroke(&stroke, Affine::IDENTITY, rect_stroke_color, None, &rect); + + // Draw a filled circle + let circle = Circle::new((420.0, 200.0), 120.0); + let circle_fill_color = Color::rgb(0.9529, 0.5451, 0.6588); + scene.fill( + vello::peniko::Fill::NonZero, + Affine::IDENTITY, + circle_fill_color, + None, + &circle, + ); + + // Draw a filled ellipse + let ellipse = Ellipse::new((250.0, 420.0), (100.0, 160.0), -90.0); + let ellipse_fill_color = Color::rgb(0.7961, 0.651, 0.9686); + scene.fill( + vello::peniko::Fill::NonZero, + Affine::IDENTITY, + ellipse_fill_color, + None, + &ellipse, + ); + + // Draw a straight line + let line = Line::new((260.0, 20.0), (620.0, 100.0)); + let line_stroke_color = Color::rgb(0.5373, 0.7059, 0.9804); + scene.stroke(&stroke, Affine::IDENTITY, line_stroke_color, None, &line); +} diff --git a/vello/src/util.rs b/vello/src/util.rs index ce0b3e89a..466448f2d 100644 --- a/vello/src/util.rs +++ b/vello/src/util.rs @@ -46,7 +46,23 @@ impl RenderContext { height: u32, present_mode: wgpu::PresentMode, ) -> Result> { - let surface = self.instance.create_surface(window.into())?; + self.create_render_surface( + self.instance.create_surface(window.into())?, + width, + height, + present_mode, + ) + .await + } + + /// Creates a new render surface for the specified window and dimensions. + pub async fn create_render_surface<'w>( + &mut self, + surface: Surface<'w>, + width: u32, + height: u32, + present_mode: wgpu::PresentMode, + ) -> Result> { let dev_id = self .device(Some(&surface)) .await