diff --git a/compile_shaders.ps1 b/compile_shaders.ps1 index 73ff936..d149184 100644 --- a/compile_shaders.ps1 +++ b/compile_shaders.ps1 @@ -1,9 +1,10 @@ -$COMMON = @("--target-env=vulkan1.3", "-O", "-g", "-Werror", "-I", "shaders") +param( + [ValidateSet("Debug", "Release", "RelWithDebInfo")] + [string]$Config = "RelWithDebInfo", + [string]$Python = "python" +) -Get-ChildItem -Path "shaders" -Include *.frag,*.vert,*.comp,*.geom,*.tesc,*.tese,*.mesh,*.task,*.rgen,*.rint,*.rahit,*.rchit,*.rmiss,*.rcall -Recurse | - ForEach-Object { - $extra = @() - if ($_.Extension -eq ".mesh") { $extra += "-fshader-stage=mesh" } - elseif ($_.Extension -eq ".task") { $extra += "-fshader-stage=task" } - glslc $_.FullName @COMMON @extra -o "$($_.FullName).spv" - } +$script_dir = Split-Path -Parent $MyInvocation.MyCommand.Path +$py = Join-Path $script_dir "compile_shaders.py" + +& $Python $py --config $Config diff --git a/compile_shaders.py b/compile_shaders.py new file mode 100644 index 0000000..0c457ec --- /dev/null +++ b/compile_shaders.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +import argparse, os, subprocess, sys +from pathlib import Path +from shutil import which + +SHADER_EXTS = { + ".frag", ".vert", ".comp", ".geom", ".tesc", ".tese", + ".mesh", ".task", + ".rgen", ".rint", ".rahit", ".rchit", ".rmiss", ".rcall", +} + +PROFILE_FLAGS = { + "Debug": ["-g"], + "Release": ["-O"], + # Matches the old `compile_shaders.ps1` defaults (-O + -g). + "RelWithDebInfo": ["-O", "-g"], +} + +STAGE_FLAGS_BY_EXT = { + ".mesh": ["-fshader-stage=mesh"], + ".task": ["-fshader-stage=task"], +} + + +def find_glslc(user_path): + if user_path: + return user_path + + env_glslc = os.environ.get("GLSLC") + if env_glslc: + return env_glslc + + vulkan_sdk = os.environ.get("VULKAN_SDK") + if vulkan_sdk: + sdk = Path(vulkan_sdk) + candidates = [ + sdk / "Bin" / "glslc.exe", + sdk / "Bin" / "glslc", + sdk / "bin" / "glslc.exe", + sdk / "bin" / "glslc", + ] + for c in candidates: + if c.exists(): + return str(c) + + for name in ("glslc", "glslc.exe"): + p = which(name) + if p: + return p + + sys.exit( + "ERROR: `glslc` not found. Install the Vulkan SDK and ensure `glslc` is on PATH,\n" + " or set `VULKAN_SDK`, `GLSLC`, or pass `--glslc /path/to/glslc`." + ) + + +def parse_args(): + p = argparse.ArgumentParser(description="Compile GLSL shaders under shaders/ to .spv next to sources.") + cfg = p.add_mutually_exclusive_group() + cfg.add_argument("--debug", action="store_true", help="Debug profile (adds -g, no -O).") + cfg.add_argument("--release", action="store_true", help="Release profile (adds -O, no -g).") + p.add_argument( + "--config", + choices=list(PROFILE_FLAGS.keys()), + default="RelWithDebInfo", + help="Compilation profile (default: RelWithDebInfo).", + ) + p.add_argument("--shader-dir", default="shaders", help="Shader source directory (default: shaders).") + p.add_argument("--glslc", default=None, help="Path to glslc (default: auto-detect).") + p.add_argument("--target-env", default="vulkan1.3", help="Vulkan target env for glslc (default: vulkan1.3).") + p.add_argument("--no-werror", action="store_true", help="Do not pass -Werror.") + p.add_argument("--quiet", action="store_true", help="Only print errors.") + return p.parse_args() + + +def collect_sources(shader_dir: Path): + return sorted([p for p in shader_dir.rglob("*") if p.is_file() and p.suffix.lower() in SHADER_EXTS]) + + +def compile_one(glslc: str, src: Path, shader_dir: Path, args) -> int: + out_path = src.parent / f"{src.name}.spv" + stage_flags = STAGE_FLAGS_BY_EXT.get(src.suffix.lower(), []) + + cmd = [ + glslc, + str(src), + f"--target-env={args.target_env}", + "-I", str(shader_dir), + *PROFILE_FLAGS[args.config], + *stage_flags, + ] + if not args.no_werror: + cmd.append("-Werror") + cmd += ["-o", str(out_path)] + + if not args.quiet: + print(subprocess.list2cmdline(cmd)) + + proc = subprocess.run(cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if proc.stdout and not args.quiet: + sys.stdout.write(proc.stdout) + if proc.returncode != 0: + if proc.stderr: + sys.stderr.write(proc.stderr) + elif proc.stderr and not args.quiet: + sys.stderr.write(proc.stderr) + return proc.returncode + + +def main() -> int: + args = parse_args() + + if args.debug: + args.config = "Debug" + elif args.release: + args.config = "Release" + + repo_root = Path(__file__).resolve().parent + shader_dir = (repo_root / args.shader_dir).resolve() + if not shader_dir.exists(): + sys.stderr.write(f"ERROR: shader dir not found: {shader_dir}\n") + return 2 + + glslc = find_glslc(args.glslc) + + sources = collect_sources(shader_dir) + if not sources: + if not args.quiet: + print(f"No shaders found under {shader_dir}") + return 0 + + failed = 0 + for src in sources: + rc = compile_one(glslc, src, shader_dir, args) + if rc != 0: + failed += 1 + + if failed: + sys.stderr.write(f"ERROR: {failed} shader(s) failed to compile.\n") + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/BUILD.md b/docs/BUILD.md index 22e8e13..16956d3 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -23,7 +23,8 @@ - Shaders - CMake compiles GLSL via `glslangValidator` to SPIR‑V targeting Vulkan 1.2: - Files under `shaders/*.vert|*.frag|*.comp` are rebuilt on `cmake --build`. - - Windows helper: `./compile_shaders.ps1` uses `glslc` with `--target-env=vulkan1.3` and supports additional stages (mesh/task/ray tracing). + - Helper: `./compile_shaders.py --config Debug|Release` uses `glslc` with `--target-env=vulkan1.3` and supports additional stages (mesh/task/ray tracing). + - Windows shim: `./compile_shaders.ps1 -Config Debug|Release` (calls the Python script). - Ensure `glslangValidator`/`glslc` is on `PATH`. See `docs/SHADERS.md`. - Windows SDK note @@ -41,4 +42,3 @@ - Validation Layers - Enabled in Debug (`kUseValidationLayers = true` in `src/core/config.h`). - Disable by building Release or toggling the flag during local experimentation. - diff --git a/docs/SHADERS.md b/docs/SHADERS.md index a17e135..56a2b64 100644 --- a/docs/SHADERS.md +++ b/docs/SHADERS.md @@ -5,8 +5,9 @@ - Build integration - CMake invokes `glslangValidator -V` for `*.vert`, `*.frag`, `*.comp` targeting Vulkan 1.2. - - Windows PowerShell helper `compile_shaders.ps1` uses `glslc` targeting Vulkan 1.3 and supports additional stages: + - Helper `compile_shaders.py` uses `glslc` targeting Vulkan 1.3 and supports additional stages: - `.mesh` (`-fshader-stage=mesh`), `.task`, and ray tracing stages (`.rgen`, `.rmiss`, `.rchit`, `.rahit`, `.rint`, `.rcall`). + - Windows shim: `compile_shaders.ps1 -Config Debug|Release|RelWithDebInfo`. - Keep `glslangValidator`/`glslc` on `PATH` and ensure your Vulkan SDK is installed. - Hot reload @@ -55,4 +56,3 @@ GLSL Includes | `input_structures.glsl` | SceneData UBO, material bindings, light structs | | `lighting_common.glsl` | BRDF evaluation, point light helpers | | `ibl_common.glsl` | IBL split-sum, SH irradiance | -