diff --git a/build.zig b/build.zig index a0467c7c..bee4a252 100644 --- a/build.zig +++ b/build.zig @@ -42,6 +42,7 @@ pub fn build_exe( "generators/sokolodin.cc", "generators/sokolrust.cc", "generators/sokoljai.cc", + "generators/sokolc3.cc", "generators/sokolzig.cc", "generators/yaml.cc", }; diff --git a/src/shdc/args.cc b/src/shdc/args.cc index 636c7e80..76a98eea 100644 --- a/src/shdc/args.cc +++ b/src/shdc/args.cc @@ -79,6 +79,7 @@ static void print_help_string(getopt_context_t& ctx) { " - sokol_rust Rust module file\n" " - sokol_d D module file\n" " - sokol_jai Jai module file\n" + " - sokol_c3 C3 module file\n" " - bare raw output of SPIRV-Cross compiler, in text or binary format\n" " - bare_yaml like bare, but with reflection file in YAML format\n\n" "Options:\n\n"); @@ -214,7 +215,7 @@ Args Args::parse(int argc, const char** argv) { case OPTION_FORMAT: args.output_format = Format::from_str(ctx.current_opt_arg); if (args.output_format == Format::INVALID) { - fmt::print(stderr, "sokol-shdc: unknown output format {}, must be [sokol|sokol_impl|sokol_zig|sokol_nim|sokol_odin|sokol_rust|sokol_jai|bare|base_yaml]\n", ctx.current_opt_arg); + fmt::print(stderr, "sokol-shdc: unknown output format {}, must be [sokol|sokol_impl|sokol_zig|sokol_nim|sokol_odin|sokol_rust|sokol_jai|sokol_c3|bare|base_yaml]\n", ctx.current_opt_arg); args.valid = false; args.exit_code = 10; return args; diff --git a/src/shdc/generators/generate.cc b/src/shdc/generators/generate.cc index a8a2eaa4..71ed83af 100644 --- a/src/shdc/generators/generate.cc +++ b/src/shdc/generators/generate.cc @@ -11,6 +11,7 @@ #include "sokolzig.h" #include "sokold.h" #include "sokoljai.h" +#include "sokolc3.h" #include "yaml.h" #include @@ -34,6 +35,8 @@ std::unique_ptr make_generator(Format::Enum format) { return std::make_unique(); case Format::SOKOL_JAI: return std::make_unique(); + case Format::SOKOL_C3: + return std::make_unique(); default: return std::make_unique(); } diff --git a/src/shdc/generators/sokolc3.cc b/src/shdc/generators/sokolc3.cc new file mode 100644 index 00000000..ec092ac0 --- /dev/null +++ b/src/shdc/generators/sokolc3.cc @@ -0,0 +1,510 @@ +/* + Generate sokol-c3 module. +*/ +#include "sokolc3.h" +#include "fmt/format.h" +#include "pystring.h" +#include + +namespace shdc::gen { + +using namespace refl; + +void SokolC3Generator::gen_prolog(const GenInput& gen) { + for (const auto& header: gen.inp.headers) { + l("{}\n", header); + } + l("import sokol;\n"); +} + +void SokolC3Generator::gen_epilog(const GenInput& gen) { + // empty +} + +void SokolC3Generator::gen_prerequisites(const GenInput& gen) { + // empty +} + + +void SokolC3Generator::gen_uniform_block_decl(const GenInput &gen, const UniformBlock& ub) { + int cur_offset = 0; + l("struct {} @align({}) @packed\n", struct_name(ub.name), ub.struct_info.align); + l_open("{{\n"); + for (const Type& uniform: ub.struct_info.struct_items) { + int next_offset = uniform.offset; + if (next_offset > cur_offset) { + l("char[{}] _pad_{};\n", next_offset - cur_offset, cur_offset); + cur_offset = next_offset; + } + if (gen.inp.ctype_map.count(uniform.type_as_glsl()) > 0) { + // user-provided type names + if (uniform.array_count == 0) { + l("{} {};\n", gen.inp.ctype_map.at(uniform.type_as_glsl()), uniform.name); + } else { + l("{}[{}] {};\n", gen.inp.ctype_map.at(uniform.type_as_glsl()), uniform.array_count, uniform.name); + } + } else { + // default type names (float) + if (uniform.array_count == 0) { + switch (uniform.type) { + case Type::Float: l("float {};\n", uniform.name); break; + case Type::Float2: l("float[2] {};\n", uniform.name); break; + case Type::Float3: l("float[3] {};\n", uniform.name); break; + case Type::Float4: l("float[4] {};\n", uniform.name); break; + case Type::Int: l("int {};\n", uniform.name); break; + case Type::Int2: l("int[2] {};\n", uniform.name); break; + case Type::Int3: l("int[3] {};\n", uniform.name); break; + case Type::Int4: l("int[4] {};\n", uniform.name); break; + case Type::Mat4x4: l("float[16] {};\n", uniform.name); break; + default: l("INVALID_UNIFORM_TYPE;\n"); break; + } + } else { + switch (uniform.type) { + case Type::Float4: l("float[{}][4] {};\n", uniform.array_count, uniform.name); break; + case Type::Int4: l("int[{}][4] {};\n", uniform.array_count, uniform.name); break; + case Type::Mat4x4: l("float[{}][16] {};\n", uniform.array_count, uniform.name); break; + default: l("INVALID_UNIFORM_TYPE;\n"); break; + } + } + } + cur_offset += uniform.size; + } + // pad to multiple of 16-bytes struct size + const int round16 = roundup(cur_offset, 16); + if (cur_offset < round16) { + l("char[{}] _pad_{};\n", round16 - cur_offset, cur_offset); + } + l_close("}}\n"); +} + +void SokolC3Generator::gen_struct_interior_decl_std430(const GenInput& gen, const Type& struc, int pad_to_size) { + assert(struc.type == Type::Struct); + assert(pad_to_size > 0); + + int cur_offset = 0; + for (const Type& item: struc.struct_items) { + int next_offset = item.offset; + if (next_offset > cur_offset) { + l("_: [{}]u8,\n", next_offset - cur_offset); + cur_offset = next_offset; + } + if (item.type == Type::Struct) { + // recurse into nested struct + if (item.array_count == 0) { + l_open("{}: struct {{\n", item.name); + } else { + l_open("{}: [{}]struct {{\n", item.name, item.array_count); + } + gen_struct_interior_decl_std430(gen, item, item.size); + l_close("}},\n"); + } else if (gen.inp.ctype_map.count(item.type_as_glsl()) > 0) { + // user-provided type names + if (item.array_count == 0) { + l("{}: {},\n", item.name, gen.inp.ctype_map.at(item.type_as_glsl())); + } else { + l("{}: [{}]{},\n", item.name, item.array_count, gen.inp.ctype_map.at(item.type_as_glsl())); + } + } else { + // default typenames + if (item.array_count == 0) { + switch (item.type) { + // NOTE: bool => int is not a bug! + case Type::Bool: l("{}: i32,\n", item.name); break; + case Type::Bool2: l("{}: [2]i32,\n", item.name); break; + case Type::Bool3: l("{}: [3]i32,\n", item.name); break; + case Type::Bool4: l("{}: [4]i32,\n", item.name); break; + case Type::Int: l("{}: i32,\n", item.name); break; + case Type::Int2: l("{}: [2]i32,\n", item.name); break; + case Type::Int3: l("{}: [3]i32,\n", item.name); break; + case Type::Int4: l("{}: [4]i32,\n", item.name); break; + case Type::UInt: l("{}: u32,\n", item.name); break; + case Type::UInt2: l("{}: [2]u32,\n", item.name); break; + case Type::UInt3: l("{}: [3]u32,\n", item.name); break; + case Type::UInt4: l("{}: [4]u32,\n", item.name); break; + case Type::Float: l("{}: f32,\n", item.name); break; + case Type::Float2: l("{}: [2]f32,\n", item.name); break; + case Type::Float3: l("{}: [3]f32,\n", item.name); break; + case Type::Float4: l("{}: [4]f32,\n", item.name); break; + case Type::Mat2x1: l("{}: [2]f32,\n", item.name); break; + case Type::Mat2x2: l("{}: [4]f32,\n", item.name); break; + case Type::Mat2x3: l("{}: [6]f32,\n", item.name); break; + case Type::Mat2x4: l("{}: [8]f32,\n", item.name); break; + case Type::Mat3x1: l("{}: [3]f32,\n", item.name); break; + case Type::Mat3x2: l("{}: [6]f32,\n", item.name); break; + case Type::Mat3x3: l("{}: [9]f32,\n", item.name); break; + case Type::Mat3x4: l("{}: [12]f32,\n", item.name); break; + case Type::Mat4x1: l("{}: [4]f32,\n", item.name); break; + case Type::Mat4x2: l("{}: [8]f32,\n", item.name); break; + case Type::Mat4x3: l("{}: [12]f32,\n", item.name); break; + case Type::Mat4x4: l("{}: [16]f32,\n", item.name); break; + default: l("INVALID_TYPE\n"); break; + } + } else { + switch (item.type) { + // NOTE: bool => int is not a bug! + case Type::Bool: l("{}: [{}]i32,\n", item.name, item.array_count); break; + case Type::Bool2: l("{}: [{}][2]i32,\n", item.name, item.array_count); break; + case Type::Bool3: l("{}: [{}][3]i32,\n", item.name, item.array_count); break; + case Type::Bool4: l("{}: [{}][4]i32,\n", item.name, item.array_count); break; + case Type::Int: l("{}: [{}]i32,\n", item.name, item.array_count); break; + case Type::Int2: l("{}: [{}][2]i32,\n", item.name, item.array_count); break; + case Type::Int3: l("{}: [{}][3]i32,\n", item.name, item.array_count); break; + case Type::Int4: l("{}: [{}][4]i32,\n", item.name, item.array_count); break; + case Type::UInt: l("{}: [{}]u32,\n", item.name, item.array_count); break; + case Type::UInt2: l("{}: [{}][2]u32,\n", item.name, item.array_count); break; + case Type::UInt3: l("{}: [{}][3]u32,\n", item.name, item.array_count); break; + case Type::UInt4: l("{}: [{}][4]u32,\n", item.name, item.array_count); break; + case Type::Float: l("{}: [{}]f32,\n", item.name, item.array_count); break; + case Type::Float2: l("{}: [{}][2]f32,\n", item.name, item.array_count); break; + case Type::Float3: l("{}: [{}][3]f32,\n", item.name, item.array_count); break; + case Type::Float4: l("{}: [{}][4]f32,\n", item.name, item.array_count); break; + case Type::Mat2x1: l("{}: [{}][2]f32,\n", item.name, item.array_count); break; + case Type::Mat2x2: l("{}: [{}][4]f32,\n", item.name, item.array_count); break; + case Type::Mat2x3: l("{}: [{}][6]f32,\n", item.name, item.array_count); break; + case Type::Mat2x4: l("{}: [{}][8]f32,\n", item.name, item.array_count); break; + case Type::Mat3x1: l("{}: [{}][3]f32,\n", item.name, item.array_count); break; + case Type::Mat3x2: l("{}: [{}][6]f32,\n", item.name, item.array_count); break; + case Type::Mat3x3: l("{}: [{}][9]f32,\n", item.name, item.array_count); break; + case Type::Mat3x4: l("{}: [{}][12]f32,\n", item.name, item.array_count); break; + case Type::Mat4x1: l("{}: [{}][4]f32,\n", item.name, item.array_count); break; + case Type::Mat4x2: l("{}: [{}][8]f32,\n", item.name, item.array_count); break; + case Type::Mat4x3: l("{}: [{}][12]f32,\n", item.name, item.array_count); break; + case Type::Mat4x4: l("{}: [{}][16]f32,\n", item.name, item.array_count); break; + default: l("INVALID_TYPE\n"); break; + } + } + } + cur_offset += item.size; + } + if (cur_offset < pad_to_size) { + l("_: [{}]u8,\n", pad_to_size - cur_offset); + } +} + +void SokolC3Generator::gen_storage_buffer_decl(const GenInput& gen, const StorageBuffer& sbuf) { + const auto& item = sbuf.struct_info.struct_items[0]; + l_open("{} :: struct #align({}) {{\n", struct_name(item.struct_typename), sbuf.struct_info.align); + l_open("using _: struct #packed {{\n"); + gen_struct_interior_decl_std430(gen, item, sbuf.struct_info.size); + l_close("}},\n"); + l_close("}}\n"); +} + +void SokolC3Generator::gen_shader_desc_func(const GenInput& gen, const ProgramReflection& prog) { + l("fn gfx::ShaderDesc {}_shader_desc(gfx::Backend backend)\n", prog.name); + l_open("{{\n"); + l("gfx::ShaderDesc desc;\n"); + l("desc.label = \"{}_shader\";\n", prog.name); + l("switch (backend)\n"); + l_open("{{\n"); + for (int i = 0; i < Slang::Num; i++) { + Slang::Enum slang = Slang::from_index(i); + if (gen.args.slang & Slang::bit(slang)) { + l_open("case {}:\n", backend(slang)); + for (int stage_index = 0; stage_index < ShaderStage::Num; stage_index++) { + const ShaderStageArrayInfo& info = shader_stage_array_info(gen, prog, ShaderStage::from_index(stage_index), slang); + const StageReflection& refl = prog.stages[stage_index]; + const std::string dsn = fmt::format("desc.{}", info.stage == ShaderStage::Vertex ? "vertex_func" : "fragment_func"); + if (info.has_bytecode) { + l("{}.bytecode.ptr = &{};\n", dsn, info.bytecode_array_name); + l("{}.bytecode.size = {};\n", dsn, info.bytecode_array_size); + } else { + l("{}.source = (ZString)&{};\n", dsn, info.source_array_name); + const char* d3d11_tgt = nullptr; + if (slang == Slang::HLSL4) { + d3d11_tgt = (0 == stage_index) ? "vs_4_0" : "ps_4_0"; + } else if (slang == Slang::HLSL5) { + d3d11_tgt = (0 == stage_index) ? "vs_5_0" : "ps_5_0"; + } + if (d3d11_tgt) { + l("{}.d3d11_target = \"{}\";\n", dsn, d3d11_tgt); + } + } + l("{}.entry = \"{}\";\n", dsn, refl.entry_point_by_slang(slang)); + } + for (int attr_index = 0; attr_index < StageAttr::Num; attr_index++) { + const StageAttr& attr = prog.vs().inputs[attr_index]; + if (attr.slot >= 0) { + if (Slang::is_glsl(slang)) { + l("desc.attrs[{}].glsl_name = \"{}\";\n", attr_index, attr.name); + } else if (Slang::is_hlsl(slang)) { + l("desc.attrs[{}].hlsl_sem_name = \"{}\";\n", attr_index, attr.sem_name); + l("desc.attrs[{}].hlsl_sem_index = {};\n", attr_index, attr.sem_index); + } + } + } + for (int ub_index = 0; ub_index < Bindings::MaxUniformBlocks; ub_index++) { + const UniformBlock* ub = prog.bindings.find_uniform_block_by_sokol_slot(ub_index); + if (ub) { + const std::string ubn = fmt::format("desc.uniform_blocks[{}]", ub_index); + l("{}.stage = {};\n", ubn, shader_stage(ub->stage)); + l("{}.layout = uniform_layout::STD140;\n", ubn); + l("{}.size = {};\n", ubn, roundup(ub->struct_info.size, 16)); + if (Slang::is_hlsl(slang)) { + l("{}.hlsl_register_b_n = {};\n", ubn, ub->hlsl_register_b_n); + } else if (Slang::is_msl(slang)) { + l("{}.msl_buffer_n = {};\n", ubn, ub->msl_buffer_n); + } else if (Slang::is_wgsl(slang)) { + l("{}.wgsl_group0_binding_n = {};\n", ubn, ub->wgsl_group0_binding_n); + } else if (Slang::is_glsl(slang) && (ub->struct_info.struct_items.size() > 0)) { + if (ub->flattened) { + // NOT A BUG (to take the type from the first struct item, but the size from the toplevel ub) + l("{}.glsl_uniforms[0].type = {};\n", ubn, flattened_uniform_type(ub->struct_info.struct_items[0].type)); + l("{}.glsl_uniforms[0].array_count = {};\n", ubn, roundup(ub->struct_info.size, 16) / 16); + l("{}.glsl_uniforms[0].glsl_name = \"{}\";\n", ubn, ub->name); + } else { + for (int u_index = 0; u_index < (int)ub->struct_info.struct_items.size(); u_index++) { + const Type& u = ub->struct_info.struct_items[u_index]; + const std::string un = fmt::format("{}.glsl_uniforms[{}]", ubn, u_index); + l("{}.type = {};\n", un, uniform_type(u.type)); + l("{}.array_count = {};\n", un, u.array_count); + l("{}.glsl_name = \"{}.{};\"\n", un, ub->inst_name, u.name); + } + } + } + } + } + for (int sbuf_index = 0; sbuf_index < Bindings::MaxStorageBuffers; sbuf_index++) { + const StorageBuffer* sbuf = prog.bindings.find_storage_buffer_by_sokol_slot(sbuf_index); + if (sbuf) { + const std::string& sbn = fmt::format("desc.storage_buffers[{}]", sbuf_index); + l("{}.stage = {};\n", sbn, shader_stage(sbuf->stage)); + l("{}.readonly = {};\n", sbn, sbuf->readonly); + if (Slang::is_hlsl(slang)) { + l("{}.hlsl_register_t_n = {};\n", sbn, sbuf->hlsl_register_t_n); + } else if (Slang::is_msl(slang)) { + l("{}.msl_buffer_n = {};\n", sbn, sbuf->msl_buffer_n); + } else if (Slang::is_wgsl(slang)) { + l("{}.wgsl_group1_binding_n = {};\n", sbn, sbuf->wgsl_group1_binding_n); + } else if (Slang::is_glsl(slang)) { + l("{}.glsl_binding_n = {};\n", sbn, sbuf->glsl_binding_n); + } + } + } + for (int img_index = 0; img_index < Bindings::MaxImages; img_index++) { + const Image* img = prog.bindings.find_image_by_sokol_slot(img_index); + if (img) { + const std::string in = fmt::format("desc.images[{}]", img_index); + l("{}.stage = {};\n", in, shader_stage(img->stage)); + l("{}.multisampled = {};\n", in, img->multisampled ? "true" : "false"); + l("{}.image_type = {};\n", in, image_type(img->type)); + l("{}.sample_type = {};\n", in, image_sample_type(img->sample_type)); + if (Slang::is_hlsl(slang)) { + l("{}.hlsl_register_t_n = {};\n", in, img->hlsl_register_t_n); + } else if (Slang::is_msl(slang)) { + l("{}.msl_texture_n = {};\n", in, img->msl_texture_n); + } else if (Slang::is_wgsl(slang)) { + l("{}.wgsl_group1_binding_n = {};\n", in, img->wgsl_group1_binding_n); + } + } + } + for (int smp_index = 0; smp_index < Bindings::MaxSamplers; smp_index++) { + const Sampler* smp = prog.bindings.find_sampler_by_sokol_slot(smp_index); + if (smp) { + const std::string sn = fmt::format("desc.samplers[{}]", smp_index); + l("{}.stage = {};\n", sn, shader_stage(smp->stage)); + l("{}.sampler_type = {};\n", sn, sampler_type(smp->type)); + if (Slang::is_hlsl(slang)) { + l("{}.hlsl_register_s_n = {};\n", sn, smp->hlsl_register_s_n); + } else if (Slang::is_msl(slang)) { + l("{}.msl_sampler_n = {};\n", sn, smp->msl_sampler_n); + } else if (Slang::is_wgsl(slang)) { + l("{}.wgsl_group1_binding_n = {};\n", sn, smp->wgsl_group1_binding_n); + } + } + } + for (int img_smp_index = 0; img_smp_index < Bindings::MaxImageSamplers; img_smp_index++) { + const ImageSampler* img_smp = prog.bindings.find_image_sampler_by_sokol_slot(img_smp_index); + if (img_smp) { + const std::string isn = fmt::format("desc.image_sampler_pairs[{}]", img_smp_index); + l("{}.stage = {};\n", isn, shader_stage(img_smp->stage)); + l("{}.image_slot = {};\n", isn, prog.bindings.find_image_by_name(img_smp->image_name)->sokol_slot); + l("{}.sampler_slot = {};\n", isn, prog.bindings.find_sampler_by_name(img_smp->sampler_name)->sokol_slot); + if (Slang::is_glsl(slang)) { + l("{}.glsl_name = \"{}\";\n", isn, img_smp->name); + } + } + } + l_close(); // current switch branch + } + } + l_close("}}\n"); // close switch statement + l("return desc;\n"); + l_close("}}\n"); // close function +} + +void SokolC3Generator::gen_shader_array_start(const GenInput& gen, const std::string& array_name, size_t num_bytes, Slang::Enum slang) { + l("const char[{}] {} @private = {{\n", num_bytes, array_name); +} + +void SokolC3Generator::gen_shader_array_end(const GenInput& gen) { + l("\n}};\n"); +} + +std::string SokolC3Generator::lang_name() { + return "C3"; +} + +std::string SokolC3Generator::comment_block_start() { + return "/*"; +} + +std::string SokolC3Generator::comment_block_end() { + return "*/"; +} + +std::string SokolC3Generator::comment_block_line_prefix() { + return ""; +} + +std::string SokolC3Generator::shader_bytecode_array_name(const std::string& snippet_name, Slang::Enum slang) { + return pystring::upper(fmt::format("{}_bytecode_{}", snippet_name, Slang::to_str(slang))); +} + +std::string SokolC3Generator::shader_source_array_name(const std::string& snippet_name, Slang::Enum slang) { + return pystring::upper(fmt::format("{}_source_{}", snippet_name, Slang::to_str(slang))); +} + +std::string SokolC3Generator::get_shader_desc_help(const std::string& prog_name) { + return fmt::format("{}_shader_desc(sg.query_backend())\n", prog_name); +} + +std::string SokolC3Generator::shader_stage(const ShaderStage::Enum e) { + switch (e) { + case ShaderStage::Vertex: return "shader_stage::VERTEX"; + case ShaderStage::Fragment: return "shader_stage::FRAGMENT"; + default: return "INVALID"; + } +} + +std::string SokolC3Generator::uniform_type(Type::Enum e) { + switch (e) { + case Type::Float: return "uniform_type::FLOAT"; + case Type::Float2: return "uniform_type::FLOAT2"; + case Type::Float3: return "uniform_type::FLOAT3"; + case Type::Float4: return "uniform_type::FLOAT4"; + case Type::Int: return "uniform_type::INT"; + case Type::Int2: return "uniform_type::INT2"; + case Type::Int3: return "uniform_type::INT3"; + case Type::Int4: return "uniform_type::INT4"; + case Type::Mat4x4: return "uniform_type::MAT4"; + default: return "INVALID"; + } +} + +std::string SokolC3Generator::flattened_uniform_type(Type::Enum e) { + switch (e) { + case Type::Float: + case Type::Float2: + case Type::Float3: + case Type::Float4: + case Type::Mat4x4: + return "uniform_type::FLOAT4"; + case Type::Int: + case Type::Int2: + case Type::Int3: + case Type::Int4: + return "uniform_type::INT4"; + default: + return "INVALID"; + } +} + +std::string SokolC3Generator::image_type(ImageType::Enum e) { + switch (e) { + case ImageType::_2D: return "image_type::TYPE_2D"; + case ImageType::CUBE: return "image_type::CUBE"; + case ImageType::_3D: return "image_type::TYPE_3D"; + case ImageType::ARRAY: return "image_type::ARRAY"; + default: return "INVALID"; + } +} + +std::string SokolC3Generator::image_sample_type(ImageSampleType::Enum e) { + switch (e) { + case ImageSampleType::FLOAT: return "image_sample_type::FLOAT"; + case ImageSampleType::DEPTH: return "image_sample_type::DEPTH"; + case ImageSampleType::SINT: return "image_sample_type::SINT"; + case ImageSampleType::UINT: return "image_sample_type::UINT"; + case ImageSampleType::UNFILTERABLE_FLOAT: return "image_sample_type::UNFILTERABLE_FLOAT"; + default: return "INVALID"; + } +} + +std::string SokolC3Generator::sampler_type(SamplerType::Enum e) { + switch (e) { + case SamplerType::FILTERING: return "sampler_type::FILTERING"; + case SamplerType::COMPARISON: return "sampler_type::COMPARISON"; + case SamplerType::NONFILTERING: return "sampler_type::NONFILTERING"; + default: return "INVALID"; + } +} + +std::string SokolC3Generator::backend(Slang::Enum e) { + switch (e) { + case Slang::GLSL410: + case Slang::GLSL430: + return "backend::GLCORE"; + case Slang::GLSL300ES: + return "backend::GLES3"; + case Slang::HLSL4: + case Slang::HLSL5: + return "backend::D3D11"; + case Slang::METAL_MACOS: + return "backend::METAL_MACOS"; + case Slang::METAL_IOS: + return "backend::METAL_IOS"; + case Slang::METAL_SIM: + return "backend::METAL_SIMULATOR"; + case Slang::WGSL: + return "backend::WGPU"; + default: + return "INVALID"; + } +} + +std::string SokolC3Generator::struct_name(const std::string& name) { + return to_pascal_case(name); +} + +std::string SokolC3Generator::vertex_attr_name(const std::string& prog_name, const StageAttr& attr) { + return pystring::upper(fmt::format("ATTR_{}_{}", prog_name, attr.name)); +} + +std::string SokolC3Generator::image_bind_slot_name(const Image& img) { + return pystring::upper(fmt::format("IMG_{}", img.name)); +} + +std::string SokolC3Generator::sampler_bind_slot_name(const Sampler& smp) { + return pystring::upper(fmt::format("SMP_{}", smp.name)); +} + +std::string SokolC3Generator::uniform_block_bind_slot_name(const UniformBlock& ub) { + return pystring::upper(fmt::format("UB_{}", ub.name)); +} + +std::string SokolC3Generator::storage_buffer_bind_slot_name(const StorageBuffer& sbuf) { + return pystring::upper(fmt::format("SBUF_{}", sbuf.name)); +} + +std::string SokolC3Generator::vertex_attr_definition(const std::string& prog_name, const StageAttr& attr) { + return fmt::format("const int {} = {};", vertex_attr_name(prog_name, attr), attr.slot); +} + +std::string SokolC3Generator::image_bind_slot_definition(const Image& img) { + return fmt::format("const int {} = {};", image_bind_slot_name(img), img.sokol_slot); +} + +std::string SokolC3Generator::sampler_bind_slot_definition(const Sampler& smp) { + return fmt::format("const int {} = {};", sampler_bind_slot_name(smp), smp.sokol_slot); +} + +std::string SokolC3Generator::uniform_block_bind_slot_definition(const UniformBlock& ub) { + return fmt::format("const int {} = {};", uniform_block_bind_slot_name(ub), ub.sokol_slot); +} + +std::string SokolC3Generator::storage_buffer_bind_slot_definition(const StorageBuffer& sbuf) { + return fmt::format("const int {} = {};", storage_buffer_bind_slot_name(sbuf), sbuf.sokol_slot); +} + +} // namespace diff --git a/src/shdc/generators/sokolc3.h b/src/shdc/generators/sokolc3.h new file mode 100644 index 00000000..d71b3064 --- /dev/null +++ b/src/shdc/generators/sokolc3.h @@ -0,0 +1,45 @@ +#pragma once +#include "generator.h" + +namespace shdc::gen { + +class SokolC3Generator: public Generator { +protected: + virtual void gen_prolog(const GenInput& gen); + virtual void gen_epilog(const GenInput& gen); + virtual void gen_prerequisites(const GenInput& gen); + virtual void gen_uniform_block_decl(const GenInput& gen, const refl::UniformBlock& ub); + virtual void gen_storage_buffer_decl(const GenInput& gen, const refl::StorageBuffer& sbuf); + virtual void gen_shader_array_start(const GenInput& gen, const std::string& array_name, size_t num_bytes, Slang::Enum slang); + virtual void gen_shader_array_end(const GenInput& gen); + virtual void gen_shader_desc_func(const GenInput& gen, const refl::ProgramReflection& prog); + virtual std::string lang_name(); + virtual std::string comment_block_start(); + virtual std::string comment_block_line_prefix(); + virtual std::string comment_block_end(); + virtual std::string shader_bytecode_array_name(const std::string& snippet_name, Slang::Enum slang); + virtual std::string shader_source_array_name(const std::string& snippet_name, Slang::Enum slang); + virtual std::string get_shader_desc_help(const std::string& prog_name); + virtual std::string shader_stage(refl::ShaderStage::Enum e); + virtual std::string uniform_type(refl::Type::Enum e); + virtual std::string flattened_uniform_type(refl::Type::Enum e); + virtual std::string image_type(refl::ImageType::Enum e); + virtual std::string image_sample_type(refl::ImageSampleType::Enum e); + virtual std::string sampler_type(refl::SamplerType::Enum e); + virtual std::string backend(Slang::Enum e); + virtual std::string struct_name(const std::string& name); + virtual std::string vertex_attr_name(const std::string& prog_name, const refl::StageAttr& attr); + virtual std::string image_bind_slot_name(const refl::Image& img); + virtual std::string sampler_bind_slot_name(const refl::Sampler& smp); + virtual std::string uniform_block_bind_slot_name(const refl::UniformBlock& ub); + virtual std::string storage_buffer_bind_slot_name(const refl::StorageBuffer& sbuf); + virtual std::string vertex_attr_definition(const std::string& prog_name, const refl::StageAttr& attr); + virtual std::string image_bind_slot_definition(const refl::Image& img); + virtual std::string sampler_bind_slot_definition(const refl::Sampler& smp); + virtual std::string uniform_block_bind_slot_definition(const refl::UniformBlock& ub); + virtual std::string storage_buffer_bind_slot_definition(const refl::StorageBuffer& sbuf); +private: + virtual void gen_struct_interior_decl_std430(const GenInput& gen, const refl::Type& struc, int pad_to_size); +}; + +} // namespace diff --git a/src/shdc/types/format.h b/src/shdc/types/format.h index c8671c06..872d3677 100644 --- a/src/shdc/types/format.h +++ b/src/shdc/types/format.h @@ -14,6 +14,7 @@ struct Format { SOKOL_RUST, SOKOL_D, SOKOL_JAI, + SOKOL_C3, BARE, BARE_YAML, NUM, @@ -34,6 +35,7 @@ inline const char* Format::to_str(Enum f) { case SOKOL_RUST: return "sokol_rust"; case SOKOL_D: return "sokol_d"; case SOKOL_JAI: return "sokol_jai"; + case SOKOL_C3: return "sokol_c3"; case BARE: return "bare"; case BARE_YAML: return "bare_yaml"; default: return ""; @@ -57,6 +59,8 @@ inline Format::Enum Format::from_str(const std::string& str) { return SOKOL_D; } else if (str == "sokol_jai") { return SOKOL_JAI; + } else if (str == "sokol_c3") { + return SOKOL_C3; } else if (str == "bare") { return BARE; } else if (str == "bare_yaml") {