编译脚本

This commit is contained in:
Nanako 2025-02-08 14:47:00 +08:00
parent b7936d2e1a
commit b38aa6d65b
6 changed files with 264 additions and 110 deletions

View File

@ -4,15 +4,18 @@ cmake_policy(SET CMP0167 NEW)
project(mirage LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 26)
#
set(SHADER_OUTPUT_DIR "${CMAKE_BINARY_DIR}/shaders" CACHE PATH "着色器编译输出路径")
#
set(SHADER_SOURCE_DIRS "" CACHE STRING "着色器源文件路径列表")
mark_as_advanced(SHADER_OUTPUT_DIR)
set(MSDFGEN_USE_SKIA OFF CACHE BOOL "Use Skia for MSDFGen" FORCE)
set(MSDFGEN_USE_VCPKG OFF CACHE BOOL "Use VCPKG for MSDFGen" FORCE)
set(MSDFGEN_USE_OPENMP ON CACHE BOOL "Use OpenMP for MSDFGen" FORCE)
set(MSDFGEN_BUILD_STANDALONE ON CACHE BOOL "Build MSDFGen standalone" FORCE)
#
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
#
set(SHADER_OUTPUT_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resource/shaders" CACHE PATH "着色器编译输出路径")
include(cmake/retrieve_files.cmake)
include(cmake/detect_os.cmake)

View File

@ -1,12 +1,6 @@
#include "mirage.h"
int main(int argc, char* argv[]) {
window_desc desc{};
desc.title = "mirage";
desc.resolution.width = 1280;
desc.resolution.height = 720;
desc.resizable = true;
mirage::init_info init{};
return run(init);
}

View File

@ -1,108 +1,226 @@
from typing import List, Tuple, Iterator, Optional
from pathlib import Path
import argparse
import os
import subprocess
import sys
import re
def find_shader_files(input_dir, extensions):
for root, _, files in os.walk(input_dir):
for file in files:
if any(file.endswith(ext) for ext in extensions):
yield os.path.join(root, file)
# 设置控制台输出编码
if sys.platform.startswith('win'):
# Windows系统下设置
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')
def find_slang_entries(input_file):
def print_utf8(message: str):
"""
以UTF-8编码打印消息
Args:
message: 要打印的消息
"""
print(message.encode('utf-8').decode(sys.stdout.encoding))
# 常量定义
SHADER_EXTENSIONS = {
'glsl': 'glsl',
'spirv': 'spirv',
'dxil': 'dxil',
'dxbc': 'dxbc',
'metallib': 'metallib',
'wgsl': 'wgsl'
}
# 不同目标平台的编译配置
TARGET_PROFILES = {
'glsl': ['-profile', 'glsl_460'],
'spirv': ['-profile', 'glsl_460', '-capability', 'glsl_spirv_1_6'],
'dxbc': ['-profile', 'sm_5_1'],
'dxil': ['-profile', 'sm_6_6'],
'metallib': ['-capability', 'metallib_3_1']
}
def find_shader_files(input_dir: Path, extensions: List[str]) -> Iterator[Path]:
"""
递归查找指定目录下的着色器文件
Args:
input_dir: 输入目录路径
extensions: 着色器文件扩展名列表
Yields:
符合扩展名的着色器文件路径
"""
for file_path in Path(input_dir).rglob('*'):
if file_path.suffix in extensions:
yield file_path
def find_slang_entries(input_file: Path) -> List[str]:
"""
从着色器文件中提取入口点函数名
Args:
input_file: 着色器文件路径
Returns:
入口点函数名列表
"""
# 匹配 [shader("xxx")] 形式的着色器入口点声明
pattern = re.compile(r'\[shader\(\s*"(?:\w+)"\s*\)\]\s*\n\s*\w+\s+(\w+)\s*\(')
try:
with open(input_file, 'r', encoding='utf-8') as f:
content = f.read()
pattern = r'\[shader\(\s*"(?:\w+)"\s*\)\]\s*\n\s*\w+\s+(\w+)\s*\('
return list(set(re.findall(pattern, content)))
content = input_file.read_text(encoding='utf-8')
return list(set(pattern.findall(content)))
except Exception as e:
print(f"Error parsing {input_file}: {str(e)}")
print_utf8(f"**错误**: 解析文件 {input_file} 失败: {e}")
return []
def gen_shader_ext(build_type):
if build_type == 'glsl':
return 'glsl'
elif build_type == 'spirv':
return 'spirv'
elif build_type == 'dxil':
return 'dxil'
elif build_type == 'dxbc':
return 'dxbc'
elif build_type == 'metallib':
return 'metallib'
elif build_type == 'wgsl':
return 'wgsl'
else:
return 'dat'
def get_shader_extension(build_type: str) -> str:
"""
根据构建类型获取对应的着色器文件扩展名
Args:
build_type: 构建类型
Returns:
对应的文件扩展名
"""
return SHADER_EXTENSIONS.get(build_type, 'dat')
def gen_slangc_cmd(input_file, entry, output_file, target_type, args):
slangc = args.slangc
cmd = [slangc, input_file, "-entry", entry, "-o", output_file, "-target", target_type]
if args.debug:
cmd.append("-g3")
else:
cmd.append("-O3")
def create_compiler_command(
input_file: Path,
entry: str,
output_file: Path,
target_type: str,
args: argparse.Namespace
) -> List[str]:
"""
生成着色器编译命令
Args:
input_file: 输入文件路径
entry: 着色器入口点
output_file: 输出文件路径
target_type: 目标平台类型
args: 命令行参数
Returns:
编译命令列表
"""
cmd = [args.slangc,
str(input_file),
"-entry", entry,
"-o", str(output_file),
"-target", target_type,
"-g3" if args.debug else "-O3"
]
# 添加优化或调试标志
if target_type == 'glsl':
cmd.extend(["-profile", "glsl_460"])
if target_type == 'spirv':
cmd.extend(["-profile", "glsl_460"])
cmd.extend(["-capability", "glsl_spirv_1_6"])
if target_type == 'dxbc':
cmd.extend(["-profile", "sm_5_1"])
if target_type == 'dxil':
cmd.extend(["-profile", "sm_6_6"])
if target_type == 'metallib':
cmd.extend(["-capability", "metallib_3_1"])
# 添加目标平台特定的编译选项
if target_type in TARGET_PROFILES:
cmd.extend(TARGET_PROFILES[target_type])
return cmd
def compile_slang(input_file, target_types, output_dir, args):
def needs_recompile(input_file: Path, output_file: Path) -> bool:
"""
检查是否需要重新编译着色器
Args:
input_file: 输入文件路径
output_file: 输出文件路径
Returns:
是否需要重新编译
"""
if not output_file.exists():
return True
try:
return input_file.stat().st_mtime > output_file.stat().st_mtime
except OSError:
return True
def compile_shader(
input_file: Path,
target_types: List[Tuple[str, bool]],
output_dir: Path,
args: argparse.Namespace
) -> bool:
"""
编译单个着色器文件
Args:
input_file: 输入文件路径
target_types: 目标平台类型列表
output_dir: 输出目录
args: 命令行参数
Returns:
编译是否成功
"""
try:
# 获取着色器入口点
entries = find_slang_entries(input_file)
if not entries:
print(f"Skipping {input_file}: No shader entries found")
print_utf8(f"**跳过**: {input_file} - 未找到着色器入口点")
return True
base = os.path.splitext(os.path.basename(input_file))[0]
abs_path = os.path.abspath(output_dir)
os.makedirs(abs_path, exist_ok=True)
# 创建输出目录
output_dir.mkdir(parents=True, exist_ok=True)
base = input_file.stem
success = True
# 针对每个目标平台编译
for target_type, enabled in target_types:
if not enabled:
continue
for entry in entries:
output_file = os.path.join(abs_path, f"{base}_{entry}.{gen_shader_ext(target_type)}")
cmd = gen_slangc_cmd(input_file, entry, output_file, target_type, args)
output_file = output_dir / f"{base}_{entry}.{get_shader_extension(target_type)}"
# 检查是否需要重新编译
if not needs_recompile(input_file, output_file):
print_utf8(f"**跳过**: {output_file} - 文件已是最新")
continue
# 执行编译
cmd = create_compiler_command(input_file, entry, output_file, target_type, args)
try:
subprocess.run(cmd, check=True, capture_output=True, text=True)
print(f"Compiled Slang: {input_file}:{entry} -> {output_file}")
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
print_utf8(f"**成功**: 编译 {input_file}:{entry} -> {output_file}")
except subprocess.CalledProcessError as e:
print(f"Error compiling {input_file}:{entry}")
print(e.stderr)
print_utf8(f"**错误**: 编译 {input_file}:{entry} 失败")
print_utf8(e.stderr)
success = False
return success
except Exception as e:
print(f"Unexpected error with {input_file}: {str(e)}")
print_utf8(f"**错误**: 处理 {input_file} 时发生异常: {e}")
return False
def main():
parser = argparse.ArgumentParser(description="Compile slang shaders using slangc")
parser.add_argument("--shader-list", help="Input shader list .txt file")
parser.add_argument("--output-dir", help="Output directory")
parser.add_argument("--slangc", default="slangc", help="Path to slangc")
parser.add_argument("--debug", action="store_true", help="Compile in debug mode")
parser.add_argument("--opengl", action="store_true", help="Compile Slang for OpenGL")
parser.add_argument("--vulkan", action="store_true", help="Compile Slang for Vulkan")
parser.add_argument("--d3d11", action="store_true", help="Compile Slang for D3D11")
parser.add_argument("--d3d12", action="store_true", help="Compile Slang for D3D12")
parser.add_argument("--metal", action="store_true", help="Compile Slang for Metal")
"""
主函数解析命令行参数并执行编译流程
"""
# 设置UTF-8编码
if sys.platform.startswith('win'):
# Windows下设置控制台代码页
subprocess.run(['chcp', '65001'], shell=True)
# 设置命令行参数
parser = argparse.ArgumentParser(description="使用 slangc 编译着色器")
parser.add_argument("--shader-list", help="着色器列表文件路径")
parser.add_argument("--output-dir", help="输出目录")
parser.add_argument("--slangc", default="slangc", help="slangc 编译器路径")
parser.add_argument("--debug", action="store_true", help="启用调试模式编译")
parser.add_argument("--opengl", action="store_true", help="编译 OpenGL 着色器")
parser.add_argument("--vulkan", action="store_true", help="编译 Vulkan 着色器")
parser.add_argument("--d3d11", action="store_true", help="编译 D3D11 着色器")
parser.add_argument("--d3d12", action="store_true", help="编译 D3D12 着色器")
parser.add_argument("--metal", action="store_true", help="编译 Metal 着色器")
args = parser.parse_args()
# 配置目标平台
target_types = [
['glsl', args.opengl],
['spirv', args.vulkan],
@ -111,21 +229,19 @@ def main():
['metallib', args.metal],
]
output_dir = args.output_dir or "shaders"
shader_dir = args.shader_list or "shader_paths.txt"
# 设置输出目录和着色器列表文件
output_dir = Path(args.output_dir or "shaders")
shader_list = Path(args.shader_list or "shader_paths.txt")
# 读取当前同级目录下的shader_paths.txt
with open(shader_dir, 'r') as f:
shader_paths = f.readlines()
slang_ext = ['.slang']
# 读取着色器路径列表
shader_paths = shader_list.read_text().splitlines()
# 编译所有着色器
all_success = True
for shader_path in shader_paths:
# Compile Slang
for file in find_shader_files(shader_path, slang_ext):
if not compile_slang(file, target_types, output_dir, args):
shader_path = shader_path.strip()
for file in find_shader_files(shader_path, ['.slang']):
if not compile_shader(file, target_types, output_dir, args):
all_success = False
if not all_success:

View File

@ -13,7 +13,7 @@ set(RENDERER_SOURCES "")
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} RENDERER_SOURCES)
add_library(${PROJECT_NAME} STATIC ${RENDERER_SOURCES})
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype glfw harfbuzz Eigen3::Eigen spdlog::spdlog msdfgen-full Boost::boost Vulkan::Vulkan)
target_link_libraries(${PROJECT_NAME} PUBLIC Freetype::Freetype glfw harfbuzz Eigen3::Eigen spdlog::spdlog msdfgen-full Boost::boost Vulkan::Vulkan LLGL)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(${PROJECT_NAME} PRIVATE NOMINMAX)
add_os_definitions(${PROJECT_NAME})
@ -27,18 +27,3 @@ endif ()
shader_compile_target(${CMAKE_CURRENT_SOURCE_DIR}/shaders)
# , mirage_core, compile_shaders
add_dependencies(${PROJECT_NAME} compile_shaders)
# example, shader
if (BUILD_EXAMPLE)
# ${SHADER_OUTPUT_DIR}${CMAKE_CURRENT_BINARY_DIR}/example/resource/shaders
set(SOURCE_DIR ${SHADER_OUTPUT_DIR})
set(DEST_DIR ${CMAKE_BINARY_DIR}/example/resource/shaders)
# , mirage_core, ${SOURCE_DIR}${DEST_DIR}
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${SOURCE_DIR} ${DEST_DIR}
COMMENT "Copying shaders to example"
)
endif ()

View File

@ -5,16 +5,56 @@ using time_type = decltype(std::chrono::high_resolution_clock::now());
std::chrono::duration<double> delta_time = {};
time_type begin_time = {};
time_type last_time = {};
LLGL::RenderSystemPtr renderer = nullptr;
namespace mirage {
void on_llgl_log(LLGL::Log::ReportType type, const char* text, void* user_data) {
switch (type) {
case LLGL::Log::ReportType::Default:
spdlog::info(text);
break;
case LLGL::Log::ReportType::Error:
spdlog::error(text);
break;
}
}
std::string to_string(renderer_api api) {
switch (api) {
case renderer_api::dx11: return "Direct3D11";
case renderer_api::dx12: return "Direct3D12";
case renderer_api::vulkan: return "Vulkan";
case renderer_api::opengl: return "OpenGL";
case renderer_api::metal: return "Metal";
}
return "Unknown";
}
bool init_renderer(const init_info& in_info) {
RegisterCallback(on_llgl_log);
LLGL::RenderSystemDescriptor desc{};
desc.moduleName = to_string(in_info.api);
spdlog::info("初始化渲染器: {}", desc.moduleName.c_str());
renderer = LLGL::RenderSystem::Load(desc);
if (renderer != nullptr) {
spdlog::info("渲染器加载成功");
return true;
}
spdlog::error("渲染器加载失败");
return false;
}
bool init(const init_info& in_info) {
spdlog::info("初始化 mirage");
begin_time = std::chrono::high_resolution_clock::now();
if (!init_renderer(in_info)) {
return false;
}
begin_time = std::chrono::high_resolution_clock::now();
last_time = std::chrono::high_resolution_clock::now();
return true;
}
void destroy() {
renderer.reset();
spdlog::info("mirage 销毁");
}
@ -36,6 +76,7 @@ namespace mirage {
int run(const init_info& in_init_info) {
try {
if (!init(in_init_info)) {
spdlog::error("初始化 mirage 失败");
return -1;
}
while (!should_exit()) {

View File

@ -1,12 +1,27 @@
#pragma once
#include <spdlog/spdlog.h>
#include "LLGL/LLGL.h"
#define MIRAGE_VERSION_MAJOR 0
#define MIRAGE_VERSION_MINOR 1
#define MIRAGE_VERSION_PATCH 0
namespace mirage {
enum class renderer_api {
dx11,
dx12,
vulkan,
opengl,
metal,
};
struct init_info {
#if MIRAGE_PLATFORM_WINDOWS
renderer_api api = renderer_api::dx11;
#elif MIRAGE_PLATFORM_MACOS
renderer_api api = renderer_api::metal;
#elif MIRAGE_PLATFORM_LINUX
renderer_api api = renderer_api::vulkan;
#endif
};
int run(const init_info& in_init_info);