+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <alloca.h>
#include "opengl.h"
#include "nexus3d_impl.h"
+#include "gfx_gl.h"
void nex_clear(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
+void nex_clearcolor(float r, float g, float b)
+{
+ glClearColor(r, g, b, 1.0f);
+}
+
+void nex_cleardepth(float z)
+{
+ glClearDepth(z);
+}
+
+void nex_clearstencil(unsigned int s)
+{
+ glClearStencil(s);
+}
+
void nex_viewport(int x, int y, int w, int h)
{
glViewport(x, y, w, h);
}
+
+nex_buffer *nex_alloc_buffer(size_t sz, const void *data)
+{
+ nex_buffer *buf;
+
+ if(!(buf = malloc(sizeof *buf))) {
+ return 0;
+ }
+ glCreateBuffers(1, &buf->bo);
+ if(data) {
+ glNamedBufferStorage(buf->bo, sz, data, GL_DYNAMIC_STORAGE_BIT);
+ }
+
+ buf->size = sz;
+ return buf;
+}
+
+void nex_free_buffer(nex_buffer *buf)
+{
+ if(!buf) return;
+ glDeleteBuffers(1, &buf->bo);
+ free(buf);
+}
+
+nex_geometry *nex_alloc_geometry(void)
+{
+ nex_geometry *geom;
+
+ if(!(geom = calloc(1, sizeof *geom))) {
+ return 0;
+ }
+ glCreateVertexArrays(1, &geom->vao);
+ return geom;
+}
+
+void nex_free_geometry(nex_geometry *geom)
+{
+ if(!geom) return;
+ glDeleteVertexArrays(1, &geom->vao);
+ free(geom);
+}
+
+void nex_geom_vbuffer(nex_geometry *geom, unsigned int bufid, const nex_buffer *buf,
+ unsigned int stride)
+{
+ glVertexArrayVertexBuffer(geom->vao, bufid, buf->bo, 0, stride);
+
+ assert(bufid >= 0 && bufid < MAX_VAO_BUF);
+ geom->buf[bufid] = buf;
+}
+
+void nex_geom_ibuffer(nex_geometry *geom, const nex_buffer *buf)
+{
+ glVertexArrayElementBuffer(geom->vao, buf->bo);
+
+ geom->ibuf = buf;
+}
+
+static int attrnelem(enum nex_vattr_type atype)
+{
+ switch(atype) {
+ case NEX_FLOAT:
+ return 1;
+ case NEX_VEC2:
+ return 2;
+ case NEX_VEC3:
+ case NEX_COL3:
+ return 3;
+ case NEX_VEC4:
+ case NEX_COL4:
+ return 4;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int attrnorm(enum nex_vattr_type atype)
+{
+ return atype > NEX_COL3 ? 1 : 0;
+}
+
+static int attrsize(enum nex_vattr_type atype)
+{
+ switch(atype) {
+ case NEX_FLOAT:
+ return sizeof(float);
+ case NEX_VEC2:
+ return 2 * sizeof(float);
+ case NEX_VEC3:
+ case NEX_COL3:
+ return 3 * sizeof(float);
+ case NEX_VEC4:
+ case NEX_COL4:
+ return 4 * sizeof(float);
+ default:
+ break;
+ }
+ return 0;
+}
+
+void nex_geom_vattr(nex_geometry *geom, unsigned int attr, enum nex_vattr_type type,
+ int bufid, unsigned int offs)
+{
+ assert(attr >= 0 && attr < MAX_VAO_ATTR);
+
+ if(bufid >= 0) {
+ glVertexArrayAttribFormat(geom->vao, attr, attrnelem(type), GL_FLOAT,
+ attrnorm(type), offs);
+ glVertexArrayAttribBinding(geom->vao, attr, bufid);
+ glEnableVertexArrayAttrib(geom->vao, attr);
+
+ assert(geom->buf[bufid]);
+ geom->attr[attr].type = type;
+ geom->attr[attr].bufid = bufid;
+ geom->attr[attr].size = attrsize(type);
+ /* XXX no attempt to validate equal vcount across all bound buffers */
+ geom->vcount = geom->buf[bufid]->size / geom->attr[attr].size;
+ } else {
+ glDisableVertexArrayAttrib(geom->vao, attr);
+ geom->attr[attr].bufid = 0;
+ geom->attr[attr].size = 0;
+ }
+}
+
+static unsigned int glprim(enum nex_primitive prim)
+{
+ switch(prim) {
+ case NEX_POINTS:
+ return GL_POINTS;
+ case NEX_LINES:
+ return GL_LINES;
+ case NEX_LINE_STRIP:
+ return GL_LINE_STRIP;
+ case NEX_TRIANGLES:
+ return GL_TRIANGLES;
+ case NEX_TRIANGLE_STRIP:
+ return GL_TRIANGLE_STRIP;
+ case NEX_TRIANGLE_FAN:
+ return GL_TRIANGLE_FAN;
+ default:
+ break;
+ }
+ return 0;
+}
+
+void nex_draw_geometry(const nex_geometry *geom, enum nex_primitive prim, unsigned int count)
+{
+ glBindVertexArray(geom->vao);
+ if(geom->ibuf) {
+ if(!count) count = geom->ibuf->size >> 2;
+ glDrawElements(glprim(prim), count, GL_UNSIGNED_INT, 0);
+ } else {
+ if(!count) count = geom->vcount;
+ glDrawArrays(glprim(prim), 0, count);
+ }
+}
+
+static unsigned int gl_sdrtype(enum nex_sdr_type type)
+{
+ switch(type) {
+ case NEX_SDR_VERTEX:
+ return GL_VERTEX_SHADER;
+ case NEX_SDR_PIXEL:
+ return GL_FRAGMENT_SHADER;
+ default:
+ break;
+ }
+ return 0;
+}
+
+nex_shader *nex_alloc_shader(enum nex_sdr_type type)
+{
+ nex_shader *sdr;
+
+ if(!(sdr = calloc(1, sizeof *sdr))) {
+ return 0;
+ }
+ sdr->sdr = glCreateShader(gl_sdrtype(type));
+ sdr->type = type;
+ return sdr;
+}
+
+void nex_free_shader(nex_shader *sdr)
+{
+ if(!sdr || --sdr->nref > 0) return;
+
+ free(sdr->name);
+ glDeleteShader(sdr->sdr);
+ free(sdr);
+}
+
+nex_sdrprog *nex_alloc_sdrprog(void)
+{
+ nex_sdrprog *prog;
+
+ if(!(prog = calloc(1, sizeof *prog))) {
+ return 0;
+ }
+ prog->prog = glCreateProgram();
+ return prog;
+}
+
+void nex_free_sdrprog(nex_sdrprog *prog)
+{
+ int i;
+
+ if(!prog) return;
+
+ glDeleteProgram(prog->prog);
+
+ for(i=0; i<prog->num_sdr; i++) {
+ nex_free_shader(prog->sdr[i]);
+ }
+}
+
+void nex_sdrname(nex_shader *sdr, const char *name)
+{
+ free(sdr->name);
+ sdr->name = strdup(name);
+}
+
+void nex_sdrsrc(nex_shader *sdr, const char *src)
+{
+ glShaderSource(sdr->sdr, 1, &src, 0);
+ sdr->src = 1;
+ sdr->compiled = 0;
+}
+
+void nex_sdrbin(nex_shader *sdr, void *bin, size_t sz)
+{
+ glShaderBinary(1, &sdr->sdr, GL_SHADER_BINARY_FORMAT_SPIR_V, bin, sz);
+ sdr->src = 0;
+ sdr->num_const = 0;
+ sdr->compiled = 0;
+}
+
+#define NEX_SDRCONST(sdr, id, val) \
+ int n; \
+ assert((sdr)->num_const < MAX_SDR_CONST); \
+ n = (sdr)->num_const++; \
+ (sdr)->cidx[n] = id; \
+ (sdr)->cval[n] = *(unsigned int*)&(val)
+
+void nex_sdrconst_int(nex_shader *sdr, int id, int val)
+{
+ NEX_SDRCONST(sdr, id, val);
+}
+
+void nex_sdrconst_float(nex_shader *sdr, int id, float val)
+{
+ NEX_SDRCONST(sdr, id, val);
+}
+
+int nex_build_shader(nex_shader *sdr)
+{
+ int status, len;
+ char *buf;
+
+ if(sdr->compiled) return 0;
+
+ if(sdr->src) {
+ glCompileShader(sdr->sdr);
+ } else {
+ glSpecializeShaderARB(sdr->sdr, "main", sdr->num_const, sdr->cidx, sdr->cval);
+ }
+
+ glGetShaderiv(sdr->sdr, GL_COMPILE_STATUS, &status);
+ glGetShaderInfoLog(sdr->sdr, 0, &len, 0);
+ if(len > 0) {
+ buf = alloca(len + 1);
+ glGetShaderInfoLog(sdr->sdr, len, 0, buf);
+ buf[len] = 0;
+ fprintf(status ? stdout : stderr, "nex_build_shader %s:\n%s\n",
+ sdr->name ? sdr->name : "<unk>", buf);
+ }
+
+ if(status) {
+ sdr->compiled = 1;
+ return 0;
+ }
+ return -1;
+}
+
+void nex_attach_shader(nex_sdrprog *prog, nex_shader *sdr)
+{
+ assert(prog->num_sdr < MAX_SDRPROG_SDR);
+
+ glAttachShader(prog->prog, sdr->sdr);
+ prog->sdr[prog->num_sdr++] = sdr;
+ sdr->nref++;
+}
+
+int nex_build_sdrprog(nex_sdrprog *prog)
+{
+ int i, status, len;
+ char *buf;
+
+ for(i=0; i<prog->num_sdr; i++) {
+ if(!prog->sdr[i]->compiled) {
+ if(nex_build_shader(prog->sdr[i]) == -1) {
+ return -1;
+ }
+ }
+ }
+
+ glLinkProgram(prog->prog);
+
+ glGetProgramiv(prog->prog, GL_LINK_STATUS, &status);
+ glGetProgramInfoLog(prog->prog, 0, &len, 0);
+ if(len) {
+ buf = alloca(len + 1);
+ glGetProgramInfoLog(prog->prog, len, 0, buf);
+ buf[len] = 0;
+ fprintf(status ? stdout : stderr, "nex_build_sdrprog:\n%s\n", buf);
+ }
+
+ return status ? 0 : -1;
+}
+
+#define SPIRV_MAGIC 0x07230203
+#define SPIRV_CIGAM 0x03022307
+struct spirv_header {
+ uint32_t magic;
+ uint32_t ver;
+ uint32_t gen;
+ uint32_t bound;
+ uint32_t rsvd;
+} __attribute__((packed));
+
+nex_shader *nex_load_shader(const char *path, enum nex_sdr_type type)
+{
+ FILE *fp;
+ nex_shader *sdr = 0;
+ size_t len;
+ char *buf;
+ struct spirv_header *hdr;
+
+ if(!(fp = fopen(path, "rb"))) {
+ fprintf(stderr, "failed to open shader file: %s\n", path);
+ return 0;
+ }
+ fseek(fp, 0, SEEK_END);
+ len = ftell(fp);
+ rewind(fp);
+
+ if(!(buf = malloc(len + 1))) {
+ fprintf(stderr, "failed to allocate shader buffer\n");
+ goto end;
+ }
+ if(fread(buf, 1, len, fp) != len) {
+ fprintf(stderr, "failed to read shader\n");
+ goto end;
+ }
+
+ if(!(sdr = nex_alloc_shader(type))) {
+ fprintf(stderr, "failed to allocate shader\n");
+ goto end;
+ }
+ nex_sdrname(sdr, path);
+
+ hdr = (struct spirv_header*)buf;
+ if(hdr->magic == SPIRV_MAGIC || hdr->magic == SPIRV_CIGAM) {
+ /* TODO parse spir-v OpEntryPoint to auto-detect shader type */
+ nex_sdrbin(sdr, buf, len);
+ } else {
+ buf[len] = 0;
+ nex_sdrsrc(sdr, buf);
+ }
+
+end:
+ free(buf);
+ fclose(fp);
+ return sdr;
+}
+
+nex_sdrprog *nex_load_sdrprog(const char *vpath, const char *ppath)
+{
+ nex_sdrprog *prog = 0;
+ nex_shader *vsdr = 0, *psdr = 0;
+
+ if(!(vsdr = nex_load_shader(vpath, NEX_SDR_VERTEX)) || nex_build_shader(vsdr) == -1) {
+ goto err;
+ }
+
+ if(!(psdr = nex_load_shader(ppath, NEX_SDR_PIXEL)) || nex_build_shader(psdr) == -1) {
+ goto err;
+ }
+
+ if(!(prog = nex_alloc_sdrprog())) {
+ fprintf(stderr, "failed to allocate shader program\n");
+ goto err;
+ }
+ nex_attach_shader(prog, vsdr);
+ nex_attach_shader(prog, psdr);
+ if(nex_build_sdrprog(prog) == -1) {
+ goto err;
+ }
+
+ return prog;
+err:
+ if(prog) {
+ nex_free_sdrprog(prog); /* will also delete shaders */
+ } else {
+ nex_free_shader(vsdr);
+ nex_free_shader(psdr);
+ }
+ return 0;
+}
+
+void nex_bind_sdrprog(nex_sdrprog *prog)
+{
+ glUseProgram(prog->prog);
+}