93 lines
2.8 KiB
Python
93 lines
2.8 KiB
Python
import os
|
|
import shutil
|
|
import struct
|
|
import subprocess
|
|
|
|
|
|
# Mixed into DevCommand (bin/dev), which provides _project_root.
|
|
class ShadersCommand:
|
|
SHADER_EXTENSIONS = (".vert", ".frag", ".comp", ".geom", ".tesc", ".tese")
|
|
|
|
def shaders(self, *args, **kwargs):
|
|
shaders_dir = os.path.join(self._project_root(), "engine", "src", "shaders")
|
|
if not os.path.isdir(shaders_dir):
|
|
raise ValueError(f"Shaders directory not found: {shaders_dir}")
|
|
|
|
glslc = self._find_glslc()
|
|
|
|
shader_paths = sorted(
|
|
os.path.join(root, filename)
|
|
for root, _dirs, files in os.walk(shaders_dir)
|
|
for filename in files
|
|
if filename.endswith(self.SHADER_EXTENSIONS)
|
|
)
|
|
|
|
if not shader_paths:
|
|
print(f"No shaders found in {shaders_dir}")
|
|
return
|
|
|
|
for shader_path in shader_paths:
|
|
self._generate_shader_header(glslc, shader_path)
|
|
|
|
def _find_glslc(self):
|
|
glslc = shutil.which("glslc")
|
|
if glslc is None:
|
|
raise RuntimeError(
|
|
"glslc not found on PATH (install the Vulkan SDK)"
|
|
)
|
|
return glslc
|
|
|
|
def _generate_shader_header(self, glslc, shader_path):
|
|
relative_path = os.path.relpath(shader_path, self._project_root())
|
|
print(f"Compiling shader: {relative_path}")
|
|
|
|
result = subprocess.run(
|
|
[glslc, shader_path, "-o", "-"],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
)
|
|
if result.returncode != 0:
|
|
raise RuntimeError(
|
|
f"failed to compile {shader_path}: {result.stderr.decode()}"
|
|
)
|
|
|
|
spirv = result.stdout
|
|
if len(spirv) % 4 != 0:
|
|
raise RuntimeError(
|
|
f"invalid SPIR-V output for {shader_path} (size not a multiple of 4)"
|
|
)
|
|
|
|
word_count = len(spirv) // 4
|
|
words = struct.unpack(f"<{word_count}I", spirv)
|
|
|
|
filename = os.path.basename(shader_path)
|
|
variable_name = f"{filename.replace('.', '_')}_spv"
|
|
header_path = shader_path + ".spv.h"
|
|
|
|
body_lines = [
|
|
" " + ", ".join(f"0x{word:08x}" for word in words[i:i + 4]) + ","
|
|
for i in range(0, len(words), 4)
|
|
]
|
|
|
|
header = "\n".join([
|
|
"#pragma once",
|
|
"",
|
|
"#include <array>",
|
|
"#include <cstdint>",
|
|
"",
|
|
"namespace sophia::shaders {",
|
|
"",
|
|
f"inline constexpr std::array<uint32_t, {word_count}> {variable_name} = {{",
|
|
*body_lines,
|
|
"};",
|
|
"",
|
|
"} // namespace sophia::shaders",
|
|
"",
|
|
])
|
|
|
|
with open(header_path, "w") as f:
|
|
f.write(header)
|
|
|
|
print(f"Wrote {os.path.relpath(header_path, self._project_root())} "
|
|
f"({word_count * 4} bytes)")
|