mepeyew/vulkan/
program.rs

1use super::*;
2use std::ffi::CString;
3
4impl VkContext {
5    pub fn new_program(
6        &mut self,
7        shaders: &ShaderSet,
8        uniforms: &[ShaderUniform],
9        ext: Option<NewProgramExt>,
10    ) -> GResult<ProgramId> {
11        let shaders = shaders
12            .0
13            .iter()
14            .map(|(ty, src)| VkShader::new(&self.core.dev, &self.drop_queue, ty, src))
15            .collect::<GResult<Vec<_>>>()?;
16
17        let descriptors = VkDescriptors::new(self, uniforms)?;
18
19        let program = VkProgram {
20            layout: new_pipeline_layout(&self.core.dev, &descriptors.descriptor_set_layouts)?,
21            shaders,
22            descriptors,
23            ext: ext.unwrap_or_default(),
24            drop_queue: Arc::clone(&self.drop_queue),
25        };
26        self.programs.push(program);
27
28        Ok(ProgramId::from_id(self.programs.len() - 1))
29    }
30}
31
32pub fn new_pipeline_layout(
33    dev: &Device,
34    descriptor_set_layouts: &[vk::DescriptorSetLayout],
35) -> GResult<vk::PipelineLayout> {
36    let pipeline_layout_create = vk::PipelineLayoutCreateInfo::builder()
37        .set_layouts(descriptor_set_layouts)
38        .build();
39    unsafe { dev.create_pipeline_layout(&pipeline_layout_create, None) }
40        .map_err(|e| gpu_api_err!("vulkan pipeline layout {}", e))
41}
42
43pub struct VkProgram {
44    pub descriptors: VkDescriptors,
45    pub layout: vk::PipelineLayout,
46    pub ext: NewProgramExt,
47    shaders: Vec<VkShader>,
48
49    drop_queue: VkDropQueueRef,
50}
51
52impl VkProgram {
53    //  TODO OPT: Find seemless and safe way to generate pipelines in one go.
54    pub fn new_graphics_pipeline(
55        &self,
56        dev: &Device,
57        render_pass: vk::RenderPass,
58        subpass: usize,
59        sample_count: Option<vk::SampleCountFlags>,
60        ext: &NewProgramExt,
61    ) -> GResult<vk::Pipeline> {
62        //  Vertex Input State Info
63        let (attributes, bindings): (Vec<_>, Vec<_>) = self
64            .shaders
65            .iter()
66            .filter_map(|shader| {
67                if let ShaderType::Vertex(vertex_inputs) = &shader.shader_ty {
68                    Some(VkShader::get_vertex_inputs(vertex_inputs))
69                } else {
70                    None
71                }
72            })
73            .collect::<Vec<_>>()
74            .into_iter()
75            .unzip();
76        let attributes = attributes.into_iter().flatten().collect::<Vec<_>>();
77        let vertex_input_state_create = vk::PipelineVertexInputStateCreateInfo::builder()
78            .vertex_binding_descriptions(&bindings)
79            .vertex_attribute_descriptions(&attributes)
80            .build();
81
82        //  Input Assembly Info
83        let input_assembly_create = vk::PipelineInputAssemblyStateCreateInfo::builder()
84            .topology(match ext.primitive_topology.unwrap_or_default() {
85                ShaderPrimitiveTopology::PointList => vk::PrimitiveTopology::POINT_LIST,
86                ShaderPrimitiveTopology::LineList => vk::PrimitiveTopology::LINE_LIST,
87                ShaderPrimitiveTopology::LineStrip => vk::PrimitiveTopology::LINE_STRIP,
88                ShaderPrimitiveTopology::TriangleList => vk::PrimitiveTopology::TRIANGLE_LIST,
89                ShaderPrimitiveTopology::TriangleStrip => vk::PrimitiveTopology::TRIANGLE_STRIP,
90            })
91            .primitive_restart_enable(false)
92            .build();
93
94        //  Rasterization Info
95        let raster_create = vk::PipelineRasterizationStateCreateInfo::builder()
96            .depth_bias_enable(false)
97            .rasterizer_discard_enable(false)
98            .polygon_mode(vk::PolygonMode::FILL)
99            .line_width(1.0)
100            .cull_mode(if ext.enable_culling.is_some() {
101                match ext.cull_mode.unwrap_or_default() {
102                    ShaderCullMode::Front => vk::CullModeFlags::FRONT,
103                    ShaderCullMode::Back => vk::CullModeFlags::BACK,
104                }
105            } else {
106                vk::CullModeFlags::NONE
107            })
108            .front_face(match ext.cull_front_face.unwrap_or_default() {
109                ShaderCullFrontFace::Clockwise => vk::FrontFace::CLOCKWISE,
110                ShaderCullFrontFace::CounterClockwise => vk::FrontFace::COUNTER_CLOCKWISE,
111            })
112            .depth_bias_enable(false)
113            .depth_bias_constant_factor(0.0)
114            .depth_bias_clamp(0.0)
115            .depth_bias_slope_factor(0.0)
116            .build();
117
118        //  Multisample Info
119        let multisample_create = vk::PipelineMultisampleStateCreateInfo::builder()
120            .sample_shading_enable(false)
121            .rasterization_samples(sample_count.unwrap_or(vk::SampleCountFlags::TYPE_1))
122            .min_sample_shading(1.0)
123            .sample_mask(&[])
124            .alpha_to_coverage_enable(false)
125            .alpha_to_one_enable(false)
126            .build();
127
128        //  Color Blend Info
129        fn blend_factor_into_vk(factor: ShaderBlendFactor) -> vk::BlendFactor {
130            match factor {
131                ShaderBlendFactor::Zero => vk::BlendFactor::ZERO,
132                ShaderBlendFactor::One => vk::BlendFactor::ONE,
133                ShaderBlendFactor::SrcColor => vk::BlendFactor::SRC_COLOR,
134                ShaderBlendFactor::OneMinusSrcColor => vk::BlendFactor::ONE_MINUS_SRC_COLOR,
135                ShaderBlendFactor::SrcAlpha => vk::BlendFactor::SRC_ALPHA,
136                ShaderBlendFactor::OneMinusSrcAlpha => vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
137                ShaderBlendFactor::DstColor => vk::BlendFactor::DST_COLOR,
138                ShaderBlendFactor::OneMinusDstColor => vk::BlendFactor::ONE_MINUS_DST_COLOR,
139                ShaderBlendFactor::DstAlpha => vk::BlendFactor::DST_ALPHA,
140                ShaderBlendFactor::OneMinusDstAlpha => vk::BlendFactor::ONE_MINUS_DST_ALPHA,
141                ShaderBlendFactor::SrcAlphaSaturated => vk::BlendFactor::SRC_ALPHA_SATURATE,
142                ShaderBlendFactor::ConstantColor => vk::BlendFactor::CONSTANT_COLOR,
143                ShaderBlendFactor::ConstantAlpha => vk::BlendFactor::CONSTANT_ALPHA,
144                ShaderBlendFactor::OneMinusConstantColor => {
145                    vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR
146                }
147                ShaderBlendFactor::OneMinusConstantAlpha => {
148                    vk::BlendFactor::ONE_MINUS_CONSTANT_ALPHA
149                }
150            }
151        }
152        fn blend_op_into_vk(op: ShaderBlendOperation) -> vk::BlendOp {
153            match op {
154                ShaderBlendOperation::Add => vk::BlendOp::ADD,
155                ShaderBlendOperation::Subtract => vk::BlendOp::SUBTRACT,
156                ShaderBlendOperation::ReverseSubtract => vk::BlendOp::REVERSE_SUBTRACT,
157                ShaderBlendOperation::Min => vk::BlendOp::MIN,
158                ShaderBlendOperation::Max => vk::BlendOp::MAX,
159            }
160        }
161        let color_blend_state = vk::PipelineColorBlendAttachmentState::builder()
162            .color_write_mask(vk::ColorComponentFlags::RGBA)
163            .blend_enable(ext.enable_blend.is_some())
164            .src_color_blend_factor(blend_factor_into_vk(
165                ext.blend_color_src_factor.unwrap_or_default(),
166            ))
167            .dst_color_blend_factor(blend_factor_into_vk(
168                ext.blend_color_dst_factor.unwrap_or_default(),
169            ))
170            .src_alpha_blend_factor(blend_factor_into_vk(
171                ext.blend_alpha_src_factor.unwrap_or_default(),
172            ))
173            .dst_alpha_blend_factor(blend_factor_into_vk(
174                ext.blend_alpha_dst_factor.unwrap_or_default(),
175            ))
176            .color_blend_op(blend_op_into_vk(
177                ext.blend_color_operation.unwrap_or_default(),
178            ))
179            .alpha_blend_op(blend_op_into_vk(
180                ext.blend_alpha_operation.unwrap_or_default(),
181            ))
182            .build();
183
184        let color_blend_create = vk::PipelineColorBlendStateCreateInfo::builder()
185            .logic_op_enable(false)
186            .logic_op(vk::LogicOp::COPY)
187            .attachments(&[color_blend_state])
188            .build();
189
190        //  Viewport State
191        let viewport_state = vk::PipelineViewportStateCreateInfo::builder()
192            .viewports(&[])
193            .scissors(&[])
194            .viewport_count(1)
195            .scissor_count(1)
196            .build();
197        let dynamic_state = vk::PipelineDynamicStateCreateInfo::builder()
198            .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR])
199            .build();
200
201        //  Depth Stencil
202        fn stencil_op_into_vk(op: ShaderStencilOp) -> vk::StencilOp {
203            match op {
204                ShaderStencilOp::Keep => vk::StencilOp::KEEP,
205                ShaderStencilOp::Zero => vk::StencilOp::ZERO,
206                ShaderStencilOp::Replace => vk::StencilOp::REPLACE,
207                ShaderStencilOp::IncrementClamp => vk::StencilOp::INCREMENT_AND_CLAMP,
208                ShaderStencilOp::DecrementClamp => vk::StencilOp::DECREMENT_AND_CLAMP,
209                ShaderStencilOp::Invert => vk::StencilOp::INVERT,
210                ShaderStencilOp::IncrementWrap => vk::StencilOp::INCREMENT_AND_WRAP,
211                ShaderStencilOp::DecrementWrap => vk::StencilOp::DECREMENT_AND_WRAP,
212            }
213        }
214
215        fn compare_op_into_vk(op: ShaderCompareOp) -> vk::CompareOp {
216            match op {
217                ShaderCompareOp::Never => vk::CompareOp::NEVER,
218                ShaderCompareOp::Less => vk::CompareOp::LESS,
219                ShaderCompareOp::Equal => vk::CompareOp::EQUAL,
220                ShaderCompareOp::LessOrEqual => vk::CompareOp::LESS_OR_EQUAL,
221                ShaderCompareOp::Greater => vk::CompareOp::GREATER,
222                ShaderCompareOp::NotEqual => vk::CompareOp::NOT_EQUAL,
223                ShaderCompareOp::GreaterOrEqual => vk::CompareOp::GREATER_OR_EQUAL,
224                ShaderCompareOp::Always => vk::CompareOp::ALWAYS,
225            }
226        }
227
228        let stencil_op_state = vk::StencilOpState::builder()
229            .compare_op(compare_op_into_vk(
230                ext.stencil_compare_op.unwrap_or_default(),
231            ))
232            .fail_op(stencil_op_into_vk(ext.stencil_fail.unwrap_or_default()))
233            .pass_op(stencil_op_into_vk(ext.stencil_pass.unwrap_or_default()))
234            .depth_fail_op(stencil_op_into_vk(
235                ext.stencil_depth_fail.unwrap_or_default(),
236            ))
237            .reference(ext.stencil_reference.unwrap_or_default())
238            .compare_mask(ext.stencil_compare_mask.unwrap_or_default())
239            .write_mask(ext.stencil_write_mask.unwrap_or_default())
240            .build();
241
242        let depth_create = vk::PipelineDepthStencilStateCreateInfo::builder()
243            .depth_test_enable(ext.enable_depth_test.is_some())
244            .depth_write_enable(ext.enable_depth_test.is_some())
245            .depth_compare_op(compare_op_into_vk(ext.depth_compare_op.unwrap_or_default()))
246            .depth_bounds_test_enable(false)
247            .stencil_test_enable(ext.enable_stencil_test.is_some())
248            .front(stencil_op_state)
249            .back(stencil_op_state)
250            .min_depth_bounds(0.0)
251            .max_depth_bounds(1.0)
252            .build();
253
254        //  Shader Stage Info
255        let entry_point = CString::new("main").unwrap();
256
257        let shader_stage_creates = self
258            .shaders
259            .iter()
260            .map(|shader| {
261                vk::PipelineShaderStageCreateInfo::builder()
262                    .name(&entry_point)
263                    .stage(match shader.shader_ty {
264                        ShaderType::Vertex(_) => vk::ShaderStageFlags::VERTEX,
265                        ShaderType::Fragment => vk::ShaderStageFlags::FRAGMENT,
266                    })
267                    .module(shader.module)
268                    .build()
269            })
270            .collect::<Vec<_>>();
271
272        //  Graphics Pipeline
273        let graphics_pipeline_create = vk::GraphicsPipelineCreateInfo::builder()
274            .stages(&shader_stage_creates)
275            .vertex_input_state(&vertex_input_state_create)
276            .input_assembly_state(&input_assembly_create)
277            .viewport_state(&viewport_state)
278            .dynamic_state(&dynamic_state)
279            .rasterization_state(&raster_create)
280            .multisample_state(&multisample_create)
281            .color_blend_state(&color_blend_create)
282            .layout(self.layout)
283            .depth_stencil_state(&depth_create)
284            .render_pass(render_pass)
285            .subpass(subpass as u32)
286            .build();
287
288        unsafe {
289            dev.create_graphics_pipelines(
290                vk::PipelineCache::null(),
291                &[graphics_pipeline_create],
292                None,
293            )
294        }
295        .map(|pipelines| pipelines.into_iter().next().unwrap())
296        .map_err(|(_, e)| gpu_api_err!("vulkan graphics pipelines {}", e))
297    }
298}
299
300impl Drop for VkProgram {
301    fn drop(&mut self) {
302        let pipeline_layout = self.layout;
303
304        self.drop_queue
305            .lock()
306            .unwrap()
307            .push(Box::new(move |dev, _| unsafe {
308                dev.destroy_pipeline_layout(pipeline_layout, None);
309            }))
310    }
311}