diff --git a/dunge/src/context.rs b/dunge/src/context.rs index 6f02616..9ec4250 100644 --- a/dunge/src/context.rs +++ b/dunge/src/context.rs @@ -8,7 +8,8 @@ use { sl::IntoModule, state::{Render, RenderView, State}, texture::{ - self, CopyBuffer, CopyBufferView, DrawTexture, Format, Make, MapResult, Mapped, Sampler, + self, CopyBuffer, CopyBufferView, DrawTexture, Filter, Format, Make, MapResult, Mapped, + Sampler, }, Vertex, }, @@ -57,8 +58,8 @@ impl Context { texture::make(&self.0, data) } - pub fn make_sampler(&self) -> Sampler { - Sampler::new(&self.0) + pub fn make_sampler(&self, filter: Filter) -> Sampler { + Sampler::new(&self.0, filter) } pub fn make_copy_buffer(&self, size: (u32, u32)) -> CopyBuffer { diff --git a/dunge/src/el.rs b/dunge/src/el.rs new file mode 100644 index 0000000..a11d1ed --- /dev/null +++ b/dunge/src/el.rs @@ -0,0 +1,134 @@ +use { + crate::{ + context::Context, + state::{Render, RenderView}, + time::{Fps, Time}, + update::Update, + window::View, + }, + std::{error, fmt}, + wgpu::SurfaceError, + winit::{ + error::EventLoopError, + event, + event_loop::{self, EventLoop}, + keyboard, + }, +}; + +pub(crate) struct Loop(pub EventLoop<()>); + +impl Loop { + #[cfg(not(target_arch = "wasm32"))] + pub fn run(self, cx: Context, view: View, update: U) -> Result<(), LoopError> + where + U: Update, + { + let handler = handler(cx, view, update); + self.0.run(handler).map_err(LoopError) + } + + #[cfg(target_arch = "wasm32")] + pub fn spawn(self, cx: Context, view: View, update: U) + where + U: Update + 'static, + { + use winit::platform::web::EventLoopExtWebSys; + + let handler = handler(cx, view, update); + self.0.spawn(handler) + } +} + +#[derive(Debug)] +pub struct LoopError(EventLoopError); + +impl fmt::Display for LoopError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl error::Error for LoopError {} + +type Event = event::Event<()>; +type Target = event_loop::EventLoopWindowTarget<()>; + +fn handler(cx: Context, mut view: View, mut update: U) -> impl FnMut(Event, &Target) +where + U: Update, +{ + use { + event::{KeyEvent, StartCause, WindowEvent}, + event_loop::ControlFlow, + keyboard::{KeyCode, PhysicalKey}, + std::time::Duration, + }; + + let mut render = Render::default(); + let mut time = Time::now(); + let mut fps = Fps::default(); + move |ev: Event, target: &Target| match ev { + Event::NewEvents(cause) => match cause { + StartCause::ResumeTimeReached { .. } => view.request_redraw(), + StartCause::WaitCancelled { + requested_resume, .. + } => { + let flow = match requested_resume { + Some(resume) => ControlFlow::WaitUntil(resume), + None => { + const WAIT_TIME: Duration = Duration::from_millis(100); + + ControlFlow::wait_duration(WAIT_TIME) + } + }; + + target.set_control_flow(flow) + } + StartCause::Poll => {} + StartCause::Init => {} + }, + Event::WindowEvent { event, window_id } if window_id == view.id() => match event { + WindowEvent::Resized(_) => view.resize(cx.state()), + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + event: + KeyEvent { + physical_key: PhysicalKey::Code(KeyCode::Escape), + .. + }, + .. + } => target.exit(), + WindowEvent::RedrawRequested => { + let delta_time = time.delta(); + let min_delta_time = 1. / 60.; + if delta_time < min_delta_time { + let wait = Duration::from_secs_f32(min_delta_time - delta_time); + target.set_control_flow(ControlFlow::wait_duration(wait)); + return; + } + + time.reset(); + if let Some(fps) = fps.count(delta_time) { + println!("fps: {fps}"); + } + + update.update(); + match view.output() { + Ok(output) => { + let view = RenderView::from_output(&output); + cx.state().draw(&mut render, view, &update); + output.present(); + } + Err(SurfaceError::Lost) => view.resize(cx.state()), + Err(SurfaceError::OutOfMemory) => target.exit(), + Err(err) => eprintln!("{err:?}"), + } + } + _ => {} + }, + Event::Suspended => {} + Event::Resumed => view.request_redraw(), + _ => {} + } +} diff --git a/dunge/src/lib.rs b/dunge/src/lib.rs index 9077025..f73118f 100644 --- a/dunge/src/lib.rs +++ b/dunge/src/lib.rs @@ -12,6 +12,8 @@ pub mod texture; pub mod update; pub mod vertex; +#[cfg(feature = "winit")] +mod el; #[cfg(feature = "winit")] mod time; #[cfg(feature = "winit")] diff --git a/dunge/src/texture.rs b/dunge/src/texture.rs index 3039ff2..bca6313 100644 --- a/dunge/src/texture.rs +++ b/dunge/src/texture.rs @@ -2,8 +2,8 @@ use { crate::state::State, std::{error, fmt, future::IntoFuture, mem}, wgpu::{ - Buffer, BufferAsyncError, BufferSlice, BufferView, CommandEncoder, TextureFormat, - TextureUsages, TextureView, WasmNotSend, + Buffer, BufferAsyncError, BufferSlice, BufferView, CommandEncoder, FilterMode, + TextureFormat, TextureUsages, TextureView, WasmNotSend, }, }; @@ -203,16 +203,32 @@ where }) } +#[derive(Clone, Copy)] +pub enum Filter { + Nearest, + Linear, +} + +impl Filter { + pub(crate) const fn wgpu(self) -> FilterMode { + match self { + Self::Nearest => FilterMode::Nearest, + Self::Linear => FilterMode::Linear, + } + } +} + pub struct Sampler(wgpu::Sampler); impl Sampler { - pub(crate) fn new(state: &State) -> Self { + pub(crate) fn new(state: &State, filter: Filter) -> Self { use wgpu::*; let inner = { + let filter = filter.wgpu(); let desc = SamplerDescriptor { - mag_filter: FilterMode::Nearest, - min_filter: FilterMode::Nearest, + mag_filter: filter, + min_filter: filter, ..Default::default() }; diff --git a/dunge/src/window.rs b/dunge/src/window.rs index f7be13b..17a468a 100644 --- a/dunge/src/window.rs +++ b/dunge/src/window.rs @@ -1,9 +1,9 @@ use { crate::{ context::{self, Context}, - state::{Render, RenderView, State}, + el::Loop, + state::State, texture::Format, - time::{Fps, Time}, update::Update, }, std::{error, fmt}, @@ -13,12 +13,14 @@ use { }, winit::{ error::{EventLoopError, OsError}, - event, - event_loop::{self, EventLoop}, - keyboard, window, + event_loop::EventLoop, + window::{self, WindowId}, }, }; +#[cfg(not(target_arch = "wasm32"))] +use crate::el::LoopError; + pub struct WindowBuilder { title: String, size: Option<(u32, u32)>, @@ -72,16 +74,17 @@ impl WindowBuilder { }; let view = View::new(cx.state(), instance, inner)?; - Ok(Window { cx, el, view }) + Ok(Window { + cx, + el: Loop(el), + view, + }) } } -type Event = event::Event<()>; -type Target = event_loop::EventLoopWindowTarget<()>; - pub struct Window { cx: Context, - el: EventLoop<()>, + el: Loop, view: View, } @@ -90,99 +93,24 @@ impl Window { self.cx.clone() } - pub fn run(self, mut update: U) -> Result<(), LoopError> + #[cfg(not(target_arch = "wasm32"))] + pub fn run(self, update: U) -> Result<(), LoopError> where U: Update, { - use { - event::{KeyEvent, StartCause, WindowEvent}, - event_loop::ControlFlow, - keyboard::{KeyCode, PhysicalKey}, - std::time::Duration, - }; - - let Self { cx, el, mut view } = self; - let mut render = Render::default(); - let mut time = Time::now(); - let mut fps = Fps::default(); - let handler = |ev: Event, target: &Target| match ev { - Event::NewEvents(cause) => match cause { - StartCause::ResumeTimeReached { .. } => view.inner.request_redraw(), - StartCause::WaitCancelled { - requested_resume, .. - } => target.set_control_flow(match requested_resume { - Some(resume) => ControlFlow::WaitUntil(resume), - None => { - const WAIT_TIME: Duration = Duration::from_millis(100); - - ControlFlow::wait_duration(WAIT_TIME) - } - }), - StartCause::Poll => {} - StartCause::Init => {} - }, - Event::WindowEvent { event, window_id } if window_id == view.inner.id() => { - match event { - WindowEvent::Resized(_) => view.resize(cx.state()), - WindowEvent::CloseRequested - | WindowEvent::KeyboardInput { - event: - KeyEvent { - physical_key: PhysicalKey::Code(KeyCode::Escape), - .. - }, - .. - } => target.exit(), - WindowEvent::RedrawRequested => { - let delta_time = time.delta(); - let min_delta_time = 1. / 60.; - if delta_time < min_delta_time { - let wait = Duration::from_secs_f32(min_delta_time - delta_time); - target.set_control_flow(ControlFlow::wait_duration(wait)); - return; - } - - time.reset(); - if let Some(fps) = fps.count(delta_time) { - println!("fps: {fps}"); - } - - update.update(); - match view.output() { - Ok(output) => { - let view = RenderView::from_output(&output); - cx.state().draw(&mut render, view, &update); - output.surface.present(); - } - Err(SurfaceError::Lost) => view.resize(cx.state()), - Err(SurfaceError::OutOfMemory) => target.exit(), - Err(err) => eprintln!("{err:?}"), - } - } - _ => {} - } - } - Event::Suspended => {} - Event::Resumed => view.inner.request_redraw(), - _ => {} - }; - - el.run(handler).map_err(LoopError) + self.el.run(self.cx, self.view, update) } -} - -#[derive(Debug)] -pub struct LoopError(EventLoopError); -impl fmt::Display for LoopError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) + #[cfg(target_arch = "wasm32")] + pub fn spawn(self, update: U) + where + U: Update + 'static, + { + self.el.spawn(self.cx, self.view, update) } } -impl error::Error for LoopError {} - -struct View { +pub(crate) struct View { conf: SurfaceConfiguration, surface: Surface, @@ -230,7 +158,15 @@ impl View { }) } - fn output(&self) -> Result { + pub fn id(&self) -> WindowId { + self.inner.id() + } + + pub fn request_redraw(&self) { + self.inner.request_redraw(); + } + + pub fn output(&self) -> Result { use wgpu::TextureViewDescriptor; let output = self.surface.get_current_texture()?; @@ -246,7 +182,7 @@ impl View { }) } - fn resize(&mut self, state: &State) { + pub fn resize(&mut self, state: &State) { let size = self.inner.inner_size(); if size.width > 0 && size.height > 0 { self.conf.width = size.width; @@ -270,6 +206,10 @@ impl Output { pub fn format(&self) -> Format { self.format } + + pub fn present(self) { + self.surface.present(); + } } #[derive(Debug)] diff --git a/dunge/tests/triangle_group.rs b/dunge/tests/triangle_group.rs index 0198e79..b9808ff 100644 --- a/dunge/tests/triangle_group.rs +++ b/dunge/tests/triangle_group.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_family = "wasm"))] + use { dunge::{ color::Rgba, @@ -6,7 +8,7 @@ use { mesh, sl::{self, Groups, Input, Out}, state::{Options, Render}, - texture::{self, Format, Sampler}, + texture::{self, Filter, Format, Sampler}, Group, Vertex, }, glam::Vec2, @@ -49,7 +51,7 @@ fn render() -> Result<(), Error> { cx.make_texture(data) }; - let sampler = cx.make_sampler(); + let sampler = cx.make_sampler(Filter::Nearest); let map = Map { tex: BoundTexture::new(&texture), sam: &sampler, diff --git a/dunge/tests/triangle_index.rs b/dunge/tests/triangle_index.rs index 8d441da..ea7835e 100644 --- a/dunge/tests/triangle_index.rs +++ b/dunge/tests/triangle_index.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_family = "wasm"))] + use { dunge::{ color::Rgba, diff --git a/dunge/tests/triangle_vertex.rs b/dunge/tests/triangle_vertex.rs index d6cb0ec..d1369b6 100644 --- a/dunge/tests/triangle_vertex.rs +++ b/dunge/tests/triangle_vertex.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_family = "wasm"))] + use { dunge::{ color::Rgba, diff --git a/helpers/src/lib.rs b/helpers/src/lib.rs index 8057ab4..f115083 100644 --- a/helpers/src/lib.rs +++ b/helpers/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg(not(target_family = "wasm"))] + mod channel; mod image;