doc(cli): document CLI

This commit is contained in:
chyyran 2024-09-26 20:45:47 -04:00 committed by Ronny Chan
parent 55ff7a93f2
commit 3993f57271
4 changed files with 911 additions and 6 deletions

884
CLI.md Normal file
View file

@ -0,0 +1,884 @@
# librashader CLI
```
Helpers and utilities to reflect and debug 'slang' shaders and presets
Usage: librashader-cli <COMMAND>
Commands:
render Render a shader preset against an image
compare Compare two runtimes and get a similarity score between the two runtimes rendering the same frame
parse Parse a preset and get a JSON representation of the data
preprocess Get the raw GLSL output of a preprocessed shader
transpile Transpile a shader in a given preset to the given format
reflect Reflect the shader relative to a preset, giving information about semantics used in a slang shader
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
```
`librashader-cli` provides a CLI interface to much of librashader's functionality to reflect and debug 'slang' shaders
and presets.
## Installation
If cargo is available, `librashader-cli` can be installed from crates.io.
```
$ cargo install librashader-cli
```
## Applying a shader preset to an image
```
Render a shader preset against an image
Usage: librashader-cli render [OPTIONS] --preset <PRESET> --image <IMAGE> --out <OUT> --runtime <RUNTIME>
Options:
-f, --frame <FRAME>
The frame to render
[default: 60]
-p, --preset <PRESET>
The path to the shader preset to load
-i, --image <IMAGE>
The path to the input image
-o, --out <OUT>
The path to the output image
If `-`, writes the image in PNG format to stdout.
-r, --runtime <RUNTIME>
The runtime to use to render the shader preset
[possible values: opengl3, opengl4, vulkan, wgpu, d3d9, d3d11, d3d12, metal]
-h, --help
Print help (see a summary with '-h')
```
The `render` command can be used to apply a shader preset to an image. The available runtimes will depend on the platform
that `librashader-cli` was built for.
For example, to apply `crt-royale.slangp` to `image.png` using the OpenGL 3.3 runtime
```
$ librashader-cli render -i image.png -p crt-royale.slangp -r opengl3 -o out.png
```
Some presets have animations that rely on a frame counter. The `--frame`/`-f` argument can be used to select which frame to render.
```
$ librashader-cli render -i image.png -p MBZ__0__SMOOTH-ADV.slangp -f 120 -r opengl3 -o out.png
```
## Comparing the similarities of two runtimes
```
Compare two runtimes and get a similarity score between the two runtimes rendering the same frame
Usage: librashader-cli compare [OPTIONS] --preset <PRESET> --image <IMAGE> --left <LEFT> --right <RIGHT>
Options:
-f, --frame <FRAME>
The frame to render
[default: 60]
-p, --preset <PRESET>
The path to the shader preset to load
-i, --image <IMAGE>
The path to the input image
-l, --left <LEFT>
The runtime to compare against
[possible values: opengl3, opengl4, vulkan, wgpu, d3d9, d3d11, d3d12, metal]
-r, --right <RIGHT>
The runtime to compare to
[possible values: opengl3, opengl4, vulkan, wgpu, d3d9, d3d11, d3d12, metal]
-o, --out <OUT>
The path to write the similarity image.
If `-`, writes the image to stdout.
-h, --help
Print help (see a summary with '-h')
```
The `compare` command can be used to get the similarity of two different runtimes, returning a similarity score and similarity image.
The available runtimes will depend on the platform that `librashader-cli` was built for. This is mainly used for debug and testing purposes; two runtimes should output
highly identical (> 0.99 similarity) with a near black similarity image.
## Parsing a shader preset
```
Parse a preset and get a JSON representation of the data
Usage: librashader-cli parse [OPTIONS] --preset <PRESET>
Options:
-p, --preset <PRESET>
The path to the shader preset to load
-w, --wildcards <WILDCARDS>...
Additional wildcard options, comma separated with equals signs. The PRESET and PRESET_DIR wildcards are always added to the preset parsing context.
For example, CONTENT-DIR=MyVerticalGames,GAME=mspacman
-h, --help
Print help (see a summary with '-h')
```
The `parse` command can be used to parse a shader preset and get a JSON represenation of its data. Wildcards can be specified
with the `--wildcards` / `-w` argument. All paths will be resolved relative to the preset.
<details>
<summary>
Getting preset information for CRT Royale
</summary>
The following command
```
$ librashader-cli parse -p crt-geom.slangp
```
will output the following JSON
```json
{
"shader_count": 12,
"shaders": [
{
"id": 0,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-first-pass-linearize-crt-gamma-bob-fields.slang",
"alias": "ORIG_LINEARIZED",
"filter": "Nearest",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 1,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-scanlines-vertical-interlacing.slang",
"alias": "VERTICAL_SCANLINES",
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Viewport",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 2,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-bloom-approx.slang",
"alias": "BLOOM_APPROX",
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Absolute",
"factor": {
"Absolute": 320
}
},
"y": {
"scale_type": "Absolute",
"factor": {
"Absolute": 240
}
}
}
},
{
"id": 3,
"name": "/tmp/shaders_slang/blurs/shaders/royale/blur9fast-vertical.slang",
"alias": null,
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 4,
"name": "/tmp/shaders_slang/blurs/shaders/royale/blur9fast-horizontal.slang",
"alias": "HALATION_BLUR",
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 5,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-mask-resize-vertical.slang",
"alias": null,
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": false,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Absolute",
"factor": {
"Absolute": 64
}
},
"y": {
"scale_type": "Viewport",
"factor": {
"Float": 0.0625
}
}
}
},
{
"id": 6,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-mask-resize-horizontal.slang",
"alias": "MASK_RESIZE",
"filter": "Nearest",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": false,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Viewport",
"factor": {
"Float": 0.0625
}
},
"y": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 7,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-scanlines-horizontal-apply-mask.slang",
"alias": "MASKED_SCANLINES",
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Viewport",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Viewport",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 8,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-brightpass.slang",
"alias": "BRIGHTPASS",
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Viewport",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Viewport",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 9,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-bloom-vertical.slang",
"alias": null,
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 10,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-bloom-horizontal-reconstitute.slang",
"alias": null,
"filter": "Linear",
"wrap_mode": "ClampToBorder",
"frame_count_mod": 0,
"srgb_framebuffer": true,
"float_framebuffer": false,
"mipmap_input": false,
"scaling": {
"valid": true,
"x": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Input",
"factor": {
"Float": 1.0
}
}
}
},
{
"id": 11,
"name": "/tmp/shaders_slang/crt/shaders/crt-royale/src/crt-royale-geometry-aa-last-pass.slang",
"alias": null,
"filter": "Linear",
"wrap_mode": "ClampToEdge",
"frame_count_mod": 0,
"srgb_framebuffer": false,
"float_framebuffer": false,
"mipmap_input": true,
"scaling": {
"valid": true,
"x": {
"scale_type": "Viewport",
"factor": {
"Float": 1.0
}
},
"y": {
"scale_type": "Viewport",
"factor": {
"Float": 1.0
}
}
}
}
],
"textures": [
{
"name": "mask_grille_texture_small",
"path": "/tmp/shaders_slang/crt/shaders/crt-royale/TileableLinearApertureGrille15Wide8And5d5SpacingResizeTo64.png",
"wrap_mode": "Repeat",
"filter_mode": "Linear",
"mipmap": false
},
{
"name": "mask_grille_texture_large",
"path": "/tmp/shaders_slang/crt/shaders/crt-royale/TileableLinearApertureGrille15Wide8And5d5Spacing.png",
"wrap_mode": "Repeat",
"filter_mode": "Linear",
"mipmap": true
},
{
"name": "mask_slot_texture_small",
"path": "/tmp/shaders_slang/crt/shaders/crt-royale/TileableLinearSlotMaskTall15Wide9And4d5Horizontal9d14VerticalSpacingResizeTo64.png",
"wrap_mode": "Repeat",
"filter_mode": "Linear",
"mipmap": false
},
{
"name": "mask_slot_texture_large",
"path": "/tmp/shaders_slang/crt/shaders/crt-royale/TileableLinearSlotMaskTall15Wide9And4d5Horizontal9d14VerticalSpacing.png",
"wrap_mode": "Repeat",
"filter_mode": "Linear",
"mipmap": true
},
{
"name": "mask_shadow_texture_small",
"path": "/tmp/shaders_slang/crt/shaders/crt-royale/TileableLinearShadowMaskEDPResizeTo64.png",
"wrap_mode": "Repeat",
"filter_mode": "Linear",
"mipmap": false
},
{
"name": "mask_shadow_texture_large",
"path": "/tmp/shaders_slang/crt/shaders/crt-royale/TileableLinearShadowMaskEDP.png",
"wrap_mode": "Repeat",
"filter_mode": "Linear",
"mipmap": true
}
],
"parameters": []
}
```
</details>
## Getting the preprocessed GLSL source of a `.slang` shader
```
Get the raw GLSL output of a preprocessed shader
Usage: librashader-cli preprocess --shader <SHADER> --output <OUTPUT>
Options:
-s, --shader <SHADER>
The path to the slang shader
-o, --output <OUTPUT>
The item to output.
`json` will print a JSON representation of the preprocessed shader.
[possible values: fragment, vertex, params, passformat, json]
-h, --help
Print help (see a summary with '-h')
```
The `preprocess` command can be used to invoke the preprocessor on `.slang`
shader source files and output the raw GLSL source that gets passed to SPIRV-Cross
during shader compilation.
For example, to get the preprocessed fragment shader source code
```
$ librashader-cli preprocess -s crt-geom.slang -o fragment
```
`preprocess` can also be used to get information about the pass format,
parameters, or meta information inferred from `#pragma` declarations in the
source code.
For example (using `jq` to truncate the output)
```
$ librashader-cli preprocess -s crt-geom.slang -o params | jq 'first(.[])'
```
will return the following JSON
```json
{
"CRTgamma": {
"id": "CRTgamma",
"description": "CRTGeom Target Gamma",
"initial": 2.4,
"minimum": 0.1,
"maximum": 5.0,
"step": 0.1
}
}
```
## Convert a `.slang` to a target shader format
```
Transpile a shader in a given preset to the given format
Usage: librashader-cli transpile --shader <SHADER> --stage <STAGE> --format <FORMAT>
Options:
-s, --shader <SHADER>
The path to the slang shader
-o, --stage <STAGE>
The shader stage to output
[possible values: fragment, vertex]
-f, --format <FORMAT>
The output format
[possible values: glsl, hlsl, wgsl, msl, spirv]
-v, --version <VERSION>
The version of the output format to parse as, if applicable
For GLSL, this should be an string corresponding to a GLSL version (e.g. '330', or '300es', or '300 es').
For HLSL, this is a shader model version as an integer (50), or a version in the format MAJ_MIN (5_0), or MAJ.MIN (5.0).
For MSL, this is the shader language version as an integer in format <MMmmpp>(30100), or a version in the format MAJ_MIN (3_1), or MAJ.MIN (3.1).
-h, --help
Print help (see a summary with '-h')
```
The `transpile` command can be used to convert a `.slang` shader source file
to a target format used by a runtime. GLSL (under OpenGL semantics), HLSL,
WGSL, MSL, and SPIR-V disassembly is supported as output formats.
For example, to get the pixel shader in Shader Model 6.0 HLSL for `crt-geom.slang`
```
$ librashader-cli transpile -s crt-geom.slang -o fragment -f hlsl -v 60
```
## Getting detailed reflection information for a shader
```
Reflect the shader relative to a preset, giving information about semantics used in a slang shader.
Usage: librashader-cli reflect [OPTIONS] --preset <PRESET> --index <INDEX>
Options:
-p, --preset <PRESET>
The path to the shader preset to load
-w, --wildcards <WILDCARDS>...
Additional wildcard options, comma separated with equals signs. The PRESET and PRESET_DIR wildcards are always added to the preset parsing context.
For example, CONTENT-DIR=MyVerticalGames,GAME=mspacman
-i, --index <INDEX>
The pass index to use
-b, --backend <BACKEND>
[default: cross]
[possible values: cross, naga]
-h, --help
Print help (see a summary with '-h')
```
The `reflect` command can be used to get reflection detailed information regarding
uniform and texture bindings for a shader pass, relative to a preset. As semantics for LUT images are defined by the preset definition, reflection information is only valid for a shader source file relative to its shader preset.
The default backend to do reflection is with SPIRV-Cross. Reflections via Naga (used in the wgpu runtime) are also available if desired, and may have different results than SPIRV-Cross.
<details>
<summary>
Getting reflection information for CRT Geom
</summary>
`crt-geom.slangp` only has a single pass, but we still need to specify the pass in relation to its preset.
```
$ librashader-cli reflect -p crt-geom.slangp -i 0
```
The above command will output the following JSON
```json
{
"ubo": {
"binding": 0,
"size": 96,
"stage_mask": "VERTEX | FRAGMENT"
},
"push_constant": {
"binding": null,
"size": 96,
"stage_mask": "VERTEX | FRAGMENT"
},
"meta": {
"param": {
"ysize": {
"offset": {
"ubo": null,
"push": 80
},
"size": 1,
"id": "ysize"
},
"xsize": {
"offset": {
"ubo": null,
"push": 76
},
"size": 1,
"id": "xsize"
},
"invert_aspect": {
"offset": {
"ubo": null,
"push": 68
},
"size": 1,
"id": "invert_aspect"
},
"x_tilt": {
"offset": {
"ubo": null,
"push": 28
},
"size": 1,
"id": "x_tilt"
},
"y_tilt": {
"offset": {
"ubo": null,
"push": 32
},
"size": 1,
"id": "y_tilt"
},
"R": {
"offset": {
"ubo": null,
"push": 16
},
"size": 1,
"id": "R"
},
"d": {
"offset": {
"ubo": null,
"push": 12
},
"size": 1,
"id": "d"
},
"vertical_scanlines": {
"offset": {
"ubo": null,
"push": 72
},
"size": 1,
"id": "vertical_scanlines"
},
"SHARPER": {
"offset": {
"ubo": null,
"push": 48
},
"size": 1,
"id": "SHARPER"
},
"interlace_detect": {
"offset": {
"ubo": null,
"push": 60
},
"size": 1,
"id": "interlace_detect"
},
"CURVATURE": {
"offset": {
"ubo": null,
"push": 56
},
"size": 1,
"id": "CURVATURE"
},
"overscan_x": {
"offset": {
"ubo": null,
"push": 36
},
"size": 1,
"id": "overscan_x"
},
"overscan_y": {
"offset": {
"ubo": null,
"push": 40
},
"size": 1,
"id": "overscan_y"
},
"cornersize": {
"offset": {
"ubo": null,
"push": 20
},
"size": 1,
"id": "cornersize"
},
"cornersmooth": {
"offset": {
"ubo": null,
"push": 24
},
"size": 1,
"id": "cornersmooth"
},
"CRTgamma": {
"offset": {
"ubo": null,
"push": 4
},
"size": 1,
"id": "CRTgamma"
},
"scanline_weight": {
"offset": {
"ubo": null,
"push": 52
},
"size": 1,
"id": "scanline_weight"
},
"lum": {
"offset": {
"ubo": null,
"push": 64
},
"size": 1,
"id": "lum"
},
"DOTMASK": {
"offset": {
"ubo": null,
"push": 44
},
"size": 1,
"id": "DOTMASK"
},
"monitorgamma": {
"offset": {
"ubo": null,
"push": 8
},
"size": 1,
"id": "monitorgamma"
}
},
"unique": {
"MVP": {
"offset": {
"ubo": 0,
"push": null
},
"size": 16,
"id": "MVP"
},
"Output": {
"offset": {
"ubo": 64,
"push": null
},
"size": 4,
"id": "OutputSize"
},
"FrameCount": {
"offset": {
"ubo": null,
"push": 0
},
"size": 1,
"id": "FrameCount"
}
},
"texture": {
"Source": {
"binding": 2
}
},
"texture_size": {
"Source": {
"offset": {
"ubo": 80,
"push": null
},
"stage_mask": "VERTEX | FRAGMENT",
"id": "SourceSize"
}
}
}
}
```
</details>

View file

@ -102,7 +102,26 @@ These types should not be exposed to the end user in the runtime's public API, a
the runtime. the runtime.
## Command-line interface ## Command-line interface
librashader provides a librashader provides a command-line interface to reflect and debug 'slang' shaders and presets.
```
Usage: librashader-cli <COMMAND>
Commands:
render Render a shader preset against an image
compare Compare two runtimes and get a similarity score between the two runtimes rendering the same frame
parse Parse a preset and get a JSON representation of the data
preprocess Get the raw GLSL output of a preprocessed shader
transpile Transpile a shader in a given preset to the given format
reflect Reflect the shader relative to a preset, giving information about semantics used in a slang shader
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
```
For more information, see [`CLI.md`](https://github.com/SnowflakePowered/librashader/blob/master/CLI.md)
## Building ## Building
@ -249,6 +268,9 @@ The `SONAME` of `librashader.so` when installed via package manager is set to `L
The above does not apply to releases of librashader prior to `0.1.0`, which were allowed to break API and ABI compatibility The above does not apply to releases of librashader prior to `0.1.0`, which were allowed to break API and ABI compatibility
in both the Rust and C API without an increase to either `LIBRASHADER_CURRENT_VERSION` or `LIBRASHADER_CURRENT_ABI`. in both the Rust and C API without an increase to either `LIBRASHADER_CURRENT_VERSION` or `LIBRASHADER_CURRENT_ABI`.
The ABI version was bumped from `1` to `2` with librashader `0.5.0`. See [`MIGRATION-ABI2.md`](https://github.com/SnowflakePowered/librashader/blob/master/MIGRATION-ABI2.md)
for migration instructions.
### MSRV Policy ### MSRV Policy
When building against nightly Rust, the following MSRV policy is enforced for unstable library features. When building against nightly Rust, the following MSRV policy is enforced for unstable library features.

View file

@ -10,6 +10,7 @@ use librashader::reflect::{CompileShader, FromCompilation, ReflectShader, SpirvC
use librashader_test::render::RenderTest; use librashader_test::render::RenderTest;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
/// Helpers and utilities to reflect and debug 'slang' shaders and presets.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version, about)] #[command(version, about)]
struct Args { struct Args {
@ -30,7 +31,7 @@ enum Commands {
/// The path to the input image. /// The path to the input image.
#[arg(short, long)] #[arg(short, long)]
image: PathBuf, image: PathBuf,
/// The path to the output image. /// The path to the output image
/// ///
/// If `-`, writes the image in PNG format to stdout. /// If `-`, writes the image in PNG format to stdout.
#[arg(short, long)] #[arg(short, long)]
@ -86,7 +87,7 @@ enum Commands {
#[arg(value_enum, short, long)] #[arg(value_enum, short, long)]
output: PreprocessOutput, output: PreprocessOutput,
}, },
/// Transpile a shader in a given preset to the given format. /// Transpile a shader to the given format.
Transpile { Transpile {
/// The path to the slang shader. /// The path to the slang shader.
#[arg(short, long)] #[arg(short, long)]
@ -113,8 +114,6 @@ enum Commands {
version: Option<String>, version: Option<String>,
}, },
/// Reflect the shader relative to a preset, giving information about semantics used in a slang shader. /// Reflect the shader relative to a preset, giving information about semantics used in a slang shader.
///
/// Due to limitations
Reflect { Reflect {
/// The path to the shader preset to load. /// The path to the shader preset to load.
#[arg(short, long)] #[arg(short, long)]

View file

@ -198,7 +198,7 @@ pub struct Semantic<T, I = usize> {
bitflags! { bitflags! {
/// The pipeline stage for which a uniform is bound. /// The pipeline stage for which a uniform is bound.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))] #[cfg_attr(feature = "serde", serde(transparent))]
pub struct BindingStage: u8 { pub struct BindingStage: u8 {
const NONE = 0b00000000; const NONE = 0b00000000;