From bd5057ec9aec61925bca2735d4cbae4a8001b9cf Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Fri, 18 May 2018 19:53:06 +0200 Subject: Limit particle update rate --- CMakeLists.txt | 6 +- main.cc | 255 ------------------------------------------------------- src/main.cc | 264 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+), 258 deletions(-) delete mode 100644 main.cc create mode 100644 src/main.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a16dced..4a89f50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,12 +7,12 @@ set( ) add_executable( - shader - main.cc + computicle + src/main.cc ) target_link_libraries( - shader + computicle GL GLEW glfw diff --git a/main.cc b/main.cc deleted file mode 100644 index 870006e..0000000 --- a/main.cc +++ /dev/null @@ -1,255 +0,0 @@ -#include -#include - -#include -#include - -#include -#include -#include - -GLFWwindow* window; - -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 local_shader = glCreateShader(type); - - if ( !local_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(local_shader, 1, &source_data, &source_length); - glCompileShader(local_shader); - - GLint compiled; - glGetShaderiv(local_shader, GL_COMPILE_STATUS, &compiled); - if ( !compiled ) { - std::cerr << "Cannot compile shader" << std::endl; - GLint maxLength = 0; - glGetShaderiv(local_shader, GL_INFO_LOG_LENGTH, &maxLength); - std::vector errorLog(maxLength); - glGetShaderInfoLog(local_shader, maxLength, &maxLength, &errorLog[0]); - for( auto c : errorLog ) { - std::cerr << c; - } - std::cerr << std::endl; - } - - return local_shader; -} - -GLint setupShader() { - #define GLSL(shader) #shader - GLint shader = glCreateProgram(); - - glAttachShader(shader, compileShader(GLSL( - uniform mat4 MVP; - - float distance(vec2 v, vec2 w) { - vec2 u = v - w; - return sqrt(u.x*u.x + u.y*u.y); - } - - void main() { - gl_Position = MVP * vec4(gl_Vertex.xy, 0.0, 1.0); - gl_FrontColor = vec4(max(1. - gl_Vertex.z/5., 0.1), 0., 0., 0.); - }), - GL_VERTEX_SHADER)); - - glAttachShader(shader, compileShader(GLSL( - void main() { - gl_FragColor = gl_Color; - }), - GL_FRAGMENT_SHADER)); - - glLinkProgram(shader); - return shader; -} - -GLint setupComputeShader() { - GLint shader = glCreateProgram(); - glAttachShader(shader, compileShader("#version 430\n" GLSL( - layout (local_size_x = 1) in; - layout (std430, binding=1) buffer bufferA{ float data[]; }; - uniform vec2 world; - - float rand(vec2 co){ - return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453); - } - - bool insideWorld(vec2 v) { - return v.x > -world.x/2. - && v.x < world.x/2. - && v.y > -world.y/2. - && v.y < world.y/2.; - } - - void main() { - uint idx = 3*gl_GlobalInvocationID.x; - 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+1] += 0.01 * sin(v.x-v.y); - data[idx+2] += 0.01; - } else { - data[idx+0] = -(world.x/2.) + rand(vec2(data[idx+1], data[idx+0])) * world.x; - data[idx+1] = -(world.y/2.) + rand(vec2(data[idx+0], data[idx+1])) * world.y; - data[idx+2] = uint(rand(v) * 5.); - } - }), - GL_COMPUTE_SHADER)); - glLinkProgram(shader); - return shader; -} - -int window_width = 800; -int window_height = 600; -double mouse_x, mouse_y; -float world_width, world_height; -glm::mat4 MVP; - -void updateMVP() { - world_width = 20.f; - world_height = world_width / window_width * window_height; - glm::mat4 projection = glm::ortho( - -(world_width /2), world_width/2, - -(world_height/2), world_height/2, - 0.1f, 100.0f - ); - glm::mat4 view = glm::lookAt( - glm::vec3(0,0,20), - glm::vec3(0,0,0), - glm::vec3(0,1,0) - ); - glm::mat4 model = glm::mat4(1.0f); - MVP = projection * view * model; -} - -void window_size_callback(GLFWwindow*, int width, int height) { - window_width = width; - window_height = height; - glViewport(0, 0, width, height); - updateMVP(); -} - -void cursor_pos_callback(GLFWwindow*, double x, double y) { - mouse_x = -(world_width/2) + world_width * (x / window_width); - mouse_y = world_height/2 - world_height * (y / window_height); -} - -int main() { - if( !glfwInit() ) { - std::cerr << "Failed to initialize GLFW" << std::endl; - return -1; - } - - glfwWindowHint(GLFW_SAMPLES, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - - window = glfwCreateWindow(window_width, window_height, "shader", NULL, NULL); - if( window == NULL ){ - std::cerr << "Failed to open GLFW window." << std::endl; - glfwTerminate(); - return -1; - } - glfwSetWindowSizeCallback(window, window_size_callback); - glfwSetCursorPosCallback(window, cursor_pos_callback); - glfwMakeContextCurrent(window); - - glewExperimental = true; - if ( glewInit() != GLEW_OK ) { - std::cerr << "Failed to initialize GLEW" << std::endl; - glfwTerminate(); - return -1; - } - - glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); - - glClearColor(0.0f, 0.0f, 0.4f, 0.0f); - - GLuint VertexArrayID; - glGenVertexArrays(1, &VertexArrayID); - glBindVertexArray(VertexArrayID); - - GLint ProgramID = setupShader(); - GLuint MatrixID = glGetUniformLocation(ProgramID, "MVP"); - GLuint MouseID = glGetUniformLocation(ProgramID, "mouse"); - - GLint ComputeProgramID = setupComputeShader(); - GLuint WorldID = glGetUniformLocation(ComputeProgramID, "world"); - - updateMVP(); - - const std::size_t particle_count = 100000; - - std::vector vertex_buffer_data; - vertex_buffer_data.reserve(3*particle_count); - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution distX(-world_width/2.f, world_width/2.f); - std::uniform_real_distribution distY(-world_height/2.f, world_height/2.f); - std::uniform_real_distribution distAge(0.f, 5.f); - - for ( int i = 0; i < particle_count; ++i ) { - vertex_buffer_data.emplace_back(distX(gen)); - vertex_buffer_data.emplace_back(distY(gen)); - vertex_buffer_data.emplace_back(distAge(gen)); - } - - GLuint vertexbuffer; - glGenBuffers(1, &vertexbuffer); - glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); - glBufferData(GL_ARRAY_BUFFER, - vertex_buffer_data.size() * sizeof(GLfloat), &vertex_buffer_data[0], GL_STATIC_DRAW); - - do { - glUseProgram(ComputeProgramID); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, vertexbuffer); - glUniform2f(WorldID, world_width, world_height); - glDispatchCompute(particle_count, 1, 1); - - glClear(GL_COLOR_BUFFER_BIT); - - glUseProgram(ProgramID); - - glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); - glUniform2f(MouseID, mouse_x, mouse_y); - - glEnableVertexAttribArray(0); - glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); - - glDrawArrays(GL_POINTS, 0, 3*particle_count); - - glDisableVertexAttribArray(0); - - glfwSwapBuffers(window); - glfwPollEvents(); - } - while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS && - glfwWindowShouldClose(window) == 0 ); - - glDeleteBuffers(1, &vertexbuffer); - glDeleteVertexArrays(1, &VertexArrayID); - glDeleteProgram(ProgramID); - glDeleteProgram(ComputeProgramID); - - glfwTerminate(); - - return 0; -} diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..a307ae3 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,264 @@ +#include +#include + +#include +#include + +#include +#include +#include +#include + +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 errorLog(maxLength); + glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); + for( auto c : errorLog ) { + std::cerr << c; + } + std::cerr << std::endl; + } + + return shader; +} + +GLint setupShader() { + #define GLSL(shader) #shader + GLint shader = glCreateProgram(); + + glAttachShader(shader, compileShader(GLSL( + uniform mat4 MVP; + + float distance(vec2 v, vec2 w) { + vec2 u = v - w; + return sqrt(u.x*u.x + u.y*u.y); + } + + void main() { + gl_Position = MVP * vec4(gl_Vertex.xy, 0.0, 1.0); + gl_FrontColor = vec4(max(1. - gl_Vertex.z/5., 0.1), 0., 0., 0.); + }), + GL_VERTEX_SHADER)); + + glAttachShader(shader, compileShader(GLSL( + void main() { + gl_FragColor = gl_Color; + }), + GL_FRAGMENT_SHADER)); + + glLinkProgram(shader); + return shader; +} + +GLint setupComputeShader() { + GLint shader = glCreateProgram(); + glAttachShader(shader, compileShader("#version 430\n" GLSL( + layout (local_size_x = 1) in; + layout (std430, binding=1) buffer bufferA{ float data[]; }; + uniform vec2 world; + + float rand(vec2 co){ + return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453); + } + + bool insideWorld(vec2 v) { + return v.x > -world.x/2. + && v.x < world.x/2. + && v.y > -world.y/2. + && v.y < world.y/2.; + } + + void main() { + uint idx = 3*gl_GlobalInvocationID.x; + 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+1] += 0.01 * sin(v.x-v.y); + data[idx+2] += 0.01; + } else { + data[idx+0] = -(world.x/2.) + rand(vec2(data[idx+1], data[idx+0])) * world.x; + data[idx+1] = -(world.y/2.) + rand(vec2(data[idx+0], data[idx+1])) * world.y; + data[idx+2] = uint(rand(v) * 5.); + } + }), + GL_COMPUTE_SHADER)); + glLinkProgram(shader); + return shader; +} + +int window_width = 800; +int window_height = 600; +double mouse_x, mouse_y; +float world_width, world_height; +glm::mat4 MVP; + +void updateMVP() { + world_width = 20.f; + world_height = world_width / window_width * window_height; + glm::mat4 projection = glm::ortho( + -(world_width /2), world_width/2, + -(world_height/2), world_height/2, + 0.1f, 100.0f + ); + glm::mat4 view = glm::lookAt( + glm::vec3(0,0,20), + glm::vec3(0,0,0), + glm::vec3(0,1,0) + ); + glm::mat4 model = glm::mat4(1.0f); + MVP = projection * view * model; +} + +void window_size_callback(GLFWwindow*, int width, int height) { + window_width = width; + window_height = height; + glViewport(0, 0, width, height); + updateMVP(); +} + +void cursor_pos_callback(GLFWwindow*, double x, double y) { + mouse_x = -(world_width/2) + world_width * (x / window_width); + mouse_y = world_height/2 - world_height * (y / window_height); +} + +std::vector makeInitialParticles(std::size_t count) { + std::vector buffer; + buffer.reserve(3*count); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution distX(-world_width/2., world_width/2.); + std::uniform_real_distribution distY(-world_height/2., world_height/2.); + std::uniform_real_distribution distAge(0., 5.); + + for ( std::size_t i = 0; i < count; ++i ) { + buffer.emplace_back(distX(gen)); + buffer.emplace_back(distY(gen)); + buffer.emplace_back(distAge(gen)); + } + + return buffer; +} + +int main() { + if( !glfwInit() ) { + std::cerr << "Failed to initialize GLFW" << std::endl; + return -1; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + GLFWwindow* const window = glfwCreateWindow(window_width, window_height, "computicle", NULL, NULL); + if( window == NULL ){ + std::cerr << "Failed to open GLFW window." << std::endl; + glfwTerminate(); + return -1; + } + glfwSetWindowSizeCallback(window, window_size_callback); + glfwSetCursorPosCallback(window, cursor_pos_callback); + glfwMakeContextCurrent(window); + + if ( glewInit() != GLEW_OK ) { + std::cerr << "Failed to initialize GLEW" << std::endl; + glfwTerminate(); + return -1; + } + + glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); + + glClearColor(0.0f, 0.0f, 0.4f, 0.0f); + + GLuint VertexArrayID; + glGenVertexArrays(1, &VertexArrayID); + glBindVertexArray(VertexArrayID); + + GLint ShaderID = setupShader(); + GLuint MatrixID = glGetUniformLocation(ShaderID, "MVP"); + GLuint MouseID = glGetUniformLocation(ShaderID, "mouse"); + + GLint ComputeShaderID = setupComputeShader(); + GLuint WorldID = glGetUniformLocation(ComputeShaderID, "world"); + + updateMVP(); + + const std::size_t particle_count = 500000; + + auto vertex_buffer_data = makeInitialParticles(particle_count); + + GLuint vertexbuffer; + glGenBuffers(1, &vertexbuffer); + glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); + glBufferData(GL_ARRAY_BUFFER, vertex_buffer_data.size() * sizeof(GLfloat), &vertex_buffer_data[0], GL_STATIC_DRAW); + + auto lastFrame = std::chrono::high_resolution_clock::now(); + + do { + // update particles at most 50 times per second + if ( std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastFrame).count() >= 20 ) { + glUseProgram(ComputeShaderID); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, vertexbuffer); + glUniform2f(WorldID, world_width, world_height); + glDispatchCompute(particle_count, 1, 1); + + lastFrame = std::chrono::high_resolution_clock::now(); + } + + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(ShaderID); + + glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); + glUniform2f(MouseID, mouse_x, mouse_y); + + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + glDrawArrays(GL_POINTS, 0, 3*particle_count); + + glDisableVertexAttribArray(0); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS && + glfwWindowShouldClose(window) == 0 ); + + glDeleteBuffers(1, &vertexbuffer); + glDeleteVertexArrays(1, &VertexArrayID); + glDeleteProgram(ShaderID); + glDeleteProgram(ComputeShaderID); + + glfwTerminate(); + + return 0; +} -- cgit v1.2.3