Started implementing Wgpu renderer

+ implemented basic setup code
+ implemented basic resize code
! renderer will default to GLES backend due to NV prime (Wgpu bug)
This commit is contained in:
Steins7 2022-07-02 23:25:33 +02:00
parent 091454922b
commit 8fb74a0381
7 changed files with 1500 additions and 29 deletions

1347
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -9,4 +9,11 @@ edition = "2021"
log = "^0.4.17" log = "^0.4.17"
chrono = "^0.4.19" chrono = "^0.4.19"
fern = { version = "^0.6.1", features = ["colored"] } fern = { version = "^0.6.1", features = ["colored"] }
bitflags = "^1.3.2"
cgmath = "^0.18.0"
pollster = "^0.2.5"
winit = "^0.26.1"
raw-window-handle = "^0.4.3"
wgpu = "^0.13.0"

View File

@ -8,7 +8,7 @@ use crate::{
sprite::{Sprite, TextureSprite, TextSprite, ShapeSprite}, sprite::{Sprite, TextureSprite, TextSprite, ShapeSprite},
texture::Texture, texture::Texture,
utils::{Size, Pixel, Position, Color}, utils::{Size, Pixel, Position, Color},
renderer::Renderer, renderer::WgpuRenderer,
}; };
use std::{ use std::{
@ -26,15 +26,16 @@ pub trait Application {
//--Canvas struct----------------------------------------------------------------------------------- //--Canvas struct-----------------------------------------------------------------------------------
pub struct Canvas { pub struct Canvas {
renderer: Renderer, renderer: WgpuRenderer,
clear_color: Pixel, clear_color: Pixel,
} }
impl Canvas { impl Canvas {
pub fn create<W: HasRawWindowHandle>(window: &W) -> Result<Canvas, &'static str> { pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
-> Result<Canvas, &'static str>
let renderer = Renderer::create(window)?; {
let renderer = WgpuRenderer::create(window, size).await?;
Ok(Self { Ok(Self {
renderer, renderer,
@ -73,13 +74,17 @@ impl Canvas {
//--Output functions-- //--Output functions--
pub fn draw<S: Sprite>(&mut self, sprite: &S) { pub fn draw<S: Sprite>(&mut self, sprite: &S) {
//update texture //update texture
self.renderer.draw(sprite); self.renderer.render(sprite);
} }
pub fn set_clear_color(&mut self, color: Pixel) { pub fn set_clear_color(&mut self, color: Pixel) {
self.clear_color = color; self.clear_color = color;
} }
pub fn set_size(&mut self, size: Size) {
self.renderer.resize(size);
}
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.renderer.clear(&self.clear_color); self.renderer.clear(&self.clear_color);
} }
@ -114,7 +119,7 @@ impl Canvas {
} }
pub fn update(&mut self) { pub fn update(&mut self) {
self.renderer.finish_frame(); self.renderer.present();
} }
} }

View File

@ -1,6 +1,8 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use pollster::FutureExt;
mod canvas; mod canvas;
pub use canvas::Canvas; pub use canvas::Canvas;
pub use canvas::Application; pub use canvas::Application;
@ -29,8 +31,8 @@ pub fn run_canvas<A: 'static + Application>(title: &'static str, size: Size, mut
.expect("Failed to create window"); .expect("Failed to create window");
// construct canvas // construct canvas
let mut canvas = Canvas::create(&window) let size = window.inner_size();
.unwrap(); let mut canvas = Canvas::create(&window, size.into()).block_on().unwrap();
// init application // init application
app.init(&mut canvas).unwrap(); app.init(&mut canvas).unwrap();
@ -46,22 +48,32 @@ pub fn run_canvas<A: 'static + Application>(title: &'static str, size: Size, mut
*control_flow = ControlFlow::Poll; *control_flow = ControlFlow::Poll;
let _ = match event { match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested, Event::WindowEvent {event, ..} => match event {
.. WindowEvent::CloseRequested => {
} => { info!("Close requested, shutting down...");
info!("Close requested, shutting down..."); *control_flow = ControlFlow::Exit;
*control_flow = ControlFlow::Exit; },
Ok(()) WindowEvent::Resized (size) => {
canvas.set_size(size.into());
},
WindowEvent::ScaleFactorChanged {new_inner_size, ..} => {
// new_inner_size is &&mut so we have to dereference it twice
canvas.set_size((*new_inner_size).into());
},
_ => (),
},
Event::MainEventsCleared => {
let _ = app.tick(&mut canvas);
}, },
Event::MainEventsCleared => app.tick(&mut canvas),
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
canvas.update(); let _ = canvas.update();
Ok(())
}, },
_ => Ok(())
}.unwrap(); _ => ()
};
}); });
} }

View File

@ -19,7 +19,7 @@ fn setup_logger() -> Result<(), fern::InitError> {
message message
)) ))
}) })
.level(log::LevelFilter::Trace) .level(log::LevelFilter::Debug)
.chain(std::io::stdout()) .chain(std::io::stdout())
.chain(fern::log_file("output.log")?) .chain(fern::log_file("output.log")?)
.apply()?; .apply()?;

93
src/renderer.rs Normal file
View File

@ -0,0 +1,93 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use raw_window_handle::HasRawWindowHandle;
use wgpu;
use crate::{
sprite::Sprite,
utils::{Pixel, Size},
};
//--Renderer struct---------------------------------------------------------------------------------
pub struct WgpuRenderer {
surface: wgpu::Surface,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
size: Size,
}
impl WgpuRenderer {
pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
-> Result<Self, &'static str>
{
if size.w == 0 || size.h == 0 {
return Err("window has zero as at least one of its dimensions");
}
let instance = wgpu::Instance::new(wgpu::Backends::all());
let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter(
&wgpu::RequestAdapterOptions {
//for now, integrated GPU is enough, may be revised later
power_preference: wgpu::PowerPreference::LowPower,
compatible_surface: Some(&surface),
force_fallback_adapter: false,
},
).await.unwrap();
let (device, queue) = adapter.request_device(
&wgpu::DeviceDescriptor {
//using minimum requirements possible since 2D isn't very demanding anyway
features: wgpu::Features::empty(),
limits: wgpu::Limits::downlevel_webgl2_defaults(),
label: None,
},
None, // Trace path
).await.unwrap();
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
//first format in the list is preferred
format: *surface.get_supported_formats(&adapter).first()
.ok_or_else(|| { "Surface is incompatible with current adapter" })?,
width: size.w,
height: size.h,
present_mode: wgpu::PresentMode::Fifo,
};
surface.configure(&device, &config);
Ok(Self {
surface,
device,
queue,
config,
size,
})
}
pub fn render<S: Sprite>(&mut self, _sprite: &S) {
unimplemented!();
}
pub fn resize(&mut self, size: Size) {
if size.w == 0 || size.h == 0 {
panic!("window has zero as at least one of its dimensions");
}
self.size = size;
self.config.width = size.w;
self.config.height = size.h;
self.surface.configure(&self.device, &self.config);
}
pub fn clear(&mut self, _color: &Pixel) {
unimplemented!();
}
pub fn present(&mut self) {
unimplemented!();
}
}

View File

@ -10,6 +10,7 @@ pub struct Position {
} }
//--Size struct------------------------------------------------------------------------------------- //--Size struct-------------------------------------------------------------------------------------
#[derive(Copy, Clone)]
pub struct Size { pub struct Size {
pub w: u32, pub w: u32,
pub h: u32, pub h: u32,
@ -18,14 +19,24 @@ pub struct Size {
impl From<Size> for winit::dpi::Size { impl From<Size> for winit::dpi::Size {
fn from(size: Size) -> Self { fn from(size: Size) -> Self {
winit::dpi::Size::Logical ( winit::dpi::Size::Physical (
winit::dpi::LogicalSize { winit::dpi::PhysicalSize {
width: size.w.into(), width: size.w,
height: size.h.into() height: size.h,
}) })
} }
} }
impl From<winit::dpi::PhysicalSize<u32>> for Size {
fn from(size: winit::dpi::PhysicalSize<u32>) -> Self {
Self {
w: size.width,
h: size.height,
}
}
}
//--Pixel struct------------------------------------------------------------------------------------ //--Pixel struct------------------------------------------------------------------------------------
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Rgba { struct Rgba {