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 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 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 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 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 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 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 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 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 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 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}