imgui_rs_vulkan_renderer/renderer/
vulkan.rs

1//! Vulkan helpers.
2//!
3//! A set of functions used to ease Vulkan resources creations. These are supposed to be internal but
4//! are exposed since they might help users create descriptors sets when using the custom textures.
5
6use crate::{Options, RendererResult};
7use ash::{vk, Device};
8pub(crate) use buffer::*;
9use std::{ffi::CString, mem};
10pub(crate) use texture::*;
11
12#[cfg(feature = "dynamic-rendering")]
13use crate::DynamicRendering;
14
15/// Return a `&[u8]` for any sized object passed in.
16pub(crate) unsafe fn any_as_u8_slice<T: Sized>(any: &T) -> &[u8] {
17    let ptr = (any as *const T) as *const u8;
18    std::slice::from_raw_parts(ptr, std::mem::size_of::<T>())
19}
20
21/// Create a descriptor set layout compatible with the graphics pipeline.
22pub fn create_vulkan_descriptor_set_layout(
23    device: &Device,
24) -> RendererResult<vk::DescriptorSetLayout> {
25    log::debug!("Creating vulkan descriptor set layout");
26    let bindings = [vk::DescriptorSetLayoutBinding::default()
27        .binding(0)
28        .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
29        .descriptor_count(1)
30        .stage_flags(vk::ShaderStageFlags::FRAGMENT)];
31
32    let descriptor_set_create_info =
33        vk::DescriptorSetLayoutCreateInfo::default().bindings(&bindings);
34
35    unsafe { Ok(device.create_descriptor_set_layout(&descriptor_set_create_info, None)?) }
36}
37
38pub(crate) fn create_vulkan_pipeline_layout(
39    device: &Device,
40    descriptor_set_layout: vk::DescriptorSetLayout,
41) -> RendererResult<vk::PipelineLayout> {
42    use ultraviolet::mat::Mat4;
43
44    log::debug!("Creating vulkan pipeline layout");
45    let push_const_range = [vk::PushConstantRange {
46        stage_flags: vk::ShaderStageFlags::VERTEX,
47        offset: 0,
48        size: mem::size_of::<Mat4>() as u32,
49    }];
50
51    let descriptor_set_layouts = [descriptor_set_layout];
52    let layout_info = vk::PipelineLayoutCreateInfo::default()
53        .set_layouts(&descriptor_set_layouts)
54        .push_constant_ranges(&push_const_range);
55    let pipeline_layout = unsafe { device.create_pipeline_layout(&layout_info, None)? };
56    Ok(pipeline_layout)
57}
58
59pub(crate) fn create_vulkan_pipeline(
60    device: &Device,
61    pipeline_layout: vk::PipelineLayout,
62    #[cfg(not(feature = "dynamic-rendering"))] render_pass: vk::RenderPass,
63    #[cfg(feature = "dynamic-rendering")] dynamic_rendering: DynamicRendering,
64    options: Options,
65) -> RendererResult<vk::Pipeline> {
66    let entry_point_name = CString::new("main").unwrap();
67
68    let vertex_shader_source = std::include_bytes!("../shaders/shader.vert.spv");
69    let fragment_shader_source = std::include_bytes!("../shaders/shader.frag.spv");
70
71    let vertex_source = read_shader_from_source(vertex_shader_source)?;
72    let vertex_create_info = vk::ShaderModuleCreateInfo::default().code(&vertex_source);
73    let vertex_module = unsafe { device.create_shader_module(&vertex_create_info, None)? };
74
75    let fragment_source = read_shader_from_source(fragment_shader_source)?;
76    let fragment_create_info = vk::ShaderModuleCreateInfo::default().code(&fragment_source);
77    let fragment_module = unsafe { device.create_shader_module(&fragment_create_info, None)? };
78
79    let shader_states_infos = [
80        vk::PipelineShaderStageCreateInfo::default()
81            .stage(vk::ShaderStageFlags::VERTEX)
82            .module(vertex_module)
83            .name(&entry_point_name),
84        vk::PipelineShaderStageCreateInfo::default()
85            .stage(vk::ShaderStageFlags::FRAGMENT)
86            .module(fragment_module)
87            .name(&entry_point_name),
88    ];
89
90    let binding_desc = [vk::VertexInputBindingDescription::default()
91        .binding(0)
92        .stride(20)
93        .input_rate(vk::VertexInputRate::VERTEX)];
94    let attribute_desc = [
95        vk::VertexInputAttributeDescription::default()
96            .binding(0)
97            .location(0)
98            .format(vk::Format::R32G32_SFLOAT)
99            .offset(0),
100        vk::VertexInputAttributeDescription::default()
101            .binding(0)
102            .location(1)
103            .format(vk::Format::R32G32_SFLOAT)
104            .offset(8),
105        vk::VertexInputAttributeDescription::default()
106            .binding(0)
107            .location(2)
108            .format(vk::Format::R8G8B8A8_UNORM)
109            .offset(16),
110    ];
111
112    let vertex_input_info = vk::PipelineVertexInputStateCreateInfo::default()
113        .vertex_binding_descriptions(&binding_desc)
114        .vertex_attribute_descriptions(&attribute_desc);
115
116    let input_assembly_info = vk::PipelineInputAssemblyStateCreateInfo::default()
117        .topology(vk::PrimitiveTopology::TRIANGLE_LIST)
118        .primitive_restart_enable(false);
119
120    let rasterizer_info = vk::PipelineRasterizationStateCreateInfo::default()
121        .depth_clamp_enable(false)
122        .rasterizer_discard_enable(false)
123        .polygon_mode(vk::PolygonMode::FILL)
124        .line_width(1.0)
125        .cull_mode(vk::CullModeFlags::NONE)
126        .front_face(vk::FrontFace::CLOCKWISE)
127        .depth_bias_enable(false)
128        .depth_bias_constant_factor(0.0)
129        .depth_bias_clamp(0.0)
130        .depth_bias_slope_factor(0.0);
131
132    let viewports = [Default::default()];
133    let scissors = [Default::default()];
134    let viewport_info = vk::PipelineViewportStateCreateInfo::default()
135        .viewports(&viewports)
136        .scissors(&scissors);
137
138    let multisampling_info = vk::PipelineMultisampleStateCreateInfo::default()
139        .sample_shading_enable(false)
140        .rasterization_samples(options.sample_count)
141        .min_sample_shading(1.0)
142        .alpha_to_coverage_enable(false)
143        .alpha_to_one_enable(false);
144
145    let color_blend_attachments = [vk::PipelineColorBlendAttachmentState::default()
146        .color_write_mask(
147            vk::ColorComponentFlags::R
148                | vk::ColorComponentFlags::G
149                | vk::ColorComponentFlags::B
150                | vk::ColorComponentFlags::A,
151        )
152        .blend_enable(true)
153        .src_color_blend_factor(vk::BlendFactor::SRC_ALPHA)
154        .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
155        .color_blend_op(vk::BlendOp::ADD)
156        .src_alpha_blend_factor(vk::BlendFactor::ONE)
157        .dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
158        .alpha_blend_op(vk::BlendOp::ADD)];
159    let color_blending_info = vk::PipelineColorBlendStateCreateInfo::default()
160        .logic_op_enable(false)
161        .logic_op(vk::LogicOp::COPY)
162        .attachments(&color_blend_attachments)
163        .blend_constants([0.0, 0.0, 0.0, 0.0]);
164
165    let depth_stencil_state_create_info = vk::PipelineDepthStencilStateCreateInfo::default()
166        .depth_test_enable(options.enable_depth_test)
167        .depth_write_enable(options.enable_depth_write)
168        .depth_compare_op(vk::CompareOp::ALWAYS)
169        .depth_bounds_test_enable(false)
170        .stencil_test_enable(false);
171
172    let dynamic_states = [vk::DynamicState::SCISSOR, vk::DynamicState::VIEWPORT];
173    let dynamic_states_info =
174        vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
175
176    let pipeline_info = vk::GraphicsPipelineCreateInfo::default()
177        .stages(&shader_states_infos)
178        .vertex_input_state(&vertex_input_info)
179        .input_assembly_state(&input_assembly_info)
180        .rasterization_state(&rasterizer_info)
181        .viewport_state(&viewport_info)
182        .multisample_state(&multisampling_info)
183        .color_blend_state(&color_blending_info)
184        .depth_stencil_state(&depth_stencil_state_create_info)
185        .dynamic_state(&dynamic_states_info)
186        .layout(pipeline_layout)
187        .subpass(options.subpass);
188
189    #[cfg(not(feature = "dynamic-rendering"))]
190    let pipeline_info = pipeline_info.render_pass(render_pass);
191
192    #[cfg(feature = "dynamic-rendering")]
193    let color_attachment_formats = [dynamic_rendering.color_attachment_format];
194    #[cfg(feature = "dynamic-rendering")]
195    let mut rendering_info = {
196        let mut rendering_info = vk::PipelineRenderingCreateInfo::default()
197            .color_attachment_formats(&color_attachment_formats);
198        if let Some(depth_attachment_format) = dynamic_rendering.depth_attachment_format {
199            rendering_info = rendering_info.depth_attachment_format(depth_attachment_format);
200        }
201        rendering_info
202    };
203    #[cfg(feature = "dynamic-rendering")]
204    let pipeline_info = pipeline_info.push_next(&mut rendering_info);
205
206    let pipeline = unsafe {
207        device
208            .create_graphics_pipelines(
209                vk::PipelineCache::null(),
210                std::slice::from_ref(&pipeline_info),
211                None,
212            )
213            .map_err(|e| e.1)?[0]
214    };
215
216    unsafe {
217        device.destroy_shader_module(vertex_module, None);
218        device.destroy_shader_module(fragment_module, None);
219    }
220
221    Ok(pipeline)
222}
223
224fn read_shader_from_source(source: &[u8]) -> RendererResult<Vec<u32>> {
225    use std::io::Cursor;
226    let mut cursor = Cursor::new(source);
227    Ok(ash::util::read_spv(&mut cursor)?)
228}
229
230/// Create a descriptor pool of sets compatible with the graphics pipeline.
231pub fn create_vulkan_descriptor_pool(
232    device: &Device,
233    max_sets: u32,
234) -> RendererResult<vk::DescriptorPool> {
235    log::debug!("Creating vulkan descriptor pool");
236
237    let sizes = [vk::DescriptorPoolSize {
238        ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER,
239        descriptor_count: 1,
240    }];
241    let create_info = vk::DescriptorPoolCreateInfo::default()
242        .pool_sizes(&sizes)
243        .max_sets(max_sets)
244        .flags(vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET);
245    unsafe { Ok(device.create_descriptor_pool(&create_info, None)?) }
246}
247
248/// Create a descriptor set compatible with the graphics pipeline from a texture.
249pub fn create_vulkan_descriptor_set(
250    device: &Device,
251    set_layout: vk::DescriptorSetLayout,
252    descriptor_pool: vk::DescriptorPool,
253    image_view: vk::ImageView,
254    sampler: vk::Sampler,
255) -> RendererResult<vk::DescriptorSet> {
256    log::debug!("Creating vulkan descriptor set");
257
258    let set = {
259        let set_layouts = [set_layout];
260        let allocate_info = vk::DescriptorSetAllocateInfo::default()
261            .descriptor_pool(descriptor_pool)
262            .set_layouts(&set_layouts);
263
264        unsafe { device.allocate_descriptor_sets(&allocate_info)?[0] }
265    };
266
267    unsafe {
268        let image_info = [vk::DescriptorImageInfo {
269            sampler,
270            image_view,
271            image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
272        }];
273
274        let writes = [vk::WriteDescriptorSet::default()
275            .dst_set(set)
276            .dst_binding(0)
277            .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
278            .image_info(&image_info)];
279        device.update_descriptor_sets(&writes, &[])
280    }
281
282    Ok(set)
283}
284
285mod buffer {
286
287    use crate::{
288        renderer::allocator::{Allocate, Allocator, Memory},
289        RendererResult,
290    };
291    use ash::vk;
292    use ash::Device;
293
294    pub fn create_and_fill_buffer<T>(
295        device: &Device,
296        allocator: &mut Allocator,
297        data: &[T],
298        usage: vk::BufferUsageFlags,
299    ) -> RendererResult<(vk::Buffer, Memory)>
300    where
301        T: Copy,
302    {
303        let size = std::mem::size_of_val(data);
304        let (buffer, mut memory) = allocator.create_buffer(device, size, usage)?;
305        allocator.update_buffer(device, &mut memory, data)?;
306        Ok((buffer, memory))
307    }
308}
309
310mod texture {
311
312    use super::buffer::*;
313    use crate::renderer::allocator::{Allocate, Allocator, Memory};
314    use crate::RendererResult;
315    use ash::vk;
316    use ash::Device;
317
318    /// Helper struct representing a sampled texture.
319    pub struct Texture {
320        pub image: vk::Image,
321        image_mem: Memory,
322        pub image_view: vk::ImageView,
323        pub sampler: vk::Sampler,
324    }
325
326    impl Texture {
327        /// Create a texture from an `u8` array containing an rgba image.
328        ///
329        /// The image data is device local and it's format is R8G8B8A8_UNORM.
330        ///     
331        /// # Arguments
332        ///
333        /// * `device` - The Vulkan logical device.
334        /// * `queue` - The queue with transfer capabilities to execute commands.
335        /// * `command_pool` - The command pool used to create a command buffer used to record commands.
336        /// * `allocator` - Allocator used to allocate memory for the image.
337        /// * `width` - The width of the image.
338        /// * `height` - The height of the image.
339        /// * `data` - The image data.
340        pub fn from_rgba8(
341            device: &Device,
342            queue: vk::Queue,
343            command_pool: vk::CommandPool,
344            allocator: &mut Allocator,
345            width: u32,
346            height: u32,
347            data: &[u8],
348        ) -> RendererResult<Self> {
349            let (texture, staging_buff, staging_mem) =
350                execute_one_time_commands(device, queue, command_pool, |buffer| {
351                    Self::cmd_from_rgba(device, allocator, buffer, width, height, data)
352                })??;
353
354            allocator.destroy_buffer(device, staging_buff, staging_mem)?;
355
356            Ok(texture)
357        }
358
359        fn cmd_from_rgba(
360            device: &Device,
361            allocator: &mut Allocator,
362            command_buffer: vk::CommandBuffer,
363            width: u32,
364            height: u32,
365            data: &[u8],
366        ) -> RendererResult<(Self, vk::Buffer, Memory)> {
367            let (buffer, buffer_mem) = create_and_fill_buffer(
368                device,
369                allocator,
370                data,
371                vk::BufferUsageFlags::TRANSFER_SRC,
372            )?;
373
374            let (image, image_mem) = allocator.create_image(device, width, height)?;
375
376            // Transition the image layout and copy the buffer into the image
377            // and transition the layout again to be readable from fragment shader.
378            {
379                let mut barrier = vk::ImageMemoryBarrier::default()
380                    .old_layout(vk::ImageLayout::UNDEFINED)
381                    .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
382                    .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
383                    .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
384                    .image(image)
385                    .subresource_range(vk::ImageSubresourceRange {
386                        aspect_mask: vk::ImageAspectFlags::COLOR,
387                        base_mip_level: 0,
388                        level_count: 1,
389                        base_array_layer: 0,
390                        layer_count: 1,
391                    })
392                    .src_access_mask(vk::AccessFlags::empty())
393                    .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE);
394
395                unsafe {
396                    device.cmd_pipeline_barrier(
397                        command_buffer,
398                        vk::PipelineStageFlags::TOP_OF_PIPE,
399                        vk::PipelineStageFlags::TRANSFER,
400                        vk::DependencyFlags::empty(),
401                        &[],
402                        &[],
403                        &[barrier],
404                    )
405                };
406
407                let region = vk::BufferImageCopy::default()
408                    .buffer_offset(0)
409                    .buffer_row_length(0)
410                    .buffer_image_height(0)
411                    .image_subresource(vk::ImageSubresourceLayers {
412                        aspect_mask: vk::ImageAspectFlags::COLOR,
413                        mip_level: 0,
414                        base_array_layer: 0,
415                        layer_count: 1,
416                    })
417                    .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 })
418                    .image_extent(vk::Extent3D {
419                        width,
420                        height,
421                        depth: 1,
422                    });
423                unsafe {
424                    device.cmd_copy_buffer_to_image(
425                        command_buffer,
426                        buffer,
427                        image,
428                        vk::ImageLayout::TRANSFER_DST_OPTIMAL,
429                        &[region],
430                    )
431                }
432
433                barrier.old_layout = vk::ImageLayout::TRANSFER_DST_OPTIMAL;
434                barrier.new_layout = vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL;
435                barrier.src_access_mask = vk::AccessFlags::TRANSFER_WRITE;
436                barrier.dst_access_mask = vk::AccessFlags::SHADER_READ;
437
438                unsafe {
439                    device.cmd_pipeline_barrier(
440                        command_buffer,
441                        vk::PipelineStageFlags::TRANSFER,
442                        vk::PipelineStageFlags::FRAGMENT_SHADER,
443                        vk::DependencyFlags::empty(),
444                        &[],
445                        &[],
446                        &[barrier],
447                    )
448                };
449            }
450
451            let image_view = {
452                let create_info = vk::ImageViewCreateInfo::default()
453                    .image(image)
454                    .view_type(vk::ImageViewType::TYPE_2D)
455                    .format(vk::Format::R8G8B8A8_UNORM)
456                    .subresource_range(vk::ImageSubresourceRange {
457                        aspect_mask: vk::ImageAspectFlags::COLOR,
458                        base_mip_level: 0,
459                        level_count: 1,
460                        base_array_layer: 0,
461                        layer_count: 1,
462                    });
463
464                unsafe { device.create_image_view(&create_info, None)? }
465            };
466
467            let sampler = {
468                let sampler_info = vk::SamplerCreateInfo::default()
469                    .mag_filter(vk::Filter::LINEAR)
470                    .min_filter(vk::Filter::LINEAR)
471                    .address_mode_u(vk::SamplerAddressMode::REPEAT)
472                    .address_mode_v(vk::SamplerAddressMode::REPEAT)
473                    .address_mode_w(vk::SamplerAddressMode::REPEAT)
474                    .anisotropy_enable(false)
475                    .max_anisotropy(1.0)
476                    .border_color(vk::BorderColor::INT_OPAQUE_BLACK)
477                    .unnormalized_coordinates(false)
478                    .compare_enable(false)
479                    .compare_op(vk::CompareOp::ALWAYS)
480                    .mipmap_mode(vk::SamplerMipmapMode::LINEAR)
481                    .mip_lod_bias(0.0)
482                    .min_lod(0.0)
483                    .max_lod(1.0);
484                unsafe { device.create_sampler(&sampler_info, None)? }
485            };
486
487            let texture = Self {
488                image,
489                image_mem,
490                image_view,
491                sampler,
492            };
493
494            Ok((texture, buffer, buffer_mem))
495        }
496
497        /// Free texture's resources.
498        pub fn destroy(self, device: &Device, allocator: &mut Allocator) -> RendererResult<()> {
499            unsafe {
500                device.destroy_sampler(self.sampler, None);
501                device.destroy_image_view(self.image_view, None);
502                allocator.destroy_image(device, self.image, self.image_mem)?;
503            }
504            Ok(())
505        }
506    }
507
508    fn execute_one_time_commands<R, F: FnOnce(vk::CommandBuffer) -> R>(
509        device: &Device,
510        queue: vk::Queue,
511        pool: vk::CommandPool,
512        executor: F,
513    ) -> RendererResult<R> {
514        let command_buffer = {
515            let alloc_info = vk::CommandBufferAllocateInfo::default()
516                .level(vk::CommandBufferLevel::PRIMARY)
517                .command_pool(pool)
518                .command_buffer_count(1);
519
520            unsafe { device.allocate_command_buffers(&alloc_info)?[0] }
521        };
522        let command_buffers = [command_buffer];
523
524        // Begin recording
525        {
526            let begin_info = vk::CommandBufferBeginInfo::default()
527                .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
528            unsafe { device.begin_command_buffer(command_buffer, &begin_info)? };
529        }
530
531        // Execute user function
532        let executor_result = executor(command_buffer);
533
534        // End recording
535        unsafe { device.end_command_buffer(command_buffer)? };
536
537        // Submit and wait
538        {
539            let submit_info = vk::SubmitInfo::default().command_buffers(&command_buffers);
540            let submit_infos = [submit_info];
541            unsafe {
542                device.queue_submit(queue, &submit_infos, vk::Fence::null())?;
543                device.queue_wait_idle(queue)?;
544            };
545        }
546
547        // Free
548        unsafe { device.free_command_buffers(pool, &command_buffers) };
549
550        Ok(executor_result)
551    }
552}