From 2ca04fbe7985ee944f3fa6302886a252a51add0c Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Sun, 24 Apr 2016 23:52:45 +0200 Subject: initial commit --- Makefile | 17 ++ debuggl/debuggl.c | 67 +++++++ debuggl/debuggl.h | 29 +++ linmath.h/LICENCE | 13 ++ linmath.h/README | 12 ++ linmath.h/linmath.h | 474 ++++++++++++++++++++++++++++++++++++++++++++ main.c | 290 +++++++++++++++++++++++++++ mvp.vs.glsl | 12 ++ positiongen.c | 51 +++++ positiongen.glsl | 24 +++ positiongen.h | 15 ++ shaderloader/shaderloader.c | 287 +++++++++++++++++++++++++++ shaderloader/shaderloader.h | 22 ++ solid.c | 64 ++++++ solid.fs.glsl | 9 + solid.h | 17 ++ stats.c | 25 +++ stats.h | 35 ++++ 18 files changed, 1463 insertions(+) create mode 100644 Makefile create mode 100644 debuggl/debuggl.c create mode 100644 debuggl/debuggl.h create mode 100644 linmath.h/LICENCE create mode 100644 linmath.h/README create mode 100644 linmath.h/linmath.h create mode 100644 main.c create mode 100644 mvp.vs.glsl create mode 100644 positiongen.c create mode 100644 positiongen.glsl create mode 100644 positiongen.h create mode 100644 shaderloader/shaderloader.c create mode 100644 shaderloader/shaderloader.h create mode 100644 solid.c create mode 100644 solid.fs.glsl create mode 100644 solid.h create mode 100644 stats.c create mode 100644 stats.h 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 +#include + +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 + + 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 +#include + +#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 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 +#include +#include +#include + +#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 +#include "positiongen.h" + +#include "shaderloader/shaderloader.h" +#include "debuggl/debuggl.h" + +#include + +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 + +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 +#include + +#if defined(_WIN32) +#include +#define shader_gl_proc(x) wglGetProcAddress(#x) +#elif defined(__APPLE__) && defined(__MACH__) +#define shader_gl_proc(x) ((void(*)())x) +#else +#include +#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 +#endif +#include + +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 +#include "solid.h" + +#include "shaderloader/shaderloader.h" +#include "debuggl/debuggl.h" + +#include + +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 + +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 + +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*/ -- cgit v1.2.3