diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 621 |
1 files changed, 6 insertions, 615 deletions
diff --git a/src/main.rs b/src/main.rs index b073b7c..5dea456 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,21 @@ #![deny(unsafe_code)] use crate::error::*; -use crate::graphics_permanent::{ PermanentGraphicsState, QueueFamilyIndices }; +use crate::graphics_permanent::PermanentGraphicsState; +use crate::graphics_window_dressing::{ + WindowDressing, N_SIMULTANEOUS_FRAMES +}; use std::cell::RefCell; -use std::collections::BTreeSet; -use vulkanalia::{ Device, Instance }; -use vulkanalia::bytecode::Bytecode; use vulkanalia::vk::{ self, Handle, HasBuilder, DeviceV1_0, KhrSwapchainExtensionDeviceCommands }; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ ActiveEventLoop, EventLoop }; -use winit::window::{ Window, WindowId }; +use winit::window::WindowId; mod error; mod graphics_permanent; - - -// TODO: use VK_KHR_swapchain_maintenance1 to put a fence on the presentation -// operation. doing that will remove the requirement that we have more -// simultaneous frames than images. -const N_SIMULTANEOUS_FRAMES: usize = 5; +mod graphics_window_dressing; struct Surreality { @@ -29,62 +24,6 @@ struct Surreality { frame_index: usize, } -// The WindowDressing collects the Vulkan graphics objects which need to be -// regenerated or modified when the window changes in certain ways, such as -// resizing. The ones which don't need that are collected above, in -// PermanentGraphicsState. -#[derive(Debug)] -struct WindowDressing { - swapchain: Swapchain, - - render_pass: vk::RenderPass, - - pipeline: vk::Pipeline, - pipeline_layout: vk::PipelineLayout, - - framebuffers: Vec<vk::Framebuffer>, - - command_pool: vk::CommandPool, - command_buffers: Vec<vk::CommandBuffer>, - - concurrency: Concurrency, -} - -// 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 - // reference to it. - // - // There is one entry in image_fences for each image. The number of images - // is not directly related to the number of frames; it will likely be - // larger, but may be smaller or the same. At the start of execution, the - // entries are all nulls. Each time an image is acquired from the swapchain, - // the corresponding entry in image_fences is overwritten with a duplicate - // 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: Vec<vk::Fence>, - image_fences: Vec<vk::Fence>, -} - - impl Surreality { fn new() -> Self { Surreality { @@ -110,23 +49,6 @@ impl Surreality { } #[allow(unsafe_code)] - fn load_spirv_shader_module(device: &Device, binary: &[u8]) - -> Result<vk::ShaderModule> - { - let bytecode = Bytecode::new(binary)?; - - let module_info = vk::ShaderModuleCreateInfo::builder() - .code(bytecode.code()) - .code_size(bytecode.code_size()); - - let module = unsafe { - device.create_shader_module(&module_info, None) - }?; - - Ok(module) - } - - #[allow(unsafe_code)] fn render(&mut self, window_id: WindowId) -> Result<()> { if let Some(permanent) = self.permanent.borrow().as_ref() && let Some(window_dressing) @@ -207,537 +129,6 @@ impl Drop for Surreality { } } -impl WindowDressing { - fn new(permanent: &PermanentGraphicsState, - physical_device: vk::PhysicalDevice, - indices: QueueFamilyIndices) - -> Result<Self> - { - let window = &permanent.window; - let instance = &permanent.instance; - let surface = &permanent.surface; - let device = &permanent.device; - - let swapchain = Self::init_swapchain( - window, instance, surface, &physical_device, device, &indices)?; - - let render_pass = Self::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)?; - - Ok(WindowDressing { - swapchain, - render_pass, - pipeline, - pipeline_layout, - framebuffers, - command_pool, - command_buffers, - concurrency, - }) - } - - #[allow(unsafe_code)] - fn destroy(self, permanent: &PermanentGraphicsState) { - let device = &permanent.device; - - for semaphore in self.concurrency.image_available_semaphores { - unsafe { device.destroy_semaphore(semaphore, None) }; - } - - for semaphore in self.concurrency.rendering_finished_semaphores { - unsafe { device.destroy_semaphore(semaphore, None) }; - } - - for fence in self.concurrency.frame_fences { - unsafe { device.destroy_fence(fence, None) }; - } - - unsafe { device.destroy_command_pool(self.command_pool, None) }; - - for framebuffer in self.framebuffers { - unsafe { device.destroy_framebuffer(framebuffer, None) }; - } - - unsafe { device.destroy_pipeline(self.pipeline, None) }; - unsafe { device.destroy_render_pass(self.render_pass, None) }; - unsafe { device.destroy_pipeline_layout(self.pipeline_layout, None) }; - - for view in self.swapchain.image_views { - unsafe { device.destroy_image_view(view, None) }; - } - - unsafe { device.destroy_swapchain_khr(self.swapchain.swapchain, None) }; - } - - #[allow(unsafe_code)] - fn init_swapchain(window: &Window, instance: &Instance, - surface: &vk::SurfaceKHR, - physical_device: &vk::PhysicalDevice, device: &Device, - indices: &QueueFamilyIndices) - -> Result<Swapchain> - { - let (capabilities, formats, presentation_modes) - = PermanentGraphicsState::find_device_swapchain_features( - instance, surface, physical_device)?.require()?; - - let format = Self::pick_surface_format(&formats)?; - - let presentation_mode - = Self::pick_presentation_mode(&presentation_modes)?; - let extent = Self::pick_image_extent(window, capabilities)?; - - let mut image_count = capabilities.min_image_count + 1; - if capabilities.max_image_count != 0 { - image_count - = image_count.clamp(0, 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); - - // 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(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 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); - } - - Ok(Swapchain { - swapchain, images, image_views, - format: format.format, - extent - }) - } - - #[allow(unsafe_code)] - 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(render_pass) - } - - #[allow(unsafe_code)] - 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 - = Surreality::load_spirv_shader_module(device, vertex_binary)?; - let fragment_module - = Surreality::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 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]; - - unsafe { - device.destroy_shader_module(vertex_module, None); - device.destroy_shader_module(fragment_module, None); - }; - - Ok((pipeline_layout, pipeline)) - } - - #[allow(unsafe_code)] - 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(); - - for image_view in swapchain_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); - } - - Ok(framebuffers) - } - - #[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. - - let command_pool_info = vk::CommandPoolCreateInfo::builder() - .flags(vk::CommandPoolCreateFlags::empty()) - .queue_family_index(indices.graphics); - - let command_pool = unsafe { - device.create_command_pool(&command_pool_info, None) - }?; - - 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) - }?; - - for (index, framebuffer) in framebuffers.iter().enumerate() { - let command_buffer = command_buffers[index]; - - let inheritance_info = vk::CommandBufferInheritanceInfo::builder(); - - let command_buffer_begin_info - = vk::CommandBufferBeginInfo::builder() - .flags(vk::CommandBufferUsageFlags::empty()) - .inheritance_info(&inheritance_info); - - unsafe { - device.begin_command_buffer(command_buffer, - &command_buffer_begin_info) - }?; - - let render_area = vk::Rect2D::builder() - .offset(vk::Offset2D::default()) - .extent(*extent); - - let clear_value = vk::ClearValue { - color: vk::ClearColorValue { - float32: [0.0, 0.0, 0.0, 1.0] - } - }; - let clear_values = [clear_value]; - - let begin_pass_info = vk::RenderPassBeginInfo::builder() - .render_pass(*render_pass) - .framebuffer(*framebuffer) - .render_area(render_area) - .clear_values(&clear_values); - - unsafe { - device.cmd_begin_render_pass(command_buffer, &begin_pass_info, - vk::SubpassContents::INLINE) - }; - - unsafe { - device.cmd_bind_pipeline(command_buffer, - vk::PipelineBindPoint::GRAPHICS, - *pipeline) - }; - - unsafe { device.cmd_draw(command_buffer, 3, 1, 0, 0) }; - - unsafe { device.cmd_end_render_pass(command_buffer) }; - - unsafe { device.end_command_buffer(command_buffer) }?; - } - - Ok((command_pool, command_buffers)) - } - - #[allow(unsafe_code)] - 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); - - 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) - }?); - - rendering_finished_semaphores.push(unsafe { - device.create_semaphore(&semaphore_info, None) - }?); - - frame_fences.push(unsafe { - device.create_fence(&fence_info, None) - }?); - } - - let mut image_fences = Vec::new(); - for _ in 0 .. swapchain_images.len() { - image_fences.push(vk::Fence::null()); - } - - Ok(Concurrency { - image_available_semaphores, - rendering_finished_semaphores, - frame_fences, - image_fences: image_fences, - }) - } - - fn pick_surface_format(available_formats: &Vec<vk::SurfaceFormatKHR>) - -> Result<vk::SurfaceFormatKHR> - { - for format in available_formats { - if format.format == vk::Format::B8G8R8A8_SRGB - && format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR - { - return Ok(format.clone()); - } - } - - return Ok(available_formats[0].clone()); - } - - 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(window: &Window, - capabilities: vk::SurfaceCapabilitiesKHR) - -> Result<vk::Extent2D> - { - if capabilities.current_extent.width != u32::MAX - && capabilities.current_extent.height != u32::MAX - { - Ok(capabilities.current_extent) - } else { - let window_size = window.inner_size(); - - let width = window_size.width - .clamp(capabilities.min_image_extent.width, - capabilities.max_image_extent.width); - let height = window_size.height - .clamp(capabilities.min_image_extent.height, - capabilities.max_image_extent.height); - - Ok(vk::Extent2D::builder().width(width).height(height).build()) - } - } -} impl ApplicationHandler for Surreality { fn resumed(&mut self, event_loop: &ActiveEventLoop) { |