| # Tint Architecture |
| |
| ``` |
| ┏━━━━━━━━┓ ┏━━━━━━┓ |
| ┃ SPIR━V ┃ ┃ WGSL ┃ |
| ┗━━━━┃━━━┛ ┗━━━┃━━┛ |
| ▼ ▼ |
| ┏━━━━━━━━━┃━━━━━━━━━━━━━━━━━━━━━━━━━━━┃━━━━━━━━┓ |
| ┃ ┃ Reader ┃ ┃ |
| ┃ ┃ ┃ ┃ |
| ┃ ┏━━━━━━━┻━━━━━━┓ ┏━━━━━━┻━━━━━━┓ ┃ |
| ┃ ┃ SPIRV-Reader ┃ ┃ WGSL-Reader ┃ ┃ |
| ┃ ┗━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━┛ ┃ |
| ┗━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┛ |
| ▼ |
| ┏━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┓ |
| ┃ ProgramBuilder ┃ |
| ┃ (mutable) ┃ |
| ┏━━━━━━━━━━━━►┫ ┏━━━━━┓ ┏━━━━━━━━━┓ ┃ |
| ┃ ┃ ┃ AST ┃ ┃ Symbols ┃ ┃ |
| ┃ ┃ ┗━━━━━┛ ┗━━━━━━━━━┛ ┃ |
| ┃ ┗━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┛ |
| ┃ ▼ |
| ┃ ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┃┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ |
| ▲ ┆ Resolve ▼ ┆ |
| ┏━━━┻━━━┓ ┆ ┏━━━━━━━━┻━━━━━━━━┓ ┆ |
| ┃ Clone ┃ ┆ ┃ Resolver ┃ ┆ |
| ┗━━━┳━━━┛ ┆ ┗━━━━━━━━━━━━━━━━━┛ ┆ |
| ▲ └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┃┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘ |
| ┃ ▼ |
| ┃ ┏━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━┓ |
| ┃ ┃ Program ┃ |
| ┃ ┃ (immutable) ┃ |
| ┣━━━━━━◄┫ ┏━━━━━┓ ┏━━━━━━━━━━┓ ┏━━━━━━━━━┓ ┃ |
| ┃ ┃ ┃ AST ┃ ┃ Semantic ┃ ┃ Symbols ┃ ┃ |
| ┃ ┃ ┗━━━━━┛ ┗━━━━━━━━━━┛ ┗━━━━━━━━━┛ ┃ |
| ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┛ |
| ▲ ▼ |
| ┏━━━━━┻━━━━━┓ ┃ ┏━━━━━━━━━━━┓ |
| ┃ Transform ┃◄━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━►┃ Inspector ┃ |
| ┗━━━━━━━━━━━┛ ┃ ┗━━━━━━━━━━━┛ |
| ▼ |
| ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ |
| ┃ Writers ┃ |
| ┃ ┃ |
| ┃ ┏━━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━┓┏━━━━━━━━━━━━┓ ┃ |
| ┃ ┃ SPIRV-Writer ┃┃ WGSL-Writer ┃┃ HLSL-Writer ┃┃ GLSL-Writer ┃┃ MSL-Writer ┃ ┃ |
| ┃ ┗━━━━━━━┳━━━━━━┛┗━━━━━━┳━━━━━━┛┗━━━━━━┳━━━━━━┛┗━━━━━━┳━━━━━━┛┗━━━━━━┳━━━━━┛ ┃ |
| ┗━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━┛ |
| ▼ ▼ ▼ ▼ ▼ |
| ┏━━━━┻━━━┓ ┏━━━┻━━┓ ┏━━━┻━━┓ ┏━━━┻━━┓ ┏━━┻━━┓ |
| ┃ SPIR-V ┃ ┃ WGSL ┃ ┃ HLSL ┃ ┃ GLSL ┃ ┃ MSL ┃ |
| ┗━━━━━━━━┛ ┗━━━━━━┛ ┗━━━━━━┛ ┗━━━━━━┛ ┗━━━━━┛ |
| ``` |
| |
| ## Reader |
| |
| Readers are responsible for parsing a shader program and populating a |
| `ProgramBuilder` with the parsed AST and symbol information. |
| |
| The WGSL reader is a recursive descent parser. It closely follows the WGSL |
| grammar in the naming of the parse methods. |
| |
| ## ProgramBuilder |
| |
| A `ProgramBuilder` is the interface to construct an immutable `Program`. |
| There are a large number of helper methods for simplifying the creation of the |
| AST nodes. A `ProgramBuilder` can only be used once, and must be discarded after |
| the `Program` is constructed. |
| |
| A `Program` is built from the `ProgramBuilder` via a call to |
| `resolver::Resolve()`, which will perform validation and semantic analysis. |
| The returned program will contain the semantic information which can be obtained |
| by calling `Program::Sem()`. |
| |
| At any time before building the `Program`, `ProgramBuilder::IsValid()` may be |
| called to ensure that no error diagnostics have been raised during the |
| construction of the AST. This includes parser syntax errors, but not semantic |
| validation which happens during the `Resolve` phase. |
| |
| If further changes to the `Program` are needed (say via a `Transform`) then a |
| new `ProgramBuilder` can be produced by cloning the `Program` into a new |
| `ProgramBuilder`. |
| |
| Unlike `Program`s, `ProgramBuilder`s are not part of the public Tint API. |
| |
| ## AST |
| |
| The Abstract Syntax Tree is a directed acyclic graph of `ast::Node`s which |
| encode the syntactic structure of the WGSL program. |
| |
| The root of the AST is the `ast::Module` class which holds each of the declared |
| functions, variables and user declared types (type aliases and structures). |
| |
| Each `ast::Node` represents a **single** part of the program's source, and so |
| `ast::Node`s are not shared. |
| |
| The AST does not perform any verification of its content. For example, the |
| `ast::Array` node has numeric size parameter, which is not validated to be |
| within the WGSL specification limits until validation of the `Resolver`. |
| |
| ## Semantic information |
| |
| Semantic information is held by `sem::Node`s which describe the program at |
| a higher / more abstract level than the AST. This includes information such as |
| the resolved type of each expression, the resolved overload of a builtin |
| function call, and the module scoped variables used by each function. |
| |
| Semantic information is generated by the `Resolver` when the `Program` |
| is built from a `ProgramBuilder`. |
| |
| The `sem::Info` class holds a map of `ast::Node`s to `sem::Node`s. |
| This map is **many-to-one** - i.e. while a AST node might have a single |
| corresponding semantic node, the reverse may not be true. For example: |
| many `ast::IdentifierExpression` nodes may map to a single `sem::Variable`, |
| and so the `sem::Variable` does not have a single corresponding |
| `ast::Node`. |
| |
| Unlike `ast::Node`s, semantic nodes may not necessarily form a directed acyclic |
| graph, and the semantic graph may contain diamonds. |
| |
| ## Types |
| |
| AST types are regular AST nodes, in that they uniquely represent a single part |
| of the parsed source code. Unlike semantic types, identical AST types are not |
| de-duplicated as they refer to the source usage of the type. |
| |
| Semantic types are constructed during the `Resolver` phase, and are held by |
| the `Program` or `ProgramBuilder`. |
| |
| Each `sem::Type` node **uniquely** represents a particular WGSL type within the |
| program, so you can compare `type::Type*` pointers to check for type |
| equivalence. For example, a `Program` will only hold one instance of the |
| `sem::I32` semantic type, no matter how many times an `i32` is mentioned in the |
| source program. |
| |
| WGSL type aliases resolve to their target semantic type. For example, given: |
| |
| ```wgsl |
| type MyI32 = i32; |
| const a : i32 = 1; |
| const b : MyI32 = 2; |
| ``` |
| |
| The **semantic** types for the variables `a` and `b` will both be the same |
| `sem::I32` node pointer. |
| |
| ## Symbols |
| |
| Symbols represent a unique string identifier in the source program. These string |
| identifiers are transformed into symbols within the `Reader`s. |
| |
| During the Writer phase, symbols may be emitted as strings using a `Namer`. |
| A `Namer` may output the symbol in any form that preserves the uniqueness of |
| that symbol. |
| |
| ## Resolver |
| |
| The `Resolver` will automatically run when a `Program` is built. |
| A `Resolver` creates the `Program`s semantic information by analyzing the |
| `Program`s AST and type information. |
| |
| The `Resolver` will validate to make sure the generated `Program` is |
| semantically valid. |
| |
| ## Program |
| |
| A `Program` holds an immutable version of the information from the |
| `ProgramBuilder` along with semantic information generated by the |
| `Resolver`. |
| |
| `Program::IsValid()` may be called to ensure the program is structurally correct |
| **and** semantically valid, and that the `Resolver` did not report any errors |
| during validation. |
| |
| Unlike the `ProgramBuilder`, a `Program` is fully immutable, and is part of the |
| public Tint API. The immutable nature of `Program`s make these entirely safe |
| to share between multiple threads without the use of synchronization primitives. |
| |
| ## Inspector |
| |
| The inspectors job is to go through the `Program` and pull out various pieces of |
| information. The information may be used to pass information into the downstream |
| compilers (things like specialization constants) or may be used to pass into |
| transforms to update the AST before generating the resulting code. |
| |
| The input `Program` to the inspector must be valid (pass validation). |
| |
| ## Transforms |
| |
| There maybe various transforms we want to run over the `Program`. |
| This is for things like Vertex Pulling or Robust Buffer Access. |
| |
| A transform operates by cloning the input `Program` into a new `ProgramBuilder`, |
| applying the required changes, and then finally building and returning a new |
| output `Program`. As the resolver is always run when a `Program` is built, |
| Transforms will always emit a `Program` with semantic information. |
| |
| The input `Program` to a transform must be valid (pass validation). |
| If the input `Program` of a transform is valid then the transform must guarantee |
| that the output program is also valid. |
| |
| ## Writers |
| |
| A writer is responsible for writing the `Program` in the target shader language. |
| |
| The input `Program` to a writer must be valid (pass validation). |