From a00fb682a7e3552ab4f5ee0a161d0e21d17d6a26 Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Sun, 24 Apr 2016 23:33:05 +0200 Subject: computeshader --- samples/OpenGL/compute_shader/.syntastic_c_config | 1 + samples/OpenGL/compute_shader/Makefile | 19 ++ samples/OpenGL/compute_shader/main.c | 275 ++++++++++++++++++++++ samples/OpenGL/compute_shader/mvp.vs.glsl | 12 + samples/OpenGL/compute_shader/phong.fs.glsl | 0 samples/OpenGL/compute_shader/phong.vs.glsl | 0 samples/OpenGL/compute_shader/positiongen.c | 51 ++++ samples/OpenGL/compute_shader/positiongen.glsl | 24 ++ samples/OpenGL/compute_shader/positiongen.h | 15 ++ samples/OpenGL/compute_shader/solid.c | 64 +++++ samples/OpenGL/compute_shader/solid.fs.glsl | 9 + samples/OpenGL/compute_shader/solid.h | 17 ++ samples/OpenGL/compute_shader/stats.c | 25 ++ samples/OpenGL/compute_shader/stats.h | 35 +++ 14 files changed, 547 insertions(+) create mode 100644 samples/OpenGL/compute_shader/.syntastic_c_config create mode 100644 samples/OpenGL/compute_shader/Makefile create mode 100644 samples/OpenGL/compute_shader/main.c create mode 100644 samples/OpenGL/compute_shader/mvp.vs.glsl create mode 100644 samples/OpenGL/compute_shader/phong.fs.glsl create mode 100644 samples/OpenGL/compute_shader/phong.vs.glsl create mode 100644 samples/OpenGL/compute_shader/positiongen.c create mode 100644 samples/OpenGL/compute_shader/positiongen.glsl create mode 100644 samples/OpenGL/compute_shader/positiongen.h create mode 100644 samples/OpenGL/compute_shader/solid.c create mode 100644 samples/OpenGL/compute_shader/solid.fs.glsl create mode 100644 samples/OpenGL/compute_shader/solid.h create mode 100644 samples/OpenGL/compute_shader/stats.c create mode 100644 samples/OpenGL/compute_shader/stats.h diff --git a/samples/OpenGL/compute_shader/.syntastic_c_config b/samples/OpenGL/compute_shader/.syntastic_c_config new file mode 100644 index 0000000..8d5751c --- /dev/null +++ b/samples/OpenGL/compute_shader/.syntastic_c_config @@ -0,0 +1 @@ +-I../../../extra diff --git a/samples/OpenGL/compute_shader/Makefile b/samples/OpenGL/compute_shader/Makefile new file mode 100644 index 0000000..f6191f1 --- /dev/null +++ b/samples/OpenGL/compute_shader/Makefile @@ -0,0 +1,19 @@ +OBJS = main.o positiongen.o solid.o stats.o shaderloader.o debuggl.o +CFLAGS = -std=c99 -I../../../extra +LDLIBS = -lGL -lGLEW -lglut -lm + +.PHONY: all clean + +all: computeshader + +clean: + -rm -f $(OBJS) computeshader + +computeshader: $(OBJS) + $(CC) $(LDFLAGS) -o computeshader $(OBJS) $(LOADLIBES) $(LDLIBS) + +shaderloader.o: ../../../extra/shaderloader/shaderloader.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< + +debuggl.o: ../../../extra/debuggl/debuggl.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< diff --git a/samples/OpenGL/compute_shader/main.c b/samples/OpenGL/compute_shader/main.c new file mode 100644 index 0000000..c6acdc3 --- /dev/null +++ b/samples/OpenGL/compute_shader/main.c @@ -0,0 +1,275 @@ +#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); +} + +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"); + if( GLEW_OK != glewInit() ) { return 1; } + + 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/samples/OpenGL/compute_shader/mvp.vs.glsl b/samples/OpenGL/compute_shader/mvp.vs.glsl new file mode 100644 index 0000000..4c4d062 --- /dev/null +++ b/samples/OpenGL/compute_shader/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/samples/OpenGL/compute_shader/phong.fs.glsl b/samples/OpenGL/compute_shader/phong.fs.glsl new file mode 100644 index 0000000..e69de29 diff --git a/samples/OpenGL/compute_shader/phong.vs.glsl b/samples/OpenGL/compute_shader/phong.vs.glsl new file mode 100644 index 0000000..e69de29 diff --git a/samples/OpenGL/compute_shader/positiongen.c b/samples/OpenGL/compute_shader/positiongen.c new file mode 100644 index 0000000..3349ff6 --- /dev/null +++ b/samples/OpenGL/compute_shader/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/samples/OpenGL/compute_shader/positiongen.glsl b/samples/OpenGL/compute_shader/positiongen.glsl new file mode 100644 index 0000000..99018b6 --- /dev/null +++ b/samples/OpenGL/compute_shader/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/samples/OpenGL/compute_shader/positiongen.h b/samples/OpenGL/compute_shader/positiongen.h new file mode 100644 index 0000000..d1a30cd --- /dev/null +++ b/samples/OpenGL/compute_shader/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/samples/OpenGL/compute_shader/solid.c b/samples/OpenGL/compute_shader/solid.c new file mode 100644 index 0000000..982e24a --- /dev/null +++ b/samples/OpenGL/compute_shader/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/samples/OpenGL/compute_shader/solid.fs.glsl b/samples/OpenGL/compute_shader/solid.fs.glsl new file mode 100644 index 0000000..397cd33 --- /dev/null +++ b/samples/OpenGL/compute_shader/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/samples/OpenGL/compute_shader/solid.h b/samples/OpenGL/compute_shader/solid.h new file mode 100644 index 0000000..c89aeba --- /dev/null +++ b/samples/OpenGL/compute_shader/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/samples/OpenGL/compute_shader/stats.c b/samples/OpenGL/compute_shader/stats.c new file mode 100644 index 0000000..1cf7fcb --- /dev/null +++ b/samples/OpenGL/compute_shader/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/samples/OpenGL/compute_shader/stats.h b/samples/OpenGL/compute_shader/stats.h new file mode 100644 index 0000000..8796161 --- /dev/null +++ b/samples/OpenGL/compute_shader/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