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