소스 검색

Getting there

Charles Croz 1 년 전
부모
커밋
612b6668f1

+ 2 - 2
CppGPU/Makefile

@@ -1,8 +1,8 @@
 
 APP=particle
-OBJ=main.o ComputeShader.o Application.o Buffer.o
+OBJ=./src/main.o ./src/ComputeShader.o ./src/Application.o ./src/Buffer.o ./src/Canvas.o
 OBJ_OPEN_GL=./src/glad.o
-HEADERS=
+HEADERS=./include/Config.hpp ./include/Helper.hpp ./include/Particle.hpp ./include/Cell.hpp
 
 GPP=g++
 CFLAGS=-O3 -I./include

+ 0 - 24
CppGPU/definitions.hpp

@@ -1,24 +0,0 @@
-#ifndef PARTICLE_DEFINITIONS
-#define PARTICLE_DEFINITIONS
-
-#include <cstdint>
-#include <cstddef>
-#include <cmath>
-
-using numeric_t = double;
-using icoord_t = size_t;
-using fcoord_t = double;
-
-typedef struct 
-{
-    icoord_t x;
-    icoord_t y;
-} ipoint_t;
-
-typedef struct 
-{
-    fcoord_t x;
-    fcoord_t y;
-} fpoint_t;
-
-#endif // PARTICLE_DEFINITIONS

+ 2 - 1
CppGPU/Application.hpp → CppGPU/include/Application.hpp

@@ -27,7 +27,8 @@ public:
     void terminate();
     bool createWindow();
     bool running();
-    void render();
+    void update();
+    float time();
 };
 
 #endif // PARTICLE_GPU

+ 1 - 6
CppGPU/Buffer.hpp → CppGPU/include/Buffer.hpp

@@ -1,12 +1,7 @@
 #ifndef PARTICLE_BUFFER
 #define PARTICLE_BUFFER
 
-
-#include <vector>
-#include <string>
 #include <iostream>
-#include <fstream>
-#include <sstream>
 
 /* OpenGL */
 #include "glad/glad.h"
@@ -23,7 +18,7 @@ public:
     void create();
     void set(void const *data, size_t count, size_t size);
     void bind();
-    void bindBase(GLint program, char const *name, int binding);
+    void bind_base(GLint program, char const *name, int binding);
     void unbind();
     void get(void *data, size_t count, size_t size);
     void release();

+ 31 - 0
CppGPU/include/Canvas.hpp

@@ -0,0 +1,31 @@
+#ifndef PARTICLE_TEXTURE
+#define PARTICLE_TEXTURE
+
+#include <iostream>
+
+/* OpenGL */
+#include "glad/glad.h"
+#include <GLFW/glfw3.h>
+
+class Canvas
+{
+private:
+    GLuint m_texture;
+    GLuint m_FBO;
+
+    GLuint m_width;
+    GLuint m_height;
+public:
+    Canvas() = default;
+    Canvas(GLuint width, GLuint height);
+    ~Canvas();
+
+    void create();
+    void bind();
+    void unbind();
+    void draw();
+    void release();
+};
+
+
+#endif // PARTICLE_TEXTURE

+ 15 - 0
CppGPU/include/Cell.hpp

@@ -0,0 +1,15 @@
+#ifndef PARTICLE_CELL
+#define PARTICLE_CELL
+
+#include <stddef.h>
+
+struct alignas(32) Cell
+{
+    float pheromone{};
+    float result{};
+    
+    int population{};
+};
+
+
+#endif // PARTICLE_CELL

+ 5 - 2
CppGPU/ComputeShader.hpp → CppGPU/include/ComputeShader.hpp

@@ -21,6 +21,8 @@ private:
     GLuint m_program = 0;
     GLuint m_shader = 0;
 public:
+    static void wait();
+
     ComputeShader() = default;
     ComputeShader(std::string &shader_filename);
     ~ComputeShader();
@@ -31,12 +33,13 @@ public:
     bool compile();
     void activate();
     void deactivate();
-    void execute(int n);
-    void wait();
+    void execute(size_t x, size_t group_size_x);
+    void execute(size_t x, size_t y, size_t group_size_x, size_t group_size_y);
     bool release();
 
     bool set_uniform(char const * name, float value);
     bool set_uniform(char const * name, int value);
+    bool set_uniform(char const * name, size_t value);
 };
 
 #endif // PARTICLE_COMPUTE_SHADER

+ 24 - 0
CppGPU/include/Config.hpp

@@ -0,0 +1,24 @@
+#ifndef PARTICLE_CONFIG
+#define PARTICLE_CONFIG
+
+#include <stddef.h>
+
+struct  Config
+{
+    int map_width{};
+    int map_height{};
+
+    size_t population{};
+
+    float increase{};
+    float decay{};
+    float diffusion{};
+
+    float speed{};
+    float steering{};
+    float sensing_distance{};
+    float sensing_angle{};
+};
+
+
+#endif // PARTICLE_CONFIG

+ 43 - 0
CppGPU/include/Helper.hpp

@@ -0,0 +1,43 @@
+#ifndef PARTICLE_HELPER
+#define PARTICLE_HELPER
+
+#include <stdlib.h>
+#include <Particle.hpp>
+#include <Config.hpp>
+#include <ComputeShader.hpp>
+
+float random_float(float min, float max){
+    float alpha = (float)(rand()) / (float) (RAND_MAX);
+    if (alpha == 1.0f)
+        alpha = 0;
+    return min + alpha * (max - min);
+}
+
+void particle_uniform(Config const &config, ComputeShader &compute_shader){
+    
+    compute_shader.set_uniform("map_width", config.map_width);
+    compute_shader.set_uniform("map_height", config.map_height);
+
+    compute_shader.set_uniform("population", config.population);
+    
+    compute_shader.set_uniform("sensing_angle", config.sensing_angle);
+    compute_shader.set_uniform("sensing_distance", config.sensing_distance);
+    compute_shader.set_uniform("steering", config.steering);
+    compute_shader.set_uniform("speed", config.speed);
+}
+
+void map_update_uniform(Config const &config, ComputeShader &compute_shader){
+    compute_shader.set_uniform("map_width", config.map_width);
+    compute_shader.set_uniform("map_height", config.map_height);
+
+    compute_shader.set_uniform("increase", config.increase);
+    compute_shader.set_uniform("decay", config.decay);
+    compute_shader.set_uniform("diffusion", config.diffusion);
+}
+
+void map_reset_uniform(Config const &config, ComputeShader &compute_shader){
+    compute_shader.set_uniform("map_width", config.map_width);
+    compute_shader.set_uniform("map_height", config.map_height);
+}
+
+#endif // PARTICLE_HELPER

+ 13 - 0
CppGPU/include/Particle.hpp

@@ -0,0 +1,13 @@
+#ifndef PARTICLE_PARTICLE
+#define PARTICLE_PARTICLE
+
+struct alignas(32) Particle
+{
+    float pos_x;
+    float pos_y;
+    float dir_x;
+    float dir_y;
+};
+
+
+#endif // PARTICLE_PARTICLE

+ 0 - 79
CppGPU/main.cpp

@@ -1,79 +0,0 @@
-/* std */
-#include <iostream>
-#include <vector>
-#include <string>
-#include <stdlib.h>
-#include <unistd.h>  
-
-/* OpenGL */
-#include "glad/glad.h"
-#include <GLFW/glfw3.h>
-
-/* Project classes */
-#include "ComputeShader.hpp"
-#include "Application.hpp"
-#include "Buffer.hpp"
-
-int main(int argc, char *argv[]){
-    std::string filename;
-    Application app;
-    ComputeShader compute_shader;
-    Buffer buffer;
-
-    int n_value = 10000;
-    int max_value = 120;
-
-    
-    app = Application(800, 600);
-    app.init();
-    app.createWindow();
-
-    
-    compute_shader.load("particle_update.comp");
-    compute_shader.compile();
-    compute_shader.activate();
-    compute_shader.set_uniform("count", n_value);
-    compute_shader.set_uniform("max_val", max_value);
-    
-
-    std::vector<int> data_in(n_value);
-    std::vector<int> data_out(n_value);
-    for(size_t i = 0 ; i < n_value ; ++i){
-        data_in[i] = i % (2 * max_value);
-    }
-    buffer.create();
-    buffer.set(data_in.data(), data_in.size(), sizeof(data_in[0]));
-
-    compute_shader.activate();
-    buffer.bind();
-    buffer.bindBase(compute_shader.get_program(), "dataBuffer", 1);
-    compute_shader.execute(n_value);
-    compute_shader.wait();
-    buffer.unbind();
-    compute_shader.deactivate();
-
-    buffer.get(data_out.data(), data_in.size(), sizeof(data_in[0]));
-
-    for(size_t i = 0 ; i < n_value ; ++i){
-        if(data_in[i] > max_value){
-            if(data_out[i] != 0){
-                std::cout << "Data too big not set to 0 by shader i=" << i << std::endl;
-                break;
-            }
-        } else if(data_in[i] != data_out[i]){
-            std::cout << "Data lower should be the same.... i=" << i << std::endl;
-            break;
-        }
-    }
-    
-    for(size_t i = 118 ; i < 130 ; ++i){
-        std::cout << "[" << i << "]" << "-in:" << data_in[i] << "-out:" << data_out[i] << std::endl;
-    }
-
-    while(app.running()){
-        app.render();
-    }
-
-
-    return 0;
-}

+ 0 - 20
CppGPU/particle_update.comp

@@ -1,20 +0,0 @@
-#version 430 core
-
-uniform int max_val;
-uniform int count;
-
-layout(std430, binding = 1) coherent buffer dataBuffer
-{
-    int data_SSBO[];
-};
-
-layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
-
-void main(){
-    if(gl_GlobalInvocationID.x >= count){
-        return;
-    }
-    if(data_SSBO[gl_GlobalInvocationID.x] > max_val){
-        data_SSBO[gl_GlobalInvocationID.x] = 0;
-    }
-}

+ 47 - 0
CppGPU/shaders/draw.glsl

@@ -0,0 +1,47 @@
+#version 430 core
+
+struct particle_t {
+    vec2 pos;
+    vec2 dir;
+};
+
+struct cell_t {
+    float pheromone;
+    float result;
+    int population;
+};
+
+layout(binding = 0, rgba8) writeonly uniform image2D canvas;
+
+uniform int map_width;
+uniform int map_height;
+
+uniform float display_population_value;
+uniform float display_pheromone_value;
+
+layout(std430, binding = 1) coherent buffer particles_buffer
+{
+    particle_t particles[];
+};
+
+layout(std430, binding = 2) coherent buffer map_buffer
+{
+    cell_t map[];
+};
+
+layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
+void main(){
+    uint x = gl_GlobalInvocationID.x;
+    uint y = gl_GlobalInvocationID.y;
+    if(x >= map_width || y >= map_height){
+        return;
+    }
+    uint cell_id = y * map_width + x;
+    cell_t cell = map[cell_id]; 
+
+    imageStore(canvas, ivec2(gl_GlobalInvocationID.xy), 
+        vec4(cell.population/display_population_value, 
+             cell.pheromone/display_pheromone_value,
+            //  0,
+             0, 1));
+}

+ 0 - 0
CppGPU/shaders/fragment.glsl


+ 26 - 0
CppGPU/shaders/map_reset.glsl

@@ -0,0 +1,26 @@
+#version 430 core
+
+struct cell_t {
+    float pheromone;
+    float result;
+    int population;
+};
+
+uniform int map_width;
+uniform int map_height;
+
+layout(std430, binding = 2) coherent buffer map_buffer
+{
+    cell_t map[];
+};
+
+layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
+void main(){
+    uint cell_id = gl_GlobalInvocationID.x;
+    if(cell_id >= map_width * map_height){
+        return;
+    }
+    map[cell_id].pheromone = map[cell_id].result;
+    map[cell_id].result = 0;
+    map[cell_id].population = 0;
+}

+ 46 - 0
CppGPU/shaders/map_update.glsl

@@ -0,0 +1,46 @@
+#version 430 core
+
+struct cell_t {
+    float pheromone;
+    float result;
+    int population;
+};
+
+uniform int map_width;
+uniform int map_height;
+
+uniform float increase;
+uniform float decay;
+uniform float diffusion;
+
+uniform float dt;
+
+layout(std430, binding = 2) coherent buffer map_buffer
+{
+    cell_t map[];
+};
+
+uint cell_id(uint x, uint y){
+    return uint(mod(x, map_width) + mod(x, map_height) * map_width);
+}
+
+layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
+void main(){
+    uint x = gl_GlobalInvocationID.x;
+    uint y = gl_GlobalInvocationID.y;
+
+    if(x >= map_width || y >= map_height){
+        return;
+    }
+
+    float frac = diffusion * dt;
+    float val = map[cell_id(x,y)].pheromone + map[cell_id(x,y)].population * increase * dt;
+    val = val * (1 - 4 * frac) 
+        + (map[cell_id(x-1,y)].pheromone
+        +  map[cell_id(x+1,y)].pheromone
+        +  map[cell_id(x,y-1)].pheromone
+        +  map[cell_id(x,y+1)].pheromone) * frac;
+    val = val * exp(-decay * dt);
+
+    map[cell_id(x,y)].result = val;
+}

+ 84 - 0
CppGPU/shaders/particles_update.glsl

@@ -0,0 +1,84 @@
+#version 430 core
+
+struct particle_t {
+    vec2 pos;
+    vec2 dir;
+};
+
+struct cell_t {
+    float pheromone;
+    float result;
+    int population;
+};
+
+uniform int map_width;
+uniform int map_height;
+
+uniform uint population;
+
+uniform float speed;
+uniform float steering;
+uniform float sensing_distance;
+uniform float sensing_angle;
+
+uniform float dt;
+
+layout(std430, binding = 1) coherent buffer particles_buffer
+{
+    particle_t particles[];
+};
+
+layout(std430, binding = 2) coherent buffer map_buffer
+{
+    cell_t map[];
+};
+
+vec2 rotate(vec2 v, float angle){
+    float c = cos(angle);
+    float s = sin(angle);
+    return vec2(v.x * c - v.y * s, v.x * s + v.y * c);
+}
+
+vec2 ahead(vec2 at, vec2 dir, float d){
+    return at + d * dir;
+}
+
+vec2 wrap_coord(vec2 v){
+    return mod(v, vec2(map_width, map_height));
+}
+
+uint cell_id(vec2 v){
+    ivec2 map_pos = ivec2(floor(v));
+    return uint(map_pos.y * map_width + map_pos.x);
+}
+
+layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
+void main(){
+    uint particle_id = gl_GlobalInvocationID.x;
+    if(particle_id >= population){
+        return;
+    }
+
+    // Update particle position
+    particle_t p = particles[particle_id];
+    particles[particle_id].pos = wrap_coord(p.pos + dt * speed * p.dir);
+    
+    // Count particle
+    uint p_cell_id = cell_id(p.pos);
+    // atomicAdd(map[p_cell_id].population, 1);
+
+    // Sense and turn
+    vec2 look_ahead = wrap_coord(ahead(p.pos, p.dir, sensing_distance));
+    vec2 look_left  = wrap_coord(ahead(p.pos, rotate(p.dir,  sensing_angle), sensing_distance));
+    vec2 look_right = wrap_coord(ahead(p.pos, rotate(p.dir, -sensing_angle), sensing_distance));
+
+    float pheromone_ahead = map[cell_id(look_ahead)].pheromone;
+    float pheromone_left  = map[cell_id(look_left )].pheromone;
+    float pheromone_right = map[cell_id(look_right)].pheromone;
+
+    if(pheromone_left > pheromone_ahead && pheromone_left > pheromone_right){
+        particles[particle_id].dir = rotate(p.dir,  steering * dt);
+    }else if(pheromone_right > pheromone_ahead && pheromone_right > pheromone_left){
+        particles[particle_id].dir = rotate(p.dir, -steering * dt);
+    }
+}

+ 0 - 0
CppGPU/shaders/vertex.glsl


+ 7 - 2
CppGPU/Application.cpp → CppGPU/src/Application.cpp

@@ -49,8 +49,13 @@ bool Application::running()
     return !glfwWindowShouldClose(window);
 }
 
-void Application::render()
+void Application::update()
 {
     glfwSwapBuffers(window);
-    glfwPollEvents();   
+    glfwPollEvents();
+}
+
+float Application::time()
+{
+    return (float)glfwGetTime();
 }

+ 14 - 14
CppGPU/Buffer.cpp → CppGPU/src/Buffer.cpp

@@ -15,13 +15,6 @@ void Buffer::release()
     glDeleteBuffers(1, &m_buffer);
 }
 
-void Buffer::set(void const *data, size_t count, size_t size)
-{
-    bind();
-    glBufferData(GL_SHADER_STORAGE_BUFFER, count * size, data, GL_STATIC_DRAW);
-    unbind();
-}
-
 void Buffer::unbind()
 {
     glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
@@ -32,15 +25,10 @@ void Buffer::bind()
     glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffer);
 }
 
-void Buffer::bindBase(GLint program, char const *name, int binding)
+void Buffer::set(void const *data, size_t count, size_t size)
 {
     bind();
-    GLuint block_index = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, name);
-    if(block_index == GL_INVALID_INDEX){
-        std::cout << "bindBase failed" << std::endl;
-    }
-    glShaderStorageBlockBinding(program, block_index, binding);
-    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, m_buffer);
+    glBufferData(GL_SHADER_STORAGE_BUFFER, count * size, data, GL_STATIC_DRAW);
     unbind();
 }
 
@@ -50,3 +38,15 @@ void Buffer::get(void *data, size_t count, size_t size)
     glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, count * size, data);
     unbind();
 }
+
+void Buffer::bind_base(GLint program, char const *name, int binding)
+{
+    bind();
+    GLuint block_index = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, name);
+    if(block_index == GL_INVALID_INDEX){
+        std::cout << "bind_base failed" << std::endl;
+    }
+    glShaderStorageBlockBinding(program, block_index, binding);
+    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, m_buffer);
+    unbind();
+}

+ 61 - 0
CppGPU/src/Canvas.cpp

@@ -0,0 +1,61 @@
+#include "Canvas.hpp"
+
+Canvas::Canvas(GLuint width, GLuint height) :
+m_width(width), m_height(height)
+{
+}
+
+Canvas::~Canvas()
+{
+    release();
+}
+
+void Canvas::create()
+{
+    /* Generation de la texture */
+    glGenTextures(1, &m_texture);
+    glBindTexture(GL_TEXTURE_2D, m_texture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 
+                 m_width, m_height, 0, GL_RGBA, 
+                 GL_UNSIGNED_BYTE, NULL);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+    
+    /* Generation du Frame Buffer */
+    glGenFramebuffers(1, &m_FBO);
+    glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
+    glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0);
+    
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+}
+
+void Canvas::bind()
+{
+
+    glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
+    glBindImageTexture(0, m_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
+    // glBindTexture(GL_TEXTURE_2D, m_texture);
+}
+
+void Canvas::unbind()
+{
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+void Canvas::draw()
+{
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, m_FBO);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+    glBlitFramebuffer(
+            0, 0, m_width, m_height,
+            0, 0, m_width, m_height,
+            GL_COLOR_BUFFER_BIT, GL_NEAREST);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+    glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+void Canvas::release()
+{
+    glDeleteTextures(1, &m_texture);
+    glDeleteFramebuffers(1, &m_FBO);  
+}

+ 37 - 4
CppGPU/ComputeShader.cpp → CppGPU/src/ComputeShader.cpp

@@ -91,9 +91,25 @@ void ComputeShader::deactivate()
     glUseProgram(0);
 }
 
-void ComputeShader::execute(int n)
+void ComputeShader::execute(size_t x, size_t group_size_x)
 {
-    glDispatchCompute(n/32 + (n % 32 ? 1 : 0), 1, 1);
+    activate();
+    size_t n_group_x = x/group_size_x + (x % group_size_x ? 1 : 0);
+    // fprintf(stdout, "Dispatching (%lu, 1, 1) groups of size (%lu, 1, 1)\n", 
+    //         n_group_x, group_size_x);
+    glDispatchCompute(n_group_x, 1, 1);
+    deactivate();
+}
+
+void ComputeShader::execute(size_t x, size_t y, size_t group_size_x, size_t group_size_y)
+{
+    activate();
+    size_t n_group_x = x/group_size_x + (x % group_size_x ? 1 : 0);
+    size_t n_group_y = y/group_size_y + (y % group_size_y ? 1 : 0);
+    // fprintf(stdout, "Dispatching (%lu, %lu, 1) groups of size (%lu, %lu, 1)\n", 
+    //         n_group_x, n_group_y, group_size_x, group_size_y);
+    glDispatchCompute(n_group_x, n_group_y, 1);
+    deactivate();
 }
 
 void ComputeShader::wait()
@@ -116,22 +132,39 @@ bool ComputeShader::release()
 
 bool ComputeShader::set_uniform(char const *name, int value)
 {
+    activate();
     GLint location = glGetUniformLocation(m_program, name);
     if(location == -1){
-        std::cout << "Uniform " << name << " not found in program." << std::endl;
+        std::cout << "Uniform 'int " << name << "' not found in program." << std::endl;
         return false;
     }
     glUniform1i(location, value);
+    deactivate();
     return true;
 }
 
 bool ComputeShader::set_uniform(char const *name, float value)
 {
+    activate();
     GLint location = glGetUniformLocation(m_program, name);
     if(location == -1){
-        std::cout << "Uniform " << name << " not found in program." << std::endl;
+        std::cout << "Uniform 'float " << name << "' not found in program." << std::endl;
         return false;
     }
     glUniform1f(location, value);
+    deactivate();
+    return true;
+}
+
+bool ComputeShader::set_uniform(char const *name, size_t value)
+{
+    activate();
+    GLint location = glGetUniformLocation(m_program, name);
+    if(location == -1){
+        std::cout << "Uniform 'uint " << name << "' not found in program." << std::endl;
+        return false;
+    }
+    glUniform1ui(location, value);
+    deactivate();
     return true;
 }

+ 182 - 0
CppGPU/src/main.cpp

@@ -0,0 +1,182 @@
+/* std */
+#include <iostream>
+#include <vector>
+#include <string>
+#include <stdlib.h>
+#include <unistd.h>  
+#include <cmath>  
+
+/* OpenGL */
+#include "glad/glad.h"
+#include <GLFW/glfw3.h>
+
+/* Project classes */
+#include "Helper.hpp"
+#include "Config.hpp"
+#include "Cell.hpp"
+#include "Particle.hpp"
+#include "ComputeShader.hpp"
+#include "Application.hpp"
+#include "Buffer.hpp"
+#include "Canvas.hpp"
+
+constexpr float deg_to_rad = (float) (3.141592653589793 / 180.0);
+
+int main(int argc, char *argv[]){
+    srand(0);
+    std::string filename;
+    Application app;
+    Config config;
+    ComputeShader cs_particles_update;
+    ComputeShader cs_map_update;
+    ComputeShader cs_map_reset;
+    ComputeShader cs_draw;
+    Buffer particles_buffer;
+    Buffer map_buffer;
+    Canvas canvas;
+
+    config = {
+        .map_width=800,
+        .map_height=600,
+
+        .population=100,
+
+        .increase=10.0f,
+        .decay=5.0f,
+        .diffusion=2.0f,
+
+        .speed=0.5f,
+        .steering=90.0f * deg_to_rad,
+        .sensing_distance=1.0f,
+        .sensing_angle=60.0f * deg_to_rad,
+    };
+    float average_population = (float)config.population / (float)(config.map_width * config.map_height);
+    float average_pheromone = (float)config.increase * average_population;
+
+    printf("Avg pop %08f - Avg phe %08f\n", average_population, average_pheromone);
+    
+    app = Application(config.map_width, config.map_height);
+    app.init();
+    app.createWindow();
+
+    /* Load and compile particles update shader */
+    cs_particles_update.load("./shaders/particles_update.glsl");
+    cs_particles_update.compile();
+    particle_uniform(config, cs_particles_update);    
+
+    /* Load and compile map update shader */
+    cs_map_update.load("./shaders/map_update.glsl");
+    cs_map_update.compile();
+    map_update_uniform(config, cs_map_update);    
+
+    /* Load and compile map reset shader */
+    cs_map_reset.load("./shaders/map_reset.glsl");
+    cs_map_reset.compile();
+    map_reset_uniform(config, cs_map_reset);    
+
+    /* Load and compile draw shader */
+    cs_draw.load("./shaders/draw.glsl");
+    cs_draw.compile();
+    cs_draw.set_uniform("map_width", config.map_width);    
+    cs_draw.set_uniform("map_height", config.map_height);    
+
+    /* Particles init */
+    std::vector<Particle> particles(config.population);
+    for(auto &particle : particles){
+        particle.pos_x = random_float(0.0f, config.map_width);
+        particle.pos_y = random_float(0.0f, config.map_height);
+
+        float angle = random_float(0, 360) * deg_to_rad;
+        particle.dir_x = cosf(angle);
+        particle.dir_y = sinf(angle);
+    }
+    particles[0].pos_x = 0.5;
+    particles[0].pos_y = 0.5;
+    particles[0].dir_x = 1;
+    particles[0].dir_y = 0;
+    printf("Bob %f, %f\n", particles[0].pos_x, particles[0].pos_y);
+
+    /* Create and fill particles buffer */
+    particles_buffer.create();
+    particles_buffer.set(particles.data(), particles.size(), sizeof(particles[0]));
+
+    /* Map init */
+    std::vector<Cell> map(config.map_width * config.map_height);
+    for(auto &cell : map){
+        cell = {
+            .pheromone = 0,
+            .result = 0,
+            .population = 0,
+        };
+    }
+
+    /* Create and fill map buffer */
+    map_buffer.create();
+    map_buffer.set(map.data(), map.size(), sizeof(map[0]));
+
+    /* Create Canvas */
+    canvas = Canvas(config.map_width, config.map_width);
+    canvas.create();
+
+    /* Bind buffers to compute shaders */
+    particles_buffer.bind_base(cs_particles_update.get_program(), "particles_buffer", 1);
+    map_buffer.bind_base(cs_particles_update.get_program(), "map_buffer", 2);
+    
+    map_buffer.bind_base(cs_map_update.get_program(), "map_buffer", 2);
+    
+    particles_buffer.bind_base(cs_draw.get_program(), "particles_buffer", 1);
+    map_buffer.bind_base(cs_draw.get_program(), "map_buffer", 2);
+    
+    map_buffer.bind_base(cs_map_reset.get_program(), "map_buffer", 2);
+
+    /* Main loop */
+    float pervious_time;
+    float current_time = app.time();
+    while(app.running()){
+        pervious_time = current_time;
+        current_time = app.time();
+
+        float dt = current_time - pervious_time;
+        // printf("[% 6f]\n", dt * 1000);
+
+        /* Update particles */
+        cs_particles_update.set_uniform("dt", dt);
+        cs_particles_update.execute(config.population, 256);
+        ComputeShader::wait();
+
+        /* Update map */
+        cs_map_update.set_uniform("dt", dt);
+        cs_map_update.execute(config.map_width, config.map_height, 16, 16);
+        ComputeShader::wait();
+        // Particle bob;
+        // particles_buffer.get(&bob, 1, sizeof(bob));
+        // size_t i = floor(bob.pos_y) * config.map_width + floor(bob.pos_x);
+        // map_buffer.get(map.data(), i+1, sizeof(map[0]));
+        // ComputeShader::wait();
+        // printf("Bob[%6f,%6f], Map[%ld] = {.pop=%d,.phe=%f}\n", 
+        //     bob.pos_x, bob.pos_y, i, map[i].population, map[i].pheromone);
+
+
+        /* Draw map */
+        cs_draw.activate();
+        canvas.bind();
+        cs_draw.execute(config.map_width, config.map_height, 16, 16);
+        ComputeShader::wait();
+        cs_draw.set_uniform("display_population_value", 10*average_population);
+        cs_draw.set_uniform("display_pheromone_value", 1000*average_pheromone);
+        canvas.draw();
+
+        app.update();
+
+        /* Reset map */
+        cs_map_reset.execute(config.population, 256);
+        ComputeShader::wait();
+
+    }
+    
+    cs_particles_update.release();
+    particles_buffer.release();
+
+
+    return 0;
+}