aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWolfgang Draxinger <Wolfgang.Draxinger@draxit.de>2016-04-24 23:52:45 +0200
committerWolfgang Draxinger <Wolfgang.Draxinger@draxit.de>2016-04-24 23:52:45 +0200
commit2ca04fbe7985ee944f3fa6302886a252a51add0c (patch)
treeb373879928a1060e564d29d44f6e20b620b272e9
downloadpointoverdrawbench-2ca04fbe7985ee944f3fa6302886a252a51add0c.tar.gz
pointoverdrawbench-2ca04fbe7985ee944f3fa6302886a252a51add0c.tar.bz2
initial commit
-rw-r--r--Makefile17
-rw-r--r--debuggl/debuggl.c67
-rw-r--r--debuggl/debuggl.h29
-rw-r--r--linmath.h/LICENCE13
-rw-r--r--linmath.h/README12
-rw-r--r--linmath.h/linmath.h474
-rw-r--r--main.c290
-rw-r--r--mvp.vs.glsl12
-rw-r--r--positiongen.c51
-rw-r--r--positiongen.glsl24
-rw-r--r--positiongen.h15
-rw-r--r--shaderloader/shaderloader.c287
-rw-r--r--shaderloader/shaderloader.h22
-rw-r--r--solid.c64
-rw-r--r--solid.fs.glsl9
-rw-r--r--solid.h17
-rw-r--r--stats.c25
-rw-r--r--stats.h35
18 files changed, 1463 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9150d16
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+EXE = pointoverdrawbench
+OBJS = \
+ main.o positiongen.o solid.o stats.o \
+ shaderloader/shaderloader.o \
+ debuggl/debuggl.o
+CFLAGS = -std=c99 -I../../../extra
+LDLIBS = -lGL -lGLEW -lglut -lm
+
+.PHONY: all clean
+
+all: $(EXE)
+
+clean:
+ -rm -f $(OBJS) $(EXE)
+
+$(EXE): $(OBJS)
+ $(CC) $(LDFLAGS) -o $(EXE) $(OBJS) $(LOADLIBES) $(LDLIBS)
diff --git a/debuggl/debuggl.c b/debuggl/debuggl.c
new file mode 100644
index 0000000..4775ade
--- /dev/null
+++ b/debuggl/debuggl.c
@@ -0,0 +1,67 @@
+#include "debuggl.h"
+#include <GL/gl.h>
+#include <stdio.h>
+
+int debuggl_check_call(
+ char const * const file,
+ char const * const func,
+ unsigned int const line,
+ char const * const what)
+{
+ int errors = 0;
+ GLenum err;
+ while( (err = glGetError()) != GL_NO_ERROR) {
+ ++errors;
+ char const * errstr = NULL;
+ switch( err ) {
+ case GL_INVALID_ENUM: errstr = "invalid enum"; break;
+ case GL_INVALID_VALUE: errstr = "invalid value"; break;
+ case GL_INVALID_OPERATION: errstr = "invalid operation"; break;
+ case GL_STACK_OVERFLOW: errstr = "invalid overflow"; break;
+ case GL_STACK_UNDERFLOW: errstr = "invalid underflow"; break;
+ case GL_OUT_OF_MEMORY: errstr = "invalid memory"; break;
+ }
+ fprintf(stderr,
+ "%s(%d)/%s: %s => OpenGL Error: %s\n",
+ file, (int)line, func, what,
+ errstr );
+ }
+ return errors;
+}
+
+int debuggl_trace_call(
+ char const * const file,
+ char const * const func,
+ unsigned int const line,
+ char const * const what)
+{
+ int errors = 0;
+ GLenum err;
+ do {
+ err = glGetError();
+ char const * errstr = NULL;
+ switch( err ) {
+ case GL_INVALID_ENUM: errstr = "invalid enum"; break;
+ case GL_INVALID_VALUE: errstr = "invalid value"; break;
+ case GL_INVALID_OPERATION: errstr = "invalid operation"; break;
+ case GL_STACK_OVERFLOW: errstr = "invalid overflow"; break;
+ case GL_STACK_UNDERFLOW: errstr = "invalid underflow"; break;
+ case GL_OUT_OF_MEMORY: errstr = "invalid memory"; break;
+ }
+
+ if( errstr ) {
+ ++errors;
+ fprintf(stderr,
+ "%s(%d)/%s: %s => OpenGL Error: %s\n",
+ file, (int)line, func, what,
+ errstr );
+ }
+ else {
+ fprintf(stderr,
+ "%s(%d): %s OK\n",
+ func, (int)line, what);
+ }
+ } while( GL_NO_ERROR != err );
+ return errors;
+}
+
diff --git a/debuggl/debuggl.h b/debuggl/debuggl.h
new file mode 100644
index 0000000..4d8dff9
--- /dev/null
+++ b/debuggl/debuggl.h
@@ -0,0 +1,29 @@
+#pragma once
+#ifndef DEBUGGL_H
+#define DEBUGGL_H
+
+/* execute a OpenGL but check for errors only if compiled for debugging */
+#ifndef NDEBUG
+#define debuggl_trace(glcmd) \
+ ({ glcmd; debuggl_trace_call(__FILE__, __func__, __LINE__, #glcmd); })
+#else
+#define debuggl_trace(glcmd) glcmd
+#endif
+
+/* check for OpenGL errors */
+#define debuggl_check(glcmd) \
+ ({ glcmd; debuggl_check_call(__FILE__, __func__, __LINE__, #glcmd); })
+
+int debuggl_check_call(
+ char const * const file,
+ char const * const func,
+ unsigned int const line,
+ char const * const what);
+
+int debuggl_trace_call(
+ char const * const file,
+ char const * const func,
+ unsigned int const line,
+ char const * const what);
+
+#endif/*DEBUGGL_H*/
diff --git a/linmath.h/LICENCE b/linmath.h/LICENCE
new file mode 100644
index 0000000..bb3444d
--- /dev/null
+++ b/linmath.h/LICENCE
@@ -0,0 +1,13 @@
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2013 Wolfgang 'datenwolf' Draxinger <code@datenwolf.net>
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
diff --git a/linmath.h/README b/linmath.h/README
new file mode 100644
index 0000000..9c43c8e
--- /dev/null
+++ b/linmath.h/README
@@ -0,0 +1,12 @@
+# linmath.h -- A small library for linear math as required for computer graphics
+
+linmath.h provides the most used types required programming computer graphice:
+
+vec3 -- 3 element vector of floats
+vec4 -- 4 element vector of floats (4th component used for homogenous computations)
+mat4x4 -- 4 by 4 elements matrix, computations are done in column major order
+quat -- quaternion
+
+The types are deliberately named like the types in GLSL. In fact they are meant to
+be used for the client side computations and passing to same typed GLSL uniforms.
+
diff --git a/linmath.h/linmath.h b/linmath.h/linmath.h
new file mode 100644
index 0000000..d21fd7d
--- /dev/null
+++ b/linmath.h/linmath.h
@@ -0,0 +1,474 @@
+#ifndef LINMATH_H
+#define LINMATH_H
+
+#include <math.h>
+#include <string.h>
+
+#define LINMATH_H_DEFINE_VEC(n) \
+typedef float vec##n[n]; \
+static inline void vec##n##_add(vec##n r, vec##n a, vec##n b) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = a[i] + b[i]; \
+} \
+static inline void vec##n##_sub(vec##n r, vec##n a, vec##n b) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = a[i] - b[i]; \
+} \
+static inline void vec##n##_scale(vec##n r, vec##n v, float s) \
+{ \
+ int i; \
+ for(i=0; i<n; ++i) \
+ r[i] = v[i] * s; \
+} \
+static inline float vec##n##_mul_inner(vec##n a, vec##n b) \
+{ \
+ float p = 0.; \
+ int i; \
+ for(i=0; i<n; ++i) \
+ p += b[i]*a[i]; \
+ return p; \
+} \
+static inline float vec##n##_len(vec##n v) \
+{ \
+ return sqrtf(vec##n##_mul_inner(v,v)); \
+} \
+static inline void vec##n##_norm(vec##n r, vec##n v) \
+{ \
+ float k = 1.0 / vec##n##_len(v); \
+ vec##n##_scale(r, v, k); \
+}
+
+LINMATH_H_DEFINE_VEC(2);
+LINMATH_H_DEFINE_VEC(3);
+LINMATH_H_DEFINE_VEC(4);
+
+static inline void vec3_mul_cross(vec3 r, vec3 a, vec3 b)
+{
+ vec3 c;
+ c[0] = a[1]*b[2] - a[2]*b[1];
+ c[1] = a[2]*b[0] - a[0]*b[2];
+ c[2] = a[0]*b[1] - a[1]*b[0];
+ memcpy(r, c, sizeof(c));
+}
+
+static inline void vec4_mul_cross(vec4 r, vec4 a, vec4 b)
+{
+ vec4 c;
+ c[0] = a[1]*b[2] - a[2]*b[1];
+ c[1] = a[2]*b[0] - a[0]*b[2];
+ c[2] = a[0]*b[1] - a[1]*b[0];
+ c[3] = 1.;
+ memcpy(r, c, sizeof(c));
+}
+
+typedef vec4 mat4x4[4];
+static inline void mat4x4_identity(mat4x4 M)
+{
+ int i, j;
+ for(j=0; j<4; ++j) for(i=0; i<4; ++i) {
+ M[i][j] = i==j ? 1 : 0;
+ }
+}
+static inline void mat4x4_dup(mat4x4 M, mat4x4 N)
+{
+ int i, j;
+ for(j=0; j<4; ++j) {
+ for(i=0; i<4; ++i) {
+ M[i][j] = N[i][j];
+ }
+ }
+}
+static inline void mat4x4_add(mat4x4 M, mat4x4 a, mat4x4 b)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ vec4_add(M[i], a[i], b[i]);
+}
+static inline void mat4x4_sub(mat4x4 M, mat4x4 a, mat4x4 b)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ vec4_sub(M[i], a[i], b[i]);
+}
+static inline void mat4x4_scale(mat4x4 M, mat4x4 a, float k)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ vec4_scale(M[i], a[i], k);
+}
+static inline void mat4x4_scale_aniso(mat4x4 M, mat4x4 a, float x, float y, float z)
+{
+ vec4_scale(M[0], a[0], x);
+ vec4_scale(M[1], a[1], y);
+ vec4_scale(M[2], a[2], z);
+}
+static inline void mat4x4_mul(mat4x4 M, mat4x4 a, mat4x4 b)
+{
+ int k, r, c;
+ mat4x4 R;
+ for(r=0; r<4; ++r) for(c=0; c<4; ++c) {
+ R[c][r] = 0;
+ for(k=0; k<4; ++k) {
+ R[c][r] += a[k][r] * b[c][k];
+ }
+ }
+ memcpy(M, R, sizeof(R));
+}
+static inline void mat4x4_mul_vec4(vec4 r, mat4x4 M, vec4 v)
+{
+ vec4 r_;
+ int i, j;
+ for(j=0; j<4; ++j) {
+ r_[j] = 0.;
+ for(i=0; i<4; ++i) {
+ r_[j] += M[i][j] * v[i];
+ }
+ }
+ memcpy(r, r_, sizeof(r_));
+}
+static inline void mat4x4_translate(mat4x4 T, float x, float y, float z)
+{
+ mat4x4_identity(T);
+ T[3][0] = x;
+ T[3][1] = y;
+ T[3][2] = z;
+}
+static inline void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 a, vec3 b)
+{
+ int i, j;
+ for(i=0; i<4; ++i) for(j=0; j<4; ++j) {
+ M[i][j] = i<3 && j<3 ? a[i] * b[j] : 0.;
+ }
+}
+static inline void mat4x4_rotate(mat4x4 R, mat4x4 M, float x, float y, float z, float angle)
+{
+ float s = sinf(angle);
+ float c = cosf(angle);
+ vec3 u = {x, y, z};
+
+ if(vec3_len(u) > 1e-4) {
+ vec3_norm(u, u);
+ mat4x4 T;
+ mat4x4_from_vec3_mul_outer(T, u, u);
+
+ mat4x4 S = {
+ { 0, u[2], -u[1], 0},
+ {-u[2], 0, u[0], 0},
+ { u[1], -u[0], 0, 0},
+ { 0, 0, 0, 0}
+ };
+ mat4x4_scale(S, S, s);
+
+ mat4x4 C;
+ mat4x4_identity(C);
+ mat4x4_sub(C, C, T);
+
+ mat4x4_scale(C, C, c);
+
+ mat4x4_add(T, T, C);
+ mat4x4_add(T, T, S);
+
+ T[3][3] = 1.;
+ mat4x4_mul(R, M, T);
+ } else {
+ mat4x4_dup(R, M);
+ }
+}
+static inline void mat4x4_rotate_X(mat4x4 Q, mat4x4 M, float angle)
+{
+ float s = sinf(angle);
+ float c = cosf(angle);
+ mat4x4 R = {
+ {1, 0, 0, 0},
+ {0, c, s, 0},
+ {0,-s, c, 0},
+ {0, 0, 0, 1}
+ };
+ mat4x4_mul(Q, M, R);
+}
+static inline void mat4x4_rotate_Y(mat4x4 Q, mat4x4 M, float angle)
+{
+ float s = sinf(angle);
+ float c = cosf(angle);
+ mat4x4 R = {
+ { c, 0, s, 0},
+ { 0, 1, 0, 0},
+ {-s, 0, c, 0},
+ { 0, 0, 0, 1}
+ };
+ mat4x4_mul(Q, M, R);
+}
+static inline void mat4x4_rotate_Z(mat4x4 Q, mat4x4 M, float angle)
+{
+ float s = sinf(angle);
+ float c = cosf(angle);
+ mat4x4 R = {
+ { c, s, 0, 0},
+ {-s, c, 0, 0},
+ { 0, 0, 1, 0},
+ { 0, 0, 0, 1}
+ };
+ mat4x4_mul(Q, M, R);
+}
+static inline void mat4x4_row(vec4 r, mat4x4 M, int i)
+{
+ int k;
+ for(k=0; k<4; ++k)
+ r[k] = M[k][i];
+}
+static inline void mat4x4_col(vec4 r, mat4x4 M, int i)
+{
+ int k;
+ for(k=0; k<4; ++k)
+ r[k] = M[i][k];
+}
+static inline void mat4x4_transpose(mat4x4 M, mat4x4 N)
+{
+ int i, j;
+ mat4x4 R;
+ for(j=0; j<4; ++j) {
+ for(i=0; i<4; ++i) {
+ R[i][j] = N[j][i];
+ }
+ }
+ memcpy(M, R, sizeof(R));
+}
+static inline void mat4x4_invert(mat4x4 T, mat4x4 M)
+{
+ mat4x4 R;
+ R[0][0] = M[1][1]*(M[2][2]*M[3][3] - M[2][3]*M[3][2]) - M[2][1]*(M[1][2]*M[3][3] - M[1][3]*M[3][2]) - M[3][1]*(M[1][3]*M[2][2] - M[1][2]*M[2][3]);
+ R[0][1] = M[0][1]*(M[2][3]*M[3][2] - M[2][2]*M[3][3]) - M[2][1]*(M[0][3]*M[3][2] - M[0][2]*M[3][3]) - M[3][1]*(M[0][2]*M[2][3] - M[0][3]*M[2][2]);
+ R[0][2] = M[0][1]*(M[1][2]*M[3][3] - M[1][3]*M[3][2]) - M[1][1]*(M[0][2]*M[3][3] - M[0][3]*M[3][2]) - M[3][1]*(M[0][3]*M[1][2] - M[0][2]*M[1][3]);
+ R[0][3] = M[0][1]*(M[1][3]*M[2][2] - M[1][2]*M[2][3]) - M[1][1]*(M[0][3]*M[2][2] - M[0][2]*M[2][3]) - M[2][1]*(M[0][2]*M[1][3] - M[0][3]*M[1][2]);
+
+ R[1][0] = M[1][0]*(M[2][3]*M[3][2] - M[2][2]*M[3][3]) - M[2][0]*(M[1][3]*M[3][2] - M[1][2]*M[3][3]) - M[3][0]*(M[1][2]*M[2][3] - M[1][3]*M[2][2]);
+ R[1][1] = M[0][0]*(M[2][2]*M[3][3] - M[2][3]*M[3][2]) - M[2][0]*(M[0][2]*M[3][3] - M[0][3]*M[3][2]) - M[3][0]*(M[0][3]*M[2][2] - M[0][2]*M[2][3]);
+ R[1][2] = M[0][0]*(M[1][3]*M[3][2] - M[1][2]*M[3][3]) - M[1][0]*(M[0][3]*M[3][2] - M[0][2]*M[3][3]) - M[3][0]*(M[0][2]*M[1][3] - M[0][3]*M[1][2]);
+ R[1][3] = M[0][0]*(M[1][2]*M[2][3] - M[1][3]*M[2][2]) - M[1][0]*(M[0][2]*M[2][3] - M[0][3]*M[2][2]) - M[2][0]*(M[0][3]*M[1][2] - M[0][2]*M[1][3]);
+
+ R[2][0] = M[1][0]*(M[2][1]*M[3][3] - M[2][3]*M[3][1]) - M[2][0]*(M[1][1]*M[3][3] - M[1][3]*M[3][1]) - M[3][0]*(M[1][3]*M[2][1] - M[1][1]*M[2][3]);
+ R[2][1] = M[0][0]*(M[2][3]*M[3][1] - M[2][1]*M[3][3]) - M[2][0]*(M[0][3]*M[3][1] - M[0][1]*M[3][3]) - M[3][0]*(M[0][1]*M[2][3] - M[0][3]*M[2][1]);
+ R[2][2] = M[0][0]*(M[1][1]*M[3][3] - M[1][3]*M[3][1]) - M[1][0]*(M[0][1]*M[3][3] - M[0][3]*M[3][1]) - M[3][0]*(M[0][3]*M[1][1] - M[0][1]*M[1][3]);
+ R[2][3] = M[0][0]*(M[1][3]*M[2][1] - M[1][1]*M[2][3]) - M[1][0]*(M[0][3]*M[2][1] - M[0][1]*M[2][3]) - M[2][0]*(M[0][1]*M[1][3] - M[0][3]*M[1][1]);
+
+ R[3][0] = M[1][0]*(M[2][2]*M[3][1] - M[2][1]*M[3][2]) - M[2][0]*(M[1][2]*M[3][1] - M[1][1]*M[3][2]) - M[3][0]*(M[1][1]*M[2][2] - M[1][2]*M[2][1]);
+ R[3][1] = M[0][0]*(M[2][1]*M[3][2] - M[2][2]*M[3][1]) - M[2][0]*(M[0][1]*M[3][2] - M[0][2]*M[3][1]) - M[3][0]*(M[0][2]*M[2][1] - M[0][1]*M[2][2]);
+ R[3][2] = M[0][0]*(M[1][2]*M[3][1] - M[1][1]*M[3][2]) - M[1][0]*(M[0][2]*M[3][1] - M[0][1]*M[3][2]) - M[3][0]*(M[0][1]*M[1][2] - M[0][2]*M[1][1]);
+ R[3][3] = M[0][0]*(M[1][1]*M[2][2] - M[1][2]*M[2][1]) - M[1][0]*(M[0][1]*M[2][2] - M[0][2]*M[2][1]) - M[2][0]*(M[0][2]*M[1][1] - M[0][1]*M[1][2]);
+ memcpy(T, R, 16*sizeof(float));
+}
+static inline void mat4x4_orthonormalize(mat4x4 R, mat4x4 M)
+{
+ mat4x4_dup(R, M);
+ float s = 1.;
+ vec3 h;
+
+ vec3_norm(R[2], R[2]);
+
+ s = vec3_mul_inner(R[1], R[2]);
+ vec3_scale(h, R[2], s);
+ vec3_sub(R[1], R[1], h);
+ vec3_norm(R[2], R[2]);
+
+ s = vec3_mul_inner(R[1], R[2]);
+ vec3_scale(h, R[2], s);
+ vec3_sub(R[1], R[1], h);
+ vec3_norm(R[1], R[1]);
+
+ s = vec3_mul_inner(R[0], R[1]);
+ vec3_scale(h, R[1], s);
+ vec3_sub(R[0], R[0], h);
+ vec3_norm(R[0], R[0]);
+}
+
+static inline void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f)
+{
+ M[0][0] = 2.*n/(r-l);
+ M[0][1] = M[0][2] = M[0][3] = 0.;
+
+ M[1][1] = 2.*n/(t-b);
+ M[1][0] = M[1][2] = M[1][3] = 0.;
+
+ M[2][0] = (r+l)/(r-l);
+ M[2][1] = (t+b)/(t-b);
+ M[2][2] = -(f+n)/(f-n);
+ M[2][3] = -1;
+
+ M[3][2] = -2.*(f*n)/(f-n);
+ M[3][0] = M[3][1] = M[3][3] = 0.;
+}
+static inline void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f)
+{
+ M[0][0] = 2./(r-l);
+ M[0][1] = M[0][2] = M[0][3] = 0.;
+
+ M[1][1] = 2./(t-b);
+ M[1][0] = M[1][2] = M[1][3] = 0.;
+
+ M[2][2] = -2./(f-n);
+ M[2][0] = M[2][1] = M[2][3] = 0.;
+
+ M[3][0] = (r+l)/(r-l);
+ M[3][1] = (t+b)/(t-b);
+ M[3][2] = (f+n)/(f-n);
+ M[3][3] = 1.;
+}
+
+typedef float quat[4];
+static inline void quat_identity(quat q)
+{
+ q[0] = q[1] = q[2] = 0.;
+ q[3] = 1.;
+}
+static inline void quat_add(quat r, quat a, quat b)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ r[i] = a[i] + b[i];
+}
+static inline void quat_sub(quat r, quat a, quat b)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ r[i] = a[i] - b[i];
+}
+static inline void quat_mul(quat r, quat p, quat q)
+{
+ vec3 w;
+ vec3_mul_cross(r, p, q);
+ vec3_scale(w, p, q[3]);
+ vec3_add(r, r, w);
+ vec3_scale(w, q, p[3]);
+ vec3_add(r, r, w);
+ r[3] = p[3]*q[3] - vec3_mul_inner(p, q);
+}
+static inline void quat_scale(quat r, quat v, float s)
+{
+ int i;
+ for(i=0; i<4; ++i)
+ r[i] = v[i] * s;
+}
+static inline float quat_inner_product(quat a, quat b)
+{
+ float p = 0.;
+ int i;
+ for(i=0; i<4; ++i)
+ p += b[i]*a[i];
+ return p;
+}
+static inline void quat_conj(quat r, quat q)
+{
+ int i;
+ for(i=0; i<3; ++i)
+ r[i] = -q[i];
+ r[3] = q[3];
+}
+static inline void quat_norm(quat r, quat v) { vec4_norm(r, v); }
+static inline void quat_mul_vec3(vec3 r, quat q, vec3 v)
+{
+ quat q_;
+ quat v_ = {v[0], v[1], v[2], 0.};
+
+ quat_conj(q_, q);
+ quat_norm(q_, q_);
+ quat_mul(q_, v_, q_);
+ quat_mul(q_, q, q_);
+ memcpy(r, q_, 3*sizeof(float));
+}
+static inline void mat4x4_from_quat(mat4x4 M, quat q)
+{
+ float a = q[3];
+ float b = q[0];
+ float c = q[1];
+ float d = q[2];
+ float a2 = a*a;
+ float b2 = b*b;
+ float c2 = c*c;
+ float d2 = d*d;
+
+ M[0][0] = a2 + b2 - c2 - d2;
+ M[0][1] = 2*(b*c + a*d);
+ M[0][2] = 2*(b*d - a*c);
+ M[0][3] = 0.;
+
+ M[1][0] = 2*(b*c - a*d);
+ M[1][1] = a2 - b2 + c2 - d2;
+ M[1][2] = 2*(c*d + a*b);
+ M[1][3] = 0.;
+
+ M[2][0] = 2*(b*d + a*c);
+ M[2][1] = 2*(c*d - a*b);
+ M[2][2] = a2 - b2 - c2 + d2;
+ M[2][3] = 0.;
+
+ M[3][0] = M[3][1] = M[3][2] = 0.;
+ M[3][3] = 1.;
+}
+static inline void mat4x4_mul_quat(mat4x4 R, mat4x4 M, quat q)
+{
+ quat_mul_vec3(R[0], M[0], q);
+ quat_mul_vec3(R[1], M[1], q);
+ quat_mul_vec3(R[2], M[2], q);
+
+ R[3][0] = R[3][1] = R[3][2] = 0.;
+ R[3][3] = 1.;
+}
+static inline void quat_from_mat4x4(quat q, mat4x4 M)
+{
+ float r=0.;
+ int i;
+
+ int perm[] = { 0, 1, 2, 0, 1 };
+ int *p = perm;
+
+ for(i = 0; i<3; i++) {
+ float m = M[i][i];
+ if( m < r )
+ continue;
+ m = r;
+ p = &perm[i];
+ }
+
+ r = sqrtf(1. + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]] );
+
+ q[0] = r/2.;
+ q[1] = (M[p[0]][p[1]] - M[p[1]][p[0]])/(2.*r);
+ q[2] = (M[p[2]][p[0]] - M[p[0]][p[2]])/(2.*r);
+ q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]])/(2.*r);
+}
+
+static inline void mat4x4_arcball(mat4x4 R, mat4x4 M, vec2 _a, vec2 _b, float s)
+{
+ vec2 a; memcpy(a, _a, sizeof(a));
+ vec2 b; memcpy(b, _b, sizeof(b));
+
+ float z_a = 0.;
+ float z_b = 0.;
+
+ if(vec2_len(a) < 1.) {
+ z_a = sqrtf(1. - vec2_mul_inner(a, a));
+ } else {
+ vec2_norm(a, a);
+ }
+
+ if(vec2_len(b) < 1.) {
+ z_b = sqrtf(1. - vec2_mul_inner(b, b));
+ } else {
+ vec2_norm(b, b);
+ }
+
+ vec3 a_ = {a[0], a[1], z_a};
+ vec3 b_ = {b[0], b[1], z_b};
+
+ vec3 c_;
+ vec3_mul_cross(c_, a_, b_);
+
+ float const angle = acos(vec3_mul_inner(a_, b_)) * s;
+ mat4x4_rotate(R, M, c_[0], c_[1], c_[2], angle);
+}
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..5cd6c66
--- /dev/null
+++ b/main.c
@@ -0,0 +1,290 @@
+#include <GL/glew.h>
+#include <GL/glut.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "debuggl/debuggl.h"
+#include "linmath.h/linmath.h"
+
+#include "positiongen.h"
+#include "solid.h"
+#include "stats.h"
+
+#define HAS_ARCBALL 0
+
+static struct {
+ int width;
+ int height;
+ float aspect;
+} window;
+
+static struct {
+ GLuint vbo;
+ GLuint vao;
+ int grid[2];
+} vertexbuffer;
+
+static GLuint timerquery;
+
+#if HAS_ARCBALL
+#define MAT4X4_IDENTITY {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}
+static struct dragging {
+ bool left;
+ vec2 v;
+} pointer_dragging;
+static mat4x4 view = MAT4X4_IDENTITY;
+static mat4x4 arcball = MAT4X4_IDENTITY;
+#endif
+
+static struct stats_running drawtime_stats = STATS_RUNNING_INIT;
+
+static
+int create_vertexbuffer(int rows, int cols)
+{
+ size_t const dim = 4;
+
+ debuggl_check( glGenVertexArrays(1, &vertexbuffer.vao) );
+ debuggl_check( glBindVertexArray(vertexbuffer.vao) );
+
+ debuggl_check( glGenBuffers(1, &vertexbuffer.vbo) );
+ debuggl_check( glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer.vbo) );
+
+ debuggl_check( glBufferStorage(GL_ARRAY_BUFFER,
+ sizeof(GLfloat)*dim * rows * cols,
+ NULL, 0 ) );
+
+ debuggl_check( glEnableVertexAttribArray(0) );
+ debuggl_check( glVertexAttribPointer(0, dim, GL_FLOAT, GL_FALSE, 0, 0) );
+
+ vertexbuffer.grid[0] = rows;
+ vertexbuffer.grid[1] = cols;
+
+ debuggl_check( glBindBuffer(GL_ARRAY_BUFFER, 0) );
+ debuggl_check( glBindVertexArray(0) );
+
+ return 0;
+}
+
+static
+int load_shaders(void)
+{
+ int rc;
+ (void)(0
+ || (rc= positiongen_load() )
+ || (rc= solid_load() )
+ );
+ return rc;
+}
+
+static
+int create_resources(void)
+{
+ int rc;
+ (void)(0
+ || (rc= create_vertexbuffer(1024, 1024) )
+ || (rc= load_shaders() )
+ );
+
+ if( !rc ) {
+ debuggl_check( glGenQueries(1, &timerquery) );
+ }
+
+ return rc;
+}
+
+static
+void keyboard(unsigned char key, int x, int y)
+{
+ switch(key) {
+ case 'r':
+ case 'R':
+ load_shaders();
+ glutPostRedisplay();
+ break;
+ }
+}
+
+#if HAS_ARCBALL
+static
+void pointer_button(int button, int state, int x, int y)
+{
+ if( GLUT_LEFT_BUTTON == button ) {
+ if( GLUT_UP == state ) {
+ pointer_dragging.left = false;
+
+ mat4x4_mul(view, arcball, view);
+ mat4x4_orthonormalize(view, view);
+ mat4x4_identity(arcball);
+ }
+ else {
+ pointer_dragging.left = true;
+
+ pointer_dragging.v[0] = 2.f * (float)x / window.width - 1.f;
+ pointer_dragging.v[1] = -2.f * (float)y / window.height + 1.f;
+ }
+ }
+}
+
+static
+void pointer_drag_motion(int x, int y)
+{
+ if( pointer_dragging.left ) {
+ vec2 motion_v = {
+ 2.f * (float)x / window.width - 1.f,
+ -2.f * (float)y / window.height + 1.f
+ };
+
+ mat4x4_identity(arcball);
+ mat4x4_arcball(arcball, arcball,
+ pointer_dragging.v,
+ motion_v,
+ 1 );
+ glutPostRedisplay();
+ }
+}
+#endif
+
+static
+void reshape(int w, int h)
+{
+ window.width = w;
+ window.height = h;
+ window.aspect = (float)w / (float)h;
+
+ stats_running_reset(&drawtime_stats);
+
+ glutPostRedisplay();
+}
+
+static
+void display(void)
+{
+ mat4x4 proj;
+ mat4x4_identity(proj);
+
+ float const fov = 0.5;
+ mat4x4_frustum(proj,
+ -window.aspect*fov,
+ window.aspect*fov,
+ -fov,
+ fov, 1, 5);
+
+ mat4x4 mv;
+ mat4x4_identity(mv);
+ mat4x4_translate(mv, 0, 0, -3);
+#if HAS_ARCBALL
+ mat4x4_mul(mv, mv, arcball);
+ mat4x4_mul(mv, mv, view);
+#endif
+
+ glViewport(0,0,window.width,window.height);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ positiongen_launch(
+ glutGet(GLUT_ELAPSED_TIME)*0.001,
+ vertexbuffer.vbo,
+ vertexbuffer.grid[0],
+ vertexbuffer.grid[1] );
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ float const white[4] = {1., 1., 1., 0.01};
+ glBeginQuery(GL_TIME_ELAPSED, timerquery);
+ solid_draw(
+ GL_POINTS,
+ vertexbuffer.vao,
+ vertexbuffer.grid[0] * vertexbuffer.grid[1],
+ white,
+ mv[0], proj[0]);
+ glEndQuery(GL_TIME_ELAPSED);
+
+ glutSwapBuffers();
+
+ GLuint timeresult;
+ glGetQueryObjectuiv(timerquery, GL_QUERY_RESULT, &timeresult);
+ stats_running_push(&drawtime_stats, timeresult*0.001);
+}
+
+enum {
+ win_width_inc = 64, win_height_inc = 64,
+ win_width_max = 1024, win_height_max = 1024
+};
+static int win_width = win_width_inc, win_height = win_height_inc;
+
+static
+void idle(void)
+{
+ if( 499 < stats_running_N(&drawtime_stats) ) {
+ printf("%4d x %4d: ( %7.1f +/- %7.1f )us\n",
+ win_width, win_height,
+ stats_running_mean(&drawtime_stats),
+ sqrt(stats_running_variance(&drawtime_stats)) );
+
+ win_width += win_width_inc;
+ if( win_width_max < win_width ) {
+ win_height += win_height_inc;
+ if( win_height_max < win_height ) {
+ exit(0);
+ }
+ win_width = win_width_inc;
+ }
+ glutReshapeWindow(win_width, win_height);
+ }
+ glutPostRedisplay();
+}
+
+static
+void gldebugcallback(
+ GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar *message,
+ const void *userParam)
+{
+ fprintf(stderr, "(GL) %s\n", message);
+}
+
+static
+void glinfodump(void)
+{
+ printf(
+ "OpenGL vendor: %s\n"
+ "OpenGL renderer: %s\n"
+ "OpenGL version: %s\n"
+ " GLSL version: %s\n",
+ glGetString(GL_VENDOR),
+ glGetString(GL_RENDERER),
+ glGetString(GL_VERSION),
+ glGetString(GL_SHADING_LANGUAGE_VERSION) );
+}
+
+int main(int argc, char *argv[])
+{
+ glutInit(&argc, argv);
+
+ glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
+ glutInitWindowPosition(0,0);
+ glutInitWindowSize(win_width, win_height);
+ glutCreateWindow("compute shader point overdraw benchmark");
+ if( GLEW_OK != glewInit() ) { return 1; }
+ glinfodump();
+
+ glDebugMessageCallback((GLDEBUGPROC)gldebugcallback, NULL);
+ glEnable(GL_DEBUG_OUTPUT);
+
+ if( create_resources() ) { return 2; }
+
+ glutReshapeFunc(reshape);
+ glutKeyboardFunc(keyboard);
+#if HAS_ARCBALL
+ glutMouseFunc(pointer_button);
+ glutMotionFunc(pointer_drag_motion);
+#endif
+ glutDisplayFunc(display);
+ glutIdleFunc(idle);
+
+ glutMainLoop();
+ return 0;
+}
diff --git a/mvp.vs.glsl b/mvp.vs.glsl
new file mode 100644
index 0000000..4c4d062
--- /dev/null
+++ b/mvp.vs.glsl
@@ -0,0 +1,12 @@
+#version 430
+
+layout(location = 0) in vec4 a_position;
+
+uniform mat4 u_modelview;
+uniform mat4 u_projection;
+
+void main()
+{
+ const mat4 mvp = u_projection * u_modelview;
+ gl_Position = mvp * a_position;
+}
diff --git a/positiongen.c b/positiongen.c
new file mode 100644
index 0000000..3349ff6
--- /dev/null
+++ b/positiongen.c
@@ -0,0 +1,51 @@
+#include <GL/glew.h>
+#include "positiongen.h"
+
+#include "shaderloader/shaderloader.h"
+#include "debuggl/debuggl.h"
+
+#include <stdio.h>
+
+static struct {
+ GLuint program;
+ GLint w_output;
+ GLint u_time;
+} positiongen;
+
+int positiongen_load(void)
+{
+ debuggl_check( "pre positiongen_load check" );
+
+ char const * const paths[] = { "positiongen.glsl", 0 };
+ shader_program_sources const sources[] = { {GL_COMPUTE_SHADER, paths}, {0,0} };
+
+ if( positiongen.program ) {
+ debuggl_check( glDeleteProgram(positiongen.program) );
+ positiongen.program = 0;
+ }
+
+ positiongen.program = shader_program_load_from_files(sources);
+ if( !positiongen.program ) {
+ fprintf(stderr, "%s failed\n", __func__);
+ return -2;
+ }
+
+ debuggl_check( positiongen.w_output = glGetProgramResourceIndex(positiongen.program, GL_SHADER_STORAGE_BLOCK, "w_output") );
+ debuggl_check( positiongen.u_time = glGetProgramResourceLocation(positiongen.program, GL_UNIFORM, "u_time") );
+
+ return 0;
+}
+
+int positiongen_launch(
+ float t,
+ GLuint vbo,
+ int width,
+ int height )
+{
+ debuggl_check( glUseProgram(positiongen.program) );
+ debuggl_check( glUniform1f(positiongen.u_time, t) );
+ debuggl_check( glBindBufferBase(GL_SHADER_STORAGE_BUFFER, positiongen.w_output, vbo) );
+ debuggl_check( glDispatchCompute(width/16, height/16, 1) );
+ debuggl_check( glBindBufferBase(GL_SHADER_STORAGE_BUFFER, positiongen.w_output, 0) );
+ debuggl_check( glUseProgram(0) );
+}
diff --git a/positiongen.glsl b/positiongen.glsl
new file mode 100644
index 0000000..99018b6
--- /dev/null
+++ b/positiongen.glsl
@@ -0,0 +1,24 @@
+#version 430
+
+layout( local_size_x = 16, local_size_y = 16 ) in;
+layout(std430) buffer;
+layout(binding = 0) buffer w_output {
+ vec4 data[];
+};
+
+uniform float u_time;
+
+void main()
+{
+ uvec3 GlobalSize = gl_WorkGroupSize * gl_NumWorkGroups;
+ uint GlobalInvocationIndex =
+ GlobalSize.x * GlobalSize.y * gl_GlobalInvocationID.z
+ + GlobalSize.x * gl_GlobalInvocationID.y
+ + gl_GlobalInvocationID.x;
+
+ vec2 p = 2*vec2(gl_GlobalInvocationID.xy)/GlobalSize.xy - 1;
+
+ float z = sin(10.*p.x + u_time) + sin(30.*p.y + 0.1*u_time);
+
+ data[GlobalInvocationIndex] = vec4(p, z*0.25, 1);
+}
diff --git a/positiongen.h b/positiongen.h
new file mode 100644
index 0000000..d1a30cd
--- /dev/null
+++ b/positiongen.h
@@ -0,0 +1,15 @@
+#pragma once
+#ifndef POSITIONGEN_H
+#define POSITIONGEN_H
+
+#include <GL/gl.h>
+
+int positiongen_load(void);
+
+int positiongen_launch(
+ float t,
+ GLuint vbo,
+ int width,
+ int height );
+
+#endif/*POSITIONGEN_H*/
diff --git a/shaderloader/shaderloader.c b/shaderloader/shaderloader.c
new file mode 100644
index 0000000..2454316
--- /dev/null
+++ b/shaderloader/shaderloader.c
@@ -0,0 +1,287 @@
+#include "shaderloader.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#if defined(_WIN32)
+#include <windows.h>
+#define shader_gl_proc(x) wglGetProcAddress(#x)
+#elif defined(__APPLE__) && defined(__MACH__)
+#define shader_gl_proc(x) ((void(*)())x)
+#else
+#include <GL/glx.h>
+#define shader_gl_proc(x) glXGetProcAddress(#x)
+#endif
+
+static
+size_t linecount(char const * str)
+{
+ size_t count = 0;
+ for(;*str;str++) { count += ('\n' == *str); }
+ return count;
+}
+
+/* OpenGL function loading -- uses no caching, but shader loading is a
+ * seldomly executed operation and never called from time critical sections. */
+typedef void(*shader_func_ptr)();
+static
+shader_func_ptr shader_gl_proc_address(char const * const procname)
+{
+ return NULL;
+}
+
+static
+GLuint shader_glCreateShader(GLenum unit)
+{
+ GLuint r = 0;
+ shader_func_ptr const proc = shader_gl_proc(glCreateShader);
+ if( proc ) { r = ((PFNGLCREATESHADERPROC)proc)(unit); }
+ return r;
+}
+
+static
+void shader_glShaderSource(
+ GLuint shader,
+ GLsizei count,
+ GLchar const * const * const string,
+ GLint const * const lengths)
+{
+ shader_func_ptr const proc = shader_gl_proc(glShaderSource);
+ if( proc ) { ((PFNGLSHADERSOURCEPROC)proc)(
+ shader, count, string, lengths);
+ }
+}
+
+static
+void shader_glCompileShader(GLuint shader)
+{
+ shader_func_ptr const proc = shader_gl_proc(glCompileShader);
+ if( proc ) { ((PFNGLCOMPILESHADERPROC)proc)(shader); }
+}
+
+static
+void shader_glGetShaderiv(GLuint shader, GLenum pname, GLint *params)
+{
+ shader_func_ptr const proc = shader_gl_proc(glGetShaderiv);
+ if( proc ) { ((PFNGLGETSHADERIVPROC)proc)(shader, pname, params); }
+}
+
+static
+void shader_glGetShaderInfoLog(
+ GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
+{
+ shader_func_ptr const proc = shader_gl_proc(glGetShaderInfoLog);
+ if( proc ) { ((PFNGLGETSHADERINFOLOGPROC)proc)(
+ shader, bufSize, length, infoLog);
+ }
+}
+
+static
+void shader_glDeleteShader(GLuint shader)
+{
+ shader_func_ptr const proc = shader_gl_proc(glDeleteShader);
+ if( proc ) { ((PFNGLDELETESHADERPROC)proc)(shader); }
+}
+
+static
+GLuint shader_glCreateProgram()
+{
+ GLuint r = 0;
+ shader_func_ptr const proc = shader_gl_proc(glCreateProgram);
+ if( proc ) { r = ((PFNGLCREATEPROGRAMPROC)proc)(); }
+ return r;
+}
+
+static
+void shader_glAttachShader(GLuint program, GLuint shader)
+{
+ shader_func_ptr const proc = shader_gl_proc(glAttachShader);
+ if( proc ) { ((PFNGLATTACHSHADERPROC)proc)(program, shader); }
+}
+
+static
+void shader_glLinkProgram(GLuint program)
+{
+ shader_func_ptr const proc = shader_gl_proc(glLinkProgram);
+ if( proc ) { ((PFNGLLINKPROGRAMPROC)proc)(program); }
+}
+
+static
+void shader_glGetProgramiv(GLuint program, GLenum pname, GLint *params)
+{
+ shader_func_ptr const proc = shader_gl_proc(glGetProgramiv);
+ if( proc ) { ((PFNGLGETPROGRAMIVPROC)proc)(program, pname, params); }
+}
+
+static
+void shader_glGetProgramInfoLog(
+ GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
+{
+ shader_func_ptr const proc = shader_gl_proc(glGetProgramInfoLog);
+ if( proc ) { ((PFNGLGETSHADERINFOLOGPROC)proc)(
+ program, bufSize, length, infoLog);
+ }
+}
+
+static
+void shader_glDeleteProgram(GLuint program)
+{
+ shader_func_ptr const proc = shader_gl_proc(glDeleteProgram);
+ if( proc ) { ((PFNGLDELETEPROGRAMPROC)proc)(program); }
+}
+
+/* shader loader functions */
+
+GLuint shader_load_from_files(
+ GLenum shader_unit,
+ char const * const * const filepaths)
+{
+ size_t i;
+ size_t n_sources_allocated = 0;
+ GLchar const ** sources = NULL;
+ GLuint * lengths = NULL;
+ GLint shader_status = GL_FALSE;
+ GLuint shader = 0;
+
+ shader = shader_glCreateShader(shader_unit);
+ if(!shader) { goto failed; }
+
+ int filecount = 0;
+ while( filepaths[filecount] ) { filecount++; }
+
+ sources = malloc(sizeof(char*)*(filecount+1));
+ if(!sources) { goto failed; }
+ sources[filecount] = 0;
+
+ lengths = malloc(sizeof(GLuint)*(filecount+1));
+ if(!lengths) { goto failed; }
+ lengths[filecount] = 0;
+
+ for(i = 0; i < filecount; i++) {
+ size_t length = 0;
+ size_t read_length = 0;
+ GLchar *sourcestring = NULL;
+ FILE *fil = fopen(filepaths[i], "r");
+ if(!fil) {
+ fprintf(stderr,
+ "could not open file '%s'\n",
+ filepaths[i] );
+ goto failed;
+ }
+
+ fseek(fil, 0, SEEK_END);
+ length = ftell(fil);
+ lengths[i] = length;
+ rewind(fil);
+
+ sourcestring = malloc(length);
+ if( !sourcestring ) { goto failed; }
+ sources[i] = sourcestring;
+ n_sources_allocated = i + 1;
+
+ read_length = fread(sourcestring, 1, length, fil);
+ fclose(fil);
+ if( length != read_length ) { goto failed; }
+ }
+
+
+ shader_glShaderSource(shader, 1, sources, lengths);
+
+ shader_glCompileShader(shader);
+
+ shader_glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_status);
+ if( shader_status == GL_FALSE ) {
+ GLint log_length, returned_length;
+ shader_glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+ char *shader_infolog = malloc(log_length);
+ if( shader_infolog ) {
+ shader_glGetShaderInfoLog(
+ shader,
+ log_length,
+ &returned_length,
+ shader_infolog );
+ fprintf(stderr, "shader compilation failed; sources:\n");
+ for(int i = 0; i < filecount; i++) {
+ fprintf(stderr, " %.2d: %s\n", i, filepaths[i]);
+ }
+ fputs("compile log:\n", stderr);
+ fwrite(shader_infolog, returned_length, 1, stderr);
+ free(shader_infolog);
+ }
+ goto failed;
+ }
+
+cleanup:
+ free(lengths);
+ for(i = 0; i < n_sources_allocated; ++i) { free((void*)sources[i]); }
+ free(sources);
+
+ return shader;
+
+failed:
+ shader_glDeleteShader(shader);
+ shader = 0;
+ goto cleanup;
+}
+
+GLuint shader_program_load_from_files(
+ shader_program_sources const * const sources )
+{
+ size_t i;
+ size_t n_shaders = 0;
+ GLuint program = 0;
+ GLuint *shaders = NULL;
+ GLint linkStatus;
+
+ for( n_shaders = 0
+ ; sources[n_shaders].unit && sources[n_shaders].paths
+ ; ++n_shaders);
+ if( !n_shaders ) { goto failed; };
+ shaders = calloc(sizeof(*shaders), n_shaders);
+ if( !shaders ) { goto failed; }
+
+ program = shader_glCreateProgram();
+ if(!program) { goto failed; }
+
+ for( i = 0; i < n_shaders; ++i ) {
+ shaders[i] =
+ shader_load_from_files(
+ sources[i].unit,
+ sources[i].paths );
+ if( !shaders[i] ) { goto failed; }
+ shader_glAttachShader(program, shaders[i]);
+ }
+
+ shader_glLinkProgram(program);
+ shader_glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if( linkStatus == GL_FALSE ) {
+ GLint log_length, returned_length;
+ shader_glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+ char *program_infolog= malloc(log_length);
+ if( program_infolog ) {
+ shader_glGetProgramInfoLog(
+ program,
+ log_length,
+ &returned_length,
+ program_infolog);
+ fwrite(program_infolog, returned_length, 1, stderr);
+ free( program_infolog );
+ }
+ }
+
+cleanup:
+ /* shaders will get actually deleted when program gets deleted */
+ for( i = 0; i < n_shaders; ++i ) {
+ if( shaders[i] ) {
+ shader_glDeleteShader(shaders[i]);
+ }
+ }
+ free(shaders);
+
+ return program;
+
+failed:
+ shader_glDeleteProgram(program);
+ program = 0;
+ goto cleanup;
+}
diff --git a/shaderloader/shaderloader.h b/shaderloader/shaderloader.h
new file mode 100644
index 0000000..b57fa8a
--- /dev/null
+++ b/shaderloader/shaderloader.h
@@ -0,0 +1,22 @@
+#pragma once
+#ifndef SHADERLOADER_H
+#define SHADERLOADER_H
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+#include <GL/gl.h>
+
+GLuint shader_load_from_files(
+ GLenum shader_unit,
+ char const * const * const filepaths );
+
+typedef struct shader_program_sources {
+ GLenum unit;
+ char const * const * paths;
+} shader_program_sources;
+
+GLuint shader_program_load_from_files(
+ shader_program_sources const * const sources );
+
+#endif/*SHADERLOADER_H*/
diff --git a/solid.c b/solid.c
new file mode 100644
index 0000000..982e24a
--- /dev/null
+++ b/solid.c
@@ -0,0 +1,64 @@
+#include <GL/glew.h>
+#include "solid.h"
+
+#include "shaderloader/shaderloader.h"
+#include "debuggl/debuggl.h"
+
+#include <stdio.h>
+
+static struct {
+ GLint program;
+ GLint u_modelview;
+ GLint u_projection;
+ GLint u_color;
+} solid;
+
+int solid_load(void)
+{
+ debuggl_check( "pre solid_load check" );
+
+ char const * const paths_vs[] = { "mvp.vs.glsl", 0 };
+ char const * const paths_fs[] = { "solid.fs.glsl", 0 };
+ shader_program_sources const sources[] = {
+ {GL_VERTEX_SHADER, paths_vs},
+ {GL_FRAGMENT_SHADER, paths_fs},
+ {0,0}
+ };
+
+ if( solid.program ) {
+ debuggl_check( glDeleteProgram(solid.program) );
+ solid.program = 0;
+ }
+
+ solid.program = shader_program_load_from_files(sources);
+ if( !solid.program ) {
+ fprintf(stderr, "%s failed\n", __func__);
+ return -2;
+ }
+
+ debuggl_check( solid.u_modelview = glGetProgramResourceLocation(solid.program, GL_UNIFORM, "u_modelview") );
+ debuggl_check( solid.u_projection = glGetProgramResourceLocation(solid.program, GL_UNIFORM, "u_projection") );
+ debuggl_check( solid.u_color = glGetProgramResourceLocation(solid.program, GL_UNIFORM, "u_color") );
+
+ return 0;
+}
+
+int solid_draw(
+ GLenum primitive,
+ GLuint vao,
+ GLsizei count,
+ GLfloat const * const color,
+ GLfloat const * const modelview,
+ GLfloat const * const projection )
+{
+ debuggl_check( (void)"pre solid_draw check" );
+
+ debuggl_check( glBindVertexArray(vao) );
+ debuggl_check( glUseProgram(solid.program) );
+ debuggl_check( glUniform4fv(solid.u_color, 1, color) );
+ debuggl_check( glUniformMatrix4fv(solid.u_modelview, 1, GL_FALSE, modelview) );
+ debuggl_check( glUniformMatrix4fv(solid.u_projection, 1, GL_FALSE, projection) );
+ debuggl_check( glDrawArrays(primitive, 0, count) );
+ debuggl_check( glUseProgram(0) );
+ debuggl_check( glBindVertexArray(0) );
+}
diff --git a/solid.fs.glsl b/solid.fs.glsl
new file mode 100644
index 0000000..397cd33
--- /dev/null
+++ b/solid.fs.glsl
@@ -0,0 +1,9 @@
+#version 430
+
+uniform vec4 u_color;
+out vec4 o_fragcolor;
+
+void main()
+{
+ o_fragcolor = u_color;
+}
diff --git a/solid.h b/solid.h
new file mode 100644
index 0000000..c89aeba
--- /dev/null
+++ b/solid.h
@@ -0,0 +1,17 @@
+#pragma once
+#ifndef SOLID_H
+#define SOLID_H
+
+#include <GL/gl.h>
+
+int solid_load(void);
+
+int solid_draw(
+ GLenum primitive,
+ GLuint vao,
+ GLsizei count,
+ GLfloat const * const color,
+ GLfloat const * const modelview,
+ GLfloat const * const projection );
+
+#endif/*POSITIONGEN_H*/
diff --git a/stats.c b/stats.c
new file mode 100644
index 0000000..1cf7fcb
--- /dev/null
+++ b/stats.c
@@ -0,0 +1,25 @@
+#include "stats.h"
+
+void stats_running_reset(struct stats_running *s)
+{
+ if( s ) {
+ s->n = 0;
+ s->S = NAN;
+ s->m = NAN;
+ }
+}
+
+void stats_running_push(struct stats_running *s, double value)
+{
+ if( s && isfinite(value) ) {
+ double const m_prev = 0 < s->n ? s->m : 0.;
+ double const S_prev = 1 < s->n ? s->S : 0.;
+ unsigned const n = (s->n += 1);
+
+ s->m = m_prev + (value - m_prev) / n;
+ if( 1 < n ) {
+ /* variance is defined only for n > 1 */
+ s->S = S_prev + (value - s->m) * (value - m_prev);
+ }
+ }
+}
diff --git a/stats.h b/stats.h
new file mode 100644
index 0000000..8796161
--- /dev/null
+++ b/stats.h
@@ -0,0 +1,35 @@
+#pragma once
+#ifndef STATS_H
+#define STATS_H
+
+#include <math.h>
+
+struct stats_running {
+ unsigned n;
+ double S;
+ double m;
+};
+#define STATS_RUNNING_INIT {0, NAN, NAN}
+
+void stats_running_reset(struct stats_running *s);
+void stats_running_push(struct stats_running *s, double value);
+
+static inline
+unsigned stats_running_N(struct stats_running const *s)
+{
+ return s ? s->n : 0;
+}
+
+static inline
+double stats_running_mean(struct stats_running const *s)
+{
+ return s && (0 < s->n) ? s->m : NAN;
+}
+
+static inline
+double stats_running_variance(struct stats_running const *s)
+{
+ return s && (1 < s->n) ? s->S/s->n : NAN;
+}
+
+#endif/*STATS_H*/