Files
mirage/tools/compiler.py
daiqingshuang a635b0464f 功能:实现着色器编译与反射信息提取
- 新增ShaderCompiler类,通过glslc工具实现GLSL到SPIR-V的编译。
- 实现构建glslc命令、运行编译器及从SPIR-V二进制文件提取反射数据的功能。
- 为SPIR-V指令集、装饰符、执行模型及存储类创建常量。
- 开发SPIR-V解析器以提取类型信息、变量细节及反射数据
- 引入类型映射函数实现SPIR-V类型到C++类型的转换,并计算std430内存布局
- 定义着色器元数据、编译结果及SPIR-V反射信息的数据类
- 添加着色器发现、分组及元数据加载的实用函数
2025-11-22 11:51:31 +08:00

170 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
着色器编译模块
负责调用glslc编译器将GLSL源代码编译为SPIR-V二进制。
"""
from __future__ import annotations
import logging
import shutil
import subprocess
import tempfile
from pathlib import Path
from typing import List
from .constants import GLSLC_STD, GLSLC_TARGET_ENV
from .spirv_parser import extract_spirv_reflection
from .types import CompilationResult, ShaderMetadata, ToolError
LOG = logging.getLogger("glsl2spirv")
class ShaderCompiler:
"""GLSL到SPIR-V编译器"""
def __init__(self, verbose: bool = False, dry_run: bool = False):
self.verbose = verbose
self.dry_run = dry_run
self.glslc_path = self._find_glslc()
def _find_glslc(self) -> str:
"""查找glslc编译器"""
glslc = shutil.which("glslc")
if glslc:
return glslc
glslc = shutil.which("glslc.exe")
if glslc:
return glslc
raise ToolError("Unable to locate glslc compiler. Please install Vulkan SDK.")
def _build_glslc_command(
self,
shader_path: Path,
output_path: Path,
metadata: ShaderMetadata,
shader_type: str,
debug_mode: bool = False,
) -> List[str]:
"""构建glslc命令行
Args:
shader_path: 着色器源文件路径
output_path: 输出SPIR-V文件路径
metadata: 着色器元数据
shader_type: 着色器类型vert, frag, comp等
debug_mode: 是否启用debug模式用于提取完整反射数据
"""
stage_map = {
'vert': 'vertex',
'frag': 'fragment',
'comp': 'compute',
'geom': 'geometry',
'tesc': 'tesscontrol',
'tese': 'tesseval',
}
cmd = [self.glslc_path]
if shader_type in stage_map:
cmd.append(f"-fshader-stage={stage_map[shader_type]}")
cmd.extend([
str(shader_path),
"-o", str(output_path),
"--target-env=" + GLSLC_TARGET_ENV,
"-std=" + GLSLC_STD,
])
# debug模式启用调试信息和禁用优化以保留完整的反射数据
if debug_mode:
cmd.extend(["-g", "-O0"])
for inc_path in metadata.include_paths:
cmd.append(f"-I{inc_path}")
for macro_name, macro_value in metadata.defines.items():
if macro_value:
cmd.append(f"-D{macro_name}={macro_value}")
else:
cmd.append(f"-D{macro_name}")
return cmd
def _run_glslc(self, cmd: List[str], shader_path: Path, shader_type: str) -> None:
"""执行glslc命令"""
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
if result.returncode != 0:
raise ToolError(
f"glslc compilation failed for {shader_path.name} ({shader_type}):\n{result.stderr}"
)
except FileNotFoundError:
raise ToolError(f"glslc executable not found at {self.glslc_path}")
def compile_shader(
self,
shader_path: Path,
metadata: ShaderMetadata,
shader_type: str,
) -> CompilationResult:
"""编译单个着色器并提取反射信息
编译流程:
1. 先用debug模式编译-g -O0以获取完整的反射数据
2. 再用正常模式编译生成最终的优化SPIR-V
这样可以确保绑定代码生成使用完整的变量名信息,
同时最终的着色器仍然是优化过的版本。
"""
with tempfile.TemporaryDirectory() as tmpdir:
debug_output_path = Path(tmpdir) / f"{shader_path.stem}_debug.spv"
release_output_path = Path(tmpdir) / f"{shader_path.stem}.spv"
if not self.dry_run:
# 步骤1用debug模式编译以提取完整反射数据
debug_cmd = self._build_glslc_command(
shader_path, debug_output_path, metadata, shader_type, debug_mode=True
)
if self.verbose:
LOG.debug("Running glslc (debug): %s", " ".join(debug_cmd))
self._run_glslc(debug_cmd, shader_path, shader_type)
debug_spirv_data = debug_output_path.read_bytes()
# 从debug版本的SPIR-V提取完整反射信息
reflection_result = extract_spirv_reflection(debug_spirv_data, shader_type)
entry_point = reflection_result['entry_point']
bindings = reflection_result['bindings']
reflection = reflection_result.get('reflection')
# 步骤2用正常模式编译生成最终SPIR-V
release_cmd = self._build_glslc_command(
shader_path, release_output_path, metadata, shader_type, debug_mode=False
)
if self.verbose:
LOG.debug("Running glslc (release): %s", " ".join(release_cmd))
self._run_glslc(release_cmd, shader_path, shader_type)
spirv_data = release_output_path.read_bytes()
else:
spirv_data = b""
entry_point = "main"
bindings = []
reflection = None
return CompilationResult(
source_file=shader_path,
shader_type=shader_type,
spirv_data=spirv_data,
entry_point=entry_point,
bindings=bindings,
reflection=reflection,
)