Skip to content

[naga wgsl-in] Support abstract operands to binary operators. #4850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ This feature allowed you to call `global_id` on any wgpu opaque handle to get a

#### Naga

- Naga'sn WGSL front and back ends now have experimental support for 64-bit floating-point literals: `1.0lf` denotes an `f64` value. There has been experimental support for an `f64` type for a while, but until now there was no syntax for writing literals with that type. As before, Naga module validation rejects `f64` values unless `naga::valid::Capabilities::FLOAT64` is requested. By @jimblandy in [#4747](https://github.com/gfx-rs/wgpu/pull/4747).
- Naga's WGSL front end now allows binary operators to produce values with abstract types, rather than concretizing thir operands. By @jimblandy in [#4850](https://github.com/gfx-rs/wgpu/pull/4850).

- Naga's WGSL front and back ends now have experimental support for 64-bit floating-point literals: `1.0lf` denotes an `f64` value. There has been experimental support for an `f64` type for a while, but until now there was no syntax for writing literals with that type. As before, Naga module validation rejects `f64` values unless `naga::valid::Capabilities::FLOAT64` is requested. By @jimblandy in [#4747](https://github.com/gfx-rs/wgpu/pull/4747).

- Naga constant evaluation can now process binary operators whose operands are both vectors. By @jimblandy in [#4861](https://github.com/gfx-rs/wgpu/pull/4861).

### Changes
Expand Down
20 changes: 20 additions & 0 deletions naga/src/front/wgsl/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ pub enum Error<'a> {
source_span: Span,
source_type: String,
},
AutoConversionLeafScalar {
dest_span: Span,
dest_scalar: String,
source_span: Span,
source_type: String,
},
ConcretizationFailed {
expr_span: Span,
expr_type: String,
Expand Down Expand Up @@ -738,6 +744,20 @@ impl<'a> Error<'a> {
],
notes: vec![],
},
Error::AutoConversionLeafScalar { dest_span, ref dest_scalar, source_span, ref source_type } => ParseError {
message: format!("automatic conversions cannot convert elements of `{source_type}` to `{dest_scalar}`"),
labels: vec![
(
dest_span,
format!("a value with elements of type {dest_scalar} is required here").into(),
),
(
source_span,
format!("this expression has type {source_type}").into(),
)
],
notes: vec![],
},
Error::ConcretizationFailed { expr_span, ref expr_type, ref scalar, ref inner } => ParseError {
message: format!("failed to convert expression to a concrete type: {}", inner),
labels: vec![
Expand Down
86 changes: 22 additions & 64 deletions naga/src/front/wgsl/lower/construction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::{Handle, Span};

use crate::front::wgsl::error::Error;
use crate::front::wgsl::lower::{ExpressionContext, Lowerer};
use crate::front::wgsl::Scalar;

/// A cooked form of `ast::ConstructorType` that uses Naga types whenever
/// possible.
Expand Down Expand Up @@ -300,7 +299,10 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
},
Constructor::Type((_, &crate::TypeInner::Vector { size, scalar })),
) => {
ctx.convert_slice_to_common_scalar(std::slice::from_mut(&mut component), scalar)?;
ctx.convert_slice_to_common_leaf_scalar(
std::slice::from_mut(&mut component),
scalar,
)?;
expr = crate::Expression::Splat {
size,
value: component,
Expand All @@ -316,10 +318,11 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
Constructor::PartialVector { size },
) => {
let consensus_scalar =
automatic_conversion_consensus(&components, ctx).map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
ctx.convert_slice_to_common_scalar(&mut components, consensus_scalar)?;
ctx.automatic_conversion_consensus(&components)
.map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
let inner = consensus_scalar.to_inner_vector(size);
let ty = ctx.ensure_type_exists(inner);
expr = crate::Expression::Compose { ty, components };
Expand All @@ -343,14 +346,15 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
Constructor::PartialMatrix { columns, rows },
) if components.len() == columns as usize * rows as usize => {
let consensus_scalar =
automatic_conversion_consensus(&components, ctx).map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
ctx.automatic_conversion_consensus(&components)
.map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
// We actually only accept floating-point elements.
let consensus_scalar = consensus_scalar
.automatic_conversion_combine(crate::Scalar::ABSTRACT_FLOAT)
.unwrap_or(consensus_scalar);
ctx.convert_slice_to_common_scalar(&mut components, consensus_scalar)?;
ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
let vec_ty = ctx.ensure_type_exists(consensus_scalar.to_inner_vector(rows));

let components = components
Expand Down Expand Up @@ -420,10 +424,11 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
Constructor::PartialMatrix { columns, rows },
) => {
let consensus_scalar =
automatic_conversion_consensus(&components, ctx).map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
ctx.convert_slice_to_common_scalar(&mut components, consensus_scalar)?;
ctx.automatic_conversion_consensus(&components)
.map_err(|index| {
Error::InvalidConstructorComponentType(spans[index], index as i32)
})?;
ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
columns,
rows,
Expand Down Expand Up @@ -456,10 +461,10 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
// Array constructor - infer type
(components, Constructor::PartialArray) => {
let mut components = components.into_components_vec();
if let Ok(consensus_scalar) = automatic_conversion_consensus(&components, ctx) {
if let Ok(consensus_scalar) = ctx.automatic_conversion_consensus(&components) {
// Note that this will *not* necessarily convert all the
// components to the same type! The `automatic_conversion_consensus`
// function only considers the parameters' leaf scalar
// method only considers the parameters' leaf scalar
// types; the parameters themselves could be any mix of
// vectors, matrices, and scalars.
//
Expand All @@ -471,7 +476,7 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
// do. And if this array construction is not well-typed,
// these conversions will not make it so, and we can let
// validation catch the error.
ctx.convert_slice_to_common_scalar(&mut components, consensus_scalar)?;
ctx.convert_slice_to_common_leaf_scalar(&mut components, consensus_scalar)?;
} else {
// There's no consensus scalar. Emit the `Compose`
// expression anyway, and let validation catch the problem.
Expand Down Expand Up @@ -609,50 +614,3 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
Ok(handle)
}
}

/// Find the consensus scalar of `components` under WGSL's automatic
/// conversions.
///
/// If `components` can all be converted to any common scalar via
/// WGSL's automatic conversions, return the best such scalar.
///
/// The `components` slice must not be empty. All elements' types must
/// have been resolved.
///
/// If `components` are definitely not acceptable as arguments to such
/// constructors, return `Err(i)`, where `i` is the index in
/// `components` of some problematic argument.
///
/// This function doesn't fully type-check the arguments - it only
/// considers their leaf scalar types. This means it may return `Ok`
/// even when the Naga validator will reject the resulting
/// construction expression later.
fn automatic_conversion_consensus(
components: &[Handle<crate::Expression>],
ctx: &ExpressionContext<'_, '_, '_>,
) -> Result<Scalar, usize> {
let types = &ctx.module.types;
let mut inners = components
.iter()
.map(|&c| ctx.typifier()[c].inner_with(types));
log::debug!(
"wgsl automatic_conversion_consensus: {:?}",
inners
.clone()
.map(|inner| inner.to_wgsl(&ctx.module.to_ctx()))
.collect::<Vec<String>>()
);
let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?;
for (inner, i) in inners.zip(1..) {
let scalar = inner.scalar().ok_or(i)?;
match best.automatic_conversion_combine(scalar) {
Some(new_best) => {
best = new_best;
}
None => return Err(i),
}
}

log::debug!(" consensus: {:?}", best.to_wgsl());
Ok(best)
}
Loading