diff options
| author | Micha White <botahamec@outlook.com> | 2022-09-17 21:40:49 -0400 |
|---|---|---|
| committer | Micha White <botahamec@outlook.com> | 2022-09-17 21:40:49 -0400 |
| commit | 4826c2c06afd21214e0c2a47944d40eeefae65bd (patch) | |
| tree | c122cb7e1769e596ad61aabb026b1ad2b966a5a7 /src | |
| parent | a005620119b9c1d18c750552d0a707f36f407ea1 (diff) | |
Big refactor
Diffstat (limited to 'src')
| -rw-r--r-- | src/renderer.rs | 150 |
1 files changed, 123 insertions, 27 deletions
diff --git a/src/renderer.rs b/src/renderer.rs index f937183..879a447 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,10 +1,12 @@ use std::num::NonZeroU32; +use pollster::FutureExt; use thiserror::Error; use winit::{ - dpi::{LogicalSize, PhysicalSize}, + dpi::{LogicalPosition, LogicalSize, PhysicalSize}, error::OsError, - event_loop::EventLoop, + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, window::{Fullscreen, Window, WindowBuilder}, }; @@ -31,22 +33,39 @@ pub enum NewRendererError { WindowInitError(#[from] OsError), } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// Describes how a window may be resized +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct Resizable { + /// The minimum width of the window, or None if unconstrained pub min_width: Option<NonZeroU32>, + /// The minimum height of the window, or None if unconstrained pub min_height: Option<NonZeroU32>, + /// The maximum width of the window, or None if unconstrained pub max_width: Option<NonZeroU32>, + /// The maximum height of the window, or None if unconstrained pub max_height: Option<NonZeroU32>, } +/// Information about a window, that is not fullscreened #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct WindowInfo { pub default_x: i32, pub default_y: i32, - pub resizability: Option<Resizable>, + pub resizable: Option<Resizable>, pub default_maximized: bool, } +impl Default for WindowInfo { + fn default() -> Self { + Self { + default_x: 100, + default_y: 100, + resizable: Some(Resizable::default()), + default_maximized: false, + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum WindowMode { Windowed(WindowInfo), @@ -54,25 +73,50 @@ pub enum WindowMode { BorderlessFullscreen, // TODO exclusive fullscreen } +impl Default for WindowMode { + fn default() -> Self { + Self::Windowed(WindowInfo::default()) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] // TODO to consider: don't allow the x and y to be too big // TODO window icon -pub struct RendererConfig { +pub struct RenderWindowConfig { + /// The width of the window, once initialized pub default_width: NonZeroU32, + /// The height of the window, once initialized pub default_height: NonZeroU32, - pub size: WindowMode, - pub title: Option<Box<str>>, + /// The window may be fullscreen + pub mode: WindowMode, + /// The title for the window + pub title: Box<str>, + /// If true, a low-power device will be selected as the GPU, if possible pub low_power: bool, + /// If true, Fifo mode is used to present frames. If false, then Mailbox or + /// Immediate will be used if available. Otherwise, Fifo will be used. pub vsync: bool, } +impl Default for RenderWindowConfig { + fn default() -> Self { + Self { + default_width: NonZeroU32::new(480).unwrap(), + default_height: NonZeroU32::new(640).unwrap(), + mode: WindowMode::default(), + title: "Alligator Game".into(), + low_power: true, + vsync: true, + } + } +} + pub struct Renderer { surface: wgpu::Surface, device: wgpu::Device, queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, window: Window, - event_loop: EventLoop<()>, } // TODO make this more complete @@ -84,22 +128,23 @@ impl Renderer { /// Returns a [`NoGpu`] error if no device could be detected that can /// display to the window // TODO make it possible to use without winit (ie, use a bitmap in memory as a surface) - // TODO make PowerPreference a configuration option - // TODO check for zero size windows #[allow(clippy::missing_panics_doc)] - pub async fn new(config: RendererConfig) -> Result<Self, NewRendererError> { + pub fn new( + config: RenderWindowConfig, + event_loop: &EventLoop<()>, + ) -> Result<Self, NewRendererError> { let mut builder = WindowBuilder::new() - .with_title(config.title.unwrap_or_else(|| "Alligator Game".into())) + .with_title(config.title) .with_inner_size(LogicalSize::new( config.default_width.get(), config.default_height.get(), )); - match config.size { - WindowMode::Windowed(size) => { - builder = builder.with_maximized(size.default_maximized); + match config.mode { + WindowMode::Windowed(window_info) => { + builder = builder.with_maximized(window_info.default_maximized); - if let Some(resizing_options) = size.resizability { + if let Some(resizing_options) = window_info.resizable { if resizing_options.max_height.is_some() || resizing_options.max_width.is_some() { builder = builder.with_max_inner_size(LogicalSize::new( @@ -118,14 +163,19 @@ impl Renderer { } else { builder = builder.with_resizable(false); } + + // TODO clamp the position to the monitor's size + builder = builder.with_position(LogicalPosition::new( + window_info.default_x, + window_info.default_y, + )); } WindowMode::BorderlessFullscreen => { builder = builder.with_fullscreen(Some(Fullscreen::Borderless(None))); } } - let event_loop = EventLoop::new(); - let window = builder.build(&event_loop)?; + let window = builder.build(event_loop)?; // the instance's main purpose is to create an adapter and a surface let instance = wgpu::Instance::new(wgpu::Backends::all()); @@ -147,7 +197,7 @@ impl Renderer { compatible_surface: Some(&surface), force_fallback_adapter: false, }) - .await; + .block_on(); let adapter = adapter.or_else(|| { instance .enumerate_adapters(wgpu::Backends::all()) @@ -166,16 +216,30 @@ impl Renderer { }, None, ) - .await + .block_on() .unwrap(); + let present_mode = if config.vsync { + wgpu::PresentMode::Fifo + } else { + let modes = surface.get_supported_modes(&adapter); + + if modes.contains(&wgpu::PresentMode::Mailbox) { + wgpu::PresentMode::Mailbox + } else if modes.contains(&wgpu::PresentMode::Immediate) { + wgpu::PresentMode::Immediate + } else { + wgpu::PresentMode::Fifo + } + }; + // configuration for the surface let config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface.get_supported_formats(&adapter)[0], // TODO make this configurable + format: surface.get_supported_formats(&adapter)[0], width: config.default_width.get(), height: config.default_height.get(), - present_mode: wgpu::PresentMode::Mailbox, // TODO make this configurable + present_mode, }; surface.configure(&device, &config); @@ -186,16 +250,19 @@ impl Renderer { queue, config, window, - event_loop, }) } - pub const fn size(&self) -> PhysicalSize<u32> { - PhysicalSize::new(self.config.width, self.config.height) + /// Get the current logical size of the window. This adapts to the + /// window's scale factor + fn logical_size(&self) -> LogicalSize<u32> { + self.window + .inner_size() + .to_logical(self.window.scale_factor()) } // TODO return error for zero-sized windows - pub fn resize(&mut self, size: PhysicalSize<u32>) { + fn resize_renderer(&mut self, size: PhysicalSize<u32>) { if size.width > 0 && size.height > 0 { self.config.height = size.height; self.config.width = size.width; @@ -210,7 +277,7 @@ impl Renderer { /// A number of problems could occur here. A timeout could occur while /// trying to acquire the next frame. There may also be no more memory left /// that can be used for the new frame. - pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + fn render(&mut self) -> Result<(), wgpu::SurfaceError> { // the new texture we can render to let output = self.surface.get_current_texture()?; let view = output @@ -247,4 +314,33 @@ impl Renderer { Ok(()) } + + pub fn run(mut self, event_loop: EventLoop<()>) { + event_loop.run(move |event, _, control_flow| match event { + Event::WindowEvent { window_id, event } => { + if window_id == self.window.id() { + match event { + WindowEvent::Resized(size) => { + if size.width > 0 && size.height > 0 { + self.config.height = size.height; + self.config.width = size.width; + self.surface.configure(&self.device, &self.config); + } + } + WindowEvent::CloseRequested => *control_flow = ControlFlow::ExitWithCode(0), + _ => (), + } + } + } + Event::RedrawRequested(window_id) => { + if window_id == self.window.id() { + _ = self.render(); + } + } + Event::MainEventsCleared => { + self.window.request_redraw(); + } + _ => {} + }) + } } |
