Skip to content

Commit

Permalink
Fix glsl backend errors regarding samplerCubeArrayShadow (gfx-rs#5171)
Browse files Browse the repository at this point in the history
* add GL_EXT_texture_shadow_lod feature detection

* allow more cases of cube depth texture sampling in glsl

* add test for sampling a cubemap array depth texture with lod

* add test for chosing GL_EXT_texture_shadow_lod over the grad workaround if instructed

* add changelog entry for GL_EXT_texture_shadow_lod

* fix criteria for requiring and using TEXTURE_SHADOW_LOD

* require gles 320 for textureSampling over cubeArrayShadow

* prevent false positives in TEXTURE_SHADOW_LOD in checks

* make workaround_lod_with_grad usecase selection less context dependant

* move 3d array texture error into the validator

* correct ImageSample logic errors
  • Loading branch information
cmrschwarz authored and cwfitzgerald committed Feb 29, 2024
1 parent 091d786 commit 7bc7220
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 58 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Bottom level categories:
#### OpenGL
- Fix internal format for the `Etc2Rgba8Unorm` format. By @andristarr in [#5178](https://github.com/gfx-rs/wgpu/pull/5178)
- Try to load `libX11.so.6` in addition to `libX11.so` on linux. [#5307](https://github.com/gfx-rs/wgpu/pull/5307)
- Make use of `GL_EXT_texture_shadow_lod` to support sampling a cube depth texture with an explicit LOD. By @cmrschwarz in #[5171](https://github.com/gfx-rs/wgpu/pull/5171).

#### `glsl-in`

Expand Down
53 changes: 51 additions & 2 deletions naga/src/back/glsl/features.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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(())
}
}
Expand Down Expand Up @@ -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);
}
}
}
_ => {}
}
}
Expand Down
93 changes: 38 additions & 55 deletions naga/src/back/glsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -2620,51 +2610,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[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);

// 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
// 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 {
Expand Down Expand Up @@ -2727,7 +2715,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,
Expand All @@ -2739,13 +2727,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
Expand Down
15 changes: 14 additions & 1 deletion naga/src/valid/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ pub enum TypeError {
MatrixElementNotFloat,
#[error("The constant {0:?} is specialized, and cannot be used as an array size")]
UnsupportedSpecializedArrayLength(Handle<crate::Constant>),
#[error("{} of dimensionality {dim:?} and class {class:?} are not supported", if *.arrayed {"Arrayed images"} else {"Images"})]
UnsupportedImageType {
dim: crate::ImageDimension,
arrayed: bool,
class: crate::ImageClass,
},
#[error("Array stride {stride} does not match the expected {expected}")]
InvalidArrayStride { stride: u32, expected: u32 },
#[error("Field '{0}' can't be dynamically-sized, has type {1:?}")]
Expand Down Expand Up @@ -596,8 +602,15 @@ impl super::Validator {
Ti::Image {
dim,
arrayed,
class: _,
class,
} => {
if arrayed && matches!(dim, crate::ImageDimension::D3) {
return Err(TypeError::UnsupportedImageType {
dim,
arrayed,
class,
});
}
if arrayed && matches!(dim, crate::ImageDimension::Cube) {
self.require_type_capability(Capabilities::CUBE_ARRAY_TEXTURES)?;
}
Expand Down
11 changes: 11 additions & 0 deletions naga/tests/in/sample-cube-array-depth-lod.param.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(
glsl: (
writer_flags: ("TEXTURE_SHADOW_LOD"),
version: Embedded(
version: 320,
is_webgl: false
),
binding_map: {},
zero_initialize_workgroup_memory: true,
),
)
12 changes: 12 additions & 0 deletions naga/tests/in/sample-cube-array-depth-lod.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// see https://github.com/gfx-rs/wgpu/issues/4455

@group(0) @binding(0) var texture: texture_depth_cube_array;
@group(0) @binding(1) var texture_sampler: sampler_comparison;

@fragment
fn main() -> @location(0) f32 {
let pos = vec3<f32>(0.0);
let array_index: i32 = 0;
let depth: f32 = 0.0;
return textureSampleCompareLevel(texture, texture_sampler, pos, array_index, depth);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(
glsl: (
writer_flags: ("TEXTURE_SHADOW_LOD"),
version: Embedded(
version: 320,
is_webgl: false
),
binding_map: {},
zero_initialize_workgroup_memory: true,
),
)
12 changes: 12 additions & 0 deletions naga/tests/in/use-gl-ext-over-grad-workaround-if-instructed.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// see https://github.com/gfx-rs/wgpu/pull/5171

@group(0) @binding(0) var texture: texture_depth_2d_array;
@group(0) @binding(1) var texture_sampler: sampler_comparison;

@fragment
fn main() -> @location(0) f32 {
let pos = vec2<f32>(0.0);
let array_index: i32 = 0;
let depth: f32 = 0.0;
return textureSampleCompareLevel(texture, texture_sampler, pos, array_index, depth);
}
18 changes: 18 additions & 0 deletions naga/tests/out/glsl/sample-cube-array-depth-lod.main.Fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#version 320 es
#extension GL_EXT_texture_cube_map_array : require
#extension GL_EXT_texture_shadow_lod : require

precision highp float;
precision highp int;

uniform highp samplerCubeArrayShadow _group_0_binding_0_fs;

layout(location = 0) out float _fs2p_location0;

void main() {
vec3 pos = vec3(0.0);
float _e6 = textureLod(_group_0_binding_0_fs, vec4(pos, 0), 0.0, 0.0);
_fs2p_location0 = _e6;
return;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#version 320 es
#extension GL_EXT_texture_shadow_lod : require

precision highp float;
precision highp int;

uniform highp sampler2DArrayShadow _group_0_binding_0_fs;

layout(location = 0) out float _fs2p_location0;

void main() {
vec2 pos = vec2(0.0);
float _e6 = textureLod(_group_0_binding_0_fs, vec4(pos, 0, 0.0), 0.0);
_fs2p_location0 = _e6;
return;
}

5 changes: 5 additions & 0 deletions naga/tests/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,11 @@ fn convert_wgsl() {
Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL,
),
("cubeArrayShadow", Targets::GLSL),
("sample-cube-array-depth-lod", Targets::GLSL),
(
"use-gl-ext-over-grad-workaround-if-instructed",
Targets::GLSL,
),
(
"math-functions",
Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL,
Expand Down

0 comments on commit 7bc7220

Please sign in to comment.