blob: 42a30a82638580c080f93e5fd9e73bd79a1c710c [file] [view]
# Dawn's code generators.
Dawn relies on a lot of code generation to produce boilerplate code, especially webgpu.h-related
code. They start by reading some JSON files (and sometimes XML too), process the data into an
in-memory representation that's then used by some [Jinja2](https://jinja.palletsprojects.com/)
templates to generate the code. This is similar to the model/view separation in Web development.
Generators are based on [generator_lib.py](../../generator/generator_lib.py) which provides
facilities for integrating in build systems and using Jinja2. Templates can be found
in [`generator/templates`](../../generator/templates) and the generated files are in
`out/<Debug/Release/foo>/gen/src` when building Dawn in standalone. Generated files can also be
found in [Chromium's code
search](https://source.chromium.org/chromium/chromium/src/+/main:out/Debug/gen/third_party/dawn/src/).
## Dawn "JSON API" generators
Most of the code generation is done from [`dawn.json`](../../src/dawn/dawn.json) which is a JSON
description of the WebGPU API with extra annotation used by some of the generators. The code for
all the "Dawn JSON" generators is
in [`dawn_json_generator.py`](../../generator/dawn_json_generator.py) (with templates in the
regular template dir).
At this time it is used to generate:
- the Dawn, Emscripten, and upstream webgpu-native `webgpu.h` C header
- the Dawn and Emscripten `webgpu_cpp.h` C++ wrapper over the C header
- libraries that implements `webgpu.h` by calling in a static or `thread_local` proc table
- the [Emscripten](https://emscripten.org/) WebGPU binding implementation
- a GMock version of the API with its proc table for testing
- validation helper functions for dawn_native
- the definition of dawn_native's proc table
- dawn_native's internal version of the webgpu.h types
- utilities for working with dawn_native's chained structs
- a lot of dawn_wire parts, see below
Internally `dawn.json` is a dictionary from the "canonical name" of things to their definition.
The "canonical name" is a space-separated (mostly) lower-case version of the name that's parsed
into a `Name` Python object. Then that name can be turned into various casings with `.CamelCase()`
`.SNAKE_CASE()`, etc. When `dawn.json` things reference each other, it is always via
these "canonical names".
The `"_metadata"` key in the JSON file is used by flexible templates for generating various Web
Standard API that contains following metadata:
- `"api"` a string, the name of the Web API
- `"namespace"` a string, the namespace of C++ wrapper
- `"c_prefix"` (optional) a string, the prefix of C function and data type, it will default to
upper-case of `"namespace"` if it's not provided.
- `"proc_table_prefix"` a string, the prefix of proc table.
- `"impl_dir"` a string, the directory of API implementation
- `"native_namespace"` a string, the namespace of native implementation
- `"copyright_year"` (optional) a string, templates will use the year of copyright.
The basic schema is that every entry is a thing with a `"category"` key what determines the
sub-schema to apply to that thing. Categories and their sub-shema are defined below. Several parts
of the schema use the concept of "record" which is a list of "record members" which are a
combination of a type, a name and other metadata. For example the list of arguments of a function
is a record. The list of structure members is a record. This combined concept is useful for the
dawn_wire generator to generate code for structure and function calls in a very similar way.
Most items and sub-items can include a list of `"tags"`, which, if specified, conditionally includes
the item if any of its tags appears in the `enabled_tags` configuration passed to `parse_json`.
This is used to include and exclude various items for Dawn, Emscripten, or upstream header
variants. Tags are applied in the "parse_json" step ([rather than
later](https://docs.google.com/document/d/1fBniVOxx3-hQbxHMugEPcQsaXaKBZYVO8yG9iXJp-fU/edit?usp=sharing)):
this has the benefit of automatically catching when, for a particular tag configuration, an
included item references an excluded item.
When used on enum values, `"tags"` may add an additional prefix to the enum value. This is to
implement [implementation-specific ranges of
enums](https://github.com/webgpu-native/webgpu-headers/issues/214). All compat enums are to start
at `0x0002_0000`, Emscripten enums are to start at `0x0004_0000`, and all Dawn enums are to start
at `0x0005_0000`. Use of `"compat"`, `"emscripten"`, or `"dawn"` tags will add these values,
respectively. So, an enum with value `"3"` but tag `"emscripten"` will actually use value
`0x0002_0003`. Multi-implementation native enums start at `0x0001_0000`, so this value is added if
the `"native"` tag is present and no other value-impacting tag is applied.
Items (such as objects, structures, enums, bitmasks, functions, callbacks, and callback infos) as well as object methods and enum/bitmask values can also include a `"stable_version"` field, specified via the `addins` mechanism in [`dawn_version.json`](../../src/dawn/dawn_version.json). This is a monotonically increasing integer version number corresponding to a date with the format `YYYYMMDDPP`, where `PP` is an additional two-digit identifier in case multiple patches land on the same day. Given a particular version, if the version is `>=` an API piece's `"stable_version"`, Dawn guarantees that the API exists and works to the best of our ability. For objects, the top-level `"stable_version"` indicates when the object itself was stably introduced, i.e. when its `AddRef` and `Release` methods (and usually `SetLabel`) were considered stable. Individual object methods (`"object::method"`) also have their own `"stable_version"` entry since they can be introduced at a later time. Similarly, enums and bitmasks have both a top-level `"stable_version"` and individual `"enum::value"` / `"bitmask::value"` entries for each of their values.
A **record** is a list of **record members**, each of which is a dictionary with the following
schema:
- `"name"` a string
- `"type"` a string, the name of the base type for this member
- `"annotation"` a string, default to "value". Define the C annotation to apply to the base type.
Allowed annotations are `"value"` (the default), `"*"`, `"const*"`
- `"length"` (default to 1 if not set), a string. Defines length of the array pointed to for
pointer arguments. If not set the length is implicitly 1 (so not an array), but otherwise it can
be set to the name of another member in the same record that will contain the length of the
array (this is heavily used in the `fooCount` `foos` pattern in the API).
- `"optional"` (default to false) a boolean that says whether this member is optional. Member
records can be optional if they are pointers (otherwise dawn_wire will always try to dereference
them), objects (otherwise dawn_wire will always try to encode their ID and crash), or if they
have a `"default"` key. Optional pointers and objects will always default to `nullptr` (unless
`"no_default"` is set to `true`).
- `"default"` (optional) a number or string. If set the record member will use that value as
default value. Depending on the member's category it can be a number, a string containing a
number, or the name of an enum/bitmask value.
- Dawn implements "trivial defaulting" for enums, similarly to the upstream WebGPU spec's WebIDL:
if a zero-valued enum (usually called `Undefined`) is passed in, Dawn applies the default
value specified here. See `WithTrivialFrontendDefaults()` in `api_structs_defaults.h` for how
this works.
- `"wire_is_data_only"` (default to false) a boolean that says whether it is safe to directly
return a pointer of this member that is pointing to a piece of memory in the transfer buffer
into dawn_wire. To prevent TOCTOU attacks, by default in dawn_wire we must ensure every single
value returned to dawn_native a copy of what's in the wire, so `"wire_is_data_only"` is set to
true only when the member is data-only and don't impact control flow.
**`"native"`** native types that can be referenced by name in other things.
- `"wire transparent"` (defaults to true) a boolean that indicates whether the native type should
be transparent (serialized as is) on the wire.
**`"typedef"`** (usually only used for gradual deprecations):
- `"type"`: the name of the things this is a typedef for.
**`"enum"`** an `uint32_t`-based enum value.
- `"values"` an array of enum values. Each value is a dictionary containing:
- `"name"` a string
- `"value"` a number that can be decimal or hexadecimal
- `"jsrepr"` (optional) a string to allow overriding how this value map to Javascript for the
Emscripten bits
- `"valid"` (defaults to true) a boolean that controls whether the dawn_native validation
utilities will consider this enum value valid.
- `"emscripten_no_enum_table"` (optional) if true, skips generating an enum table in
`library_webgpu_enum_tables.js`
**`"bitmask"`** an `uint32_t`-based bitmask. It is similar to **`"enum"`** but can be output
differently.
**`"function pointer"`** defines a function pointer type that can be used by other things.
- `"returns"` a string that's the name of the return type
- `"args"` a **record**, so an array of **record members**
**`"structure"`**
- `"members"` a **record**, so an array of **record members**
- `"extensible"` (defaults to false) a boolean defining if this is an "extensible" WebGPU
structure (i.e. has `nextInChain`). "descriptor" structures should usually have this set to
true.
- `"chained"` (defaults to None) a string defining if this is a structure that can be "chained" in
a WebGPU structure (i.e. has `nextInChain` and `sType`) and in which direction ('in' for inputs
to WebGPU, 'out' for outputs)
- `"chain roots"` (defaults to []) a list of strings that are the canonical names of structures
that can be extended by this structure.
**`"object"`**
- `**methods**` an array of methods for this object. Note that "release" and "reference" don't need
to be specified. Each method is a dictionary containing:
- `"name"` a string
- `"return_type"` (default to no return type) a string that's the name of the return type.
- `"arguments"` a **record**, so an array of **record members**
- `"no autolock"`: a boolean flag (default is false) indicates that the method's generated code
won't automatically do thread synchronization. This flag can only be true on device or device
child objects currently.
- `"no autolock"`: a boolean flag (default is false) to indicate that the object's generated code
won't automatically do thread synchronization. This will override individual method's `"no
autolock"` flag. This flag can only be true on device or device child objects currently.
**`"constant"`**
- `"type"`: a string, the name of the base data type
- `"value"`: a string, the value is defined with preprocessor macro
**`"function"`** declares a function that not belongs to any class.
- `"returns"` a string that's the name of the return type
- `"args"` a **record**, so an array of **record members**
## Dawn "wire" generators
The generator for the pieces of dawn_wire need additional data which is found
in [`dawn_wire_json`](../../src/dawn/dawn_wire.json). Examples of pieces that are generated are:
- `WireCmd.cpp/.h` the most important piece: the meat of the serialization / deserialization code
for WebGPU structures and commands
- `ServerHandlers/Doers.cpp` that does the complete handling of all regular WebGPU methods in the
server
- `ApiProcs.cpp` that implements the complete handling of all regular WebGPU methods in the client
Most of the WebGPU methods can be handled automatically by the wire client/server but some of them
need custom handling (for example because they handle callbacks or need client-side state
tracking). `dawn_wire.json` defines which methods need special handling, and extra wire commands
that can be used by that special handling (and will get `WireCmd` support).
The schema of `dawn_wire.json` is a dictionary with the following keys:
- `"commands"` an array of **records** defining extra client->server commands that can be used in
special-cased code path.
- Each **record member** can have an extra `"skip_serialize"` key that's a boolean that default
to false and makes `WireCmd` skip it on its on-wire format.
- `"return commands"` like `"commands"` but in revers, an array of **records** defining extra
server->client commands
- `"special items"` a dictionary containing various lists of methods or object that require special
handling in places in the dawn_wire autogenerated files
- `"client_side_structures"`: a list of structure that we shouldn't generate
serialization/deserialization code for because they are client-side only
- `"client_handwritten_commands"`: a list of methods that are written manually and won't be
automatically generated in the client
- `"client_side_commands"`: a list of methods that won't be automatically generated in the
server. Gets added to `"client_handwritten_commands"`
- `"client_special_objects"`: a list of objects that need special manual state-tracking in the
client and won't be autogenerated
- `"server_custom_pre_handler_commands"`: a list of methods that will run custom "pre-handlers"
before calling the autogenerated handlers in the server
- `"server_handwrittten_commands"`: a list of methods that are written manually and won't be
automatically generated in the server.
- `server_reverse_object_lookup_objects`: a list of objects for which the server will maintain an
object -> ID mapping.
## Dawn "native" generators
The generator for the pieces of dawn_native need additional data which is found
in [`dawn_native_json`](../../src/dawn/dawn_native.json). Examples of pieces that are generated
are:
- `ProcTable.cpp` that implements all of the WebGPU function by converting arguments and forwarding
to the correct method/function in `dawn::native`.
- `dawn_platform_autogen.h` and `wgpu_structs_autogen.cpp` that define the `dawn::native`
equivalents of types in `webgpu.h` (for example with objects being actual pointers to objects
and not just opaque handles) as well as conversion functions.
- `api_absl_format.cpp` to make it easy to print any WebGPU type in error messages.
The schema of `dawn_native.json` is a dictionary with the following keys:
- `"metadata"` a dictionary containing various other containers that can be used in templates. Its
keys are:
- `addins`: A dictionary mapping fully qualified instance names (structs, struct members,
functions, function arguments, objects, object methods, or method arguments) to a dictionary
of attribute addins (e.g. `{"spanify": false}` or `{"index_type": "BindingIndex"}`).
## Dawn "Kotlin" generators
The generator for the Android Kotlin/JNI bindings needs additional data which is found
in [`dawn_kotlin.json`](../../src/dawn/dawn_kotlin.json). It generates the Kotlin classes (one per
object, structure, enum and callback) and the JNI glue that converts between Kotlin objects and
`webgpu.h` structures.
The schema of `dawn_kotlin.json` is a dictionary with the following keys:
- `"kotlin_package"` a string, the package for the generated Kotlin classes.
- `"jni_primitives"` / `"jni_signatures"` dictionaries mapping native type names to their JNI C
type and JNI signature letter.
- `"kdocs_blocklist"` / `"kdocs_replacements"` filters applied to the upstream documentation when
generating KDoc.
- `"metadata"` a dictionary with the same shape as in `dawn_native.json`:
- `addins`: maps fully qualified instance names to attribute addins. Supported addins are:
- `"omitted": true` on a structure, enum, function pointer, function, object method,
or **structure member** to exclude it from the Kotlin API. It is not supported on
function/method arguments (the generator asserts) since the native call would silently
receive a zero-initialized value.
- `"kotlin_name": "<name>"` on a function or object method to override its Kotlin name (e.g.
`"create instance": {"kotlin_name": "createGPUInstance"}`).
- `"additional_members": [...]` on a structure to add Kotlin-only members that have no native
counterpart. Each entry has a `"name"`, a `"type"` (either a `dawn.json` type name or a
Kotlin type when `"category": "kotlin type"`), and optional `"category"`, `"annotation"`,
`"optional"` and `"default_value"` keys.
- `"graduated_batches": [[...], ...]` on a structure, a list of member-name batches that were
added after the structure first shipped, used to emit hidden legacy constructors for binary
compatibility.
## OpenGL loader generator
The code to load OpenGL entrypoints from a `GetProcAddress` function is generated
from [`gl.xml`](../../third_party/OpenGL-Registry/src/xml/gl.xml) and the [list of
extensions](../../src/dawn/native/opengl/supported_extensions.json) it supports.