Skip to content

Commit

Permalink
WIP: WGPU renderer
Browse files Browse the repository at this point in the history
TODO:
 - generate uniform struct from wgsl
  • Loading branch information
tronical committed Nov 9, 2024
1 parent 7f9bcf8 commit 048f457
Show file tree
Hide file tree
Showing 23 changed files with 2,324 additions and 361 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
- run: cargo build --no-default-features --verbose
- run: cargo build --verbose --target wasm32-unknown-unknown
- run: cargo build --verbose --examples
- run: cargo build --verbose --examples --features wgpu
- run: cargo build --target=wasm32-unknown-unknown --example demo
- run: cargo test
format:
Expand Down
11 changes: 9 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ debug = true
[dependencies]
fnv = "1.0.7"
rgb = "0.8.50"
bytemuck = { version = "1.16", features = ["derive"] }
imgref = "1.11.0"
bitflags = "2.6.0"
rustybuzz = "0.20.0"
Expand All @@ -31,6 +32,7 @@ image = { version = "0.25.0", optional = true, default-features = false }
serde = { version = "1.0", optional = true, features = ["derive", "rc"] }
glow = { version = "0.15.0", default-features = false }
log = "0.4"
wgpu = { version = "23", optional = true, default-features = false, features = ["wgsl"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
glutin = { version = "0.31", optional = true, default-features = false }
Expand All @@ -44,12 +46,14 @@ web_sys = { version = "0.3", package = "web-sys", features = [
wasm-bindgen = "0.2"

[features]
default = ["image-loading"]
default = ["image-loading", "wgpu"]
#default = ["image-loading"]
image-loading = ["image"]
debug_inspector = []
wgpu = ["dep:wgpu"]

[dev-dependencies]
winit = { version = "0.29", default-features = false }
winit = { version = "0.29.1" }
euclid = "0.22.3"
rand = "0.8"
svg = "0.14.0"
Expand All @@ -63,6 +67,8 @@ image = { version = "0.25.0", default-features = false, features = [
cosmic-text = { git = "https://github.com/pop-os/cosmic-text", rev = "e00109d77f06d5a2e3057865eda3f530bc40a046" }
swash = "=0.1.17" # keep this in sync with cosmic-text
lazy_static = "1.4.0"
spin_on = "0.1"
wgpu = { version = "23" }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
glutin = "0.31.0"
Expand All @@ -78,6 +84,7 @@ console_error_panic_hook = "0.1.5"
instant = { version = "0.1", features = ["wasm-bindgen", "now"] }
resource = { version = "0.5.0", features = ["force-static"] }
getrandom = { version = "0.2.2", features = ["js"] }
wgpu = { version = "23", features = ["webgl"] }

[[example]]
name = "book_example_1_1"
Expand Down
8 changes: 4 additions & 4 deletions book/src/1_getting_started/2_rendering.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::num::NonZeroU32;

use femtovg::renderer::OpenGl;
use femtovg::{Canvas, Color, Renderer};
use femtovg::{Canvas, Color};
use glutin::surface::Surface;
use glutin::{context::PossiblyCurrentContext, display::Display};
use glutin_winit::DisplayBuilder;
Expand Down Expand Up @@ -71,11 +71,11 @@ fn create_window(event_loop: &EventLoop<()>) -> (PossiblyCurrentContext, Display
)
}

fn render<T: Renderer>(
fn render(
context: &PossiblyCurrentContext,
surface: &Surface<WindowSurface>,
window: &Window,
canvas: &mut Canvas<T>,
canvas: &mut Canvas<OpenGl>,
) {
// Make sure the canvas has the right size:
let size = window.inner_size();
Expand All @@ -85,7 +85,7 @@ fn render<T: Renderer>(
canvas.clear_rect(30, 30, 30, 30, Color::rgbf(1., 0., 0.));

// Tell renderer to execute all drawing commands
canvas.flush();
canvas.flush_to_surface(&());

// Display what we've just rendered
surface.swap_buffers(context).expect("Could not swap buffers");
Expand Down
8 changes: 4 additions & 4 deletions book/src/1_getting_started/3_event_loop.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::num::NonZeroU32;

use femtovg::renderer::OpenGl;
use femtovg::{Canvas, Color, Renderer};
use femtovg::{Canvas, Color};
use glutin::surface::Surface;
use glutin::{context::PossiblyCurrentContext, display::Display};
use glutin_winit::DisplayBuilder;
Expand Down Expand Up @@ -88,11 +88,11 @@ fn create_window(event_loop: &EventLoop<()>) -> (PossiblyCurrentContext, Display
)
}

fn render<T: Renderer>(
fn render(
context: &PossiblyCurrentContext,
surface: &Surface<WindowSurface>,
window: &Window,
canvas: &mut Canvas<T>,
canvas: &mut Canvas<OpenGl>,
square_position: PhysicalPosition<f64>,
) {
// Make sure the canvas has the right size:
Expand All @@ -111,7 +111,7 @@ fn render<T: Renderer>(
);

// Tell renderer to execute all drawing commands
canvas.flush();
canvas.flush_to_surface(&());
// Display what we've just rendered
surface.swap_buffers(context).expect("Could not swap buffers");
}
51 changes: 19 additions & 32 deletions examples/breakout.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use femtovg::{renderer::OpenGl, Align, Baseline, Color, FontId, ImageFlags, ImageId, Paint, Path};
use std::sync::Arc;

use femtovg::{Align, Baseline, Canvas, Color, FontId, ImageFlags, ImageId, Paint, Path, Renderer};
use instant::Instant;
use rand::{
distributions::{Distribution, Standard},
Expand All @@ -12,6 +14,7 @@ use winit::{
};

mod helpers;
use helpers::WindowSurface;

fn main() {
#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -20,10 +23,6 @@ fn main() {
helpers::start();
}

#[cfg(not(target_arch = "wasm32"))]
use glutin::prelude::*;

type Canvas = femtovg::Canvas<OpenGl>;
type Point = euclid::default::Point2D<f32>;
type Vector = euclid::default::Vector2D<f32>;
type Size = euclid::default::Size2D<f32>;
Expand Down Expand Up @@ -93,7 +92,7 @@ struct Game {
}

impl Game {
fn new(canvas: &mut Canvas, levels: Vec<Vec<Vec<Cmd>>>) -> Self {
fn new<R: Renderer + 'static>(canvas: &mut Canvas<R>, levels: Vec<Vec<Vec<Cmd>>>) -> Self {
let logo_image_id = canvas
.load_image_mem(
&resource!("examples/assets/rust-logo.png"),
Expand Down Expand Up @@ -497,7 +496,7 @@ impl Game {
}
}

fn draw(&self, canvas: &mut Canvas) {
fn draw<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
// draw background
let step_size_x = self.size.width / 50.0;

Expand Down Expand Up @@ -528,7 +527,7 @@ impl Game {
}
}

fn draw_title_screen(&self, canvas: &mut Canvas) {
fn draw_title_screen<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
// curtain
let mut path = Path::new();
path.rect(0.0, 0.0, self.size.width, self.size.height);
Expand Down Expand Up @@ -564,7 +563,7 @@ impl Game {
let _ = canvas.fill_text(self.size.width / 2.0, (self.size.height / 2.0) + 40.0, text, &paint);
}

fn draw_game(&self, canvas: &mut Canvas) {
fn draw_game<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
// Paddle
let side_size = 15.0;

Expand Down Expand Up @@ -666,29 +665,29 @@ impl Game {
let _ = canvas.fill_text(20.0, 25.0, format!("Score: {}", self.score), &paint);
}

fn draw_round_info(&self, canvas: &mut Canvas) {
fn draw_round_info<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
let heading = format!("ROUND {}", self.current_level + 1);

self.draw_generic_info(canvas, &heading, "");
}

fn draw_paused(&self, canvas: &mut Canvas) {
fn draw_paused<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
self.draw_generic_info(canvas, "PAUSE", "Click anywhere to resume. Press ESC to exit");
}

fn draw_game_over(&self, canvas: &mut Canvas) {
fn draw_game_over<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
let score = format!("Score: {}", self.score);

self.draw_generic_info(canvas, "Game Over", &score);
}

fn draw_win(&self, canvas: &mut Canvas) {
fn draw_win<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
let score = format!("Final score: {}", self.score);

self.draw_generic_info(canvas, "All cleared!", &score);
}

fn draw_bricks(&self, canvas: &mut Canvas) {
fn draw_bricks<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
// Bricks
for brick in &self.bricks {
if brick.destroyed {
Expand All @@ -699,7 +698,7 @@ impl Game {
}
}

fn draw_generic_info(&self, canvas: &mut Canvas, heading: &str, subtext: &str) {
fn draw_generic_info<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>, heading: &str, subtext: &str) {
self.draw_bricks(canvas);

// curtain
Expand Down Expand Up @@ -775,7 +774,7 @@ struct Powerup {
}

impl Powerup {
fn draw(&self, canvas: &mut Canvas, fonts: &Fonts) {
fn draw<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>, fonts: &Fonts) {
let mut path = Path::new();
path.rounded_rect(
self.rect.origin.x,
Expand Down Expand Up @@ -847,7 +846,7 @@ impl Brick {
}
}

fn draw(&self, canvas: &mut Canvas) {
fn draw<R: Renderer + 'static>(&self, canvas: &mut Canvas<R>) {
if self.destroyed {
return;
}
Expand Down Expand Up @@ -897,13 +896,7 @@ enum Cmd {
B(u8), // Brick Id
}

fn run(
mut canvas: Canvas,
el: EventLoop<()>,
#[cfg(not(target_arch = "wasm32"))] context: glutin::context::PossiblyCurrentContext,
#[cfg(not(target_arch = "wasm32"))] surface: glutin::surface::Surface<glutin::surface::WindowSurface>,
window: Window,
) {
fn run<W: WindowSurface>(mut canvas: Canvas<W::Renderer>, el: EventLoop<()>, mut surface: W, window: Arc<Window>) {
let level1 = vec![
vec![
Cmd::Spac,
Expand Down Expand Up @@ -1278,11 +1271,7 @@ fn run(
Event::WindowEvent { ref event, .. } => match event {
WindowEvent::Resized(physical_size) => {
#[cfg(not(target_arch = "wasm32"))]
surface.resize(
&context,
physical_size.width.try_into().unwrap(),
physical_size.height.try_into().unwrap(),
);
surface.resize(physical_size.width, physical_size.height);
game.size = Size::new(physical_size.width as f32, physical_size.height as f32);
}
WindowEvent::CloseRequested => event_loop_window_target.exit(),
Expand All @@ -1299,9 +1288,7 @@ fn run(
game.update(dt);
game.draw(&mut canvas);

canvas.flush();
#[cfg(not(target_arch = "wasm32"))]
surface.swap_buffers(&context).unwrap();
surface.present(&mut canvas);
}
_ => (),
},
Expand Down
52 changes: 32 additions & 20 deletions examples/demo.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::f32::consts::PI;
use std::{f32::consts::PI, sync::Arc};

use femtovg::{
Align, Baseline, Canvas, Color, FillRule, FontId, ImageFlags, ImageId, LineCap, LineJoin, Paint, Path, Renderer,
Expand All @@ -15,6 +15,7 @@ use winit::{

mod helpers;
use helpers::PerfGraph;
use helpers::WindowSurface;

fn main() {
#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -23,9 +24,6 @@ fn main() {
helpers::start();
}

#[cfg(not(target_arch = "wasm32"))]
use glutin::prelude::*;

pub fn quantize(a: f32, d: f32) -> f32 {
(a / d + 0.5).trunc() * d
}
Expand All @@ -36,13 +34,7 @@ struct Fonts {
icons: FontId,
}

fn run<R: Renderer + 'static>(
mut canvas: Canvas<R>,
el: EventLoop<()>,
#[cfg(not(target_arch = "wasm32"))] context: glutin::context::PossiblyCurrentContext,
#[cfg(not(target_arch = "wasm32"))] surface: glutin::surface::Surface<glutin::surface::WindowSurface>,
window: Window,
) {
fn run<W: WindowSurface>(mut canvas: Canvas<W::Renderer>, el: EventLoop<()>, mut surface: W, window: Arc<Window>) {
let fonts = Fonts {
regular: canvas
.add_font_mem(&resource!("examples/assets/Roboto-Regular.ttf"))
Expand Down Expand Up @@ -113,11 +105,7 @@ fn run<R: Renderer + 'static>(
Event::WindowEvent { ref event, .. } => match event {
#[cfg(not(target_arch = "wasm32"))]
WindowEvent::Resized(physical_size) => {
surface.resize(
&context,
physical_size.width.try_into().unwrap(),
physical_size.height.try_into().unwrap(),
);
surface.resize(physical_size.width, physical_size.height);
}
WindowEvent::CursorMoved {
device_id: _, position, ..
Expand Down Expand Up @@ -184,14 +172,40 @@ fn run<R: Renderer + 'static>(

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

let height = size.height as f32;
let width = size.width as f32;

let pt = canvas.transform().inverse().transform_point(mousex, mousey);
let rel_mousex = pt.0;
let rel_mousey = pt.1;

//canvas.clear_rect(10, 10, 40, 40, Color::rgb(255, 0, 0));
//canvas.clear_rect(150, 50, 40, 40, Color::rgb(0, 255, 0));

/*
{
let mut p = femtovg::Path::new();
p.rect(10., 10., 40., 40.);
let paint = femtovg::Paint::color(femtovg::Color::rgb(0, 255, 0));
canvas.fill_path(&p, &paint);
}
{
let mut p = femtovg::Path::new();
p.rounded_rect(150., 50., 40., 40., 20.);
let paint = femtovg::Paint::color(femtovg::Color::rgb(0, 0, 255));
// canvas.fill_path(&p, &paint);
canvas.stroke_path(&p, &paint);
}
*/
/*
{
let mut p = femtovg::Path::new();
p.rect(200., 50., 140., 140.);
let paint = femtovg::Paint::image(images[1], 200., 50., 140., 140., 0., 1.0);
canvas.fill_path(&p, &paint);
} */

draw_graph(&mut canvas, 0.0, height / 2.0, width, height / 2.0, t);

draw_eyes(
Expand Down Expand Up @@ -305,9 +319,7 @@ fn run<R: Renderer + 'static>(
perf.render(canvas, 5.0, 5.0);
});

canvas.flush();
#[cfg(not(target_arch = "wasm32"))]
surface.swap_buffers(&context).unwrap();
surface.present(&mut canvas);
}
WindowEvent::CloseRequested => event_loop_window_target.exit(),
_ => (),
Expand Down
Loading

0 comments on commit 048f457

Please sign in to comment.