207 lines
11 KiB
Markdown
207 lines
11 KiB
Markdown
# 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` by `std::move()`ing the
|
|
`ProgramBuilder` to a new `Program` object. When built, resolution is performed
|
|
so the produced `Program` will contain all the needed semantic information.
|
|
|
|
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).
|