Skip to main content

ursus_core/vulkan/gfx_pipeline/
builder.rs

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}