267 lines
10 KiB
Python
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;')
|