From 44f5ac32a68a617f93704d44c4339f7db13b323e Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Sat, 15 Dec 2018 23:09:32 +0100 Subject: Hacky D2Q9 BGK LBM on GPU using GLSL compute shaders Improvised on top of computicles's scaffolding. Works in a world where _works_ is defined as "displays stuff on screen that invokes thoughts of fluid movement". --- src/buffer/vertex/fluid_cell_buffer.cc | 33 +++++++ src/buffer/vertex/fluid_cell_buffer.h | 21 +++++ src/buffer/vertex/lattice_cell_buffer.cc | 40 ++++++++ src/buffer/vertex/lattice_cell_buffer.h | 19 ++++ src/glfw/guard.cc | 16 ++++ src/glfw/guard.h | 11 +++ src/glfw/key_watcher.cc | 24 +++++ src/glfw/key_watcher.h | 17 ++++ src/glfw/window.cc | 32 +++++++ src/glfw/window.h | 58 ++++++++++++ src/main.cc | 156 +++++++++++++++++++++++++++++++ src/shader/code/collide.glsl | 95 +++++++++++++++++++ src/shader/code/fragment.glsl | 5 + src/shader/code/stream.glsl | 30 ++++++ src/shader/code/vertex.glsl | 8 ++ src/shader/util.cc | 56 +++++++++++ src/shader/util.h | 13 +++ src/shader/wrap/compute_shader.cc | 51 ++++++++++ src/shader/wrap/compute_shader.h | 32 +++++++ src/shader/wrap/graphic_shader.cc | 45 +++++++++ src/shader/wrap/graphic_shader.h | 29 ++++++ src/timer.cc | 16 ++++ src/timer.h | 12 +++ 23 files changed, 819 insertions(+) create mode 100644 src/buffer/vertex/fluid_cell_buffer.cc create mode 100644 src/buffer/vertex/fluid_cell_buffer.h create mode 100644 src/buffer/vertex/lattice_cell_buffer.cc create mode 100644 src/buffer/vertex/lattice_cell_buffer.h create mode 100644 src/glfw/guard.cc create mode 100644 src/glfw/guard.h create mode 100644 src/glfw/key_watcher.cc create mode 100644 src/glfw/key_watcher.h create mode 100644 src/glfw/window.cc create mode 100644 src/glfw/window.h create mode 100644 src/main.cc create mode 100644 src/shader/code/collide.glsl create mode 100644 src/shader/code/fragment.glsl create mode 100644 src/shader/code/stream.glsl create mode 100644 src/shader/code/vertex.glsl create mode 100644 src/shader/util.cc create mode 100644 src/shader/util.h create mode 100644 src/shader/wrap/compute_shader.cc create mode 100644 src/shader/wrap/compute_shader.h create mode 100644 src/shader/wrap/graphic_shader.cc create mode 100644 src/shader/wrap/graphic_shader.h create mode 100644 src/timer.cc create mode 100644 src/timer.h (limited to 'src') diff --git a/src/buffer/vertex/fluid_cell_buffer.cc b/src/buffer/vertex/fluid_cell_buffer.cc new file mode 100644 index 0000000..5056569 --- /dev/null +++ b/src/buffer/vertex/fluid_cell_buffer.cc @@ -0,0 +1,33 @@ +#include "fluid_cell_buffer.h" + +FluidCellBuffer::FluidCellBuffer(): + _data(3*128*128, GLfloat{}) { + glGenVertexArrays(1, &_array); + glGenBuffers(1, &_buffer); + + glBindVertexArray(_array); + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + glBufferData( + GL_ARRAY_BUFFER, + _data.size() * sizeof(GLfloat), + _data.data(), + GL_DYNAMIC_DRAW + ); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); +} + +FluidCellBuffer::~FluidCellBuffer() { + glDeleteBuffers(1, &_buffer); + glDeleteVertexArrays(1, &_array); +} + +GLuint FluidCellBuffer::getBuffer() const { + return _buffer; +} + +void FluidCellBuffer::draw() const { + glBindVertexArray(_array); + glDrawArrays(GL_POINTS, 0, _data.size()); +} diff --git a/src/buffer/vertex/fluid_cell_buffer.h b/src/buffer/vertex/fluid_cell_buffer.h new file mode 100644 index 0000000..b2b880a --- /dev/null +++ b/src/buffer/vertex/fluid_cell_buffer.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +class FluidCellBuffer { +private: + std::vector _data; + + GLuint _array; + GLuint _buffer; + +public: + FluidCellBuffer(); + ~FluidCellBuffer(); + + GLuint getBuffer() const; + + void draw() const; +}; diff --git a/src/buffer/vertex/lattice_cell_buffer.cc b/src/buffer/vertex/lattice_cell_buffer.cc new file mode 100644 index 0000000..7fb51d8 --- /dev/null +++ b/src/buffer/vertex/lattice_cell_buffer.cc @@ -0,0 +1,40 @@ +#include "lattice_cell_buffer.h" + +#include + +LatticeCellBuffer::LatticeCellBuffer(): + _data(9*128*128, GLfloat{1./9.}) { + glGenVertexArrays(1, &_array); + glGenBuffers(1, &_buffer); + + for (int x = 50; x < 128-50; x++) { + for (int y = 50; y < 128-50; y++) { + for ( int i = -1; i <= 1; ++i ) { + for ( int j = -1; j <= 1; ++j ) { + _data[9*128*y + 9*x + (i+1)*3 + j+1] = 1./128.; + } + } + } + } + + glBindVertexArray(_array); + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + glBufferData( + GL_ARRAY_BUFFER, + _data.size() * sizeof(GLfloat), + _data.data(), + GL_DYNAMIC_DRAW + ); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, nullptr); +} + +LatticeCellBuffer::~LatticeCellBuffer() { + glDeleteBuffers(1, &_buffer); + glDeleteVertexArrays(1, &_array); +} + +GLuint LatticeCellBuffer::getBuffer() const { + return _buffer; +} diff --git a/src/buffer/vertex/lattice_cell_buffer.h b/src/buffer/vertex/lattice_cell_buffer.h new file mode 100644 index 0000000..c28319e --- /dev/null +++ b/src/buffer/vertex/lattice_cell_buffer.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +class LatticeCellBuffer { +private: + std::vector _data; + + GLuint _array; + GLuint _buffer; + +public: + LatticeCellBuffer(); + ~LatticeCellBuffer(); + + GLuint getBuffer() const; +}; diff --git a/src/glfw/guard.cc b/src/glfw/guard.cc new file mode 100644 index 0000000..5ba853f --- /dev/null +++ b/src/glfw/guard.cc @@ -0,0 +1,16 @@ +#include "guard.h" + +#include +#include + +GlfwGuard::GlfwGuard() { + _good = glfwInit(); +} + +GlfwGuard::~GlfwGuard() { + glfwTerminate(); +} + +bool GlfwGuard::isGood() const { + return _good; +} diff --git a/src/glfw/guard.h b/src/glfw/guard.h new file mode 100644 index 0000000..f68954d --- /dev/null +++ b/src/glfw/guard.h @@ -0,0 +1,11 @@ +#pragma once + +class GlfwGuard { +private: + bool _good = false; +public: + GlfwGuard(); + ~GlfwGuard(); + + bool isGood() const; +}; diff --git a/src/glfw/key_watcher.cc b/src/glfw/key_watcher.cc new file mode 100644 index 0000000..21a570a --- /dev/null +++ b/src/glfw/key_watcher.cc @@ -0,0 +1,24 @@ +#include "key_watcher.h" + +KeyWatcher::KeyWatcher(GLFWwindow* handle, int key): + _handle(handle), + _key(key), + _last_state(glfwGetKey(_handle, _key)) +{ } + +bool KeyWatcher::wasClicked() { + switch ( glfwGetKey(_handle, _key) ) { + case GLFW_RELEASE: + _last_state = GLFW_RELEASE; + return false; + case GLFW_PRESS: + if ( _last_state == GLFW_RELEASE ) { + _last_state = GLFW_PRESS; + return true; + } else { + return false; + } + default: + return false; + } +} diff --git a/src/glfw/key_watcher.h b/src/glfw/key_watcher.h new file mode 100644 index 0000000..538829f --- /dev/null +++ b/src/glfw/key_watcher.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +class KeyWatcher { +private: + GLFWwindow* const _handle; + + int _key; + int _last_state; + +public: + KeyWatcher(GLFWwindow* handle, int key); + + bool wasClicked(); + +}; diff --git a/src/glfw/window.cc b/src/glfw/window.cc new file mode 100644 index 0000000..0e57a65 --- /dev/null +++ b/src/glfw/window.cc @@ -0,0 +1,32 @@ +#include "window.h" + +Window::Window(const std::string& title): + _handle(glfwCreateWindow(_width, _height, title.c_str(), NULL, NULL)) { + if ( _handle != nullptr ) { + glfwMakeContextCurrent(_handle); + if ( glewInit() == GLEW_OK ) { + _good = true; + } + glfwMakeContextCurrent(nullptr); + } +} + +Window::~Window() { + glfwDestroyWindow(_handle); +} + +bool Window::isGood() const { + return _good; +} + +int Window::getWidth() const { + return _width; +} + +int Window::getHeight() const { + return _height; +} + +KeyWatcher Window::getKeyWatcher(int key) { + return KeyWatcher(_handle, key); +} diff --git a/src/glfw/window.h b/src/glfw/window.h new file mode 100644 index 0000000..92a1b56 --- /dev/null +++ b/src/glfw/window.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include +#include + +#include "key_watcher.h" + +class Window { +private: + bool _good = false; + int _width = 800; + int _height = 600; + + GLFWwindow* const _handle; + +public: + Window(const std::string& title); + ~Window(); + + bool isGood() const; + + int getWidth() const; + int getHeight() const; + + KeyWatcher getKeyWatcher(int key); + + template + void init(F f); + + template + void render(F loop); +}; + +template +void Window::init(F f) { + glfwMakeContextCurrent(_handle); + f(); + glfwMakeContextCurrent(nullptr); +} + +template +void Window::render(F loop) { + glfwMakeContextCurrent(_handle); + + while ( glfwGetKey(_handle, GLFW_KEY_ESCAPE) != GLFW_PRESS && + glfwWindowShouldClose(_handle) == 0 ) { + glfwGetWindowSize(_handle, &_width, &_height); + + loop(); + + glfwSwapBuffers(_handle); + glfwPollEvents(); + } + + glfwMakeContextCurrent(nullptr); +} diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..914d505 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,156 @@ +#include +#include +#include + +#include +#include + +#include "glfw/guard.h" +#include "glfw/window.h" + +#include "buffer/vertex/fluid_cell_buffer.h" +#include "buffer/vertex/lattice_cell_buffer.h" + +#include "shader/wrap/graphic_shader.h" +#include "shader/wrap/compute_shader.h" + +#include "shader/code/vertex.glsl" +#include "shader/code/fragment.glsl" + +#include "shader/code/collide.glsl" +#include "shader/code/stream.glsl" + +#include "timer.h" + +float getWorldHeight(int window_width, int window_height, float world_width) { + return world_width / window_width * window_height; +} + +glm::mat4 getMVP(float world_width, float world_height) { + const glm::mat4 projection = glm::ortho( + -(world_width/2), world_width/2, + -(world_height/2), world_height/2, + 0.1f, 100.0f + ); + + const glm::mat4 view = glm::lookAt( + glm::vec3(0,0,1), + glm::vec3(0,0,0), + glm::vec3(0,1,0) + ); + + return projection * view; +} + +int renderWindow() { + Window window("compustream"); + + if ( !window.isGood() ) { + std::cerr << "Failed to open GLFW window." << std::endl; + return -1; + } + + int window_width = window.getWidth(); + int window_height = window.getHeight(); + + float world_width = 256.0; + float world_height = getWorldHeight(window_width, window_height, world_width); + + glm::mat4 MVP = getMVP(world_width, world_height); + + std::unique_ptr scene_shader; + + std::unique_ptr lattice_buffer_a; + std::unique_ptr lattice_buffer_b; + std::unique_ptr fluid_buffer; + + std::unique_ptr collide_shader; + std::unique_ptr stream_shader; + + window.init([&]() { + scene_shader = std::make_unique( + VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE); + + lattice_buffer_a = std::make_unique(); + lattice_buffer_b = std::make_unique(); + fluid_buffer = std::make_unique(); + + collide_shader = std::make_unique(COLLIDE_SHADER_CODE); + stream_shader = std::make_unique(STREAM_SHADER_CODE); + }); + + if ( !collide_shader->isGood() || !stream_shader->isGood() ) { + std::cerr << "Compute shader error. Check vector field definition." << std::endl; + return -1; + } + + auto last_frame = timer::now(); + + bool update_lattice = true; + bool tick = true; + + auto pause_key = window.getKeyWatcher(GLFW_KEY_SPACE); + + window.render([&]() { + if ( pause_key.wasClicked() ) { + update_lattice = !update_lattice; + } + + if ( window.getWidth() != window_width + || window.getHeight() != window_height ) { + window_width = window.getWidth(); + window_height = window.getHeight(); + glViewport(0, 0, window_width, window_height); + + world_height = getWorldHeight(window_width, window_height, world_width); + MVP = getMVP(world_width, world_height); + } + + if ( update_lattice ) { + if ( timer::millisecondsSince(last_frame) >= 1000/25 ) { + if ( tick ) { + collide_shader->workOn(lattice_buffer_a->getBuffer(), lattice_buffer_b->getBuffer(), fluid_buffer->getBuffer()); + stream_shader->workOn(lattice_buffer_a->getBuffer(), lattice_buffer_b->getBuffer(), fluid_buffer->getBuffer()); + tick = false; + } else { + collide_shader->workOn(lattice_buffer_b->getBuffer(), lattice_buffer_a->getBuffer(), fluid_buffer->getBuffer()); + stream_shader->workOn(lattice_buffer_b->getBuffer(), lattice_buffer_a->getBuffer(), fluid_buffer->getBuffer()); + tick = true; + } + + { + auto guard = collide_shader->use(); + collide_shader->dispatch(); + } + { + auto guard = stream_shader->use(); + stream_shader->dispatch(); + } + + last_frame = timer::now(); + } + + { + auto sdrGuard = scene_shader->use(); + + scene_shader->setUniform("MVP", MVP); + + glClear(GL_COLOR_BUFFER_BIT); + fluid_buffer->draw(); + } + } + }); + + return 0; +} + +int main(int argc, char* argv[]) { + GlfwGuard glfw; + + if( !glfw.isGood() ) { + std::cerr << "Failed to initialize GLFW." << std::endl; + return -1; + } + + return renderWindow(); +} diff --git a/src/shader/code/collide.glsl b/src/shader/code/collide.glsl new file mode 100644 index 0000000..67e762b --- /dev/null +++ b/src/shader/code/collide.glsl @@ -0,0 +1,95 @@ +static const std::string COLLIDE_SHADER_CODE = R"( +#version 430 + +layout (local_size_x = 1, local_size_y = 1) in; +layout (std430, binding=1) buffer bufferCollide{ float collideCells[]; }; +layout (std430, binding=2) buffer bufferStream{ float streamCells[]; }; +layout (std430, binding=3) buffer bufferFluid{ float fluidCells[]; }; + +float get(uint x, uint y, int i, int j) { + return collideCells[9*128*y + 9*x + (i+1)*3 + j+1]; +} + +void set(uint x, uint y, int i, int j, float v) { + collideCells[9*128*y + 9*x + (i+1)*3 + j+1] = v; +} + +void setFluid(uint x, uint y, vec2 v, float d) { + fluidCells[3*128*y + 3*x + 0] = float(x)-64. + 10.*v.x; + fluidCells[3*128*y + 3*x + 1] = float(y)-64. + 10.*v.y; + fluidCells[3*128*y + 3*x + 2] = d; +} + +float density(uint x, uint y) { + return collideCells[9*128*y + 9*x + 0] + + collideCells[9*128*y + 9*x + 1] + + collideCells[9*128*y + 9*x + 2] + + collideCells[9*128*y + 9*x + 3] + + collideCells[9*128*y + 9*x + 4] + + collideCells[9*128*y + 9*x + 5] + + collideCells[9*128*y + 9*x + 6] + + collideCells[9*128*y + 9*x + 7] + + collideCells[9*128*y + 9*x + 8]; +} + +vec2 velocity(uint x, uint y, float d) { + return 1./d * vec2( + get(x,y, 1, 0) - get(x,y,-1, 0) + get(x,y, 1, 1) - get(x,y,-1,-1) + get(x,y, 1,-1) - get(x,y,-1,1), + get(x,y, 0, 1) - get(x,y, 0,-1) + get(x,y, 1, 1) - get(x,y,-1,-1) - get(x,y, 1,-1) + get(x,y,-1,1) + ); +} + +float w(int i, int j) { + if ( i == -1 ) { + if ( j != 0 ) { + return 1./36.; + } else { + return 1./9.; + } + } else if ( i == 0 ) { + if ( j != 0 ) { + return 1./9.; + } else { + return 4./9.; + } + } else { + if ( j != 0 ) { + return 1./36.; + } else { + return 1./9.; + } + } +} + +float comp(int x, int y, vec2 v) { + return x*v.x + y*v.y; +} + +float sq(float x) { + return x*x; +} + +float norm(vec2 v) { + return sqrt(sq(v.x)+sq(v.y)); +} + +void main() { + const uint x = gl_GlobalInvocationID.x; + const uint y = gl_GlobalInvocationID.y; + + const float omega = 0.6; + + const float d = density(x,y); + const vec2 v = velocity(x,y,d); + + setFluid(x,y,v,d); + + for ( int i = -1; i <= 1; ++i ) { + for ( int j = -1; j <= 1; ++j ) { + const float eq = w(i,j) * d * (1 + 3*comp(i,j,v) + 4.5*sq(comp(i,j,v)) - 1.5*sq(norm(v))); + set(x,y,i,j, get(x,y,i,j) + omega * (eq - get(x,y,i,j))); + } + } + +} +)"; diff --git a/src/shader/code/fragment.glsl b/src/shader/code/fragment.glsl new file mode 100644 index 0000000..37e18bd --- /dev/null +++ b/src/shader/code/fragment.glsl @@ -0,0 +1,5 @@ +static const std::string FRAGMENT_SHADER_CODE = R"( +void main() { + gl_FragColor = gl_Color; +} +)"; diff --git a/src/shader/code/stream.glsl b/src/shader/code/stream.glsl new file mode 100644 index 0000000..7dd5ba8 --- /dev/null +++ b/src/shader/code/stream.glsl @@ -0,0 +1,30 @@ +static const std::string STREAM_SHADER_CODE = R"( +#version 430 + +layout (local_size_x = 1, local_size_y = 1) in; +layout (std430, binding=1) buffer bufferCollide{ float collideCells[]; }; +layout (std430, binding=2) buffer bufferStream{ float streamCells[]; }; + +float get(uint x, uint y, int i, int j) { + return collideCells[9*128*y + 9*x + (i+1)*3 + j+1]; +} + +void set(uint x, uint y, int i, int j, float v) { + streamCells[9*128*y + 9*x + (i+1)*3 + j+1] = v; +} + +void main() { + const uint x = gl_GlobalInvocationID.x; + const uint y = gl_GlobalInvocationID.y; + + if ( x != 0 && x != 128-1 && y != 0 && y != 128-1 ) { + for ( int i = -1; i <= 1; ++i ) { + for ( int j = -1; j <= 1; ++j ) { + set(x+i,y+j,i,j, get(x,y,i,j)); + } + } + } else { + + } +} +)"; diff --git a/src/shader/code/vertex.glsl b/src/shader/code/vertex.glsl new file mode 100644 index 0000000..4c307d8 --- /dev/null +++ b/src/shader/code/vertex.glsl @@ -0,0 +1,8 @@ +static const std::string VERTEX_SHADER_CODE = R"( +uniform mat4 MVP; + +void main() { + gl_Position = MVP * vec4(gl_Vertex.xy, 0.0, 1.0); + gl_FrontColor = vec4(1., 0., 0., 0.); +} +)"; diff --git a/src/shader/util.cc b/src/shader/util.cc new file mode 100644 index 0000000..be59ac1 --- /dev/null +++ b/src/shader/util.cc @@ -0,0 +1,56 @@ +#include "util.h" + +#include +#include + +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 -1; + } + + 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; + return -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 errorLog(maxLength); + glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); + + for( auto c : errorLog ) { + std::cerr << c; + } + std::cerr << std::endl; + + return -1; + } + + return shader; +} + +} diff --git a/src/shader/util.h b/src/shader/util.h new file mode 100644 index 0000000..31224e9 --- /dev/null +++ b/src/shader/util.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include + +namespace util { + +GLint getUniform(GLuint program, const std::string& name); + +GLint compileShader(const std::string& source, GLenum type); + +} diff --git a/src/shader/wrap/compute_shader.cc b/src/shader/wrap/compute_shader.cc new file mode 100644 index 0000000..c90c370 --- /dev/null +++ b/src/shader/wrap/compute_shader.cc @@ -0,0 +1,51 @@ +#include "compute_shader.h" + +#include "shader/util.h" + +ComputeShader::Guard::Guard(GLuint id): + _id(id) { + glUseProgram(_id); +} + +ComputeShader::Guard::~Guard() { + glUseProgram(0); +} + +ComputeShader::Guard ComputeShader::use() const { + return Guard(_id); +} + +ComputeShader::ComputeShader(const std::string& src): + _id(glCreateProgram()) { + GLint shader = util::compileShader(src, GL_COMPUTE_SHADER); + + if ( shader != -1 ) { + glAttachShader(_id, shader); + glLinkProgram(_id); + _good = true; + } +} + +ComputeShader::~ComputeShader() { + glDeleteProgram(_id); +} + +bool ComputeShader::isGood() const { + return _good; +} + +GLuint ComputeShader::setUniform(const std::string& name, float x, float y) const { + GLuint id = util::getUniform(_id, name); + glUniform2f(id, x, y); + return id; +} + +void ComputeShader::workOn(GLuint bufferA, GLuint bufferB, GLuint bufferC) const { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, bufferA); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, bufferB); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, bufferC); +} + +void ComputeShader::dispatch() const { + glDispatchCompute(128, 128, 1); +} diff --git a/src/shader/wrap/compute_shader.h b/src/shader/wrap/compute_shader.h new file mode 100644 index 0000000..74c8270 --- /dev/null +++ b/src/shader/wrap/compute_shader.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +class ComputeShader { +private: + const GLuint _id; + + bool _good; + +public: + struct Guard { + const GLuint _id; + + Guard(GLuint id); + ~Guard(); + }; + + Guard use() const; + + ComputeShader(const std::string& src); + ~ComputeShader(); + + bool isGood() const; + + GLuint setUniform(const std::string& name, float x, float y) const; + + void workOn(GLuint bufferA, GLuint bufferB, GLuint bufferC) const; + void dispatch() const; +}; diff --git a/src/shader/wrap/graphic_shader.cc b/src/shader/wrap/graphic_shader.cc new file mode 100644 index 0000000..0ed37ff --- /dev/null +++ b/src/shader/wrap/graphic_shader.cc @@ -0,0 +1,45 @@ +#include "graphic_shader.h" + +#include "shader/util.h" + +GraphicShader::Guard::Guard(GLuint id): + _id(id) { + glUseProgram(_id); +} + +GraphicShader::Guard::~Guard() { + glUseProgram(0); +} + +GraphicShader::Guard GraphicShader::use() const { + return Guard(_id); +} + +GraphicShader::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::~GraphicShader() { + glDeleteProgram(_id); +} + +GLuint GraphicShader::setUniform(const std::string& name, int value) const { + GLuint id = util::getUniform(_id, name); + glUniform1i(id, value); + return id; +} + +GLuint GraphicShader::setUniform(const std::string& name, const std::vector& v) const { + GLuint id = util::getUniform(_id, name); + glUniform1iv(id, v.size(), reinterpret_cast(v.data())); + return id; +} + +GLuint GraphicShader::setUniform(const std::string& name, glm::mat4& M) const { + GLuint id = util::getUniform(_id, name); + glUniformMatrix4fv(id, 1, GL_FALSE, &M[0][0]); + return id; +} diff --git a/src/shader/wrap/graphic_shader.h b/src/shader/wrap/graphic_shader.h new file mode 100644 index 0000000..25a5efb --- /dev/null +++ b/src/shader/wrap/graphic_shader.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include +#include + +class GraphicShader { +private: + const GLuint _id; + +public: + struct Guard { + const GLuint _id; + + Guard(GLuint id); + ~Guard(); + }; + + Guard use() const; + + GraphicShader(const std::string& vertex, const std::string fragment); + ~GraphicShader(); + + GLuint setUniform(const std::string& name, int value) const; + GLuint setUniform(const std::string& name, const std::vector& v) const; + GLuint setUniform(const std::string& name, glm::mat4& M) const; +}; diff --git a/src/timer.cc b/src/timer.cc new file mode 100644 index 0000000..c46017c --- /dev/null +++ b/src/timer.cc @@ -0,0 +1,16 @@ +#include "timer.h" + +namespace timer { + +std::chrono::time_point now() { + return std::chrono::high_resolution_clock::now(); +} + +double millisecondsSince( + std::chrono::time_point& pit) { + return std::chrono::duration_cast( + now() - pit + ).count(); +} + +} diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..aed1b61 --- /dev/null +++ b/src/timer.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace timer { + +std::chrono::time_point now(); + +double millisecondsSince( + std::chrono::time_point& pit); + +} -- cgit v1.2.3