From 81f1ee1f96f1c342241b995b73b09509b0de7f20 Mon Sep 17 00:00:00 2001 From: Janis Date: Mon, 30 Dec 2024 04:56:07 +0100 Subject: [PATCH] pipeline --- crates/renderer/shaders/compile.sh | 8 + crates/renderer/shaders/egui.slang | 48 +++ crates/renderer/shaders/egui_frag.spv | Bin 0 -> 688 bytes crates/renderer/shaders/egui_vert.spv | Bin 0 -> 1236 bytes crates/renderer/src/commands.rs | 16 + crates/renderer/src/device.rs | 20 +- crates/renderer/src/lib.rs | 4 +- crates/renderer/src/pipeline.rs | 486 ++++++++++++++++++++++++++ 8 files changed, 579 insertions(+), 3 deletions(-) create mode 100755 crates/renderer/shaders/compile.sh create mode 100644 crates/renderer/shaders/egui.slang create mode 100644 crates/renderer/shaders/egui_frag.spv create mode 100644 crates/renderer/shaders/egui_vert.spv create mode 100644 crates/renderer/src/pipeline.rs diff --git a/crates/renderer/shaders/compile.sh b/crates/renderer/shaders/compile.sh new file mode 100755 index 0000000..f080029 --- /dev/null +++ b/crates/renderer/shaders/compile.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +SLANGC="/opt/shader-slang-bin/bin/slangc" + +$SLANGC egui.slang -profile glsl_450 -target spirv -o egui_vert.spv -entry vertex +$SLANGC egui.slang -profile glsl_450 -target spirv -o egui_frag.spv -entry fragment diff --git a/crates/renderer/shaders/egui.slang b/crates/renderer/shaders/egui.slang new file mode 100644 index 0000000..886e09f --- /dev/null +++ b/crates/renderer/shaders/egui.slang @@ -0,0 +1,48 @@ +struct Fragment { + float4 color : SV_Target; +} + +struct VertexIn { + [[vk::layout(0)]] float2 pos; + [[vk::layout(1)]] float2 uv; + [[vk::layout(2)]] float4 color; +} +struct VertexOut { + [[vk::layout(0)]] float4 color; + [[vk::layout(1)]] float2 uv; + float4 position : SV_Position; +} + +struct PushConstant { + float2 screen_size; +} + +[[vk::push_constant]] +ConstantBuffer push_constant; + +[shader("vertex")] +VertexOut vertex(VertexIn vertex) { + VertexOut output; + + output.position = float4( + 2.0 * vertex.pos.x / push_constant.screen_size.x - 1.0, + 2.0 * vertex.pos.y / push_constant.screen_size.y - 1.0, + 0.0, +1.0, + ); + output.color = vertex.color; + output.uv = vertex.uv; + + return output; +} + +[[vk::binding(0)]] +Sampler2D texture; + +[shader("fragment")] +Fragment fragment(VertexOut input) { + Fragment output; + + output.color = input.color * texture.Sample(input.uv); + return output; +} diff --git a/crates/renderer/shaders/egui_frag.spv b/crates/renderer/shaders/egui_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..b79d54ffe2ef1d9ddbe43e3383f74043c877ed0e GIT binary patch literal 688 zcmXw$OH0E*6oqdSYkh!yX-ln0q8nFTicoaxwmX*+G)kaJN=!t5fdAQz;Q1z*TTbts zGxs?OgX@8XJ-acpD*+o@U_JR03mxY?v;*6d4}?6)3NB({Da?eCV!0a#p?=5m6Bj9Kj3`5+-c+7?X>bs*|CH*XB8XMK5f;2(P!*Q&@1|1^ad&5K=Mg) zB$<=!O5RDT`iLfPTpzycBrlVjoy)Ai*02THI#NT{3YY|UG%=rtlQmZN9{HE}1)QQ^?Cij^{ z>v%oh4Rb|*M@X>;q$z) { + unsafe { + self.device + .dev() + .cmd_begin_rendering(self.buffer(), &rendering_info); + } + } + + // pub fn + + pub fn end_rendering(&self) { + unsafe { + self.device.dev().cmd_end_rendering(self.buffer()); + } + } + pub fn submit_fence( &self, wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, diff --git a/crates/renderer/src/device.rs b/crates/renderer/src/device.rs index 85b7d91..b101987 100644 --- a/crates/renderer/src/device.rs +++ b/crates/renderer/src/device.rs @@ -1,6 +1,10 @@ use std::{borrow::Cow, collections::BTreeMap, ops::Deref, sync::Arc}; -use ash::{khr, prelude::VkResult, vk}; +use ash::{ + khr, + prelude::VkResult, + vk::{self, Handle}, +}; use parking_lot::Mutex; use tinyvec::{array_vec, ArrayVec}; @@ -273,8 +277,20 @@ pub struct DeviceOwnedDebugObject { name: Option>, } +impl std::fmt::Debug for DeviceOwnedDebugObject { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct(core::any::type_name::()) + .field_with("device", |f| { + write!(f, "0x{:x}", self.device.0.device.handle().as_raw()) + }) + .field_with("handle", |f| write!(f, "0x{:x}", &self.object.as_raw())) + .field("name", &self.name) + .finish() + } +} + impl DeviceOwnedDebugObject { - fn new>>( + pub fn new>>( device: crate::Device, object: T, name: Option, diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index 79b9503..d67b897 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -3,7 +3,8 @@ closure_lifetime_binder, let_chains, negative_impls, - map_try_insert + map_try_insert, + debug_closure_helpers )] #![allow(unused)] use std::{ @@ -38,6 +39,7 @@ mod buffers; mod commands; mod device; mod images; +mod pipeline; mod render_graph; mod sync; mod util; diff --git a/crates/renderer/src/pipeline.rs b/crates/renderer/src/pipeline.rs new file mode 100644 index 0000000..9bc5a6d --- /dev/null +++ b/crates/renderer/src/pipeline.rs @@ -0,0 +1,486 @@ +use std::{borrow::Cow, path::Path, sync::Arc}; + +use ash::{prelude::*, vk}; + +use crate::device::{Device, DeviceOwnedDebugObject}; + +#[derive(Debug, Default)] +pub struct ShaderStageDesc<'a> { + pub flags: vk::PipelineShaderStageCreateFlags, + pub module: vk::ShaderModule, + pub stage: vk::ShaderStageFlags, + pub entry: Cow<'a, std::ffi::CStr>, + // specialization: Option +} + +#[derive(Debug, Default)] +pub struct DescriptorSetLayoutBindingDesc { + pub binding: u32, + pub count: u32, + pub kind: vk::DescriptorType, + pub stage: vk::ShaderStageFlags, +} + +#[derive(Debug, Default)] +pub struct DescriptorSetLayoutDesc<'a> { + pub flags: vk::DescriptorSetLayoutCreateFlags, + pub bindings: &'a [DescriptorSetLayoutBindingDesc], + pub name: Option>, +} + +#[derive(Debug, Default)] +pub struct PipelineLayoutDesc<'a> { + pub descriptor_set_layouts: &'a [Arc], + pub push_constant_ranges: &'a [vk::PushConstantRange], + pub name: Option>, +} + +#[derive(Debug)] +pub enum PipelineDesc<'a> { + Compute(ComputePipelineDesc<'a>), + Graphics(GraphicsPipelineDesc<'a>), +} + +impl PipelineDesc<'_> { + fn name(self) -> Option> { + match self { + PipelineDesc::Compute(desc) => desc.name, + PipelineDesc::Graphics(desc) => desc.name, + } + } +} + +#[derive(Debug)] +pub struct ComputePipelineDesc<'a> { + pub flags: vk::PipelineCreateFlags, + pub name: Option>, + pub shader_stage: ShaderStageDesc<'a>, + pub layout: Arc, + pub base_pipeline: Option>, +} + +#[derive(Debug, Default)] +pub struct VertexInputState<'a> { + // pub flags: vk::PipelineVertexInputStateCreateFlags, + pub bindings: &'a [vk::VertexInputBindingDescription], + pub attributes: &'a [vk::VertexInputAttributeDescription], +} + +#[derive(Debug, Default)] +pub struct TessellationState { + pub flags: vk::PipelineTessellationStateCreateFlags, + pub patch_control_points: u32, +} + +#[derive(Debug, Default)] +pub struct InputAssemblyState { + // pub flags: vk::PipelineInputAssemblyStateCreateFlags, + pub topology: vk::PrimitiveTopology, + pub primitive_restart: bool, +} + +#[derive(Debug, Default)] +pub struct ViewportState<'a> { + pub num_scissors: u32, + pub scissors: Option<&'a [vk::Rect2D]>, + pub num_viewports: u32, + pub viewports: Option<&'a [vk::Viewport]>, +} + +#[derive(Debug, Default)] +pub struct DepthBiasState { + pub clamp: f32, + pub constant_factor: f32, + pub slope_factor: f32, +} + +#[derive(Debug)] +pub struct RasterizationState { + pub depth_clamp_enable: bool, + pub discard_enable: bool, + pub line_width: f32, + pub cull_mode: vk::CullModeFlags, + pub depth_bias: Option, + pub polygon_mode: vk::PolygonMode, +} + +impl Default for RasterizationState { + fn default() -> Self { + Self { + depth_clamp_enable: false, + line_width: 1.0, + cull_mode: vk::CullModeFlags::BACK, + depth_bias: Default::default(), + polygon_mode: vk::PolygonMode::FILL, + discard_enable: false, + } + } +} + +#[derive(Debug, Default)] +pub struct MultisampleState<'a> { + pub flags: vk::PipelineMultisampleStateCreateFlags, + pub sample_shading_enable: bool, + pub rasterization_samples: vk::SampleCountFlags, + pub min_sample_shading: f32, + pub sample_mask: &'a [vk::SampleMask], + pub alpha_to_coverage_enable: bool, + pub alpha_to_one_enable: bool, +} + +#[derive(Debug)] +pub struct DepthBounds { + pub min: f32, + pub max: f32, +} + +#[derive(Debug, Default)] +pub struct DepthState { + pub write_enable: bool, + /// sets depthTestEnable to true when `Some` + pub compare_op: Option, + /// sets depthBoundsTestEnable to true when `Some` + pub bounds: Option, +} + +#[derive(Debug, Default)] +pub struct StencilState { + pub front: vk::StencilOpState, + pub back: vk::StencilOpState, +} + +#[derive(Debug, Default)] +pub struct DepthStencilState { + pub flags: vk::PipelineDepthStencilStateCreateFlags, + pub depth: Option, + pub stencil: Option, +} + +#[derive(Debug, Default)] +pub struct ColorBlendState<'a> { + pub flags: vk::PipelineColorBlendStateCreateFlags, + pub attachments: &'a [vk::PipelineColorBlendAttachmentState], + pub logic_op: Option, + pub blend_constants: [f32; 4], +} + +#[derive(Debug, Default)] +pub struct DynamicState<'a> { + pub flags: vk::PipelineDynamicStateCreateFlags, + pub dynamic_states: &'a [vk::DynamicState], +} + +#[derive(Debug)] +pub struct GraphicsPipelineDesc<'a> { + pub flags: vk::PipelineCreateFlags, + pub name: Option>, + pub shader_stages: &'a [ShaderStageDesc<'a>], + pub render_pass: Option, + pub layout: Arc, + pub subpass: Option, + pub base_pipeline: Option>, + + pub vertex_input: Option>, + pub input_assembly: Option, + pub tessellation: Option, + pub viewport: Option>, + pub rasterization: Option, + pub multisample: Option>, + pub depth_stencil: Option, + pub color_blend: Option>, + pub dynamic: Option>, +} + +#[derive(Debug)] +pub struct DescriptorSetLayout { + set_layout: DeviceOwnedDebugObject, +} + +impl Drop for DescriptorSetLayout { + fn drop(&mut self) { + unsafe { + self.set_layout + .dev() + .dev() + .destroy_descriptor_set_layout(self.set_layout.handle(), None); + } + } +} + +impl DescriptorSetLayout { + pub fn new(device: Device, desc: DescriptorSetLayoutDesc) -> VkResult { + let bindings = desc + .bindings + .iter() + .map(|binding| { + vk::DescriptorSetLayoutBinding::default() + .binding(binding.binding) + .descriptor_count(binding.count) + .descriptor_type(binding.kind) + .stage_flags(binding.stage) + }) + .collect::>(); + + let info = &vk::DescriptorSetLayoutCreateInfo::default() + .bindings(&bindings) + .flags(desc.flags); + + let layout = unsafe { device.dev().create_descriptor_set_layout(info, None)? }; + + Ok(Self { + set_layout: DeviceOwnedDebugObject::new(device, layout, desc.name)?, + }) + } +} + +#[derive(Debug)] +pub struct PipelineLayout { + pipeline_layout: DeviceOwnedDebugObject, +} + +impl Drop for PipelineLayout { + fn drop(&mut self) { + unsafe { + self.pipeline_layout + .dev() + .dev() + .destroy_pipeline_layout(self.pipeline_layout.handle(), None); + } + } +} + +impl PipelineLayout { + pub fn new(device: Device, desc: PipelineLayoutDesc) -> VkResult { + let set_layouts = desc + .descriptor_set_layouts + .iter() + .map(|desc| desc.set_layout.handle()) + .collect::>(); + let info = &vk::PipelineLayoutCreateInfo::default() + .set_layouts(&set_layouts) + .push_constant_ranges(desc.push_constant_ranges); + let layout = unsafe { device.dev().create_pipeline_layout(info, None)? }; + + Ok(Self { + pipeline_layout: DeviceOwnedDebugObject::new(device, layout, desc.name)?, + }) + } +} + +#[derive(Debug)] +pub struct Pipeline { + pipeline: DeviceOwnedDebugObject, +} + +impl Drop for Pipeline { + fn drop(&mut self) { + unsafe { + self.pipeline + .dev() + .dev() + .destroy_pipeline(self.pipeline.handle(), None); + } + } +} + +impl ShaderStageDesc<'_> { + fn into_create_info(&self) -> vk::PipelineShaderStageCreateInfo { + vk::PipelineShaderStageCreateInfo::default() + .module(self.module) + .flags(self.flags) + .stage(self.stage) + .name(&self.entry) + } +} + +impl Pipeline { + pub fn new(device: Device, desc: PipelineDesc) -> VkResult { + let name: Option>; + let result = match desc { + PipelineDesc::Compute(desc) => { + name = desc.name; + let info = &vk::ComputePipelineCreateInfo::default() + .layout(desc.layout.pipeline_layout.handle()) + .stage(desc.shader_stage.into_create_info()); + + unsafe { + device.dev().create_compute_pipelines( + vk::PipelineCache::null(), + core::slice::from_ref(info), + None, + ) + } + } + PipelineDesc::Graphics(desc) => { + name = desc.name; + + let stages = desc + .shader_stages + .iter() + .map(|stage| stage.into_create_info()) + .collect::>(); + + let vertex_input = desc.vertex_input.map(|vertex| { + vk::PipelineVertexInputStateCreateInfo::default() + .vertex_attribute_descriptions(vertex.attributes) + .vertex_binding_descriptions(vertex.bindings) + }); + let input_assembly = desc.input_assembly.map(|state| { + vk::PipelineInputAssemblyStateCreateInfo::default() + .primitive_restart_enable(state.primitive_restart) + .topology(state.topology) + }); + let tessellation = desc.tessellation.map(|state| { + vk::PipelineTessellationStateCreateInfo::default() + .flags(state.flags) + .patch_control_points(state.patch_control_points) + }); + let viewport = desc.viewport.map(|state| { + let mut info = vk::PipelineViewportStateCreateInfo::default() + .scissor_count(state.num_scissors) + .viewport_count(state.num_viewports); + if let Some(viewports) = state.viewports { + info = info.viewports(viewports); + } + if let Some(scissors) = state.scissors { + info = info.scissors(scissors); + } + + info + }); + + let rasterization = desc.rasterization.map(|state| { + let mut info = vk::PipelineRasterizationStateCreateInfo::default() + .line_width(state.line_width) + .cull_mode(state.cull_mode) + .polygon_mode(state.polygon_mode) + .rasterizer_discard_enable(state.discard_enable) + .depth_clamp_enable(state.depth_clamp_enable); + + if let Some(depth_bias) = state.depth_bias { + info = info + .depth_bias_enable(true) + .depth_bias_clamp(depth_bias.clamp) + .depth_bias_constant_factor(depth_bias.constant_factor) + .depth_bias_slope_factor(depth_bias.slope_factor); + } + + info + }); + + let multisample = desc.multisample.map(|state| { + let mut info = vk::PipelineMultisampleStateCreateInfo::default() + .flags(state.flags) + .min_sample_shading(state.min_sample_shading) + .rasterization_samples(state.rasterization_samples) + .sample_mask(state.sample_mask) + .alpha_to_coverage_enable(state.alpha_to_coverage_enable) + .alpha_to_one_enable(state.alpha_to_one_enable); + + info + }); + + let color_blend = desc.color_blend.map(|state| { + let mut info = vk::PipelineColorBlendStateCreateInfo::default() + .flags(state.flags) + .attachments(state.attachments) + .blend_constants(state.blend_constants) + .logic_op(state.logic_op.unwrap_or(Default::default())) + .logic_op_enable(state.logic_op.is_some()); + + info + }); + + let depth_stencil = desc.depth_stencil.map(|state| { + let mut info = + vk::PipelineDepthStencilStateCreateInfo::default().flags(state.flags); + + if let Some(depth) = state.depth { + info = info + .depth_compare_op(depth.compare_op.unwrap_or(vk::CompareOp::default())) + .depth_test_enable(depth.compare_op.is_some()) + .depth_write_enable(depth.write_enable) + .depth_bounds_test_enable(depth.bounds.is_some()); + if let Some(bounds) = depth.bounds { + info = info + .max_depth_bounds(bounds.max) + .min_depth_bounds(bounds.min); + } + } + + if let Some(stencil) = state.stencil { + info = info + .stencil_test_enable(true) + .front(stencil.front) + .back(stencil.back); + } + + info + }); + + let dynamic = desc.dynamic.map(|state| { + let mut info = vk::PipelineDynamicStateCreateInfo::default() + .flags(state.flags) + .dynamic_states(state.dynamic_states); + + info + }); + + fn option_to_ptr(option: &Option) -> *const T { + option + .as_ref() + .map(|t| t as *const T) + .unwrap_or(core::ptr::null()) + } + + let info = &vk::GraphicsPipelineCreateInfo { + flags: desc.flags, + stage_count: stages.len() as u32, + p_stages: stages.as_ptr(), + p_vertex_input_state: option_to_ptr(&vertex_input), + p_input_assembly_state: option_to_ptr(&input_assembly), + p_tessellation_state: option_to_ptr(&tessellation), + p_viewport_state: option_to_ptr(&viewport), + p_rasterization_state: option_to_ptr(&rasterization), + p_multisample_state: option_to_ptr(&multisample), + p_depth_stencil_state: option_to_ptr(&depth_stencil), + p_color_blend_state: option_to_ptr(&color_blend), + p_dynamic_state: option_to_ptr(&dynamic), + layout: desc.layout.pipeline_layout.handle(), + render_pass: desc.render_pass.unwrap_or(vk::RenderPass::null()), + subpass: desc.subpass.unwrap_or(0), + base_pipeline_handle: desc + .base_pipeline + .map(|piepline| piepline.pipeline.handle()) + .unwrap_or(vk::Pipeline::null()), + ..Default::default() + }; + + unsafe { + device.dev().create_graphics_pipelines( + vk::PipelineCache::null(), + core::slice::from_ref(info), + None, + ) + } + } + }; + + let pipeline = match result { + Ok(pipelines) => pipelines[0], + Err((pipelines, error)) => { + tracing::error!("failed to create pipelines with :{error}"); + for pipeline in pipelines { + unsafe { + device.dev().destroy_pipeline(pipeline, None); + } + } + return Err(error.into()); + } + }; + + Ok(Self { + pipeline: DeviceOwnedDebugObject::new(device, pipeline, name)?, + }) + } +}