summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMicha White <botahamec@outlook.com>2022-09-18 13:49:35 -0400
committerMicha White <botahamec@outlook.com>2022-09-18 13:49:35 -0400
commitb92e827eb623a8d93872c6f95aceeb882b867a29 (patch)
treea6024b190ac142f2b10c96fd454fa475278e83ff /src
parent8f08ff0b62d82bc969aed135b37b864e6b51e0c8 (diff)
Reorganized the config
Diffstat (limited to 'src')
-rw-r--r--src/config.rs166
-rw-r--r--src/lib.rs2
-rw-r--r--src/renderer.rs169
3 files changed, 184 insertions, 153 deletions
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..1e8c43e
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,166 @@
+use std::borrow::Cow;
+use std::num::NonZeroU32;
+
+use winit::dpi::{LogicalPosition, LogicalSize};
+use winit::window::{Fullscreen, WindowBuilder};
+
+/// 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 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),
+ // TODO support choosing a monitor
+ BorderlessFullscreen, // TODO exclusive fullscreen
+}
+
+impl Default for WindowMode {
+ fn default() -> Self {
+ Self::Windowed(WindowInfo::default())
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+// TODO window icon
+pub struct RenderWindowConfig<'a> {
+ /// The width of the window, once initialized
+ pub default_width: NonZeroU32,
+ /// The height of the window, once initialized
+ pub default_height: NonZeroU32,
+ /// The window may be fullscreen
+ pub mode: WindowMode,
+ /// The title for the window
+ pub title: Cow<'a, 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<'a> Default for RenderWindowConfig<'a> {
+ fn default() -> Self {
+ Self {
+ default_width: NonZeroU32::new(640).unwrap(),
+ default_height: NonZeroU32::new(480).unwrap(),
+ mode: WindowMode::default(),
+ title: "Alligator Game".into(),
+ low_power: true,
+ vsync: true,
+ }
+ }
+}
+
+impl<'a> RenderWindowConfig<'a> {
+ /// Create a `WindowBuilder` from the configuration given
+ pub(crate) fn to_window(&self) -> WindowBuilder {
+ // start building the window
+ let mut builder = WindowBuilder::new()
+ .with_title(self.title.as_ref())
+ .with_inner_size(LogicalSize::new(
+ self.default_width.get(),
+ self.default_height.get(),
+ ));
+
+ match self.mode {
+ WindowMode::Windowed(window_info) => {
+ builder = builder.with_maximized(window_info.default_maximized);
+
+ 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(
+ resizing_options.max_width.unwrap_or(NonZeroU32::MAX).get(),
+ resizing_options.max_height.unwrap_or(NonZeroU32::MAX).get(),
+ ));
+ }
+
+ if resizing_options.min_height.is_some() || resizing_options.min_width.is_some()
+ {
+ builder = builder.with_min_inner_size(LogicalSize::new(
+ resizing_options.min_width.unwrap_or(NonZeroU32::MAX).get(),
+ resizing_options.min_height.unwrap_or(NonZeroU32::MAX).get(),
+ ));
+ }
+ } 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)));
+ }
+ }
+
+ builder
+ }
+
+ /// Gets a surface configuration out of the config.
+ pub(crate) fn to_surface_configuration(
+ &self,
+ supported_modes: &[wgpu::PresentMode],
+ texture_format: wgpu::TextureFormat,
+ ) -> wgpu::SurfaceConfiguration {
+ let present_mode = if self.vsync {
+ wgpu::PresentMode::Fifo
+ } else if supported_modes.contains(&wgpu::PresentMode::Mailbox) {
+ wgpu::PresentMode::Mailbox
+ } else if supported_modes.contains(&wgpu::PresentMode::Immediate) {
+ wgpu::PresentMode::Immediate
+ } else {
+ wgpu::PresentMode::Fifo
+ };
+
+ // configuration for the surface
+ wgpu::SurfaceConfiguration {
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+ format: texture_format,
+ width: self.default_width.get(),
+ height: self.default_height.get(),
+ present_mode,
+ }
+ }
+
+ pub(crate) const fn power_preference(&self) -> wgpu::PowerPreference {
+ if self.low_power {
+ wgpu::PowerPreference::LowPower
+ } else {
+ wgpu::PowerPreference::HighPerformance
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index a902878..1bdc5f6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,6 +4,8 @@
#![warn(clippy::nursery)]
#![allow(clippy::module_name_repetitions)]
+pub mod config;
pub mod renderer;
+pub use config::RenderWindowConfig;
pub use renderer::Renderer;
diff --git a/src/renderer.rs b/src/renderer.rs
index c9c51bc..bf7226d 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -1,13 +1,12 @@
-use std::{borrow::Cow, num::NonZeroU32};
-
+use crate::config::RenderWindowConfig;
use pollster::FutureExt;
use thiserror::Error;
use winit::{
- dpi::{LogicalPosition, LogicalSize, PhysicalSize},
+ dpi::PhysicalSize,
error::OsError,
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
- window::{Fullscreen, Window, WindowBuilder},
+ window::Window,
};
/// No device could be found which supports the given surface
@@ -33,83 +32,6 @@ pub enum NewRendererError {
WindowInitError(#[from] OsError),
}
-/// 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 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),
- // TODO support choosing a monitor
- BorderlessFullscreen, // TODO exclusive fullscreen
-}
-
-impl Default for WindowMode {
- fn default() -> Self {
- Self::Windowed(WindowInfo::default())
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-// TODO window icon
-pub struct RenderWindowConfig<'a> {
- /// The width of the window, once initialized
- pub default_width: NonZeroU32,
- /// The height of the window, once initialized
- pub default_height: NonZeroU32,
- /// The window may be fullscreen
- pub mode: WindowMode,
- /// The title for the window
- pub title: Cow<'a, 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<'a> Default for RenderWindowConfig<'a> {
- fn default() -> Self {
- Self {
- default_width: NonZeroU32::new(640).unwrap(),
- default_height: NonZeroU32::new(480).unwrap(),
- mode: WindowMode::default(),
- title: "Alligator Game".into(),
- low_power: true,
- vsync: true,
- }
- }
-}
-
pub struct Renderer {
surface: wgpu::Surface,
device: wgpu::Device,
@@ -126,57 +48,19 @@ impl Renderer {
///
/// Returns a [`NoGpu`] error if no device could be detected that can
/// display to the window
+ ///
+ /// # Panics
+ ///
+ /// This function **must** be called on the main thread, or else it may
+ /// panic on some platforms.
// TODO make it possible to use without a window (ie, use a bitmap in memory as a surface)
// TODO this function needs to be smaller
- #[allow(clippy::missing_panics_doc)]
pub fn new(
- config: RenderWindowConfig,
+ config: &RenderWindowConfig,
event_loop: &EventLoop<()>,
) -> Result<Self, NewRendererError> {
- // start building the window
- let mut builder = WindowBuilder::new()
- .with_title(config.title)
- .with_inner_size(LogicalSize::new(
- config.default_width.get(),
- config.default_height.get(),
- ));
-
- match config.mode {
- WindowMode::Windowed(window_info) => {
- builder = builder.with_maximized(window_info.default_maximized);
-
- 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(
- resizing_options.max_width.unwrap_or(NonZeroU32::MAX).get(),
- resizing_options.max_height.unwrap_or(NonZeroU32::MAX).get(),
- ));
- }
-
- if resizing_options.min_height.is_some() || resizing_options.min_width.is_some()
- {
- builder = builder.with_min_inner_size(LogicalSize::new(
- resizing_options.min_width.unwrap_or(NonZeroU32::MAX).get(),
- resizing_options.min_height.unwrap_or(NonZeroU32::MAX).get(),
- ));
- }
- } 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 window = builder.build(event_loop)?;
+ // build the window
+ let window = config.to_window().build(event_loop)?;
// the instance's main purpose is to create an adapter and a surface
let instance = wgpu::Instance::new(wgpu::Backends::all());
@@ -184,11 +68,7 @@ impl Renderer {
// the surface is the part of the screen we'll draw to
let surface = unsafe { instance.create_surface(&window) };
- let power_preference = if config.low_power {
- wgpu::PowerPreference::LowPower
- } else {
- wgpu::PowerPreference::HighPerformance
- };
+ let power_preference = config.power_preference();
// the adapter is the handle to the GPU
let adapter = instance
@@ -219,28 +99,11 @@ impl Renderer {
.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],
- width: config.default_width.get(),
- height: config.default_height.get(),
- present_mode,
- };
+ let config = config.to_surface_configuration(
+ &surface.get_supported_modes(&adapter),
+ surface.get_supported_formats(&adapter)[0],
+ );
surface.configure(&device, &config);