diff --git a/__init__.py b/__init__.py index ad47ae1..dd86a1e 100644 --- a/__init__.py +++ b/__init__.py @@ -3,7 +3,7 @@ SDL3_GPU Slang Compiler Package 自动处理资源绑定点的Slang着色器编译器 """ - +from shader_layout_generator import ShaderLayoutGenerator from shader_types import ShaderStage, ResourceType, TargetFormat, Resource, ShaderInfo from compiler import SDL3GPUSlangCompiler from slangc_finder import SlangcFinder @@ -21,7 +21,8 @@ __all__ = [ 'SlangcFinder', 'ShaderParser', 'BindingManager', - 'CodeGenerator' + 'CodeGenerator', + 'ShaderLayoutGenerator' ] version = '1.0.0' \ No newline at end of file diff --git a/code_generator.py b/code_generator.py index e4c51a7..ac5d2a8 100644 --- a/code_generator.py +++ b/code_generator.py @@ -96,6 +96,7 @@ class CodeGenerator: '#include ', '#include ', '#include ', + '#include ', ] for header in headers: @@ -108,43 +109,49 @@ class CodeGenerator: source_file_name = Path(source_file_pathname).stem with writer.block(f'class {source_file_name}ShaderBindings','};'): - self._write_public_section(writer) + self._write_public_section(writer, binding_infos) self._write_private_section(writer) self._write_constructor(writer, binding_infos) self._write_public_methods(writer) - def _write_public_section(self, writer: IndentManager) -> None: + def _write_public_section(self, writer: IndentManager, binding_infos: List[Dict]) -> 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() + # 写入二进制内容 + for info in binding_infos: + entry_point = info['entry_point'] + hex_list = ', '.join(f'0x{byte:02x}' for byte in info['blob']) + with writer.block(f'static constexpr uint8_t {entry_point}_blob[] = ', '};'): + writer.write(hex_list) - # ShaderInfo结构体 - with writer.block('struct ShaderInfo','};'): - writer.write('std::string entryPoint;') - writer.write('SDL_GPUShaderStage stage;') - writer.write('std::vector resources;') + # Resource结构体 + 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 resources;') + writer.write() def _write_private_section(self, writer: IndentManager) -> None: """写入private部分""" diff --git a/main.py b/main.py index 1917c6f..beaf77d 100644 --- a/main.py +++ b/main.py @@ -18,7 +18,7 @@ def main(): parser.add_argument('input', help='Input Slang shader file') parser.add_argument('-t', '--target', choices=['spirv', 'dxil', 'dxbc', 'msl'], required=True, help='Target shader format') - parser.add_argument('--binding-output-dir', required=True, help='Output path for binding code') + parser.add_argument('-o', '--output-dir', required=True, help='Output path for binding code') args = parser.parse_args() @@ -46,7 +46,7 @@ def main(): binding_info = compiler.compile_shader(shader_info, target) binding_infos.append(binding_info) - binding_output_file_pathname = os.path.abspath(args.binding_output_dir) + binding_output_file_pathname = os.path.abspath(args.output_dir) binding_output_file_pathname = os.path.join(binding_output_file_pathname, f"{source_file_name}.shader.h") # 生成绑定代码 diff --git a/shader_layout_generator.py b/shader_layout_generator.py new file mode 100644 index 0000000..e489c4d --- /dev/null +++ b/shader_layout_generator.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +import json +import sys +from typing import Dict, List, Any, Tuple + +class ShaderLayoutGenerator: + def __init__(self): + # SDL GPU 格式映射 + self.format_mapping = { + ('float32', 1): 'SDL_GPU_VERTEXELEMENTFORMAT_FLOAT', + ('float32', 2): 'SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2', + ('float32', 3): 'SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3', + ('float32', 4): 'SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4', + ('int32', 1): 'SDL_GPU_VERTEXELEMENTFORMAT_INT', + ('int32', 2): 'SDL_GPU_VERTEXELEMENTFORMAT_INT2', + ('int32', 3): 'SDL_GPU_VERTEXELEMENTFORMAT_INT3', + ('int32', 4): 'SDL_GPU_VERTEXELEMENTFORMAT_INT4', + ('uint32', 1): 'SDL_GPU_VERTEXELEMENTFORMAT_UINT', + ('uint32', 2): 'SDL_GPU_VERTEXELEMENTFORMAT_UINT2', + ('uint32', 3): 'SDL_GPU_VERTEXELEMENTFORMAT_UINT3', + ('uint32', 4): 'SDL_GPU_VERTEXELEMENTFORMAT_UINT4', + } + + # 类型大小映射 + self.type_sizes = { + 'float32': 4, + 'int32': 4, + 'uint32': 4, + } + + def get_type_info(self, type_obj: Dict) -> Tuple[str, int, int]: + """获取类型信息:C类型名称、元素数量、字节大小""" + kind = type_obj.get('kind') + + if kind == 'vector': + element_type = type_obj['elementType']['scalarType'] + element_count = type_obj['elementCount'] + c_type = 'float' if element_type == 'float32' else element_type + size = self.type_sizes[element_type] * element_count + return c_type, element_count, size + elif kind == 'scalar': + scalar_type = type_obj['scalarType'] + c_type = 'float' if scalar_type == 'float32' else scalar_type + size = self.type_sizes[scalar_type] + return c_type, 1, size + elif kind == 'matrix': + rows = type_obj['rowCount'] + cols = type_obj['columnCount'] + element_type = type_obj['elementType']['scalarType'] + size = self.type_sizes[element_type] * rows * cols + return 'float', rows * cols, size + + return 'float', 1, 4 + + def parse_vertex_input(self, json_data: Dict) -> List[Dict]: + """解析顶点输入字段""" + vertex_fields = [] + + # 查找顶点输入参数 + entry_points = json_data.get('entryPoints', []) + for entry in entry_points: + if entry.get('stage') == 'vertex': + parameters = entry.get('parameters', []) + for param in parameters: + if param.get('name') == 'input' and param.get('stage') == 'vertex': + fields = param.get('type', {}).get('fields', []) + for field in fields: + field_info = { + 'name': field['name'], + 'type': field['type'], + 'location': field['binding']['index'], + 'semantic': field.get('semanticName', ''), + 'semantic_index': field.get('semanticIndex', 0) + } + vertex_fields.append(field_info) + + return vertex_fields + + def parse_uniform_buffers(self, json_data: Dict) -> List[Dict]: + """解析Uniform缓冲区""" + uniform_buffers = [] + + parameters = json_data.get('parameters', []) + for param in parameters: + binding = param.get('binding', {}) + if binding.get('kind') == 'constantBuffer': + buffer_info = { + 'name': param['name'], + 'binding': binding['index'], + 'fields': [] + } + + # 解析缓冲区字段 + element_type = param.get('type', {}).get('elementType', {}) + if element_type.get('kind') == 'struct': + fields = element_type.get('fields', []) + for field in fields: + field_info = { + 'name': field['name'], + 'type': field['type'], + 'offset': field['binding']['offset'], + 'size': field['binding']['size'] + } + buffer_info['fields'].append(field_info) + + uniform_buffers.append(buffer_info) + + return uniform_buffers + + def generate_vertex_struct(self, vertex_fields: List[Dict]) -> str: + """生成顶点结构体""" + code = "// Auto-generated vertex structure\n" + code += "typedef struct Vertex {\n" + + for field in vertex_fields: + c_type, count, _ = self.get_type_info(field['type']) + if count == 1: + code += f" {c_type} {field['name']};\n" + else: + code += f" {c_type} {field['name']}[{count}];\n" + + # 添加注释 + semantic = field['semantic'] + if field['semantic_index'] > 0: + semantic += str(field['semantic_index']) + code += f" // location: {field['location']}, semantic: {semantic}\n" + + code += "} Vertex;\n\n" + return code + + def generate_vertex_attributes(self, vertex_fields: List[Dict]) -> str: + """生成顶点属性数组""" + code = "// Vertex attribute descriptions\n" + code += f"#define VERTEX_ATTRIBUTE_COUNT {len(vertex_fields)}\n\n" + code += "static const SDL_GPUVertexAttribute g_vertexAttributes[] = {\n" + + offset = 0 + for i, field in enumerate(vertex_fields): + c_type, count, size = self.get_type_info(field['type']) + scalar_type = field['type'].get('elementType', field['type']).get('scalarType', 'float32') + + format_key = (scalar_type, count) + format_name = self.format_mapping.get(format_key, 'SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4') + + code += " {\n" + code += f" .location = {field['location']},\n" + code += f" .buffer_slot = 0,\n" + code += f" .format = {format_name},\n" + code += f" .offset = {offset}\n" + code += " }" + + if i < len(vertex_fields) - 1: + code += "," + + code += f" // {field['name']}\n" + + offset += size + + code += "};\n\n" + + # 生成顶点缓冲区描述 + code += "// Vertex buffer description\n" + code += "static const SDL_GPUVertexBufferDescription g_vertexBufferDesc = {\n" + code += " .slot = 0,\n" + code += f" .pitch = {offset}, // sizeof(Vertex)\n" + code += " .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,\n" + code += " .instance_step_rate = 0\n" + code += "};\n\n" + + # 生成顶点输入状态 + code += "// Vertex input state\n" + code += "static const SDL_GPUVertexInputState g_vertexInputState = {\n" + code += " .vertex_buffer_descriptions = &g_vertexBufferDesc,\n" + code += " .num_vertex_buffers = 1,\n" + code += " .vertex_attributes = g_vertexAttributes,\n" + code += " .num_vertex_attributes = VERTEX_ATTRIBUTE_COUNT\n" + code += "};\n\n" + + return code + + def generate_uniform_structs(self, uniform_buffers: List[Dict]) -> str: + """生成Uniform缓冲区结构体""" + code = "// Uniform buffer structures\n" + + for buffer in uniform_buffers: + struct_name = buffer['name'].replace('_buffer', '').title().replace('_', '') + 'Buffer' + code += f"typedef struct {struct_name} {{\n" + + for field in buffer['fields']: + c_type, count, _ = self.get_type_info(field['type']) + if field['type']['kind'] == 'matrix': + rows = field['type']['rowCount'] + cols = field['type']['columnCount'] + code += f" {c_type} {field['name']}[{rows}][{cols}];\n" + elif count == 1: + code += f" {c_type} {field['name']};\n" + else: + code += f" {c_type} {field['name']}[{count}];\n" + + code += f"}} {struct_name};\n" + code += f"// Binding: {buffer['binding']}, Size: {sum(f['size'] for f in buffer['fields'])} bytes\n\n" + + return code + + def generate_helper_functions(self, vertex_fields: List[Dict]) -> str: + """生成辅助函数""" + code = "// Helper functions\n\n" + + # 创建顶点缓冲区函数 + code += "static SDL_GPUBuffer* createVertexBuffer(SDL_GPUDevice* device, \n" + code += " const Vertex* vertices, \n" + code += " Uint32 vertexCount) {\n" + code += " SDL_GPUBufferCreateInfo bufferInfo = {\n" + code += " .usage = SDL_GPU_BUFFERUSAGE_VERTEX,\n" + code += " .size = static_cast(sizeof(Vertex)) * vertexCount\n" + code += " };\n" + code += " \n" + code += " SDL_GPUBuffer* buffer = SDL_CreateGPUBuffer(device, &bufferInfo);\n" + code += " \n" + code += " // Upload vertex data\n" + code += " SDL_GPUTransferBuffer* transfer = SDL_CreateGPUTransferBuffer(device,\n" + code += " SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, bufferInfo.size);\n" + code += " \n" + code += " void* mapped = SDL_MapGPUTransferBuffer(device, transfer, SDL_FALSE);\n" + code += " SDL_memcpy(mapped, vertices, bufferInfo.size);\n" + code += " SDL_UnmapGPUTransferBuffer(device, transfer);\n" + code += " \n" + code += " // Copy to GPU\n" + code += " SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device);\n" + code += " SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd);\n" + code += " \n" + code += " SDL_GPUTransferBufferLocation src = {.transfer_buffer = transfer, .offset = 0};\n" + code += " SDL_GPUBufferRegion dst = {.buffer = buffer, .offset = 0, .size = bufferInfo.size};\n" + code += " \n" + code += " SDL_UploadToGPUBuffer(copy, &src, &dst, SDL_FALSE);\n" + code += " SDL_EndGPUCopyPass(copy);\n" + code += " SDL_SubmitGPUCommandBuffer(cmd);\n" + code += " \n" + code += " SDL_ReleaseGPUTransferBuffer(device, transfer);\n" + code += " return buffer;\n" + code += "}\n\n" + + return code + + def generate_header(self, json_file: str) -> str: + """生成头文件内容""" + with open(json_file, 'r') as f: + json_data = json.load(f) + + # 解析数据 + vertex_fields = self.parse_vertex_input(json_data) + uniform_buffers = self.parse_uniform_buffers(json_data) + + # 生成代码 + code = "#pragma once\n\n" + code += "#include \n" + code += "#include \n\n" + code += "// Auto-generated from: " + json_file + "\n\n" + + code += self.generate_vertex_struct(vertex_fields) + code += self.generate_uniform_structs(uniform_buffers) + code += self.generate_vertex_attributes(vertex_fields) + code += self.generate_helper_functions(vertex_fields) + + return code