From d672e147db0df6186c528262367770dcde9d75fc Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Sun, 9 Aug 2026 17:36:41 -0700 Subject: rename and reexport a bunch of graphics state types the word "graphics" is part of the module name, so it does not need to be part of the struct name the fact that an object holds state is... not strictly implied by the fact it's a struct, but it happens that ALL our objects exist primarily to hold state, so we remove that word as well Force-Push: yes Change-Id: I1b3e2ac0f1c29304e75a190172463aabfba4b3a4 --- src/graphics/mod.rs | 10 +- src/graphics/model.rs | 11 +- src/graphics/permanent.rs | 33 ++- src/graphics/render.rs | 497 +++++++++++++++++++++++++++++++++++++++ src/graphics/render_state.rs | 504 ---------------------------------------- src/graphics/scene.rs | 18 +- src/graphics/texture.rs | 4 +- src/graphics/window_dressing.rs | 18 +- 8 files changed, 544 insertions(+), 551 deletions(-) create mode 100644 src/graphics/render.rs delete mode 100644 src/graphics/render_state.rs (limited to 'src/graphics') diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs index 3d03f9c..bb12c80 100644 --- a/src/graphics/mod.rs +++ b/src/graphics/mod.rs @@ -1,9 +1,15 @@ #![deny(unsafe_code)] - pub mod model; pub mod permanent; -pub mod render_state; +pub mod render; pub mod texture; pub mod scene; pub mod util; pub mod window_dressing; + +pub use crate::graphics::model::Model; +pub use crate::graphics::texture::Texture; +pub use crate::graphics::permanent::{ Permanent, ForReinit }; +pub use crate::graphics::render::Render; +pub use crate::graphics::window_dressing::WindowDressing; + diff --git a/src/graphics/model.rs b/src/graphics/model.rs index 7b912b6..9c62d51 100644 --- a/src/graphics/model.rs +++ b/src/graphics/model.rs @@ -1,7 +1,6 @@ #![deny(unsafe_code)] use crate::error::*; -use crate::graphics::permanent::PermanentGraphicsState; -use crate::graphics::render_state::RenderState; +use crate::graphics::{ Permanent, Render }; use crate::graphics::util::init_buffer; use crate::linear_algebra::{ Vec3, Vec4, Transformation }; use crate::shader_data::{ Vertex, VertexPushBlock }; @@ -25,7 +24,7 @@ pub struct Model { impl Model { pub fn new(vertices: Vec>, indices: Vec, - permanent: &PermanentGraphicsState) + permanent: &Permanent) -> Result { let device = &permanent.device; @@ -61,7 +60,7 @@ impl Model { } #[allow(unsafe_code)] - pub fn render(&self, time: f32, device: &Device, render_state: &RenderState, + pub fn render(&self, time: f32, device: &Device, render: &Render, command_buffer: &vk::CommandBuffer, descriptor_set: &vk::DescriptorSet) { @@ -78,7 +77,7 @@ impl Model { unsafe { device.cmd_bind_descriptor_sets(*command_buffer, vk::PipelineBindPoint::GRAPHICS, - render_state.pipeline_layout, + render.pipeline_layout, 0, &[*descriptor_set], &[]) @@ -99,7 +98,7 @@ impl Model { }; unsafe { - device.cmd_push_constants(*command_buffer, render_state.pipeline_layout, + device.cmd_push_constants(*command_buffer, render.pipeline_layout, vk::ShaderStageFlags::VERTEX, 0, push_block_bytes) diff --git a/src/graphics/permanent.rs b/src/graphics/permanent.rs index f359dbd..df86284 100644 --- a/src/graphics/permanent.rs +++ b/src/graphics/permanent.rs @@ -19,10 +19,10 @@ use winit::window::{ Window, WindowAttributes }; const VULKAN_FIRST_PORTABILITY_VERSION: Version = Version::new(1, 3, 216); -// The PermanentGraphicsState collects the various windowing-system and -// Vulkan objects which never need to be regenerated once they're created. The -// ones which do need that are collected below, in WindowDressing. -pub struct PermanentGraphicsState { +// Permanent is a state object which collects the various windowing-system +// and Vulkan objects which never need to be regenerated once they're created. +// The ones which do need that are collected below, in WindowDressing. +pub struct Permanent { // The "window" is the usual operating-system concept of a window; it's // 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; @@ -100,11 +100,10 @@ pub struct EnableAnisotropy(pub bool); pub struct EnableSwapchain(pub bool); -impl PermanentGraphicsState { +impl Permanent { #[allow(unsafe_code)] pub fn new(event_loop: &ActiveEventLoop) - -> Result<(Self, GraphicsStateForReinit, EnableAnisotropy, - EnableSwapchain)> + -> Result<(Self, ForReinit, EnableAnisotropy, EnableSwapchain)> { let window = init_window(event_loop)?; @@ -141,11 +140,11 @@ impl PermanentGraphicsState { let (primary_command_pool, transient_command_pool) = init_command_pools(&device, &indices)?; - Ok((PermanentGraphicsState { + Ok((Permanent { window, entry, instance, debug_messager, surface, device, graphics_queue, presentation_queue, primary_command_pool, transient_command_pool, - }, GraphicsStateForReinit { + }, ForReinit { indices, sample_count, descriptor_set_layout, }, enable_anisotropy, enable_swapchain)) } @@ -230,18 +229,18 @@ impl PermanentGraphicsState { } -// The GraphicsStateForReinit connects Vulkan objects which are only needed -// during the creation of the window-dressing objects. They are used during -// initial startup, and again any time the window-dressing needs to be -// reinitialized. Most notably, they are not needed when rendering. -pub struct GraphicsStateForReinit { +// ForReinit is a state object that collects Vulkan objects which are only +// needed during the creation of the window-dressing objects. They are used +// during initial startup, and again any time the window-dressing needs to be +// reinitialized. Most notably, they are not needed when rendering. +pub struct ForReinit { pub indices: QueueFamilyIndices, pub sample_count: vk::SampleCountFlags, pub descriptor_set_layout: vk::DescriptorSetLayout, } -impl GraphicsStateForReinit { +impl ForReinit { #[allow(unsafe_code)] pub fn destroy(self, device: &Device) -> () { unsafe { @@ -494,7 +493,7 @@ fn init_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, // 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(_) - = PermanentGraphicsState::find_device_swapchain_features( + = Permanent::find_device_swapchain_features( &instance, &surface, &physical_device)? { extensions.push(swapchain_extension_name.as_ptr()); @@ -752,7 +751,7 @@ fn score_vulkan_device(instance: &Instance, surface: &vk::SurfaceKHR, // 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(_) - = PermanentGraphicsState::find_device_swapchain_features( + = Permanent::find_device_swapchain_features( instance, surface, physical_device)? { // We don't count it for enough points to override a device type diff --git a/src/graphics/render.rs b/src/graphics/render.rs new file mode 100644 index 0000000..0b4a12a --- /dev/null +++ b/src/graphics/render.rs @@ -0,0 +1,497 @@ +#![deny(unsafe_code)] +use crate::error::*; +use crate::graphics::{ Permanent, ForReinit, WindowDressing, Model, Texture }; +use crate::shader_data::{ Vertex, UniformBlock, 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 framebuffers: Vec, + pub command_buffers: Vec, + pub descriptor_sets: Vec, +} + + +impl Render { + pub fn new(permanent: &Permanent, for_reinit: &ForReinit, + window_dressing: &WindowDressing, texture: &Texture) + -> Result + { + let device = &permanent.device; + let primary_command_pool = &permanent.primary_command_pool; + 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 color_image_view = &window_dressing.color_image_view; + let depth_image_view = &window_dressing.depth_image_view; + let uniform_buffers = &window_dressing.uniform_buffers; + let descriptor_pool = &window_dressing.descriptor_pool; + let sampler = &window_dressing.sampler; + + 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 framebuffers = init_framebuffers( + device, &swapchain.extent, &swapchain.image_views, + &color_image_view, &depth_image_view, &render_pass)?; + + let command_buffers = init_command_buffers(device, &framebuffers, + primary_command_pool)?; + + let descriptor_sets + = init_descriptor_sets(device, descriptor_set_layout, + &uniform_buffers, &descriptor_pool, + swapchain.images.len(), + &texture.image_view, &sampler)?; + + let model = None; + + Ok(Render { + render_pass, + pipeline, + pipeline_layout, + model, + framebuffers, + command_buffers, + descriptor_sets, + }) + } + + // 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 primary_command_pool = &permanent.primary_command_pool; + 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 color_image_view = &window_dressing.color_image_view; + let depth_image_view = &window_dressing.depth_image_view; + let uniform_buffers = &window_dressing.uniform_buffers; + let descriptor_pool = &window_dressing.descriptor_pool; + let sampler = &window_dressing.sampler; + + 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 framebuffers = init_framebuffers( + device, &swapchain.extent, &swapchain.image_views, + &color_image_view, &depth_image_view, &render_pass)?; + + // Notice that we reused the command pool. + let command_buffers = init_command_buffers(device, &framebuffers, + primary_command_pool)?; + + let descriptor_sets + = init_descriptor_sets(device, descriptor_set_layout, + &uniform_buffers, &descriptor_pool, + swapchain.images.len(), + &texture.image_view, sampler)?; + + self.render_pass = render_pass; + self.pipeline = pipeline; + self.pipeline_layout = pipeline_layout; + self.framebuffers = framebuffers; + self.command_buffers = command_buffers; + self.descriptor_sets = descriptor_sets; + + 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; + + for framebuffer in &self.framebuffers { + unsafe { device.destroy_framebuffer(*framebuffer, None) }; + } + + // 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, + &self.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 = include_bytes!( + concat!(env!("OUT_DIR"), "/shader.vert.spv")); + let fragment_binary = include_bytes!( + concat!(env!("OUT_DIR"), "/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)) +} + + +#[allow(unsafe_code)] +fn init_framebuffers(device: &Device, extent: &vk::Extent2D, + swapchain_image_views: &Vec, + color_image_view: &vk::ImageView, + depth_image_view: &vk::ImageView, + render_pass: &vk::RenderPass) + -> Result> +{ + let mut framebuffers = Vec::new(); + + for color_resolve_image_view in swapchain_image_views { + let attachments = [*color_image_view, + *depth_image_view, + *color_resolve_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_command_buffers(device: &Device, + framebuffers: &Vec, + command_pool: &vk::CommandPool) + -> Result> +{ + 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) + }?; + + Ok(command_buffers) +} + + +#[allow(unsafe_code)] +fn init_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout, + buffers: &Vec, pool: &vk::DescriptorPool, + count: usize, texture_image_view: &vk::ImageView, + sampler: &vk::Sampler) + -> Result> +{ + let layouts = vec![*layout; count]; + let set_info = vk::DescriptorSetAllocateInfo::builder() + .descriptor_pool(*pool) + .set_layouts(&layouts); + let sets = unsafe { device.allocate_descriptor_sets(&set_info) }?; + + for index in 0 .. count { + let buffer_info = vk::DescriptorBufferInfo::builder() + .buffer(buffers[index]) + .offset(0) + .range(size_of::>() as vk::DeviceSize); + + let buffer_info_list = [buffer_info]; + let uniform_block_write_info = vk::WriteDescriptorSet::builder() + .dst_set(sets[index]) + .dst_binding(0) + .dst_array_element(0) + .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) + .buffer_info(&buffer_info_list); + + let image_info = vk::DescriptorImageInfo::builder() + .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) + .image_view(*texture_image_view) + .sampler(*sampler); + let image_info_list = [image_info]; + let sampler_write_info = vk::WriteDescriptorSet::builder() + .dst_set(sets[index]) + .dst_binding(1) + .dst_array_element(0) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(&image_info_list); + + let write_info_list = [uniform_block_write_info, sampler_write_info]; + let copy_info_list: [vk::CopyDescriptorSet; 0] = []; + + unsafe { + device.update_descriptor_sets(&write_info_list, ©_info_list) + }; + } + + Ok(sets) +} diff --git a/src/graphics/render_state.rs b/src/graphics/render_state.rs deleted file mode 100644 index 5848ac8..0000000 --- a/src/graphics/render_state.rs +++ /dev/null @@ -1,504 +0,0 @@ -#![deny(unsafe_code)] -use crate::error::*; -use crate::graphics::permanent::{ - PermanentGraphicsState, GraphicsStateForReinit -}; -use crate::graphics::model::Model; -use crate::graphics::texture::Texture; -use crate::graphics::window_dressing::WindowDressing; -use crate::shader_data::{ Vertex, UniformBlock, VertexPushBlock }; - -use std::mem::size_of; - -use vulkanalia::Device; -use vulkanalia::vk::{ self, Handle, HasBuilder, DeviceV1_0 }; - - -// The RenderState 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 RenderState { - pub render_pass: vk::RenderPass, - - pub pipeline: vk::Pipeline, - pub pipeline_layout: vk::PipelineLayout, - - pub model: Option, - - pub framebuffers: Vec, - pub command_buffers: Vec, - pub descriptor_sets: Vec, -} - - -impl RenderState { - pub fn new(permanent: &PermanentGraphicsState, - for_reinit: &GraphicsStateForReinit, - window_dressing: &WindowDressing, texture: &Texture) - -> Result - { - let device = &permanent.device; - let primary_command_pool = &permanent.primary_command_pool; - 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 color_image_view = &window_dressing.color_image_view; - let depth_image_view = &window_dressing.depth_image_view; - let uniform_buffers = &window_dressing.uniform_buffers; - let descriptor_pool = &window_dressing.descriptor_pool; - let sampler = &window_dressing.sampler; - - 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 framebuffers = init_framebuffers( - device, &swapchain.extent, &swapchain.image_views, - &color_image_view, &depth_image_view, &render_pass)?; - - let command_buffers = init_command_buffers(device, &framebuffers, - primary_command_pool)?; - - let descriptor_sets - = init_descriptor_sets(device, descriptor_set_layout, - &uniform_buffers, &descriptor_pool, - swapchain.images.len(), - &texture.image_view, &sampler)?; - - let model = None; - - Ok(RenderState { - render_pass, - pipeline, - pipeline_layout, - model, - framebuffers, - command_buffers, - descriptor_sets, - }) - } - - // This relies on its caller to have already waited for the device to be - // idle. - pub fn reinit(&mut self, permanent: &PermanentGraphicsState, - for_reinit: &GraphicsStateForReinit, - window_dressing: &WindowDressing, texture: &Texture) - -> Result<()> - { - self.destroy_replaceable(permanent); - - let device = &permanent.device; - let primary_command_pool = &permanent.primary_command_pool; - 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 color_image_view = &window_dressing.color_image_view; - let depth_image_view = &window_dressing.depth_image_view; - let uniform_buffers = &window_dressing.uniform_buffers; - let descriptor_pool = &window_dressing.descriptor_pool; - let sampler = &window_dressing.sampler; - - 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 framebuffers = init_framebuffers( - device, &swapchain.extent, &swapchain.image_views, - &color_image_view, &depth_image_view, &render_pass)?; - - // Notice that we reused the command pool. - let command_buffers = init_command_buffers(device, &framebuffers, - primary_command_pool)?; - - let descriptor_sets - = init_descriptor_sets(device, descriptor_set_layout, - &uniform_buffers, &descriptor_pool, - swapchain.images.len(), - &texture.image_view, sampler)?; - - self.render_pass = render_pass; - self.pipeline = pipeline; - self.pipeline_layout = pipeline_layout; - self.framebuffers = framebuffers; - self.command_buffers = command_buffers; - self.descriptor_sets = descriptor_sets; - - 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: &PermanentGraphicsState) - { - self.destroy_replaceable(permanent); - - if let Some(model) = self.model { - model.destroy(&permanent.device); - } - } - - #[allow(unsafe_code)] - fn destroy_replaceable(&mut self, permanent: &PermanentGraphicsState) - { - let device = &permanent.device; - - for framebuffer in &self.framebuffers { - unsafe { device.destroy_framebuffer(*framebuffer, None) }; - } - - // 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, - &self.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 = include_bytes!( - concat!(env!("OUT_DIR"), "/shader.vert.spv")); - let fragment_binary = include_bytes!( - concat!(env!("OUT_DIR"), "/shader.frag.spv")); - - let vertex_module = PermanentGraphicsState::load_spirv_shader_module( - device, vertex_binary)?; - let fragment_module = PermanentGraphicsState::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)) -} - - -#[allow(unsafe_code)] -fn init_framebuffers(device: &Device, extent: &vk::Extent2D, - swapchain_image_views: &Vec, - color_image_view: &vk::ImageView, - depth_image_view: &vk::ImageView, - render_pass: &vk::RenderPass) - -> Result> -{ - let mut framebuffers = Vec::new(); - - for color_resolve_image_view in swapchain_image_views { - let attachments = [*color_image_view, - *depth_image_view, - *color_resolve_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_command_buffers(device: &Device, - framebuffers: &Vec, - command_pool: &vk::CommandPool) - -> Result> -{ - 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) - }?; - - Ok(command_buffers) -} - - -#[allow(unsafe_code)] -fn init_descriptor_sets(device: &Device, layout: &vk::DescriptorSetLayout, - buffers: &Vec, pool: &vk::DescriptorPool, - count: usize, texture_image_view: &vk::ImageView, - sampler: &vk::Sampler) - -> Result> -{ - let layouts = vec![*layout; count]; - let set_info = vk::DescriptorSetAllocateInfo::builder() - .descriptor_pool(*pool) - .set_layouts(&layouts); - let sets = unsafe { device.allocate_descriptor_sets(&set_info) }?; - - for index in 0 .. count { - let buffer_info = vk::DescriptorBufferInfo::builder() - .buffer(buffers[index]) - .offset(0) - .range(size_of::>() as vk::DeviceSize); - - let buffer_info_list = [buffer_info]; - let uniform_block_write_info = vk::WriteDescriptorSet::builder() - .dst_set(sets[index]) - .dst_binding(0) - .dst_array_element(0) - .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) - .buffer_info(&buffer_info_list); - - let image_info = vk::DescriptorImageInfo::builder() - .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .image_view(*texture_image_view) - .sampler(*sampler); - let image_info_list = [image_info]; - let sampler_write_info = vk::WriteDescriptorSet::builder() - .dst_set(sets[index]) - .dst_binding(1) - .dst_array_element(0) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .image_info(&image_info_list); - - let write_info_list = [uniform_block_write_info, sampler_write_info]; - let copy_info_list: [vk::CopyDescriptorSet; 0] = []; - - unsafe { - device.update_descriptor_sets(&write_info_list, ©_info_list) - }; - } - - Ok(sets) -} diff --git a/src/graphics/scene.rs b/src/graphics/scene.rs index f1399b2..874e9ea 100644 --- a/src/graphics/scene.rs +++ b/src/graphics/scene.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code)] use crate::error::*; -use crate::graphics::render_state::RenderState; +use crate::graphics::Render; use vulkanalia::Device; use vulkanalia::vk::{ self, HasBuilder, DeviceV1_0 }; @@ -8,13 +8,13 @@ use vulkanalia::vk::{ self, HasBuilder, DeviceV1_0 }; #[allow(unsafe_code)] pub fn generate_scene_commands<'a>(image_index: usize, time: f32, - render_state: &'a RenderState, + render: &'a Render, device: &Device, extent: &vk::Extent2D) -> Result<&'a vk::CommandBuffer> { - let command_buffer = &render_state.command_buffers[image_index]; - let framebuffer = &render_state.framebuffers[image_index]; - let descriptor_set = &render_state.descriptor_sets[image_index]; + let command_buffer = &render.command_buffers[image_index]; + let framebuffer = &render.framebuffers[image_index]; + let descriptor_set = &render.descriptor_sets[image_index]; let inheritance_info = vk::CommandBufferInheritanceInfo::builder(); @@ -44,7 +44,7 @@ pub fn generate_scene_commands<'a>(image_index: usize, time: f32, let clear_values = [color_clear_value, depth_clear_value]; let begin_pass_info = vk::RenderPassBeginInfo::builder() - .render_pass(render_state.render_pass) + .render_pass(render.render_pass) .framebuffer(*framebuffer) .render_area(render_area) .clear_values(&clear_values); @@ -57,11 +57,11 @@ pub fn generate_scene_commands<'a>(image_index: usize, time: f32, unsafe { device.cmd_bind_pipeline(*command_buffer, vk::PipelineBindPoint::GRAPHICS, - render_state.pipeline) + render.pipeline) }; - if let Some(model) = &render_state.model { - model.render(time, device, render_state, command_buffer, descriptor_set); + if let Some(model) = &render.model { + model.render(time, device, render, command_buffer, descriptor_set); } unsafe { device.cmd_end_render_pass(*command_buffer) }; diff --git a/src/graphics/texture.rs b/src/graphics/texture.rs index cd4dd7a..a6085e3 100644 --- a/src/graphics/texture.rs +++ b/src/graphics/texture.rs @@ -1,6 +1,6 @@ #![deny(unsafe_code)] use crate::error::*; -use crate::graphics::permanent::PermanentGraphicsState; +use crate::graphics::Permanent; use crate::graphics::util::{ stage_in_buffer, allocate_image, init_image_view, begin_transient_commands, end_transient_commands @@ -22,7 +22,7 @@ pub struct Texture { impl Texture { - pub fn new(permanent: &PermanentGraphicsState) -> Result<(Self, u32)> { + pub fn new(permanent: &Permanent) -> Result<(Self, u32)> { let graphics_queue = &permanent.graphics_queue; let instance = &permanent.instance; let device = &permanent.device; diff --git a/src/graphics/window_dressing.rs b/src/graphics/window_dressing.rs index 9afb03e..cca0dc8 100644 --- a/src/graphics/window_dressing.rs +++ b/src/graphics/window_dressing.rs @@ -1,9 +1,7 @@ #![deny(unsafe_code)] use crate::error::*; -use crate::graphics::permanent::{ - PermanentGraphicsState, GraphicsStateForReinit, QueueFamilyIndices, - EnableAnisotropy -}; +use crate::graphics::{ Permanent, ForReinit }; +use crate::graphics::permanent::{ QueueFamilyIndices, EnableAnisotropy }; use crate::graphics::util::{ allocate_buffer, allocate_image, init_image_view }; @@ -27,8 +25,8 @@ pub const N_SIMULTANEOUS_FRAMES: usize = 5; // The WindowDressing collects the Vulkan graphics objects which need to be // regenerated or modified when the window changes in certain ways, such as // resizing, but are not needed during rendering. The ones which don't need to -// be regenerated are collected in PermanentGraphicsState. The ones which are -// needed during rendering are collected in RenderState, below. +// be regenerated are collected in Permanent. The ones which are needed during +// rendering are collected in RenderState, below. #[derive(Debug)] pub struct WindowDressing { pub swapchain: Swapchain, @@ -89,8 +87,7 @@ pub struct Concurrency { impl WindowDressing { - pub fn new(permanent: &PermanentGraphicsState, - for_reinit: &GraphicsStateForReinit, + pub fn new(permanent: &Permanent, for_reinit: &ForReinit, enable_anisotropy: EnableAnisotropy, mip_count: u32) -> Result { @@ -140,8 +137,7 @@ impl WindowDressing { #[allow(unsafe_code)] - pub fn reinit(&mut self, permanent: &PermanentGraphicsState, - for_reinit: &GraphicsStateForReinit) + pub fn reinit(&mut self, permanent: &Permanent, for_reinit: &ForReinit) -> Result<()> { let window = &permanent.window; @@ -254,7 +250,7 @@ fn init_swapchain(window: &Window, instance: &Instance, let physical_device = device.physical_device(); let (capabilities, formats, presentation_modes) - = PermanentGraphicsState::find_device_swapchain_features( + = Permanent::find_device_swapchain_features( instance, surface, &physical_device)?.require()?; let format = pick_surface_format(&formats)?; -- cgit 1.4.1