A vector constructor with multiple arguments is represented in IR as a
Compose expression. These can be nested, for example due to code such
as this: `vec4(1, vec3(2, vec2(3, 4)))`. The total number of
components of each argument must match the vector size, meaning
`vec4(1, 2, 3, 4, 5)` is invalid. The validation pass already catches
such occurences. However, validation runs after constant evaluation,
meaning this can cause issues if the expression is evaluated as part
of a const expression.
When applying an operation to a vector compose during const
evaluation, we typically "flatten" the vector using the function
`proc::flatten_compose()`. This silently truncates the list of
components to the size implied by the type, meaning invalid code is
accepted.
This patch validates that the total number of components in a Compose
expression, once flattened, matches the size implied by the type. We
do so once as each expression is registered to avoid needing to handle
this each time the expression is used in a const expression.
For nested compose expressions, the inner expressions will be registed
before the outer ones. This means we can trust that the size implied
by the inner expression's type is correct, as it will have already
been validated, and can therefore avoid recursing through each nested
expression when registering the outer expression.
Add a new method to the `naga::common::wgsl::TryToWgsl` trait,
`to_wgsl_for_diagnostics`, which always returns a `String`, falling
back to the `Debug` representation of the type if necessary.
Provide a custom implementation for `Scalar` that shows abstract types
in a reasonable way.
Provide default implementations for the `write_non_wgsl_inner` and
`write_non_wgsl_scalar` methods of `naga::common::wgsl::TypeContext`.
We will be implementing this trait for various types in the WGSL front
end and the validator, and almost all of these applications will need
basically identical implementations of these methods.
Add new default methods, `type_to_string`, `type_inner_to_string`, and
`resolution_to_string` to the `naga::common::wgsl::TypeResolution`
trait, for generating WGSL source strings from these types.
In `naga::common::wgsl::types`, move the type parameter `<W: Write>`
from the `TypeContext` trait itself to the individual methods of the
trait. It is very unlikely for Naga to need to be able to implement
`TypeContext`, but only for one particular kind of output stream.
The motivation for this change is that the old parameterization makes
it awkward to provide utility methods for generating `String`s on the
trait, which we do in subsequent commits. In order to write to a
`String`, such utility methods need `Self` to implement
`TypeContext<String>`, so you can add bounds to the methods like this:
fn type_to_string(..., out: &mut W) -> ...
where Self: TypeContext<String>
{
... self.write(..., &mut string_buf)?;
}
That will compile, but if you try to actually call it, Rust gets
confused. Remember, the above is not a method on
`TypeContext<String>`, it's a method on `TypeContext<W>` that uses
`TypeContext<String>` internally. So when you write
`ctx.type_to_string(...)`, Rust needs to decide whether `ctx`
implements `TypeContext<W>` for some completely unspecified `W`, and
asks for type annotations.
You could supply type annotations, but this would be basically
supplying some never-used type that implements `core::fmt::Write`.
Instead of
ctx.type_to_string(handle)
you'd have to write
TypeContext::<String>::type_to_string(ctx, handle)
which is dumb.
(I don't *think* this explanation belongs in the code, since it's an
explanation of a design *not* used, replaced by a design that's pretty
natural --- so I'll leave it here.)
Put the `use` directives at the top of `front::wgsl::error` in the
usual order:
- identifiers defined by this crate itself
- identifiers defined by other crates
- identifiers from the standard library
This commit should cause no change in behavior.
Extend the `naga::common::wgsl::TypeContext` trait with new methods,
`write_non_wgsl_inner` and `write_non_wgsl_scalar`, which are called
when `write_type`, `write_type_inner`, or `write_scalar` are presented
with Naga IR that cannot be expressed in WGSL. Delegating these cases
lets most uses of `unreachable!` be removed.
In the WGSL backend, implement `write_non_wgsl_inner` and
`write_non_wgsl_scalar` to call `unreachable!`, preserving existing
behavior.
In `naga::common::wgsl::types`, remove `unwrap_to_wgsl` from
`try_write_type_inner` and replace its uses with calls to a new trait
method, `write_scalar`, with a default definition. This should have no
effect on externally visible behavior, but it makes subsequent commits
easier to read.
Move the guts of the `naga::common::wgsl::TypeContext` trait's
`write_type_inner` method into a new function, `try_write_type_inner`,
private to the module.
This commit is only code motion; there should be no change to
behavior. Subsequent commits will introduce a private error type, with
the new function serving as a `try` block.
In `naga::common::wgsl`, move the `ToWgsl` and `TryToWgsl` trait
definitions and impls into their own submodule, `to_wgsl`.
This change is only code motion; there should be no change in
behavior.
In `naga::common::wgsl`, move `DisplayFilterableTriggeringRule` and
diagnostics-related impls into a new sub-module, `diagonostics`.
This change is only code motion; there should be no change in
behavior.
Use `String` in variants of `naga::front::wgsl::Error`, not
`Box<str>`.
Delete test `naga::front::wgsl::error::test_error_size`. Since `Error`
is always returned boxed, its size should have no effect on `Result`
size.
Change the definition of `naga::front::wgsl::Result` to carry
a boxed `Error`, so that the size of `Error` does not affect
performance of success paths.
Introduce a new type alias, `naga::front::wgsl::Result`, that is
`core::result::Result` with the error type fixed to
`naga::front::wgsl::Error`. Use this alias where appropriate.
This commit should cause no changes in behavior or interface.
The types remain available at their original paths by `pub use ir::*`.
This may or may not be a temporary measure for migration.
The only changes to the moved code are:
* Fixing doc links.
* Moving `pub use block::Block` to the top.
The `#![allow(dead_code)]` on the module was hiding dead code in the
child modules. I changed it to be conditional on actually having no
backends enabled. (Note that with #7349, there would actually be no
warnings and we could remove the `allow(dead_code)` entirely, but I
expect that state of affairs won’t necessarily persist.)
Place the `#[track_caller]` attribute on the `check` and
`validation_error` functions in the Naga `wgsl_errors` test module, so
that panics or assertion failures will point to the actual test,
not to the code in the utility function.
This is a useful shortcut for tests and example code, allowing it to
create a noop device without needing to deal with nonexistent
fallibility and asynchrony.
* Added “upward” links from `VertexAttribute` and `VertexBufferLayout`
to the places they are used.
* Documented the exact expected inputs of `vertex_attr_array!` and hid
the internal implementation.
* Added doc-test examples for:
* `VertexBufferLayout`, demonstrating how to construct a
`VertexBufferLayout` that corresponds to a `struct`.
* `vertex_attr_array!`, demonstrating exactly what the output of the
macro is.
This will help users determine whether the problem is:
* a backend is not statically enabled
* a backend is not dynamically enabled
* no drivers or physical adapters are present for that backend
(further distinction would be useful here)
* no adapters met the required criteria
There are deficiencies in the reporting of WebGPU vs. WebGL support.
Those would best be fixed by also fixing the mutual exclusion of those
backends.
* Add `# Performance considerations` heading, to separate the facts
about how to use the method from the advice about how best to use the
method.
Back when I was learning `wgpu`, I thought that “does *not* submit the
transfer to the GPU immediately” was a claim about *ordering* relevant
to correctness, not just performance. This reorganization and
rewording should help future readers build an accurate model quicker.
* Clarify when it is best to use `write_buffer_with()` — in particular,
if you just copy from other memory, then you aren't making any
improvement over `write_buffer()`, and the advantage comes from being
able to assemble your data in-place.
* Flesh out the first `queue.submit([])` example code block,
and trim back the second one.
* Mention what techniques you might use for writing to a texture other
other than `write_texture()`.
Change the match in the `naga::common::wgsl::TypeContext` trait's
default implementation of `write_type_inner` to be exhaustive, so that
it is more likely to be kept up to date when changes are made to the
IR.
Coincidentally, add support for `TypeInner::RayQuery`.
In `naga::back::wgsl`, move `WriterTypeContext` out of the way, and
coalesce `Writer`'s methods back into a single `impl` block.
This is just code motion; there should be no change in behavior.
Move the `TypeContext` trait from `naga::back::wgsl` into
`naga::common::wgsl`. Adjust imports and publicity markers as needed.
There should be no changes to behavior in this commit, only code
motion.