154 lines
4.6 KiB
Python
154 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
工具函数模块
|
|
|
|
包含着色器文件发现、分组、元数据加载等辅助功能。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
|
|
from .constants import SHADER_EXTENSIONS
|
|
from .types import ShaderMetadata
|
|
|
|
LOG = logging.getLogger("glsl2spirv")
|
|
|
|
# 项目根目录
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
DEFAULT_OUTPUT_DIR = ROOT / "build" / "generated" / "shaders"
|
|
|
|
|
|
# ============ Shader Discovery ============
|
|
|
|
def discover_shaders(directories: List[Path]) -> List[Path]:
|
|
"""递归扫描目录,发现所有.glsl着色器文件"""
|
|
shaders: List[Path] = []
|
|
for base_dir in directories:
|
|
if not base_dir.exists():
|
|
LOG.warning("Shader directory %s does not exist (skipping).", base_dir)
|
|
continue
|
|
|
|
for glsl_file in base_dir.rglob("*.glsl"):
|
|
if glsl_file.name.startswith("lib_"):
|
|
if LOG.isEnabledFor(logging.DEBUG):
|
|
LOG.debug("Skipping library file: %s", glsl_file)
|
|
continue
|
|
|
|
shaders.append(glsl_file.resolve())
|
|
|
|
return sorted(shaders)
|
|
|
|
|
|
def group_shader_files(shader_files: List[Path]) -> Dict[str, List[Path]]:
|
|
"""按着色器组进行分组(同一个基础名称的所有阶段)"""
|
|
groups: Dict[str, List[Path]] = {}
|
|
|
|
for shader_file in shader_files:
|
|
for ext, stage_name in SHADER_EXTENSIONS.items():
|
|
if shader_file.name.endswith(f"{ext}.glsl"):
|
|
base_name = shader_file.name[: -len(f"{ext}.glsl")]
|
|
if base_name not in groups:
|
|
groups[base_name] = []
|
|
groups[base_name].append(shader_file)
|
|
break
|
|
|
|
return groups
|
|
|
|
|
|
# ============ Metadata Loading ============
|
|
|
|
def load_shader_metadata(shader_path: Path, args: argparse.Namespace) -> ShaderMetadata:
|
|
"""从文件名推导着色器配置元数据"""
|
|
stem = shader_path.stem
|
|
|
|
# 检查是否存在JSON文件并警告
|
|
json_candidates = [
|
|
shader_path.with_suffix(".glsl.json"),
|
|
shader_path.with_suffix(".json"),
|
|
]
|
|
for candidate in json_candidates:
|
|
if candidate.exists():
|
|
LOG.warning("JSON metadata file %s found but ignored. Using SPIR-V reflection.", candidate)
|
|
break
|
|
|
|
# 从文件名推断shader_type
|
|
shader_type = "compute"
|
|
for ext in SHADER_EXTENSIONS:
|
|
if shader_path.name.endswith(f"{ext}.glsl"):
|
|
if ext in ('.vert', '.frag'):
|
|
shader_type = "graphics"
|
|
else:
|
|
shader_type = "compute"
|
|
break
|
|
|
|
# 默认入口点
|
|
if shader_type == "compute":
|
|
entry_points = {"compute": "main"}
|
|
else:
|
|
entry_points = {"vertex": "main", "fragment": "main"}
|
|
|
|
# 输出头文件名
|
|
base_stem = stem
|
|
for ext in SHADER_EXTENSIONS:
|
|
suffix = ext[1:] # 移除点
|
|
if base_stem.endswith(f".{suffix}"):
|
|
base_stem = base_stem[:-len(f".{suffix}")]
|
|
break
|
|
|
|
if shader_type == "compute":
|
|
ext_suffix = "_comp"
|
|
else:
|
|
ext_suffix = "_vert_frag"
|
|
output_header = f"{base_stem}{ext_suffix}.h"
|
|
|
|
namespace = "mirage::shaders"
|
|
|
|
# include路径默认为shader所在目录的lib子目录
|
|
include_paths: List[Path] = []
|
|
lib_path = shader_path.parent / "lib"
|
|
if lib_path.exists():
|
|
include_paths.append(lib_path)
|
|
parent_lib = shader_path.parent.parent / "lib"
|
|
if parent_lib.exists():
|
|
include_paths.append(parent_lib)
|
|
|
|
return ShaderMetadata(
|
|
name=base_stem,
|
|
shader_type=shader_type,
|
|
entry_points=entry_points,
|
|
output_header=output_header,
|
|
namespace=namespace,
|
|
bindings=[], # 将从SPIR-V反射填充
|
|
include_paths=include_paths,
|
|
defines={},
|
|
)
|
|
|
|
|
|
# ============ Directory List Reading ============
|
|
|
|
def read_directory_list(list_file: Optional[str]) -> List[Path]:
|
|
"""读取着色器目录列表文件"""
|
|
if not list_file:
|
|
return []
|
|
|
|
path = Path(list_file)
|
|
if not path.exists():
|
|
LOG.warning("Shader directory list %s does not exist", path)
|
|
return []
|
|
|
|
directories: List[Path] = []
|
|
for line in path.read_text(encoding="utf-8").splitlines():
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
dir_path = Path(line)
|
|
if dir_path.exists():
|
|
directories.append(dir_path)
|
|
else:
|
|
LOG.warning("Shader directory %s does not exist (skipping).", dir_path)
|
|
|
|
return directories |