#!/usr/bin/env python3 """ Slang Compiler - Code Generator 生成C/C++绑定代码 """ from pathlib import Path from global_vars import global_vars from indent_manager import IndentManager from shader_reflection_type import * class CodeGenerator: """C++绑定代码生成器""" def generate_binding_functions(self, binding_infos: ShaderInfos, output_path: str) -> None: """生成C++绑定函数的入口方法""" self._generate_cpp_bindings(binding_infos, output_path) def _generate_cpp_bindings(self, binding_infos: ShaderInfos, 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, binding_infos) def _write_complete_file(self, writer: IndentManager, binding_infos: ShaderInfos) -> None: """写入完整的文件内容""" self._write_header(writer) with writer.indent(f'namespace {global_vars.source_file_name}_shaders ''{', '}'): self._write_shader_bindings_class(writer, binding_infos) writer.write() def _write_header(self, writer: IndentManager) -> None: """写入文件头部""" headers = [ '#pragma once', '#include ', '#include ', ] for header in headers: writer.write(header) writer.write() def _write_shader_bindings_class(self, writer: IndentManager, binding_infos: ShaderInfos) -> None: """写入ShaderBindings类""" # self._generate_uniform_structs(writer, global_vars.vertex_layout.uniform_buffers) self._write_blob(writer, binding_infos) self._write_public_methods(writer, binding_infos) self._write_get_pipeline_desc_method(writer, binding_infos) def _write_blob(self, writer: IndentManager, binding_infos: ShaderInfos) -> None: """写入public部分的结构体定义""" # 写入二进制内容 for stage, info in binding_infos.stages.items(): entry_point = info.get_entry_name() blob_data = info.blob with writer.block(f'static constexpr std::array {global_vars.source_file_name}_{entry_point}_blob =', '};'): # 每行16个字节,提高可读性 for i in range(0, len(blob_data), 16): chunk = blob_data[i:i + 16] hex_str = ', '.join(f'0x{byte:02x}' for byte in chunk) if i + 16 < len(blob_data): hex_str += ',' writer.write(hex_str) writer.write() writer.write() def _write_public_methods(self, writer: IndentManager, binding_infos: ShaderInfos) -> None: """写入公共方法""" self._write_get_shader_info_method(writer, binding_infos) def _write_get_shader_info_method(self, writer: IndentManager, binding_infos: ShaderInfos) -> None: """写入getShaderInfo方法""" with writer.indent(f'static sg_shader_desc get_{global_vars.source_file_name}_shader_desc()'' {', '}'): writer.write('sg_shader_desc desc = {};') writer.write(f'desc.label = "{global_vars.source_file_name}_shader_desc";') writer.write('// 顶点布局') with writer.indent('{', '}'): for i, vertex_field in enumerate(binding_infos.vertex_layout): writer.write(f'desc.attrs[{i}].hlsl_sem_name = "{vertex_field.semanticName}";') writer.write(f'desc.attrs[{i}].hlsl_sem_index = {vertex_field.semanticIndex or 0};') for stage, info in binding_infos.stages.items(): entry_point = info.get_entry_name() entry_point_name = f'{global_vars.source_file_name}_{entry_point}_blob' stage_name = info.get_stage().name writer.write(f'// {stage_name}') with writer.indent('{','}'): writer.write(f'desc.{stage_name.lower()}_func.bytecode = SG_RANGE({entry_point_name});') writer.write(f'desc.{stage_name.lower()}_func.entry = "{entry_point}";') writer.write('// 资源绑定') self._write_resource_binding(writer, info.parameters) def _write_resource_binding(self, writer: IndentManager, parameters: List[Parameter]) -> None: resource_index = {} target = global_vars.target glsl_block_index = {} for p in parameters: binding_kind = p.get_binding_kind() index = resource_index.get(binding_kind, 0) resource_index[binding_kind] = index + 1 stage_name = p.stage.name.upper() if binding_kind == BindingKind.UNIFORM: t = f'desc.uniform_blocks[{index}]' writer.write(f'{t}.stage = SG_SHADERSTAGE_{stage_name};') writer.write(f'{t}.size = {p.get_byte_size()};') if target == TargetFormat.GLSL: glsl_index = glsl_block_index.get(binding_kind, 0) glsl_block_index[binding_kind] = glsl_index + 1 writer.write(f'{t}.glsl_uniforms[{glsl_index}].type = ;') writer.write(f'{t}.glsl_uniforms[{glsl_index}].array_count = 1;') writer.write(f'{t}.glsl_uniforms[{glsl_index}].glsl_name = "{p.name}";') elif target == TargetFormat.DXBC: writer.write(f'{t}.hlsl_register_b_n = {p.get_register_index()};') def _write_get_pipeline_desc_method(self, writer: IndentManager, binding_infos: ShaderInfos) -> None: """写入getPipelineDesc方法""" function_params = ('\n\t\t\tsg_shader shader, \n' '\t\t\tsg_pixel_format pixel_format, \n' '\t\t\tint32_t sample_count = 1, \n' '\t\t\tsg_primitive_type primitive_type = SG_PRIMITIVETYPE_TRIANGLES, \n' '\t\t\tsg_cull_mode cull_mode = SG_CULLMODE_NONE\n') with writer.indent(f'static sg_pipeline_desc get_{global_vars.source_file_name}_pipeline_desc({function_params}\t)'' {', '}'): writer.write('sg_pipeline_desc desc = {};') writer.write('desc.shader = shader;') writer.write('desc.index_type = SG_INDEXTYPE_UINT32;') writer.write() # 处理顶点缓冲区大小 writer.write(f'desc.layout.buffers[0].stride = {binding_infos.vertex_size};') writer.write('desc.layout.buffers[0].step_func = SG_VERTEXSTEP_PER_VERTEX;') writer.write('desc.layout.buffers[0].step_rate = 1;') writer.write() # 处理顶点输入布局 for i, vertex_field in enumerate(binding_infos.vertex_layout): writer.write(f'// {vertex_field.semanticName} {vertex_field.semanticIndex or ''}') writer.write(f'desc.layout.attrs[{i}].buffer_index = 0;') writer.write(f'desc.layout.attrs[{i}].offset = {vertex_field.binding.offset};') writer.write(f'desc.layout.attrs[{i}].format = {vertex_field.get_sg_format()};') writer.write() writer.write('desc.primitive_type = primitive_type;') writer.write('desc.cull_mode = cull_mode;') writer.write('desc.face_winding = SG_FACEWINDING_CW;') writer.write('desc.depth.write_enabled = false;') writer.write('desc.depth.compare = SG_COMPAREFUNC_NEVER;') writer.write('desc.depth.pixel_format = SG_PIXELFORMAT_NONE;') writer.write('desc.colors[0].blend.enabled = true;') writer.write('desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;') writer.write('desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;') writer.write('desc.colors[0].pixel_format = pixel_format;') writer.write('desc.colors[0].write_mask = SG_COLORMASK_RGBA;') writer.write('desc.sample_count = sample_count;') writer.write(f'desc.label = "{global_vars.source_file_name}_pipeline_desc";') writer.write('return desc;')