use std::sync::Arc; use ash::vk; use glam::{f32::Mat4, vec3}; pub use crate::egui_pass::{egui_pass, egui_pre_pass}; use crate::{ Result, buffers::{Buffer, BufferDesc}, commands::{self, traits::CommandBufferExt}, device::{Device, DeviceOwned}, images::ImageViewDesc, pipeline, render_graph::{ Access, GraphResourceId, PassDesc, RecordFn, RenderContext, RenderGraph, buffer_barrier, }, sync, util::Rgba8, }; pub struct Wireframe { positions: Arc, indices: Arc, num_indices: u32, colors: Arc, pipeline: Arc, layout: Arc, } impl Wireframe { pub async fn from_triangles_indexed( dev: Device, positions: Vec, indices: Vec, colors: Vec, ) -> Result { let positions_size = positions.len() * size_of::(); let num_indices = indices.len() as u32; let indices_size = indices.len() * size_of::(); let indices_offset = positions_size; let colors_size = colors.len() * size_of::(); let colors_offset = indices_offset + indices_size; let staging_size = positions_size + indices_size + colors_size; let mut staging = Buffer::new( dev.clone(), BufferDesc { name: Some("wireframe-staging".into()), size: staging_size as u64, usage: vk::BufferUsageFlags::TRANSFER_SRC, mem_usage: vk_mem::MemoryUsage::AutoPreferHost, alloc_flags: vk_mem::AllocationCreateFlags::MAPPED | vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE | vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT, ..Default::default() }, )?; { let mut map = staging.map()?; map[..positions_size].copy_from_slice(bytemuck::cast_slice(&positions)); map[indices_offset..][..indices_size].copy_from_slice(bytemuck::cast_slice(&indices)); map[colors_offset..][..colors_size].copy_from_slice(bytemuck::cast_slice(&colors)); } let positions = Buffer::new( dev.clone(), BufferDesc { name: Some("wireframe-positions".into()), size: positions_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, ..Default::default() }, )?; let indices = Buffer::new( dev.clone(), BufferDesc { name: Some("wireframe-indices".into()), size: indices_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, ..Default::default() }, )?; let colors = Buffer::new( dev.clone(), BufferDesc { name: Some("wireframe-colors".into()), size: colors_size as u64, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, ..Default::default() }, )?; let pool = commands::SingleUseCommandPool::new(dev.clone(), dev.main_queue().clone())?; let cmd = pool.alloc()?; cmd.copy_buffers( staging.handle(), positions.handle(), &[vk::BufferCopy { src_offset: 0, dst_offset: 0, size: positions_size as u64, }], ); cmd.copy_buffers( staging.handle(), indices.handle(), &[vk::BufferCopy { src_offset: indices_offset as u64, dst_offset: 0, size: indices_size as u64, }], ); cmd.copy_buffers( staging.handle(), colors.handle(), &[vk::BufferCopy { src_offset: colors_offset as u64, dst_offset: 0, size: colors_size as u64, }], ); let barriers = [ buffer_barrier( positions.handle(), 0, positions.len(), Access::transfer_write(), Access::vertex_read(), None, ), buffer_barrier( indices.handle(), 0, indices.len(), Access::transfer_write(), Access::index_read(), None, ), buffer_barrier( colors.handle(), 0, colors.len(), Access::transfer_write(), Access::vertex_read(), None, ), ]; unsafe { dev.dev().cmd_pipeline_barrier2( cmd.buffer(), &vk::DependencyInfo::default().buffer_memory_barriers(&barriers), ); } let future = cmd.submit_async( None, None, Arc::new(sync::Fence::from_pool(&dev.pools.fences, None)?), )?; let (pipeline, layout) = Self::create_pipeline(dev.clone())?; future.await; Ok(Self { positions: Arc::new(positions), indices: Arc::new(indices), colors: Arc::new(colors), pipeline: Arc::new(pipeline), layout: Arc::new(layout), num_indices, }) } fn create_pipeline(device: Device) -> Result<(pipeline::Pipeline, pipeline::PipelineLayout)> { let pipeline_layout = pipeline::PipelineLayout::new( device.clone(), pipeline::PipelineLayoutDesc { descriptor_set_layouts: &[], push_constant_ranges: &[vk::PushConstantRange { offset: 0, size: 128, stage_flags: vk::ShaderStageFlags::VERTEX, }], name: Some("wireframe-pipeline-layout".into()), }, )?; let shader = pipeline::ShaderModule::new_from_path( device.clone(), "crates/renderer/shaders/wireframe.spv", )?; let pipeline = pipeline::Pipeline::new( device.clone(), pipeline::PipelineDesc::Graphics(pipeline::GraphicsPipelineDesc { flags: Default::default(), name: Some("wireframe-pipeline".into()), shader_stages: &[ pipeline::ShaderStageDesc { flags: vk::PipelineShaderStageCreateFlags::empty(), module: &shader, stage: vk::ShaderStageFlags::FRAGMENT, entry: c"main".into(), }, pipeline::ShaderStageDesc { flags: vk::PipelineShaderStageCreateFlags::empty(), module: &shader, stage: vk::ShaderStageFlags::VERTEX, entry: c"main".into(), }, ], render_pass: None, layout: &pipeline_layout, subpass: None, base_pipeline: None, vertex_input: Some(pipeline::VertexInputState { bindings: &[ vk::VertexInputBindingDescription { binding: 0, stride: size_of::() as u32, input_rate: vk::VertexInputRate::VERTEX, }, vk::VertexInputBindingDescription { binding: 1, stride: size_of::() as u32, input_rate: vk::VertexInputRate::VERTEX, }, ], attributes: &[ vk::VertexInputAttributeDescription { location: 0, binding: 0, format: vk::Format::R32G32B32_SFLOAT, offset: 0, }, vk::VertexInputAttributeDescription { location: 1, binding: 1, format: vk::Format::R8G8B8A8_UNORM, offset: 0, }, ], }), input_assembly: Some(pipeline::InputAssemblyState { topology: vk::PrimitiveTopology::TRIANGLE_LIST, primitive_restart: false, }), tessellation: None, viewport: Some(pipeline::ViewportState { num_viewports: 1, num_scissors: 1, ..Default::default() }), rasterization: Some(pipeline::RasterizationState { cull_mode: vk::CullModeFlags::NONE, polygon_mode: vk::PolygonMode::LINE, ..Default::default() }), multisample: Some(pipeline::MultisampleState { ..Default::default() }), depth_stencil: Some(pipeline::DepthStencilState { depth: Some(pipeline::DepthState { write_enable: false, compare_op: Some(vk::CompareOp::LESS), bounds: Some(pipeline::DepthBounds { min: 0.0, max: 1.0 }), }), ..Default::default() }), color_blend: Some(pipeline::ColorBlendState { attachments: &[vk::PipelineColorBlendAttachmentState::default() .color_write_mask(vk::ColorComponentFlags::RGBA) .blend_enable(true) .color_blend_op(vk::BlendOp::ADD) .src_color_blend_factor(vk::BlendFactor::ONE) .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) .alpha_blend_op(vk::BlendOp::ADD) .src_alpha_blend_factor(vk::BlendFactor::ONE) .dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)], ..Default::default() }), rendering: Some(pipeline::RenderingState { color_formats: &[vk::Format::R8G8B8A8_UNORM], ..Default::default() }), dynamic: Some(pipeline::DynamicState { dynamic_states: &[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR], ..Default::default() }), }), )?; Ok((pipeline, pipeline_layout)) } pub fn pass(&self, rg: &mut RenderGraph, target: GraphResourceId) -> Result<()> { let reads = [(target, Access::color_attachment_read_write())].to_vec(); let writes = [(target, Access::color_attachment_write_only())].to_vec(); let record: Box = Box::new({ let positions = self.positions.clone(); let indices = self.indices.clone(); let colors = self.colors.clone(); let num_indices = self.num_indices; let pipeline = self.pipeline.clone(); let layout = self.layout.clone(); move |ctx: &RenderContext| -> Result<()> { let target = ctx.get_image(target).unwrap(); let cmd = &ctx.cmd; //let model = Mat4::from_scale(vec3(0.01, 0.01, 0.01)); let proj = Mat4::perspective_lh( core::f32::consts::FRAC_PI_4, target.width() as f32 / target.height() as f32, 0.1, 100.0, ); let pos = vec3(0.0, 0.0, -5.0); let view = Mat4::look_at_rh(pos, pos + vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0)); let to_clip = proj * view; // * model; let color_attachment = &vk::RenderingAttachmentInfo::default() .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) .image_view(target.get_view(ImageViewDesc { kind: vk::ImageViewType::TYPE_2D, format: target.format(), aspect: vk::ImageAspectFlags::COLOR, ..Default::default() })?) .load_op(vk::AttachmentLoadOp::LOAD) .store_op(vk::AttachmentStoreOp::STORE); cmd.begin_rendering( vk::RenderingInfo::default() .color_attachments(core::slice::from_ref(color_attachment)) .layer_count(1) .render_area(vk::Rect2D::default().extent(target.extent_2d())), ); cmd.set_scissors(&[vk::Rect2D::default() .offset(vk::Offset2D::default()) .extent(target.extent_2d())]); cmd.set_viewport(&[vk::Viewport::default() .x(0.0) .y(0.0) .min_depth(0.0) .max_depth(1.0) .width(target.width() as f32) .height(target.height() as f32)]); cmd.bind_pipeline(&pipeline); cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32); cmd.bind_vertex_buffers(&[positions.handle(), colors.handle()], &[0, 0]); cmd.push_constants( &layout, vk::ShaderStageFlags::VERTEX, 0, bytemuck::cast_slice(&[to_clip]), ); cmd.draw_indexed(num_indices, 1, 0, 0, 0); cmd.end_rendering(); Ok(()) } }); rg.add_pass(PassDesc { reads, writes, record: Some(record), }); Ok(()) } }