1use super::shader::ShaderModule;
2use crate::render::gfx::types::{Format, PushConstantRange};
3use ash::vk;
4
5pub struct PipelineBuilder<'a> {
6 vert_spv: &'a [u8],
7 frag_spv: Option<&'a [u8]>,
8 color_formats: &'a [Format],
9 depth_format: vk::Format,
10 cull_mode: vk::CullModeFlags,
11 depth_test: bool,
12 depth_write: bool,
13 depth_compare: vk::CompareOp,
14 depth_bias: Option<DepthBias>,
15 vertex_bindings: &'a [vk::VertexInputBindingDescription],
16 vertex_attributes: &'a [vk::VertexInputAttributeDescription],
17 set_layouts: &'a [vk::DescriptorSetLayout],
18 push_constant_ranges: &'a [PushConstantRange],
19 blend_attachments: Option<&'a [vk::PipelineColorBlendAttachmentState]>,
20}
21
22#[derive(Clone, Copy)]
23pub struct DepthBias {
24 pub constant_factor: f32,
25 pub slope_factor: f32,
26}
27
28impl<'a> PipelineBuilder<'a> {
29 pub fn fullscreen(vert_spv: &'a [u8], frag_spv: &'a [u8], color_formats: &'a [Format]) -> Self {
30 Self {
31 vert_spv,
32 frag_spv: Some(frag_spv),
33 color_formats,
34 depth_format: vk::Format::UNDEFINED,
35 cull_mode: vk::CullModeFlags::NONE,
36 depth_test: false,
37 depth_write: false,
38 depth_compare: vk::CompareOp::ALWAYS,
39 depth_bias: None,
40 vertex_bindings: &[],
41 vertex_attributes: &[],
42 set_layouts: &[],
43 push_constant_ranges: &[],
44 blend_attachments: None,
45 }
46 }
47
48 pub fn mesh(
49 vert_spv: &'a [u8],
50 frag_spv: &'a [u8],
51 color_formats: &'a [Format],
52 vertex_bindings: &'a [vk::VertexInputBindingDescription],
53 vertex_attributes: &'a [vk::VertexInputAttributeDescription],
54 ) -> Self {
55 Self {
56 vert_spv,
57 frag_spv: Some(frag_spv),
58 color_formats,
59 depth_format: vk::Format::D32_SFLOAT,
60 cull_mode: vk::CullModeFlags::BACK,
61 depth_test: true,
62 depth_write: true,
63 depth_compare: vk::CompareOp::LESS,
64 depth_bias: None,
65 vertex_bindings,
66 vertex_attributes,
67 set_layouts: &[],
68 push_constant_ranges: &[],
69 blend_attachments: None,
70 }
71 }
72
73 pub fn depth_only(
74 vert_spv: &'a [u8],
75 frag_spv: Option<&'a [u8]>,
76 vertex_bindings: &'a [vk::VertexInputBindingDescription],
77 vertex_attributes: &'a [vk::VertexInputAttributeDescription],
78 ) -> Self {
79 Self {
80 vert_spv,
81 frag_spv,
82 color_formats: &[],
83 depth_format: vk::Format::D32_SFLOAT,
84 cull_mode: vk::CullModeFlags::NONE,
85 depth_test: true,
86 depth_write: true,
87 depth_compare: vk::CompareOp::LESS_OR_EQUAL,
88 depth_bias: None,
89 vertex_bindings,
90 vertex_attributes,
91 set_layouts: &[],
92 push_constant_ranges: &[],
93 blend_attachments: None,
94 }
95 }
96
97 pub fn cull_mode(mut self, mode: vk::CullModeFlags) -> Self {
98 self.cull_mode = mode;
99 self
100 }
101
102 pub fn depth_test(mut self, test: bool, write: bool) -> Self {
103 self.depth_test = test;
104 self.depth_write = write;
105 self
106 }
107
108 pub fn depth_format(mut self, format: vk::Format) -> Self {
109 self.depth_format = format;
110 self
111 }
112
113 pub fn depth_compare(mut self, op: vk::CompareOp) -> Self {
114 self.depth_compare = op;
115 self
116 }
117
118 pub fn depth_bias(mut self, constant_factor: f32, slope_factor: f32) -> Self {
119 self.depth_bias = Some(DepthBias { constant_factor, slope_factor });
120 self
121 }
122
123 pub fn set_layouts(mut self, layouts: &'a [vk::DescriptorSetLayout]) -> Self {
124 self.set_layouts = layouts;
125 self
126 }
127
128 pub fn push_constants(mut self, ranges: &'a [PushConstantRange]) -> Self {
129 self.push_constant_ranges = ranges;
130 self
131 }
132
133 pub fn blend_attachments(mut self, attachments: &'a [vk::PipelineColorBlendAttachmentState]) -> Self {
134 self.blend_attachments = Some(attachments);
135 self
136 }
137
138 pub fn build(self, device: &ash::Device) -> anyhow::Result<(vk::Pipeline, vk::PipelineLayout)> {
139 let vert = ShaderModule::from_bytes(device, self.vert_spv)?;
140 let frag = self.frag_spv.map(|spv| ShaderModule::from_bytes(device, spv)).transpose()?;
141
142 let entry = c"main";
143 let mut stages = vec![vk::PipelineShaderStageCreateInfo::default()
144 .stage(vk::ShaderStageFlags::VERTEX)
145 .module(vert.handle)
146 .name(entry)];
147 if let Some(ref f) = frag {
148 stages.push(
149 vk::PipelineShaderStageCreateInfo::default()
150 .stage(vk::ShaderStageFlags::FRAGMENT)
151 .module(f.handle)
152 .name(entry),
153 );
154 }
155
156 let vertex_input = vk::PipelineVertexInputStateCreateInfo::default()
157 .vertex_binding_descriptions(self.vertex_bindings)
158 .vertex_attribute_descriptions(self.vertex_attributes);
159
160 let input_assembly =
161 vk::PipelineInputAssemblyStateCreateInfo::default().topology(vk::PrimitiveTopology::TRIANGLE_LIST);
162
163 let viewport_state = vk::PipelineViewportStateCreateInfo::default().viewport_count(1).scissor_count(1);
164
165 let mut rasterizer = vk::PipelineRasterizationStateCreateInfo::default()
166 .polygon_mode(vk::PolygonMode::FILL)
167 .cull_mode(self.cull_mode)
168 .front_face(vk::FrontFace::COUNTER_CLOCKWISE)
169 .line_width(1.0);
170 if let Some(bias) = self.depth_bias {
171 rasterizer = rasterizer
172 .depth_bias_enable(true)
173 .depth_bias_constant_factor(bias.constant_factor)
174 .depth_bias_slope_factor(bias.slope_factor);
175 }
176
177 let multisampling =
178 vk::PipelineMultisampleStateCreateInfo::default().rasterization_samples(vk::SampleCountFlags::TYPE_1);
179
180 let default_blend: Vec<vk::PipelineColorBlendAttachmentState>;
181 let blend_attachments = if let Some(explicit) = self.blend_attachments {
182 explicit
183 } else {
184 default_blend = self
185 .color_formats
186 .iter()
187 .map(|_| {
188 vk::PipelineColorBlendAttachmentState::default().color_write_mask(vk::ColorComponentFlags::RGBA)
189 })
190 .collect();
191 &default_blend
192 };
193
194 let color_blending = vk::PipelineColorBlendStateCreateInfo::default().attachments(blend_attachments);
195
196 let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR];
197 let dynamic_state = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
198
199 let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default()
200 .depth_test_enable(self.depth_test)
201 .depth_write_enable(self.depth_write)
202 .depth_compare_op(self.depth_compare)
203 .depth_bounds_test_enable(false)
204 .stencil_test_enable(false);
205
206 let vk_ranges: Vec<vk::PushConstantRange> = self.push_constant_ranges.iter().map(|r| r.to_vk()).collect();
207
208 let layout = unsafe {
209 device.create_pipeline_layout(
210 &vk::PipelineLayoutCreateInfo::default().set_layouts(self.set_layouts).push_constant_ranges(&vk_ranges),
211 None,
212 )?
213 };
214
215 let vk_formats: Vec<vk::Format> = self.color_formats.iter().map(|format| format.to_vk()).collect();
216 let mut rendering_info = vk::PipelineRenderingCreateInfo::default()
217 .color_attachment_formats(vk_formats.as_slice())
218 .depth_attachment_format(self.depth_format);
219
220 let pipeline_info = vk::GraphicsPipelineCreateInfo::default()
221 .stages(&stages)
222 .vertex_input_state(&vertex_input)
223 .input_assembly_state(&input_assembly)
224 .viewport_state(&viewport_state)
225 .rasterization_state(&rasterizer)
226 .multisample_state(&multisampling)
227 .color_blend_state(&color_blending)
228 .dynamic_state(&dynamic_state)
229 .depth_stencil_state(&depth_stencil)
230 .layout(layout)
231 .push_next(&mut rendering_info);
232
233 let pipeline = unsafe {
234 device
235 .create_graphics_pipelines(vk::PipelineCache::null(), std::slice::from_ref(&pipeline_info), None)
236 .map_err(|(_, e)| {
237 device.destroy_pipeline_layout(layout, None);
238 e
239 })?[0]
240 };
241
242 Ok((pipeline, layout))
243 }
244}
245
246pub mod cmd {
247 use ash::vk;
248
249 pub fn begin_rendering_clear(
250 device: &ash::Device,
251 cmd: vk::CommandBuffer,
252 view: vk::ImageView,
253 extent: vk::Extent2D,
254 clear: [f32; 4],
255 ) {
256 let color_attachment = color_attachment_clear(view, clear);
257 begin_rendering_impl(device, cmd, &[color_attachment], None, extent);
258 }
259
260 pub fn begin_rendering_discard(
261 device: &ash::Device,
262 cmd: vk::CommandBuffer,
263 view: vk::ImageView,
264 extent: vk::Extent2D,
265 ) {
266 let color_attachment = vk::RenderingAttachmentInfo::default()
267 .image_view(view)
268 .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
269 .load_op(vk::AttachmentLoadOp::DONT_CARE)
270 .store_op(vk::AttachmentStoreOp::STORE);
271 begin_rendering_impl(device, cmd, &[color_attachment], None, extent);
272 }
273
274 pub fn begin_rendering_load(
275 device: &ash::Device,
276 cmd: vk::CommandBuffer,
277 view: vk::ImageView,
278 extent: vk::Extent2D,
279 ) {
280 let color_attachment = vk::RenderingAttachmentInfo::default()
281 .image_view(view)
282 .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
283 .load_op(vk::AttachmentLoadOp::LOAD)
284 .store_op(vk::AttachmentStoreOp::STORE);
285 begin_rendering_impl(device, cmd, &[color_attachment], None, extent);
286 }
287
288 pub fn begin_rendering_with_depth(
289 device: &ash::Device,
290 cmd: vk::CommandBuffer,
291 color_views: &[(vk::ImageView, [f32; 4])],
292 depth_view: vk::ImageView,
293 depth_load_op: vk::AttachmentLoadOp,
294 extent: vk::Extent2D,
295 ) {
296 let color_attachments: Vec<_> =
297 color_views.iter().map(|&(view, clear)| color_attachment_clear(view, clear)).collect();
298
299 let depth_attachment = vk::RenderingAttachmentInfo::default()
300 .image_view(depth_view)
301 .image_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
302 .load_op(depth_load_op)
303 .store_op(vk::AttachmentStoreOp::STORE)
304 .clear_value(vk::ClearValue { depth_stencil: vk::ClearDepthStencilValue { depth: 1.0, stencil: 0 } });
305
306 begin_rendering_impl(device, cmd, &color_attachments, Some(depth_attachment), extent);
307 }
308
309 pub fn begin_rendering_depth_only(
310 device: &ash::Device,
311 cmd: vk::CommandBuffer,
312 depth_view: vk::ImageView,
313 extent: vk::Extent2D,
314 ) {
315 let depth_attachment = depth_attachment_clear(depth_view);
316 begin_rendering_impl(device, cmd, &[], Some(depth_attachment), extent);
317 }
318
319 fn begin_rendering_impl(
320 device: &ash::Device,
321 cmd: vk::CommandBuffer,
322 color_attachments: &[vk::RenderingAttachmentInfo],
323 depth_attachment: Option<vk::RenderingAttachmentInfo>,
324 extent: vk::Extent2D,
325 ) {
326 let mut rendering_info = vk::RenderingInfo::default()
327 .render_area(vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent })
328 .layer_count(1)
329 .color_attachments(color_attachments);
330
331 if let Some(ref depth) = depth_attachment {
332 rendering_info = rendering_info.depth_attachment(depth);
333 }
334
335 unsafe {
336 device.cmd_begin_rendering(cmd, &rendering_info);
337 set_full_viewport_scissor(device, cmd, extent);
338 }
339 }
340
341 pub fn set_full_viewport_scissor(device: &ash::Device, cmd: vk::CommandBuffer, extent: vk::Extent2D) {
342 unsafe {
343 device.cmd_set_viewport(
344 cmd,
345 0,
346 &[vk::Viewport {
347 x: 0.0,
348 y: 0.0,
349 width: extent.width as f32,
350 height: extent.height as f32,
351 min_depth: 0.0,
352 max_depth: 1.0,
353 }],
354 );
355 device.cmd_set_scissor(cmd, 0, &[vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent }]);
356 }
357 }
358
359 pub fn color_attachment_clear(view: vk::ImageView, clear: [f32; 4]) -> vk::RenderingAttachmentInfo<'static> {
360 vk::RenderingAttachmentInfo::default()
361 .image_view(view)
362 .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
363 .load_op(vk::AttachmentLoadOp::CLEAR)
364 .store_op(vk::AttachmentStoreOp::STORE)
365 .clear_value(vk::ClearValue { color: vk::ClearColorValue { float32: clear } })
366 }
367
368 pub fn depth_attachment_clear(view: vk::ImageView) -> vk::RenderingAttachmentInfo<'static> {
369 vk::RenderingAttachmentInfo::default()
370 .image_view(view)
371 .image_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
372 .load_op(vk::AttachmentLoadOp::CLEAR)
373 .store_op(vk::AttachmentStoreOp::STORE)
374 .clear_value(vk::ClearValue { depth_stencil: vk::ClearDepthStencilValue { depth: 1.0, stencil: 0 } })
375 }
376}
377
378pub mod descriptor {
379 use ash::vk;
380
381 pub fn alloc_sets(
382 device: &ash::Device,
383 layout: vk::DescriptorSetLayout,
384 pool_sizes: &[vk::DescriptorPoolSize],
385 count: u32,
386 ) -> anyhow::Result<(vk::DescriptorPool, Vec<vk::DescriptorSet>)> {
387 let pool = unsafe {
388 device.create_descriptor_pool(
389 &vk::DescriptorPoolCreateInfo::default().pool_sizes(pool_sizes).max_sets(count),
390 None,
391 )?
392 };
393
394 let layouts = vec![layout; count as usize];
395 let sets = unsafe {
396 device.allocate_descriptor_sets(
397 &vk::DescriptorSetAllocateInfo::default().descriptor_pool(pool).set_layouts(&layouts),
398 )?
399 };
400
401 Ok((pool, sets))
402 }
403
404 pub fn alloc_single_set(
405 device: &ash::Device,
406 layout: vk::DescriptorSetLayout,
407 pool_sizes: &[vk::DescriptorPoolSize],
408 ) -> anyhow::Result<(vk::DescriptorPool, vk::DescriptorSet)> {
409 let (pool, mut sets) = alloc_sets(device, layout, pool_sizes, 1)?;
410 Ok((pool, sets.remove(0)))
411 }
412}