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/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 ++++++++++++ 10 files changed, 364 insertions(+) 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 (limited to 'src/shader') 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; +}; -- cgit v1.2.3