added openmp implementation
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 1 Dec 2020 20:10:15 +0000 (14:10 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 1 Dec 2020 20:10:15 +0000 (14:10 -0600)
Makefile
sim_omp [new file with mode: 0755]
sim_seq
src/sim_omp.cpp [new file with mode: 0644]

index 813c05e095c908b3d392ac8535d9ab31cea4e5d7..d75906b510775056916e21aeb466fa3e6d9c0652 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -17,29 +17,33 @@ INCLUDES=-I./include
 LIBS=-lGL -lglfw -lm -lpthread
 PTH_TARGET=./sim
 SEQ_TARGET=./sim_seq
+OMP_TARGET=./sim_omp
 
 ifeq ($(RELEASE), 1)
-       FLAGS=-O3
+       FLAGS=-O3 -fopenmp
 else
-       FLAGS=-g3
+       FLAGS=-g3 -fopenmp
 endif
 
 lib/%.o: lib/%.c
        $(CC) $(FLAGS) -c $< -o $@ $(INCLUDES)
 
 build/%.o: src/%.cpp
-       $(CC) $(FLAGS) -c $< -o $@ $(INCLUDES)
+       $(CC) -std=c++2a $(FLAGS) -c $< -o $@ $(INCLUDES)
 
 build/%.o: src/%.c
        $(CC) $(FLAGS) -c $< -o $@ $(INCLUDES)
 
 $(PTH_TARGET): $(OBJ_FILES) build/sim.o $(LIB_FILES)
-       $(CC) $(FLAGS) $(OBJ_FILES) build/sim.o $(LIB_FILES) -o $@ $(LIBS)
+       $(CC) -std=c++2a $(FLAGS) $(OBJ_FILES) build/sim.o $(LIB_FILES) -o $@ $(LIBS)
 
 $(SEQ_TARGET): $(OBJ_FILES) build/sim_seq.o $(LIB_FILES)
-       $(CC) $(FLAGS) $(OBJ_FILES) build/sim_seq.o $(LIB_FILES) -o $@ $(LIBS)
+       $(CC) -std=c++2a $(FLAGS) $(OBJ_FILES) build/sim_seq.o $(LIB_FILES) -o $@ $(LIBS)
+
+$(OMP_TARGET): $(OBJ_FILES) build/sim_omp.o $(LIB_FILES)
+       $(CC) -fopenmp -std=c++2a $(FLAGS) $(OBJ_FILES) build/sim_omp.o $(LIB_FILES) -o $@ $(LIBS)
 
 clean:
        rm -f $(OBJ_FILES) 2>&1 >/dev/null
 
-all: ./sim ./sim_seq
+all: ./sim ./sim_seq ./sim_omp
diff --git a/sim_omp b/sim_omp
new file mode 100755 (executable)
index 0000000..1d89882
Binary files /dev/null and b/sim_omp differ
diff --git a/sim_seq b/sim_seq
index fcfc2c41a180bd982ab8a108869d4085b7d4656a..b5823647c292d3ba5113972e9701df11c1e81a83 100755 (executable)
Binary files a/sim_seq and b/sim_seq differ
diff --git a/src/sim_omp.cpp b/src/sim_omp.cpp
new file mode 100644 (file)
index 0000000..b8c128f
--- /dev/null
@@ -0,0 +1,354 @@
+#include <cstdio>
+#include <cstdlib>
+#include <cstdbool>
+#include <cassert>
+#include <malloc.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <omp.h>
+
+#include <GLES3/gl3.h>
+#include <GLFW/glfw3.h>
+
+#include "types.h"
+#include "container.h"
+#include "utils.h"
+#include "physics.h"
+#include "ui.h"
+#include "log.h"
+#include "settings.h"
+
+// TODO(Brendan): Maybe this can be removed because it isn't really necessary?
+internal void
+glfw_key_handler(GLFWwindow* window, i32 key, i32 scancode, i32 action, i32 mods)
+{
+       if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
+               glfwSetWindowShouldClose(window, 1);
+
+    if (key == GLFW_KEY_F && action == GLFW_PRESS)
+    {
+        persist bool is_fullscreen = false;
+        is_fullscreen = !is_fullscreen;
+
+        if (is_fullscreen)
+        {
+            GLFWmonitor* primary_mon = glfwGetPrimaryMonitor();
+            const GLFWvidmode* vidmode = glfwGetVideoMode(primary_mon);
+            glfwSetWindowMonitor(window, primary_mon, 0, 0, vidmode->width, vidmode->height, GLFW_DONT_CARE);
+        }
+        else
+        {
+            glfwSetWindowMonitor(window, NULL, 0, 0, 800, 600, GLFW_DONT_CARE);
+        }
+    }
+}
+
+internal void
+glfw_error_handler(i32 error, const char* desc)
+{
+       panic_and_die("GLFW Error (%d): %s\n", error, desc);
+}
+
+GLFWwindow* window;
+internal void
+init_glfw()
+{
+    logprint(LOG_LEVEL_INFO, "Initializing GLFW");
+
+       if (!glfwInit()) panic_and_die("Failed to initalize GLFW.");
+       glfwSetErrorCallback(glfw_error_handler);
+
+       window = glfwCreateWindow(1600, 900, "N-Body Simulation", NULL, NULL);
+       glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+       glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+    glfwMakeContextCurrent(window);
+
+       glfwSwapInterval(1);
+       glfwSetKeyCallback(window, glfw_key_handler);
+
+    // NOTE(Brendan): This may need to be changed if the screen orientation changes.
+    glEnable(GL_CULL_FACE);
+    glFrontFace(GL_CW);
+    glCullFace(GL_BACK);
+
+    glEnable(GL_TEXTURE_2D);
+
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+internal void
+deinit_glfw()
+{
+       glfwDestroyWindow(window);
+       glfwTerminate();
+}
+
+#define CIRCLE_POINT_COUNT 12 // NOTE(Brendan): Treat a circle as a many-sided polygon.
+
+// NOTE(Brendan): Returns the VAO where the mesh data was bound.
+internal GLuint
+create_circle_mesh()
+{
+    logprint(LOG_LEVEL_INFO, "Generating circle mesh");
+
+    GLuint vao;
+    glGenVertexArrays(1, &vao);
+    glBindVertexArray(vao);
+
+    V2f circle_points[CIRCLE_POINT_COUNT] = {};
+    foreach (i, 0, CIRCLE_POINT_COUNT)
+    {
+        f32 t = (f32) i / (f32) CIRCLE_POINT_COUNT;
+        circle_points[i].x = cos(t * 2 * 3.1415926535897);
+        circle_points[i].y = sin(t * 2 * 3.1415926535897);
+    }
+
+    GLuint vertex_buffer;
+    glGenBuffers(1, &vertex_buffer);
+    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(circle_points), &circle_points, GL_STATIC_DRAW);
+    glEnableVertexAttribArray(0);
+    glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(V2f), (void *) offsetof(V2f, x));
+    glBindBuffer(GL_ARRAY_BUFFER, -1);
+
+    u8 circle_indicies[CIRCLE_POINT_COUNT] = {};
+    foreach(i, 0, CIRCLE_POINT_COUNT) circle_indicies[i] = i;
+
+    GLuint index_buffer;
+    glGenBuffers(1, &index_buffer);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(circle_indicies), &circle_indicies, GL_STATIC_DRAW);
+
+    glBindVertexArray(-1);
+
+    return vao;
+}
+
+struct SimState;
+struct ThreadData
+{
+    i32 id;
+    SimState* state;
+};
+
+struct SimState
+{
+    Array<Body> bodies;
+    QuadTree<Body> qt_bodies;
+    FixedArenaAllocator<QuadTree<Body>> qt_body_allocator;
+
+    Camera camera;
+};
+
+// @CLEANUP
+internal f64 TEMP_dt = 0;
+
+internal void
+sim_state_init(SimState* state)
+{
+    // NOTE(Brendan): Need to initialize the array since it does not get constructed because I refuse to use the 'new' keyword. alloc<T> uses malloc under the hood and cannot initialize the result.
+    state->bodies.init(global_settings.body_count);
+
+    foreach (i, 0, global_settings.body_count)
+    {
+        Body tmp_body;
+        tmp_body.body_type = (rand() % global_settings.body_type_count);
+        tmp_body.pos = V2f{ randf(-1000, 1000), randf(-1000, 1000) };
+        tmp_body.vel = V2f{ 0.0f, 0.0f };
+
+        BodyMassRange bmr = global_settings.body_mass_range[tmp_body.body_type];
+        tmp_body.mass = randf(bmr.min, bmr.max);
+
+        state->bodies.push(tmp_body);
+    }
+
+    state->qt_body_allocator.init(global_settings.body_count);
+    state->qt_bodies.init(AABB { -2000, -2000, 4000, 4000 });
+
+    state->camera.scale = 1.0f;
+}
+
+// NOTE CLEANUP(Brendan): Bunch of graphics state that should go elsewhere.
+internal GLuint body_buffer;
+internal GLuint circle_mesh;
+internal GLuint body_program;
+
+// TEMPORARY
+internal i32 frame_rate = 0;
+
+internal void
+draw(SimState* state)
+{
+    // NOTE(Brendan): Rebuffer all the body data to the GPU.
+    glBindBuffer(GL_ARRAY_BUFFER, body_buffer);
+    glBufferSubData(GL_ARRAY_BUFFER, 0, state->bodies.size_in_bytes(), state->bodies.data);
+    glBindBuffer(GL_ARRAY_BUFFER, -1);
+
+    glUseProgram(body_program);
+
+    mat4 ortho_mat;
+    camera_ortho_mat4(state->camera, &ortho_mat);
+    GLuint ortho_mat_loc = glGetUniformLocation(body_program, "u_proj");
+    glUniformMatrix4fv(ortho_mat_loc, 1, false, (f32 *) ortho_mat);
+    glViewport(0, 0, state->camera.window_width, state->camera.window_height);
+
+    mat4 camera_mat;
+    camera_world_mat4(state->camera, &camera_mat);
+    GLuint camera_mat_loc = glGetUniformLocation(body_program, "u_camera");
+    glUniformMatrix4fv(camera_mat_loc, 1, false, (f32 *) camera_mat);
+
+    // NOTE(Brendan): Clear the screen.
+    glClearColor(0.1, 0.1, 0.1, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    // NOTE(Brendan): Draw the bodies.
+    glBindVertexArray(circle_mesh);
+    glDrawElementsInstanced(GL_TRIANGLE_FAN, CIRCLE_POINT_COUNT, GL_UNSIGNED_BYTE, 0, state->bodies.count);
+    glBindVertexArray(-1);
+
+    font_set_projection_matrix(ortho_mat);
+    char fps_str[64];
+    snprintf(fps_str, 63, "FPS: %d", frame_rate);
+    font_print(0, 32, fps_str);
+
+    // NOTE(Brendan): Present the changes to the screen.
+    glfwSwapBuffers(window);
+}
+
+internal void
+loop(SimState* state)
+{
+    f64 last_time = glfwGetTime();
+    f64 curr_time = last_time;
+    f64 delta = 0.0;
+
+    f64 frame_delta = 0.0;
+    i32 frame_count = 0;
+    clock_t total_clock_delta = 0;
+    clock_t before_update, after_update;
+
+#pragma omp parallel
+    {
+        while (!glfwWindowShouldClose(window))
+        {
+            #pragma omp master
+            {
+                curr_time = glfwGetTime();
+                delta = curr_time - last_time;
+                last_time = curr_time;
+
+                before_update = clock();
+
+                glfwGetWindowSize(window, &state->camera.window_width, &state->camera.window_height);
+
+                persist const f32 camera_move_speed = 6;
+                if (glfwGetKey(window, GLFW_KEY_UP))    state->camera.offset.y -= camera_move_speed;
+                if (glfwGetKey(window, GLFW_KEY_DOWN))  state->camera.offset.y += camera_move_speed;
+                if (glfwGetKey(window, GLFW_KEY_LEFT))  state->camera.offset.x -= camera_move_speed;
+                if (glfwGetKey(window, GLFW_KEY_RIGHT)) state->camera.offset.x += camera_move_speed;
+                if (glfwGetKey(window, GLFW_KEY_Q))     state->camera.scale *= 1.02f;
+                if (glfwGetKey(window, GLFW_KEY_A))     state->camera.scale /= 1.02f;
+
+                TEMP_dt = delta;
+            }
+
+            #pragma omp for
+            for (int i = 0; i < global_settings.body_count; i++)
+                body_accumulate_move(&state->bodies[i], &state->qt_bodies, TEMP_dt);
+
+            #pragma omp for
+            for (int i = 0; i < global_settings.body_count; i++)
+                body_apply_move(&state->bodies[i], TEMP_dt);
+
+            #pragma omp master
+            {
+                after_update = clock();
+                total_clock_delta += (after_update - before_update);
+
+                frame_count += 1;
+                draw(state);
+
+                frame_delta += delta;
+                if (frame_delta >= 1.0)
+                {
+                    total_clock_delta = 0;
+                    frame_delta -= 1.0;
+                    frame_rate = frame_count;
+                    frame_count = 0;
+                }
+
+                glfwPollEvents();
+            }
+
+            #pragma omp single
+            {
+                state->qt_bodies.init(AABB { -2000, -2000, 4000, 4000 });
+                state->qt_body_allocator.reset();
+                For (state->bodies) state->qt_bodies.insert(&it, &state->qt_body_allocator);
+            }
+        }
+    }
+}
+
+i32
+main(i32 argc, char* argv[])
+{
+    srand(time(NULL));
+
+    if (argc > 1)
+    {
+        load_settings_from_file(&global_settings, argv[1]);
+    }
+    else
+    {
+        generate_random_settings(&global_settings);
+    }
+
+    omp_set_num_threads(global_settings.thread_count);
+
+    init_glfw();
+    defer { deinit_glfw(); };
+
+    circle_mesh = create_circle_mesh();
+
+    init_font("res/font/Hack-Regular.ttf");
+
+    body_program = create_program(load_shader(GL_VERTEX_SHADER, "res/shaders/planet_vert.glsl"),
+                                  load_shader(GL_FRAGMENT_SHADER, "res/shaders/planet_frag.glsl"));
+    glUseProgram(body_program);
+
+    GLuint planet_colors_loc = glGetUniformLocation(body_program, "u_planet_colors");
+    GLfloat* planet_colors = (GLfloat *) global_settings.body_colors.data;
+    glUniform3fv(planet_colors_loc, global_settings.body_type_count, planet_colors);
+
+    auto state = alloc<SimState>();
+    sim_state_init(state);
+
+    // NOTE(Brendan): Other OpenGL initializations that could probably happen earlier.
+    {
+        glBindVertexArray(circle_mesh);
+        defer { glBindVertexArray(-1); };
+
+        glGenBuffers(1, &body_buffer);
+        glBindBuffer(GL_ARRAY_BUFFER, body_buffer);
+        glBufferData(GL_ARRAY_BUFFER, state->bodies.size_in_bytes(), state->bodies.data, GL_STREAM_DRAW);
+
+        foreach (i, 1, 4)
+        {
+            glEnableVertexAttribArray(i);
+            glVertexAttribDivisor(i, 1);
+        }
+        glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(Body), (void *) offsetof(Body, pos.x));
+        glVertexAttribPointer(2, 1, GL_FLOAT, false, sizeof(Body), (void *) offsetof(Body, mass));
+        glVertexAttribPointer(3, 1, GL_BYTE, false, sizeof(Body), (void *) offsetof(Body, body_type));
+
+        glBindBuffer(GL_ARRAY_BUFFER, -1);
+    }
+
+    loop(state);
+
+    return 0;
+}