diff options
-rw-r--r-- | .gitignore | 8 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile | 29 | ||||
-rw-r--r-- | cfg/Makefile | 21 | ||||
-rw-r--r-- | cfg/cfgparse.c | 236 | ||||
-rw-r--r-- | cfg/cfgparse.h | 62 | ||||
-rw-r--r-- | intermediate/triangle.glsl | 30 | ||||
-rw-r--r-- | qstd/Makefile | 25 | ||||
-rw-r--r-- | qstd/memory.c | 225 | ||||
-rw-r--r-- | qstd/memory.h | 56 | ||||
-rw-r--r-- | qstd/plat.c | 86 | ||||
-rw-r--r-- | qstd/plat.h | 32 | ||||
-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 | ||||
-rw-r--r-- | todo.txt | 22 |
19 files changed, 1378 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e6f69f --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +tags + +*.d +*.o +/data/ +/cfg/libcfg.a +/qstd/libqstd.a +/sc/sc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..02b9e16 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sc/glslang"] + path = sc/glslang + url = https://github.com/KhronosGroup/glslang diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e506470 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +.POSIX: + +data_dir = data +shaders = data/triangle.csh +tools = qstd cfg sc + +.PHONY: all clean $(tools) +all: $(shaders) $(tools) + +qstd: + $(MAKE) -C qstd + +cfg: qstd + $(MAKE) -C cfg + +sc: qstd cfg + $(MAKE) -C sc + +data/triangle.csh: intermediate/triangle.glsl | $(data_dir) sc + ./sc/sc intermediate/triangle.glsl data/triangle.csh + +$(data_dir): + mkdir -p $(data_dir) + +clean: + rm -rf $(data_dir) + make -C qstd clean + make -C cfg clean + make -C sc clean diff --git a/cfg/Makefile b/cfg/Makefile new file mode 100644 index 0000000..e10e70f --- /dev/null +++ b/cfg/Makefile @@ -0,0 +1,21 @@ +.POSIX: +target = libcfg.a +includes = -I../qstd +cflags = -std=c90 -pedantic -Wall -Wextra $(DEBUG_COMPILE_FLAG) $(includes) +lflags = $(DEBUG_LINK_FLAG) + +objects = cfgparse.o + +.PHONY: all clean + +all: $(target) + +cfgparse.o: cfgparse.c cfgparse.h + $(CC) -c $(cflags) cfgparse.c -o cfgparse.o + +$(target): $(objects) + $(AR) -rcs $(target) $(objects) + +clean: + rm -f $(target) + rm -f $(objects) diff --git a/cfg/cfgparse.c b/cfg/cfgparse.c new file mode 100644 index 0000000..4dc6dbb --- /dev/null +++ b/cfg/cfgparse.c @@ -0,0 +1,236 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "cfgparse.h" + +static const char* next_line(const char* line) { + const char* c; + for (c = line; *c != '\n'; c++) { + if (!*c) return 0; + } + return c + 1; +} + +static int is_object( + const char* line, + const char** start, + int* len +) { + const char* c; + *start = line; + if (line[0] != '[') return 0; + *start = line + 1; + for ( + c = line, *len = 0; + *c && *c != '\n'; + c++, *len = *len + 1 + ) if (*c == ']') { + *len = *len - 1; + return 1; + } + return 0; +} + +static int is_iden(char c) { + return + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c == '_'); +} + +static int is_prop( + const char* line, + const char** start, + int* len +) { + const char* c; + *start = line; + for ( + c = line, *len = 0; + *c && *c != '\n'; + c++, *len = *len + 1 + ) if (*c == ':' || (*c == ' ' && c[1] == ':')) { + *len = *len; + return 1; + } else if (!is_iden(*c)) return 0; + return 0; +} + +static void fill_prop_string( + cfg_Prop* p, + Arena* arena, + const char* start, + int len +) { + p->type = cfg_type_string; + p->as.str = arena_alloc(arena, len + 1); + memcpy(p->as.str, start, len); + p->as.str[len] = 0; +} + +static void fill_prop_number( + cfg_Prop* p, + const char* start, + int len +) { + (void)len; + p->type = cfg_type_int; + p->as.num = (int)strtol(start, 0, 10); +} + +static void fill_prop_data( + cfg_Prop* p, + Arena* arena, + const char* start, + int len +) { + int i, size; + char s[3]; + s[2] = 0; + size = len / 2; + p->type = cfg_type_data; + p->as.data.data = arena_alloc(arena, size); + p->as.data.size = size; + for (i = 0; i < size; i++) { + s[0] = start[i * 2]; + s[1] = start[i * 2 + 1]; + sscanf(s, "%x", (unsigned*)&p->as.data.data[i]); + } +} + +static void fill_prop_val( + cfg_Prop* p, + Arena* arena, + const char* start +) { + const char* c; + int len; + for (; *start && (*start == ' ' || *start == '\t'); start++); + for (len = 0, c = start; *c && *c != '\n'; c++, len++); + switch (start[0]) { + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': case '-': + fill_prop_number(p, start, len); + break; + case '$': + fill_prop_data(p, arena, start + 1, len - 1); + break; + default: + fill_prop_string(p, arena, start, len); + break; + } +} + +const char* prop_start(const char* line) { + const char* s = line; + while (*s && *s != ':') s++; + s++; + if (*s == ' ') s++; + return s; +} + +cfg_Object* cfg_parse(const char* src, Arena* arena) { + const char* line, * start; + int len; + cfg_Object* root = 0, * obj = 0, * pobj; + cfg_Prop* prop = 0, * pprop; + for (line = src; line; line = next_line(line)) { + len = 0; + if (line[0] == '#') continue; + if (is_object(line, &start, &len) || line == src) { + pobj = obj; + obj = arena_alloc(arena, sizeof *obj); + obj->prop_count = 0; + obj->props = 0; + obj->next = 0; + memcpy(obj->name, start, len); + obj->name[len] = 0; + if (!root) + root = obj; + else + pobj->next = obj; + } else if (!obj) continue; + if (is_prop(line, &start, &len)) { + pprop = prop; + prop = arena_alloc(arena, sizeof *prop); + prop->next = 0; + memcpy(prop->name, start, len); + prop->name[len] = 0; + fill_prop_val(prop, arena, prop_start(line)); + obj->prop_count++; + if (!obj->props) + obj->props = prop; + else + pprop->next = prop; + } + } + return root; +} + +const cfg_Prop* find_prop( + const cfg_Object* obj, + const char* name +) { + const cfg_Prop* p; + for (p = obj->props; p; p = p->next) { + if (!strcmp(p->name, name)) { + return p; + } + } + return 0; +} + +const cfg_Prop* find_prop_of( + const cfg_Object* obj, + const char* name, + cfg_Type type +) { + const cfg_Prop* p; + for (p = obj->props; p; p = p->next) { + if (p->type == type && !strcmp(p->name, name)) { + return p; + } + } + return 0; +} + +const cfg_Object* find_child( + const cfg_Object* obj, + const char* name +) { + const cfg_Object* ch = obj; + while (ch) { + if (!strcmp(ch->name, name)) return ch; + ch = ch->next; + } + return 0; +} + +int find_int_default( + const cfg_Object* obj, + const char* name, + int def +) { + const cfg_Prop* prop; + prop = find_prop_of(obj, name, cfg_type_int); + if (prop) { + return prop->as.num; + } + return def; +} + +const char* find_string_default( + const cfg_Object* obj, + const char* name, + const char* def +) { + const cfg_Prop* prop; + prop = find_prop_of(obj, name, cfg_type_string); + if (prop) { + return prop->as.str; + } + return def; +} diff --git a/cfg/cfgparse.h b/cfg/cfgparse.h new file mode 100644 index 0000000..4865b48 --- /dev/null +++ b/cfg/cfgparse.h @@ -0,0 +1,62 @@ +#ifndef cfgparse_h +#define cfgparse_h + +#include "memory.h" + +typedef enum { + cfg_type_int, + cfg_type_string, + cfg_type_data, + cfg_type_none +} cfg_Type; + +typedef struct { + int size; + char* data; +} cfg_Data; + +typedef struct cfg_Prop { + char name[28]; + cfg_Type type; + union { + int num; + cfg_Data data; + char* str; + } as; + struct cfg_Prop* next; +} cfg_Prop; + +typedef struct cfg_Object { + char name[28]; + int prop_count; + cfg_Prop* props; + struct cfg_Object* next; +} cfg_Object; + +cfg_Object* cfg_parse(const char* src, Arena* arena); +const cfg_Prop* find_prop( + const cfg_Object* obj, + const char* name +); +const cfg_Prop* find_prop_of( + const cfg_Object* obj, + const char* name, + cfg_Type type +); +const cfg_Object* find_child( + const cfg_Object* obj, + const char* name +); + +int find_int_default( + const cfg_Object* obj, + const char* name, + int def +); +const char* find_string_default( + const cfg_Object* obj, + const char* name, + const char* def +); + +#endif diff --git a/intermediate/triangle.glsl b/intermediate/triangle.glsl new file mode 100644 index 0000000..ed6052f --- /dev/null +++ b/intermediate/triangle.glsl @@ -0,0 +1,30 @@ +#ifdef DESC +[program] +type: graphics +vertex: main +fragment: main + +[attribute] +name: position +type: vec2 + +[target] +name: colour +type: vec4 +#endif + +#ifdef VERTEX_SHADER + +void main() { + gl_Position = vec4(position, 0.0, 1.0); +} + +#endif + +#ifdef FRAGMENT_SHADER + +void main() { + colour = 1.0.xxxx; +} + +#endif diff --git a/qstd/Makefile b/qstd/Makefile new file mode 100644 index 0000000..fa952dc --- /dev/null +++ b/qstd/Makefile @@ -0,0 +1,25 @@ +.POSIX: +target = libqstd.a +includes = -I../qstd +defines = -Dplat_x86 -Dplat_posix -Dallocation_default_alignment=8 +cflags = -std=c90 -pedantic -Wall -Wextra $(DEBUG_COMPILE_FLAG) $(includes) $(defines) +lflags = $(DEBUG_LINK_FLAG) + +objects = plat.o memory.o + +.PHONY: all clean + +all: $(target) + +memory.o: memory.c memory.h plat.h + $(CC) -c $(cflags) memory.c -o memory.o + +plat.o: plat.c plat.h + $(CC) -c $(cflags) plat.c -o plat.o + +$(target): $(objects) + $(AR) -rcs $(target) $(objects) + +clean: + rm -f $(target) + rm -f $(objects) diff --git a/qstd/memory.c b/qstd/memory.c new file mode 100644 index 0000000..91e4a5b --- /dev/null +++ b/qstd/memory.c @@ -0,0 +1,225 @@ +#include "memory.h" +#include "plat.h" +#include <stdint.h> +#include <stddef.h> + +int aligned(const void* p, int a) { + return (uintptr_t)p % a == 0; +} + +int align_size(int s, int a) { + return (s + (a - 1)) & -a; +} + +static uintptr_t align_address( + uintptr_t ad, + size_t al +) { + size_t m; + m = al - 1; + return (ad + m) & ~m; +} + +void init_arena( + Arena* a, + void* mem, + int size +) { + a->buf = mem; + a->size = size; + a->ptr = 0; +} + +void clear_arena(Arena* a) { + a->ptr = 0; +} + +void* imp_arena_alloc( + Arena* a, + int size +) { + char* r; + assert(a->ptr + size < a->size); + r = &a->buf[a->ptr]; + a->ptr += size; + return r; +} + +void* arena_alloc( + Arena* a, + int size +) { + return arena_alloc_aligned( + a, + size, + allocation_default_alignment + ); +} + +void* arena_alloc_aligned( + Arena* a, + int size, + int align +) { + void* p; + p = imp_arena_alloc( + a, + size + align + 1 + ); + return (void*)align_address((uintptr_t)p, align); +} + +void init_heap( + Heap* h, + void* mem, + int size +) { + int* fb; + assert(aligned(mem, 4)); + assert(size > 8); + h->buf = mem; + h->size = size; + h->blocks = 1; + fb = (int*)h->buf; + fb[0] = size; +} + +void* imp2_heap_alloc( + Heap* h, + int size +) { + int o, i; + int hs = sizeof(int); + int as = align_size(size + hs, hs); + int f = ~((unsigned)-1 >> 1); + for (i = o = 0; i < h->blocks; i++) { + int* phdr = (int*)&h->buf[o]; + int hdr = *phdr, bs; + assert(aligned(phdr, sizeof hdr)); + bs = hdr & ~f; + if (~hdr & f) { + if (as == bs) { + phdr[0] |= 1; + return phdr + 1; + } else { + int ns = bs - as; + if (ns > hs) { + int* nhdr = &phdr[ns / 4]; + phdr[0] = ns; + nhdr[0] = as | f; + h->blocks++; + return &nhdr[1]; + } + } + } else + o += bs; + } + return 0; +} + +void* imp_heap_alloc(Heap* h, int s) { + void* p = imp2_heap_alloc(h, s); + if (!p) { + heap_defrag(h); + p = imp2_heap_alloc(h, s); + } + return p; +} + +void imp_heap_free(Heap* h, void* p) { + assert((char*)p > h->buf); + assert((char*)p < h->buf + h->size); + (void)h; + ((int*)p)[-1] &= (unsigned)-1 >> 1; +} + +void* heap_alloc_aligned( + Heap* h, + int size, + int align +) { + unsigned char* p, * a; + ptrdiff_t shift; + size += (int)align; + p = imp_heap_alloc(h, size); + if (!p) { return 0; } + a = (unsigned char*)align_address((uintptr_t)p, align); + a += align * (unsigned)(p == a); + shift = a - p; + a[-1] = shift & 0xff; + return a; +} + +void heap_free_aligned(Heap* h, void* p) { + unsigned char* a; + ptrdiff_t shift; + a = p; + shift = a[-1]; + shift += 256 * shift == 0; + a -= shift; + imp_heap_free(h, a); +} + +void heap_defrag(Heap* h) { + int i, o, mtc; + int f = ~((unsigned)-1 >> 1); + for (i = o = mtc = 0; i < h->blocks; i++) { + int* phdr = (int*)&h->buf[o]; + int hdr = *phdr, bs, m, mc; + assert(aligned(phdr, sizeof hdr)); + bs = hdr & ~f; + if (~hdr & f) { + for ( + m = bs, mc = 0, i++; + i < h->blocks; + i++, mc++ + ) { + int mhdr = *(int*)&h->buf[o + m]; + if (~mhdr & f) + m += mhdr & ~f; + else + break; + } + i--; + bs = m; + phdr[0] = bs; + mtc += mc; + } + o += bs; + } + h->blocks -= mtc; +} + +void* heap_alloc( + Heap* h, + int size +) { + return heap_alloc_aligned( + h, + size, + allocation_default_alignment + ); +} + +void heap_free( + Heap* h, + void* p +) { + heap_free_aligned(h, p); +} + +/* +void print_blocks(Heap* h) { + int i, o; + int fb = ~((unsigned)-1 >> 1); + for (i = o = 0; i < h->blocks; i++) { + int b = *(int*)&h->buf[o]; + int bs = b & ~fb; + int f = ~b & fb; + printf("%s %d\n", f? "free": " ", bs); + o += bs; + } + assert(o == h->size); +} +*/ + diff --git a/qstd/memory.h b/qstd/memory.h new file mode 100644 index 0000000..26c0382 --- /dev/null +++ b/qstd/memory.h @@ -0,0 +1,56 @@ +#ifndef memory_h +#define memory_h + +int aligned(const void* p, int a); +int align_size(int s, int a); + +typedef struct Arena { + char* buf; + int size, ptr; +} Arena; + +void init_arena( + Arena* a, + void* mem, + int size +); +void clear_arena(Arena* a); +void* arena_alloc( + Arena* a, + int size +); +void* arena_alloc_aligned( + Arena* a, + int size, + int align +); + +typedef struct Heap { + char* buf; + int size, blocks; +} Heap; + +void init_heap( + Heap* h, + void* mem, + int size +); +void* heap_alloc( + Heap* h, + int size +); +void* heap_alloc_aligned( + Heap* h, + int size, + int align +); +void heap_free_aligned( + Heap* h, + void* p +); +void heap_free( + Heap* h, + void* p +); +void heap_defrag(Heap* h); +#endif diff --git a/qstd/plat.c b/qstd/plat.c new file mode 100644 index 0000000..61385ff --- /dev/null +++ b/qstd/plat.c @@ -0,0 +1,86 @@ +#include "plat.h" + +#ifdef plat_posix +#define _POSIX_SOURCE +#define _GNU_SOURCE + +#include <fcntl.h> +#include <stdlib.h> +#include <sys/time.h> +#include <time.h> +#include <stdarg.h> +#include <stdio.h> + +extern int isatty(int); +extern int fileno(FILE*); + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +) { + if (!val) { + print_err( + "%d:%s: Assertion failed: %s.\n", + line, + file, + expr + ); + pbreak(420); + return 0; + } + return 1; +} + +void print(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void print_err(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;31m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void print_war(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;35m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void pbreak(int code) { +#if defined(DEBUG) && defined(plat_x86) + __asm__("int3;"); + (void)code; +#else + exit(code); +#endif +} + +#endif diff --git a/qstd/plat.h b/qstd/plat.h new file mode 100644 index 0000000..27521fa --- /dev/null +++ b/qstd/plat.h @@ -0,0 +1,32 @@ +#ifndef plat_h +#define plat_h + +#ifdef assert +#undef assert +#endif + +#ifdef DEBUG +#define assert(expr) \ + imp_assert( \ + expr, \ + #expr, \ + __FILE__, \ + __LINE__ \ + ) +#else +#define assert(expr) +#endif + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +); + +void print(const char* fmt, ...); +void print_err(const char* fmt, ...); +void print_war(const char* fmt, ...); +void pbreak(int code); + +#endif 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 diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..d85fe76 --- /dev/null +++ b/todo.txt @@ -0,0 +1,22 @@ +todo list + - [x] shader compiling + - [ ] windowing + input + - [ ] vulkan device and swapchain, etc + - [ ] draw a triangle + - [ ] texture conversion + compression, etc + - [ ] texture the triangle + - [ ] simple text rendering + - [ ] 3D maths library + - [ ] model conversion + - [ ] render a model + - [ ] render a sky box + - [ ] PBR + IBL + - [ ] shadows + - [ ] MSAA + - [ ] GI + - [ ] clustering + - [ ] bloom + - [ ] SSAO + - [ ] SSDO + - [ ] SSR + |