From d1af94520209a863fcf55f007a3bee67c8053619 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Mon, 7 Aug 2023 00:32:39 +0300 Subject: [PATCH] start reworking for checking looping neighbouring boids --- src/main.cpp | 19 ++--- src/raycast.cpp | 5 +- src/raycast.hpp | 5 +- src/simd.cpp | 2 + src/world.cpp | 201 ++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 176 insertions(+), 56 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a39cc7c..1115dc3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,10 @@ static App g_app; +// TODO: Repeat visuals in looping walls mode + +// TODO: Make WASM canvas resizable + // TODO: Add prompt for restarting program in WASM on crash // TODO: Add controls window @@ -73,14 +77,11 @@ static void profiling_test() { SetRandomSeed(10); - for (int i = 0; i < 50000; i++) { - Boid boid; - boid_rand_init(&world, &boid, 5); - world.boids.push_back(boid); - } + world_spawn_boids(&world, 50000); for (int i = 0; i < FRAMERATE; i++) { - // world_update(&world, TIME_PER_FRAME); + arena_clear(&world.frame_arena); + world_update(&world, TIME_PER_FRAME); } printf("arena: %ld (%.03fMiB)\n", world.frame_arena.offset, (float)world.frame_arena.offset / 1024 / 1024); @@ -89,8 +90,8 @@ static void profiling_test() { rprof_end(); printf("interactions: %d\n", g_prof_interactions); - int expected_interactions = 40501984; - if (g_prof_interactions != expected_interactions) { // 22 051 739 + int expected_interactions = 40134438; + if (g_prof_interactions != expected_interactions) { printf("!!!!!! ITERACTIONS DONT MATCH, %d\n", g_prof_interactions - expected_interactions); } @@ -111,7 +112,7 @@ void UpdateDrawFrame() { world_update(&g_world, dt); } #else - world_update(&g_app, &g_app.world, dt); + world_update(&g_app.world, dt); #endif visuals_update(&g_app.world, &g_app.visuals); RPROF_STOP(); diff --git a/src/raycast.cpp b/src/raycast.cpp index be27707..227ac19 100644 --- a/src/raycast.cpp +++ b/src/raycast.cpp @@ -1,4 +1,5 @@ #include "raycast.hpp" +#include "raymath.hpp" static float get_intersect_point(Vector2 ray_origin, Vector2 ray_dir, Vector2 line1, Vector2 line2) { Vector2 line_dir = Vector2Subtract(line2, line1); @@ -17,8 +18,8 @@ static void set_nearest_hit(RayHitResult *nearest_hit, float hit, Vector2 line1, bool is_gotten_hit_better = (nearest_hit->hit < 0 || nearest_hit->hit > hit); if (got_hit && is_gotten_hit_better) { nearest_hit->hit = hit; - nearest_hit->line1 = line1; - nearest_hit->line2 = line2; + // nearest_hit->line1 = line1; + // nearest_hit->line2 = line2; } } diff --git a/src/raycast.hpp b/src/raycast.hpp index 8c84d08..c77e9f5 100644 --- a/src/raycast.hpp +++ b/src/raycast.hpp @@ -4,9 +4,8 @@ struct RayHitResult { float hit = -1; - // TODO: `line1` and `line2` are not used, maybe remove them? - Vector2 line1; - Vector2 line2; + // Vector2 line1; // not used + // Vector2 line2; // not used }; static float get_intersect_point(Vector2 ray_origin, Vector2 ray_dir, Vector2 line1, Vector2 line2); diff --git a/src/simd.cpp b/src/simd.cpp index 2eb9254..3aacb80 100644 --- a/src/simd.cpp +++ b/src/simd.cpp @@ -13,6 +13,7 @@ static inline bool mm_is_zero(__simdi x) { #define mm_set1_ps(x) _mm256_set1_ps(x) #define mm_sub_ps(a, b) _mm256_sub_ps(a, b) #define mm_mul_ps(a, b) _mm256_mul_ps(a, b) +#define mm_add_ps(a, b) _mm256_add_ps(a, b) #define mm_and_ps(a, b) _mm256_and_ps(a, b) #define mm_store_ps(a, b) _mm256_store_ps(a, b) #define mm_fmadd_ps(a, b, c) _mm256_fmadd_ps(a, b, c) @@ -32,6 +33,7 @@ static inline bool mm_is_zero(__simdi x) { #define mm_set1_ps(x) _mm_set1_ps(x) #define mm_sub_ps(a, b) _mm_sub_ps(a, b) #define mm_mul_ps(a, b) _mm_mul_ps(a, b) +#define mm_add_ps(a, b) _mm_add_ps(a, b) #define mm_and_ps(a, b) _mm_and_ps(a, b) #define mm_store_ps(a, b) _mm_store_ps(a, b) #define mm_fmadd_ps(a, b, c) _mm_add_ps(_mm_mul_ps(a, b), c) diff --git a/src/world.cpp b/src/world.cpp index 8d26a4b..5b5224e 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -239,27 +239,9 @@ static void app_cleanup(App *app) { // --------------------- Utils ----------------------- -void world_adjust_boid_count(World *world, int new_boid_count) { - if (new_boid_count >= MAX_BOIDS) return; - - int boid_count = world->boids.size(); - int diff = new_boid_count - boid_count; - if (diff > 0) { - for (int i = 0; i < diff; i++) { - Boid boid; - boid_rand_init(world, &boid, 5); - world->boids.push_back(boid); - } - } else if (diff < 0) { - for (int i = 0; i < -diff ; i++) { - world->boids.pop_back(); - } - } -} - static void world_spawn_boids(World *world, size_t count) { float border = 0; - if (world->looping_walls) { + if (!world->looping_walls) { border = world->collision_avoidance_distance; } @@ -270,6 +252,25 @@ static void world_spawn_boids(World *world, size_t count) { } } +void world_adjust_boid_count(World *world, int new_boid_count) { + if (new_boid_count >= MAX_BOIDS) return; + + int boid_count = world->boids.size(); + int diff = new_boid_count - boid_count; + if (diff > 0) { + world_spawn_boids(world, diff); + } else if (diff < 0) { + for (int i = 0; i < -diff ; i++) { + world->boids.pop_back(); + } + } +} + +// TODO: Enforce max view radius, so boid could not see himself in looping world +// static float world_get_max_boid_view_radius(World *world) { +// return MIN(world->size.x/2, world->size.y/2); +// } + // --------------------- Update ----------------------- static size_t chunkgrid_get_idx(ChunkGrid *grid, int x, int y) { @@ -306,9 +307,10 @@ static int nearest_multiple(int num, int divisor) { return (num / divisor + (num % divisor > 0 ? 1 : 0)) * divisor; } -struct boid_pair { +struct boid_cmp { uboid_t from; uboid_t to; + Vector2 to_offset; }; #ifdef SIMD256 @@ -323,7 +325,25 @@ struct boid_pair { boids[b2b_cmps[8*i+1].SIDE].FIELD, \ boids[b2b_cmps[8*i+0].SIDE].FIELD \ ) +#define GET_TO_OFFSET_FROM_CMPS(i, FIELD) \ + _mm256_set_ps( \ + b2b_cmps[8*i+7].to_offset.FIELD, \ + b2b_cmps[8*i+6].to_offset.FIELD, \ + b2b_cmps[8*i+5].to_offset.FIELD, \ + b2b_cmps[8*i+4].to_offset.FIELD, \ + b2b_cmps[8*i+3].to_offset.FIELD, \ + b2b_cmps[8*i+2].to_offset.FIELD, \ + b2b_cmps[8*i+1].to_offset.FIELD, \ + b2b_cmps[8*i+0].to_offset.FIELD \ + ) #else +#define GET_TO_OFFSET_FROM_CMPS(i, FIELD) \ + _mm_set_ps( \ + b2b_cmps[4*i+3].to_offset.FIELD, \ + b2b_cmps[4*i+2].to_offset.FIELD, \ + b2b_cmps[4*i+1].to_offset.FIELD, \ + b2b_cmps[4*i+0].to_offset.FIELD \ + ) #define GET_F32_CHUNK_FROM_BOIDS(i, SIDE, FIELD) \ _mm_set_ps( \ boids[b2b_cmps[4*i+3].SIDE].FIELD, \ @@ -332,7 +352,7 @@ struct boid_pair { boids[b2b_cmps[4*i+0].SIDE].FIELD \ ) #endif -static void world_calc_distances_and_angles(World *world, BoidList *local_boids, boid_pair *b2b_cmps, int *b2b_cmps_count) { +static void world_calc_distances_and_angles(World *world, BoidList *local_boids, boid_cmp *b2b_cmps, int *b2b_cmps_count) { RPROF_START("Calc dot products and distances"); // Simplified from: // float dot_threshold = Vector2DotProduct(dir, Vector2Rotate(dir, world->view_angle/2)); @@ -359,9 +379,15 @@ static void world_calc_distances_and_angles(World *world, BoidList *local_boids, __simd from_pos_x = GET_F32_CHUNK_FROM_BOIDS(i, from, pos.x); __simd from_pos_y = GET_F32_CHUNK_FROM_BOIDS(i, from, pos.y); + __simd to_offset_pos_x = GET_TO_OFFSET_FROM_CMPS(i, x); + __simd to_offset_pos_y = GET_TO_OFFSET_FROM_CMPS(i, y); + __simd to_pos_x = GET_F32_CHUNK_FROM_BOIDS(i, to, pos.x); __simd to_pos_y = GET_F32_CHUNK_FROM_BOIDS(i, to, pos.y); + to_pos_x = mm_add_ps(to_pos_x, to_offset_pos_x); + to_pos_y = mm_add_ps(to_pos_y, to_offset_pos_y); + __simd sub_x = mm_sub_ps(from_pos_x, to_pos_x); __simd sub_y = mm_sub_ps(from_pos_y, to_pos_y); @@ -418,7 +444,8 @@ static void world_calc_distances_and_angles(World *world, BoidList *local_boids, #define B2B_CAPACITY 1024*8 #define B2B_THRESHOLD B2B_CAPACITY*0.25 -static inline void append_b2b_cmp(World *world, BoidList *local_boids, boid_pair *b2b_cmps, int *b2b_cmps_count, boid_pair b2b_cmp) { +// TODO: eeehh, a lot of parameters. Shorten? +static inline void append_b2b_cmp(World *world, BoidList *local_boids, boid_cmp *b2b_cmps, int *b2b_cmps_count, boid_cmp b2b_cmp) { if ((*b2b_cmps_count) == B2B_CAPACITY) { world_calc_distances_and_angles(world, local_boids, b2b_cmps, b2b_cmps_count); } @@ -426,12 +453,96 @@ static inline void append_b2b_cmp(World *world, BoidList *local_boids, boid_pair b2b_cmps[(*b2b_cmps_count)++] = b2b_cmp; } +// TODO: eeehh, a lot of parameters. Shorten? +static inline void append_many_b2b_cmp(World *world, BoidList *local_boids, boid_cmp *b2b_cmps, int *b2b_cmps_count, Vector2 to_offset, uboid_t from_boid, uboid_t *to_boids, int to_boids_count) { + for (int i = 0; i < to_boids_count; i++) { + boid_cmp b2b_cmp = { + .from = from_boid, + .to = to_boids[i], + .to_offset = to_offset + }; + append_b2b_cmp(world, local_boids, b2b_cmps, b2b_cmps_count, b2b_cmp); + } +} + +// TODO: Dear god :O, this name. +static inline void world_compute_local_boids_looping_top_edge(BoidList *local_boids, World *world, ChunkGrid *chunks, uboid_t **static_chunks, boid_cmp *b2b_cmps, int *b2b_cmps_count, int neighbour_y) { + Vector2 to_offset = { 0, -world->size.y }; + + for (int x = 1; x < chunks->width; x++) { + size_t chunk_idx = chunkgrid_get_idx(chunks, x, 0); + BoidList *chunk = &chunks->data[chunk_idx]; + uboid_t *chunk_boids = static_chunks[chunk_idx]; + + for (int ox = -1; ox <= 1; ox++) { + int neighbour_x = x+ox; + if (neighbour_x < 0 || neighbour_x >= chunks->width) continue; + size_t neighbour_idx = chunkgrid_get_idx(chunks, neighbour_x, neighbour_y); + BoidList *neighbour_chunk = &chunks->data[neighbour_idx]; + uboid_t *neighbour_boids = static_chunks[neighbour_idx]; + + for (int i = 0; i < chunk->count; i++) { + uboid_t from_boid = chunk_boids[i]; + append_many_b2b_cmp(world, local_boids, b2b_cmps, b2b_cmps_count, to_offset, chunk_boids[i], neighbour_boids, neighbour_chunk->count); + } + } + } +} + +// TODO: Dear god :O, this name. +static inline void world_compute_local_boids_looping_left_edge(BoidList *local_boids, World *world, ChunkGrid *chunks, uboid_t **static_chunks, boid_cmp *b2b_cmps, int *b2b_cmps_count, int neighbour_x) { + Vector2 to_offset = { -world->size.x, 0 }; + + for (int y = 1; y < chunks->height; y++) { + size_t chunk_idx = chunkgrid_get_idx(chunks, 0, y); + BoidList *chunk = &chunks->data[chunk_idx]; + uboid_t *chunk_boids = static_chunks[chunk_idx]; + + for (int oy = -1; oy <= 1; oy++) { + int neighbour_y = y+oy; + if (neighbour_y < 0 || neighbour_y >= chunks->height) continue; + size_t neighbour_idx = chunkgrid_get_idx(chunks, neighbour_x, neighbour_y); + BoidList *neighbour_chunk = &chunks->data[neighbour_idx]; + uboid_t *neighbour_boids = static_chunks[neighbour_idx]; + + for (int i = 0; i < chunk->count; i++) { + uboid_t from_boid = chunk_boids[i]; + append_many_b2b_cmp(world, local_boids, b2b_cmps, b2b_cmps_count, to_offset, chunk_boids[i], neighbour_boids, neighbour_chunk->count); + } + } + } +} + +// TODO: Dear god :O, this name. PLEASE DO SOMETHING +static inline void world_compute_local_boids_looping_top_left(BoidList *local_boids, World *world, ChunkGrid *chunks, uboid_t **static_chunks, boid_cmp *b2b_cmps, int *b2b_cmps_count, int neighbour_x) { + Vector2 to_offset = { -world->size.x, 0 }; + + for (int y = 1; y < chunks->height; y++) { + size_t chunk_idx = chunkgrid_get_idx(chunks, 0, y); + BoidList *chunk = &chunks->data[chunk_idx]; + uboid_t *chunk_boids = static_chunks[chunk_idx]; + + for (int oy = -1; oy <= 1; oy++) { + int neighbour_y = y+oy; + if (neighbour_y < 0 || neighbour_y >= chunks->height) continue; + size_t neighbour_idx = chunkgrid_get_idx(chunks, neighbour_x, neighbour_y); + BoidList *neighbour_chunk = &chunks->data[neighbour_idx]; + uboid_t *neighbour_boids = static_chunks[neighbour_idx]; + + for (int i = 0; i < chunk->count; i++) { + uboid_t from_boid = chunk_boids[i]; + append_many_b2b_cmp(world, local_boids, b2b_cmps, b2b_cmps_count, to_offset, chunk_boids[i], neighbour_boids, neighbour_chunk->count); + } + } + } +} + static void world_compute_local_boids(BoidList *local_boids, World *world, ChunkGrid *chunks) { Boid *boids = world->boids.data(); int boid_count = world->boids.size(); MemoryArena *arena = &world->frame_arena; - boid_pair *b2b_cmps = (boid_pair*)arena_malloc(arena, (B2B_CAPACITY + SIMD_32B_LANES)*sizeof(boid_pair)); + boid_cmp *b2b_cmps = (boid_cmp*)arena_malloc(arena, (B2B_CAPACITY + SIMD_32B_LANES)*sizeof(boid_cmp)); int b2b_cmps_count = 0; RPROF_START("Move chunk data to static arrays"); @@ -446,7 +557,7 @@ static void world_compute_local_boids(BoidList *local_boids, World *world, Chunk } RPROF_STOP(); - RPROF_START("Iterate over chunks"); + RPROF_START("Iterate over chunks (for local boids)"); for (int y = 0; y < chunks->height; y++) { for (int x = 0; x < chunks->width; x++) { size_t chunk_idx = chunkgrid_get_idx(chunks, x, y); @@ -459,13 +570,7 @@ static void world_compute_local_boids(BoidList *local_boids, World *world, Chunk uboid_t *to_boids = &chunk_boids[i+1]; uboid_t to_boids_count = chunk->count-i-1; - for (int j = 0; j < to_boids_count; j++) { - boid_pair b2b_cmp = { - .from = from_boid, - .to = to_boids[j] - }; - append_b2b_cmp(world, local_boids, b2b_cmps, &b2b_cmps_count, b2b_cmp); - } + append_many_b2b_cmp(world, local_boids, b2b_cmps, &b2b_cmps_count, { 0 }, from_boid, to_boids, to_boids_count); } Vector2 neighbours[] = { { 1, 0 }, { 0, 1 }, { 1, 1 }, { -1, 1 } }; @@ -477,17 +582,11 @@ static void world_compute_local_boids(BoidList *local_boids, World *world, Chunk size_t neighbour_idx = chunkgrid_get_idx(chunks, chunk_x, chunk_y); BoidList *neighbour_chunk = &chunks->data[neighbour_idx]; - if (neighbour_chunk->count == 0) continue; uboid_t *neighbour_boids = static_chunks[neighbour_idx]; for (int i = 0; i < chunk->count; i++) { - for (int j = 0; j < neighbour_chunk->count; j++) { - boid_pair b2b_cmp = { - .from = chunk_boids[i], - .to = neighbour_boids[j] - }; - append_b2b_cmp(world, local_boids, b2b_cmps, &b2b_cmps_count, b2b_cmp); - } + uboid_t from_boid = chunk_boids[i]; + append_many_b2b_cmp(world, local_boids, b2b_cmps, &b2b_cmps_count, { 0 }, from_boid, neighbour_boids, neighbour_chunk->count); } } @@ -497,6 +596,23 @@ static void world_compute_local_boids(BoidList *local_boids, World *world, Chunk } } + // For checking neighbours on the edge of the wall + if (world->looping_walls) { + // TODO: Rewrite a nicer version here. + + // world_compute_local_boids_looping_top_edge(local_boids, world, chunks, static_chunks, b2b_cmps, &b2b_cmps_count, chunks->height-1); + // world_compute_local_boids_looping_left_edge(local_boids, world, chunks, static_chunks, b2b_cmps, &b2b_cmps_count, chunks->width-1); + // + // if (fmod(world->size.y, chunks->chunk_size) >= 1) { + // world_compute_local_boids_looping_top_edge(local_boids, world, chunks, static_chunks, b2b_cmps, &b2b_cmps_count, chunks->height-2); + // } + // if (fmod(world->size.x, chunks->chunk_size) >= 1) { + // world_compute_local_boids_looping_left_edge(local_boids, world, chunks, static_chunks, b2b_cmps, &b2b_cmps_count, chunks->width-2); + // } + + // TODO: Check top left chunk + } + if (b2b_cmps_count > 0) { world_calc_distances_and_angles(world, local_boids, b2b_cmps, &b2b_cmps_count); } @@ -544,6 +660,7 @@ static BoidList* world_compute_local_boids(World *world) { } static void aggregate_flock_modifiers(World *world, BoidList *flock, Boid *boid, Vector2 *center, Vector2 *heading, Vector2 *separation_force) { + // TODO: use simd here *center = { 0, 0 }; *heading = { 0, 0 }; *separation_force = { 0, 0 }; @@ -564,14 +681,14 @@ static void aggregate_flock_modifiers(World *world, BoidList *flock, Boid *boid, *center = vector2_div_value(*center, flock->count); } -static void world_update(App *app, World *world, float dt) { - if (world->freeze) return; - +static void world_update(World *world, float dt) { Boid *boids = world->boids.data(); int boid_count = world->boids.size(); DEBUG_ASSERT(boid_count <= MAX_BOIDS); world_place_boids_into_chunks(world); + if (world->freeze) return; + BoidList *list_of_local_boids = world_compute_local_boids(world); RPROF_START("Apply forces");