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, Hash)]
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, Hash)]
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, Hash)]
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, Hash)]
// 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: &'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,
/// The initial capacity of the instance buffer. The size will increase if
/// it's not large enough. Increasing this value may improve performance
/// towards the beginning, if a lot of instances are being created.
pub instance_capacity: usize,
}
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",
low_power: true,
vsync: true,
instance_capacity: 0,
}
}
}
impl<'a> RenderWindowConfig<'a> {
pub(crate) fn present_mode(
vsync: bool,
supported_modes: &[wgpu::PresentMode],
) -> wgpu::PresentMode {
if 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
}
}
/// Create a `WindowBuilder` from the configuration given. This window is
/// initially invisible and must later be made visible.
pub(crate) fn to_window(&self) -> WindowBuilder {
// start building the window
let mut builder = WindowBuilder::new()
.with_title(self.title)
.with_visible(false)
.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 = Self::present_mode(self.vsync, supported_modes);
// 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
}
}
}
|