summaryrefslogtreecommitdiff
path: root/engine/src/rendering/shader.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/rendering/shader.rs')
-rw-r--r--engine/src/rendering/shader.rs1017
1 files changed, 206 insertions, 811 deletions
diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs
index a4edbc0..926302a 100644
--- a/engine/src/rendering/shader.rs
+++ b/engine/src/rendering/shader.rs
@@ -1,6 +1,5 @@
use std::any::type_name;
use std::borrow::Cow;
-use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Display, Write};
use std::path::Path;
use std::str::Utf8Error;
@@ -15,10 +14,7 @@ use shader_slang::{
EntryPoint as SlangEntryPoint,
GlobalSession as SlangGlobalSession,
Module as SlangModule,
- ParameterCategory as SlangParameterCategory,
- ScalarType as SlangScalarType,
Session as SlangSession,
- TypeKind as SlangTypeKind,
};
use crate::asset::{
@@ -39,6 +35,7 @@ use crate::ecs::{declare_entity, pair, Sole};
use crate::mesh::vertex_buffer::VertexLabel;
pub mod cursor;
+pub mod reflection;
pub const STD_VERT_IN_SEM_NAME_POSITION: &str = "STD_POSITION";
pub const STD_VERT_IN_SEM_NAME_NORMAL: &str = "STD_NORMAL";
@@ -113,68 +110,14 @@ pub struct EntryPoint
impl EntryPoint
{
#[must_use]
- pub fn function(&self) -> FunctionReflection<'_>
+ pub fn function_reflection(&self) -> self::reflection::Function<'_>
{
- FunctionReflection {
+ self::reflection::Function {
inner: self.inner.function_reflection(),
}
}
}
-pub struct FunctionReflection<'a>
-{
- inner: &'a shader_slang::reflection::Function,
-}
-
-impl FunctionReflection<'_>
-{
- #[must_use]
- pub fn name(&self) -> Option<&str>
- {
- self.inner.name()
- }
-}
-
-pub struct EntryPointReflection<'a>
-{
- inner: &'a shader_slang::reflection::EntryPoint,
-}
-
-impl<'a> EntryPointReflection<'a>
-{
- #[must_use]
- pub fn name(&self) -> Option<&str>
- {
- self.inner.name()
- }
-
- #[must_use]
- pub fn name_override(&self) -> Option<&str>
- {
- self.inner.name_override()
- }
-
- #[must_use]
- pub fn stage(&self) -> Stage
- {
- Stage::from_slang_stage(self.inner.stage())
- }
-
- #[must_use]
- pub fn parameters(&self) -> impl ExactSizeIterator<Item = VariableLayout<'a>>
- {
- self.inner
- .parameters()
- .map(|param| VariableLayout { inner: param })
- }
-
- #[must_use]
- pub fn var_layout(&self) -> Option<VariableLayout<'a>>
- {
- Some(VariableLayout { inner: self.inner.var_layout()? })
- }
-}
-
#[derive(Clone)]
pub struct Program
{
@@ -200,641 +143,61 @@ impl Program
&self.metadata
}
- pub fn get_entry_point_code(&self, entry_point_index: u32) -> Result<Blob, Error>
- {
- let blob = self.inner.entry_point_code(entry_point_index.into(), 0)?;
-
- Ok(Blob { inner: blob })
- }
-
- pub fn reflection(&self, target: u32) -> Result<ProgramReflection<'_>, Error>
- {
- let reflection = self.inner.layout(i64::from(target))?;
-
- Ok(ProgramReflection { inner: reflection })
- }
-}
-
-impl Debug for Program
-{
- fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
- {
- formatter
- .debug_struct(type_name::<Self>())
- .finish_non_exhaustive()
- }
-}
-
-pub struct ProgramReflection<'a>
-{
- inner: &'a shader_slang::reflection::Shader,
-}
-
-impl<'a> ProgramReflection<'a>
-{
- #[must_use]
- pub fn get_entry_point_by_index(&self, index: u32)
- -> Option<EntryPointReflection<'a>>
- {
- Some(EntryPointReflection {
- inner: self.inner.entry_point_by_index(index)?,
- })
- }
-
- #[must_use]
- pub fn get_entry_point_by_name(&self, name: &str)
- -> Option<EntryPointReflection<'a>>
- {
- Some(EntryPointReflection {
- inner: self.inner.find_entry_point_by_name(name)?,
- })
- }
-
- #[must_use]
- pub fn entry_points(
+ pub fn get_entry_point_code(
&self,
- ) -> impl ExactSizeIterator<Item = EntryPointReflection<'a>> + use<'a>
- {
- self.inner
- .entry_points()
- .map(|entry_point| EntryPointReflection { inner: entry_point })
- }
-
- #[must_use]
- pub fn global_params_type_layout(&self) -> Option<TypeLayout<'a>>
- {
- Some(TypeLayout {
- inner: self.inner.global_params_type_layout()?,
- })
- }
-
- #[must_use]
- pub fn global_params_var_layout(&self) -> Option<VariableLayout<'a>>
- {
- Some(VariableLayout {
- inner: self.inner.global_params_var_layout()?,
- })
- }
-
- #[must_use]
- pub fn get_type(&self, name: &str) -> Option<TypeReflection<'a>>
- {
- Some(TypeReflection {
- inner: self.inner.find_type_by_name(name)?,
- })
- }
-
- #[must_use]
- pub fn get_type_layout(&self, ty: &TypeReflection<'a>) -> Option<TypeLayout<'a>>
- {
- Some(TypeLayout {
- inner: self
- .inner
- .type_layout(ty.inner, shader_slang::LayoutRules::Default)?,
- })
- }
-}
-
-#[derive(Clone, Copy)]
-pub struct VariableLayout<'a>
-{
- inner: &'a shader_slang::reflection::VariableLayout,
-}
-
-impl<'a> VariableLayout<'a>
-{
- #[must_use]
- pub fn name(&self) -> Option<&'a str>
+ entry_point_index: u32,
+ target_id: CompileTargetId,
+ ) -> Result<Blob, Error>
{
- self.inner.name()
- }
-
- #[must_use]
- pub fn semantic_name(&self) -> Option<&str>
- {
- self.inner.semantic_name()
- }
-
- #[must_use]
- pub fn binding_index(&self) -> u32
- {
- self.inner
- .offset(shader_slang::ParameterCategory::DescriptorTableSlot) as u32
-
- // self.inner.binding_index()
- }
-
- #[must_use]
- pub fn varying_input_offset(&self) -> Option<usize>
- {
- if !self
+ let blob = self
.inner
- .categories()
- .any(|category| category == SlangParameterCategory::VaryingInput)
- {
- return None;
- }
-
- Some(self.inner.offset(SlangParameterCategory::VaryingInput))
- }
-
- #[must_use]
- pub fn binding_space(&self) -> u32
- {
- self.inner.binding_space()
- }
-
- #[must_use]
- pub fn semantic_index(&self) -> usize
- {
- self.inner.semantic_index()
- }
-
- #[must_use]
- pub fn offset(&self) -> usize
- {
- self.inner.offset(shader_slang::ParameterCategory::Uniform)
- }
-
- #[must_use]
- pub fn ty(&self) -> Option<TypeReflection<'a>>
- {
- self.inner.ty().map(|ty| TypeReflection { inner: ty })
- }
-
- #[must_use]
- pub fn type_layout(&self) -> Option<TypeLayout<'a>>
- {
- Some(TypeLayout { inner: self.inner.type_layout()? })
- }
-}
-
-#[derive(Clone, Copy)]
-pub struct TypeLayout<'a>
-{
- inner: &'a shader_slang::reflection::TypeLayout,
-}
-
-impl<'a> TypeLayout<'a>
-{
- #[must_use]
- pub fn kind(&self) -> TypeKind
- {
- TypeKind::from_slang_type_kind(self.inner.kind())
- }
+ .entry_point_code(entry_point_index.into(), i64::from(target_id.index))?;
- #[must_use]
- pub fn scalar_type(&self) -> Option<ScalarType>
- {
- Some(ScalarType::from_slang_scalar_type(
- self.inner.scalar_type()?,
- ))
- }
-
- #[must_use]
- pub fn resource_shape(&self) -> Option<ResourceShape>
- {
- Some(ResourceShape::from_bits_retain(
- self.inner.resource_shape()? as u32,
- ))
- }
-
- #[must_use]
- pub fn get_field_by_name(&self, name: &str) -> Option<VariableLayout<'a>>
- {
- let index = self.inner.find_field_index_by_name(name);
-
- if index < 0 {
- return None;
- }
-
- let index = u32::try_from(index.cast_unsigned()).expect("Should not happend");
-
- let field = self.inner.field_by_index(index)?;
-
- Some(VariableLayout { inner: field })
- }
-
- #[must_use]
- pub fn parameter_category(&self) -> ParameterCategory
- {
- ParameterCategory::from_slang_parameter_category(self.inner.parameter_category())
- }
-
- #[must_use]
- pub fn binding_range_descriptor_set_index(&self, index: i64) -> i64
- {
- self.inner.binding_range_descriptor_set_index(index)
- }
-
- #[must_use]
- pub fn get_field_binding_range_offset_by_name(&self, name: &str) -> Option<u64>
- {
- let field_index = self.inner.find_field_index_by_name(name);
-
- if field_index < 0 {
- return None;
- }
-
- let field_binding_range_offset =
- self.inner.field_binding_range_offset(field_index);
-
- if field_binding_range_offset < 0 {
- return None;
- }
-
- Some(field_binding_range_offset.cast_unsigned())
- }
-
- #[must_use]
- pub fn ty(&self) -> Option<TypeReflection<'a>>
- {
- self.inner.ty().map(|ty| TypeReflection { inner: ty })
- }
-
- #[must_use]
- pub fn fields(&self) -> FieldIter<'a>
- {
- FieldIter {
- type_layout: *self,
- cnt: self.field_cnt(),
- index: 0,
- }
- }
-
- #[must_use]
- pub fn field_cnt(&self) -> u32
- {
- self.inner.field_count()
- }
-
- #[must_use]
- pub fn element_cnt(&self) -> Option<usize>
- {
- self.inner.element_count()
- }
-
- #[must_use]
- pub fn row_cnt(&self) -> Option<u32>
- {
- self.inner.row_count()
- }
-
- #[must_use]
- pub fn column_cnt(&self) -> Option<u32>
- {
- self.inner.column_count()
- }
-
- #[must_use]
- pub fn element_type_layout(&self) -> Option<TypeLayout<'a>>
- {
- self.inner
- .element_type_layout()
- .map(|type_layout| TypeLayout { inner: type_layout })
+ Ok(Blob { inner: blob })
}
- #[must_use]
- pub fn element_var_layout(&self) -> Option<VariableLayout<'a>>
+ pub fn reflection(
+ &self,
+ target_id: CompileTargetId,
+ ) -> Result<self::reflection::Program<'_>, Error>
{
- self.inner
- .element_var_layout()
- .map(|var_layout| VariableLayout { inner: var_layout })
- }
+ let reflection = self.inner.layout(i64::from(target_id.index))?;
- #[must_use]
- pub fn container_var_layout(&self) -> Option<VariableLayout<'a>>
- {
- self.inner
- .container_var_layout()
- .map(|var_layout| VariableLayout { inner: var_layout })
+ Ok(self::reflection::Program { inner: reflection })
}
- #[must_use]
- pub fn uniform_size(&self) -> Option<usize>
+ pub fn create_vertex_desc(
+ &self,
+ target_id: CompileTargetId,
+ ) -> Result<VertexDescription, VertexDescCreationError>
{
- // tracing::debug!(
- // "uniform_size: {:?} categories: {:?}",
- // self.inner.name(),
- // self.inner.categories().collect::<Vec<_>>(),
- // );
-
if !self
- .inner
- .categories()
- .any(|category| category == SlangParameterCategory::Uniform)
+ .metadata
+ .link_entrypoints
+ .contains(EntrypointFlags::VERTEX)
{
- return None;
+ return Err(VertexDescCreationError::NoVertexStageEntrypointInProgram);
}
- // let category = self.inner.categories().next().unwrap();
-
- // println!(
- // "AARGH size Category: {category:?} Category count: {}",
- // self.inner.category_count()
- // );
+ let program_reflection = self
+ .reflection(target_id)
+ .map_err(VertexDescCreationError::GetProgramReflectionFailed)?;
- // Some(self.inner.size(category))
-
- Some(self.inner.size(SlangParameterCategory::Uniform))
- }
-
- #[must_use]
- pub fn stride(&self) -> usize
- {
- self.inner.stride(self.inner.categories().next().unwrap())
+ VertexDescription::new(
+ &program_reflection
+ .get_entry_point_by_name("vertex_main")
+ .expect("Not possible"),
+ )
}
}
-pub struct FieldIter<'a>
-{
- type_layout: TypeLayout<'a>,
- cnt: u32,
- index: u32,
-}
-
-impl<'a> Iterator for FieldIter<'a>
-{
- type Item = VariableLayout<'a>;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- if self.index == self.cnt {
- return None;
- }
-
- let Some(field) = self.type_layout.inner.field_by_index(self.index) else {
- unreachable!();
- };
-
- self.index += 1;
-
- Some(VariableLayout { inner: field })
- }
-
- fn size_hint(&self) -> (usize, Option<usize>)
- {
- let len = (self.cnt - self.index) as usize;
-
- (len, Some(len))
- }
-}
-
-impl ExactSizeIterator for FieldIter<'_>
-{
- fn len(&self) -> usize
- {
- (self.cnt - self.index) as usize
- }
-}
-
-impl DoubleEndedIterator for FieldIter<'_>
-{
- fn next_back(&mut self) -> Option<Self::Item>
- {
- if self.cnt == 0 || self.index == self.cnt - 1 {
- return None;
- }
-
- let Some(field) = self.type_layout.inner.field_by_index(self.cnt - 1) else {
- unreachable!();
- };
-
- self.cnt -= 1;
-
- Some(VariableLayout { inner: field })
- }
-}
-
-pub struct TypeReflection<'a>
-{
- inner: &'a shader_slang::reflection::Type,
-}
-
-impl TypeReflection<'_>
-{
- #[must_use]
- pub fn kind(&self) -> TypeKind
- {
- TypeKind::from_slang_type_kind(self.inner.kind())
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[non_exhaustive]
-pub enum TypeKind
-{
- None,
- Struct,
- Enum,
- Array,
- Matrix,
- Vector,
- Scalar,
- ConstantBuffer,
- Resource,
- SamplerState,
- TextureBuffer,
- ShaderStorageBuffer,
- ParameterBlock,
- GenericTypeParameter,
- Interface,
- OutputStream,
- MeshOutput,
- Specialized,
- Feedback,
- Pointer,
- DynamicResource,
- Count,
-}
-
-impl TypeKind
-{
- fn from_slang_type_kind(type_kind: SlangTypeKind) -> Self
- {
- match type_kind {
- SlangTypeKind::None => Self::None,
- SlangTypeKind::Struct => Self::Struct,
- SlangTypeKind::Enum => Self::Enum,
- SlangTypeKind::Array => Self::Array,
- SlangTypeKind::Matrix => Self::Matrix,
- SlangTypeKind::Vector => Self::Vector,
- SlangTypeKind::Scalar => Self::Scalar,
- SlangTypeKind::ConstantBuffer => Self::ConstantBuffer,
- SlangTypeKind::Resource => Self::Resource,
- SlangTypeKind::SamplerState => Self::SamplerState,
- SlangTypeKind::TextureBuffer => Self::TextureBuffer,
- SlangTypeKind::ShaderStorageBuffer => Self::ShaderStorageBuffer,
- SlangTypeKind::ParameterBlock => Self::ParameterBlock,
- SlangTypeKind::GenericTypeParameter => Self::GenericTypeParameter,
- SlangTypeKind::Interface => Self::Interface,
- SlangTypeKind::OutputStream => Self::OutputStream,
- SlangTypeKind::MeshOutput => Self::MeshOutput,
- SlangTypeKind::Specialized => Self::Specialized,
- SlangTypeKind::Feedback => Self::Feedback,
- SlangTypeKind::Pointer => Self::Pointer,
- SlangTypeKind::DynamicResource => Self::DynamicResource,
- SlangTypeKind::Count => Self::Count,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-#[non_exhaustive]
-pub enum ScalarType
-{
- None,
- Void,
- Bool,
- Int32,
- Uint32,
- Int64,
- Uint64,
- Float16,
- Float32,
- Float64,
- Int8,
- Uint8,
- Int16,
- Uint16,
- Intptr,
- Uintptr,
- Bfloat16,
- FloatE4m3,
- FloatE5m2,
-}
-
-impl ScalarType
-{
- fn from_slang_scalar_type(scalar_type: SlangScalarType) -> Self
- {
- match scalar_type {
- SlangScalarType::None => Self::None,
- SlangScalarType::Void => Self::Void,
- SlangScalarType::Bool => Self::Bool,
- SlangScalarType::Int32 => Self::Int32,
- SlangScalarType::Uint32 => Self::Uint32,
- SlangScalarType::Int64 => Self::Int64,
- SlangScalarType::Uint64 => Self::Uint64,
- SlangScalarType::Float16 => Self::Float16,
- SlangScalarType::Float32 => Self::Float32,
- SlangScalarType::Float64 => Self::Float64,
- SlangScalarType::Int8 => Self::Int8,
- SlangScalarType::Uint8 => Self::Uint8,
- SlangScalarType::Int16 => Self::Int16,
- SlangScalarType::Uint16 => Self::Uint16,
- SlangScalarType::Intptr => Self::Intptr,
- SlangScalarType::Uintptr => Self::Uintptr,
- SlangScalarType::Bfloat16 => Self::Bfloat16,
- SlangScalarType::FloatE4m3 => Self::FloatE4m3,
- SlangScalarType::FloatE5m2 => Self::FloatE5m2,
- #[allow(unreachable_patterns)]
- _ => unimplemented!("conversion from slang scalar type"),
- }
- }
-}
-
-bitflags! {
-#[derive(Debug, Copy, Clone)]
-pub struct ResourceShape: u32 {
- const BASE = shader_slang::ResourceShape::SlangResourceBaseShapeMask as u32;
- const NONE = shader_slang::ResourceShape::SlangResourceNone as u32;
- const TEXTURE_1D = shader_slang::ResourceShape::SlangTexture1d as u32;
- const TEXTURE_2D = shader_slang::ResourceShape::SlangTexture2d as u32;
- const TEXTURE_3D = shader_slang::ResourceShape::SlangTexture3d as u32;
- const TEXTURE_CUBE = shader_slang::ResourceShape::SlangTextureCube as u32;
- const TEXTURE_BUFFER = shader_slang::ResourceShape::SlangTextureBuffer as u32;
- const STRUCTURED_BUFFER = shader_slang::ResourceShape::SlangStructuredBuffer as u32;
- const BYTE_ADDRESS_BUFFER = shader_slang::ResourceShape::SlangByteAddressBuffer as u32;
- const UNKNOWN = shader_slang::ResourceShape::SlangResourceUnknown as u32;
- const ACCELERATION_STRUCTURE = shader_slang::ResourceShape::SlangAccelerationStructure as u32;
- const TEXTURE_SUBPASS = shader_slang::ResourceShape::SlangTextureSubpass as u32;
- const EXT_SHAPE = shader_slang::ResourceShape::SlangResourceExtShapeMask as u32;
- const TEXTURE_FEEDBACK_FLAG = shader_slang::ResourceShape::SlangTextureFeedbackFlag as u32;
- const TEXTURE_SHADOW_FLAG = shader_slang::ResourceShape::SlangTextureShadowFlag as u32;
- const TEXTURE_ARRAY_FLAG = shader_slang::ResourceShape::SlangTextureArrayFlag as u32;
- const TEXTURE_MULTISAMPLE_FLAG = shader_slang::ResourceShape::SlangTextureMultisampleFlag as u32;
- const TEXTURE_COMBINED_FLAG = shader_slang::ResourceShape::SlangTextureCombinedFlag as u32;
- const TEXTURE_1D_ARRAY = shader_slang::ResourceShape::SlangTexture1dArray as u32;
- const TEXTURE_2D_ARRAY = shader_slang::ResourceShape::SlangTexture2dArray as u32;
- const TEXTURE_CUBE_ARRAY = shader_slang::ResourceShape::SlangTextureCubeArray as u32;
- const TEXTURE_2D_MULTISAMPLE = shader_slang::ResourceShape::SlangTexture2dMultisample as u32;
- const TEXTURE_2D_MULTISAMPLE_ARRAY = shader_slang::ResourceShape::SlangTexture2dMultisampleArray as u32;
- const TEXTURE_SUBPASS_MULTISAMPLE = shader_slang::ResourceShape::SlangTextureSubpassMultisample as u32;
-}
-}
-
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
-pub enum ParameterCategory
-{
- None,
- Mixed,
- ConstantBuffer,
- ShaderResource,
- UnorderedAccess,
- VaryingInput,
- VaryingOutput,
- SamplerState,
- Uniform,
- DescriptorTableSlot,
- SpecializationConstant,
- PushConstantBuffer,
- RegisterSpace,
- Generic,
- RayPayload,
- HitAttributes,
- CallablePayload,
- ShaderRecord,
- ExistentialTypeParam,
- ExistentialObjectParam,
- SubElementRegisterSpace,
- Subpass,
- MetalArgumentBufferElement,
- MetalAttribute,
- MetalPayload,
- Count,
-}
-
-impl ParameterCategory
+impl Debug for Program
{
- fn from_slang_parameter_category(parameter_category: SlangParameterCategory) -> Self
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
- match parameter_category {
- SlangParameterCategory::None => Self::None,
- SlangParameterCategory::Mixed => Self::Mixed,
- SlangParameterCategory::ConstantBuffer => Self::ConstantBuffer,
- SlangParameterCategory::ShaderResource => Self::ShaderResource,
- SlangParameterCategory::UnorderedAccess => Self::UnorderedAccess,
- SlangParameterCategory::VaryingInput => Self::VaryingInput,
- SlangParameterCategory::VaryingOutput => Self::VaryingOutput,
- SlangParameterCategory::SamplerState => Self::SamplerState,
- SlangParameterCategory::Uniform => Self::Uniform,
- SlangParameterCategory::DescriptorTableSlot => Self::DescriptorTableSlot,
- SlangParameterCategory::SpecializationConstant => {
- Self::SpecializationConstant
- }
- SlangParameterCategory::PushConstantBuffer => Self::PushConstantBuffer,
- SlangParameterCategory::RegisterSpace => Self::RegisterSpace,
- SlangParameterCategory::Generic => Self::Generic,
- SlangParameterCategory::RayPayload => Self::RayPayload,
- SlangParameterCategory::HitAttributes => Self::HitAttributes,
- SlangParameterCategory::CallablePayload => Self::CallablePayload,
- SlangParameterCategory::ShaderRecord => Self::ShaderRecord,
- SlangParameterCategory::ExistentialTypeParam => Self::ExistentialTypeParam,
- SlangParameterCategory::ExistentialObjectParam => {
- Self::ExistentialObjectParam
- }
- SlangParameterCategory::SubElementRegisterSpace => {
- Self::SubElementRegisterSpace
- }
- SlangParameterCategory::Subpass => Self::Subpass,
- SlangParameterCategory::MetalArgumentBufferElement => {
- Self::MetalArgumentBufferElement
- }
- SlangParameterCategory::MetalAttribute => Self::MetalAttribute,
- SlangParameterCategory::MetalPayload => Self::MetalPayload,
- SlangParameterCategory::Count => Self::Count,
- }
+ formatter
+ .debug_struct(type_name::<Self>())
+ .finish_non_exhaustive()
}
}
@@ -851,61 +214,13 @@ impl Blob
self.inner.as_slice()
}
+ #[allow(clippy::missing_errors_doc)]
pub fn as_str(&self) -> Result<&str, Utf8Error>
{
self.inner.as_str()
}
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[non_exhaustive]
-pub enum Stage
-{
- None,
- Vertex,
- Hull,
- Domain,
- Geometry,
- Fragment,
- Compute,
- RayGeneration,
- Intersection,
- AnyHit,
- ClosestHit,
- Miss,
- Callable,
- Mesh,
- Amplification,
- Dispatch,
- Count,
-}
-
-impl Stage
-{
- fn from_slang_stage(stage: shader_slang::Stage) -> Self
- {
- match stage {
- shader_slang::Stage::None => Self::None,
- shader_slang::Stage::Vertex => Self::Vertex,
- shader_slang::Stage::Hull => Self::Hull,
- shader_slang::Stage::Domain => Self::Domain,
- shader_slang::Stage::Geometry => Self::Geometry,
- shader_slang::Stage::Fragment => Self::Fragment,
- shader_slang::Stage::Compute => Self::Compute,
- shader_slang::Stage::RayGeneration => Self::RayGeneration,
- shader_slang::Stage::Intersection => Self::Intersection,
- shader_slang::Stage::AnyHit => Self::AnyHit,
- shader_slang::Stage::ClosestHit => Self::ClosestHit,
- shader_slang::Stage::Miss => Self::Miss,
- shader_slang::Stage::Callable => Self::Callable,
- shader_slang::Stage::Mesh => Self::Mesh,
- shader_slang::Stage::Amplification => Self::Amplification,
- shader_slang::Stage::Dispatch => Self::Dispatch,
- shader_slang::Stage::Count => Self::Count,
- }
- }
-}
-
builder! {
#[builder(name = SettingsBuilder, derives=(Debug))]
#[derive(Debug)]
@@ -919,14 +234,63 @@ pub struct Settings
#[derive(Sole)]
pub struct Context
{
- _global_session: SlangGlobalSession,
- session: SlangSession,
- modules: HashMap<AssetId, Module>,
- programs: HashMap<AssetId, Program>,
+ global_session: SlangGlobalSession,
+ session: Option<SlangSession>,
+ modules: hashbrown::HashMap<AssetId, Module>,
+ programs: hashbrown::HashMap<AssetId, Program>,
}
impl Context
{
+ #[allow(clippy::missing_errors_doc)]
+ pub fn init_session(
+ &mut self,
+ compile_targets: &[&CompileTarget],
+ ) -> Result<Vec<CompileTargetId>, ContextSessionInitError>
+ {
+ if self.session.is_some() {
+ return Err(ContextSessionInitError::AlreadyInitialized);
+ }
+
+ if compile_targets.len() > u32::MAX as usize {
+ return Err(ContextSessionInitError::TooManyCompileTargets);
+ }
+
+ let session_options = shader_slang::CompilerOptions::default()
+ .optimization(shader_slang::OptimizationLevel::None)
+ .matrix_layout_row(true)
+ .debug_information(SlangDebugInfoLevel::Maximal)
+ .no_mangle(true);
+
+ let (compile_target_ids, target_descs) = compile_targets
+ .iter()
+ .enumerate()
+ .map(|(index, compile_target)| {
+ // The length of compile_targets is not greater than u32::MAX
+ #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
+ (
+ CompileTargetId { index: index as u32 },
+ shader_slang::TargetDesc::default()
+ .format(compile_target.format.into_slang()),
+ )
+ })
+ .collect::<(Vec<_>, Vec<_>)>();
+
+ let session_desc = shader_slang::SessionDesc::default()
+ .targets(&target_descs)
+ .search_paths([""])
+ .options(&session_options);
+
+ let Some(session) = self.global_session.create_session(&session_desc) else {
+ tracing::error!("Failed to create shader-slang session");
+ return Err(ContextSessionInitError::Internal);
+ };
+
+ self.session = Some(session);
+
+ Ok(compile_target_ids)
+ }
+
#[must_use]
pub fn get_module(&self, asset_id: &AssetId) -> Option<&Module>
{
@@ -946,6 +310,10 @@ impl Context
link_entrypoints: EntrypointFlags,
) -> Result<Program, ComposeProgramError>
{
+ let Some(session) = self.session.as_ref() else {
+ return Err(ComposeProgramError::ContextSessionNotInitialized);
+ };
+
let entry_points = link_entrypoints
.iter()
.filter_map(|entrypoint_flag| {
@@ -971,42 +339,63 @@ impl Context
.chain([SlangComponentType::from(module.inner)])
.collect::<Vec<_>>();
- let program = self
- .session
+ let program = session
.create_composite_component_type(&components)
.map_err(|err| ComposeProgramError::Other(Error(err)))?;
- let vertex_desc = if link_entrypoints.contains(EntrypointFlags::VERTEX) {
- // TODO: Do not have hard coded target here
- let Ok(program_reflection) = program.layout(0) else {
- unreachable!();
- };
-
- let program_reflection = ProgramReflection { inner: program_reflection };
-
- Some(VertexDescription::new(
- &program_reflection
- .get_entry_point_by_name("vertex_main")
- .expect("Not possible"),
- )?)
- } else {
- None
- };
-
Ok(Program {
inner: program,
- metadata: ProgramMetadata { vertex_desc },
+ metadata: ProgramMetadata { link_entrypoints },
})
}
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CompileTargetId
+{
+ index: u32,
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[non_exhaustive]
+pub struct CompileTarget
+{
+ pub format: CompileTargetFormat,
+}
+
+impl CompileTarget
+{
+ #[must_use]
+ pub fn format(mut self, format: CompileTargetFormat) -> Self
+ {
+ self.format = format;
+ self
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[non_exhaustive]
+pub enum CompileTargetFormat
+{
+ #[default]
+ Glsl,
+}
+
+impl CompileTargetFormat
+{
+ fn into_slang(self) -> shader_slang::CompileTarget
+ {
+ match self {
+ Self::Glsl => shader_slang::CompileTarget::Glsl,
+ }
+ }
+}
+
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ProgramMetadata
{
- /// If the program has a entry point in the vertex stage, this field will contain a
- /// description of the vertex type passed to the entry point.
- pub vertex_desc: Option<VertexDescription>,
+ pub link_entrypoints: EntrypointFlags,
}
#[derive(Debug, Clone)]
@@ -1019,16 +408,12 @@ pub struct VertexDescription
impl VertexDescription
{
#[tracing::instrument(skip_all, fields(vs_entry_point_name = vs_entrypoint.name()))]
- pub fn new(
- vs_entrypoint: &EntryPointReflection<'_>,
- ) -> Result<Self, VertexDescriptionError>
+ fn new(
+ vs_entrypoint: &self::reflection::EntryPointReflection<'_>,
+ ) -> Result<Self, VertexDescCreationError>
{
- if vs_entrypoint.stage() != Stage::Vertex {
- return Err(VertexDescriptionError::EntrypointNotInVertexStage);
- }
-
let inputs = VarInputDfsIter::new(vs_entrypoint).inspect(|var_input| {
- if var_input.type_layout.kind() != TypeKind::Struct {
+ if var_input.type_layout.kind() != self::reflection::TypeKind::Struct {
return;
}
@@ -1043,17 +428,19 @@ impl VertexDescription
);
});
- let mut seen_inputs = HashSet::<VertexInputSemName>::new();
+ let mut seen_inputs = hashbrown::HashSet::<VertexInputSemName>::new();
Ok(Self {
inputs: inputs
- .filter(|var_input| var_input.type_layout.kind() != TypeKind::Struct)
+ .filter(|var_input| {
+ var_input.type_layout.kind() != self::reflection::TypeKind::Struct
+ })
.map(|var_input| {
let name = var_input.var_layout.name().unwrap_or("<unnamed>");
let semantic_name =
var_input.var_layout.semantic_name().ok_or_else(|| {
- VertexDescriptionError::VertexInputMissingSemanticName {
+ VertexDescCreationError::VertexInputMissingSemanticName {
name: name.to_owned(),
}
})?;
@@ -1063,19 +450,26 @@ impl VertexDescription
if seen_inputs.contains(&semantic_name) {
return Err(
- VertexDescriptionError::VertexInputHasOccupiedSemanticName {
+ VertexDescCreationError::VertexInputHasOccupiedSemanticName {
name: name.to_owned(),
},
);
}
- let (TypeKind::Scalar | TypeKind::Vector, Some(scalar_type)) = (
+ let (
+ self::reflection::TypeKind::Scalar
+ | self::reflection::TypeKind::Vector,
+ Some(scalar_type),
+ ) = (
var_input.type_layout.kind(),
var_input.type_layout.scalar_type(),
- ) else {
- return Err(VertexDescriptionError::UnsupportedVertexInputType {
- name: name.to_owned(),
- });
+ )
+ else {
+ return Err(
+ VertexDescCreationError::UnsupportedVertexInputType {
+ name: name.to_owned(),
+ },
+ );
};
seen_inputs.insert(semantic_name.clone());
@@ -1099,8 +493,8 @@ pub struct VertexInputDescription
{
pub semantic_name: VertexInputSemName,
pub index: usize,
- pub type_kind: TypeKind,
- pub scalar_type: ScalarType,
+ pub type_kind: self::reflection::TypeKind,
+ pub scalar_type: self::reflection::ScalarType,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1166,10 +560,13 @@ impl Display for VertexInputSemName
}
#[derive(Debug, thiserror::Error)]
-pub enum VertexDescriptionError
+pub enum VertexDescCreationError
{
- #[error("Entrypoint is not in vertex stage")]
- EntrypointNotInVertexStage,
+ #[error("Shader program has no entry point in vertex stage")]
+ NoVertexStageEntrypointInProgram,
+
+ #[error("Failed to get shader program reflection")]
+ GetProgramReflectionFailed(#[source] Error),
#[error("Type of vertex input '{name}' is not supported")]
UnsupportedVertexInputType
@@ -1195,6 +592,9 @@ pub enum VertexDescriptionError
#[derive(Debug, thiserror::Error)]
pub enum ComposeProgramError
{
+ #[error("A shader context session has not been initialized")]
+ ContextSessionNotInitialized,
+
#[error("Shader module does not have a '{entrypoint_name}' entry point")]
EntrypointNotFoundError
{
@@ -1202,24 +602,41 @@ pub enum ComposeProgramError
},
#[error("Failed to create vertex description")]
- VertexDescriptionCreationFailed(#[from] VertexDescriptionError),
+ VertexDescriptionCreationFailed(#[from] VertexDescCreationError),
#[error(transparent)]
Other(Error),
}
#[derive(Debug, thiserror::Error)]
+pub enum ContextSessionInitError
+{
+ #[error("A shader context session has already been initialized")]
+ AlreadyInitialized,
+
+ #[error("Too many compile targets. Count would overflow a u32")]
+ TooManyCompileTargets,
+
+ #[error("An internal error occurred")]
+ Internal,
+}
+
+#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct Error(#[from] shader_slang::Error);
struct VarInputDfsIter<'a>
{
- stack: Vec<(VariableLayout<'a>, TypeLayout<'a>, usize)>,
+ stack: Vec<(
+ self::reflection::VariableLayout<'a>,
+ self::reflection::TypeLayout<'a>,
+ usize,
+ )>,
}
impl<'a> VarInputDfsIter<'a>
{
- fn new(entry_point: &EntryPointReflection<'a>) -> Self
+ fn new(entry_point: &self::reflection::EntryPointReflection<'a>) -> Self
{
Self {
stack: entry_point
@@ -1251,7 +668,7 @@ impl<'a> Iterator for VarInputDfsIter<'a>
}
};
- if type_layout.kind() == TypeKind::Struct {
+ if type_layout.kind() == self::reflection::TypeKind::Struct {
self.stack.extend(type_layout.fields().map(|field| {
let Some(field_type_layout) = field.type_layout() else {
// I do not know in which cases type_layout can return
@@ -1269,8 +686,8 @@ impl<'a> Iterator for VarInputDfsIter<'a>
struct VarInput<'a>
{
- var_layout: VariableLayout<'a>,
- type_layout: TypeLayout<'a>,
+ var_layout: self::reflection::VariableLayout<'a>,
+ type_layout: self::reflection::TypeLayout<'a>,
index: usize,
}
@@ -1300,9 +717,7 @@ fn import(
source: std::fs::read_to_string(file_path)
.map_err(ImportError::ReadFileFailed)?
.into(),
- link_entrypoints: settings
- .map(|settings| settings.link_entrypoints)
- .unwrap_or_default(),
+ link_entrypoints: settings.map_or_default(|settings| settings.link_entrypoints),
});
Ok(())
@@ -1338,36 +753,12 @@ pub(super) fn prepare(collector: &mut crate::ecs::extension::Collector<'_>)
return;
};
- let session_options = shader_slang::CompilerOptions::default()
- .optimization(shader_slang::OptimizationLevel::None)
- .matrix_layout_row(true)
- .debug_information(SlangDebugInfoLevel::Maximal)
- .no_mangle(true);
-
- let target_desc = shader_slang::TargetDesc::default()
- .format(shader_slang::CompileTarget::Glsl)
- // .format(shader_slang::CompileTarget::Spirv)
- .profile(global_session.find_profile("glsl_330"));
- // .profile(global_session.find_profile("spirv_1_5"));
-
- let targets = [target_desc];
-
- let session_desc = shader_slang::SessionDesc::default()
- .targets(&targets)
- .search_paths([""])
- .options(&session_options);
-
- let Some(session) = global_session.create_session(&session_desc) else {
- tracing::error!("Failed to create shader-slang session");
- return;
- };
-
collector
.add_sole(Context {
- _global_session: global_session,
- session,
- modules: HashMap::new(),
- programs: HashMap::new(),
+ global_session,
+ session: None,
+ modules: hashbrown::HashMap::new(),
+ programs: hashbrown::HashMap::new(),
})
.ok();
@@ -1400,7 +791,11 @@ fn load_modules(
let assets = assets.get()?;
let asset_events = asset_events.get()?;
- for AssetEvent::Stored(asset_id, asset_label) in asset_events.iter_last_tick() {
+ let Some(session) = context.session.as_ref() else {
+ return Ok(());
+ };
+
+ for AssetEvent::Loaded(asset_id, asset_label) in asset_events.iter_last_tick() {
let asset_handle = AssetHandle::<ModuleSource>::from_id(*asset_id);
if !assets.is_loaded_and_has_type(&asset_handle) {
@@ -1413,7 +808,7 @@ fn load_modules(
tracing::debug!(asset_label=?asset_label, "Loading shader module");
- let module = match load_module(&context.session, module_source) {
+ let module = match load_module(session, module_source) {
Ok(module) => module,
Err(err) => {
tracing::error!("Failed to load shader module: {err}");