From fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:14:29 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- third_party/rust/naga/src/back/glsl/features.rs | 53 ++++++- third_party/rust/naga/src/back/glsl/mod.rs | 194 ++++++++++++++++-------- 2 files changed, 185 insertions(+), 62 deletions(-) (limited to 'third_party/rust/naga/src/back/glsl') diff --git a/third_party/rust/naga/src/back/glsl/features.rs b/third_party/rust/naga/src/back/glsl/features.rs index e7de05f695..99c128c6d9 100644 --- a/third_party/rust/naga/src/back/glsl/features.rs +++ b/third_party/rust/naga/src/back/glsl/features.rs @@ -1,8 +1,8 @@ use super::{BackendResult, Error, Version, Writer}; use crate::{ back::glsl::{Options, WriterFlags}, - AddressSpace, Binding, Expression, Handle, ImageClass, ImageDimension, Interpolation, Sampling, - Scalar, ScalarKind, ShaderStage, StorageFormat, Type, TypeInner, + AddressSpace, Binding, Expression, Handle, ImageClass, ImageDimension, Interpolation, + SampleLevel, Sampling, Scalar, ScalarKind, ShaderStage, StorageFormat, Type, TypeInner, }; use std::fmt::Write; @@ -48,6 +48,8 @@ bitflags::bitflags! { /// /// We can always support this, either through the language or a polyfill const INSTANCE_INDEX = 1 << 22; + /// Sample specific LODs of cube / array shadow textures + const TEXTURE_SHADOW_LOD = 1 << 23; } } @@ -125,6 +127,7 @@ impl FeaturesManager { check_feature!(TEXTURE_SAMPLES, 150); check_feature!(TEXTURE_LEVELS, 130); check_feature!(IMAGE_SIZE, 430, 310); + check_feature!(TEXTURE_SHADOW_LOD, 200, 300); // Return an error if there are missing features if missing.is_empty() { @@ -251,6 +254,11 @@ impl FeaturesManager { } } + if self.0.contains(Features::TEXTURE_SHADOW_LOD) { + // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_shadow_lod.txt + writeln!(out, "#extension GL_EXT_texture_shadow_lod : require")?; + } + Ok(()) } } @@ -469,6 +477,47 @@ impl<'a, W> Writer<'a, W> { } } } + Expression::ImageSample { image, level, offset, .. } => { + if let TypeInner::Image { + dim, + arrayed, + class: ImageClass::Depth { .. }, + } = *info[image].ty.inner_with(&module.types) { + let lod = matches!(level, SampleLevel::Zero | SampleLevel::Exact(_)); + let bias = matches!(level, SampleLevel::Bias(_)); + let auto = matches!(level, SampleLevel::Auto); + let cube = dim == ImageDimension::Cube; + let array2d = dim == ImageDimension::D2 && arrayed; + let gles = self.options.version.is_es(); + + // We have a workaround of using `textureGrad` instead of `textureLod` if the LOD is zero, + // so we don't *need* this extension for those cases. + // But if we're explicitly allowed to use the extension (`WriterFlags::TEXTURE_SHADOW_LOD`), + // we always use it instead of the workaround. + let grad_workaround_applicable = (array2d || (cube && !arrayed)) && level == SampleLevel::Zero; + let prefer_grad_workaround = grad_workaround_applicable && !self.options.writer_flags.contains(WriterFlags::TEXTURE_SHADOW_LOD); + + let mut ext_used = false; + + // float texture(sampler2DArrayShadow sampler, vec4 P [, float bias]) + // float texture(samplerCubeArrayShadow sampler, vec4 P, float compare [, float bias]) + ext_used |= (array2d || cube && arrayed) && bias; + + // The non `bias` version of this was standardized in GL 4.3, but never in GLES. + // float textureOffset(sampler2DArrayShadow sampler, vec4 P, ivec2 offset [, float bias]) + ext_used |= array2d && (bias || (gles && auto)) && offset.is_some(); + + // float textureLod(sampler2DArrayShadow sampler, vec4 P, float lod) + // float textureLodOffset(sampler2DArrayShadow sampler, vec4 P, float lod, ivec2 offset) + // float textureLod(samplerCubeShadow sampler, vec4 P, float lod) + // float textureLod(samplerCubeArrayShadow sampler, vec4 P, float compare, float lod) + ext_used |= (cube || array2d) && lod && !prefer_grad_workaround; + + if ext_used { + features.request(Features::TEXTURE_SHADOW_LOD); + } + } + } _ => {} } } diff --git a/third_party/rust/naga/src/back/glsl/mod.rs b/third_party/rust/naga/src/back/glsl/mod.rs index e346d43257..9bda594610 100644 --- a/third_party/rust/naga/src/back/glsl/mod.rs +++ b/third_party/rust/naga/src/back/glsl/mod.rs @@ -178,7 +178,7 @@ impl Version { /// Note: `location=` for vertex inputs and fragment outputs is supported /// unconditionally for GLES 300. fn supports_explicit_locations(&self) -> bool { - *self >= Version::Desktop(410) || *self >= Version::new_gles(310) + *self >= Version::Desktop(420) || *self >= Version::new_gles(310) } fn supports_early_depth_test(&self) -> bool { @@ -646,16 +646,6 @@ impl<'a, W: Write> Writer<'a, W> { // preprocessor not the processor ¯\_(ツ)_/¯ self.features.write(self.options, &mut self.out)?; - // Write the additional extensions - if self - .options - .writer_flags - .contains(WriterFlags::TEXTURE_SHADOW_LOD) - { - // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_shadow_lod.txt - writeln!(self.out, "#extension GL_EXT_texture_shadow_lod : require")?; - } - // glsl es requires a precision to be specified for floats and ints // TODO: Should this be user configurable? if es { @@ -1300,7 +1290,14 @@ impl<'a, W: Write> Writer<'a, W> { let inner = expr_info.ty.inner_with(&self.module.types); - if let Expression::Math { fun, arg, arg1, .. } = *expr { + if let Expression::Math { + fun, + arg, + arg1, + arg2, + .. + } = *expr + { match fun { crate::MathFunction::Dot => { // if the expression is a Dot product with integer arguments, @@ -1315,6 +1312,14 @@ impl<'a, W: Write> Writer<'a, W> { } } } + crate::MathFunction::ExtractBits => { + // Only argument 1 is re-used. + self.need_bake_expressions.insert(arg1.unwrap()); + } + crate::MathFunction::InsertBits => { + // Only argument 2 is re-used. + self.need_bake_expressions.insert(arg2.unwrap()); + } crate::MathFunction::CountLeadingZeros => { if let Some(crate::ScalarKind::Sint) = inner.scalar_kind() { self.need_bake_expressions.insert(arg); @@ -2451,6 +2456,9 @@ impl<'a, W: Write> Writer<'a, W> { crate::Literal::I64(_) => { return Err(Error::Custom("GLSL has no 64-bit integer type".into())); } + crate::Literal::U64(_) => { + return Err(Error::Custom("GLSL has no 64-bit integer type".into())); + } crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => { return Err(Error::Custom( "Abstract types should not appear in IR presented to backends".into(), @@ -2620,51 +2628,49 @@ impl<'a, W: Write> Writer<'a, W> { level, depth_ref, } => { - let dim = match *ctx.resolve_type(image, &self.module.types) { - TypeInner::Image { dim, .. } => dim, + let (dim, class, arrayed) = match *ctx.resolve_type(image, &self.module.types) { + TypeInner::Image { + dim, + class, + arrayed, + .. + } => (dim, class, arrayed), _ => unreachable!(), }; - - if dim == crate::ImageDimension::Cube - && array_index.is_some() - && depth_ref.is_some() - { - match level { - crate::SampleLevel::Zero - | crate::SampleLevel::Exact(_) - | crate::SampleLevel::Gradient { .. } - | crate::SampleLevel::Bias(_) => { - return Err(Error::Custom(String::from( - "gsamplerCubeArrayShadow isn't supported in textureGrad, \ - textureLod or texture with bias", - ))) - } - crate::SampleLevel::Auto => {} + let mut err = None; + if dim == crate::ImageDimension::Cube { + if offset.is_some() { + err = Some("gsamplerCube[Array][Shadow] doesn't support texture sampling with offsets"); + } + if arrayed + && matches!(class, crate::ImageClass::Depth { .. }) + && matches!(level, crate::SampleLevel::Gradient { .. }) + { + err = Some("samplerCubeArrayShadow don't support textureGrad"); } } + if gather.is_some() && level != crate::SampleLevel::Zero { + err = Some("textureGather doesn't support LOD parameters"); + } + if let Some(err) = err { + return Err(Error::Custom(String::from(err))); + } - // textureLod on sampler2DArrayShadow and samplerCubeShadow does not exist in GLSL. - // To emulate this, we will have to use textureGrad with a constant gradient of 0. - let workaround_lod_array_shadow_as_grad = (array_index.is_some() - || dim == crate::ImageDimension::Cube) - && depth_ref.is_some() - && gather.is_none() - && !self - .options - .writer_flags - .contains(WriterFlags::TEXTURE_SHADOW_LOD); - - //Write the function to be used depending on the sample level + // `textureLod[Offset]` on `sampler2DArrayShadow` and `samplerCubeShadow` does not exist in GLSL, + // unless `GL_EXT_texture_shadow_lod` is present. + // But if the target LOD is zero, we can emulate that by using `textureGrad[Offset]` with a constant gradient of 0. + let workaround_lod_with_grad = ((dim == crate::ImageDimension::Cube && !arrayed) + || (dim == crate::ImageDimension::D2 && arrayed)) + && level == crate::SampleLevel::Zero + && matches!(class, crate::ImageClass::Depth { .. }) + && !self.features.contains(Features::TEXTURE_SHADOW_LOD); + + // Write the function to be used depending on the sample level let fun_name = match level { crate::SampleLevel::Zero if gather.is_some() => "textureGather", + crate::SampleLevel::Zero if workaround_lod_with_grad => "textureGrad", crate::SampleLevel::Auto | crate::SampleLevel::Bias(_) => "texture", - crate::SampleLevel::Zero | crate::SampleLevel::Exact(_) => { - if workaround_lod_array_shadow_as_grad { - "textureGrad" - } else { - "textureLod" - } - } + crate::SampleLevel::Zero | crate::SampleLevel::Exact(_) => "textureLod", crate::SampleLevel::Gradient { .. } => "textureGrad", }; let offset_name = match offset { @@ -2727,7 +2733,7 @@ impl<'a, W: Write> Writer<'a, W> { crate::SampleLevel::Auto => (), // Zero needs level set to 0 crate::SampleLevel::Zero => { - if workaround_lod_array_shadow_as_grad { + if workaround_lod_with_grad { let vec_dim = match dim { crate::ImageDimension::Cube => 3, _ => 2, @@ -2739,13 +2745,8 @@ impl<'a, W: Write> Writer<'a, W> { } // Exact and bias require another argument crate::SampleLevel::Exact(expr) => { - if workaround_lod_array_shadow_as_grad { - log::warn!("Unable to `textureLod` a shadow array, ignoring the LOD"); - write!(self.out, ", vec2(0,0), vec2(0,0)")?; - } else { - write!(self.out, ", ")?; - self.write_expr(expr, ctx)?; - } + write!(self.out, ", ")?; + self.write_expr(expr, ctx)?; } crate::SampleLevel::Bias(_) => { // This needs to be done after the offset writing @@ -3155,7 +3156,29 @@ impl<'a, W: Write> Writer<'a, W> { Mf::Abs => "abs", Mf::Min => "min", Mf::Max => "max", - Mf::Clamp => "clamp", + Mf::Clamp => { + let scalar_kind = ctx + .resolve_type(arg, &self.module.types) + .scalar_kind() + .unwrap(); + match scalar_kind { + crate::ScalarKind::Float => "clamp", + // Clamp is undefined if min > max. In practice this means it can use a median-of-three + // instruction to determine the value. This is fine according to the WGSL spec for float + // clamp, but integer clamp _must_ use min-max. As such we write out min/max. + _ => { + write!(self.out, "min(max(")?; + self.write_expr(arg, ctx)?; + write!(self.out, ", ")?; + self.write_expr(arg1.unwrap(), ctx)?; + write!(self.out, "), ")?; + self.write_expr(arg2.unwrap(), ctx)?; + write!(self.out, ")")?; + + return Ok(()); + } + } + } Mf::Saturate => { write!(self.out, "clamp(")?; @@ -3370,8 +3393,59 @@ impl<'a, W: Write> Writer<'a, W> { } Mf::CountOneBits => "bitCount", Mf::ReverseBits => "bitfieldReverse", - Mf::ExtractBits => "bitfieldExtract", - Mf::InsertBits => "bitfieldInsert", + Mf::ExtractBits => { + // The behavior of ExtractBits is undefined when offset + count > bit_width. We need + // to first sanitize the offset and count first. If we don't do this, AMD and Intel chips + // will return out-of-spec values if the extracted range is not within the bit width. + // + // This encodes the exact formula specified by the wgsl spec, without temporary values: + // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin + // + // w = sizeof(x) * 8 + // o = min(offset, w) + // c = min(count, w - o) + // + // bitfieldExtract(x, o, c) + // + // extract_bits(e, min(offset, w), min(count, w - min(offset, w)))) + let scalar_bits = ctx + .resolve_type(arg, &self.module.types) + .scalar_width() + .unwrap(); + + write!(self.out, "bitfieldExtract(")?; + self.write_expr(arg, ctx)?; + write!(self.out, ", int(min(")?; + self.write_expr(arg1.unwrap(), ctx)?; + write!(self.out, ", {scalar_bits}u)), int(min(",)?; + self.write_expr(arg2.unwrap(), ctx)?; + write!(self.out, ", {scalar_bits}u - min(")?; + self.write_expr(arg1.unwrap(), ctx)?; + write!(self.out, ", {scalar_bits}u))))")?; + + return Ok(()); + } + Mf::InsertBits => { + // InsertBits has the same considerations as ExtractBits above + let scalar_bits = ctx + .resolve_type(arg, &self.module.types) + .scalar_width() + .unwrap(); + + write!(self.out, "bitfieldInsert(")?; + self.write_expr(arg, ctx)?; + write!(self.out, ", ")?; + self.write_expr(arg1.unwrap(), ctx)?; + write!(self.out, ", int(min(")?; + self.write_expr(arg2.unwrap(), ctx)?; + write!(self.out, ", {scalar_bits}u)), int(min(",)?; + self.write_expr(arg3.unwrap(), ctx)?; + write!(self.out, ", {scalar_bits}u - min(")?; + self.write_expr(arg2.unwrap(), ctx)?; + write!(self.out, ", {scalar_bits}u))))")?; + + return Ok(()); + } Mf::FindLsb => "findLSB", Mf::FindMsb => "findMSB", // data packing -- cgit v1.2.3