# 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 -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 --params ... Parameters to pass to the shader preset, comma separated with equals signs. For example, crt_gamma=2.5,halation_weight=0.001 --passes-enabled Set the number of passes enabled for the preset -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] -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 -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 --params ... Parameters to pass to the shader preset, comma separated with equals signs. For example, crt_gamma=2.5,halation_weight=0.001 --passes-enabled Set the number of passes enabled for the preset -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" } } } } ```