#![deny(unsafe_code)] use crate::error::*; use crate::assets::asset; use crate::graphics::{ Permanent, ForReinit, WindowDressing, Frame, Model, Texture }; use crate::shader_data::{ Vertex, VertexPushBlock }; use std::mem::size_of; use vulkanalia::Device; use vulkanalia::vk::{ self, Handle, HasBuilder, DeviceV1_0 }; // Render is a state object that collects the Vulkan graphics objects which // need to be regenerated or modified when the window changes, as with // WindowDressing, and which are also used as part of rendering. #[derive(Debug)] pub struct Render { pub render_pass: vk::RenderPass, pub pipeline: vk::Pipeline, pub pipeline_layout: vk::PipelineLayout, pub model: Option, pub per_frame: Vec, } impl Render { pub fn new(permanent: &Permanent, for_reinit: &ForReinit, window_dressing: &WindowDressing, texture: &Texture) -> Result { let device = &permanent.device; let sample_count = for_reinit.sample_count; let descriptor_set_layout = &for_reinit.descriptor_set_layout; let swapchain = &window_dressing.swapchain; let depth_format = &window_dressing.depth_format; let render_pass = init_render_pass(device, sample_count, &swapchain.format, &depth_format)?; let (pipeline_layout, pipeline) = init_pipeline(device, descriptor_set_layout, &swapchain.extent, sample_count, &render_pass)?; let per_frame = Frame::new(permanent, for_reinit, window_dressing, texture, &render_pass)?; let model = None; Ok(Render { render_pass, pipeline, pipeline_layout, model, per_frame, }) } // This relies on its caller to have already waited for the device to be // idle. pub fn reinit(&mut self, permanent: &Permanent, for_reinit: &ForReinit, window_dressing: &WindowDressing, texture: &Texture) -> Result<()> { self.destroy_replaceable(permanent); let device = &permanent.device; let sample_count = for_reinit.sample_count; let descriptor_set_layout = &for_reinit.descriptor_set_layout; let swapchain = &window_dressing.swapchain; let depth_format = &window_dressing.depth_format; let render_pass = init_render_pass(device, sample_count, &swapchain.format, &depth_format)?; let (pipeline_layout, pipeline) = init_pipeline(device, descriptor_set_layout, &swapchain.extent, sample_count, &render_pass)?; Frame::reinit(&mut self.per_frame, permanent, for_reinit, window_dressing, texture, &render_pass)?; self.render_pass = render_pass; self.pipeline = pipeline; self.pipeline_layout = pipeline_layout; Ok(()) } // This relies on its caller to have already waited for the device to be // idle. #[allow(unsafe_code)] pub fn destroy(mut self, permanent: &Permanent) { self.destroy_replaceable(permanent); if let Some(model) = self.model { model.destroy(&permanent.device); } } #[allow(unsafe_code)] fn destroy_replaceable(&mut self, permanent: &Permanent) { let device = &permanent.device; let mut command_buffers = Vec::new(); for frame in &self.per_frame { unsafe { device.destroy_framebuffer(frame.framebuffer, None) }; command_buffers.push(frame.command_buffer); } // Notice that we free the buffers in the pool, but do not destroy the // pool itself. Notice also that we only do this for the primary command // pool, because that's the only one where we've kept track of the // buffers. We promise ourselves to free buffers in the transient pool // immediately after using them. unsafe { device.free_command_buffers(permanent.primary_command_pool, &command_buffers) }; unsafe { device.destroy_pipeline(self.pipeline, None) }; unsafe { device.destroy_pipeline_layout(self.pipeline_layout, None) }; unsafe { device.destroy_render_pass(self.render_pass, None) }; } pub fn set_model(&mut self, model: Model) { self.model = Some(model); } } #[allow(unsafe_code)] fn init_render_pass(device: &Device, sample_count: vk::SampleCountFlags, color_format: &vk::Format, depth_format: &vk::Format) -> Result { let color_attachment = vk::AttachmentDescription::builder() .format(*color_format) .samples(sample_count) .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::COLOR_ATTACHMENT_OPTIMAL); let color_attachment_reference = vk::AttachmentReference::builder() .attachment(0) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let depth_attachment = vk::AttachmentDescription::builder() .format(*depth_format) .samples(sample_count) .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::DONT_CARE) .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) .initial_layout(vk::ImageLayout::UNDEFINED) .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); let depth_attachment_reference = vk::AttachmentReference::builder() .attachment(1) .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); let color_resolve_attachment = vk::AttachmentDescription::builder() .format(*color_format) .samples(vk::SampleCountFlags::_1) .load_op(vk::AttachmentLoadOp::DONT_CARE) .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_resolve_attachment_reference = vk::AttachmentReference::builder() .attachment(2) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let color_attachments = [color_attachment_reference]; let resolve_attachments = [color_resolve_attachment_reference]; let subpass = vk::SubpassDescription::builder() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) .color_attachments(&color_attachments) .depth_stencil_attachment(&depth_attachment_reference) .resolve_attachments(&resolve_attachments); let dependency = vk::SubpassDependency::builder() .src_subpass(vk::SUBPASS_EXTERNAL) .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS) .src_access_mask(vk::AccessFlags::empty()) .dst_subpass(0) .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS) .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE); let render_attachments = [color_attachment, depth_attachment, color_resolve_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, descriptor_set_layout: &vk::DescriptorSetLayout, extent: &vk::Extent2D, sample_count: vk::SampleCountFlags, render_pass: &vk::RenderPass) -> Result<(vk::PipelineLayout, vk::Pipeline)> { let vertex_binary = asset("shaders/shader.vert.spv")?; let fragment_binary = asset("shaders/shader.frag.spv")?; let vertex_module = Permanent::load_spirv_shader_module( device, vertex_binary)?; let fragment_module = Permanent::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 binding_descriptions = [Vertex::::binding_description()]; let attribute_descriptions = Vertex::::attribute_descriptions(); let vertex_input_state_info = vk::PipelineVertexInputStateCreateInfo::builder() .vertex_binding_descriptions(&binding_descriptions) .vertex_attribute_descriptions(&attribute_descriptions); 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(sample_count); let depth_state_info = vk::PipelineDepthStencilStateCreateInfo::builder() .depth_test_enable(true) .depth_write_enable(true) .depth_compare_op(vk::CompareOp::LESS) .depth_bounds_test_enable(false) .min_depth_bounds(0.0) .max_depth_bounds(1.0) .stencil_test_enable(false); 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 vertex_push_constant_range = vk::PushConstantRange::builder() .stage_flags(vk::ShaderStageFlags::VERTEX) .offset(0) .size(size_of::>() as u32); let layouts = [*descriptor_set_layout]; let push_constant_ranges = [vertex_push_constant_range]; let pipeline_layout_info = vk::PipelineLayoutCreateInfo::builder() .set_layouts(&layouts) .push_constant_ranges(&push_constant_ranges); 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) .depth_stencil_state(&depth_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)) }