// GLSL is mostly a superset of C but it also removes some parts of it this is a list of relevant // aspects for this backend. // // The most notable change is the introduction of the version preprocessor directive that must // always be the first line of a glsl file and is written as // `#version number profile` // `number` is the version itself (i.e. 300) and `profile` is the // shader profile we only support "core" and "es", the former is used in desktop applications and // the later is used in embedded contexts, mobile devices and browsers. Each one as it's own // versions (at the time of writing this the latest version for "core" is 460 and for "es" is 320) // // Other important preprocessor addition is the extension directive which is written as // `#extension name: behaviour` // Extensions provide increased features in a plugin fashion but they aren't required to be // supported hence why they are called extensions, that's why `behaviour` is used it specifies // whether the extension is strictly required or if it should only be enabled if needed. In our case // when we use extensions we set behaviour to `require` always. // // The only thing that glsl removes that makes a difference are pointers. // // Additions that are relevant for the backend are the discard keyword, the introduction of // vector, matrices, samplers, image types and functions that provide common shader operations
pubuse features::Features; pubuse writer::Writer;
use alloc::{
borrow::ToOwned,
format,
string::{String, ToString},
vec,
vec::Vec,
}; use core::{
cmp::Ordering,
fmt::{self, Error as FmtError, Write},
mem,
};
use hashbrown::hash_map; use thiserror::Error;
usecrate::{
back::{self, Baked},
common,
proc::{self, NameKey},
valid, Handle, ShaderStage, TypeInner,
}; use conv::*; use features::FeaturesManager;
/// Contains simple 1:1 conversion functions. mod conv; /// Contains the features related code and the features querying method mod features; /// Contains a constant with a slice of all the reserved keywords RESERVED_KEYWORDS mod keywords; /// Contains the [`Writer`] type. mod writer;
/// List of supported `core` GLSL versions. pubconst SUPPORTED_CORE_VERSIONS: &[u16] = &[140, 150, 330, 400, 410, 420, 430, 440, 450, 460]; /// List of supported `es` GLSL versions. pubconst SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];
/// The suffix of the variable that will hold the calculated clamped level /// of detail for bounds checking in `ImageLoad` const CLAMPED_LOD_SUFFIX: &str = "_clamped_lod";
/// Checks the list of currently supported versions and returns true if it contains the /// specified version /// /// # Notes /// As an invalid version number will never be added to the supported version list /// so this also checks for version validity fn is_supported(&self) -> bool { match *self {
Version::Desktop(v) => SUPPORTED_CORE_VERSIONS.contains(&v),
Version::Embedded { version: v, .. } => SUPPORTED_ES_VERSIONS.contains(&v),
}
}
/// Checks if the version supports all of the explicit layouts: /// - `location=` qualifiers for bindings /// - `binding=` qualifiers for resources /// /// Note: `location=` for vertex inputs and fragment outputs is supported /// unconditionally for GLES 300. fn supports_explicit_locations(&self) -> bool {
*self >= Version::Desktop(420) || *self >= Version::new_gles(310)
}
bitflags::bitflags! { /// Configuration flags for the [`Writer`]. #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubstruct WriterFlags: u32 { /// Flip output Y and extend Z from (0, 1) to (-1, 1). const ADJUST_COORDINATE_SPACE = 0x1; /// Supports GL_EXT_texture_shadow_lod on the host, which provides /// additional functions on shadows and arrays of shadows. const TEXTURE_SHADOW_LOD = 0x2; /// Supports ARB_shader_draw_parameters on the host, which provides /// support for `gl_BaseInstanceARB`, `gl_BaseVertexARB`, `gl_DrawIDARB`, and `gl_DrawID`. const DRAW_PARAMETERS = 0x4; /// Include unused global variables, constants and functions. By default the output will exclude /// global variables that are not used in the specified entrypoint (including indirect use), /// all constant declarations, and functions that use excluded global variables. const INCLUDE_UNUSED_ITEMS = 0x10; /// Emit `PointSize` output builtin to vertex shaders, which is /// required for drawing with `PointList` topology. /// /// https://registry.khronos.org/OpenGL/specs/es/3.2/GLSL_ES_Specification_3.20.html#built-in-language-variables /// The variable gl_PointSize is intended for a shader to write the size of the point to be rasterized. It is measured in pixels. /// If gl_PointSize is not written to, its value is undefined in subsequent pipe stages. const FORCE_POINT_SIZE = 0x20;
}
}
/// Configuration used in the [`Writer`]. #[derive(Debug, Clone)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] #[cfg_attr(feature = "deserialize", serde(default))] pubstruct Options { /// The GLSL version to be used. pub version: Version, /// Configuration flags for the [`Writer`]. pub writer_flags: WriterFlags, /// Map of resources association to binding locations. #[cfg_attr(
feature = "deserialize",
serde(deserialize_with = "deserialize_binding_map")
)] pub binding_map: BindingMap, /// Should workgroup variables be zero initialized (by polyfilling)? pub zero_initialize_workgroup_memory: bool,
}
/// A subset of options meant to be changed per pipeline. #[derive(Debug, Clone)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] pubstruct PipelineOptions { /// The stage of the entry point. pub shader_stage: ShaderStage, /// The name of the entry point. /// /// If no entry point that matches is found while creating a [`Writer`], an /// error will be thrown. pub entry_point: String, /// How many views to render to, if doing multiview rendering. pub multiview: Option<core::num::NonZeroU32>,
}
#[derive(Debug)] pubstruct VaryingLocation { /// The location of the global. /// This corresponds to `layout(location = ..)` in GLSL. pub location: u32, /// The index which can be used for dual source blending. /// This corresponds to `layout(index = ..)` in GLSL. pub index: u32,
}
/// Reflection info for texture mappings and uniforms. #[derive(Debug)] pubstruct ReflectionInfo { /// Mapping between texture names and variables/samplers. pub texture_mapping: crate::FastHashMap<String, TextureMapping>, /// Mapping between uniform variables and names. pub uniforms: crate::FastHashMap<Handle<crate::GlobalVariable>, String>, /// Mapping between names and attribute locations. pub varying: crate::FastHashMap<String, VaryingLocation>, /// List of immediate data items in the shader. pub immediates_items: Vec<ImmediateItem>, /// Number of user-defined clip planes. Only applicable to vertex shaders. pub clip_distance_count: u32,
}
/// Mapping between a texture and its sampler, if it exists. /// /// GLSL pre-Vulkan has no concept of separate textures and samplers. Instead, everything is a /// `gsamplerN` where `g` is the scalar type and `N` is the dimension. But naga uses separate textures /// and samplers in the IR, so the backend produces a [`FastHashMap`](crate::FastHashMap) with the texture name /// as a key and a [`TextureMapping`] as a value. This way, the user knows where to bind. /// /// [`Storage`](crate::ImageClass::Storage) images produce `gimageN` and don't have an associated sampler, /// so the [`sampler`](Self::sampler) field will be [`None`]. #[derive(Debug, Clone)] pubstruct TextureMapping { /// Handle to the image global variable. pub texture: Handle<crate::GlobalVariable>, /// Handle to the associated sampler global variable, if it exists. pub sampler: Option<Handle<crate::GlobalVariable>>,
}
/// All information to bind a single uniform value to the shader. /// /// Immediates are emulated using traditional uniforms in OpenGL. /// /// These are composed of a set of primitives (scalar, vector, matrix) that /// are given names. Because they are not backed by the concept of a buffer, /// we must do the work of calculating the offset of each primitive in the /// immediate data block. #[derive(Debug, Clone)] pubstruct ImmediateItem { /// GL uniform name for the item. This name is the same as if you were /// to access it directly from a GLSL shader. /// /// The with the following example, the following names will be generated, /// one name per GLSL uniform. /// /// ```glsl /// struct InnerStruct { /// value: f32, /// } /// /// struct ImmediateData { /// InnerStruct inner; /// vec4 array[2]; /// } /// /// uniform ImmediateData _immediates_binding_cs; /// ``` /// /// ```text /// - _immediates_binding_cs.inner.value /// - _immediates_binding_cs.array[0] /// - _immediates_binding_cs.array[1] /// ``` /// pub access_path: String, /// Type of the uniform. This will only ever be a scalar, vector, or matrix. /// /// Stored as a [`TypeInner`] rather than a [`Handle<Type>`] because /// `process_overrides` may compact the module, renumbering type handles. /// Leaf types don't reference other types, so a `TypeInner` is self-contained. /// /// [`TypeInner`]: crate::TypeInner /// [`Handle<Type>`]: Handle pub ty: TypeInner, /// The offset in the immediate data memory block this uniform maps to. pub offset: u32, /// Size of this uniform in bytes. pub size_bytes: u32,
}
/// Helper structure that generates a number #[derive(Default)] struct IdGenerator(u32);
impl IdGenerator { /// Generates a number that's guaranteed to be unique for this `IdGenerator` constfn generate(&mutself) -> u32 { // It's just an increasing number but it does the job let ret = self.0; self.0 += 1;
ret
}
}
/// Helper wrapper used to get a name for a varying /// /// Varying have different naming schemes depending on their binding: /// - Varyings with builtin bindings get their name from [`glsl_built_in`]. /// - Varyings with location bindings are named `_S_location_X` where `S` is a /// prefix identifying which pipeline stage the varying connects, and `X` is /// the location. struct VaryingName<'a> {
binding: &'a crate::Binding,
stage: ShaderStage,
options: VaryingOptions,
} impl fmt::Display for VaryingName<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self.binding { crate::Binding::Location {
blend_src: Some(1), ..
} => {
write!(f, "_fs2p_location1",)
} crate::Binding::Location { location, .. } => { let prefix = match (self.stage, self.options.output) {
(ShaderStage::Compute, _) => unreachable!(), // pipeline to vertex
(ShaderStage::Vertex, false) => "p2vs", // vertex to fragment
(ShaderStage::Vertex, true) | (ShaderStage::Fragment, false) => "vs2fs", // fragment to pipeline
(ShaderStage::Fragment, true) => "fs2p",
(
ShaderStage::Task
| ShaderStage::Mesh
| ShaderStage::RayGeneration
| ShaderStage::AnyHit
| ShaderStage::ClosestHit
| ShaderStage::Miss,
_,
) => unreachable!(),
};
write!(f, "_{prefix}_location{location}",)
} crate::Binding::BuiltIn(built_in) => {
write!(f, "{}", glsl_built_in(built_in, self.options))
}
}
}
}
/// Shorthand result used internally by the backend type BackendResult<T = ()> = Result<T, Error>;
/// A GLSL compilation error. #[derive(Debug, Error)] pubenum Error { /// A error occurred while writing to the output. #[error("Format error")]
FmtError(#[from] FmtError), /// The specified [`Version`] doesn't have all required [`Features`]. /// /// Contains the missing [`Features`]. #[error("The selected version doesn't support {0:?}")]
MissingFeatures(Features), /// [`AddressSpace::Immediate`](crate::AddressSpace::Immediate) was used more than /// once in the entry point, which isn't supported. #[error("Multiple immediates aren't supported")]
MultipleImmediateData, /// The specified [`Version`] isn't supported. #[error("The specified version isn't supported")]
VersionNotSupported, /// The entry point couldn't be found. #[error("The requested entry point couldn't be found")]
EntryPointNotFound, /// A call was made to an unsupported external. #[error("A call was made to an unsupported external: {0}")]
UnsupportedExternal(String), /// A scalar with an unsupported width was requested. #[error("A scalar with an unsupported width was requested: {0:?}")]
UnsupportedScalar(crate::Scalar), /// A image was used with multiple samplers, which isn't supported. #[error("A image was used with multiple samplers")]
ImageMultipleSamplers, #[error("{0}")]
Custom(String), #[error("overrides should not be present at this stage")] Override, /// [`crate::Sampling::First`] is unsupported. #[error("`{:?}` sampling is unsupported", crate::Sampling::First)]
FirstSamplingNotSupported, #[error(transparent)]
ResolveArraySizeError(#[from] proc::ResolveArraySizeError),
}
/// Binary operation with a different logic on the GLSL side. enum BinaryOperation { /// Vector comparison should use the function like `greaterThan()`, etc.
VectorCompare, /// Vector component wise operation; used to polyfill unsupported ops like `|` and `&` for `bvecN`'s
VectorComponentWise, /// GLSL `%` is SPIR-V `OpUMod/OpSMod` and `mod()` is `OpFMod`, but [`BinaryOperator::Modulo`](crate::BinaryOperator::Modulo) is `OpFRem`.
Modulo, /// Any plain operation. No additional logic required.
Other,
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.