From: Brendan Hansen Date: Tue, 1 Dec 2020 20:10:15 +0000 (-0600) Subject: added openmp implementation X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b79587072fd4bfa12a2a49feab84bf3571efad2d;p=csc718.git added openmp implementation --- diff --git a/Makefile b/Makefile index 813c05e..d75906b 100644 --- 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 index 0000000..1d89882 Binary files /dev/null and b/sim_omp differ diff --git a/sim_seq b/sim_seq index fcfc2c4..b582364 100755 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 index 0000000..b8c128f --- /dev/null +++ b/src/sim_omp.cpp @@ -0,0 +1,354 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 bodies; + QuadTree qt_bodies; + FixedArenaAllocator> 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 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(); + 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; +}