Source browser

scripts/validate_glsl.py

Read the complete file below or download its original bytes. Line numbers provide stable links to a declaration or processing step.

File fingerprint
1#!/usr/bin/env python3
2"""Export every dual shader and validate all supported GLSL language profiles."""
3
4import argparse  # Read the exporter, compiler and source paths.
5from pathlib import Path  # Build paths with pathlib.
6import subprocess  # Run the exporter and GLSL compiler.
7import tempfile  # Remove exported shader files after validation.
8
9
10def main():  # Validate every bundled compute shader for every supported profile.
11    parser = argparse.ArgumentParser(description=__doc__)  # Provide command help.
12    parser.add_argument("--exporter", type=Path, required=True)  # Locate the built shader exporter.
13    parser.add_argument("--validator", type=Path, required=True)  # Locate glslangValidator.
14    parser.add_argument("--source-dir", type=Path, required=True)  # Locate the shader examples.
15    options = parser.parse_args()  # Read all required paths.
16    shaders = sorted(  # Collect bundled shader filenames in reproducible order.
17        (options.source_dir / "examples" / "shaders").glob("*.cpp")  # Read native compute sources in the example directory.
18    )  # Use a stable order.
19    if not shaders:  # Validate the selected source directory.
20        parser.error("no shaders found")  # Stop with a useful path-related error.
21    profiles = ("es31", "es32", "gl43", "gl45", "gl46")  # Cover all runtime language choices.
22    with tempfile.TemporaryDirectory(  # Remove all exported source files after the checks.
23        prefix="shdr-glsl-"  # Identify temporary shader validation directories.
24    ) as directory:  # Keep exports outside the source tree.
25        for shader in shaders:  # Visit each registered shader factory.
26            for profile in profiles:  # Export it using each version preamble.
27                command = [  # Build this factory's profile-specific export command.
28                    str(options.exporter),  # Use the built executable containing embedded shader text.
29                    shader.stem + "_kernel",  # Name the factory registered for this source file.
30                    profile,  # Select the exported shader language version.
31                ]  # Select factory and profile.
32                result = subprocess.run(  # Capture the exact text embedded in the executable.
33                    command,  # Pass each compiler argument directly.
34                    check=True,  # Propagate shader export failures.
35                    stdout=subprocess.PIPE,  # Read the exported source from standard output.
36                    text=True,  # Decode compiler output as text.
37                )  # The exporter result now contains the source.
38                path = Path(directory) / (  # Choose one temporary output path per source and profile.
39                    shader.stem + "_" + profile + ".comp"  # Keep compiler diagnostics easy to associate with their input.
40                )  # Give each check a separate file.
41                path.write_text(  # Preserve the exact standalone source emitted by the runtime.
42                    result.stdout, encoding="utf-8"  # Write shader text with a deterministic encoding.
43                )  # Write standard GLSL for the compiler.
44                command = [  # Build the standard GLSL compiler command.
45                    str(options.validator),  # Use the configured standalone validator.
46                    "-S",  # Select a shader stage independently of the filename suffix.
47                    "comp",  # Require compute-shader grammar and built-ins.
48                    str(path),  # Compile this exported source/profile pair.
49                ]  # Select the compute shader stage.
50                subprocess.run(command, check=True)  # Stop on the first language error.
51    total = len(shaders) * len(profiles)  # Count all successful source/profile pairs.
52    message = "Validated {} shaders for {} profiles ({} compilations)"  # Keep the summary readable.
53    print(message.format(len(shaders), len(profiles), total))  # Report the completed checks.
54
55
56if __name__ == "__main__":  # Run only when this file is used as a command.
57    main()  # Read options and start validation.