use ash::vk; use crate::helper::record_and_submit_command_buffer; use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme, Allocator}; use gpu_allocator::MemoryLocation; #[repr(C)] #[derive(Clone, Copy)] struct ImGuiCBuffer { scale: [f32; 2], translation: [f32; 2], } pub struct ImGuiRenderer { sampler: vk::Sampler, vb_capacity: u64, ib_capacity: u64, vb_allocation: Allocation, ib_allocation: Allocation, vertex_buffer: vk::Buffer, index_buffer: vk::Buffer, cb_allocation: Allocation, constant_buffer: vk::Buffer, font_image: vk::Image, font_image_memory: Allocation, font_image_view: vk::ImageView, descriptor_sets: Vec, vs_module: vk::ShaderModule, ps_module: vk::ShaderModule, descriptor_set_layouts: Vec, pipeline_layout: vk::PipelineLayout, pub(crate) render_pass: vk::RenderPass, pipeline: vk::Pipeline, } impl ImGuiRenderer { #[allow(clippy::too_many_arguments)] pub(crate) fn new( imgui: &mut imgui::Context, device: &ash::Device, descriptor_pool: vk::DescriptorPool, render_target_format: vk::Format, allocator: &mut Allocator, cmd: vk::CommandBuffer, cmd_reuse_fence: vk::Fence, queue: vk::Queue, ) -> Result { let (pipeline_layout, descriptor_set_layouts) = { let bindings = [ vk::DescriptorSetLayoutBinding { binding: 0, descriptor_type: vk::DescriptorType::UNIFORM_BUFFER, descriptor_count: 1, stage_flags: vk::ShaderStageFlags::VERTEX, p_immutable_samplers: std::ptr::null(), }, vk::DescriptorSetLayoutBinding { binding: 1, descriptor_type: vk::DescriptorType::SAMPLER, descriptor_count: 1, stage_flags: vk::ShaderStageFlags::FRAGMENT, p_immutable_samplers: std::ptr::null(), }, vk::DescriptorSetLayoutBinding { binding: 2, descriptor_type: vk::DescriptorType::SAMPLED_IMAGE, descriptor_count: 1, stage_flags: vk::ShaderStageFlags::FRAGMENT, p_immutable_samplers: std::ptr::null(), }, ]; let set_layout_infos = [vk::DescriptorSetLayoutCreateInfo::builder().bindings(&bindings)]; let set_layouts = set_layout_infos .iter() .map(|info| unsafe { device.create_descriptor_set_layout(info, None) }) .collect::, vk::Result>>()?; let layout_info = vk::PipelineLayoutCreateInfo::builder().set_layouts(&set_layouts); let pipeline_layout = unsafe { device.create_pipeline_layout(&layout_info, None) }?; (pipeline_layout, set_layouts) }; let render_pass = { let attachments = vk::AttachmentDescription::builder() .format(render_target_format) .samples(vk::SampleCountFlags::TYPE_1) .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::STORE) .final_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let subpass_attachment = vk::AttachmentReference::builder() .attachment(0) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let subpass_description = vk::SubpassDescription::builder() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) .color_attachments(std::slice::from_ref(&subpass_attachment)); let dependencies = vk::SubpassDependency::builder() .src_subpass(vk::SUBPASS_EXTERNAL) .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .dst_access_mask( vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE, ) .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT); let render_pass_create_info = vk::RenderPassCreateInfo::builder() .attachments(std::slice::from_ref(&attachments)) .subpasses(std::slice::from_ref(&subpass_description)) .dependencies(std::slice::from_ref(&dependencies)); unsafe { device.create_render_pass(&render_pass_create_info, None) }.unwrap() }; let vs_module = { let vs = include_bytes!("./spirv/imgui.vs.spv"); #[allow(clippy::cast_ptr_alignment)] let shader_info = vk::ShaderModuleCreateInfo::builder().code(unsafe { assert_eq!(vs.len() % 4, 0); std::slice::from_raw_parts(vs.as_ptr().cast(), vs.len() / 4) }); unsafe { device.create_shader_module(&shader_info, None) }? }; let ps_module = { let ps = include_bytes!("./spirv/imgui.ps.spv"); #[allow(clippy::cast_ptr_alignment)] let shader_info = vk::ShaderModuleCreateInfo::builder().code(unsafe { assert_eq!(ps.len() % 4, 0); std::slice::from_raw_parts(ps.as_ptr().cast(), ps.len() / 4) }); unsafe { device.create_shader_module(&shader_info, None) }? }; let pipeline = { let vertex_stage = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::VERTEX) .module(vs_module) .name(std::ffi::CStr::from_bytes_with_nul(b"main\0").unwrap()); let fragment_stage = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::FRAGMENT) .module(ps_module) .name(std::ffi::CStr::from_bytes_with_nul(b"main\0").unwrap()); let stages = [vertex_stage.build(), fragment_stage.build()]; let vertex_binding_descriptions = [vk::VertexInputBindingDescription { binding: 0, stride: std::mem::size_of::() as u32, input_rate: vk::VertexInputRate::VERTEX, }]; let vertex_attribute_descriptions = [ vk::VertexInputAttributeDescription { location: 0, binding: 0, format: vk::Format::R32G32_SFLOAT, offset: 0, }, vk::VertexInputAttributeDescription { location: 1, binding: 0, format: vk::Format::R32G32_SFLOAT, offset: 8, }, vk::VertexInputAttributeDescription { location: 2, binding: 0, format: vk::Format::R8G8B8A8_UNORM, offset: 16, }, ]; let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::builder() .vertex_binding_descriptions(&vertex_binding_descriptions) .vertex_attribute_descriptions(&vertex_attribute_descriptions); let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo::builder() .topology(vk::PrimitiveTopology::TRIANGLE_LIST); let viewport_state = vk::PipelineViewportStateCreateInfo::builder() .viewport_count(1) .scissor_count(1); let rasterization_state = vk::PipelineRasterizationStateCreateInfo::builder() .polygon_mode(vk::PolygonMode::FILL) .cull_mode(vk::CullModeFlags::NONE) .front_face(vk::FrontFace::CLOCKWISE) .depth_bias_enable(false) .line_width(1.0); let multisample_state = vk::PipelineMultisampleStateCreateInfo::builder() .rasterization_samples(vk::SampleCountFlags::TYPE_1); let noop_stencil_state = vk::StencilOpState { fail_op: vk::StencilOp::KEEP, pass_op: vk::StencilOp::KEEP, depth_fail_op: vk::StencilOp::KEEP, compare_op: vk::CompareOp::ALWAYS, ..Default::default() }; let depth_stencil_state = vk::PipelineDepthStencilStateCreateInfo::builder() .depth_test_enable(false) .depth_write_enable(false) .depth_compare_op(vk::CompareOp::ALWAYS) .depth_bounds_test_enable(false) .stencil_test_enable(false) .front(noop_stencil_state) .back(noop_stencil_state) .max_depth_bounds(1.0); let attachments = vk::PipelineColorBlendAttachmentState::builder() .blend_enable(true) .src_color_blend_factor(vk::BlendFactor::SRC_ALPHA) .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) .color_blend_op(vk::BlendOp::ADD) .src_alpha_blend_factor(vk::BlendFactor::ZERO) .dst_alpha_blend_factor(vk::BlendFactor::ZERO) .alpha_blend_op(vk::BlendOp::ADD) .color_write_mask({ vk::ColorComponentFlags::R | vk::ColorComponentFlags::G | vk::ColorComponentFlags::B | vk::ColorComponentFlags::A }); let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder() .logic_op(vk::LogicOp::CLEAR) .attachments(std::slice::from_ref(&attachments)); let dynamic_state = vk::PipelineDynamicStateCreateInfo::builder() .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]); let pipeline_create_info = vk::GraphicsPipelineCreateInfo::builder() .stages(&stages) .vertex_input_state(&vertex_input_state) .input_assembly_state(&input_assembly_state) .viewport_state(&viewport_state) .rasterization_state(&rasterization_state) .multisample_state(&multisample_state) .depth_stencil_state(&depth_stencil_state) .color_blend_state(&color_blend_state) .dynamic_state(&dynamic_state) .layout(pipeline_layout) .render_pass(render_pass) .subpass(0); unsafe { device.create_graphics_pipelines( vk::PipelineCache::null(), std::slice::from_ref(&pipeline_create_info), None, ) } .unwrap()[0] }; let (font_image, font_image_memory, font_image_view) = { let fonts = imgui.fonts(); let font_atlas = fonts.build_rgba32_texture(); // Create image let image_usage = vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::TRANSFER_SRC; let create_info = vk::ImageCreateInfo::builder() .image_type(vk::ImageType::TYPE_2D) .format(vk::Format::R8G8B8A8_UNORM) .extent(vk::Extent3D { width: font_atlas.width, height: font_atlas.height, depth: 1, }) .mip_levels(1) .array_layers(1) .samples(vk::SampleCountFlags::TYPE_1) .tiling(vk::ImageTiling::OPTIMAL) .usage(image_usage) .initial_layout(vk::ImageLayout::UNDEFINED); let image = unsafe { device.create_image(&create_info, None) }?; // Allocate and bind memory to image let requirements = unsafe { device.get_image_memory_requirements(image) }; let allocation = allocator .allocate(&AllocationCreateDesc { name: "ImGui font image", requirements, location: MemoryLocation::GpuOnly, linear: false, allocation_scheme: AllocationScheme::GpuAllocatorManaged, }) .unwrap(); unsafe { device.bind_image_memory(image, allocation.memory(), allocation.offset()) } .unwrap(); // Create image view let view_create_info = vk::ImageViewCreateInfo::builder() .image(image) .view_type(vk::ImageViewType::TYPE_2D) .format(vk::Format::R8G8B8A8_UNORM) .components(vk::ComponentMapping { r: vk::ComponentSwizzle::R, g: vk::ComponentSwizzle::G, b: vk::ComponentSwizzle::B, a: vk::ComponentSwizzle::A, }) .subresource_range(vk::ImageSubresourceRange { aspect_mask: vk::ImageAspectFlags::COLOR, base_mip_level: 0, level_count: 1, base_array_layer: 0, layer_count: 1, }); let image_view = unsafe { device.create_image_view(&view_create_info, None) }?; // Create upload buffer let (upload_buffer, upload_buffer_memory) = { let create_info = vk::BufferCreateInfo::builder() .size((font_atlas.width * font_atlas.height * 4) as u64) .usage(vk::BufferUsageFlags::TRANSFER_SRC); let buffer = unsafe { device.create_buffer(&create_info, None) }?; let requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; let buffer_memory = allocator .allocate(&AllocationCreateDesc { name: "ImGui font image upload buffer", requirements, location: MemoryLocation::CpuToGpu, linear: true, allocation_scheme: AllocationScheme::GpuAllocatorManaged, }) .unwrap(); unsafe { device.bind_buffer_memory( buffer, buffer_memory.memory(), buffer_memory.offset(), ) }?; (buffer, buffer_memory) }; // Copy font data to upload buffer let dst = upload_buffer_memory.mapped_ptr().unwrap().cast().as_ptr(); unsafe { std::ptr::copy_nonoverlapping( font_atlas.data.as_ptr(), dst, (font_atlas.width * font_atlas.height * 4) as usize, ); } // Copy upload buffer to image record_and_submit_command_buffer( device, cmd, cmd_reuse_fence, queue, &[], &[], &[], |device, cmd| { { let layout_transition_barriers = vk::ImageMemoryBarrier::builder() .image(image) .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE) .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) .old_layout(vk::ImageLayout::UNDEFINED) .subresource_range(vk::ImageSubresourceRange { aspect_mask: vk::ImageAspectFlags::COLOR, base_mip_level: 0, level_count: vk::REMAINING_MIP_LEVELS, base_array_layer: 0, layer_count: vk::REMAINING_ARRAY_LAYERS, }); unsafe { device.cmd_pipeline_barrier( cmd, vk::PipelineStageFlags::BOTTOM_OF_PIPE, vk::PipelineStageFlags::TRANSFER, vk::DependencyFlags::empty(), &[], &[], std::slice::from_ref(&layout_transition_barriers), ) }; } let regions = vk::BufferImageCopy::builder() .buffer_offset(0) .buffer_row_length(font_atlas.width) .buffer_image_height(font_atlas.height) .image_subresource(vk::ImageSubresourceLayers { aspect_mask: vk::ImageAspectFlags::COLOR, mip_level: 0, base_array_layer: 0, layer_count: 1, }) .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 }) .image_extent(vk::Extent3D { width: font_atlas.width, height: font_atlas.height, depth: 1, }); unsafe { device.cmd_copy_buffer_to_image( cmd, upload_buffer, image, vk::ImageLayout::TRANSFER_DST_OPTIMAL, std::slice::from_ref(®ions), ) }; { let layout_transition_barriers = vk::ImageMemoryBarrier::builder() .image(image) .dst_access_mask(vk::AccessFlags::SHADER_READ) .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) .subresource_range(vk::ImageSubresourceRange { aspect_mask: vk::ImageAspectFlags::COLOR, base_mip_level: 0, level_count: vk::REMAINING_MIP_LEVELS, base_array_layer: 0, layer_count: vk::REMAINING_ARRAY_LAYERS, }); unsafe { device.cmd_pipeline_barrier( cmd, vk::PipelineStageFlags::BOTTOM_OF_PIPE, vk::PipelineStageFlags::FRAGMENT_SHADER, vk::DependencyFlags::empty(), &[], &[], std::slice::from_ref(&layout_transition_barriers), ) }; } }, ); unsafe { device.queue_wait_idle(queue) }?; // Free upload buffer unsafe { device.destroy_buffer(upload_buffer, None) }; allocator.free(upload_buffer_memory).unwrap(); (image, allocation, image_view) }; let sampler = { let create_info = vk::SamplerCreateInfo::builder() .mag_filter(vk::Filter::NEAREST) .min_filter(vk::Filter::NEAREST) .mipmap_mode(vk::SamplerMipmapMode::NEAREST) .address_mode_u(vk::SamplerAddressMode::REPEAT) .address_mode_v(vk::SamplerAddressMode::REPEAT) .address_mode_w(vk::SamplerAddressMode::REPEAT) .mip_lod_bias(0.0) .anisotropy_enable(false) .compare_enable(false) .unnormalized_coordinates(false); unsafe { device.create_sampler(&create_info, None) }? }; let (vertex_buffer, vb_allocation, vb_capacity) = { let capacity = 1024 * 1024; let create_info = vk::BufferCreateInfo::builder() .size(capacity) .usage(vk::BufferUsageFlags::VERTEX_BUFFER) .sharing_mode(vk::SharingMode::EXCLUSIVE); let buffer = unsafe { device.create_buffer(&create_info, None) }?; let requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; let allocation = allocator .allocate(&AllocationCreateDesc { name: "ImGui Vertex buffer", requirements, location: MemoryLocation::CpuToGpu, linear: true, allocation_scheme: AllocationScheme::GpuAllocatorManaged, }) .unwrap(); unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) }?; (buffer, allocation, capacity) }; let (index_buffer, ib_allocation, ib_capacity) = { let capacity = 1024 * 1024; let create_info = vk::BufferCreateInfo::builder() .size(capacity) .usage(vk::BufferUsageFlags::INDEX_BUFFER) .sharing_mode(vk::SharingMode::EXCLUSIVE); let buffer = unsafe { device.create_buffer(&create_info, None) }?; let requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; let allocation = allocator .allocate(&AllocationCreateDesc { name: "ImGui Index buffer", requirements, location: MemoryLocation::CpuToGpu, linear: true, allocation_scheme: AllocationScheme::GpuAllocatorManaged, }) .unwrap(); unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) }?; (buffer, allocation, capacity) }; let (constant_buffer, cb_allocation) = { let create_info = vk::BufferCreateInfo::builder() .size(std::mem::size_of::() as u64) .usage(vk::BufferUsageFlags::UNIFORM_BUFFER) .sharing_mode(vk::SharingMode::EXCLUSIVE); let buffer = unsafe { device.create_buffer(&create_info, None) }?; let requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; let allocation = allocator .allocate(&AllocationCreateDesc { name: "ImGui Constant buffer", requirements, location: MemoryLocation::CpuToGpu, linear: true, allocation_scheme: AllocationScheme::GpuAllocatorManaged, }) .unwrap(); unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) }?; (buffer, allocation) }; let descriptor_sets = { let alloc_info = vk::DescriptorSetAllocateInfo::builder() .descriptor_pool(descriptor_pool) .set_layouts(&descriptor_set_layouts); let descriptor_sets = unsafe { device.allocate_descriptor_sets(&alloc_info) }?; let buffer_info = vk::DescriptorBufferInfo::builder() .buffer(constant_buffer) .offset(0) .range(std::mem::size_of::() as u64); let uniform_buffer = vk::WriteDescriptorSet::builder() .dst_set(descriptor_sets[0]) .dst_binding(0) .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER) .buffer_info(std::slice::from_ref(&buffer_info)); let image_info = vk::DescriptorImageInfo::builder().sampler(sampler); let sampler = vk::WriteDescriptorSet::builder() .dst_set(descriptor_sets[0]) .dst_binding(1) .descriptor_type(vk::DescriptorType::SAMPLER) .image_info(std::slice::from_ref(&image_info)); let image_info = vk::DescriptorImageInfo::builder() .image_view(font_image_view) .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL); let sampled_image = vk::WriteDescriptorSet::builder() .dst_set(descriptor_sets[0]) .dst_binding(2) .descriptor_type(vk::DescriptorType::SAMPLED_IMAGE) .image_info(std::slice::from_ref(&image_info)); unsafe { device.update_descriptor_sets( &[ uniform_buffer.build(), sampler.build(), sampled_image.build(), ], &[], ) }; descriptor_sets }; Ok(Self { sampler, vb_capacity, ib_capacity, vb_allocation, ib_allocation, vertex_buffer, index_buffer, cb_allocation, constant_buffer, font_image, font_image_memory, font_image_view, descriptor_sets, vs_module, ps_module, descriptor_set_layouts, pipeline_layout, render_pass, pipeline, }) } pub(crate) fn render( &mut self, imgui_draw_data: &imgui::DrawData, device: &ash::Device, window_width: u32, window_height: u32, framebuffer: vk::Framebuffer, cmd: vk::CommandBuffer, ) { // Update constant buffer { let left = imgui_draw_data.display_pos[0]; let right = imgui_draw_data.display_pos[0] + imgui_draw_data.display_size[0]; let top = imgui_draw_data.display_pos[1]; let bottom = imgui_draw_data.display_pos[1] + imgui_draw_data.display_size[1]; let cbuffer_data = ImGuiCBuffer { scale: [(2.0 / (right - left)), (2.0 / (bottom - top))], translation: [ (right + left) / (left - right), (top + bottom) / (top - bottom), ], }; unsafe { std::ptr::copy_nonoverlapping( &cbuffer_data, self.cb_allocation.mapped_ptr().unwrap().cast().as_ptr(), 1, ) }; } let render_pass_begin_info = vk::RenderPassBeginInfo::builder() .render_pass(self.render_pass) .framebuffer(framebuffer) .render_area(vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent: vk::Extent2D { width: window_width, height: window_height, }, }) .clear_values(&[vk::ClearValue { color: vk::ClearColorValue { float32: [1.0, 0.5, 1.0, 0.0], }, }]); unsafe { device.cmd_begin_render_pass(cmd, &render_pass_begin_info, vk::SubpassContents::INLINE) }; unsafe { device.cmd_bind_pipeline(cmd, vk::PipelineBindPoint::GRAPHICS, self.pipeline) }; let viewport = vk::Viewport::builder() .x(0.0) .y(0.0) .width(window_width as f32) .height(window_height as f32); unsafe { device.cmd_set_viewport(cmd, 0, std::slice::from_ref(&viewport)) }; { let scissor_rect = vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent: vk::Extent2D { width: window_width, height: window_height, }, }; unsafe { device.cmd_set_scissor(cmd, 0, &[scissor_rect]) }; } unsafe { device.cmd_bind_descriptor_sets( cmd, vk::PipelineBindPoint::GRAPHICS, self.pipeline_layout, 0, &self.descriptor_sets, &[], ) }; let (vtx_count, idx_count) = imgui_draw_data .draw_lists() .fold((0, 0), |(vtx_count, idx_count), draw_list| { ( vtx_count + draw_list.vtx_buffer().len(), idx_count + draw_list.idx_buffer().len(), ) }); let vtx_size = (vtx_count * std::mem::size_of::()) as u64; if vtx_size > self.vb_capacity { // reallocate vertex buffer todo!(); } let idx_size = (idx_count * std::mem::size_of::()) as u64; if idx_size > self.ib_capacity { // reallocate index buffer todo!(); } let mut vb_offset = 0; let mut ib_offset = 0; for draw_list in imgui_draw_data.draw_lists() { unsafe { device.cmd_bind_vertex_buffers( cmd, 0, &[self.vertex_buffer], &[vb_offset as u64 * std::mem::size_of::() as u64], ) }; unsafe { device.cmd_bind_index_buffer( cmd, self.index_buffer, ib_offset as u64 * std::mem::size_of::() as u64, vk::IndexType::UINT16, ) }; { let vertices = draw_list.vtx_buffer(); let dst_ptr = self .vb_allocation .mapped_ptr() .unwrap() .cast::() .as_ptr(); let dst_ptr = unsafe { dst_ptr.offset(vb_offset) }; unsafe { std::ptr::copy_nonoverlapping(vertices.as_ptr(), dst_ptr, vertices.len()) }; vb_offset += vertices.len() as isize; } { let indices = draw_list.idx_buffer(); let dst_ptr = self .ib_allocation .mapped_ptr() .unwrap() .cast::() .as_ptr(); let dst_ptr = unsafe { dst_ptr.offset(ib_offset) }; unsafe { std::ptr::copy_nonoverlapping(indices.as_ptr(), dst_ptr, indices.len()) }; ib_offset += indices.len() as isize; } for command in draw_list.commands() { match command { imgui::DrawCmd::Elements { count, cmd_params } => { let scissor_rect = vk::Rect2D { offset: vk::Offset2D { x: cmd_params.clip_rect[0] as i32, y: cmd_params.clip_rect[1] as i32, }, extent: vk::Extent2D { width: (cmd_params.clip_rect[2] - cmd_params.clip_rect[0]) as u32, height: (cmd_params.clip_rect[3] - cmd_params.clip_rect[1]) as u32, }, }; unsafe { device.cmd_set_scissor(cmd, 0, &[scissor_rect]) }; unsafe { device.cmd_draw_indexed( cmd, count as u32, 1, cmd_params.idx_offset as u32, cmd_params.vtx_offset as i32, 0, ) }; } _ => todo!(), } } } unsafe { device.cmd_end_render_pass(cmd) }; } pub(crate) fn destroy(self, device: &ash::Device, allocator: &mut Allocator) { unsafe { device.destroy_buffer(self.constant_buffer, None) }; allocator.free(self.cb_allocation).unwrap(); unsafe { device.destroy_buffer(self.index_buffer, None) }; allocator.free(self.ib_allocation).unwrap(); unsafe { device.destroy_buffer(self.vertex_buffer, None) }; allocator.free(self.vb_allocation).unwrap(); unsafe { device.destroy_sampler(self.sampler, None); } unsafe { device.destroy_image_view(self.font_image_view, None); } unsafe { device.destroy_image(self.font_image, None); } allocator.free(self.font_image_memory).unwrap(); unsafe { device.destroy_shader_module(self.ps_module, None) }; unsafe { device.destroy_shader_module(self.vs_module, 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 &layout in self.descriptor_set_layouts.iter() { unsafe { device.destroy_descriptor_set_layout(layout, None) }; } } } pub(crate) fn handle_imgui_event( io: &mut imgui::Io, window: &winit::window::Window, event: &winit::event::Event<'_, ()>, ) -> bool { use winit::event::{ DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode, WindowEvent, }; match event { Event::WindowEvent { event, window_id } if *window_id == window.id() => match *event { WindowEvent::Resized(physical_size) => { io.display_size = [physical_size.width as f32, physical_size.height as f32]; false } WindowEvent::KeyboardInput { input: KeyboardInput { virtual_keycode: Some(key), state, .. }, .. } => { let pressed = state == ElementState::Pressed; io.keys_down[key as usize] = pressed; match key { VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = pressed, VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = pressed, VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = pressed, VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = pressed, _ => (), } io.want_capture_keyboard } WindowEvent::ReceivedCharacter(ch) => { io.add_input_character(ch); io.want_capture_keyboard } WindowEvent::CursorMoved { position, .. } => { io.mouse_pos = [position.x as f32, position.y as f32]; io.want_capture_mouse } WindowEvent::MouseWheel { delta, phase: TouchPhase::Moved, .. } => { match delta { MouseScrollDelta::LineDelta(h, v) => { io.mouse_wheel_h = h; io.mouse_wheel = v; } MouseScrollDelta::PixelDelta(pos) => { match pos.x.partial_cmp(&0.0) { Some(std::cmp::Ordering::Greater) => io.mouse_wheel_h += 1.0, Some(std::cmp::Ordering::Less) => io.mouse_wheel_h -= 1.0, _ => (), } match pos.y.partial_cmp(&0.0) { Some(std::cmp::Ordering::Greater) => io.mouse_wheel += 1.0, Some(std::cmp::Ordering::Less) => io.mouse_wheel -= 1.0, _ => (), } } } io.want_capture_mouse } WindowEvent::MouseInput { state, button, .. } => { let pressed = state == ElementState::Pressed; match button { MouseButton::Left => io.mouse_down[0] = pressed, MouseButton::Right => io.mouse_down[1] = pressed, MouseButton::Middle => io.mouse_down[2] = pressed, MouseButton::Other(idx @ 0..=4) => io.mouse_down[idx as usize] = pressed, MouseButton::Other(_) => (), } io.want_capture_mouse } _ => false, }, // Track key release events outside our window. If we don't do this, // we might never see the release event if some other window gets focus. Event::DeviceEvent { event: DeviceEvent::Key(KeyboardInput { state: ElementState::Released, virtual_keycode: Some(key), .. }), .. } => { io.keys_down[*key as usize] = false; match *key { VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = false, VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = false, VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = false, VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = false, _ => (), } io.want_capture_keyboard } _ => false, } }