Files
mirage_slang/code_generator.py
2025-06-05 20:29:36 +08:00

267 lines
10 KiB
Python

#!/usr/bin/env python3
"""
SDL3_GPU Slang Compiler - Code Generator
生成C/C++绑定代码
"""
import os.path
from typing import List, Dict, TextIO, Optional
from pathlib import Path
from contextlib import contextmanager
class IndentManager:
"""RAII风格的缩进管理器"""
def __init__(self, file: TextIO, indent_char: str = '\t'):
self.file = file
self.indent_char = indent_char
self.indent_level = 0
def write(self, text: str = '') -> None:
"""写入带缩进的文本"""
if text:
self.file.write(self.indent_char * self.indent_level + text + '\n')
else:
self.file.write('\n')
self.file.flush()
def write_raw(self, text: str) -> None:
"""写入原始文本(无缩进)"""
self.file.write(text)
@contextmanager
def indent(self, header: Optional[str] = None, footer: Optional[str] = None):
"""缩进上下文管理器"""
if header:
self.write(header)
self.indent_level += 1
try:
yield self
finally:
self.indent_level -= 1
if footer:
self.write(footer)
@contextmanager
def block(self, header: str, footer: str = '}'):
"""代码块上下文管理器"""
self.write(header + ' {')
self.indent_level += 1
try:
yield self
finally:
self.indent_level -= 1
self.write(footer)
class CodeGenerator:
"""C++绑定代码生成器"""
# 资源类型映射表
RESOURCE_TYPE_MAP = {
'sampled_texture': 'Resource::SampledTexture',
'storage_texture': 'Resource::StorageTexture',
'storage_buffer': 'Resource::StorageBuffer',
'uniform_buffer': 'Resource::UniformBuffer',
'sampler': 'Resource::Sampler'
}
def generate_binding_functions(self, source_file_pathname, binding_infos: List[Dict], output_path: str) -> None:
"""生成C++绑定函数的入口方法"""
self._generate_cpp_bindings(source_file_pathname, binding_infos, output_path)
def _generate_cpp_bindings(self, source_file_pathname, binding_infos: List[Dict], output_path: str) -> None:
"""生成C++绑定函数"""
output_file = Path(output_path)
# 尝试创建输出目录
output_file.parent.mkdir(parents=True, exist_ok=True)
with open(output_file, 'w', encoding='utf-8') as file:
writer = IndentManager(file)
self._write_complete_file(writer, source_file_pathname, binding_infos)
def _write_complete_file(self, writer: IndentManager, source_file_pathname, binding_infos: List[Dict]) -> None:
"""写入完整的文件内容"""
self._write_header(writer)
with writer.block('namespace SDL3GPU', '} // namespace SDL3GPU'):
writer.write()
self._write_shader_bindings_class(writer, source_file_pathname, binding_infos)
writer.write()
def _write_header(self, writer: IndentManager) -> None:
"""写入文件头部"""
headers = [
'#pragma once',
'#include <SDL3/SDL_gpu.h>',
'#include <string>',
'#include <vector>',
'#include <unordered_map>',
]
for header in headers:
writer.write(header)
writer.write()
def _write_shader_bindings_class(self, writer: IndentManager, source_file_pathname: str, binding_infos: List[Dict]) -> None:
"""写入ShaderBindings类"""
# 获取源文件名(不带路径和拓展名)
source_file_name = Path(source_file_pathname).stem
with writer.block(f'class {source_file_name}ShaderBindings','};'):
self._write_public_section(writer)
self._write_private_section(writer)
self._write_constructor(writer, binding_infos)
self._write_public_methods(writer)
def _write_public_section(self, writer: IndentManager) -> None:
"""写入public部分的结构体定义"""
writer.write('public:')
# Resource结构体
with writer.indent():
with writer.block('struct Resource', '};'):
with writer.block('enum Type'):
enum_values = [
'SampledTexture,',
'StorageTexture,',
'StorageBuffer,',
'UniformBuffer,',
'Sampler'
]
for value in enum_values:
writer.write(value)
writer.write()
writer.write('std::string name;')
writer.write('Type type;')
writer.write('int binding = -1;')
writer.write('int set = -1;')
writer.write('int space = -1;')
writer.write('int index = -1;')
writer.write()
# ShaderInfo结构体
with writer.block('struct ShaderInfo','};'):
writer.write('std::string entryPoint;')
writer.write('SDL_GPUShaderStage stage;')
writer.write('std::vector<Resource> resources;')
writer.write()
def _write_private_section(self, writer: IndentManager) -> None:
"""写入private部分"""
writer.write('private:')
with writer.indent():
writer.write('std::unordered_map<std::string, ShaderInfo> m_shaderInfos;')
writer.write()
def _write_constructor(self, writer: IndentManager, binding_infos: List[Dict]) -> None:
"""写入构造函数"""
writer.write('public:')
with writer.indent():
with writer.block('ShaderBindings()'):
for info in binding_infos:
self._write_shader_initialization(writer, info)
def _write_shader_initialization(self, writer: IndentManager, shader_info: Dict) -> None:
"""写入单个着色器的初始化代码"""
entry_point = shader_info['entry_point']
stage = shader_info['stage'].upper()
writer.write(f'// {entry_point}')
with writer.indent('{', '}'):
writer.write('ShaderInfo info;')
writer.write(f'info.entryPoint = "{entry_point}";')
writer.write(f'info.stage = SDL_GPU_SHADERSTAGE_{stage};')
# 写入资源信息
for resource in shader_info['resources']:
self._write_resource_initialization(writer, resource)
writer.write(f'm_shaderInfos["{entry_point}"] = std::move(info);')
writer.write()
def _write_resource_initialization(self, writer: IndentManager, resource: Dict) -> None:
"""写入单个资源的初始化代码"""
with writer.indent('{', '}'):
writer.write('Resource r;')
writer.write(f'r.name = "{resource["name"]}";')
# 设置资源类型
resource_type = self.RESOURCE_TYPE_MAP.get(resource["type"])
if resource_type:
writer.write(f'r.type = {resource_type};')
# 设置资源绑定信息
if 'binding' in resource:
writer.write(f'r.binding = {resource["binding"]};')
writer.write(f'r.set = {resource["set"]};')
elif 'register' in resource:
writer.write(f'r.space = {resource["space"]};')
elif 'index' in resource:
writer.write(f'r.index = {resource["index"]};')
writer.write('info.resources.push_back(r);')
def _write_public_methods(self, writer: IndentManager) -> None:
"""写入公共方法"""
writer.write()
self._write_bind_resources_method(writer)
writer.write()
self._write_get_shader_info_method(writer)
def _write_bind_resources_method(self, writer: IndentManager) -> None:
"""写入bindResources方法"""
method_signature = (
'void bindResources(\n'
f'{writer.indent_char * (writer.indent_level + 2)}SDL_GPUCommandBuffer* cmd,\n'
f'{writer.indent_char * (writer.indent_level + 2)}const std::string& shaderName,\n'
f'{writer.indent_char * (writer.indent_level + 2)}const std::unordered_map<std::string, void*>& resources)'
)
with writer.block(method_signature):
writer.write('auto it = m_shaderInfos.find(shaderName);')
with writer.block('if (it == m_shaderInfos.end())'):
writer.write('return;')
writer.write()
writer.write('const auto& info = it->second;')
writer.write()
writer.write('// 根据着色器阶段和资源类型进行绑定')
writer.write('// 这里需要根据SDL3 GPU的实际API来实现')
with writer.block('for (const auto& res : info.resources)'):
writer.write('auto resIt = resources.find(res.name);')
with writer.block('if (resIt == resources.end())'):
writer.write('continue;')
writer.write()
writer.write('void* resource = resIt->second;')
writer.write()
writer.write('// 示例绑定逻辑')
with writer.block('switch (res.type)'):
writer.write('case Resource::SampledTexture:')
with writer.indent():
writer.write('// SDL_BindGPUVertexSamplers(cmd, ...);')
writer.write('break;')
writer.write('case Resource::StorageBuffer:')
with writer.indent():
writer.write('// SDL_BindGPUVertexStorageBuffers(cmd, ...);')
writer.write('break;')
writer.write('// ... 其他资源类型')
writer.write('default:')
with writer.indent():
writer.write('break;')
def _write_get_shader_info_method(self, writer: IndentManager) -> None:
"""写入getShaderInfo方法"""
with writer.block('const ShaderInfo* getShaderInfo(const std::string& shaderName) const'):
writer.write('auto it = m_shaderInfos.find(shaderName);')
writer.write('return it != m_shaderInfos.end() ? &it->second : nullptr;')