summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--.gitmodules3
-rw-r--r--Makefile29
-rw-r--r--cfg/Makefile21
-rw-r--r--cfg/cfgparse.c236
-rw-r--r--cfg/cfgparse.h62
-rw-r--r--intermediate/triangle.glsl30
-rw-r--r--qstd/Makefile25
-rw-r--r--qstd/memory.c225
-rw-r--r--qstd/memory.h56
-rw-r--r--qstd/plat.c86
-rw-r--r--qstd/plat.h32
-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
-rw-r--r--todo.txt22
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
+