Skip to main content

blade_graphics/vulkan/
pipeline.rs

1use ash::vk;
2use naga::back::spv;
3use std::{ffi, mem, str};
4
5const DUMP_PREFIX: Option<&str> = None;
6
7struct CompiledShader<'a> {
8    vk_module: vk::ShaderModule,
9    _entry_point: ffi::CString,
10    create_info: vk::PipelineShaderStageCreateInfo<'a>,
11    attribute_mappings: Vec<crate::VertexAttributeMapping>,
12    wg_size: [u32; 3],
13}
14
15impl super::Context {
16    fn make_spv_options(&self, data_layouts: &[&crate::ShaderDataLayout]) -> spv::Options<'_> {
17        // collect all the array bindings into overrides
18        let mut binding_map = spv::BindingMap::default();
19        for (group_index, layout) in data_layouts.iter().enumerate() {
20            for (binding_index, &(_, binding)) in layout.bindings.iter().enumerate() {
21                let binding_array_size = match binding {
22                    crate::ShaderBinding::TextureArray { count }
23                    | crate::ShaderBinding::BufferArray { count }
24                    | crate::ShaderBinding::AccelerationStructureArray { count } => Some(count),
25                    _ => None,
26                };
27                let rb = naga::ResourceBinding {
28                    group: group_index as u32,
29                    binding: binding_index as u32,
30                };
31                binding_map.insert(
32                    rb,
33                    spv::BindingInfo {
34                        descriptor_set: group_index as u32,
35                        binding: binding_index as u32,
36                        binding_array_size,
37                    },
38                );
39            }
40        }
41
42        spv::Options {
43            lang_version: match self.device.ray_tracing {
44                // Required for ray queries
45                Some(_) => (1, 4),
46                None => (1, 3),
47            },
48            flags: self.naga_flags,
49            fake_missing_bindings: false,
50            binding_map,
51            capabilities: None,
52            bounds_check_policies: naga::proc::BoundsCheckPolicies::default(),
53            zero_initialize_workgroup_memory: spv::ZeroInitializeWorkgroupMemoryMode::None,
54            force_loop_bounding: false,
55            ray_query_initialization_tracking: true,
56            use_storage_input_output_16: false,
57            debug_info: None,
58            task_dispatch_limits: None,
59            mesh_shader_primitive_indices_clamp: true,
60        }
61    }
62
63    fn load_shader(
64        &self,
65        sf: crate::ShaderFunction,
66        naga_options_base: &spv::Options,
67        group_layouts: &[&crate::ShaderDataLayout],
68        group_infos: &mut [crate::ShaderDataInfo],
69        vertex_fetch_states: &[crate::VertexFetchState],
70    ) -> CompiledShader<'_> {
71        let ep_index = sf.entry_point_index();
72        let ep = &sf.shader.module.entry_points[ep_index];
73        let ep_info = sf.shader.info.get_entry_point(ep_index);
74
75        let (mut module, module_info) = sf.shader.resolve_constants(sf.constants);
76        crate::Shader::fill_resource_bindings(
77            &mut module,
78            group_infos,
79            ep.stage,
80            ep_info,
81            group_layouts,
82        );
83        let attribute_mappings =
84            crate::Shader::fill_vertex_locations(&mut module, ep_index, vertex_fetch_states);
85
86        let pipeline_options = spv::PipelineOptions {
87            shader_stage: ep.stage,
88            entry_point: sf.entry_point.to_string(),
89        };
90        let file_path;
91        let file_name_str;
92        let mut naga_options_debug;
93        let naga_options = if let Some(ref temp_dir) = self.shader_debug_path {
94            use std::{
95                fs,
96                hash::{DefaultHasher, Hash as _, Hasher as _},
97            };
98            let mut hasher = DefaultHasher::new();
99            sf.shader.source.hash(&mut hasher);
100            file_path = temp_dir.join(format!("{}-{:x}.wgsl", sf.entry_point, hasher.finish()));
101            let _ = fs::write(&file_path, &sf.shader.source);
102
103            naga_options_debug = naga_options_base.clone();
104            file_name_str = file_path.to_string_lossy().into_owned();
105            naga_options_debug.debug_info = Some(naga::back::spv::DebugInfo {
106                source_code: &sf.shader.source,
107                file_name: &file_name_str,
108                //TODO: switch to WGSL once NSight Graphics recognizes it
109                language: naga::back::spv::SourceLanguage::GLSL,
110            });
111            &naga_options_debug
112        } else {
113            naga_options_base
114        };
115
116        let spv =
117            spv::write_vec(&module, &module_info, naga_options, Some(&pipeline_options)).unwrap();
118
119        if let Some(dump_prefix) = DUMP_PREFIX {
120            let mut file_name = String::new();
121            for i in 1.. {
122                file_name = format!("{}{}_{:?}{}.spv", dump_prefix, sf.entry_point, ep.stage, i);
123                if !std::path::Path::new(&file_name).exists() {
124                    break;
125                }
126            }
127            let spv_bytes =
128                unsafe { std::slice::from_raw_parts(spv.as_ptr() as *const u8, spv.len() * 4) };
129            println!("Dumping {}", file_name);
130            std::fs::write(file_name, spv_bytes).unwrap();
131        }
132
133        let vk_info = vk::ShaderModuleCreateInfo::default().code(&spv);
134
135        let vk_module = unsafe {
136            self.device
137                .core
138                .create_shader_module(&vk_info, None)
139                .unwrap()
140        };
141
142        let vk_stage = match ep.stage {
143            naga::ShaderStage::Compute => vk::ShaderStageFlags::COMPUTE,
144            naga::ShaderStage::Vertex => vk::ShaderStageFlags::VERTEX,
145            naga::ShaderStage::Fragment => vk::ShaderStageFlags::FRAGMENT,
146            _ => panic!("Unsupported shader stage: {:?}", ep.stage),
147        };
148
149        let entry_point = ffi::CString::new(sf.entry_point).unwrap();
150        let create_info = vk::PipelineShaderStageCreateInfo {
151            stage: vk_stage,
152            module: vk_module,
153            p_name: entry_point.as_ptr(),
154            ..Default::default()
155        };
156
157        CompiledShader {
158            vk_module,
159            _entry_point: entry_point,
160            create_info,
161            attribute_mappings,
162            wg_size: ep.workgroup_size,
163        }
164    }
165
166    fn create_descriptor_set_layout(
167        &self,
168        layout: &crate::ShaderDataLayout,
169        info: &crate::ShaderDataInfo,
170    ) -> super::DescriptorSetLayout {
171        if info.visibility.is_empty() {
172            // we need to have a valid `VkDescriptorSetLayout` regardless
173            return super::DescriptorSetLayout {
174                raw: unsafe {
175                    self.device
176                        .core
177                        .create_descriptor_set_layout(&Default::default(), None)
178                        .unwrap()
179                },
180                ..Default::default()
181            };
182        }
183
184        let stage_flags = map_shader_visibility(info.visibility);
185        let mut vk_bindings = Vec::with_capacity(layout.bindings.len());
186        let mut template_entries = Vec::with_capacity(layout.bindings.len());
187        let mut template_offsets = Vec::with_capacity(layout.bindings.len());
188        let mut binding_flags = Vec::with_capacity(layout.bindings.len());
189        let mut inline_uniform_mask = 0u64;
190        let mut update_offset = 0;
191        for (binding_index, (&(_, binding), &access)) in layout
192            .bindings
193            .iter()
194            .zip(info.binding_access.iter())
195            .enumerate()
196        {
197            let (descriptor_type, descriptor_size, descriptor_count, flag) = match binding {
198                crate::ShaderBinding::Texture => (
199                    if access.is_empty() {
200                        vk::DescriptorType::SAMPLED_IMAGE
201                    } else {
202                        vk::DescriptorType::STORAGE_IMAGE
203                    },
204                    mem::size_of::<vk::DescriptorImageInfo>(),
205                    1u32,
206                    vk::DescriptorBindingFlags::empty(),
207                ),
208                crate::ShaderBinding::TextureArray { count } => (
209                    if access.is_empty() {
210                        vk::DescriptorType::SAMPLED_IMAGE
211                    } else {
212                        vk::DescriptorType::STORAGE_IMAGE
213                    },
214                    mem::size_of::<vk::DescriptorImageInfo>(),
215                    count,
216                    vk::DescriptorBindingFlags::PARTIALLY_BOUND,
217                ),
218                crate::ShaderBinding::Sampler => (
219                    vk::DescriptorType::SAMPLER,
220                    mem::size_of::<vk::DescriptorImageInfo>(),
221                    1u32,
222                    vk::DescriptorBindingFlags::empty(),
223                ),
224                crate::ShaderBinding::Buffer => (
225                    vk::DescriptorType::STORAGE_BUFFER,
226                    mem::size_of::<vk::DescriptorBufferInfo>(),
227                    1u32,
228                    vk::DescriptorBindingFlags::empty(),
229                ),
230                crate::ShaderBinding::BufferArray { count } => (
231                    vk::DescriptorType::STORAGE_BUFFER,
232                    mem::size_of::<vk::DescriptorBufferInfo>(),
233                    count,
234                    vk::DescriptorBindingFlags::PARTIALLY_BOUND,
235                ),
236                crate::ShaderBinding::AccelerationStructure => (
237                    vk::DescriptorType::ACCELERATION_STRUCTURE_KHR,
238                    mem::size_of::<vk::AccelerationStructureKHR>(),
239                    1u32,
240                    vk::DescriptorBindingFlags::empty(),
241                ),
242                crate::ShaderBinding::AccelerationStructureArray { count } => (
243                    vk::DescriptorType::ACCELERATION_STRUCTURE_KHR,
244                    mem::size_of::<vk::AccelerationStructureKHR>(),
245                    count,
246                    vk::DescriptorBindingFlags::PARTIALLY_BOUND,
247                ),
248                crate::ShaderBinding::Plain { size } => {
249                    if size <= self.device.max_inline_uniform_block_size {
250                        (
251                            vk::DescriptorType::INLINE_UNIFORM_BLOCK_EXT,
252                            1,
253                            size,
254                            vk::DescriptorBindingFlags::empty(),
255                        )
256                    } else {
257                        (
258                            vk::DescriptorType::UNIFORM_BUFFER,
259                            mem::size_of::<vk::DescriptorBufferInfo>(),
260                            1u32,
261                            vk::DescriptorBindingFlags::empty(),
262                        )
263                    }
264                }
265            };
266            if descriptor_type == vk::DescriptorType::INLINE_UNIFORM_BLOCK_EXT {
267                assert_eq!(
268                    descriptor_count % 4,
269                    0,
270                    "Inline uniform block binding {} size must be 4-byte aligned, got {}",
271                    binding_index,
272                    descriptor_count
273                );
274                inline_uniform_mask |= 1 << binding_index;
275            }
276
277            vk_bindings.push(vk::DescriptorSetLayoutBinding {
278                binding: binding_index as u32,
279                descriptor_type,
280                descriptor_count,
281                stage_flags,
282                ..Default::default()
283            });
284            template_entries.push(vk::DescriptorUpdateTemplateEntryKHR {
285                dst_binding: binding_index as u32,
286                dst_array_element: 0,
287                descriptor_count,
288                descriptor_type,
289                offset: update_offset,
290                stride: descriptor_size,
291            });
292            binding_flags.push(flag);
293            template_offsets.push(update_offset as u32);
294            update_offset += descriptor_size * descriptor_count as usize;
295        }
296
297        let mut binding_flags_info =
298            vk::DescriptorSetLayoutBindingFlagsCreateInfo::default().binding_flags(&binding_flags);
299        let set_layout_info = vk::DescriptorSetLayoutCreateInfo::default()
300            .bindings(&vk_bindings)
301            .push_next(&mut binding_flags_info);
302        let raw = unsafe {
303            self.device
304                .core
305                .create_descriptor_set_layout(&set_layout_info, None)
306                .unwrap()
307        };
308
309        let template_create_info = vk::DescriptorUpdateTemplateCreateInfo::default()
310            .descriptor_update_entries(&template_entries)
311            .template_type(vk::DescriptorUpdateTemplateTypeKHR::DESCRIPTOR_SET)
312            .descriptor_set_layout(raw);
313        let update_template = unsafe {
314            self.device
315                .core
316                .create_descriptor_update_template(&template_create_info, None)
317                .unwrap()
318        };
319
320        super::DescriptorSetLayout {
321            raw,
322            update_template,
323            template_size: update_offset as u32,
324            template_offsets: template_offsets.into_boxed_slice(),
325            inline_uniform_mask,
326        }
327    }
328
329    fn create_pipeline_layout(
330        &self,
331        group_layouts: &[&crate::ShaderDataLayout],
332        group_infos: &[crate::ShaderDataInfo],
333    ) -> super::PipelineLayout {
334        let mut descriptor_set_layouts = Vec::with_capacity(group_layouts.len());
335        let mut vk_set_layouts = Vec::with_capacity(group_layouts.len());
336        for (&layout, info) in group_layouts.iter().zip(group_infos) {
337            let dsl = self.create_descriptor_set_layout(layout, info);
338            vk_set_layouts.push(dsl.raw);
339            descriptor_set_layouts.push(dsl);
340        }
341
342        let vk_info = vk::PipelineLayoutCreateInfo::default().set_layouts(&vk_set_layouts);
343        let raw = unsafe {
344            self.device
345                .core
346                .create_pipeline_layout(&vk_info, None)
347                .unwrap()
348        };
349
350        super::PipelineLayout {
351            raw,
352            descriptor_set_layouts,
353        }
354    }
355
356    fn destroy_pipeline_layout(&self, layout: &mut super::PipelineLayout) {
357        unsafe {
358            self.device
359                .core
360                .destroy_pipeline_layout(mem::take(&mut layout.raw), None);
361        }
362        for dsl in layout.descriptor_set_layouts.drain(..) {
363            unsafe {
364                self.device
365                    .core
366                    .destroy_descriptor_set_layout(dsl.raw, None);
367            }
368            if !dsl.is_empty() {
369                unsafe {
370                    self.device
371                        .core
372                        .destroy_descriptor_update_template(dsl.update_template, None);
373                }
374            }
375        }
376    }
377}
378
379#[hidden_trait::expose]
380impl crate::traits::ShaderDevice for super::Context {
381    type ComputePipeline = super::ComputePipeline;
382    type RenderPipeline = super::RenderPipeline;
383
384    fn create_compute_pipeline(&self, desc: crate::ComputePipelineDesc) -> super::ComputePipeline {
385        let mut group_infos = desc
386            .data_layouts
387            .iter()
388            .map(|layout| layout.to_info())
389            .collect::<Vec<_>>();
390
391        let options = self.make_spv_options(desc.data_layouts);
392        let cs = self.load_shader(
393            desc.compute,
394            &options,
395            desc.data_layouts,
396            &mut group_infos,
397            &[],
398        );
399
400        let layout = self.create_pipeline_layout(desc.data_layouts, &group_infos);
401
402        let create_info = vk::ComputePipelineCreateInfo::default()
403            .layout(layout.raw)
404            .stage(cs.create_info);
405
406        let mut raw_vec = unsafe {
407            self.device
408                .core
409                .create_compute_pipelines(vk::PipelineCache::null(), &[create_info], None)
410                .unwrap_or_else(|(_, err)| {
411                    panic!("Failed to create compute pipeline '{}': {err:?}", desc.name)
412                })
413        };
414        let raw = raw_vec.pop().unwrap();
415
416        unsafe { self.device.core.destroy_shader_module(cs.vk_module, None) };
417
418        if let Some(ref ext) = self.device.shader_info
419            && let Ok(statistics) =
420                unsafe { ext.get_shader_info_statistics(raw, vk::ShaderStageFlags::COMPUTE) }
421        {
422            let ru = &statistics.resource_usage;
423            log::info!(
424                "Compute pipeline '{}' uses: {} VGPRs, {} SGPRs",
425                desc.name,
426                ru.num_used_vgprs,
427                ru.num_used_sgprs,
428            );
429        }
430
431        if !desc.name.is_empty() {
432            self.set_object_name(raw, desc.name);
433        }
434        super::ComputePipeline {
435            raw,
436            layout,
437            wg_size: cs.wg_size,
438        }
439    }
440
441    fn destroy_compute_pipeline(&self, pipeline: &mut super::ComputePipeline) {
442        self.destroy_pipeline_layout(&mut pipeline.layout);
443        unsafe {
444            self.device.core.destroy_pipeline(pipeline.raw, None);
445        }
446    }
447
448    fn create_render_pipeline(&self, desc: crate::RenderPipelineDesc) -> super::RenderPipeline {
449        let mut group_infos = desc
450            .data_layouts
451            .iter()
452            .map(|layout| layout.to_info())
453            .collect::<Vec<_>>();
454
455        let options = self.make_spv_options(desc.data_layouts);
456        let vs = self.load_shader(
457            desc.vertex,
458            &options,
459            desc.data_layouts,
460            &mut group_infos,
461            desc.vertex_fetches,
462        );
463        let fs = desc.fragment.map(|desc_fragment| {
464            self.load_shader(
465                desc_fragment,
466                &options,
467                desc.data_layouts,
468                &mut group_infos,
469                &[],
470            )
471        });
472
473        let mut stages = [vs.create_info, vk::PipelineShaderStageCreateInfo::default()];
474        let mut stage_count = 1;
475        if let Some(ref fs) = fs {
476            stages[1] = fs.create_info;
477            stage_count += 1;
478        }
479        let stages = &stages[..stage_count]; // 'dynamic' stack allocated array
480
481        let layout = self.create_pipeline_layout(desc.data_layouts, &group_infos);
482
483        let vertex_buffers = desc
484            .vertex_fetches
485            .iter()
486            .enumerate()
487            .map(|(i, vf)| vk::VertexInputBindingDescription {
488                binding: i as u32,
489                stride: vf.layout.stride,
490                input_rate: if vf.instanced {
491                    vk::VertexInputRate::INSTANCE
492                } else {
493                    vk::VertexInputRate::VERTEX
494                },
495            })
496            .collect::<Vec<_>>();
497        let vertex_attributes = vs
498            .attribute_mappings
499            .into_iter()
500            .enumerate()
501            .map(|(index, mapping)| {
502                let (_, ref at) = desc.vertex_fetches[mapping.buffer_index].layout.attributes
503                    [mapping.attribute_index];
504                vk::VertexInputAttributeDescription {
505                    location: index as u32,
506                    binding: mapping.buffer_index as u32,
507                    format: super::map_vertex_format(at.format),
508                    offset: at.offset,
509                }
510            })
511            .collect::<Vec<_>>();
512
513        let vk_vertex_input = vk::PipelineVertexInputStateCreateInfo::default()
514            .vertex_binding_descriptions(&vertex_buffers)
515            .vertex_attribute_descriptions(&vertex_attributes);
516        let (raw_topology, supports_restart) = map_primitive_topology(desc.primitive.topology);
517        let vk_input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
518            .topology(raw_topology)
519            .primitive_restart_enable(supports_restart);
520
521        let mut vk_rasterization = vk::PipelineRasterizationStateCreateInfo::default()
522            .polygon_mode(if desc.primitive.wireframe {
523                vk::PolygonMode::LINE
524            } else {
525                vk::PolygonMode::FILL
526            })
527            .front_face(map_front_face(desc.primitive.front_face))
528            .line_width(1.0);
529        if let Some(face) = desc.primitive.cull_mode {
530            vk_rasterization = vk_rasterization.cull_mode(map_cull_face(face));
531        }
532
533        let mut vk_depth_clip_state =
534            vk::PipelineRasterizationDepthClipStateCreateInfoEXT::default()
535                .depth_clip_enable(false);
536        if desc.primitive.unclipped_depth {
537            vk_rasterization = vk_rasterization.push_next(&mut vk_depth_clip_state);
538        }
539
540        let dynamic_states = [
541            vk::DynamicState::VIEWPORT,
542            vk::DynamicState::SCISSOR,
543            vk::DynamicState::BLEND_CONSTANTS,
544            vk::DynamicState::STENCIL_REFERENCE,
545        ];
546        let vk_dynamic_state =
547            vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
548
549        let vk_viewport = vk::PipelineViewportStateCreateInfo::default()
550            .flags(vk::PipelineViewportStateCreateFlags::empty())
551            .scissor_count(1)
552            .viewport_count(1);
553
554        let vk_sample_mask = [
555            desc.multisample_state.sample_mask as u32,
556            (desc.multisample_state.sample_mask >> 32) as u32,
557        ];
558
559        let vk_multisample = vk::PipelineMultisampleStateCreateInfo::default()
560            .rasterization_samples(vk::SampleCountFlags::from_raw(
561                desc.multisample_state.sample_count,
562            ))
563            .alpha_to_coverage_enable(desc.multisample_state.alpha_to_coverage)
564            .sample_mask(&vk_sample_mask);
565
566        let mut d_format = vk::Format::UNDEFINED;
567        let mut s_format = vk::Format::UNDEFINED;
568        let mut vk_depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default();
569        if let Some(ref ds) = desc.depth_stencil {
570            let ds_format = super::map_texture_format(ds.format);
571            if ds.format.aspects().contains(crate::TexelAspects::DEPTH) {
572                d_format = ds_format;
573            }
574            if ds.format.aspects().contains(crate::TexelAspects::STENCIL) {
575                s_format = ds_format;
576            }
577
578            if ds.depth_write_enabled || ds.depth_compare != crate::CompareFunction::Always {
579                vk_depth_stencil = vk_depth_stencil
580                    .depth_test_enable(true)
581                    .depth_write_enable(ds.depth_write_enabled)
582                    .depth_compare_op(map_comparison(ds.depth_compare));
583            }
584            if ds.stencil != crate::StencilState::default() {
585                let s = &ds.stencil;
586                let front = map_stencil_face_state(&s.front, s.read_mask, s.write_mask);
587                let back = map_stencil_face_state(&s.back, s.read_mask, s.write_mask);
588                vk_depth_stencil = vk_depth_stencil
589                    .stencil_test_enable(true)
590                    .front(front)
591                    .back(back);
592            }
593
594            if ds.bias != crate::DepthBiasState::default() {
595                vk_rasterization = vk_rasterization
596                    .depth_bias_enable(true)
597                    .depth_bias_constant_factor(ds.bias.constant as f32)
598                    .depth_bias_clamp(ds.bias.clamp)
599                    .depth_bias_slope_factor(ds.bias.slope_scale);
600            }
601        }
602
603        let mut color_formats = Vec::with_capacity(desc.color_targets.len());
604        let mut vk_attachments = Vec::with_capacity(desc.color_targets.len());
605        for ct in desc.color_targets {
606            let mut vk_attachment = vk::PipelineColorBlendAttachmentState::default()
607                .color_write_mask(vk::ColorComponentFlags::from_raw(ct.write_mask.bits()));
608            if let Some(ref blend) = ct.blend {
609                assert!(
610                    !blend.uses_dual_source() || self.dual_source_blending,
611                    "Dual-source blending is not supported by this Vulkan device"
612                );
613
614                let (color_op, color_src, color_dst) = map_blend_component(&blend.color);
615                let (alpha_op, alpha_src, alpha_dst) = map_blend_component(&blend.alpha);
616                vk_attachment = vk_attachment
617                    .blend_enable(true)
618                    .color_blend_op(color_op)
619                    .src_color_blend_factor(color_src)
620                    .dst_color_blend_factor(color_dst)
621                    .alpha_blend_op(alpha_op)
622                    .src_alpha_blend_factor(alpha_src)
623                    .dst_alpha_blend_factor(alpha_dst);
624            }
625
626            color_formats.push(super::map_texture_format(ct.format));
627            vk_attachments.push(vk_attachment);
628        }
629        let vk_color_blend =
630            vk::PipelineColorBlendStateCreateInfo::default().attachments(&vk_attachments);
631
632        let mut rendering_info = vk::PipelineRenderingCreateInfo::default()
633            .color_attachment_formats(&color_formats)
634            .depth_attachment_format(d_format)
635            .stencil_attachment_format(s_format);
636
637        let create_info = vk::GraphicsPipelineCreateInfo::default()
638            .layout(layout.raw)
639            .stages(stages)
640            .vertex_input_state(&vk_vertex_input)
641            .input_assembly_state(&vk_input_assembly)
642            .rasterization_state(&vk_rasterization)
643            .viewport_state(&vk_viewport)
644            .multisample_state(&vk_multisample)
645            .depth_stencil_state(&vk_depth_stencil)
646            .color_blend_state(&vk_color_blend)
647            .dynamic_state(&vk_dynamic_state)
648            .push_next(&mut rendering_info);
649
650        let mut raw_vec = unsafe {
651            self.device
652                .core
653                .create_graphics_pipelines(vk::PipelineCache::null(), &[create_info], None)
654                .unwrap_or_else(|(_, err)| {
655                    panic!("Failed to create render pipeline '{}': {err:?}", desc.name)
656                })
657        };
658        let raw = raw_vec.pop().unwrap();
659
660        unsafe { self.device.core.destroy_shader_module(vs.vk_module, None) };
661        if let Some(fs) = fs {
662            unsafe { self.device.core.destroy_shader_module(fs.vk_module, None) };
663        }
664
665        if !desc.name.is_empty() {
666            self.set_object_name(raw, desc.name);
667        }
668        super::RenderPipeline { raw, layout }
669    }
670
671    fn destroy_render_pipeline(&self, pipeline: &mut super::RenderPipeline) {
672        self.destroy_pipeline_layout(&mut pipeline.layout);
673        unsafe {
674            self.device.core.destroy_pipeline(pipeline.raw, None);
675        }
676    }
677}
678
679fn map_shader_visibility(visibility: crate::ShaderVisibility) -> vk::ShaderStageFlags {
680    use crate::ShaderVisibility as Sv;
681    use vk::ShaderStageFlags as Flags;
682
683    let mut flags = Flags::empty();
684    if visibility.contains(Sv::COMPUTE) {
685        flags |= Flags::COMPUTE;
686    }
687    if visibility.contains(Sv::VERTEX) {
688        flags |= Flags::VERTEX;
689    }
690    if visibility.contains(Sv::FRAGMENT) {
691        flags |= Flags::FRAGMENT;
692    }
693
694    flags
695}
696
697fn map_primitive_topology(topology: crate::PrimitiveTopology) -> (vk::PrimitiveTopology, bool) {
698    use crate::PrimitiveTopology as Pt;
699    match topology {
700        Pt::PointList => (vk::PrimitiveTopology::POINT_LIST, false),
701        Pt::LineList => (vk::PrimitiveTopology::LINE_LIST, false),
702        Pt::LineStrip => (vk::PrimitiveTopology::LINE_STRIP, true),
703        Pt::TriangleList => (vk::PrimitiveTopology::TRIANGLE_LIST, false),
704        Pt::TriangleStrip => (vk::PrimitiveTopology::TRIANGLE_STRIP, true),
705    }
706}
707
708fn map_front_face(front_face: crate::FrontFace) -> vk::FrontFace {
709    match front_face {
710        crate::FrontFace::Cw => vk::FrontFace::CLOCKWISE,
711        crate::FrontFace::Ccw => vk::FrontFace::COUNTER_CLOCKWISE,
712    }
713}
714
715fn map_cull_face(face: crate::Face) -> vk::CullModeFlags {
716    match face {
717        crate::Face::Front => vk::CullModeFlags::FRONT,
718        crate::Face::Back => vk::CullModeFlags::BACK,
719    }
720}
721
722fn map_comparison(fun: crate::CompareFunction) -> vk::CompareOp {
723    use crate::CompareFunction as Cf;
724    match fun {
725        Cf::Never => vk::CompareOp::NEVER,
726        Cf::Less => vk::CompareOp::LESS,
727        Cf::LessEqual => vk::CompareOp::LESS_OR_EQUAL,
728        Cf::Equal => vk::CompareOp::EQUAL,
729        Cf::GreaterEqual => vk::CompareOp::GREATER_OR_EQUAL,
730        Cf::Greater => vk::CompareOp::GREATER,
731        Cf::NotEqual => vk::CompareOp::NOT_EQUAL,
732        Cf::Always => vk::CompareOp::ALWAYS,
733    }
734}
735
736fn map_stencil_op(op: crate::StencilOperation) -> vk::StencilOp {
737    use crate::StencilOperation as So;
738    match op {
739        So::Keep => vk::StencilOp::KEEP,
740        So::Zero => vk::StencilOp::ZERO,
741        So::Replace => vk::StencilOp::REPLACE,
742        So::Invert => vk::StencilOp::INVERT,
743        So::IncrementClamp => vk::StencilOp::INCREMENT_AND_CLAMP,
744        So::IncrementWrap => vk::StencilOp::INCREMENT_AND_WRAP,
745        So::DecrementClamp => vk::StencilOp::DECREMENT_AND_CLAMP,
746        So::DecrementWrap => vk::StencilOp::DECREMENT_AND_WRAP,
747    }
748}
749
750fn map_stencil_face_state(
751    face: &crate::StencilFaceState,
752    compare_mask: u32,
753    write_mask: u32,
754) -> vk::StencilOpState {
755    vk::StencilOpState {
756        fail_op: map_stencil_op(face.fail_op),
757        pass_op: map_stencil_op(face.pass_op),
758        depth_fail_op: map_stencil_op(face.depth_fail_op),
759        compare_op: map_comparison(face.compare),
760        compare_mask,
761        write_mask,
762        reference: 0,
763    }
764}
765
766fn map_blend_factor(factor: crate::BlendFactor) -> vk::BlendFactor {
767    use crate::BlendFactor as Bf;
768    match factor {
769        Bf::Zero => vk::BlendFactor::ZERO,
770        Bf::One => vk::BlendFactor::ONE,
771        Bf::Src => vk::BlendFactor::SRC_COLOR,
772        Bf::OneMinusSrc => vk::BlendFactor::ONE_MINUS_SRC_COLOR,
773        Bf::SrcAlpha => vk::BlendFactor::SRC_ALPHA,
774        Bf::OneMinusSrcAlpha => vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
775        Bf::Dst => vk::BlendFactor::DST_COLOR,
776        Bf::OneMinusDst => vk::BlendFactor::ONE_MINUS_DST_COLOR,
777        Bf::DstAlpha => vk::BlendFactor::DST_ALPHA,
778        Bf::OneMinusDstAlpha => vk::BlendFactor::ONE_MINUS_DST_ALPHA,
779        Bf::SrcAlphaSaturated => vk::BlendFactor::SRC_ALPHA_SATURATE,
780        Bf::Constant => vk::BlendFactor::CONSTANT_COLOR,
781        Bf::OneMinusConstant => vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR,
782        Bf::Src1 => vk::BlendFactor::SRC1_COLOR,
783        Bf::OneMinusSrc1 => vk::BlendFactor::ONE_MINUS_SRC1_COLOR,
784        Bf::Src1Alpha => vk::BlendFactor::SRC1_ALPHA,
785        Bf::OneMinusSrc1Alpha => vk::BlendFactor::ONE_MINUS_SRC1_ALPHA,
786    }
787}
788
789fn map_blend_op(operation: crate::BlendOperation) -> vk::BlendOp {
790    use crate::BlendOperation as Bo;
791    match operation {
792        Bo::Add => vk::BlendOp::ADD,
793        Bo::Subtract => vk::BlendOp::SUBTRACT,
794        Bo::ReverseSubtract => vk::BlendOp::REVERSE_SUBTRACT,
795        Bo::Min => vk::BlendOp::MIN,
796        Bo::Max => vk::BlendOp::MAX,
797    }
798}
799
800fn map_blend_component(
801    component: &crate::BlendComponent,
802) -> (vk::BlendOp, vk::BlendFactor, vk::BlendFactor) {
803    let op = map_blend_op(component.operation);
804    let src = map_blend_factor(component.src_factor);
805    let dst = map_blend_factor(component.dst_factor);
806    (op, src, dst)
807}