diff --git a/res/shader/compute.glsl b/res/shader/compute.glsl new file mode 100644 index 0000000..38a7a60 --- /dev/null +++ b/res/shader/compute.glsl @@ -0,0 +1,23 @@ +#version 430 + +layout (location = 1) uniform float t; + +// size of local work group - 1 pixel +layout(local_size_x = 1, local_size_y = 1) in; + +// rgba32f defines internal format, image2d for random write to output texture +layout(rgba32f, binding = 0) uniform image2D img_output; + + +void main() +{ + // base pixel colour for the image + vec4 pixel = vec4(1.0, t, 0.0, 1.0); + // get index in global work group ie xy position + ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy); + + // + + // output to a specific pixel in the image + imageStore(img_output, pixel_coords, pixel); +} diff --git a/src/gfx.c b/src/gfx.c index 9cbfba1..dbb3ec8 100644 --- a/src/gfx.c +++ b/src/gfx.c @@ -1,9 +1,6 @@ #include "gfx.h" #include "stb_image.h" -const char* vertShaderPath = "res/shader/shader.vert"; -const char* fragShaderPath = "res/shader/shader.frag"; - float vertices[] = { // position color uvs 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right @@ -22,7 +19,7 @@ SDL_GLContext* sdlContext; GLuint compileShader(const char* path, GLenum type); -SDL_Window* gfxInit() +SDL_Window* gfxInit(int width, int height) { // load sdl modules if (SDL_Init(SDL_INIT_VIDEO) != 0) @@ -40,8 +37,8 @@ SDL_Window* gfxInit() "oglc", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - 800, - 600, + width, + height, SDL_WINDOW_OPENGL); sdlContext = SDL_GL_CreateContext(sdlWindow); @@ -58,10 +55,10 @@ SDL_Window* gfxInit() return sdlWindow; } -unsigned int compileShaderProgram() +unsigned int compileQuadShaderProgram(const char* vsPath, const char* fsPath) { - GLuint vs = compileShader(vertShaderPath, GL_VERTEX_SHADER); - GLuint fs = compileShader(fragShaderPath, GL_FRAGMENT_SHADER); + GLuint vs = compileShader(vsPath, GL_VERTEX_SHADER); + GLuint fs = compileShader(fsPath, GL_FRAGMENT_SHADER); unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vs); @@ -70,12 +67,41 @@ unsigned int compileShaderProgram() // TODO: check program linking success glLinkProgram(shaderProgram); + GLint result = GL_FALSE; + glGetProgramiv(shaderProgram, GL_LINK_STATUS, &result); + if (result == GL_FALSE) + { + GLint logLength = 0; + glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &logLength); + + char err[logLength]; + glGetProgramInfoLog(shaderProgram, logLength, NULL, err); + + fputs("error linking quad shader program:\n", stderr); + fputs(err, stderr); + + exit(1); + } + glDeleteShader(vs); glDeleteShader(fs); return shaderProgram; } +unsigned int compileComputeShaderProgram(const char* computeShaderPath) +{ + GLuint cs = compileShader(computeShaderPath, GL_COMPUTE_SHADER); + GLuint computeProgram = glCreateProgram(); + glAttachShader(computeProgram, cs); + + glLinkProgram(computeProgram); + + glDeleteShader(cs); + + return computeProgram; +} + GLuint compileShader(const char* path, GLenum type) { // read shader file into buffer @@ -129,7 +155,7 @@ void setVertexAttributes() glEnableVertexAttribArray(2); } -void createTextureFromFile() +void createTextureFromFile(const char* imagePath) { // create an opengl texture and bind it to the GL_TEXTURE_2D target unsigned int texture; @@ -141,7 +167,6 @@ void createTextureFromFile() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_REPEAT); // load image data from file - const char* imagePath = "res/tex.png"; int width, height, nrChannels; stbi_set_flip_vertically_on_load(1); unsigned char* data = stbi_load(imagePath, &width, &height, &nrChannels, 0); @@ -160,6 +185,24 @@ void createTextureFromFile() stbi_image_free(data); } +GLuint createWriteOnlyTexture(int width, int height) +{ + GLuint texture; + glGenTextures(1, &texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, NULL); + glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); + + return texture; +} + void initBuffers() { unsigned int VAO, VBO, EBO; @@ -175,3 +218,30 @@ void initBuffers() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); } + +void printWorkGroupLimits() +{ + int workGroupCount[3]; + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &workGroupCount[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &workGroupCount[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &workGroupCount[2]); + printf( + "max work group counts x:%i y:%i z:%i\n", + workGroupCount[0], + workGroupCount[1], + workGroupCount[2]); + + int workGroupSize[3]; + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &workGroupSize[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &workGroupSize[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &workGroupSize[2]); + printf( + "max work group sizes x:%i y:%i z:%i\n", + workGroupSize[0], + workGroupSize[1], + workGroupSize[2]); + + int workGroupInvocations; + glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &workGroupInvocations); + printf("max work group invocations %i\n", workGroupInvocations); +} diff --git a/src/gfx.h b/src/gfx.h index e1d66f6..ef8b305 100644 --- a/src/gfx.h +++ b/src/gfx.h @@ -3,17 +3,20 @@ #define GLEW_STATIC #include "GL/glew.h" - #include #include #include "io.h" -SDL_Window* gfxInit(); +SDL_Window* gfxInit(int width, int height); -unsigned int compileShaderProgram(); +unsigned int compileQuadShaderProgram(const char* vsPath, const char* fsPath); +unsigned int compileComputeShaderProgram(); + +void createTextureFromFile(const char* path); +GLuint createWriteOnlyTexture(int width, int height); +void printWorkGroupLimits(); -void createTextureFromFile(); void setVertexAttributes(); void initBuffers(); diff --git a/src/main.c b/src/main.c index e9a6e73..518af9f 100644 --- a/src/main.c +++ b/src/main.c @@ -12,26 +12,53 @@ float time(); int main() { - SDL_Window* window = gfxInit(); + int width = 800; + int height = 600; + const char* texPath = "res/tex.png"; - createTextureFromFile(); + // create a window and opengl context + SDL_Window* window = gfxInit(width, height); + // create a texture for the compute shader to write to + GLuint textureOutput = createWriteOnlyTexture(width, height); + printWorkGroupLimits(); + + // compile shader programs + unsigned int computeProgram = compileComputeShaderProgram("res/shader/compute.glsl"); + unsigned int quadProgram = compileQuadShaderProgram( + "res/shader/shader.vert", + "res/shader/shader.frag"); + + // initialise quad initBuffers(); - setVertexAttributes(); - unsigned int shaderProgram = compileShaderProgram(); - glUseProgram(shaderProgram); - // render loop while (!checkQuit()) { - // clear background + glUseProgram(computeProgram); + + // update uniforms + float t = time(); + t = (sin(t)/2.0f)+0.5f; + int tLocation = glGetUniformLocation(computeProgram, "t"); + glUniform1f(tLocation, t); + + // dispatch compute shader + glDispatchCompute((GLuint)width, (GLuint)height, 1); + + // make sure we're finished writing to the texture before trying to read it + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + // normal drawing pass + glUseProgram(quadProgram); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - - // draw + glActiveTexture(GL_TEXTURE0); // use computed texture + glBindTexture(GL_TEXTURE_2D, textureOutput); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + + // swip swap SDL_GL_SwapWindow(window); }