summaryrefslogtreecommitdiff
path: root/sc
diff options
context:
space:
mode:
Diffstat (limited to 'sc')
-rw-r--r--sc/Makefile51
m---------sc/glslang0
-rw-r--r--sc/includer.cpp43
-rw-r--r--sc/includer.hpp23
-rw-r--r--sc/sc.cpp385
-rw-r--r--sc/sh_enums.h41
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