From ffa662ec5acdaae36f0ffeaf0cee78a4200d897b Mon Sep 17 00:00:00 2001
From: Adrian Kummerlaender
Date: Mon, 21 May 2018 13:04:41 +0200
Subject: Introduce basic shader, texture buffer abstraction

---
 src/compute_shader.h    |  41 +++++++++
 src/graphic_shader.h    |  44 ++++++++++
 src/main.cc             | 229 ++++++++++++++++--------------------------------
 src/shader/compute.glsl |   2 +-
 src/texture_buffer.h    |  53 +++++++++++
 src/util.h              |  44 ++++++++++
 6 files changed, 258 insertions(+), 155 deletions(-)
 create mode 100644 src/compute_shader.h
 create mode 100644 src/graphic_shader.h
 create mode 100644 src/texture_buffer.h
 create mode 100644 src/util.h

diff --git a/src/compute_shader.h b/src/compute_shader.h
new file mode 100644
index 0000000..298a454
--- /dev/null
+++ b/src/compute_shader.h
@@ -0,0 +1,41 @@
+#include "util.h"
+
+class ComputeShader {
+private:
+	const GLuint _id;
+
+public:
+	struct Guard {
+		const GLuint _id;
+
+		Guard(GLuint id): _id(id) {
+			glUseProgram(_id);
+		}
+		~Guard() {
+			glUseProgram(0);
+		}
+	};
+
+	Guard use() {
+		return Guard(_id);
+	}
+
+	ComputeShader(const std::string& src):
+		_id(glCreateProgram()) {
+		glAttachShader(_id, util::compileShader(src, GL_COMPUTE_SHADER));
+		glLinkProgram(_id);
+	};
+	~ComputeShader() {
+		glDeleteProgram(_id);
+	}
+
+	GLuint setUniform(const std::string& name, float x, float y) {
+		GLuint id = util::getUniform(_id, name);
+		glUniform2f(id, x, y);
+		return id;
+	}
+
+	void dispatch(std::size_t dimX) {
+		glDispatchCompute(dimX, 1, 1);
+	}
+};
diff --git a/src/graphic_shader.h b/src/graphic_shader.h
new file mode 100644
index 0000000..2dd8c41
--- /dev/null
+++ b/src/graphic_shader.h
@@ -0,0 +1,44 @@
+#include "util.h"
+
+class GraphicShader {
+private:
+	const GLuint _id;
+
+public:
+	struct Guard {
+		const GLuint _id;
+
+		Guard(GLuint id): _id(id) {
+			glUseProgram(_id);
+		}
+		~Guard() {
+			glUseProgram(0);
+		}
+	};
+
+	Guard use() {
+		return Guard(_id);
+	}
+
+	GraphicShader(const std::string& vertex, const std::string fragment):
+		_id(glCreateProgram()) {
+		glAttachShader(_id, util::compileShader(vertex, GL_VERTEX_SHADER));
+		glAttachShader(_id, util::compileShader(fragment, GL_FRAGMENT_SHADER));
+		glLinkProgram(_id);
+	};
+	~GraphicShader() {
+		glDeleteProgram(_id);
+	}
+
+	GLuint setUniform(const std::string& name, int value) {
+		GLuint id = util::getUniform(_id, name);
+		glUniform1i(id, value);
+		return id;
+	}
+
+	GLuint setUniform(const std::string& name, glm::mat4& M) {
+		GLuint id = util::getUniform(_id, name);
+		glUniformMatrix4fv(id, 1, GL_FALSE, &M[0][0]);
+		return id;
+	}
+};
diff --git a/src/main.cc b/src/main.cc
index 8a1d6de..c6945fb 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -8,6 +8,11 @@
 #include <iostream>
 #include <random>
 #include <chrono>
+#include <memory>
+
+#include "graphic_shader.h"
+#include "compute_shader.h"
+#include "texture_buffer.h"
 
 #include "shader/vertex.glsl"
 #include "shader/fragment.glsl"
@@ -15,92 +20,11 @@
 
 const std::size_t particle_count = 100000;
 
-GLint getUniform(GLuint program, const std::string& name) {
-	const GLint uniform = glGetUniformLocation(program, name.c_str());
-	if ( uniform == -1 ) {
-		std::cerr << "Could not bind uniform " << name << std::endl;
-	}
-	return uniform;
-}
-
-GLint compileShader(const std::string& source, GLenum type) {
-	GLint shader = glCreateShader(type);
-
-	if ( !shader ) {
-		std::cerr << "Cannot create a shader of type " << type << std::endl;
-		exit(-1);
-	}
-
-	const char* source_data = source.c_str();
-	const int source_length = source.size();
-
-	glShaderSource(shader, 1, &source_data, &source_length);
-	glCompileShader(shader);
-
-	GLint compiled;
-	glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
-	if ( !compiled ) {
-		std::cerr << "Cannot compile shader" << std::endl;
-		GLint maxLength = 0;
-		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
-		std::vector<GLchar> errorLog(maxLength);
-		glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);
-		for( auto c : errorLog ) {
-			std::cerr << c;
-		}
-		std::cerr << std::endl;
-	}
-
-	return shader;
-}
-
-GLint setupShader() {
-	GLint shader = glCreateProgram();
-	glAttachShader(shader, compileShader(VERTEX_SHADER_CODE, GL_VERTEX_SHADER));
-	glAttachShader(shader, compileShader(FRAGMENT_SHADER_CODE, GL_FRAGMENT_SHADER));
-	glLinkProgram(shader);
-	return shader;
-}
-
-GLint setupComputeShader() {
-	GLint shader = glCreateProgram();
-	glAttachShader(shader, compileShader(COMPUTE_SHADER_CODE, GL_COMPUTE_SHADER));
-	glLinkProgram(shader);
-	return shader;
-}
-
-GLint setupTextureShader() {
-	GLint shader = glCreateProgram();
-	glAttachShader(shader, compileShader(R"(
-		#version 330 core
-		layout (location = 0) in vec2 screen_vertex;
-		layout (location = 1) in vec2 texture_vertex;
-		out vec2 TexCoords;
-
-		void main() {
-			gl_Position = vec4(screen_vertex, 0.0, 1.0);
-			TexCoords = texture_vertex;
-		}
-	)", GL_VERTEX_SHADER));
-	glAttachShader(shader, compileShader(R"(
-		#version 330 core
-		out vec4 FragColor;
-		in vec2 TexCoords;
-		uniform sampler2D screen_texture;
-
-		void main() {
-			FragColor = texture(screen_texture, TexCoords);
-		}
-	)", GL_FRAGMENT_SHADER));
-	glLinkProgram(shader);
-	return shader;
-}
-
 int window_width  = 800;
 int window_height = 600;
 float world_width, world_height;
 glm::mat4 MVP;
-GLuint FramebufferID;
+std::unique_ptr<TextureBuffer> textureBuffer;
 
 void updateMVP() {
 	world_width  = 20.f;
@@ -123,10 +47,7 @@ void window_size_callback(GLFWwindow*, int width, int height) {
 	window_width  = width;
 	window_height = height;
 
-	glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID);
-
-	glViewport(0, 0, width, height);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)0);
+	textureBuffer->resize(width, height);
 
 	updateMVP();
 }
@@ -178,51 +99,43 @@ int main() {
 
 	glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
 
-	glGenFramebuffers(1, &FramebufferID);
-	glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID);
-
-	GLuint TextureID;
-	glGenTextures(1, &TextureID);
-	glBindTexture(GL_TEXTURE_2D, TextureID);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)0);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, TextureID, 0);
+	updateMVP();
 
-	if ( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) {
-		std::cerr << "Texture framebuffer error" << std::endl;
-		return -1;
-	}
+	textureBuffer = std::make_unique<TextureBuffer>(window_width, window_height);
 
-	glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
+	{
+		auto guard = textureBuffer->use();
 
-	updateMVP();
+		glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
+	}
 
-	GLint ShaderID  = setupShader();
-	GLuint MatrixID = glGetUniformLocation(ShaderID, "MVP");
+	GraphicShader sceneShader(VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE);
+	sceneShader.setUniform("MVP", MVP);
 
-	GLint ComputeShaderID = setupComputeShader();
-	GLuint WorldID = glGetUniformLocation(ComputeShaderID, "world");
+	ComputeShader computeShader(COMPUTE_SHADER_CODE);
 
 	auto vertex_buffer_data = makeInitialParticles(particle_count);
 
 	GLuint VertexArrayID;
-	glGenVertexArrays(1, &VertexArrayID);
-	glBindVertexArray(VertexArrayID);
-	glEnableVertexAttribArray(0);
-
 	GLuint VertexBufferID;
-	glGenBuffers(1, &VertexBufferID);
-	glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, VertexBufferID);
 
-	glBufferData(
-		GL_SHADER_STORAGE_BUFFER,
-		vertex_buffer_data.size() * sizeof(GLfloat),
-		vertex_buffer_data.data(),
-		GL_STATIC_DRAW
-	);
+	{
+		auto guard = textureBuffer->use();
 
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+		glGenVertexArrays(1, &VertexArrayID);
+		glBindVertexArray(VertexArrayID);
+		glEnableVertexAttribArray(0);
+
+		glGenBuffers(1, &VertexBufferID);
+		glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, VertexBufferID);
+
+		glBufferData(
+			GL_SHADER_STORAGE_BUFFER,
+			vertex_buffer_data.size() * sizeof(GLfloat),
+			vertex_buffer_data.data(),
+			GL_STATIC_DRAW
+		);
+	}
 
 	GLuint QuadVertexArrayID;
 	glGenVertexArrays(1, &QuadVertexArrayID);
@@ -230,7 +143,7 @@ int main() {
 	GLuint QuadVertexBufferID;
 	glGenBuffers(1, &QuadVertexBufferID);
 	glBindBuffer(GL_ARRAY_BUFFER, QuadVertexBufferID);
-	std::vector<GLfloat> quad_buffer_data{
+	const std::vector<GLfloat> quad_buffer_data{
 		-1.f,  1.f, 0.f, 1.f,
 		-1.f, -1.f, 0.f, 0.f,
 		 1.f, -1.f, 1.f, 0.f,
@@ -246,56 +159,67 @@ int main() {
 		GL_STATIC_DRAW
 	);
 	glEnableVertexAttribArray(0);
-	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)0);
 	glEnableVertexAttribArray(1);
-	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat)));
 
-	GLuint TextureShaderID = setupTextureShader();
-	GLuint ScreenTextureID = glGetUniformLocation(TextureShaderID, "screen_texture");
-	glUniform1i(ScreenTextureID, 0);
+	GraphicShader displayShader(R"(
+		#version 330 core
+		layout (location = 0) in vec2 screen_vertex;
+		layout (location = 1) in vec2 texture_vertex;
+		out vec2 TexCoords;
+
+		void main() {
+			gl_Position = vec4(screen_vertex, 0.0, 1.0);
+			TexCoords = texture_vertex;
+		}
+		)", R"(
+		#version 330 core
+		out vec4 FragColor;
+		in vec2 TexCoords;
+		uniform sampler2D screen_texture;
+
+		void main() {
+			FragColor = texture(screen_texture, TexCoords);
+		}
+		)");
+	displayShader.setUniform("screen_texture", 0);
 
 	auto lastFrame = std::chrono::high_resolution_clock::now();
 
 	do {
-		glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID);
-
 		// update particles at most 50 times per second
 		if ( std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - lastFrame).count() >= 20 ) {
-			glUseProgram(ComputeShaderID);
+			auto guard = computeShader.use();
 
-			glUniform2f(WorldID, world_width, world_height);
-			glDispatchCompute(particle_count, 1, 1);
-
-			glUseProgram(0);
+			computeShader.setUniform("world", world_width, world_height);
+			computeShader.dispatch(particle_count);
 
 			lastFrame = std::chrono::high_resolution_clock::now();
 		}
 
-		glUseProgram(ShaderID);
-
-		glClear(GL_COLOR_BUFFER_BIT);
+		{
+			auto texGuard = textureBuffer->use();
+			auto sdrGuard = sceneShader.use();
 
-		glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
+			glClear(GL_COLOR_BUFFER_BIT);
 
-		glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
-		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
-		glDrawArrays(GL_POINTS, 0, 3*particle_count);
+			sceneShader.setUniform("MVP", MVP);
 
-		glUseProgram(0);
-
-		glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
-		glUseProgram(TextureShaderID);
+			glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
+			glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
+			glDrawArrays(GL_POINTS, 0, 3*particle_count);
+		}
 
-		glBindTexture(GL_TEXTURE_2D, TextureID);
-		glBindBuffer(GL_ARRAY_BUFFER, QuadVertexBufferID);
-		glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)0);
-		glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat)));
+		{
+			auto guard = displayShader.use();
 
-		glClear(GL_COLOR_BUFFER_BIT);
-		glDrawArrays(GL_TRIANGLES, 0, 6);
+			glBindTexture(GL_TEXTURE_2D, textureBuffer->getTexture());
+			glBindBuffer(GL_ARRAY_BUFFER, QuadVertexBufferID);
+			glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)0);
+			glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat)));
 
-		glUseProgram(0);
+			glClear(GL_COLOR_BUFFER_BIT);
+			glDrawArrays(GL_TRIANGLES, 0, 6);
+		}
 
 		glfwSwapBuffers(window);
 		glfwPollEvents();
@@ -306,9 +230,6 @@ int main() {
 	glDeleteBuffers(1, &VertexBufferID);
 	glDisableVertexAttribArray(0);
 	glDeleteVertexArrays(1, &VertexArrayID);
-	glDeleteProgram(ShaderID);
-	glDeleteProgram(ComputeShaderID);
-	glDeleteFramebuffers(1, &FramebufferID);
 
 	glfwTerminate();
 
diff --git a/src/shader/compute.glsl b/src/shader/compute.glsl
index 8b1deab..58bf08b 100644
--- a/src/shader/compute.glsl
+++ b/src/shader/compute.glsl
@@ -22,7 +22,7 @@ void main() {
 	vec2 v = vec2(data[idx+0], data[idx+1]);
 
 	if ( data[idx+2] < 5. && insideWorld(v) ) {
-		data[idx+0] += 0.01 * cos(v.x*cos(v.y));
+		data[idx+0] += 0.01 * cos(v.x*sin(v.y));
 		data[idx+1] += 0.01 * sin(v.x-v.y);
 		data[idx+2] += 0.01;
 	} else {
diff --git a/src/texture_buffer.h b/src/texture_buffer.h
new file mode 100644
index 0000000..851833c
--- /dev/null
+++ b/src/texture_buffer.h
@@ -0,0 +1,53 @@
+class TextureBuffer {
+private:
+	GLuint _id;
+	GLuint _texture;
+
+public:
+	struct Guard {
+		const GLuint _id;
+
+		Guard(GLuint id): _id(id) {
+			glBindFramebuffer(GL_FRAMEBUFFER, _id);
+		}
+		~Guard() {
+			glBindFramebuffer(GL_FRAMEBUFFER, 0);
+		}
+	};
+
+	Guard use() {
+		return Guard(_id);
+	}
+
+	TextureBuffer(std::size_t width, std::size_t height) {
+		glGenFramebuffers(1, &_id);
+
+		auto guard = use();
+
+		glGenTextures(1, &_texture);
+		glBindTexture(GL_TEXTURE_2D, _texture);
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)0);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
+
+		if ( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) {
+			std::cerr << "Texture framebuffer error" << std::endl;
+		}
+	}
+
+	~TextureBuffer() {
+		glDeleteFramebuffers(1, &_id);
+	}
+
+	void resize(std::size_t width, std::size_t height) {
+		auto guard = use();
+
+		glViewport(0, 0, width, height);
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*)0);
+	}
+
+	GLuint getTexture() const {
+		return _texture;
+	}
+};
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..ce8d012
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,44 @@
+#pragma once
+
+namespace util {
+
+GLint getUniform(GLuint program, const std::string& name) {
+	const GLint uniform = glGetUniformLocation(program, name.c_str());
+	if ( uniform == -1 ) {
+		std::cerr << "Could not bind uniform " << name << std::endl;
+	}
+	return uniform;
+}
+
+GLint compileShader(const std::string& source, GLenum type) {
+	GLint shader = glCreateShader(type);
+
+	if ( !shader ) {
+		std::cerr << "Cannot create a shader of type " << type << std::endl;
+		exit(-1);
+	}
+
+	const char* source_data = source.c_str();
+	const int source_length = source.size();
+
+	glShaderSource(shader, 1, &source_data, &source_length);
+	glCompileShader(shader);
+
+	GLint compiled;
+	glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+	if ( !compiled ) {
+		std::cerr << "Cannot compile shader" << std::endl;
+		GLint maxLength = 0;
+		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
+		std::vector<GLchar> errorLog(maxLength);
+		glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);
+		for( auto c : errorLog ) {
+			std::cerr << c;
+		}
+		std::cerr << std::endl;
+	}
+
+	return shader;
+}
+
+}
-- 
cgit v1.2.3