diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 1474 |
1 files changed, 722 insertions, 752 deletions
diff --git a/src/main.rs b/src/main.rs index 7c04245..3bba33b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ #![deny(unsafe_code)] use crate::error::*; -use std::cell::OnceCell; +use std::cell::RefCell; use std::collections::{ BTreeMap, BTreeSet, HashSet }; use std::ffi::{ c_void, CStr }; use vulkanalia::{ Device, Entry, Instance, Version }; @@ -52,6 +52,13 @@ impl<T> Acceptable<T> { panic!("Unwrapped a rejected Acceptable."); } } + + fn require(self) -> Result<T> { + match self { + Acceptable::Accepted(value) => Ok(value), + Acceptable::Rejected(message) => Err(Error { message }), + } + } } #[derive(Debug)] @@ -73,46 +80,35 @@ struct Surreality { // provided by winit, and may be X11, Wayland, or some more curious thing. // The way we initialize Vulkan requires us to have at least one of these; // we could have more, but for now, we don't. - window: OnceCell<Window>, - - // There are a few Vulkan features (in the informal sense of "feature") - // that we want to be able to run both with and without. Here, we have - // booleans describing which we're doing. These are computed during instance - // and device initialization. - // - // In the cases of validation and portability, the booleans are only used - // to communicate between initialization phases, and aren't actually needed - // during rendering. We keep them around anyway though because they're small - // and it's more convenient to have them all in one place. - enable_validation: OnceCell<bool>, - enable_portability: OnceCell<bool>, - enable_swapchain: OnceCell<bool>, + window: RefCell<Option<Window>>, // The Vulkan "entry" is the part of the Vulkan library ecosystem that's - // responsible for finding and loading the other parts. - entry: OnceCell<Entry>, + // responsible for finding and loading the other parts. Once we have the + // instance, the entry is never directly used again, but we retain it + // because doing otherwise would segfault. + entry: RefCell<Option<Entry>>, // The Vulkan "instance" is the bulk of the Vulkan library, with most of // the high-level responsibilities around lifecycle management. - instance: OnceCell<Instance>, + instance: RefCell<Option<Instance>>, // The debug messager is a Vulkan object representing our callback which // Vulkan uses to tell us things. // // Vulkan spells "messager" as "messenger", but this is absurd // over-formality and we don't indulge it. - debug_messager: OnceCell<vk::DebugUtilsMessengerEXT>, + debug_messager: RefCell<Option<vk::DebugUtilsMessengerEXT>>, // The Vulkan "surface" is the destination that rendering happens into. // It is connected to the window but distinct from it. - surface: OnceCell<vk::SurfaceKHR>, + surface: RefCell<Option<vk::SurfaceKHR>>, // The Vulkan "device" is the abstraction for a GPU. A physical one is the - // actual GPU, and a logical one is our connection to it. We track both. - // We'll be referencing the logical device a lot, so we follow Vulkan's lead - // and let it have a short variable name. - physical_device: OnceCell<vk::PhysicalDevice>, - device: OnceCell<Device>, + // actual GPU, and a logical one is our connection to it. We pick a physical + // device during initialization, but only the logical one is used later, so + // it's all we track. We'll be referencing the logical device a lot, so we + // follow Vulkan's lead and let it have a short variable name. + device: RefCell<Option<Device>>, // Vulkan has a first-class concept of command queues. We have two of // them, one for graphics drawing commands and one for presentation. @@ -125,31 +121,59 @@ struct Surreality { // // Yes, this means the compiler has to deal with pointer aliasing // concerns, which have a tendency to defeat optimizations. - graphics_queue: OnceCell<vk::Queue>, - presentation_queue: OnceCell<vk::Queue>, + graphics_queue: RefCell<Option<vk::Queue>>, + presentation_queue: RefCell<Option<vk::Queue>>, + + swapchain: RefCell<Option<Swapchain>>, - // A swapchain is the generalized facility that is used to implement - // double buffering, triple buffering, rendering passes that feed into each - // other, and other things of that nature. It's a first-class thing but for - // now, we use at most one of it. We also support running without one. - swapchain: OnceCell<vk::SwapchainKHR>, - swapchain_images: OnceCell<Vec<vk::Image>>, - swapchain_image_views: OnceCell<Vec<vk::ImageView>>, - format: OnceCell<vk::Format>, - extent: OnceCell<vk::Extent2D>, + render_pass: RefCell<Option<vk::RenderPass>>, - render_pass: OnceCell<vk::RenderPass>, + pipeline: RefCell<Option<vk::Pipeline>>, + pipeline_layout: RefCell<Option<vk::PipelineLayout>>, - pipeline: OnceCell<vk::Pipeline>, - pipeline_layout: OnceCell<vk::PipelineLayout>, + framebuffers: RefCell<Option<Vec<vk::Framebuffer>>>, - framebuffers: OnceCell<Vec<vk::Framebuffer>>, + command_pool: RefCell<Option<vk::CommandPool>>, + command_buffers: RefCell<Option<Vec<vk::CommandBuffer>>>, - command_pool: OnceCell<vk::CommandPool>, - command_buffers: OnceCell<Vec<vk::CommandBuffer>>, + concurrency: RefCell<Option<Concurrency>>, - image_available_semaphores: OnceCell<Vec<vk::Semaphore>>, - rendering_finished_semaphores: OnceCell<Vec<vk::Semaphore>>, + frame_index: usize, +} + +struct InstanceCreation { + instance: Instance, + debug_messager: Option<vk::DebugUtilsMessengerEXT>, + enable_portability: bool, + enable_validation: bool, +} + +struct DeviceCreation { + physical_device: vk::PhysicalDevice, + device: Device, + indices: QueueFamilyIndices, + graphics_queue: vk::Queue, + presentation_queue: vk::Queue, + enable_swapchain: bool, +} + +// A swapchain is the generalized facility that is used to implement +// double buffering, triple buffering, rendering passes that feed into each +// other, and other things of that nature. It's a first-class thing but for +// now, we use at most one of it. We also support running without one. +#[derive(Debug)] +struct Swapchain { + swapchain: vk::SwapchainKHR, + images: Vec<vk::Image>, + image_views: Vec<vk::ImageView>, + format: vk::Format, + extent: vk::Extent2D, +} + +#[derive(Debug)] +struct Concurrency { + image_available_semaphores: Vec<vk::Semaphore>, + rendering_finished_semaphores: Vec<vk::Semaphore>, // Okay, the lifetime management on the fences is really subtle. There is // one fence for each frame, and frame_fences holds the authoritative @@ -163,99 +187,104 @@ struct Surreality { // of the frame fence. This happens during rendering of the frame, so the // frame fence is in the "signaled" state. It will be reset right before // submitting the queue, then signaled again when the submission completes. - frame_fences: OnceCell<Vec<vk::Fence>>, - image_fences: Vec<vk::Fence>, - - frame_index: usize, + frame_fences: Vec<vk::Fence>, + image_fences: RefCell<Vec<vk::Fence>>, } impl Surreality { fn new() -> Self { Surreality { - window: OnceCell::new(), - enable_validation: OnceCell::new(), - enable_portability: OnceCell::new(), - enable_swapchain: OnceCell::new(), - entry: OnceCell::new(), - instance: OnceCell::new(), - debug_messager: OnceCell::new(), - surface: OnceCell::new(), - physical_device: OnceCell::new(), - device: OnceCell::new(), - graphics_queue: OnceCell::new(), - presentation_queue: OnceCell::new(), - swapchain: OnceCell::new(), - swapchain_images: OnceCell::new(), - swapchain_image_views: OnceCell::new(), - format: OnceCell::new(), - extent: OnceCell::new(), - render_pass: OnceCell::new(), - pipeline: OnceCell::new(), - pipeline_layout: OnceCell::new(), - framebuffers: OnceCell::new(), - command_pool: OnceCell::new(), - command_buffers: OnceCell::new(), - image_available_semaphores: OnceCell::new(), - rendering_finished_semaphores: OnceCell::new(), - frame_fences: OnceCell::new(), - image_fences: Vec::new(), + window: RefCell::new(None), + entry: RefCell::new(None), + instance: RefCell::new(None), + debug_messager: RefCell::new(None), + surface: RefCell::new(None), + device: RefCell::new(None), + graphics_queue: RefCell::new(None), + presentation_queue: RefCell::new(None), + swapchain: RefCell::new(None), + render_pass: RefCell::new(None), + pipeline: RefCell::new(None), + pipeline_layout: RefCell::new(None), + framebuffers: RefCell::new(None), + command_pool: RefCell::new(None), + command_buffers: RefCell::new(None), + concurrency: RefCell::new(None), frame_index: 0, } } fn init(&mut self, event_loop: &ActiveEventLoop) -> Result<()> { - // TODO refactor all these to not be methods, for better isolation + let window = Self::init_window(event_loop)?; + let entry = Self::init_vulkan_entry()?; - if self.window.get().is_none() { - self.init_window(event_loop)?; - } + let InstanceCreation { + instance, + debug_messager, - if self.entry.get().is_none() { - self.init_vulkan_entry()?; - } - - if self.instance.get().is_none() { - self.init_vulkan_instance()?; - } - - if self.surface.get().is_none() { - self.init_vulkan_surface()?; - } - - if self.device.get().is_none() { - self.init_vulkan_device()?; - } - - if self.swapchain.get().is_none() - && *self.enable_swapchain.get().unwrap() - { - self.init_vulkan_swapchain()?; - } - - if self.render_pass.get().is_none() { - self.init_render_pass()?; - } - - if self.pipeline.get().is_none() { - self.init_pipeline()?; - } - - if self.framebuffers.get().is_none() { - self.init_framebuffers()?; - } - - if self.command_buffers.get().is_none() { - self.init_commands()?; + // There are a few Vulkan features (in the informal sense of + // "feature") that we want to be able to run both with and without. + // Here, we have booleans describing which we're doing. + // + // These are only used to communicate between initialization phases; + // we don't keep them around after that. + enable_portability, + enable_validation + } = Self::init_vulkan_instance(&window, &entry)?; + + let surface = Self::init_surface(&window, &instance)?; + + let DeviceCreation { + physical_device, device, indices, graphics_queue, presentation_queue, + enable_swapchain + } = Self::init_vulkan_device(&instance, &surface, + enable_validation, enable_portability)?; + + if enable_swapchain { + let swapchain = Self::init_swapchain( + &window, &instance, &surface, &physical_device, &device, + &indices)?; + + let render_pass = Surreality::init_render_pass(&device, + &swapchain.format)?; + + let (pipeline_layout, pipeline) + = Self::init_pipeline(&device, &swapchain.extent, + &render_pass)?; + + let framebuffers = Self::init_framebuffers( + &device, &swapchain.extent, &swapchain.image_views, + &render_pass)?; + + let (command_pool, command_buffers) + = Self::init_commands(&device, &swapchain.extent, &framebuffers, + &render_pass, &pipeline, &indices)?; + + let concurrency = Self::init_concurrency(&device, &swapchain.images)?; + + *self.swapchain.get_mut() = Some(swapchain); + *self.render_pass.get_mut() = Some(render_pass); + *self.pipeline.get_mut() = Some(pipeline); + *self.pipeline_layout.get_mut() = Some(pipeline_layout); + *self.framebuffers.get_mut() = Some(framebuffers); + *self.command_pool.borrow_mut() = Some(command_pool); + *self.command_buffers.borrow_mut() = Some(command_buffers); + *self.concurrency.get_mut() = Some(concurrency); } - if self.image_available_semaphores.get().is_none() { - self.init_concurrency()?; - } + *self.window.get_mut() = Some(window); + *self.entry.get_mut() = Some(entry); + *self.instance.get_mut() = Some(instance); + *self.debug_messager.get_mut() = debug_messager; + *self.surface.get_mut() = Some(surface); + *self.device.get_mut() = Some(device); + *self.graphics_queue.get_mut() = Some(graphics_queue); + *self.presentation_queue.get_mut() = Some(presentation_queue); Ok(()) } - fn init_window(&mut self, event_loop: &ActiveEventLoop) -> Result<()> { + fn init_window(event_loop: &ActiveEventLoop) -> Result<Window> { // Notice that we do this before having a Vulkan instance. The window is // actually a parameter needed to create the instance; see // init_vulkan_instance(), below. @@ -263,15 +292,13 @@ impl Surreality { .with_title("Love, Curiosity, Justice") .with_inner_size(LogicalSize::new(1024, 768)); - let window: Window = event_loop.create_window(window_attributes)?; - - self.window.set(window).unwrap(); - - Ok(()) + Ok(event_loop.create_window(window_attributes)?) } + // TODO this is another candidate for rolling into its parent + // (in addition to init_surface, which is below. what is time) #[allow(unsafe_code)] - fn init_vulkan_entry(&mut self) -> Result<()> { + fn init_vulkan_entry() -> Result<Entry> { // Okay, so, a Vulkan "entry" is a small set of functions which are used // to dynamically load all the rest of Vulkan. It's our responsibility to // know how to load the entry, then it will take care of the rest. At @@ -284,15 +311,13 @@ impl Surreality { let loader = unsafe { LibloadingLoader::new(LIBRARY) }?; let entry = unsafe { Entry::new(loader) }?; - self.entry.set(entry).unwrap(); - - Ok(()) + Ok(entry) } #[allow(unsafe_code)] - fn init_vulkan_instance(&mut self) -> Result<()> { - let entry = self.entry.get().unwrap(); - + fn init_vulkan_instance(window: &Window, entry: &Entry) + -> Result<InstanceCreation> + { let enable_validation = cfg!(feature = "vulkan-validation") || cfg!(debug_assertions); @@ -325,14 +350,16 @@ impl Surreality { // This is possible because of an integration between Vulkanalia and // winit, which is enabled by Vulkanalia's "window" feature. for extension in vulkanalia::window::get_required_instance_extensions( - self.window.get().unwrap()) + window) { extensions.push(extension.as_ptr()); } // Deal with Vulkan's thing about opting in to non-conforming // implementations. - if entry.version()? >= VULKAN_FIRST_PORTABILITY_VERSION { + let enable_portability = if entry.version()? + >= VULKAN_FIRST_PORTABILITY_VERSION + { if cfg!(target_os = "macos") { // Vulkan on the Mac is not fully conforming. extensions.push( @@ -341,31 +368,31 @@ impl Surreality { vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name.as_ptr()); flags.insert(vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR); - self.enable_portability.set(true).unwrap(); + true } else { - self.enable_portability.set(false).unwrap(); + false } } else { - self.enable_portability.set(false).unwrap(); - } + false + }; // Request the LunarG validation layer, when appropriate. let validation_layer_name = vk::ExtensionName::from_bytes( b"VK_LAYER_KHRONOS_validation"); - if enable_validation { + let enable_validation = if enable_validation { if available_layers.contains(&validation_layer_name) { layers.push(validation_layer_name.as_ptr()); - self.enable_validation.set(enable_validation).unwrap(); + true } else { eprintln!("Vulkan validation requested at build time, \ but no validation layer available."); - self.enable_validation.set(false).unwrap(); + false } } else { - self.enable_validation.set(false).unwrap(); - } + false + }; // Request the debug extension. This is the first of three bits of code // that deal with this, and has the resonsibility of making sure the @@ -428,27 +455,28 @@ impl Surreality { // Configure the debug extension. This is the last of three bits of code // that deal with this, and has the responsibility of asking the instance, // which now exists, to create the debug messager. - if let Some(debug_info) = debug_info - && self.debug_messager.get().is_none() - { + let debug_messager = if let Some(debug_info) = debug_info { #[allow(unsafe_code)] - let debug_messager = unsafe { + Some(unsafe { instance.create_debug_utils_messenger_ext(&debug_info, None) - }?; - - self.debug_messager.set(debug_messager).unwrap(); - } - - self.instance.set(instance).unwrap(); + }?) + } else { + None + }; - Ok(()) + Ok(InstanceCreation { + instance, + debug_messager, + enable_portability, + enable_validation, + }) } + // TODO this is so short that it can likely be eliminated #[allow(unsafe_code)] - fn init_vulkan_surface(&mut self) -> Result<()> { - let window = self.window.get().unwrap(); - let instance = self.instance.get().unwrap(); - + fn init_surface(window: &Window, instance: &Instance) + -> Result<vk::SurfaceKHR> + { // Conveniently, Vulkanalia's "window" feature allows it to get the // platform-specific stuff directly out of winit for us. This wrapper does // not correspond 1:1 to a Vulkan function; rather, it picks the Vulkan @@ -461,561 +489,517 @@ impl Surreality { vulkanalia::window::create_surface(&instance, &window, &window) }?; - self.surface.set(surface).unwrap(); - - Ok(()) + Ok(surface) } #[allow(unsafe_code)] - fn init_vulkan_device(&mut self) -> Result<()> { - if self.physical_device.get().is_none() { - let physical_device = self.pick_vulkan_device()?; - - self.physical_device.set(physical_device).unwrap(); + fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, + enable_validation: bool, enable_portability: bool) + -> Result<DeviceCreation> + { + let physical_device = Self::pick_vulkan_device(instance, surface)?; + + // We already did the check in score_vulkan_device(), so if it fails + // this second time, that's our own bug and we don't need to explain it + // to our users. + // TODO looks sus though + let indices = Self::find_device_queue_family_indices( + instance, surface, &physical_device)?.unwrap(); + + // We enumerate the device extensions here so they can inform + // configuration. We already did this in score_vulkan_device(), but here + // it is again. + let mut available_extensions = HashSet::new(); + for extension in unsafe { + instance.enumerate_device_extension_properties(physical_device, None) + }? { + available_extensions.insert(extension.extension_name); } + let available_extensions = available_extensions; - if self.device.get().is_none() { - // Usually we're content to borrow these, but here, that would give us - // ownership problems. In reality they are smart pointer objects that do - // not directly contain their substantive pieces, so we can freely clone - // them, and in this case it's convenient to do that. - let instance = self.instance.get().unwrap().clone(); - let physical_device = self.physical_device.get().unwrap().clone(); - - // We already did the check in score_vulkan_device(), so if it fails - // this second time, that's our own bug and we don't need to explain it - // to our users. - let indices = self.find_device_queue_family_indices(&physical_device)? - .unwrap(); - - // We enumerate the device extensions here so they can inform - // configuration. We already did this in score_vulkan_device(), but here - // it is again. - let mut available_extensions = HashSet::new(); - for extension in unsafe { - instance.enumerate_device_extension_properties(physical_device, None) - }? { - available_extensions.insert(extension.extension_name); - } - let available_extensions = available_extensions; - - // Old versions of Vulkan want layers to be enabled at the device - // level as well. Newer ones will ignore this and just use the instance - // layers. - let features = vk::PhysicalDeviceFeatures::builder(); - let mut extensions = Vec::new(); - let mut layers = Vec::new(); - - let validation_layer_name = vk::ExtensionName::from_bytes( - b"VK_LAYER_KHRONOS_validation"); - if *self.enable_validation.get().unwrap() { - // It's not concerning if this isn't supported, because device - // layers are ignored on recent versions, they're purely historical. - if available_extensions.contains(&validation_layer_name) { - layers.push(validation_layer_name.as_ptr()); - } - } + // Old versions of Vulkan want layers to be enabled at the device + // level as well. Newer ones will ignore this and just use the instance + // layers. + let features = vk::PhysicalDeviceFeatures::builder(); + let mut extensions = Vec::new(); + let mut layers = Vec::new(); - let portability_extension_name = vk::ExtensionName::from_bytes( - b"VK_KHR_portability_subset"); - if *self.enable_portability.get().unwrap() { - // This is untested, since the only scenario where it would come up - // is on a Mac, which we don't actually support. Sorry, and good luck. - if available_extensions.contains(&portability_extension_name) { - extensions.push(portability_extension_name.as_ptr()); - } + let validation_layer_name = vk::ExtensionName::from_bytes( + b"VK_LAYER_KHRONOS_validation"); + if enable_validation { + // It's not concerning if this isn't supported, because device + // layers are ignored on recent versions, they're purely historical. + if available_extensions.contains(&validation_layer_name) { + layers.push(validation_layer_name.as_ptr()); } + } - let swapchain_extension_name = vk::KHR_SWAPCHAIN_EXTENSION.name; - if available_extensions.contains(&swapchain_extension_name) { - // It's important that we not call the swapchain extension - // functions until we've verified the extension is supported. To - // emphasize that, we do it on a separate line. - // - // We've done this check once already, in scoring, and now here - // we are discarding its results a second time. We'll do it for the - // third and last time in swapchain creation. - if let Acceptable::Accepted(_) - = self.find_device_swapchain_features(&physical_device)? - { - extensions.push(swapchain_extension_name.as_ptr()); - - self.enable_swapchain.set(true).unwrap(); - } else { - self.enable_swapchain.set(false).unwrap(); - } - } else { - self.enable_swapchain.set(false).unwrap(); + let portability_extension_name = vk::ExtensionName::from_bytes( + b"VK_KHR_portability_subset"); + if enable_portability { + // This is untested, since the only scenario where it would come up + // is on a Mac, which we don't actually support. Sorry, and good luck. + if available_extensions.contains(&portability_extension_name) { + extensions.push(portability_extension_name.as_ptr()); } + } + + let swapchain_extension_name = vk::KHR_SWAPCHAIN_EXTENSION.name; + let enable_swapchain = if available_extensions.contains( + &swapchain_extension_name) + { + // It's important that we not call the swapchain extension + // functions until we've verified the extension is supported. To + // emphasize that, we do it on a separate line. + // + // We've done this check once already, in scoring, and now here + // we are discarding its results a second time. We'll do it for the + // third and last time in swapchain creation. + if let Acceptable::Accepted(_) = Self::find_device_swapchain_features( + &instance, &surface, &physical_device)? + { + extensions.push(swapchain_extension_name.as_ptr()); - // We have one or more queue family indices; we don't know a priori - // how many, because it's possible some of them are the same. We only - // want to create one queue per distinct family, so we find the unique - // indices... - let mut unique_queue_family_indices = BTreeSet::new(); - unique_queue_family_indices.insert(indices.graphics); - unique_queue_family_indices.insert(indices.presentation); - - // ... then add a queue create info struct for each. - let mut queues = Vec::new(); - for index in unique_queue_family_indices { - // Passing the priorities vector also implicitly sets the count of - // how many queues we are creating within the family. This nicety is - // one of the fun things Vulkanalia's builders do for us. - queues.push(vk::DeviceQueueCreateInfo::builder() - .queue_family_index(index) - .queue_priorities(&[1.0])); + true + } else { + false } + } else { + false + }; - let device_info = vk::DeviceCreateInfo::builder() - .queue_create_infos(&queues) - .enabled_layer_names(&layers) - .enabled_extension_names(&extensions) - .enabled_features(&features); + // We have one or more queue family indices; we don't know a priori + // how many, because it's possible some of them are the same. We only + // want to create one queue per distinct family, so we find the unique + // indices... + let mut unique_queue_family_indices = BTreeSet::new(); + unique_queue_family_indices.insert(indices.graphics); + unique_queue_family_indices.insert(indices.presentation); + + // ... then add a queue create info struct for each. + let mut queues = Vec::new(); + for index in unique_queue_family_indices { + // Passing the priorities vector also implicitly sets the count of + // how many queues we are creating within the family. This nicety is + // one of the fun things Vulkanalia's builders do for us. + queues.push(vk::DeviceQueueCreateInfo::builder() + .queue_family_index(index) + .queue_priorities(&[1.0])); + } - let device = unsafe { - instance.create_device(*self.physical_device.get().unwrap(), - &device_info, None) - }?; + let device_info = vk::DeviceCreateInfo::builder() + .queue_create_infos(&queues) + .enabled_layer_names(&layers) + .enabled_extension_names(&extensions) + .enabled_features(&features); - // So, this is a little confusing. Queues are found in queue families. - // The family has an index within the device, and the queue has an index - // within the family. We computed the family index above, and when we - // created the device we told it to create just a single queue in that - // family. Now we pass both indices to find the actual queue object. - let graphics_queue = unsafe { - device.get_device_queue(indices.graphics, 0) - }; + let device = unsafe { + instance.create_device(physical_device, &device_info, None) + }?; - let presentation_queue = unsafe { - device.get_device_queue(indices.presentation, 0) - }; + // So, this is a little confusing. Queues are found in queue families. + // The family has an index within the device, and the queue has an index + // within the family. We computed the family index above, and when we + // created the device we told it to create just a single queue in that + // family. Now we pass both indices to find the actual queue object. + let graphics_queue = unsafe { + device.get_device_queue(indices.graphics, 0) + }; - self.device.set(device).unwrap(); - self.graphics_queue.set(graphics_queue).unwrap(); - self.presentation_queue.set(presentation_queue).unwrap(); - } + let presentation_queue = unsafe { + device.get_device_queue(indices.presentation, 0) + }; - Ok(()) + Ok(DeviceCreation { + physical_device, + device, + indices, + graphics_queue, + presentation_queue, + enable_swapchain + }) } #[allow(unsafe_code)] - fn init_vulkan_swapchain(&mut self) -> Result<()> { - // Here, too, we need to clone the smart pointer to simplify ownership. - let physical_device = self.physical_device.get().unwrap().clone(); + fn init_swapchain(window: &Window, instance: &Instance, + surface: &vk::SurfaceKHR, + physical_device: &vk::PhysicalDevice, device: &Device, + indices: &QueueFamilyIndices) + -> Result<Swapchain> + { + let features = Self::find_device_swapchain_features( + instance, surface, physical_device)?.require()?; - if let Acceptable::Accepted(features) - = self.find_device_swapchain_features(&physical_device)? - { - let physical_device = self.physical_device.get().unwrap().clone(); - let device = self.device.get().unwrap().clone(); - let surface = self.surface.get().unwrap().clone(); + let format = Self::pick_surface_format(&features.formats)?; - let format = self.pick_surface_format(&features.formats)?; + let presentation_mode + = Self::pick_presentation_mode(&features.presentation_modes)?; + let extent = Self::pick_image_extent(window, features.capabilities)?; - let presentation_mode - = self.pick_presentation_mode(&features.presentation_modes)?; - let extent = self.pick_image_extent(features.capabilities)?; + let mut image_count = features.capabilities.min_image_count + 1; + if features.capabilities.max_image_count != 0 { + image_count + = image_count.clamp(0, features.capabilities.max_image_count); + } - let mut image_count = features.capabilities.min_image_count + 1; - if features.capabilities.max_image_count != 0 { - image_count - = image_count.clamp(0, features.capabilities.max_image_count); - } + let mut unique_queue_family_indices = BTreeSet::new(); + unique_queue_family_indices.insert(indices.graphics); + unique_queue_family_indices.insert(indices.presentation); - // We call this yet again. It needs to return the same thing here - // that it did before; fortunately it's simple enough that that's a safe - // assumption. - let indices = self.find_device_queue_family_indices(&physical_device)? - .unwrap(); - - let mut unique_queue_family_indices = BTreeSet::new(); - unique_queue_family_indices.insert(indices.graphics); - unique_queue_family_indices.insert(indices.presentation); - - // If there's only one queue, we use exclusive sharing mode, which - // will allow things to work without locks. Otherwise we use concurrent - // mode. - let (ordered_indices, sharing_mode) - = if unique_queue_family_indices.len() < 2 - { - (vec![indices.graphics], vk::SharingMode::EXCLUSIVE) - } else { - (vec![indices.graphics, indices.presentation], - vk::SharingMode::CONCURRENT) - }; + // If there's only one queue, we use exclusive sharing mode, which + // will allow things to work without locks. Otherwise we use concurrent + // mode. + let (ordered_indices, sharing_mode) + = if unique_queue_family_indices.len() < 2 + { + (vec![indices.graphics], vk::SharingMode::EXCLUSIVE) + } else { + (vec![indices.graphics, indices.presentation], + vk::SharingMode::CONCURRENT) + }; - let swapchain_info = vk::SwapchainCreateInfoKHR::builder() - .surface(surface) - .min_image_count(image_count) - .image_format(format.format) - .image_color_space(format.color_space) - .image_extent(extent) - .image_array_layers(1) - .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) - .image_sharing_mode(sharing_mode) - .queue_family_indices(&ordered_indices) - .pre_transform(features.capabilities.current_transform) - .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) - .present_mode(presentation_mode) - .clipped(true) - .old_swapchain(vk::SwapchainKHR::null()); - - let swapchain = unsafe { - device.create_swapchain_khr(&swapchain_info, None) - }?; + let swapchain_info = vk::SwapchainCreateInfoKHR::builder() + .surface(*surface) + .min_image_count(image_count) + .image_format(format.format) + .image_color_space(format.color_space) + .image_extent(extent) + .image_array_layers(1) + .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) + .image_sharing_mode(sharing_mode) + .queue_family_indices(&ordered_indices) + .pre_transform(features.capabilities.current_transform) + .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) + .present_mode(presentation_mode) + .clipped(true) + .old_swapchain(vk::SwapchainKHR::null()); + + let swapchain = unsafe { + device.create_swapchain_khr(&swapchain_info, None) + }?; - let images = unsafe { - device.get_swapchain_images_khr(swapchain) - }?; + let images = unsafe { + device.get_swapchain_images_khr(swapchain) + }?; - let mut image_views = Vec::new(); - for image in &images { - let components = vk::ComponentMapping::builder() - .r(vk::ComponentSwizzle::IDENTITY) - .g(vk::ComponentSwizzle::IDENTITY) - .b(vk::ComponentSwizzle::IDENTITY) - .a(vk::ComponentSwizzle::IDENTITY); - - let subresource_range = vk::ImageSubresourceRange::builder() - .aspect_mask(vk::ImageAspectFlags::COLOR) - .base_mip_level(0) - .level_count(1) - .base_array_layer(0) - .layer_count(1); - - let view_info = vk::ImageViewCreateInfo::builder() - .image(*image) - .view_type(vk::ImageViewType::_2D) - .format(format.format) - .components(components) - .subresource_range(subresource_range); - - let view = unsafe { - device.create_image_view(&view_info, None) - }?; - - image_views.push(view); - } + let mut image_views = Vec::new(); + for image in &images { + let components = vk::ComponentMapping::builder() + .r(vk::ComponentSwizzle::IDENTITY) + .g(vk::ComponentSwizzle::IDENTITY) + .b(vk::ComponentSwizzle::IDENTITY) + .a(vk::ComponentSwizzle::IDENTITY); + + let subresource_range = vk::ImageSubresourceRange::builder() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .base_mip_level(0) + .level_count(1) + .base_array_layer(0) + .layer_count(1); + + let view_info = vk::ImageViewCreateInfo::builder() + .image(*image) + .view_type(vk::ImageViewType::_2D) + .format(format.format) + .components(components) + .subresource_range(subresource_range); + + let view = unsafe { + device.create_image_view(&view_info, None) + }?; - self.swapchain.set(swapchain).unwrap(); - self.swapchain_images.set(images).unwrap(); - self.swapchain_image_views.set(image_views).unwrap(); - self.format.set(format.format).unwrap(); - self.extent.set(extent).unwrap(); + image_views.push(view); } - Ok(()) + Ok(Swapchain { + swapchain, images, image_views, + format: format.format, + extent + }) } #[allow(unsafe_code)] - fn init_render_pass(&mut self) -> Result<()> { - if self.render_pass.get().is_none() { - let device = self.device.get().unwrap(); - let format = self.format.get().unwrap(); - - let color_attachment - = vk::AttachmentDescription::builder() - .format(*format) - .samples(vk::SampleCountFlags::_1) - .load_op(vk::AttachmentLoadOp::CLEAR) - .store_op(vk::AttachmentStoreOp::STORE) - .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) - .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) - .initial_layout(vk::ImageLayout::UNDEFINED) - .final_layout(vk::ImageLayout::PRESENT_SRC_KHR); - - let color_attachment_reference - = vk::AttachmentReference::builder() - .attachment(0) - .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); - - let subpass_attachments = [color_attachment_reference]; - let subpass = vk::SubpassDescription::builder() - .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) - .color_attachments(&subpass_attachments); - - let dependency - = vk::SubpassDependency::builder() - .src_subpass(vk::SUBPASS_EXTERNAL) - .src_stage_mask( - vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) - .src_access_mask(vk::AccessFlags::empty()) - .dst_subpass(0) - .dst_stage_mask( - vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) - .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE); - - let render_attachments = [color_attachment]; - let subpasses = [subpass]; - let dependencies = [dependency]; - let render_pass_info = vk::RenderPassCreateInfo::builder() - .attachments(&render_attachments) - .subpasses(&subpasses) - .dependencies(&dependencies); - - let render_pass = unsafe { - device.create_render_pass(&render_pass_info, None) - }?; - - self.render_pass.set(render_pass).unwrap(); - } + fn init_render_pass(device: &Device, format: &vk::Format) + -> Result<vk::RenderPass> + { + let color_attachment + = vk::AttachmentDescription::builder() + .format(*format) + .samples(vk::SampleCountFlags::_1) + .load_op(vk::AttachmentLoadOp::CLEAR) + .store_op(vk::AttachmentStoreOp::STORE) + .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) + .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) + .initial_layout(vk::ImageLayout::UNDEFINED) + .final_layout(vk::ImageLayout::PRESENT_SRC_KHR); + + let color_attachment_reference + = vk::AttachmentReference::builder() + .attachment(0) + .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); + + let subpass_attachments = [color_attachment_reference]; + let subpass = vk::SubpassDescription::builder() + .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) + .color_attachments(&subpass_attachments); + + let dependency + = vk::SubpassDependency::builder() + .src_subpass(vk::SUBPASS_EXTERNAL) + .src_stage_mask( + vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) + .src_access_mask(vk::AccessFlags::empty()) + .dst_subpass(0) + .dst_stage_mask( + vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) + .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE); + + let render_attachments = [color_attachment]; + let subpasses = [subpass]; + let dependencies = [dependency]; + let render_pass_info = vk::RenderPassCreateInfo::builder() + .attachments(&render_attachments) + .subpasses(&subpasses) + .dependencies(&dependencies); + + let render_pass = unsafe { + device.create_render_pass(&render_pass_info, None) + }?; - Ok(()) + Ok(render_pass) } #[allow(unsafe_code)] - fn init_pipeline(&mut self) -> Result<()> { - if self.pipeline.get().is_none() { - let device = self.device.get().unwrap().clone(); - let extent = self.extent.get().unwrap().clone(); - let render_pass = self.render_pass.get().unwrap().clone(); - - let vertex_binary = include_bytes!( - concat!(env!("OUT_DIR"), "/shader.vert.spv")); - let fragment_binary = include_bytes!( - concat!(env!("OUT_DIR"), "/shader.frag.spv")); - - let vertex_module = self.load_spirv_shader_module(vertex_binary)?; - let fragment_module = self.load_spirv_shader_module(fragment_binary)?; - - let vertex_stage_info = vk::PipelineShaderStageCreateInfo::builder() - .stage(vk::ShaderStageFlags::VERTEX) - .module(vertex_module) + fn init_pipeline(device: &Device, extent: &vk::Extent2D, + render_pass: &vk::RenderPass) + -> Result<(vk::PipelineLayout, vk::Pipeline)> + { + let vertex_binary = include_bytes!( + concat!(env!("OUT_DIR"), "/shader.vert.spv")); + let fragment_binary = include_bytes!( + concat!(env!("OUT_DIR"), "/shader.frag.spv")); + + let vertex_module + = Self::load_spirv_shader_module(device, vertex_binary)?; + let fragment_module + = Self::load_spirv_shader_module(device, fragment_binary)?; + + let vertex_stage_info = vk::PipelineShaderStageCreateInfo::builder() + .stage(vk::ShaderStageFlags::VERTEX) + .module(vertex_module) + .name(b"main\0"); + + let fragment_stage_info = vk::PipelineShaderStageCreateInfo::builder() + .stage(vk::ShaderStageFlags::FRAGMENT) + .module(fragment_module) .name(b"main\0"); - let fragment_stage_info = vk::PipelineShaderStageCreateInfo::builder() - .stage(vk::ShaderStageFlags::FRAGMENT) - .module(fragment_module) - .name(b"main\0"); - - let vertex_input_state_info - = vk::PipelineVertexInputStateCreateInfo::builder(); - - let input_assembly_state_info - = vk::PipelineInputAssemblyStateCreateInfo::builder() - .topology(vk::PrimitiveTopology::TRIANGLE_LIST) - .primitive_restart_enable(false); - - let viewport = vk::Viewport::builder() - .x(0.0) - .y(0.0) - .width(extent.width as f32) - .height(extent.height as f32) - .min_depth(0.0) - .max_depth(1.0); - let viewports = [viewport]; - - let scissor = vk::Rect2D::builder() - .offset(vk::Offset2D { x: 0, y: 0 }) - .extent(extent); - let scissor_list = [scissor]; - - let viewport_state_info = vk::PipelineViewportStateCreateInfo::builder() - .viewports(&viewports) - .scissors(&scissor_list); - - let rasterizer_state_info - = vk::PipelineRasterizationStateCreateInfo::builder() - .depth_clamp_enable(false) - .rasterizer_discard_enable(false) - .polygon_mode(vk::PolygonMode::FILL) - .line_width(1.0) - .cull_mode(vk::CullModeFlags::BACK) - .front_face(vk::FrontFace::CLOCKWISE) - .depth_bias_enable(false); - - let multisample_state_info - = vk::PipelineMultisampleStateCreateInfo::builder() - .sample_shading_enable(false) - .rasterization_samples(vk::SampleCountFlags::_1); - - let blend_attachment_info - = vk::PipelineColorBlendAttachmentState::builder() - .color_write_mask(vk::ColorComponentFlags::all()) - .blend_enable(false) - .src_color_blend_factor(vk::BlendFactor::ONE) - .dst_color_blend_factor(vk::BlendFactor::ZERO) - .color_blend_op(vk::BlendOp::ADD) - .src_alpha_blend_factor(vk::BlendFactor::ONE) - .dst_alpha_blend_factor(vk::BlendFactor::ZERO) - .alpha_blend_op(vk::BlendOp::ADD); - let blend_attachments = [blend_attachment_info]; - - let blend_info = vk::PipelineColorBlendStateCreateInfo::builder() - .logic_op_enable(false) - .logic_op(vk::LogicOp::COPY) - .attachments(&blend_attachments) - .blend_constants([0.0, 0.0, 0.0, 0.0]); - - let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder(); - - let pipeline_layout = unsafe { - device.create_pipeline_layout(&pipeline_layout_info, None) - }?; - - let stages = [vertex_stage_info, fragment_stage_info]; - let pipeline_info - = vk::GraphicsPipelineCreateInfo::builder() - .stages(&stages) - .vertex_input_state(&vertex_input_state_info) - .input_assembly_state(&input_assembly_state_info) - .viewport_state(&viewport_state_info) - .rasterization_state(&rasterizer_state_info) - .multisample_state(&multisample_state_info) - .color_blend_state(&blend_info) - .layout(pipeline_layout) - .render_pass(render_pass) - .subpass(0); - - let pipeline = unsafe { - device.create_graphics_pipelines(vk::PipelineCache::null(), - &[pipeline_info], None) - }?.0[0]; - - self.pipeline_layout.set(pipeline_layout).unwrap(); - self.pipeline.set(pipeline).unwrap(); + let vertex_input_state_info + = vk::PipelineVertexInputStateCreateInfo::builder(); + + let input_assembly_state_info + = vk::PipelineInputAssemblyStateCreateInfo::builder() + .topology(vk::PrimitiveTopology::TRIANGLE_LIST) + .primitive_restart_enable(false); + + let viewport = vk::Viewport::builder() + .x(0.0) + .y(0.0) + .width(extent.width as f32) + .height(extent.height as f32) + .min_depth(0.0) + .max_depth(1.0); + let viewports = [viewport]; + + let scissor = vk::Rect2D::builder() + .offset(vk::Offset2D { x: 0, y: 0 }) + .extent(*extent); + let scissor_list = [scissor]; + + let viewport_state_info = vk::PipelineViewportStateCreateInfo::builder() + .viewports(&viewports) + .scissors(&scissor_list); + + let rasterizer_state_info + = vk::PipelineRasterizationStateCreateInfo::builder() + .depth_clamp_enable(false) + .rasterizer_discard_enable(false) + .polygon_mode(vk::PolygonMode::FILL) + .line_width(1.0) + .cull_mode(vk::CullModeFlags::BACK) + .front_face(vk::FrontFace::CLOCKWISE) + .depth_bias_enable(false); + + let multisample_state_info + = vk::PipelineMultisampleStateCreateInfo::builder() + .sample_shading_enable(false) + .rasterization_samples(vk::SampleCountFlags::_1); + + let blend_attachment_info + = vk::PipelineColorBlendAttachmentState::builder() + .color_write_mask(vk::ColorComponentFlags::all()) + .blend_enable(false) + .src_color_blend_factor(vk::BlendFactor::ONE) + .dst_color_blend_factor(vk::BlendFactor::ZERO) + .color_blend_op(vk::BlendOp::ADD) + .src_alpha_blend_factor(vk::BlendFactor::ONE) + .dst_alpha_blend_factor(vk::BlendFactor::ZERO) + .alpha_blend_op(vk::BlendOp::ADD); + let blend_attachments = [blend_attachment_info]; + + let blend_info = vk::PipelineColorBlendStateCreateInfo::builder() + .logic_op_enable(false) + .logic_op(vk::LogicOp::COPY) + .attachments(&blend_attachments) + .blend_constants([0.0, 0.0, 0.0, 0.0]); + + let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder(); + + let pipeline_layout = unsafe { + device.create_pipeline_layout(&pipeline_layout_info, None) + }?; - unsafe { - device.destroy_shader_module(vertex_module, None); - device.destroy_shader_module(fragment_module, None); - }; - } + let stages = [vertex_stage_info, fragment_stage_info]; + let pipeline_info + = vk::GraphicsPipelineCreateInfo::builder() + .stages(&stages) + .vertex_input_state(&vertex_input_state_info) + .input_assembly_state(&input_assembly_state_info) + .viewport_state(&viewport_state_info) + .rasterization_state(&rasterizer_state_info) + .multisample_state(&multisample_state_info) + .color_blend_state(&blend_info) + .layout(pipeline_layout) + .render_pass(*render_pass) + .subpass(0); + + let pipeline = unsafe { + device.create_graphics_pipelines(vk::PipelineCache::null(), + &[pipeline_info], None) + }?.0[0]; + + unsafe { + device.destroy_shader_module(vertex_module, None); + device.destroy_shader_module(fragment_module, None); + }; - Ok(()) + Ok((pipeline_layout, pipeline)) } #[allow(unsafe_code)] - fn init_framebuffers(&mut self) -> Result<()> { - if self.framebuffers.get().is_none() { - let device = self.device.get().unwrap().clone(); - let extent = self.extent.get().unwrap().clone(); - let image_views = self.swapchain_image_views.get().unwrap(); - let render_pass = self.render_pass.get().unwrap(); - - let mut framebuffers = Vec::new(); - - for image_view in image_views { - let attachments = [*image_view]; - - let framebuffer_info = vk::FramebufferCreateInfo::builder() - .render_pass(*render_pass) - .attachments(&attachments) - .width(extent.width) - .height(extent.height) - .layers(1); - - let framebuffer = unsafe { - device.create_framebuffer(&framebuffer_info, None) - }?; - - framebuffers.push(framebuffer); - } + fn init_framebuffers(device: &Device, extent: &vk::Extent2D, + swapchain_image_views: &Vec<vk::ImageView>, + render_pass: &vk::RenderPass) + -> Result<Vec<vk::Framebuffer>> + { + let mut framebuffers = Vec::new(); - self.framebuffers.set(framebuffers).unwrap(); - } + for image_view in swapchain_image_views { + let attachments = [*image_view]; - Ok(()) - } + let framebuffer_info = vk::FramebufferCreateInfo::builder() + .render_pass(*render_pass) + .attachments(&attachments) + .width(extent.width) + .height(extent.height) + .layers(1); - #[allow(unsafe_code)] - fn init_commands(&mut self) -> Result<()> { - if self.command_pool.get().is_none() { - let physical_device = self.physical_device.get().unwrap().clone(); - let device = self.device.get().unwrap().clone(); + let framebuffer = unsafe { + device.create_framebuffer(&framebuffer_info, None) + }?; - // We call this one last time. It's kind of a problem. - let indices = self.find_device_queue_family_indices(&physical_device)? - .unwrap(); + framebuffers.push(framebuffer); + } - let command_pool_info = vk::CommandPoolCreateInfo::builder() - .flags(vk::CommandPoolCreateFlags::empty()) - .queue_family_index(indices.graphics); + Ok(framebuffers) + } - let command_pool = unsafe { - device.create_command_pool(&command_pool_info, None) - }?; + #[allow(unsafe_code)] + fn init_commands(device: &Device, + extent: &vk::Extent2D, + framebuffers: &Vec<vk::Framebuffer>, + render_pass: &vk::RenderPass, + pipeline: &vk::Pipeline, + indices: &QueueFamilyIndices) + -> Result<(vk::CommandPool, Vec<vk::CommandBuffer>)> + { + // We call this one last time. It's kind of a problem. - self.command_pool.set(command_pool).unwrap(); - } + let command_pool_info = vk::CommandPoolCreateInfo::builder() + .flags(vk::CommandPoolCreateFlags::empty()) + .queue_family_index(indices.graphics); - if self.command_buffers.get().is_none() { - let device = self.device.get().unwrap(); - let extent = self.extent.get().unwrap(); - let framebuffers = self.framebuffers.get().unwrap(); - let render_pass = self.render_pass.get().unwrap(); - let pipeline = self.pipeline.get().unwrap(); - let command_pool = self.command_pool.get().unwrap(); - - let command_buffer_allocation_info - = vk::CommandBufferAllocateInfo::builder() - .command_pool(*command_pool) - .level(vk::CommandBufferLevel::PRIMARY) - .command_buffer_count(framebuffers.len() as u32); - let command_buffers = unsafe { - device.allocate_command_buffers(&command_buffer_allocation_info) - }?; + let command_pool = unsafe { + device.create_command_pool(&command_pool_info, None) + }?; - for (index, framebuffer) in framebuffers.iter().enumerate() { - let command_buffer = command_buffers[index]; + let command_buffer_allocation_info + = vk::CommandBufferAllocateInfo::builder() + .command_pool(command_pool) + .level(vk::CommandBufferLevel::PRIMARY) + .command_buffer_count(framebuffers.len() as u32); + let command_buffers = unsafe { + device.allocate_command_buffers(&command_buffer_allocation_info) + }?; - let inheritance_info = vk::CommandBufferInheritanceInfo::builder(); + for (index, framebuffer) in framebuffers.iter().enumerate() { + let command_buffer = command_buffers[index]; - let command_buffer_begin_info - = vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::empty()) - .inheritance_info(&inheritance_info); + let inheritance_info = vk::CommandBufferInheritanceInfo::builder(); - unsafe { - device.begin_command_buffer(command_buffer, - &command_buffer_begin_info) - }?; + let command_buffer_begin_info + = vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::empty()) + .inheritance_info(&inheritance_info); - let render_area = vk::Rect2D::builder() - .offset(vk::Offset2D::default()) - .extent(*extent); + unsafe { + device.begin_command_buffer(command_buffer, + &command_buffer_begin_info) + }?; - let clear_value = vk::ClearValue { - color: vk::ClearColorValue { - float32: [0.0, 0.0, 0.0, 1.0] - } - }; - let clear_values = [clear_value]; + let render_area = vk::Rect2D::builder() + .offset(vk::Offset2D::default()) + .extent(*extent); - let begin_pass_info = vk::RenderPassBeginInfo::builder() - .render_pass(*render_pass) - .framebuffer(*framebuffer) - .render_area(render_area) - .clear_values(&clear_values); + let clear_value = vk::ClearValue { + color: vk::ClearColorValue { + float32: [0.0, 0.0, 0.0, 1.0] + } + }; + let clear_values = [clear_value]; - unsafe { - device.cmd_begin_render_pass(command_buffer, &begin_pass_info, - vk::SubpassContents::INLINE) - }; + let begin_pass_info = vk::RenderPassBeginInfo::builder() + .render_pass(*render_pass) + .framebuffer(*framebuffer) + .render_area(render_area) + .clear_values(&clear_values); - unsafe { - device.cmd_bind_pipeline(command_buffer, - vk::PipelineBindPoint::GRAPHICS, - *pipeline) - }; + unsafe { + device.cmd_begin_render_pass(command_buffer, &begin_pass_info, + vk::SubpassContents::INLINE) + }; - unsafe { device.cmd_draw(command_buffer, 3, 1, 0, 0) }; + unsafe { + device.cmd_bind_pipeline(command_buffer, + vk::PipelineBindPoint::GRAPHICS, + *pipeline) + }; - unsafe { device.cmd_end_render_pass(command_buffer) }; + unsafe { device.cmd_draw(command_buffer, 3, 1, 0, 0) }; - unsafe { device.end_command_buffer(command_buffer) }?; - } + unsafe { device.cmd_end_render_pass(command_buffer) }; - self.command_buffers.set(command_buffers).unwrap(); + unsafe { device.end_command_buffer(command_buffer) }?; } - Ok(()) + Ok((command_pool, command_buffers)) } #[allow(unsafe_code)] - fn init_concurrency(&mut self) -> Result<()> { - let device = self.device.get().unwrap(); - let swapchain_images = self.swapchain_images.get().unwrap(); + fn init_concurrency(device: &Device, + swapchain_images: &Vec<vk::Image>) + -> Result<Concurrency> + { let semaphore_info = vk::SemaphoreCreateInfo::builder(); let fence_info = vk::FenceCreateInfo::builder() .flags(vk::FenceCreateFlags::SIGNALED); @@ -1023,7 +1007,6 @@ impl Surreality { let mut image_available_semaphores = Vec::new(); let mut rendering_finished_semaphores = Vec::new(); let mut frame_fences = Vec::new(); - for _ in 0 .. N_SIMULTANEOUS_FRAMES { image_available_semaphores.push(unsafe { device.create_semaphore(&semaphore_info, None) @@ -1038,17 +1021,17 @@ impl Surreality { }?); } - self.image_available_semaphores - .set(image_available_semaphores).unwrap(); - self.rendering_finished_semaphores - .set(rendering_finished_semaphores).unwrap(); - self.frame_fences.set(frame_fences).unwrap(); - + let mut image_fences = Vec::new(); for _ in 0 .. swapchain_images.len() { - self.image_fences.push(vk::Fence::null()); + image_fences.push(vk::Fence::null()); } - Ok(()) + Ok(Concurrency { + image_available_semaphores, + rendering_finished_semaphores, + frame_fences, + image_fences: RefCell::new(image_fences), + }) } // To Vulkan, a "physical" device is the actual GPU, and a "logical" @@ -1056,15 +1039,15 @@ impl Surreality { // Before we can create a logical device, we must choose which physical // device to connect it to. #[allow(unsafe_code)] - fn pick_vulkan_device(&mut self) -> Result<vk::PhysicalDevice> { + fn pick_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR) + -> Result<vk::PhysicalDevice> + { let mut best_device = None; let mut best_score = None; let mut rejected = BTreeMap::new(); - for device in unsafe { - self.instance.get().unwrap().enumerate_physical_devices() - }? { - match self.score_vulkan_device(&device)? { + for device in unsafe { instance.enumerate_physical_devices() }? { + match Self::score_vulkan_device(instance, surface, &device)? { Acceptable::Accepted(new_score) => { if let Some(old_score) = best_score { if new_score > old_score { @@ -1078,8 +1061,7 @@ impl Surreality { } Acceptable::Rejected(reason) => { let properties = unsafe { - self.instance.get().unwrap() - .get_physical_device_properties(device) + instance.get_physical_device_properties(device) }; let name = properties.device_name.to_string_lossy().into_owned(); @@ -1114,7 +1096,8 @@ impl Surreality { // the outer Result is whether we successfully evaluated the device, and the // inner Acceptable is whether we approve of it. #[allow(unsafe_code)] - fn score_vulkan_device(&mut self, device: &vk::PhysicalDevice) + fn score_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, + physical_device: &vk::PhysicalDevice) -> Result<Acceptable<u64>> { // Not all devices support graphics, and not all devices support @@ -1122,17 +1105,16 @@ impl Surreality { // by looking up the indices of the queue families we would use, though // we ignore the actual values and recompute them later. if let Acceptable::Rejected(rationale) - = self.find_device_queue_family_indices(device)? + = Self::find_device_queue_family_indices( + instance, surface, physical_device)? { return Ok(Acceptable::Rejected(rationale)); } - let instance = self.instance.get().unwrap(); - // At this point we know the device meets our high-level requirements, // so it's just a question of scoring. let properties = unsafe { - instance.get_physical_device_properties(*device) + instance.get_physical_device_properties(*physical_device) }; let mut score = 0; @@ -1158,7 +1140,7 @@ impl Surreality { // supports, so we enumerate those. let mut available_extensions = HashSet::new(); for extension in unsafe { - instance.enumerate_device_extension_properties(*device, None) + instance.enumerate_device_extension_properties(*physical_device, None) }? { available_extensions.insert(extension.extension_name); } @@ -1171,8 +1153,8 @@ impl Surreality { // // With that said, however, it only counts if we're able to actually // use it on the surface we have. Let's find out... - if let Acceptable::Accepted(_) - = self.find_device_swapchain_features(device)? + if let Acceptable::Accepted(_) = Self::find_device_swapchain_features( + instance, surface, physical_device)? { // We don't count it for enough points to override a device type // bracket, but it's good for a lot within the bracket. @@ -1187,14 +1169,12 @@ impl Surreality { Ok(Acceptable::Accepted(score)) } - // TODO: save the result of this somewhere and only call it once #[allow(unsafe_code)] - fn find_device_queue_family_indices(&mut self, device: &vk::PhysicalDevice) + fn find_device_queue_family_indices(instance: &Instance, + surface: &vk::SurfaceKHR, + device: &vk::PhysicalDevice) -> Result<Acceptable<QueueFamilyIndices>> { - let instance = self.instance.get().unwrap(); - let surface = self.surface.get().unwrap(); - // We need a queue family that supports graphics drawing commands, and a // queue family that supports presentation commands. These may or may not // be the same family. @@ -1235,23 +1215,22 @@ impl Surreality { // We expect our caller to have already verified that the device supports // the swapchain extension. #[allow(unsafe_code)] - fn find_device_swapchain_features(&mut self, device: &vk::PhysicalDevice) + fn find_device_swapchain_features(instance: &Instance, + surface: &vk::SurfaceKHR, + physical_device: &vk::PhysicalDevice) -> Result<Acceptable<SwapchainFeatures>> { - let instance = self.instance.get().unwrap(); - let surface = self.surface.get().unwrap(); - let capabilities = unsafe { instance.get_physical_device_surface_capabilities_khr( - *device, *surface) + *physical_device, *surface) }?; let formats = unsafe { instance.get_physical_device_surface_formats_khr( - *device, *surface) + *physical_device, *surface) }?; let presentation_modes = unsafe { instance.get_physical_device_surface_present_modes_khr( - *device, *surface) + *physical_device, *surface) }?; if formats.is_empty() { @@ -1265,8 +1244,7 @@ impl Surreality { } } - fn pick_surface_format(&mut self, - available_formats: &Vec<vk::SurfaceFormatKHR>) + fn pick_surface_format(available_formats: &Vec<vk::SurfaceFormatKHR>) -> Result<vk::SurfaceFormatKHR> { for format in available_formats { @@ -1280,15 +1258,15 @@ impl Surreality { return Ok(available_formats[0].clone()); } - fn pick_presentation_mode(&mut self, - _available_modes: &Vec<vk::PresentModeKHR>) + fn pick_presentation_mode(_available_modes: &Vec<vk::PresentModeKHR>) -> Result<vk::PresentModeKHR> { // It's guaranteed to have this one. return Ok(vk::PresentModeKHR::FIFO); } - fn pick_image_extent(&mut self, capabilities: vk::SurfaceCapabilitiesKHR) + fn pick_image_extent(window: &Window, + capabilities: vk::SurfaceCapabilitiesKHR) -> Result<vk::Extent2D> { if capabilities.current_extent.width != u32::MAX @@ -1296,7 +1274,6 @@ impl Surreality { { Ok(capabilities.current_extent) } else { - let window = self.window.get().unwrap(); let window_size = window.inner_size(); let width = window_size.width @@ -1311,11 +1288,9 @@ impl Surreality { } #[allow(unsafe_code)] - fn load_spirv_shader_module(&mut self, binary: &[u8]) + fn load_spirv_shader_module(device: &Device, binary: &[u8]) -> Result<vk::ShaderModule> { - let device = self.device.get().unwrap(); - let bytecode = Bytecode::new(binary)?; let module_info = vk::ShaderModuleCreateInfo::builder() @@ -1331,42 +1306,51 @@ impl Surreality { #[allow(unsafe_code)] fn render(&mut self, window_id: WindowId) -> Result<()> { - if let Some(window) = self.window.get() + if let Some(window) = self.window.borrow().as_ref() && window_id == window.id() { - let device = self.device.get().unwrap(); - let graphics_queue = self.graphics_queue.get().unwrap(); - let presentation_queue = self.presentation_queue.get().unwrap(); - let swapchain = self.swapchain.get().unwrap(); - let command_buffers = self.command_buffers.get().unwrap(); + let device = self.device.borrow(); + let device = device.as_ref().unwrap(); + let graphics_queue = self.graphics_queue.borrow(); + let graphics_queue = graphics_queue.as_ref().unwrap(); + let presentation_queue = self.presentation_queue.borrow(); + let presentation_queue = presentation_queue.as_ref().unwrap(); + let swapchain = self.swapchain.borrow(); + let swapchain = swapchain.as_ref().unwrap().swapchain; + let command_buffers = self.command_buffers.borrow(); let frame_index = self.frame_index; let image_available_semaphore - = self.image_available_semaphores.get().unwrap()[frame_index]; + = self.concurrency.borrow().as_ref().unwrap() + .image_available_semaphores[frame_index]; let rendering_finished_semaphore - = self.rendering_finished_semaphores.get() - .unwrap()[frame_index]; - let frame_fence = self.frame_fences.get().unwrap()[frame_index]; + = self.concurrency.borrow().as_ref().unwrap() + .rendering_finished_semaphores[frame_index]; + let frame_fence = self.concurrency.borrow().as_ref().unwrap() + .frame_fences[frame_index]; unsafe { device.wait_for_fences(&[frame_fence], true, u64::MAX) }?; let image_index = unsafe { - device.acquire_next_image_khr(*swapchain, u64::MAX, + device.acquire_next_image_khr(swapchain, u64::MAX, image_available_semaphore, vk::Fence::null()) }?.0 as usize; - let image_fence = self.image_fences[image_index]; + let image_fence = self.concurrency.borrow().as_ref().unwrap() + .image_fences.borrow()[image_index]; if !image_fence.is_null() { unsafe { device.wait_for_fences(&[image_fence], true, u64::MAX) }?; } - self.image_fences[image_index] = frame_fence; + if let Some(concurrency) = self.concurrency.borrow().as_ref() { + concurrency.image_fences.borrow_mut()[image_index] = frame_fence; + } let first_semaphores = [image_available_semaphore]; let second_semaphores = [rendering_finished_semaphore]; let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; - let command_buffers = [command_buffers[image_index]]; + let command_buffers = [command_buffers.as_ref().unwrap()[image_index]]; let submit_info = vk::SubmitInfo::builder() .wait_semaphores(&first_semaphores) .wait_dst_stage_mask(&wait_stages) @@ -1379,7 +1363,7 @@ impl Surreality { frame_fence) }?; - let swapchains = [*swapchain]; + let swapchains = [swapchain]; let image_indices = [image_index as u32]; let present_info = vk::PresentInfoKHR::builder() .wait_semaphores(&second_semaphores) @@ -1400,89 +1384,75 @@ impl Surreality { impl Drop for Surreality { #[allow(unsafe_code)] fn drop(&mut self) { - if let Some(device) = self.device.get() { + if let Some(device) = self.device.borrow().as_ref() { unsafe { device.device_wait_idle() }.unwrap(); } - if let Some(image_available_semaphores) - = self.image_available_semaphores.get() - && let Some(device) = self.device.get() + if let Some(concurrency) = self.concurrency.replace(None) + && let Some(device) = self.device.borrow().as_ref() { - for semaphore in image_available_semaphores { - unsafe { device.destroy_semaphore(*semaphore, None) }; + for semaphore in concurrency.image_available_semaphores { + unsafe { device.destroy_semaphore(semaphore, None) }; } - } - if let Some(rendering_finished_semaphores) - = self.rendering_finished_semaphores.get() - && let Some(device) = self.device.get() - { - for semaphore in rendering_finished_semaphores { - unsafe { device.destroy_semaphore(*semaphore, None) }; + for semaphore in concurrency.rendering_finished_semaphores { + unsafe { device.destroy_semaphore(semaphore, None) }; } - } - if let Some(frame_fences) = self.frame_fences.get() - && let Some(device) = self.device.get() - { - for fence in frame_fences { - unsafe { device.destroy_fence(*fence, None) }; + for fence in concurrency.frame_fences { + unsafe { device.destroy_fence(fence, None) }; } } - if let Some(command_pool) = self.command_pool.get() - && let Some(device) = self.device.get() + if let Some(command_pool) = self.command_pool.replace(None) + && let Some(device) = self.device.borrow().as_ref() { - unsafe { device.destroy_command_pool(*command_pool, None) }; + unsafe { device.destroy_command_pool(command_pool, None) }; } - if let Some(framebuffers) = self.framebuffers.get() - && let Some(device) = self.device.get() + if let Some(framebuffers) = self.framebuffers.replace(None) + && let Some(device) = self.device.borrow().as_ref() { for framebuffer in framebuffers { - unsafe { device.destroy_framebuffer(*framebuffer, None) }; + unsafe { device.destroy_framebuffer(framebuffer, None) }; } } - if let Some(pipeline) = self.pipeline.get() - && let Some(device) = self.device.get() + if let Some(pipeline) = self.pipeline.replace(None) + && let Some(device) = self.device.borrow().as_ref() { - unsafe { device.destroy_pipeline(*pipeline, None) }; + unsafe { device.destroy_pipeline(pipeline, None) }; } - if let Some(render_pass) = self.render_pass.get() - && let Some(device) = self.device.get() + if let Some(render_pass) = self.render_pass.replace(None) + && let Some(device) = self.device.borrow().as_ref() { - unsafe { device.destroy_render_pass(*render_pass, None) }; + unsafe { device.destroy_render_pass(render_pass, None) }; } - if let Some(pipeline_layout) = self.pipeline_layout.get() - && let Some(device) = self.device.get() + if let Some(pipeline_layout) = self.pipeline_layout.replace(None) + && let Some(device) = self.device.borrow().as_ref() { - unsafe { device.destroy_pipeline_layout(*pipeline_layout, None) }; + unsafe { device.destroy_pipeline_layout(pipeline_layout, None) }; } - if let Some(image_views) = self.swapchain_image_views.get() - && let Some(device) = self.device.get() + if let Some(swapchain) = self.swapchain.replace(None) + && let Some(device) = self.device.borrow().as_ref() { - for view in image_views { - unsafe { device.destroy_image_view(*view, None) }; + for view in swapchain.image_views { + unsafe { device.destroy_image_view(view, None) }; } - } - if let Some(swapchain) = self.swapchain.get() - && let Some(device) = self.device.get() - { - unsafe { device.destroy_swapchain_khr(*swapchain, None) }; + unsafe { device.destroy_swapchain_khr(swapchain.swapchain, None) }; } - if let Some(surface) = self.surface.get() - && let Some(instance) = self.instance.get() + if let Some(surface) = self.surface.replace(None) + && let Some(instance) = self.instance.borrow().as_ref() { - unsafe { instance.destroy_surface_khr(*surface, None) }; + unsafe { instance.destroy_surface_khr(surface, None) }; } - if let Some(device) = self.device.get() { + if let Some(device) = self.device.replace(None) { unsafe { device.destroy_device(None) }; } @@ -1491,15 +1461,15 @@ impl Drop for Surreality { // messages while destroying the instance itself only applies to the // instance and the messager, so if we were to destroy anything we // shouldn't after this point, we'd miss out on diagnostics. - if let Some(debug_messager) = self.debug_messager.get() - && let Some(instance) = self.instance.get() + if let Some(debug_messager) = self.debug_messager.replace(None) + && let Some(instance) = self.instance.borrow().as_ref() { unsafe { - instance.destroy_debug_utils_messenger_ext(*debug_messager, None); + instance.destroy_debug_utils_messenger_ext(debug_messager, None); } } - if let Some(instance) = self.instance.get() { + if let Some(instance) = self.instance.replace(None) { unsafe { instance.destroy_instance(None) }; } } |