diff --git a/CLI.md b/CLI.md new file mode 100644 index 0000000..f953a02 --- /dev/null +++ b/CLI.md @@ -0,0 +1,884 @@ +# librashader CLI + +``` +Helpers and utilities to reflect and debug 'slang' shaders and presets + +Usage: librashader-cli + +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 --image --out --runtime + +Options: + -f, --frame + The frame to render + + [default: 60] + + -p, --preset + The path to the shader preset to load + + -i, --image + The path to the input image + + -o, --out + The path to the output image + + If `-`, writes the image in PNG format to stdout. + + -r, --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 --image --left --right + +Options: + -f, --frame + The frame to render + + [default: 60] + + -p, --preset + The path to the shader preset to load + + -i, --image + The path to the input image + + -l, --left + The runtime to compare against + + [possible values: opengl3, opengl4, vulkan, wgpu, d3d9, d3d11, d3d12, metal] + + -r, --right + The runtime to compare to + + [possible values: opengl3, opengl4, vulkan, wgpu, d3d9, d3d11, d3d12, metal] + + -o, --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 + +Options: + -p, --preset + The path to the shader preset to load + + -w, --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. + +
+ +Getting preset information for CRT Royale + + +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": [] +} + +``` +
+ +## Getting the preprocessed GLSL source of a `.slang` shader + +``` +Get the raw GLSL output of a preprocessed shader + +Usage: librashader-cli preprocess --shader --output + +Options: + -s, --shader + The path to the slang shader + + -o, --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 --stage --format + +Options: + -s, --shader + The path to the slang shader + + -o, --stage + The shader stage to output + + [possible values: fragment, vertex] + + -f, --format + The output format + + [possible values: glsl, hlsl, wgsl, msl, spirv] + + -v, --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 (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 --index + +Options: + -p, --preset + The path to the shader preset to load + + -w, --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 + The pass index to use + + -b, --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. + +
+ +Getting reflection information for CRT Geom + + +`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" + } + } + } +} +``` +
diff --git a/README.md b/README.md index 2ac22ff..6e0a309 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,26 @@ These types should not be exposed to the end user in the runtime's public API, a the runtime. ## Command-line interface -librashader provides a +librashader provides a command-line interface to reflect and debug 'slang' shaders and presets. + +``` +Usage: librashader-cli + +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 @@ -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 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 When building against nightly Rust, the following MSRV policy is enforced for unstable library features. diff --git a/librashader-cli/src/cli/main.rs b/librashader-cli/src/cli/main.rs index ad73f13..6f71450 100644 --- a/librashader-cli/src/cli/main.rs +++ b/librashader-cli/src/cli/main.rs @@ -10,6 +10,7 @@ use librashader::reflect::{CompileShader, FromCompilation, ReflectShader, SpirvC use librashader_test::render::RenderTest; use std::path::{Path, PathBuf}; +/// Helpers and utilities to reflect and debug 'slang' shaders and presets. #[derive(Parser, Debug)] #[command(version, about)] struct Args { @@ -30,7 +31,7 @@ enum Commands { /// The path to the input image. #[arg(short, long)] image: PathBuf, - /// The path to the output image. + /// The path to the output image /// /// If `-`, writes the image in PNG format to stdout. #[arg(short, long)] @@ -86,7 +87,7 @@ enum Commands { #[arg(value_enum, short, long)] output: PreprocessOutput, }, - /// Transpile a shader in a given preset to the given format. + /// Transpile a shader to the given format. Transpile { /// The path to the slang shader. #[arg(short, long)] @@ -113,8 +114,6 @@ enum Commands { version: Option, }, /// Reflect the shader relative to a preset, giving information about semantics used in a slang shader. - /// - /// Due to limitations Reflect { /// The path to the shader preset to load. #[arg(short, long)] diff --git a/librashader-reflect/src/reflect/semantics.rs b/librashader-reflect/src/reflect/semantics.rs index 0cb35c9..55f4d74 100644 --- a/librashader-reflect/src/reflect/semantics.rs +++ b/librashader-reflect/src/reflect/semantics.rs @@ -198,7 +198,7 @@ pub struct Semantic { bitflags! { /// The pipeline stage for which a uniform is bound. #[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))] pub struct BindingStage: u8 { const NONE = 0b00000000;