24 KiB
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:
-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
-f, --frame <FRAME>
The frame to render.
The renderer will run up to the number of frames specified here to ensure feedback and history.
[default: 0]
--params <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 <PASSES_ENABLED>
Set the number of passes enabled for the preset
-i, --image <IMAGE>
The path to the input image
--frame-direction <FRAME_DIRECTION>
The direction of rendering. -1 indicates that the frames are played in reverse order
[default: 1]
--rotation <ROTATION>
The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 270deg
[default: 0]
--total-subframes <TOTAL_SUBFRAMES>
The total number of subframes ran. Default is 1
[default: 1]
--current-subframe <CURRENT_SUBFRAME>
The current sub frame. Default is 1
[default: 1]
-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:
-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
-f, --frame <FRAME>
The frame to render.
The renderer will run up to the number of frames specified here to ensure feedback and history.
[default: 0]
--params <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 <PASSES_ENABLED>
Set the number of passes enabled for the preset
-i, --image <IMAGE>
The path to the input image
--frame-direction <FRAME_DIRECTION>
The direction of rendering. -1 indicates that the frames are played in reverse order
[default: 1]
--rotation <ROTATION>
The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 270deg
[default: 0]
--total-subframes <TOTAL_SUBFRAMES>
The total number of subframes ran. Default is 1
[default: 1]
--current-subframe <CURRENT_SUBFRAME>
The current sub frame. Default is 1
[default: 1]
-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.
Getting preset information for CRT Royale
The following command
$ librashader-cli parse -p crt-geom.slangp
will output the following 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 <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
{
"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.
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
{
"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"
}
}
}
}