Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,13 @@ Bottom level categories:
- `TextureFormat::is_srgb()` has been renamed to `TextureFormat::has_srgb_suffix()` to clarify its function. By @kpreid in [#9758](https://github.com/gfx-rs/wgpu/pull/9758).
- Remove the never-constructed `CreateBlasError::InvalidAabbStride` variant. `create_blas` takes no stride, so it could never be produced; AABB stride is validated at build time as `BuildAccelerationStructureError::InvalidAabbStride`. By @mstampfli in [#9935](https://github.com/gfx-rs/wgpu/pull/9935).

- Added `DownlevelFlags::LINEAR_INTERPOLATION`, indicating that the adapter supports `@interpolate(linear)`. It is absent on GLES/WebGL2, since GLSL ES has no `noperspective` qualifier. By @emilk in [#9972](https://github.com/gfx-rs/wgpu/pull/9972).

#### naga

- `naga::valid::ValidationError` is now always returned boxed, to avoid `clippy::large_result_err` warning. By @beicause in [#9612](https://github.com/gfx-rs/wgpu/pull/9612)
- Added `naga::valid::Capabilities::LINEAR_INTERPOLATION`, which is now required in order to use `@interpolate(linear)`. By @emilk in [#9972](https://github.com/gfx-rs/wgpu/pull/9972).
- The GLSL backend's `MissingFeatures` error now names the GLSL version that lacks the features, e.g. `GLSL 300 es doesn't support the required feature(s): NOPERSPECTIVE_QUALIFIER`. By @emilk in [#9972](https://github.com/gfx-rs/wgpu/pull/9972).

### Bug Fixes

Expand Down Expand Up @@ -103,6 +107,7 @@ Bottom level categories:

#### GLES

- `@interpolate(linear)` is now rejected by `Device::create_shader_module` on adapters that lack `DownlevelFlags::LINEAR_INTERPOLATION` (GLES/WebGL2), with a shader label and a source span. Previously such a shader validated fine and then failed at pipeline creation with `The selected version doesn't support Features(NOPERSPECTIVE_QUALIFIER)`. By @emilk in [#9972](https://github.com/gfx-rs/wgpu/pull/9972).
- Fixed signed integer `%` (and `%=`) returning the wrong result for negative operands in the GLSL (OpenGL/GLES) backend, e.g. `-1 % 768` yielding `255` instead of `-1`. GLSL's `%` is undefined when either operand is negative, so signed remainder is now lowered as `a - b * (a / b)`, matching the SPIR-V, HLSL, and Metal backends. By @mstampfli in [#9687](https://github.com/gfx-rs/wgpu/pull/9687).

#### WebGPU
Expand Down
9 changes: 8 additions & 1 deletion naga/src/back/glsl/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ bitflags::bitflags! {
}
}

impl core::fmt::Display for Features {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
// `Debug` would print this as `Features(A | B)`; we only want `A | B`.
bitflags::parser::to_writer(self, f)
}
}

/// Helper structure used to store the required [`Features`] needed to output a
/// [`Module`](crate::Module)
///
Expand Down Expand Up @@ -144,7 +151,7 @@ impl FeaturesManager {
if missing.is_empty() {
Ok(())
} else {
Err(Error::MissingFeatures(missing))
Err(Error::MissingFeatures(missing, version))
}
}

Expand Down
8 changes: 5 additions & 3 deletions naga/src/back/glsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,9 @@ pub enum 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),
/// Contains the missing [`Features`], and the version that lacks them.
#[error("GLSL {1} doesn't support the required feature(s): {0}")]
MissingFeatures(Features, Version),
/// [`AddressSpace::Immediate`](crate::AddressSpace::Immediate) was used more than
/// once in the entry point, which isn't supported.
#[error("Multiple immediates aren't supported")]
Expand Down Expand Up @@ -506,4 +506,6 @@ pub fn supported_capabilities() -> valid::Capabilities {
| Caps::MEMORY_DECORATION_COHERENT
| Caps::MEMORY_DECORATION_VOLATILE
| Caps::STORAGE_TEXTURE_16BIT_NORM_FORMATS
// `noperspective` only exists in desktop GLSL; `check_availability` rejects it on ES.
| Caps::LINEAR_INTERPOLATION
Comment thread
emilk marked this conversation as resolved.
Outdated
}
1 change: 1 addition & 0 deletions naga/src/back/hlsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -830,4 +830,5 @@ pub fn supported_capabilities() -> crate::valid::Capabilities {
// No DRAW_INDEX
// No MEMORY_DECORATION_VOLATILE
| Caps::MEMORY_DECORATION_COHERENT
| Caps::LINEAR_INTERPOLATION
}
1 change: 1 addition & 0 deletions naga/src/back/msl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ pub fn supported_capabilities() -> crate::valid::Capabilities {
// No DRAW_INDEX
// No MEMORY_DECORATION_VOLATILE
| Caps::MEMORY_DECORATION_COHERENT
| Caps::LINEAR_INTERPOLATION
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions naga/src/back/spv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1226,4 +1226,5 @@ pub fn supported_capabilities() -> crate::valid::Capabilities {
| Caps::DRAW_INDEX
| Caps::MEMORY_DECORATION_COHERENT
| Caps::MEMORY_DECORATION_VOLATILE
| Caps::LINEAR_INTERPOLATION
}
9 changes: 7 additions & 2 deletions naga/src/valid/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,12 +785,17 @@ impl VaryingContext<'_> {
// qualifiers, so we won't complain about that here.
let _ = sampling;

let required = match sampling {
let mut required = match sampling {
Some(crate::Sampling::Sample) => Capabilities::MULTISAMPLED_SHADING,
_ => Capabilities::empty(),
};
if interpolation == Some(crate::Interpolation::Linear) {
required |= Capabilities::LINEAR_INTERPOLATION;
}
if !self.capabilities.contains(required) {
return Err(VaryingError::UnsupportedCapability(required));
return Err(VaryingError::UnsupportedCapability(
required - self.capabilities,
));
}

if interpolation != Some(crate::Interpolation::PerVertex) {
Expand Down
8 changes: 8 additions & 0 deletions naga/src/valid/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@ bitflags::bitflags! {
const MEMORY_DECORATION_VOLATILE = 1 << 42;
/// Support for 16-bit integer types.
const SHADER_INT16 = 1 << 43;
/// Support for [`Interpolation::Linear`] (`@interpolate(linear)` in WGSL).
///
/// This is core WebGPU, but GLSL ES (and thus WebGL2) has no
/// `noperspective` qualifier, so the GLES backend can only offer it on
/// desktop GL.
///
/// [`Interpolation::Linear`]: crate::Interpolation::Linear
const LINEAR_INTERPOLATION = 1 << 44;
}
}

Expand Down
1 change: 1 addition & 0 deletions naga/tests/in/wgsl/interpolate.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
targets = "SPIRV | METAL | HLSL | WGSL"
capabilities = "LINEAR_INTERPOLATION"

[glsl]
version.Desktop = 400
Expand Down
2 changes: 2 additions & 0 deletions naga/tests/in/wgsl/interpolate_compat.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
capabilities = "LINEAR_INTERPOLATION"

[glsl]
version.Desktop = 400
writer_flags = ""
Expand Down
46 changes: 46 additions & 0 deletions naga/tests/naga/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,52 @@ fn no_flat_first_in_glsl() {
));
}

#[test]
fn no_linear_interpolation_in_glsl_es() {
use dummy_interpolation_shader::DummyInterpolationShader;

let DummyInterpolationShader {
source: _,
module,
interpolate_attr,
entry_point,
} = DummyInterpolationShader::new(naga::Interpolation::Linear, None);

let mut validator = naga::valid::Validator::new(Default::default(), valid::Capabilities::all());
let module_info = validator.validate(&module).unwrap();

let options = naga::back::glsl::Options {
version: naga::back::glsl::Version::Embedded {
version: 300,
is_webgl: true,
},
..Default::default()
};
let pipeline_options = naga::back::glsl::PipelineOptions {
shader_stage: naga::ShaderStage::Fragment,
entry_point: entry_point.to_owned(),
multiview: None,
};
let err = naga::back::glsl::Writer::new(
String::new(),
&module,
&module_info,
&options,
&pipeline_options,
Default::default(),
)
.err()
.unwrap_or_else(|| {
panic!("`{interpolate_attr}` should fail backend validation on GLSL ES");
});

// The message must name the version and the feature, so that it is actionable.
assert_eq!(
err.to_string(),
"GLSL 300 es doesn't support the required feature(s): NOPERSPECTIVE_QUALIFIER"
);
}

mod dummy_interpolation_shader {
pub struct DummyInterpolationShader {
pub source: String,
Expand Down
30 changes: 30 additions & 0 deletions naga/tests/naga/wgsl_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,36 @@ fn per_vertex_capability() {
}
}

#[test]
fn linear_interpolation_capability() {
// Regression test for https://github.com/gfx-rs/wgpu/issues/9971: `@interpolate(linear)`
// has no GLSL ES equivalent, so it must be rejected during validation rather than
// silently passing and then failing in the GLSL backend at pipeline creation.
let source = r#"
@fragment
fn fs_main(@location(0) @interpolate(linear) v: f32) -> @location(0) vec4<f32> {
return vec4(v, 0.0, 0.0, 1.0);
}
"#;

check_one_validation! {
source,
Err(naga::valid::ValidationError::EntryPoint {
stage: naga::ShaderStage::Fragment,
source: valid::EntryPointError::Argument(
0,
valid::VaryingError::UnsupportedCapability(Capabilities::LINEAR_INTERPOLATION),
),
..
})
}

no_validation_error(
source,
Capabilities::default() | Capabilities::LINEAR_INTERPOLATION,
);
}

#[test]
fn multiple_enables_valid() {
check_success(
Expand Down
64 changes: 64 additions & 0 deletions tests/tests/wgpu-gpu/linear_interpolation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use wgpu_test::{
apply, fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext,
};

pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
vec.push(LINEAR_INTERPOLATION_GATED);
}

const SHADER_SRC: &str = "
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
var out: VertexOutput;
out.pos = vec4f(f32(vertex_index), 0.0, 0.0, 1.0);
out.v = 1.0;
return out;
}

struct VertexOutput {
@builtin(position) pos: vec4f,
@location(0) @interpolate(linear) v: f32,
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
return vec4f(in.v);
}
";

/// `@interpolate(linear)` has no GLSL ES equivalent, so on adapters without
/// [`wgpu::DownlevelFlags::LINEAR_INTERPOLATION`] it must be rejected up front by
/// `create_shader_module`, instead of failing later during pipeline creation with an
/// internal naga bitflag name.
///
/// Regression test for <https://github.com/gfx-rs/wgpu/issues/9971>.
fn linear_interpolation_gated(ctx: TestingContext) {
let supported = ctx
.adapter
.get_downlevel_capabilities()
.flags
.contains(wgpu::DownlevelFlags::LINEAR_INTERPOLATION);

let create = || {
ctx.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("linear-interpolation"),
source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),
})
};

if supported {
create();
} else {
fail(&ctx.device, create, Some("LINEAR_INTERPOLATION"));
}
}

#[apply(gpu_test!)]
static LINEAR_INTERPOLATION_GATED: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.downlevel_flags(wgpu::DownlevelFlags::empty())
.limits(wgpu::Limits::downlevel_webgl2_defaults()),
)
.run_sync(linear_interpolation_gated);
2 changes: 2 additions & 0 deletions tests/tests/wgpu-gpu/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod image_atomics;
mod immediates;
mod instance;
mod life_cycle;
mod linear_interpolation;
mod mem_leaks;
mod mesh_shader;
mod multiview;
Expand Down Expand Up @@ -117,6 +118,7 @@ fn all_tests() -> Vec<wgpu_test::GpuTestInitializer> {
image_atomics::all_tests(&mut tests);
instance::all_tests(&mut tests);
life_cycle::all_tests(&mut tests);
linear_interpolation::all_tests(&mut tests);
mem_leaks::all_tests(&mut tests);
mesh_shader::all_tests(&mut tests);
multiview::all_tests(&mut tests);
Expand Down
7 changes: 7 additions & 0 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,13 @@ impl super::Adapter {
wgt::DownlevelFlags::MULTISAMPLED_SHADING,
supported((3, 2), (4, 0)) || extensions.contains("OES_sample_variables"),
);
// GLSL ES has no `noperspective` qualifier, so `@interpolate(linear)` is only
// expressible on desktop GLSL (where we require at least 330, well past the 130
// that introduced `noperspective`).
downlevel_flags.set(
wgt::DownlevelFlags::LINEAR_INTERPOLATION,
!shading_language_version.is_es(),
);
let query_buffers = extensions.contains("GL_ARB_query_buffer_object")
|| extensions.contains("GL_AMD_query_buffer_object");
if query_buffers {
Expand Down
4 changes: 4 additions & 0 deletions wgpu-naga-bridge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ pub fn features_to_naga_capabilities(
Caps::RAY_TRACING_PIPELINE,
features.intersects(wgt::Features::EXPERIMENTAL_RAY_TRACING_PIPELINES),
);
caps.set(
Caps::LINEAR_INTERPOLATION,
downlevel.contains(wgt::DownlevelFlags::LINEAR_INTERPOLATION),
);
caps
}

Expand Down
7 changes: 7 additions & 0 deletions wgpu-types/src/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,13 @@ bitflags::bitflags! {
///
/// See <https://www.w3.org/TR/webgpu/#adapter-capability-guarantees>.
const TEXTURE_COMPRESSION = 1 << 25;

/// Supports `@interpolate(linear)` (a.k.a. `noperspective`) on shader inter-stage
/// variables.
///
/// GLSL ES has no `noperspective` qualifier, so the GLES backend only supports this
/// on desktop OpenGL, not on GLES/WebGL2.
const LINEAR_INTERPOLATION = 1 << 26;
}
}

Expand Down
Loading