use std::{marker::PhantomData, ops::Range, sync::Arc}; use smallvec::{smallvec, SmallVec}; use vulkano::{ buffer::{ allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}, BufferContents, BufferUsage, Subbuffer, }, descriptor_set::{ layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags}, DescriptorSet, WriteDescriptorSet, }, format::Format, image::{ sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, view::ImageView, }, memory::allocator::MemoryTypeFilter, pipeline::{ graphics::{ self, color_blend::{AttachmentBlend, ColorBlendAttachmentState, ColorBlendState}, input_assembly::{InputAssemblyState, PrimitiveTopology}, multisample::MultisampleState, rasterization::RasterizationState, subpass::PipelineRenderingCreateInfo, vertex_input::{Vertex, VertexDefinition, VertexInputState}, viewport::ViewportState, GraphicsPipelineCreateInfo, }, layout::PipelineDescriptorSetLayoutCreateInfo, DynamicState, GraphicsPipeline, Pipeline, PipelineLayout, }, shader::{EntryPoint, ShaderModule}, }; use super::{pass::WGfxPass, WGfx}; pub struct WGfxPipeline { pub graphics: Arc, pub pipeline: Arc, pub format: Format, _dummy: PhantomData, } impl WGfxPipeline where V: Sized, { #[allow(clippy::too_many_arguments)] fn new_from_stages( graphics: Arc, format: Format, blend: Option, topology: PrimitiveTopology, vert_entry_point: EntryPoint, frag_entry_point: EntryPoint, vertex_input_state: Option, updatable_sets: &[usize], ) -> anyhow::Result { let stages = smallvec![ vulkano::pipeline::PipelineShaderStageCreateInfo::new(vert_entry_point), vulkano::pipeline::PipelineShaderStageCreateInfo::new(frag_entry_point), ]; let mut layout_info = PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages); for (idx_l, l) in layout_info.set_layouts.iter_mut().enumerate() { if updatable_sets.contains(&idx_l) { // mark all bindings in the set as UAB l.flags |= DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL; for b in l.bindings.values_mut() { b.binding_flags |= DescriptorBindingFlags::UPDATE_AFTER_BIND; } } } let layout = PipelineLayout::new( graphics.device.clone(), layout_info.into_pipeline_layout_create_info(graphics.device.clone())?, )?; let subpass = PipelineRenderingCreateInfo { color_attachment_formats: vec![Some(format)], ..Default::default() }; let pipeline = GraphicsPipeline::new( graphics.device.clone(), None, GraphicsPipelineCreateInfo { stages, vertex_input_state, input_assembly_state: Some(InputAssemblyState { topology, ..InputAssemblyState::default() }), viewport_state: Some(ViewportState::default()), rasterization_state: Some(RasterizationState { cull_mode: vulkano::pipeline::graphics::rasterization::CullMode::None, ..RasterizationState::default() }), multisample_state: Some(MultisampleState::default()), color_blend_state: Some(ColorBlendState { attachments: vec![ColorBlendAttachmentState { blend, ..Default::default() }], ..Default::default() }), dynamic_state: [DynamicState::Viewport, DynamicState::Scissor].into_iter().collect(), subpass: Some(subpass.into()), ..GraphicsPipelineCreateInfo::layout(layout) }, )?; Ok(Self { graphics, pipeline, format, _dummy: PhantomData, }) } pub fn inner(&self) -> Arc { self.pipeline.clone() } pub fn uniform_sampler( &self, set: usize, texture: Arc, filter: Filter, ) -> anyhow::Result> { let sampler = Sampler::new( self.graphics.device.clone(), SamplerCreateInfo { mag_filter: filter, min_filter: filter, address_mode: [SamplerAddressMode::Repeat; 3], ..Default::default() }, )?; let layout = self.pipeline.layout().set_layouts().get(set).unwrap(); // want panic Ok(DescriptorSet::new( self.graphics.descriptor_set_allocator.clone(), layout.clone(), [WriteDescriptorSet::image_view_sampler(0, texture, sampler)], [], )?) } // uniform or storage buffer pub fn buffer(&self, set: usize, buffer: Subbuffer<[T]>) -> anyhow::Result> where T: BufferContents + Copy, { let layout = self.pipeline.layout().set_layouts().get(set).unwrap(); // want panic Ok(DescriptorSet::new( self.graphics.descriptor_set_allocator.clone(), layout.clone(), [WriteDescriptorSet::buffer(0, buffer)], [], )?) } #[allow(clippy::needless_pass_by_value)] pub fn uniform_buffer_upload(&self, set: usize, data: Vec) -> anyhow::Result> where T: BufferContents + Copy, { let buf = SubbufferAllocator::new( self.graphics.memory_allocator.clone(), SubbufferAllocatorCreateInfo { buffer_usage: BufferUsage::UNIFORM_BUFFER, memory_type_filter: MemoryTypeFilter::PREFER_DEVICE | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, ); let uniform_buffer_subbuffer = { let subbuffer = buf.allocate_slice(data.len() as _)?; subbuffer.write()?.copy_from_slice(data.as_slice()); subbuffer }; self.buffer(set, uniform_buffer_subbuffer) } } pub struct WPipelineCreateInfo { format: Format, blend: Option, topology: PrimitiveTopology, instanced: bool, updatable_sets: SmallVec<[usize; 8]>, } impl WPipelineCreateInfo { pub fn new(format: Format) -> Self { Self { format, blend: None, topology: PrimitiveTopology::TriangleStrip, instanced: false, updatable_sets: smallvec![], } } #[must_use] pub const fn use_blend(mut self, blend: AttachmentBlend) -> Self { self.blend = Some(blend); self } #[must_use] pub const fn use_topology(mut self, topology: PrimitiveTopology) -> Self { self.topology = topology; self } #[must_use] pub const fn use_instanced(mut self) -> Self { self.instanced = true; self } #[must_use] pub fn use_updatable_descriptors(mut self, updatable_sets: SmallVec<[usize; 8]>) -> Self { self.updatable_sets = updatable_sets; self } } impl WGfxPipeline where V: BufferContents + Vertex, { pub(super) fn new_with_vert_input( graphics: Arc, vert: &Arc, frag: &Arc, info: WPipelineCreateInfo, ) -> anyhow::Result { let vert_entry_point = vert.entry_point("main").unwrap(); // want panic let frag_entry_point = frag.entry_point("main").unwrap(); // want panic let vertex_input_state = Some(if info.instanced { V::per_instance().definition(&vert_entry_point)? } else { V::per_vertex().definition(&vert_entry_point)? }); Self::new_from_stages( graphics, info.format, info.blend, info.topology, vert_entry_point, frag_entry_point, vertex_input_state, &info.updatable_sets, ) } pub fn create_pass( self: &Arc, dimensions: [f32; 2], offset: [f32; 2], vertex_buffer: Subbuffer<[V]>, vertices: Range, instances: Range, descriptor_sets: Vec>, vk_scissor: &graphics::viewport::Scissor, ) -> anyhow::Result> { WGfxPass::new( &self.clone(), dimensions, offset, vertex_buffer, vertices, instances, descriptor_sets, vk_scissor, ) } }