Skip to content

Commit

Permalink
Add SDL2 & C-powered window handling compatibility. (#671)
Browse files Browse the repository at this point in the history
This pull request adds `create_surface_unsafe()`, that allows you to add
unsafe surface targets.

This allows Vello to be used in SDL2, and it should also work in other
libraries like GLFW, but that's not tested yet.

Here's a screenshot of Vello running in SDL2, i'll push an example of it
soon :D.


![image](https://github.com/user-attachments/assets/f968ea22-bbcd-4408-91d4-2e8c15b2df20)

---------

Co-authored-by: Daniel McNab <[email protected]>
  • Loading branch information
TheNachoBIT and DJMcNab authored Aug 26, 2024
1 parent 3c8dc79 commit 5a7c34e
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"



Expand Down
57 changes: 57 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"examples/run_wasm",
"examples/scenes",
"examples/simple",
"examples/simple_sdl2",
]

[workspace.package]
Expand Down
17 changes: 17 additions & 0 deletions examples/simple_sdl2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"] }
153 changes: 153 additions & 0 deletions examples/simple_sdl2/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<Option<Renderer>> = 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);
}
18 changes: 17 additions & 1 deletion vello/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,23 @@ impl RenderContext {
height: u32,
present_mode: wgpu::PresentMode,
) -> Result<RenderSurface<'w>> {
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<RenderSurface<'w>> {
let dev_id = self
.device(Some(&surface))
.await
Expand Down

0 comments on commit 5a7c34e

Please sign in to comment.