use crate::app::LoopMode;
use crate::event::{
Key, MouseButton, MouseScrollDelta, TouchEvent, TouchPhase, TouchpadPressure, WindowEvent,
};
use crate::frame::{self, Frame, RawFrame};
use crate::geom;
use crate::geom::{Point2, Vector2};
use crate::vk::{self, win::VkSurfaceBuild, VulkanObject};
use crate::App;
use std::any::Any;
use std::error::Error as StdError;
use std::path::PathBuf;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use std::{cmp, env, fmt, ops};
use winit::dpi::LogicalSize;
use winit::{self, MonitorId, MouseCursor};
pub use winit::WindowId as Id;
pub const DEFAULT_DIMENSIONS: LogicalSize = LogicalSize {
width: 1024.0,
height: 768.0,
};
pub struct Builder<'app> {
app: &'app App,
vk_physical_device: Option<vk::PhysicalDevice<'app>>,
vk_device_extensions: Option<vk::DeviceExtensions>,
vk_device_queue: Option<Arc<vk::Queue>>,
window: winit::WindowBuilder,
title_was_set: bool,
swapchain_builder: SwapchainBuilder,
user_functions: UserFunctions,
msaa_samples: Option<u32>,
}
#[derive(Debug, Default)]
pub(crate) struct UserFunctions {
pub(crate) view: Option<View>,
pub(crate) event: Option<EventFnAny>,
pub(crate) raw_event: Option<RawEventFnAny>,
pub(crate) key_pressed: Option<KeyPressedFnAny>,
pub(crate) key_released: Option<KeyReleasedFnAny>,
pub(crate) mouse_moved: Option<MouseMovedFnAny>,
pub(crate) mouse_pressed: Option<MousePressedFnAny>,
pub(crate) mouse_released: Option<MouseReleasedFnAny>,
pub(crate) mouse_entered: Option<MouseEnteredFnAny>,
pub(crate) mouse_exited: Option<MouseExitedFnAny>,
pub(crate) mouse_wheel: Option<MouseWheelFnAny>,
pub(crate) moved: Option<MovedFnAny>,
pub(crate) resized: Option<ResizedFnAny>,
pub(crate) touch: Option<TouchFnAny>,
pub(crate) touchpad_pressure: Option<TouchpadPressureFnAny>,
pub(crate) hovered_file: Option<HoveredFileFnAny>,
pub(crate) hovered_file_cancelled: Option<HoveredFileCancelledFnAny>,
pub(crate) dropped_file: Option<DroppedFileFnAny>,
pub(crate) focused: Option<FocusedFnAny>,
pub(crate) unfocused: Option<UnfocusedFnAny>,
pub(crate) closed: Option<ClosedFnAny>,
}
pub type ViewFn<Model> = fn(&App, &Model, Frame) -> Frame;
pub type RawViewFn<Model> = fn(&App, &Model, RawFrame) -> RawFrame;
pub type SketchFn = fn(&App, Frame) -> Frame;
#[derive(Clone)]
pub(crate) enum View {
WithModel(ViewFnAny),
WithModelRaw(RawViewFnAny),
Sketch(SketchFn),
}
pub type RawEventFn<Model> = fn(&App, &mut Model, winit::WindowEvent);
pub type EventFn<Model> = fn(&App, &mut Model, WindowEvent);
pub type KeyPressedFn<Model> = fn(&App, &mut Model, Key);
pub type KeyReleasedFn<Model> = fn(&App, &mut Model, Key);
pub type MouseMovedFn<Model> = fn(&App, &mut Model, Point2);
pub type MousePressedFn<Model> = fn(&App, &mut Model, MouseButton);
pub type MouseReleasedFn<Model> = fn(&App, &mut Model, MouseButton);
pub type MouseEnteredFn<Model> = fn(&App, &mut Model);
pub type MouseExitedFn<Model> = fn(&App, &mut Model);
pub type MouseWheelFn<Model> = fn(&App, &mut Model, MouseScrollDelta, TouchPhase);
pub type MovedFn<Model> = fn(&App, &mut Model, Vector2);
pub type ResizedFn<Model> = fn(&App, &mut Model, Vector2);
pub type TouchFn<Model> = fn(&App, &mut Model, TouchEvent);
pub type TouchpadPressureFn<Model> = fn(&App, &mut Model, TouchpadPressure);
pub type HoveredFileFn<Model> = fn(&App, &mut Model, PathBuf);
pub type HoveredFileCancelledFn<Model> = fn(&App, &mut Model);
pub type DroppedFileFn<Model> = fn(&App, &mut Model, PathBuf);
pub type FocusedFn<Model> = fn(&App, &mut Model);
pub type UnfocusedFn<Model> = fn(&App, &mut Model);
pub type ClosedFn<Model> = fn(&App, &mut Model);
macro_rules! fn_any {
($TFn:ident<M>, $TFnAny:ident) => {
#[derive(Clone)]
pub(crate) struct $TFnAny {
fn_ptr: Arc<Any>,
}
impl $TFnAny {
pub fn from_fn_ptr<M>(fn_ptr: $TFn<M>) -> Self
where
M: 'static,
{
let fn_ptr = Arc::new(fn_ptr) as Arc<Any>;
$TFnAny { fn_ptr }
}
pub fn to_fn_ptr<M>(&self) -> Option<&$TFn<M>>
where
M: 'static,
{
self.fn_ptr.downcast_ref::<$TFn<M>>()
}
}
impl fmt::Debug for $TFnAny {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", stringify!($TFnAny))
}
}
};
}
fn_any!(ViewFn<M>, ViewFnAny);
fn_any!(RawViewFn<M>, RawViewFnAny);
fn_any!(EventFn<M>, EventFnAny);
fn_any!(RawEventFn<M>, RawEventFnAny);
fn_any!(KeyPressedFn<M>, KeyPressedFnAny);
fn_any!(KeyReleasedFn<M>, KeyReleasedFnAny);
fn_any!(MouseMovedFn<M>, MouseMovedFnAny);
fn_any!(MousePressedFn<M>, MousePressedFnAny);
fn_any!(MouseReleasedFn<M>, MouseReleasedFnAny);
fn_any!(MouseEnteredFn<M>, MouseEnteredFnAny);
fn_any!(MouseExitedFn<M>, MouseExitedFnAny);
fn_any!(MouseWheelFn<M>, MouseWheelFnAny);
fn_any!(MovedFn<M>, MovedFnAny);
fn_any!(ResizedFn<M>, ResizedFnAny);
fn_any!(TouchFn<M>, TouchFnAny);
fn_any!(TouchpadPressureFn<M>, TouchpadPressureFnAny);
fn_any!(HoveredFileFn<M>, HoveredFileFnAny);
fn_any!(HoveredFileCancelledFn<M>, HoveredFileCancelledFnAny);
fn_any!(DroppedFileFn<M>, DroppedFileFnAny);
fn_any!(FocusedFn<M>, FocusedFnAny);
fn_any!(UnfocusedFn<M>, UnfocusedFnAny);
fn_any!(ClosedFn<M>, ClosedFnAny);
#[derive(Debug)]
pub struct Window {
pub(crate) queue: Arc<vk::Queue>,
pub(crate) surface: Arc<Surface>,
msaa_samples: u32,
pub(crate) swapchain: Arc<WindowSwapchain>,
pub(crate) frame_render_data: Option<frame::RenderData>,
pub(crate) frame_count: u64,
pub(crate) user_functions: UserFunctions,
pub(crate) user_specified_present_mode: Option<vk::swapchain::PresentMode>,
pub(crate) user_specified_image_count: Option<u32>,
}
pub type Surface = vk::swapchain::Surface<winit::Window>;
pub type Swapchain = vk::swapchain::Swapchain<winit::Window>;
pub type SwapchainImage = vk::image::swapchain::SwapchainImage<winit::Window>;
pub type SwapchainAcquireFuture = vk::swapchain::SwapchainAcquireFuture<winit::Window>;
pub(crate) struct WindowSwapchain {
pub(crate) needs_recreation: AtomicBool,
pub(crate) frame_created: u64,
pub(crate) swapchain: Arc<Swapchain>,
pub(crate) images: Vec<Arc<SwapchainImage>>,
pub(crate) previous_frame_end: Mutex<Option<vk::FenceSignalFuture<Box<vk::GpuFuture>>>>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SwapchainBuilder {
pub format: Option<vk::Format>,
pub color_space: Option<vk::swapchain::ColorSpace>,
pub layers: Option<u32>,
pub present_mode: Option<vk::swapchain::PresentMode>,
pub composite_alpha: Option<vk::swapchain::CompositeAlpha>,
pub clipped: Option<bool>,
pub image_count: Option<u32>,
pub surface_transform: Option<vk::swapchain::SurfaceTransform>,
}
#[derive(Default)]
pub struct SwapchainFramebuffers {
framebuffers: Vec<Arc<vk::FramebufferAbstract + Send + Sync>>,
}
pub type SwapchainFramebufferBuilder<A> =
vk::FramebufferBuilder<Arc<vk::RenderPassAbstract + Send + Sync>, A>;
pub type FramebufferBuildResult<A> =
Result<SwapchainFramebufferBuilder<A>, vk::FramebufferCreationError>;
impl SwapchainFramebuffers {
pub fn update<F, A>(
&mut self,
frame: &RawFrame,
render_pass: Arc<vk::RenderPassAbstract + Send + Sync>,
builder: F,
) -> Result<(), vk::FramebufferCreationError>
where
F: Fn(SwapchainFramebufferBuilder<()>, Arc<SwapchainImage>) -> FramebufferBuildResult<A>,
A: 'static + vk::AttachmentsList + Send + Sync,
{
let mut just_created = false;
while frame.swapchain_image_index() >= self.framebuffers.len() {
let builder = builder(
vk::Framebuffer::start(render_pass.clone()),
frame.swapchain_image().clone(),
)?;
let fb = builder.build()?;
self.framebuffers.push(Arc::new(fb));
just_created = true;
}
let old_rp =
vk::RenderPassAbstract::inner(&self.framebuffers[frame.swapchain_image_index()])
.internal_object();
let new_rp = render_pass.inner().internal_object();
if !just_created && (frame.swapchain_image_is_new() || old_rp != new_rp) {
let fb = &mut self.framebuffers[frame.swapchain_image_index()];
let builder = builder(
vk::Framebuffer::start(render_pass.clone()),
frame.swapchain_image().clone(),
)?;
let new_fb = builder.build()?;
*fb = Arc::new(new_fb);
}
Ok(())
}
}
impl ops::Deref for SwapchainFramebuffers {
type Target = [Arc<vk::FramebufferAbstract + Send + Sync>];
fn deref(&self) -> &Self::Target {
&self.framebuffers
}
}
#[derive(Debug)]
pub enum BuildError {
SurfaceCreation(vk::win::CreationError),
DeviceCreation(vk::DeviceCreationError),
SwapchainCreation(vk::SwapchainCreationError),
SwapchainCapabilities(vk::swapchain::CapabilitiesError),
RenderDataCreation(frame::RenderDataCreationError),
SurfaceDoesNotSupportCompositeAlphaOpaque,
}
impl SwapchainBuilder {
pub const DEFAULT_CLIPPED: bool = true;
pub const DEFAULT_COLOR_SPACE: vk::swapchain::ColorSpace =
vk::swapchain::ColorSpace::SrgbNonLinear;
pub const DEFAULT_COMPOSITE_ALPHA: vk::swapchain::CompositeAlpha =
vk::swapchain::CompositeAlpha::Opaque;
pub const DEFAULT_LAYERS: u32 = 1;
pub const DEFAULT_SURFACE_TRANSFORM: vk::swapchain::SurfaceTransform =
vk::swapchain::SurfaceTransform::Identity;
pub fn new() -> Self {
Default::default()
}
pub fn from_swapchain(swapchain: &Swapchain) -> Self {
SwapchainBuilder::new()
.format(swapchain.format())
.image_count(swapchain.num_images())
.layers(swapchain.layers())
.surface_transform(swapchain.transform())
.composite_alpha(swapchain.composite_alpha())
.present_mode(swapchain.present_mode())
.clipped(swapchain.clipped())
}
pub fn format(mut self, format: vk::Format) -> Self {
self.format = Some(format);
self
}
pub fn color_space(mut self, color_space: vk::swapchain::ColorSpace) -> Self {
self.color_space = Some(color_space);
self
}
pub fn composite_alpha(mut self, composite_alpha: vk::swapchain::CompositeAlpha) -> Self {
self.composite_alpha = Some(composite_alpha);
self
}
pub fn present_mode(mut self, present_mode: vk::swapchain::PresentMode) -> Self {
self.present_mode = Some(present_mode);
self
}
pub fn image_count(mut self, image_count: u32) -> Self {
self.image_count = Some(image_count);
self
}
pub fn clipped(mut self, clipped: bool) -> Self {
self.clipped = Some(clipped);
self
}
pub fn surface_transform(mut self, surface_transform: vk::swapchain::SurfaceTransform) -> Self {
self.surface_transform = Some(surface_transform);
self
}
pub fn layers(mut self, layers: u32) -> Self {
self.layers = Some(layers);
self
}
pub(crate) fn build<S>(
self,
device: Arc<vk::Device>,
surface: Arc<Surface>,
sharing_mode: S,
loop_mode: &LoopMode,
fallback_dimensions: Option<[u32; 2]>,
old_swapchain: Option<&Arc<Swapchain>>,
) -> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), vk::SwapchainCreationError>
where
S: Into<vk::sync::SharingMode>,
{
let capabilities = surface
.capabilities(device.physical_device())
.expect("failed to retrieve surface capabilities");
let dimensions = capabilities
.current_extent
.or(fallback_dimensions)
.unwrap_or([
DEFAULT_DIMENSIONS.width as _,
DEFAULT_DIMENSIONS.height as _,
]);
let format = match self.format {
Some(fmt) => fmt,
None => {
let color_space = self.color_space.unwrap_or(Self::DEFAULT_COLOR_SPACE);
capabilities
.supported_formats
.iter()
.filter(|&&(fmt, cs)| vk::format_is_srgb(fmt) && cs == color_space)
.next()
.or_else(|| {
capabilities
.supported_formats
.iter()
.filter(|&&(_, cs)| cs == color_space)
.next()
})
.map(|&(fmt, _cs)| fmt)
.ok_or(vk::SwapchainCreationError::UnsupportedFormat)?
}
};
let (present_mode, image_count) = preferred_present_mode_and_image_count(
&loop_mode,
capabilities.min_image_count,
self.present_mode,
self.image_count,
&capabilities.present_modes,
);
let composite_alpha = match self.composite_alpha {
Some(alpha) => alpha,
None => match capabilities.supported_composite_alpha.opaque {
true => Self::DEFAULT_COMPOSITE_ALPHA,
false => return Err(vk::SwapchainCreationError::UnsupportedCompositeAlpha),
},
};
let layers = self.layers.unwrap_or(Self::DEFAULT_LAYERS);
let clipped = self.clipped.unwrap_or(Self::DEFAULT_CLIPPED);
let surface_transform = self
.surface_transform
.unwrap_or(Self::DEFAULT_SURFACE_TRANSFORM);
Swapchain::new(
device,
surface,
image_count,
format,
dimensions,
layers,
capabilities.supported_usage_flags,
sharing_mode,
surface_transform,
composite_alpha,
present_mode,
clipped,
old_swapchain,
)
}
}
pub fn preferred_present_mode_and_image_count(
loop_mode: &LoopMode,
min_image_count: u32,
present_mode: Option<vk::swapchain::PresentMode>,
image_count: Option<u32>,
supported_present_modes: &vk::swapchain::SupportedPresentModes,
) -> (vk::swapchain::PresentMode, u32) {
match (present_mode, image_count) {
(Some(pm), Some(ic)) => (pm, ic),
(None, _) => match *loop_mode {
LoopMode::RefreshSync { .. } => {
let image_count = image_count.unwrap_or_else(|| cmp::max(min_image_count, 2));
(vk::swapchain::PresentMode::Fifo, image_count)
}
LoopMode::Wait { .. } | LoopMode::Rate { .. } => {
if supported_present_modes.mailbox {
let image_count = image_count.unwrap_or_else(|| cmp::max(min_image_count, 3));
(vk::swapchain::PresentMode::Mailbox, image_count)
} else {
let image_count = image_count.unwrap_or_else(|| cmp::max(min_image_count, 2));
(vk::swapchain::PresentMode::Fifo, image_count)
}
}
},
(Some(present_mode), None) => {
let image_count = match present_mode {
vk::swapchain::PresentMode::Immediate => min_image_count,
vk::swapchain::PresentMode::Mailbox => cmp::max(min_image_count, 3),
vk::swapchain::PresentMode::Fifo => cmp::max(min_image_count, 2),
vk::swapchain::PresentMode::Relaxed => cmp::max(min_image_count, 2),
};
(present_mode, image_count)
}
}
}
impl<'app> Builder<'app> {
pub fn new(app: &'app App) -> Self {
Builder {
app,
vk_physical_device: None,
vk_device_extensions: None,
vk_device_queue: None,
window: winit::WindowBuilder::new(),
title_was_set: false,
swapchain_builder: Default::default(),
user_functions: Default::default(),
msaa_samples: None,
}
}
pub fn window(mut self, window: winit::WindowBuilder) -> Self {
self.window = window;
self
}
pub fn vk_physical_device(mut self, device: vk::PhysicalDevice<'app>) -> Self {
self.vk_physical_device = Some(device);
self
}
pub fn vk_device_extensions(mut self, extensions: vk::DeviceExtensions) -> Self {
self.vk_device_extensions = Some(extensions);
self
}
pub fn vk_device_queue(mut self, queue: Arc<vk::Queue>) -> Self {
self.vk_device_queue = Some(queue);
self
}
pub fn swapchain_builder(mut self, swapchain_builder: SwapchainBuilder) -> Self {
self.swapchain_builder = swapchain_builder;
self
}
pub fn msaa_samples(mut self, msaa_samples: u32) -> Self {
self.msaa_samples = Some(msaa_samples);
self
}
pub fn sketch(mut self, sketch_fn: SketchFn) -> Self {
self.user_functions.view = Some(View::Sketch(sketch_fn));
self
}
pub fn view<M>(mut self, view_fn: ViewFn<M>) -> Self
where
M: 'static,
{
self.user_functions.view = Some(View::WithModel(ViewFnAny::from_fn_ptr(view_fn)));
self
}
pub fn raw_view<M>(mut self, raw_view_fn: RawViewFn<M>) -> Self
where
M: 'static,
{
self.user_functions.view = Some(View::WithModelRaw(RawViewFnAny::from_fn_ptr(raw_view_fn)));
self
}
pub fn event<M>(mut self, event_fn: EventFn<M>) -> Self
where
M: 'static,
{
self.user_functions.event = Some(EventFnAny::from_fn_ptr(event_fn));
self
}
pub fn raw_event<M>(mut self, raw_event_fn: RawEventFn<M>) -> Self
where
M: 'static,
{
self.user_functions.raw_event = Some(RawEventFnAny::from_fn_ptr(raw_event_fn));
self
}
pub fn key_pressed<M>(mut self, f: KeyPressedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.key_pressed = Some(KeyPressedFnAny::from_fn_ptr(f));
self
}
pub fn key_released<M>(mut self, f: KeyReleasedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.key_released = Some(KeyReleasedFnAny::from_fn_ptr(f));
self
}
pub fn mouse_moved<M>(mut self, f: MouseMovedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_moved = Some(MouseMovedFnAny::from_fn_ptr(f));
self
}
pub fn mouse_pressed<M>(mut self, f: MousePressedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_pressed = Some(MousePressedFnAny::from_fn_ptr(f));
self
}
pub fn mouse_released<M>(mut self, f: MouseReleasedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_released = Some(MouseReleasedFnAny::from_fn_ptr(f));
self
}
pub fn mouse_wheel<M>(mut self, f: MouseWheelFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_wheel = Some(MouseWheelFnAny::from_fn_ptr(f));
self
}
pub fn mouse_entered<M>(mut self, f: MouseEnteredFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_entered = Some(MouseEnteredFnAny::from_fn_ptr(f));
self
}
pub fn mouse_exited<M>(mut self, f: MouseExitedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_exited = Some(MouseExitedFnAny::from_fn_ptr(f));
self
}
pub fn touch<M>(mut self, f: TouchFn<M>) -> Self
where
M: 'static,
{
self.user_functions.touch = Some(TouchFnAny::from_fn_ptr(f));
self
}
pub fn touchpad_pressure<M>(mut self, f: TouchpadPressureFn<M>) -> Self
where
M: 'static,
{
self.user_functions.touchpad_pressure = Some(TouchpadPressureFnAny::from_fn_ptr(f));
self
}
pub fn moved<M>(mut self, f: MovedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.moved = Some(MovedFnAny::from_fn_ptr(f));
self
}
pub fn resized<M>(mut self, f: ResizedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.resized = Some(ResizedFnAny::from_fn_ptr(f));
self
}
pub fn hovered_file<M>(mut self, f: HoveredFileFn<M>) -> Self
where
M: 'static,
{
self.user_functions.hovered_file = Some(HoveredFileFnAny::from_fn_ptr(f));
self
}
pub fn hovered_file_cancelled<M>(mut self, f: HoveredFileCancelledFn<M>) -> Self
where
M: 'static,
{
self.user_functions.hovered_file_cancelled =
Some(HoveredFileCancelledFnAny::from_fn_ptr(f));
self
}
pub fn dropped_file<M>(mut self, f: DroppedFileFn<M>) -> Self
where
M: 'static,
{
self.user_functions.dropped_file = Some(DroppedFileFnAny::from_fn_ptr(f));
self
}
pub fn focused<M>(mut self, f: FocusedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.focused = Some(FocusedFnAny::from_fn_ptr(f));
self
}
pub fn unfocused<M>(mut self, f: UnfocusedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.unfocused = Some(UnfocusedFnAny::from_fn_ptr(f));
self
}
pub fn closed<M>(mut self, f: ClosedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.closed = Some(ClosedFnAny::from_fn_ptr(f));
self
}
pub fn build(self) -> Result<Id, BuildError> {
let Builder {
app,
vk_physical_device,
vk_device_extensions,
vk_device_queue,
mut window,
title_was_set,
swapchain_builder,
user_functions,
msaa_samples,
} = self;
if !title_was_set {
if let Ok(exe_path) = env::current_exe() {
if let Some(os_str) = exe_path.file_stem() {
if let Some(exe_name) = os_str.to_str() {
let title = format!("nannou - {}", exe_name);
window = window.with_title(title);
}
}
}
}
let initial_swapchain_dimensions = window
.window
.dimensions
.or_else(|| {
window
.window
.fullscreen
.as_ref()
.map(|monitor| monitor.get_dimensions().to_logical(1.0))
})
.unwrap_or_else(|| {
let mut dim = DEFAULT_DIMENSIONS;
if let Some(min) = window.window.min_dimensions {
dim.width = dim.width.max(min.width);
dim.height = dim.height.max(min.height);
}
if let Some(max) = window.window.max_dimensions {
dim.width = dim.width.min(max.width);
dim.height = dim.height.min(max.height);
}
dim
});
if window.window.dimensions.is_none() && window.window.fullscreen.is_none() {
window.window.dimensions = Some(initial_swapchain_dimensions);
}
let surface = window.build_vk_surface(&app.events_loop, app.vk_instance.clone())?;
let queue = match vk_device_queue {
Some(queue) => queue,
None => {
let physical_device = vk_physical_device
.or_else(|| app.default_vk_physical_device())
.unwrap_or_else(|| unimplemented!());
let queue_family = physical_device
.queue_families()
.find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false))
.unwrap_or_else(|| unimplemented!("couldn't find a graphical queue family"));
let queue_priority = 0.5;
let mut device_ext =
vk_device_extensions.unwrap_or_else(vk::DeviceExtensions::none);
device_ext.khr_swapchain = true;
let features = physical_device.supported_features();
let (_device, mut queues) = vk::Device::new(
physical_device,
features,
&device_ext,
[(queue_family, queue_priority)].iter().cloned(),
)?;
let queue = queues.next().expect("expected a single device queue");
queue
}
};
let user_specified_present_mode = swapchain_builder.present_mode;
let user_specified_image_count = swapchain_builder.image_count;
let (swapchain, images) = {
let fallback_dimensions = [
initial_swapchain_dimensions.width as _,
initial_swapchain_dimensions.height as _,
];
swapchain_builder.build(
queue.device().clone(),
surface.clone(),
&queue,
&app.loop_mode(),
Some(fallback_dimensions),
None,
)?
};
let (frame_render_data, msaa_samples) = match user_functions.view {
Some(View::WithModel(_)) | Some(View::Sketch(_)) | None => {
let target_msaa_samples = msaa_samples.unwrap_or(Frame::DEFAULT_MSAA_SAMPLES);
let physical_device = queue.device().physical_device();
let msaa_samples = vk::msaa_samples_limited(&physical_device, target_msaa_samples);
let render_data = frame::RenderData::new(
queue.device().clone(),
swapchain.dimensions(),
msaa_samples,
swapchain.format(),
)?;
(Some(render_data), msaa_samples)
}
Some(View::WithModelRaw(_)) => (None, 1),
};
let window_id = surface.window().id();
let needs_recreation = AtomicBool::new(false);
let previous_frame_end = Mutex::new(None);
let frame_count = 0;
let swapchain = Arc::new(WindowSwapchain {
needs_recreation,
frame_created: frame_count,
swapchain,
images,
previous_frame_end,
});
let window = Window {
queue,
surface,
msaa_samples,
swapchain,
frame_render_data,
frame_count,
user_functions,
user_specified_present_mode,
user_specified_image_count,
};
app.windows.borrow_mut().insert(window_id, window);
if app.windows.borrow().len() == 1 {
*app.focused_window.borrow_mut() = Some(window_id);
}
Ok(window_id)
}
fn map_window<F>(self, map: F) -> Self
where
F: FnOnce(winit::WindowBuilder) -> winit::WindowBuilder,
{
let Builder {
app,
vk_physical_device,
vk_device_extensions,
vk_device_queue,
window,
title_was_set,
swapchain_builder,
user_functions,
msaa_samples,
} = self;
let window = map(window);
Builder {
app,
vk_physical_device,
vk_device_extensions,
vk_device_queue,
window,
title_was_set,
swapchain_builder,
user_functions,
msaa_samples,
}
}
pub fn with_dimensions(self, width: u32, height: u32) -> Self {
self.map_window(|w| w.with_dimensions((width, height).into()))
}
pub fn with_min_dimensions(self, width: u32, height: u32) -> Self {
self.map_window(|w| w.with_min_dimensions((width, height).into()))
}
pub fn with_max_dimensions(self, width: u32, height: u32) -> Self {
self.map_window(|w| w.with_max_dimensions((width, height).into()))
}
pub fn with_title<T>(mut self, title: T) -> Self
where
T: Into<String>,
{
self.title_was_set = true;
self.map_window(|w| w.with_title(title))
}
pub fn with_fullscreen(self, monitor: Option<MonitorId>) -> Self {
self.map_window(|w| w.with_fullscreen(monitor))
}
pub fn with_maximized(self, maximized: bool) -> Self {
self.map_window(|w| w.with_maximized(maximized))
}
pub fn with_visibility(self, visible: bool) -> Self {
self.map_window(|w| w.with_visibility(visible))
}
pub fn with_transparency(self, transparent: bool) -> Self {
self.map_window(|w| w.with_transparency(transparent))
}
pub fn with_decorations(self, decorations: bool) -> Self {
self.map_window(|w| w.with_decorations(decorations))
}
pub fn with_multitouch(self) -> Self {
self.map_window(|w| w.with_multitouch())
}
}
impl Window {
const NO_LONGER_EXISTS: &'static str = "the window no longer exists";
pub fn set_title(&self, title: &str) {
self.surface.window().set_title(title);
}
pub fn show(&self) {
self.surface.window().show()
}
pub fn hide(&self) {
self.surface.window().hide()
}
pub fn position(&self) -> (i32, i32) {
self.surface
.window()
.get_position()
.expect(Self::NO_LONGER_EXISTS)
.into()
}
pub fn set_position(&self, x: i32, y: i32) {
self.surface.window().set_position((x, y).into())
}
pub fn inner_size_pixels(&self) -> (u32, u32) {
self.surface
.window()
.get_inner_size()
.map(|logical_px| {
let hidpi_factor = self.surface.window().get_hidpi_factor();
logical_px.to_physical(hidpi_factor)
})
.expect(Self::NO_LONGER_EXISTS)
.into()
}
pub fn inner_size_points(&self) -> (geom::scalar::Default, geom::scalar::Default) {
let size = self
.surface
.window()
.get_inner_size()
.expect(Self::NO_LONGER_EXISTS);
let (w, h): (f64, f64) = size.into();
(w as _, h as _)
}
pub fn outer_size_pixels(&self) -> (u32, u32) {
self.surface
.window()
.get_outer_size()
.map(|logical_px| {
let hidpi_factor = self.surface.window().get_hidpi_factor();
logical_px.to_physical(hidpi_factor)
})
.expect(Self::NO_LONGER_EXISTS)
.into()
}
pub fn outer_size_points(&self) -> (f32, f32) {
let size = self
.surface
.window()
.get_outer_size()
.expect(Self::NO_LONGER_EXISTS);
let (w, h): (f64, f64) = size.into();
(w as _, h as _)
}
pub fn set_inner_size_pixels(&self, width: u32, height: u32) {
self.surface.window().set_inner_size((width, height).into())
}
pub fn set_inner_size_points(&self, width: f32, height: f32) {
let hidpi_factor = self.hidpi_factor();
let w_px = (width * hidpi_factor) as _;
let h_px = (height * hidpi_factor) as _;
self.set_inner_size_pixels(w_px, h_px);
}
pub fn hidpi_factor(&self) -> geom::scalar::Default {
self.surface.window().get_hidpi_factor() as _
}
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), String> {
self.surface.window().set_cursor_position((x, y).into())
}
pub fn set_cursor(&self, state: MouseCursor) {
self.surface.window().set_cursor(state);
}
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
self.surface.window().grab_cursor(grab)
}
pub fn hide_cursor(&self, hide: bool) {
self.surface.window().hide_cursor(hide)
}
pub fn set_maximized(&self, maximized: bool) {
self.surface.window().set_maximized(maximized)
}
pub fn set_fullscreen(&self, monitor: Option<MonitorId>) {
self.surface.window().set_fullscreen(monitor)
}
pub fn current_monitor(&self) -> MonitorId {
self.surface.window().get_current_monitor()
}
pub fn id(&self) -> Id {
self.surface.window().id()
}
pub fn surface(&self) -> &Surface {
&self.surface
}
pub fn swapchain(&self) -> &Swapchain {
&self.swapchain.swapchain
}
pub fn swapchain_device(&self) -> &Arc<vk::Device> {
vk::DeviceOwned::device(self.swapchain())
}
pub fn swapchain_queue(&self) -> &Arc<vk::Queue> {
&self.queue
}
pub fn swapchain_images(&self) -> &[Arc<SwapchainImage>] {
&self.swapchain.images
}
pub fn msaa_samples(&self) -> u32 {
self.msaa_samples
}
pub(crate) fn replace_swapchain(
&mut self,
new_swapchain: Arc<Swapchain>,
new_images: Vec<Arc<SwapchainImage>>,
) {
let previous_frame_end = self
.swapchain
.previous_frame_end
.lock()
.expect("failed to lock `previous_frame_end`")
.take();
self.swapchain = Arc::new(WindowSwapchain {
needs_recreation: AtomicBool::new(false),
frame_created: self.frame_count,
swapchain: new_swapchain,
images: new_images,
previous_frame_end: Mutex::new(previous_frame_end),
});
}
pub fn is_fullscreen(&self) -> bool {
let (w, h) = self.outer_size_pixels();
let (mw, mh): (u32, u32) = self.current_monitor().get_dimensions().into();
w == mw && h == mh
}
pub fn elapsed_frames(&self) -> u64 {
self.frame_count
}
}
impl fmt::Debug for View {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let variant = match *self {
View::WithModel(ref v) => format!("WithModel({:?})", v),
View::WithModelRaw(ref v) => format!("WithModelRaw({:?})", v),
View::Sketch(_) => "Sketch".to_string(),
};
write!(f, "View::{}", variant)
}
}
impl ops::Deref for WindowSwapchain {
type Target = Arc<Swapchain>;
fn deref(&self) -> &Self::Target {
&self.swapchain
}
}
impl fmt::Debug for WindowSwapchain {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"WindowSwapchain ( swapchain: {:?}, swapchain_images: {:?} )",
self.swapchain,
self.images.len(),
)
}
}
impl StdError for BuildError {
fn description(&self) -> &str {
match *self {
BuildError::SurfaceCreation(ref err) => err.description(),
BuildError::DeviceCreation(ref err) => err.description(),
BuildError::SwapchainCreation(ref err) => err.description(),
BuildError::SwapchainCapabilities(ref err) => err.description(),
BuildError::RenderDataCreation(ref err) => err.description(),
BuildError::SurfaceDoesNotSupportCompositeAlphaOpaque => {
"`CompositeAlpha::Opaque` not supported by window surface"
}
}
}
}
impl fmt::Display for BuildError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl From<vk::win::CreationError> for BuildError {
fn from(e: vk::win::CreationError) -> Self {
BuildError::SurfaceCreation(e)
}
}
impl From<vk::DeviceCreationError> for BuildError {
fn from(e: vk::DeviceCreationError) -> Self {
BuildError::DeviceCreation(e)
}
}
impl From<vk::swapchain::SwapchainCreationError> for BuildError {
fn from(e: vk::swapchain::SwapchainCreationError) -> Self {
BuildError::SwapchainCreation(e)
}
}
impl From<vk::swapchain::CapabilitiesError> for BuildError {
fn from(e: vk::swapchain::CapabilitiesError) -> Self {
BuildError::SwapchainCapabilities(e)
}
}
impl From<frame::RenderDataCreationError> for BuildError {
fn from(e: frame::RenderDataCreationError) -> Self {
BuildError::RenderDataCreation(e)
}
}