1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
use std::{borrow::Cow, fmt};
use super::{builtins::MacroCall, Span};
use crate::{
AddressSpace, BinaryOperator, Binding, Constant, Expression, Function, GlobalVariable, Handle,
Interpolation, Literal, Sampling, StorageAccess, Type, UnaryOperator,
};
#[derive(Debug, Clone, Copy)]
pub enum GlobalLookupKind {
Variable(Handle<GlobalVariable>),
Constant(Handle<Constant>, Handle<Type>),
BlockSelect(Handle<GlobalVariable>, u32),
}
#[derive(Debug, Clone, Copy)]
pub struct GlobalLookup {
pub kind: GlobalLookupKind,
pub entry_arg: Option<usize>,
pub mutable: bool,
}
#[derive(Debug, Clone)]
pub struct ParameterInfo {
pub qualifier: ParameterQualifier,
/// Whether the parameter should be treated as a depth image instead of a
/// sampled image.
pub depth: bool,
}
/// How the function is implemented
#[derive(Clone, Copy)]
pub enum FunctionKind {
/// The function is user defined
Call(Handle<Function>),
/// The function is a builtin
Macro(MacroCall),
}
impl fmt::Debug for FunctionKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Call(_) => write!(f, "Call"),
Self::Macro(_) => write!(f, "Macro"),
}
}
}
#[derive(Debug)]
pub struct Overload {
/// Normalized function parameters, modifiers are not applied
pub parameters: Vec<Handle<Type>>,
pub parameters_info: Vec<ParameterInfo>,
/// How the function is implemented
pub kind: FunctionKind,
/// Whether this function was already defined or is just a prototype
pub defined: bool,
/// Whether this overload is the one provided by the language or has
/// been redeclared by the user (builtins only)
pub internal: bool,
/// Whether or not this function returns void (nothing)
pub void: bool,
}
bitflags::bitflags! {
/// Tracks the variations of the builtin already generated, this is needed because some
/// builtins overloads can't be generated unless explicitly used, since they might cause
/// unneeded capabilities to be requested
#[derive(Default)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BuiltinVariations: u32 {
/// Request the standard overloads
const STANDARD = 1 << 0;
/// Request overloads that use the double type
const DOUBLE = 1 << 1;
/// Request overloads that use `samplerCubeArray(Shadow)`
const CUBE_TEXTURES_ARRAY = 1 << 2;
/// Request overloads that use `sampler2DMSArray`
const D2_MULTI_TEXTURES_ARRAY = 1 << 3;
}
}
#[derive(Debug, Default)]
pub struct FunctionDeclaration {
pub overloads: Vec<Overload>,
/// Tracks the builtin overload variations that were already generated
pub variations: BuiltinVariations,
}
#[derive(Debug)]
pub struct EntryArg {
pub name: Option<String>,
pub binding: Binding,
pub handle: Handle<GlobalVariable>,
pub storage: StorageQualifier,
}
#[derive(Debug, Clone)]
pub struct VariableReference {
pub expr: Handle<Expression>,
/// Whether the variable is of a pointer type (and needs loading) or not
pub load: bool,
/// Whether the value of the variable can be changed or not
pub mutable: bool,
pub constant: Option<(Handle<Constant>, Handle<Type>)>,
pub entry_arg: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct HirExpr {
pub kind: HirExprKind,
pub meta: Span,
}
#[derive(Debug, Clone)]
pub enum HirExprKind {
Access {
base: Handle<HirExpr>,
index: Handle<HirExpr>,
},
Select {
base: Handle<HirExpr>,
field: String,
},
Literal(Literal),
Binary {
left: Handle<HirExpr>,
op: BinaryOperator,
right: Handle<HirExpr>,
},
Unary {
op: UnaryOperator,
expr: Handle<HirExpr>,
},
Variable(VariableReference),
Call(FunctionCall),
/// Represents the ternary operator in glsl (`:?`)
Conditional {
/// The expression that will decide which branch to take, must evaluate to a boolean
condition: Handle<HirExpr>,
/// The expression that will be evaluated if [`condition`] returns `true`
///
/// [`condition`]: Self::Conditional::condition
accept: Handle<HirExpr>,
/// The expression that will be evaluated if [`condition`] returns `false`
///
/// [`condition`]: Self::Conditional::condition
reject: Handle<HirExpr>,
},
Assign {
tgt: Handle<HirExpr>,
value: Handle<HirExpr>,
},
/// A prefix/postfix operator like `++`
PrePostfix {
/// The operation to be performed
op: BinaryOperator,
/// Whether this is a postfix or a prefix
postfix: bool,
/// The target expression
expr: Handle<HirExpr>,
},
/// A method call like `what.something(a, b, c)`
Method {
/// expression the method call applies to (`what` in the example)
expr: Handle<HirExpr>,
/// the method name (`something` in the example)
name: String,
/// the arguments to the method (`a`, `b`, and `c` in the example)
args: Vec<Handle<HirExpr>>,
},
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub enum QualifierKey<'a> {
String(Cow<'a, str>),
/// Used for `std140` and `std430` layout qualifiers
Layout,
/// Used for image formats
Format,
}
#[derive(Debug)]
pub enum QualifierValue {
None,
Uint(u32),
Layout(StructLayout),
Format(crate::StorageFormat),
}
#[derive(Debug, Default)]
pub struct TypeQualifiers<'a> {
pub span: Span,
pub storage: (StorageQualifier, Span),
pub invariant: Option<Span>,
pub interpolation: Option<(Interpolation, Span)>,
pub precision: Option<(Precision, Span)>,
pub sampling: Option<(Sampling, Span)>,
/// Memory qualifiers used in the declaration to set the storage access to be used
/// in declarations that support it (storage images and buffers)
pub storage_access: Option<(StorageAccess, Span)>,
pub layout_qualifiers: crate::FastHashMap<QualifierKey<'a>, (QualifierValue, Span)>,
}
impl<'a> TypeQualifiers<'a> {
/// Appends `errors` with errors for all unused qualifiers
pub fn unused_errors(&self, errors: &mut Vec<super::Error>) {
if let Some(meta) = self.invariant {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Invariant qualifier can only be used in in/out variables".into(),
),
meta,
});
}
if let Some((_, meta)) = self.interpolation {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Interpolation qualifiers can only be used in in/out variables".into(),
),
meta,
});
}
if let Some((_, meta)) = self.sampling {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Sampling qualifiers can only be used in in/out variables".into(),
),
meta,
});
}
if let Some((_, meta)) = self.storage_access {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Memory qualifiers can only be used in storage variables".into(),
),
meta,
});
}
for &(_, meta) in self.layout_qualifiers.values() {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError("Unexpected qualifier".into()),
meta,
});
}
}
/// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't
/// a [`QualifierValue::Uint`]
pub fn uint_layout_qualifier(
&mut self,
name: &'a str,
errors: &mut Vec<super::Error>,
) -> Option<u32> {
match self
.layout_qualifiers
.remove(&QualifierKey::String(name.into()))
{
Some((QualifierValue::Uint(v), _)) => Some(v),
Some((_, meta)) => {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError("Qualifier expects a uint value".into()),
meta,
});
// Return a dummy value instead of `None` to differentiate from
// the qualifier not existing, since some parts might require the
// qualifier to exist and throwing another error that it doesn't
// exist would be unhelpful
Some(0)
}
_ => None,
}
}
/// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't
/// a [`QualifierValue::None`]
pub fn none_layout_qualifier(&mut self, name: &'a str, errors: &mut Vec<super::Error>) -> bool {
match self
.layout_qualifiers
.remove(&QualifierKey::String(name.into()))
{
Some((QualifierValue::None, _)) => true,
Some((_, meta)) => {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Qualifier doesn't expect a value".into(),
),
meta,
});
// Return a `true` to since the qualifier is defined and adding
// another error for it not being defined would be unhelpful
true
}
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub enum FunctionCallKind {
TypeConstructor(Handle<Type>),
Function(String),
}
#[derive(Debug, Clone)]
pub struct FunctionCall {
pub kind: FunctionCallKind,
pub args: Vec<Handle<HirExpr>>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StorageQualifier {
AddressSpace(AddressSpace),
Input,
Output,
Const,
}
impl Default for StorageQualifier {
fn default() -> Self {
StorageQualifier::AddressSpace(AddressSpace::Function)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StructLayout {
Std140,
Std430,
}
// TODO: Encode precision hints in the IR
/// A precision hint used in GLSL declarations.
///
/// Precision hints can be used to either speed up shader execution or control
/// the precision of arithmetic operations.
///
/// To use a precision hint simply add it before the type in the declaration.
/// ```glsl
/// mediump float a;
/// ```
///
/// The default when no precision is declared is `highp` which means that all
/// operations operate with the type defined width.
///
/// For `mediump` and `lowp` operations follow the spir-v
/// [`RelaxedPrecision`][RelaxedPrecision] decoration semantics.
///
/// [RelaxedPrecision]: https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#_a_id_relaxedprecisionsection_a_relaxed_precision
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum Precision {
/// `lowp` precision
Low,
/// `mediump` precision
Medium,
/// `highp` precision
High,
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum ParameterQualifier {
In,
Out,
InOut,
Const,
}
impl ParameterQualifier {
/// Returns true if the argument should be passed as a lhs expression
pub const fn is_lhs(&self) -> bool {
match *self {
ParameterQualifier::Out | ParameterQualifier::InOut => true,
_ => false,
}
}
}
/// The GLSL profile used by a shader.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Profile {
/// The `core` profile, default when no profile is specified.
Core,
}