Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

separate glow and wgpu into features #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -13,6 +13,14 @@ edition = "2018"
[profile.release]
debug = true

[features]
default = ["image-loading", "convert-rgb", "glow-renderer"]
image-loading = ["image"]
glow-renderer = ["glow"]
wgpu-renderer = ["wgpu", "raw-window-handle"]
debug_inspector = []
convert-rgb = []

[dependencies]
fnv = "1.0.7"
rgb = "0.8.20"
@@ -25,12 +33,8 @@ unicode-segmentation = "1.6.0"
generational-arena = "0.2.8"
lru = { version = "0.5.3", default-features = false }
image = { version = "0.23.6", optional = true, default-features = false }
serde = { version = "1.0", optional = true, features = ["derive"] }
wgpu = { git = "https://github.com/gfx-rs/wgpu-rs/" , rev = "82b7068498864de44bbdf3e02d086c03d83a04e0" }
#wgpu = { git = "https://github.com/gfx-rs/wgpu-rs/" , features = ["trace"], rev = "0f1b290af947d3cd16280c4eb3b48507f5e6c9fb" }
pollster = "0.2"
#winit = { version = "0.24", default-features = false }
winit = { git = "https://github.com/adamnemecek/winit", rev = "8fb77892bdb785045396700391195b10eaf656d8" }
wgpu = { git = "https://github.com/gfx-rs/wgpu-rs/" , rev = "82b7068498864de44bbdf3e02d086c03d83a04e0", optional = true }
raw-window-handle = { version = "0.3.3", optional = true }
#metal = { rev = "439c986eb7a9b91e88b61def2daa66e4043fcbef" }

#git = "https://github.com/gfx-rs/gfx"
@@ -45,21 +49,14 @@ winit = { git = "https://github.com/adamnemecek/winit", rev = "8fb77892bdb785045
# features = ["auto-capture"]

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
glow = { version = "0.7.0", default-features = false }
glow = { version = "0.7.0", default-features = false, optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
glow = { version = "0.7.0", features = ["web-sys"], default-features = false }
glow = { version = "0.7.0", features = ["web-sys"], default-features = false, optional = true }
web_sys = { version = "0.3", package = "web-sys", features = ["WebGlContextAttributes", "HtmlImageElement"] }
wasm-bindgen = { version = "=0.2.73" }

[features]
default = ["image-loading", "convert-rgb"]
image-loading = ["image"]
debug_inspector = []
convert-rgb = []

[dev-dependencies]

euclid = "0.20.13"
rand = "0.7"
svg = "0.8.0"
@@ -69,7 +66,9 @@ resource = "0.5.0"
image = { version = "0.23.6", default-features = false, features = ["jpeg", "png"] }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
winit = "0.24.0"
glutin = "0.26.0"
pollster = "0.2"

# [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
# winit = { version = "0.24", default-features = false, features = ["web-sys"] }
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -12,7 +12,13 @@ Most of the implementation is the same as the original C code with some bug fixe
## Screenshots
### Demo
![demo](assets/demo.png)
Run with `cargo run --example demo` for the `OpenGL` backend and run with `cargo run --example wgpu_demo` for the `wgpu` backend. Run with `--release` for best performance.
Run with `cargo run --example demo`.

Run the wgpu demo with:

`cargo run --example wgpu_demo --no-default-features --features=image-loading,convert-rgb,wgpu-renderer`

Run the examples with `--release` for best performance.

### Breakout
![breakout](assets/breakout.png)
2 changes: 1 addition & 1 deletion examples/demo.rs
Original file line number Diff line number Diff line change
@@ -228,7 +228,7 @@ fn main() {
canvas.delete_image(screenshot_image_id);
}

if let Ok(image) = canvas.screenshot() {
if let Ok(image) = canvas.screenshot(None) {
screenshot_image_id = Some(canvas.create_image(image.as_ref(), ImageFlags::empty()).unwrap());
}
}
2 changes: 1 addition & 1 deletion examples/svg.rs
Original file line number Diff line number Diff line change
@@ -146,7 +146,7 @@ fn main() {
canvas.delete_image(screenshot_image_id);
}

if let Ok(image) = canvas.screenshot() {
if let Ok(image) = canvas.screenshot(None) {
screenshot_image_id = Some(canvas.create_image(image.as_ref(), ImageFlags::empty()).unwrap());
}
}
57 changes: 25 additions & 32 deletions examples/wgpu_demo.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
fn main() {
#[cfg(feature="wgpu-renderer")]
demo::demo_main();
}

#[cfg(feature="wgpu-renderer")]
mod demo {

use std::f32::consts::PI;

use resource::resource;
@@ -15,8 +23,6 @@ use winit::event_loop::{
ControlFlow,
EventLoop,
};
use winit::window::WindowBuilder;
//use glutin::{GlRequest, Api};

use femtovg::{
renderer::{
@@ -52,7 +58,7 @@ struct Fonts {
icons: FontId,
}

fn main() {
pub fn demo_main() {
// This provides better error messages in debug mode.
// It's disabled in release mode so it doesn't bloat up the file size.
#[cfg(all(debug_assertions, target_arch = "wasm32"))]
@@ -115,36 +121,11 @@ fn main() {

async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {
let size = window.inner_size();
// let instance = wgpu::Instance::new(wgpu::BackendBit::all());
// let surface = unsafe { instance.create_surface(&window) };
// let adapter = instance
// .request_adapter(&wgpu::RequestAdapterOptions {
// power_preference: wgpu::PowerPreference::default(),
// // Request an adapter which can render to our surface
// compatible_surface: Some(&surface),
// })
// .await
// .expect("Failed to find an appropriate adapter");

// Create the logical device and command queue
// let (device, queue) = adapter
// .request_device(
// &wgpu::DeviceDescriptor {
// label: None,
// features: wgpu::Features::empty(),
// limits: wgpu::Limits::default(),
// },
// None,
// )
// .await
// .expect("Failed to create device");

let size = Size::new(size.width as _, size.height as _);


let instance = WGPUInstance::from_window(&window).await.unwrap();
let ctx = WGPUContext::new(instance).await.unwrap();
let size = Size::new(size.width as _, size.height as _);
let mut swap_chain = WGPUSwapChain::new(&ctx, size);

let renderer = WGPU::new(&ctx, size, swap_chain.format());

let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
@@ -291,6 +272,15 @@ async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {
_ => (),
},
Event::RedrawRequested(_) => {
let frame = match swap_chain.get_current_frame() {
Ok(frame) => frame,
Err(_) => {
// The swapchain is outdated. Try again next frame.
window.request_redraw();
return;
}
};

let now = Instant::now();
let dt = (now - prevt).as_secs_f32();
prevt = now;
@@ -302,7 +292,7 @@ async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {

let t = start.elapsed().as_secs_f32();

canvas.set_size(size.width as u32, size.height as u32, dpi_factor as f32);
// canvas.set_size(size.width as u32, size.height as u32, dpi_factor as f32);
canvas.clear_rect(0, 0, size.width as u32, size.height as u32, Color::rgbf(0.3, 0.3, 0.32));

let height = size.height as f32;
@@ -428,9 +418,10 @@ async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {
});

//canvas.restore();
let frame = swap_chain.get_current_frame().unwrap();

let target = &frame.output.view;
canvas.flush(Some(target));

// #[cfg(not(target_arch = "wasm32"))]
// windowed_context.swap_buffers().unwrap();
// todo!("swap buffers");
@@ -1629,3 +1620,5 @@ fn draw_spinner<T: Renderer>(canvas: &mut Canvas<T>, cx: f32, cy: f32, r: f32, t

canvas.restore();
}

}
190 changes: 190 additions & 0 deletions examples/wgpu_integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
fn main() {
#[cfg(feature="wgpu-renderer")]
integration::integration_main();
}

#[cfg(feature="wgpu-renderer")]
mod integration {

use std::rc::Rc;

use winit::event::{
Event,
WindowEvent,
};
use winit::event_loop::{
ControlFlow,
EventLoop,
};

use femtovg::{
renderer::{
WGPUContext,
WGPUInstance,
WGPUSwapChain,
WGPU,
},
Align,
Baseline,
Canvas,
Color,
FillRule,
FontId,
ImageFlags,
ImageId,
LineCap,
LineJoin,
Paint,
Path,
Renderer,
Size,
Solidity,
};

pub fn integration_main() {
let event_loop = EventLoop::new();
let size = winit::dpi::LogicalSize::new(1024, 800);
let window = winit::window::WindowBuilder::new()
.with_inner_size(size)
.with_title("wgpu integration")
.build(&event_loop)
.unwrap();

pollster::block_on(run(event_loop, window));
}

async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {
let size = window.inner_size();

// Normal wgpu structs ---------------------------

let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(&window) };
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface),
})
.await
.unwrap();

let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
features: wgpu::Features::PUSH_CONSTANTS,
limits: wgpu::Limits {
max_push_constant_size: 4096, // This must be at-least 8
..wgpu::Limits::default()
},
label: None,
},
None, // Trace path
)
.await
.unwrap();

let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8Unorm,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
};
let mut swap_chain = device.create_swap_chain(&surface, &sc_desc);


// Femtovg integration -----------------------------

let instance = Rc::new(instance);
let surface = Rc::new(surface);
let adapter = Rc::new(adapter);
let device = Rc::new(device);
let queue = Rc::new(queue);

let instance = WGPUInstance::from_instance(Rc::clone(&instance), Rc::clone(&adapter), Some(Rc::clone(&surface)));
let ctx = WGPUContext::from_device(instance, Rc::clone(&device), Rc::clone(&queue));
let size = Size::new(size.width as _, size.height as _);
let renderer = WGPU::new(&ctx, size, wgpu::TextureFormat::Bgra8Unorm);

let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;

match event {
Event::LoopDestroyed => return,
Event::WindowEvent { ref event, .. } => match event {
#[cfg(not(target_arch = "wasm32"))]
WindowEvent::Resized(new_size) => {
let new_size = Size::new(new_size.width as _, new_size.height as _);
canvas.set_size(new_size.w as _, new_size.h as _, 1.0);

let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8Unorm,
width: new_size.w as _,
height: new_size.h as _,
present_mode: wgpu::PresentMode::Fifo,
};
swap_chain = device.create_swap_chain(&surface, &sc_desc);
}
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
_ => (),
},
Event::RedrawRequested(_) => {
// Normal wgpu rendering ------------------------------

let frame = match swap_chain.get_current_frame() {
Ok(frame) => frame,
Err(_) => {
// The swapchain is outdated. Try again next frame.
window.request_redraw();
return;
}
};

let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});

{
let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[
wgpu::RenderPassColorAttachment {
view: &frame.output.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: true,
}
}
],
depth_stencil_attachment: None,
});
}

queue.submit(std::iter::once(encoder.finish()));


// Femtovg rendering -----------------------------------

canvas.clear_rect(100, 100, 100, 100, Color::rgbf(1.0, 0.0, 0.0));

let target = &frame.output.view;
canvas.flush(Some(target));
}
Event::MainEventsCleared => {
window.request_redraw()
}
_ => (),
}
});
}

}
30 changes: 22 additions & 8 deletions examples/wgpu_simple.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use std::f32::consts::PI;
fn main() {
#[cfg(feature="wgpu-renderer")]
simple::simple_main();
}

#[cfg(feature="wgpu-renderer")]
mod simple {

use resource::resource;

@@ -15,8 +21,6 @@ use winit::event_loop::{
ControlFlow,
EventLoop,
};
use winit::window::WindowBuilder;
//use glutin::{GlRequest, Api};

use femtovg::{
renderer::{
@@ -52,7 +56,7 @@ struct Fonts {
icons: FontId,
}

fn main() {
pub fn simple_main() {
// This provides better error messages in debug mode.
// It's disabled in release mode so it doesn't bloat up the file size.
#[cfg(all(debug_assertions, target_arch = "wasm32"))]
@@ -140,9 +144,9 @@ async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {

let instance = WGPUInstance::from_window(&window).await.unwrap();
let ctx = WGPUContext::new(instance).await.unwrap();
let mut swap_chain = WGPUSwapChain::new(&ctx, size);
let renderer = WGPU::new(&ctx, Size::new(size.width as _, size.height as _), swap_chain.format());
let size = Size::new(size.width as _, size.height as _);
let mut swap_chain = WGPUSwapChain::new(&ctx, size);
let renderer = WGPU::new(&ctx, size, swap_chain.format());
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");

let fonts = Fonts {
@@ -294,6 +298,15 @@ async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {
_ => (),
},
Event::RedrawRequested(_) => {
let frame = match swap_chain.get_current_frame() {
Ok(frame) => frame,
Err(_) => {
// The swapchain is outdated. Try again next frame.
window.request_redraw();
return;
}
};

let now = Instant::now();
let dt = (now - prevt).as_secs_f32();
prevt = now;
@@ -305,7 +318,7 @@ async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {

// let t = start.elapsed().as_secs_f32();

canvas.set_size(size.width as u32, size.height as u32, dpi_factor as f32);
// canvas.set_size(size.width as u32, size.height as u32, dpi_factor as f32);
let bg_color = Color::rgbf(0.3, 0.3, 0.3);
canvas.clear_rect(0, 0, size.width as u32, size.height as u32, bg_color);

@@ -350,7 +363,6 @@ async fn run(event_loop: EventLoop<()>, window: winit::window::Window) {

// stroke_rect(&mut canvas, 200.0, 200.0, 100.0, 100.0);

let frame = swap_chain.get_current_frame().unwrap();
let target = &frame.output.view;
canvas.flush(Some(target));

@@ -1701,3 +1713,5 @@ fn draw_graph<T: Renderer>(canvas: &mut Canvas<T>, x: f32, y: f32, w: f32, h: f3

// canvas.restore();
// }

}
1 change: 1 addition & 0 deletions src/geometry.rs
Original file line number Diff line number Diff line change
@@ -233,6 +233,7 @@ pub struct Size {
pub h: u32,
}

#[cfg(feature = "wgpu-renderer")]
impl From<Size> for wgpu::Extent3d {
fn from(a: Size) -> Self {
Self {
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -563,8 +563,6 @@ where
filename: P,
flags: ImageFlags,
) -> Result<ImageId, ErrorKind> {
use ::image::DynamicImage;

let image = ::image::open(filename)?.convert_rgb_if_needed();
use std::convert::TryFrom;

4 changes: 4 additions & 0 deletions src/renderer.rs
Original file line number Diff line number Diff line change
@@ -14,10 +14,14 @@ use crate::{
ImageStore,
};

#[cfg(feature = "glow-renderer")]
mod opengl;
#[cfg(feature = "glow-renderer")]
pub use opengl::OpenGl;

#[cfg(feature = "wgpu-renderer")]
mod webgpu;
#[cfg(feature = "wgpu-renderer")]
pub use webgpu::{
WGPUContext,
WGPUInstance,
2 changes: 1 addition & 1 deletion src/renderer/opengl.rs
Original file line number Diff line number Diff line change
@@ -474,7 +474,7 @@ impl Renderer for OpenGl {

fn render(
&mut self,
target: Option<&Self::Target>,
_target: Option<&Self::Target>,
images: &ImageStore<Self::Image>,
verts: &[Vertex],
commands: &[Command],
2 changes: 1 addition & 1 deletion src/renderer/webgpu.rs
Original file line number Diff line number Diff line change
@@ -757,7 +757,7 @@ impl Renderer for WGPU {
{
let (target_view, stencil_view, view_size, texture_format) = match render_target {
RenderTarget::Screen => (
target.expect("In order to render into the screen, you have to provice the render target from SwapChainFrame"),
target.expect("In order to render into the screen, you have to provide the render target from wgpu::SwapChainFrame"),
self.stencil_texture.view(),
self.view_size,
swap_chain_format,
55 changes: 39 additions & 16 deletions src/renderer/webgpu/wgpu_context.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use std::rc::Rc;

use raw_window_handle::HasRawWindowHandle;

use std::future::Future;
#[derive(Clone)]
pub struct WGPUInstance {
instance: std::rc::Rc<wgpu::Instance>,
adapter: std::rc::Rc<wgpu::Adapter>,
surface: Option<std::rc::Rc<wgpu::Surface>>,
pub instance: Rc<wgpu::Instance>,
pub adapter: Rc<wgpu::Adapter>,
pub surface: Option<Rc<wgpu::Surface>>,
}

impl WGPUInstance {
// pub fn from_window(window: &winit::window::Window) -> impl Future<Output = Result<Self, wgpu::RequestDeviceError>> {
pub fn from_window(window: &winit::window::Window) -> impl Future<Output = Option<Self>> {
pub fn from_window(window: &impl HasRawWindowHandle) -> impl Future<Output = Option<Self>> {
let instance = wgpu::Instance::new(wgpu::BackendBit::all());
let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
@@ -18,21 +21,33 @@ impl WGPUInstance {
});
async move {
adapter.await.map(|adapter| Self {
instance: std::rc::Rc::new(instance),
adapter: std::rc::Rc::new(adapter),
surface: Some(std::rc::Rc::new(surface)),
instance: Rc::new(instance),
adapter: Rc::new(adapter),
surface: Some(Rc::new(surface)),
})
}
}

pub fn from_instance(
instance: Rc<wgpu::Instance>,
adapter: Rc<wgpu::Adapter>,
surface: Option<Rc<wgpu::Surface>>,
) -> Self {
Self {
instance,
adapter,
surface,
}
}

pub fn new() -> impl Future<Output = Option<Self>> {
let instance = wgpu::Instance::new(wgpu::BackendBit::all());
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default());

async move {
adapter.await.map(|adapter| Self {
instance: std::rc::Rc::new(instance),
adapter: std::rc::Rc::new(adapter),
instance: Rc::new(instance),
adapter: Rc::new(adapter),
surface: None,
})
}
@@ -53,9 +68,9 @@ impl WGPUQueueExt for wgpu::Queue {

#[derive(Clone)]
pub struct WGPUContext {
instance: WGPUInstance,
device: std::rc::Rc<wgpu::Device>,
queue: std::rc::Rc<wgpu::Queue>,
pub instance: WGPUInstance,
pub device: Rc<wgpu::Device>,
pub queue: Rc<wgpu::Queue>,
}

impl WGPUContext {
@@ -77,11 +92,19 @@ impl WGPUContext {
async move {
f.await.map(|(device, queue)| Self {
instance: instance.clone(),
device: std::rc::Rc::new(device),
queue: std::rc::Rc::new(queue),
device: Rc::new(device),
queue: Rc::new(queue),
})
}
}

pub fn from_device(instance: WGPUInstance, device: Rc<wgpu::Device>, queue: Rc<wgpu::Queue>) -> Self {
Self {
instance,
device,
queue
}
}
}

impl WGPUContext {
@@ -114,5 +137,5 @@ impl WGPUContext {
}
// #[derive(Clone)]
// pub struct WGPUDevice {
// inner: std::rc::Rc<wgpu::Device>
// inner: Rc<wgpu::Device>
// }