diff options
Diffstat (limited to 'sc')
-rw-r--r-- | sc/Makefile | 51 | ||||
m--------- | sc/glslang | 0 | ||||
-rw-r--r-- | sc/includer.cpp | 43 | ||||
-rw-r--r-- | sc/includer.hpp | 23 | ||||
-rw-r--r-- | sc/sc.cpp | 385 | ||||
-rw-r--r-- | sc/sh_enums.h | 41 |
6 files changed, 543 insertions, 0 deletions
diff --git a/sc/Makefile b/sc/Makefile new file mode 100644 index 0000000..ce8a24d --- /dev/null +++ b/sc/Makefile @@ -0,0 +1,51 @@ +.POSIX: +target = sc +includes = -I../cfg -I../qstd -Iglslang +cflags = $(includes) -Wall -Wextra -pedantic -std=c++20 $(DEBUG_COMPILE_FLAG) +lflags = \ + $(DEBUG_LINK_FLAG) \ + -Lglslang/build/glslang \ + -Lglslang/build/SPIRV \ + -Lglslang/build/glslang/OSDependent/Unix \ + -Lglslang/build/External/spirv-tools/source \ + -Lglslang/build/External/spirv-tools/source/link \ + -Lglslang/build/External/spirv-tools/source/opt \ + -L../qstd \ + -L../cfg \ + -lcfg \ + -lqstd \ + -lglslang \ + -lglslang-default-resource-limits \ + -lSPIRV \ + -lMachineIndependent \ + -lSPVRemapper \ + -lOSDependent \ + -lGenericCodeGen \ + -lSPIRV-Tools-link \ + -lSPIRV-Tools-opt \ + -lSPIRV-Tools + +objects = sc.o includer.o + +.PHONY: all clean + +all: $(target) + +sc.o: sc.cpp includer.hpp + $(CXX) -c $(cflags) sc.cpp -o sc.o + +includer.o: includer.cpp includer.hpp + $(CXX) -c $(cflags) includer.cpp -o includer.o + +$(target): $(objects) ../qstd/libqstd.a ../cfg/libcfg.a + $(CXX) $(objects) $(lflags) -o $(target) + +../qstd/libqstd.a: + $(MAKE) -C ../qstd + +../cfg/libcfg.a: + $(MAKE) -C ../cfg + +clean: + rm -f $(target) + rm -f $(objects) diff --git a/sc/glslang b/sc/glslang new file mode 160000 +Subproject 1062752a891c95b2bfeed9e356562d88f9df84a diff --git a/sc/includer.cpp b/sc/includer.cpp new file mode 100644 index 0000000..69d9b6a --- /dev/null +++ b/sc/includer.cpp @@ -0,0 +1,43 @@ +#include "includer.hpp" + +#include <stdio.h> +#include <filesystem> + +extern "C" { +#include "plat.h" +} + +using IncludeResult = glslang::TShader::Includer::IncludeResult; + +IncludeResult* Includer::includeSystem( + const char* headerName, + const char* includerName, + size_t inclusionDepth +) { + return 0; +} + +extern bool rf(const char* n, char*& buf, size_t& size); + +IncludeResult* Includer::includeLocal( + const char* header_name, + const char* includer_name, + size_t inclusionDepth +) { + char* src; + size_t src_size; + std::filesystem::path inc(includer_name); + inc.remove_filename(); + inc /= header_name; + auto hn = std::filesystem::absolute(inc).string(); + if (!rf(hn.c_str(), src, src_size)) { + print_err("Failed to include %s\n", header_name); + print_err("\tTried %s\n", hn.c_str()); + return 0; + } + return new IncludeResult(hn, src, src_size, 0); +} + +void Includer::releaseInclude(IncludeResult* i) { + delete i; +} diff --git a/sc/includer.hpp b/sc/includer.hpp new file mode 100644 index 0000000..17a40fe --- /dev/null +++ b/sc/includer.hpp @@ -0,0 +1,23 @@ +#ifndef includer_hpp +#define includer_hpp + +#define max_includes 128 + +#include "glslang/glslang/Public/ShaderLang.h" + +class Includer : public glslang::TShader::Includer { +public: + virtual IncludeResult* includeSystem( + const char* header_name, + const char* includer_name, + size_t inclusion_depth + ) override; + virtual IncludeResult* includeLocal( + const char* header_name, + const char* includer_name, + size_t inclusion_depth + ) override; + virtual void releaseInclude(IncludeResult* i) override; +}; + +#endif diff --git a/sc/sc.cpp b/sc/sc.cpp new file mode 100644 index 0000000..ae4ae02 --- /dev/null +++ b/sc/sc.cpp @@ -0,0 +1,385 @@ +#include "glslang/Public/ShaderLang.h" +#include "glslang/Public/ResourceLimits.h" +#include "SPIRV/GlslangToSpv.h" +#include "includer.hpp" + +extern "C" { +#include "cfgparse.h" +#include "plat.h" +#include "sh_enums.h" +} + +#include <sstream> +#include <tuple> +#include <stdint.h> + +const char* glsl_version_s = "#version 440 core"; +const int glsl_version = 440; +const char* builtin_src = ""; +static const auto client_version = glslang::EShTargetVulkan_1_0; +static const auto target_version = glslang::EShTargetSpv_1_0; +const int desc_parse_memory = 4096; + +std::string get_desc(const char* src) { + const char* srcs[] = { + glsl_version_s, "\n", + "#define DESC", "\n", + builtin_src, + src + }; + glslang::TShader shader(EShLangVertex); + Includer inc; + std::string prepr; + shader.setStrings(srcs, sizeof srcs / sizeof *srcs); + shader.setEnvClient(glslang::EShClientVulkan, client_version); + shader.setEnvTarget(glslang::EShTargetSpv, target_version); + if (!shader.preprocess( + GetDefaultResources(), + glsl_version, + ENoProfile, + false, + false, + (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules), + &prepr, + inc + )) { + print_err("Preprocessing failed.\n"); + print_err("%s\n", shader.getInfoLog()); + exit(1); + } + return prepr; +} + +bool rf(const char* n, char*& buf, size_t& size) { + FILE* f = fopen(n, "r"); + if (!f) return false; + fseek(f, 0, SEEK_END); + size = ftell(f); + rewind(f); + buf = new char[size + 1]; + buf[size] = 0; + if (size != fread(buf, 1, size, f)) { + delete[] buf; + return false; + } + return true; +} + +cfg_Object* parse_desc(void*& mem, const std::string& s) { + Arena a; + mem = malloc(desc_parse_memory); + if (!mem) { + print_err("Out of memory.\n"); + pbreak(30); + } + init_arena(&a, mem, desc_parse_memory); + return cfg_parse(s.c_str(), &a); +} + +static const char* program_type_strings[] = { +#define x(n) #n, + sprogram_type_xmacro() +#undef x +}; + +static const char* type_strings[] = { +#define x(n) #n, + svariable_type_xmacro() +#undef x +}; + +int get_program_type(cfg_Object* desc) { + int i; + const char* s = find_string_default(desc, "type", 0); + if (!s) { + print_err("No program type\n", s); + pbreak(35); + } + for (i = 0; i < sprogram_type_count; i++) { + if (!strcmp(s, program_type_strings[i])) + return i; + } + print_err("Invalid program type %s\n", s); + pbreak(36); + return sprogram_type_graphics; +} + +int type_from_string(const char* s) { + int i; + for (i = 0; i < svariable_type_count; i++) + if (!strcmp(type_strings[i], s)) + return i; + print_err("Invalid variable type %s\n", s); + pbreak(302); + return 0; +} + +struct Desc { + enum { + VERTEX, + FRAGMENT, + stage_count + }; + struct Variable { + int type; + std::string name; + std::string tname; + }; + int type; + std::vector<Variable> attrs; + std::vector<Variable> trgts; + std::string entrypoints[stage_count]; + void read_var(Variable& d, cfg_Object* desc) { + const char* stype = find_string_default(desc, "type", 0); + if (!stype) { + print_err("%s needs to have a type\n", desc->name); + pbreak(303); + } + const char* sname = find_string_default(desc, "name", 0); + if (!sname) { + print_err("%s needs to have a name\n", desc->name); + pbreak(304); + } + d.type = type_from_string(stype); + d.tname = std::string(stype); + d.name = std::string(sname); + if (d.name.size() > 28) { + print_err("variable name %s is too long (max 28 chars).\n", sname); + pbreak(305); + } + } + void build(cfg_Object* desc) { + type = get_program_type(desc); + if (type != sprogram_type_graphics) { + assert(0); /* todo */ + return; + } + const char* sv = find_string_default(desc, "vertex", 0); + if (!sv) { + print_err("Graphics programs must define a vertex shader entry point.\n"); + pbreak(301); + } + entrypoints[VERTEX] = sv; + const char* sf = find_string_default(desc, "fragment", 0); + if (!sf) { + print_err("Graphics programs must define a fragment shader entry point.\n"); + pbreak(302); + } + entrypoints[FRAGMENT] = sf; + desc = desc->next; + while (desc) { + Variable v; + if (!strcmp(desc->name, "attribute")) { + read_var(v, desc); + attrs.push_back(v); + } else if (!strcmp(desc->name, "target")) { + read_var(v, desc); + trgts.push_back(v); + } + desc = desc->next; + } + } + + std::string build_vs() const { + std::stringstream ss; + size_t i, l = attrs.size(); + for (i = 0; i < l; i++) { + auto& attr = attrs[i]; + ss << "layout (location = 0) in "; + ss << attr.tname << " "; + ss << attr.name << ";\n"; + } + return ss.str(); + } + + std::string build_fs() const { + std::stringstream ss; + size_t i, l = trgts.size(); + for (i = 0; i < l; i++) { + auto& attr = trgts[i]; + ss << "layout (location = 0) out "; + ss << attr.tname << " "; + ss << attr.name << ";\n"; + } + return ss.str(); + } +}; + +std::vector<uint32_t> compile_shader( + const Desc& d, + const char* presrc, + const char* src, + const char* define, + int stage, + EShLanguage lang +) { + std::string vars = d.build_vs(); + const char* srcs[] = { + glsl_version_s, "\n", + "#define ", define, "\n", + builtin_src, + presrc, + src + }; + glslang::TShader shader(lang); + glslang::TProgram program; + glslang::TIntermediate* ir; + glslang::SpvOptions options; + Includer inc; + std::string prepr; + std::vector<uint32_t> spv; + EShMessages msg = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules); + shader.setStrings(srcs, sizeof srcs / sizeof *srcs); + shader.setEnvClient(glslang::EShClientVulkan, client_version); + shader.setEnvTarget(glslang::EShTargetSpv, target_version); + shader.setEntryPoint(d.entrypoints[stage].c_str()); + if (!shader.preprocess( + GetDefaultResources(), + glsl_version, + ENoProfile, + false, + false, + msg, + &prepr, + inc + )) { + print_err("%s\n", shader.getInfoLog()); + exit(400); + } + if (!shader.parse( + GetDefaultResources(), + glsl_version, + ENoProfile, + false, + false, + msg, + inc + )) { + print_err("%s\n", shader.getInfoLog()); + exit(401); + } + program.addShader(&shader); + if (!program.link(msg)) { + print_err("%s\n", program.getInfoLog()); + exit(402); + } + ir = program.getIntermediate(lang); + options.validate = true; + glslang::GlslangToSpv(*ir, spv, &options); + return spv; +} + +void configure( + const Desc& d, + int stage, + const char*& dfn, + EShLanguage& l, + std::string& ps +) { + switch (stage) { + case Desc::VERTEX: + dfn = "VERTEX_SHADER"; + l = EShLangVertex; + ps += d.build_vs(); + break; + case Desc::FRAGMENT: + dfn = "FRAGMENT_SHADER"; + l = EShLangFragment; + ps += d.build_fs(); + break; + } +} + +void compile_shaders( + std::vector<uint32_t>* spv, + const char* src, + const Desc& d +) { + const char* define; + EShLanguage lang; + int i; + for (i = 0; i < Desc::stage_count; i++) { + if (!d.entrypoints[i].empty()) { + std::string ps; + configure(d, i, define, lang, ps); + spv[i] = compile_shader( + d, + ps.c_str(), + src, + define, + i, + lang + ); + } + } +} + +void write_csh( + const char* fname, + const Desc& d, + const std::vector<uint32_t>* stages +) { + int hsize = 8, i, coff; + FILE* f = fopen(fname, "wb"); + if (!f) { + print_err("Failed to open %s\n", fname); + pbreak(500); + } + fwrite("CSH2", 4, 1, f); + fwrite(&d.type, 4, 1, f); + for (const auto& a : d.attrs) { + char buf[28]; + memset(buf, 0, sizeof buf); + strcpy(buf, a.name.c_str()); + fwrite(buf, 1, sizeof buf, f); + fwrite(&a.type, 4, 1, f); + hsize += 32; + } + for (const auto& t : d.trgts) { + char buf[28]; + memset(buf, 0, sizeof buf); + strcpy(buf, t.name.c_str()); + fwrite(buf, 1, sizeof buf, f); + fwrite(&t.type, 4, 1, f); + hsize += 32; + } + for (i = 0, coff = 0; i < Desc::stage_count; i++) { + int o = 0; + if (d.entrypoints[i].empty()) + fwrite(&o, 4, 1, f); + else { + o = hsize + coff; + fwrite(&o, 4, 1, f); + coff += stages[i].size() * sizeof(uint32_t); + } + } + for (i = 0; i < Desc::stage_count; i++) + if (!d.entrypoints[i].empty()) { + auto& stage = stages[i]; + fwrite(&stage[0], sizeof(uint32_t), stage.size(), f); + } +} + +int main(int argc, const char** argv) { + char* src; + size_t src_size; + std::string desc_src; + std::vector<uint32_t> spv[Desc::stage_count]; + cfg_Object* cdesc; + void* dp_mem; + Desc desc; + if (argc < 3) { + print_err("Usage: %s infile outfile.\n", argv[0]); + } + if (!rf(argv[1], src, src_size)) { + print_err("Failed to read %s\n", argv[1]); + return 1; + } + desc_src = get_desc(src); + cdesc = parse_desc(dp_mem, desc_src.c_str()); + desc.build(cdesc); + compile_shaders(spv, src, desc); + write_csh(argv[2], desc, spv); + return 0; +} + diff --git a/sc/sh_enums.h b/sc/sh_enums.h new file mode 100644 index 0000000..8f3c2a5 --- /dev/null +++ b/sc/sh_enums.h @@ -0,0 +1,41 @@ +#ifndef sh_enums_h +#define sh_enums_h + +#define shader_type_xmacro() \ + x(fragment) \ + x(vertex) + +typedef enum { +#define x(n) shader_type_ ## n, +shader_type_xmacro() +#undef x + shader_type_count +} Shader_Type; + +#define sprogram_type_xmacro() \ + x(graphics) + +typedef enum { +#define x(n) sprogram_type_ ## n, +sprogram_type_xmacro() +#undef x + sprogram_type_count +} SProgram_Type; + +#define svariable_type_xmacro() \ + x(float) \ + x(vec2) \ + x(vec3) \ + x(vec4) \ + x(mat2) \ + x(mat3) \ + x(mat4) + +typedef enum { +#define x(n) svariable_type_ ## n, +svariable_type_xmacro() +#undef x + svariable_type_count +} SVariable_Type; + +#endif |