Skip to content

Commit

Permalink
Merge pull request #41 from robtfm/rusty-imports
Browse files Browse the repository at this point in the history
Rusty imports
  • Loading branch information
cart authored Oct 18, 2023
2 parents 58e2272 + 39dd090 commit ac56a00
Show file tree
Hide file tree
Showing 45 changed files with 1,404 additions and 664 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ default = ["test_shader"]
test_shader = []
prune = []
override_any = []
allow_deprecated = []

[dependencies]
naga = { version = "0.13", features = ["wgsl-in", "wgsl-out", "glsl-in", "glsl-out", "clone", "span"] }
Expand Down
31 changes: 25 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,51 @@ fn my_func() -> f32 {
}
```

shaders can then import the module with an `#import` directive (with an optional `as` name). at point of use, imported items must be qualified:
alternatively the module name can be specified as an argument to `Composer::add_composable_module`.

shaders can then import the module with an `#import` directive (with an optional `as` name) :

```wgsl
#import my_module
#import my_other_module as Mod2
#import my_module;
#import my_other_module as mod2;
fn main() -> f32 {
let x = my_module::my_func();
let y = Mod2::my_other_func();
let y = mod2::my_other_func();
return x*y;
}
```

or import a comma-separated list of individual items :

```wgsl
#import my_module my_func, my_const
#import my_module::{my_func, my_const}
fn main() -> f32 {
return my_func(my_const);
}
```

Some rust-style import syntax is supported, and items can be directly imported using the fully qualified item name :

```wgsl
#import my_package::{
first_module::{item_one as item, item_two},
second_module::submodule,
}
fn main() -> f32 {
return item + item_two + submodule::subitem + my_package::third_module::item;
}
```

`module::self` and `module::*` are not currently supported.

imports can be nested - modules may import other modules, but not recursively. when a new module is added, all its `#import`s must already have been added.
the same module can be imported multiple times by different modules in the import tree.
there is no overlap of namespaces, so the same function names (or type, constant, or variable names) may be used in different modules.

note: when importing an item with the `#import module item` directive, the final shader will include the required dependencies (bindings, globals, consts, other functions) of the imported item, but will not include the rest of the imported module. it will however still include all of any modules imported by the imported module. this is probably not desired in general and may be fixed in a future version. currently for a more complete culling of unused dependencies the `prune` module can be used.
note: the final shader will include the required dependencies (bindings, globals, consts, other functions) of any imported items that are used, but will not include the rest of the imported module.

## overriding functions

Expand All @@ -79,6 +96,8 @@ override fn Lighting::point_light (world_position: vec3<f32>) -> vec3<f32> {
}
```

overrides must either be declared in the top-level shader, or the module containing the override must be imported as an `additional_import` in a `Composer::add_composable_module` or `Composer::make_naga_module` call. using `#import` to import a module with overrides will not work due to tree-shaking.

override function definitions cause *all* calls to the original function in the entire shader scope to be replaced by calls to the new function, with the exception of calls within the override function itself.

the function signature of the override must match the base function.
Expand Down
17 changes: 8 additions & 9 deletions src/compose/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ use codespan_reporting::{
use thiserror::Error;
use tracing::trace;

use super::{
preprocess::{PreprocessOutput, PreprocessorMetaData},
Composer, ShaderDefValue,
};
use super::{preprocess::PreprocessOutput, Composer, ShaderDefValue};
use crate::{compose::SPAN_SHIFT, redirect::RedirectError};

#[derive(Debug)]
Expand Down Expand Up @@ -42,18 +39,14 @@ impl ErrSource {
let raw_source = &composer.module_sets.get(name).unwrap().sanitized_source;
let Ok(PreprocessOutput {
preprocessed_source: source,
meta: PreprocessorMetaData { imports, .. },
..
}) = composer
.preprocessor
.preprocess(raw_source, defs, composer.validate)
else {
return Default::default();
};

let Ok(source) = composer.substitute_shader_string(&source, &imports) else {
return Default::default();
};

Cow::Owned(source)
}
ErrSource::Constructing { source, .. } => Cow::Borrowed(source),
Expand All @@ -77,6 +70,8 @@ pub struct ComposerError {

#[derive(Debug, Error)]
pub enum ComposerErrorInner {
#[error("{0}")]
ImportParseError(String, usize),
#[error("required import '{0}' not found")]
ImportNotFound(String, usize),
#[error("{0}")]
Expand Down Expand Up @@ -215,6 +210,10 @@ impl ComposerError {
vec![Label::primary((), *pos..*pos)],
vec![format!("missing import '{msg}'")],
),
ComposerErrorInner::ImportParseError(msg, pos) => (
vec![Label::primary((), *pos..*pos)],
vec![format!("invalid import spec: '{msg}'")],
),
ComposerErrorInner::WgslParseError(e) => (
e.labels()
.map(|(range, msg)| {
Expand Down
Loading

0 comments on commit ac56a00

Please sign in to comment.