Created
July 15, 2023 19:31
-
-
Save matias-eduardo/788fa6374c60e453b7534cd796f98867 to your computer and use it in GitHub Desktop.
Particles with SOA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
#if defined(__linux) | |
#define HAVE_POSIX_TIMER | |
#include <time.h> | |
#ifdef CLOCK_MONOTONIC | |
#define CLOCKID CLOCK_MONOTONIC | |
#else | |
#define CLOCKID CLOCK_REALTIME | |
#endif | |
#elif defined(__APPLE__) | |
#define HAVE_MACH_TIMER | |
#include <mach/mach_time.h> | |
#elif defined(_WIN32) | |
#define WIN32_LEAN_AND_MEAN | |
#include <windows.h> | |
#endif | |
static uint64_t ns() { | |
static uint64_t is_init = 0; | |
#if defined(__APPLE__) | |
static mach_timebase_info_data_t info; | |
if (0 == is_init) { | |
mach_timebase_info(&info); | |
is_init = 1; | |
} | |
uint64_t now; | |
now = mach_absolute_time(); | |
now *= info.numer; | |
now /= info.denom; | |
return now; | |
#elif defined(__linux) | |
static struct timespec linux_rate; | |
if (0 == is_init) { | |
clock_getres(CLOCKID, &linux_rate); | |
is_init = 1; | |
} | |
uint64_t now; | |
struct timespec spec; | |
clock_gettime(CLOCKID, &spec); | |
now = spec.tv_sec * 1.0e9 + spec.tv_nsec; | |
return now; | |
#elif defined(_WIN32) | |
static LARGE_INTEGER win_frequency; | |
if (0 == is_init) { | |
QueryPerformanceFrequency(&win_frequency); | |
is_init = 1; | |
} | |
LARGE_INTEGER now; | |
QueryPerformanceCounter(&now); | |
return (uint64_t) ((1e9 * now.QuadPart) / win_frequency.QuadPart); | |
#endif | |
} | |
/////////////////////////////// | |
typedef struct vec3 { | |
float x, y, z; | |
} vec3; | |
vec3 vec3_add(vec3 a, vec3 b) { | |
return (vec3){ | |
a.x + b.x, | |
a.y + b.y, | |
a.z + b.z, | |
}; | |
} | |
vec3 vec3_sub(vec3 a, vec3 b) { | |
return (vec3){ | |
a.x - b.x, | |
a.y - b.y, | |
a.z - b.z, | |
}; | |
} | |
vec3 vec3_mulf(vec3 a, float f) { | |
return (vec3){ | |
a.x * f, | |
a.y * f, | |
a.z * f, | |
}; | |
} | |
float rand_float(float min, float max) { | |
return ((float)rand() / (float)RAND_MAX) * (max - min) + min; | |
} | |
/////////////////////////////// | |
#define PARTICLES_MAX 32768 | |
typedef struct Globals { | |
int particle_count; | |
vec3 particle_pos[PARTICLES_MAX]; | |
vec3 particle_vel[PARTICLES_MAX]; | |
float particle_time[PARTICLES_MAX]; | |
} Globals; | |
static Globals o = {0}; | |
static void particles_create() { | |
#define SPAWN_CHANCE 50 | |
int new_chance = rand() % SPAWN_CHANCE; | |
if (new_chance == 0) { | |
int new_count = rand() % 1000; | |
if ((o.particle_count + new_count) > PARTICLES_MAX) | |
new_count = PARTICLES_MAX - o.particle_count; | |
for (int i = 0; i < new_count; i++) | |
o.particle_pos[o.particle_count+i] = (vec3){0, 0, 0}; | |
for (int i = 0; i < new_count; i++) | |
o.particle_vel[o.particle_count+i] = (vec3){rand_float(-1000, 1000), rand_float(0, 1000), rand_float(-1000, 1000)}; | |
for (int i = 0; i < new_count; i++) | |
o.particle_time[o.particle_count+i] = rand_float(0.5, 5.0); | |
o.particle_count += new_count; | |
} | |
} | |
static void particles_update() { | |
float tick = 1.0 / 60.0; | |
float half_tick = tick * 0.5; | |
for (int i = 0; i < o.particle_count; i++) | |
o.particle_vel[i].y += 8.0; | |
for (int i = 0; i < o.particle_count; i++) | |
o.particle_vel[i] = vec3_sub(o.particle_vel[i], vec3_mulf(o.particle_vel[i], half_tick)); | |
for (int i = 0; i < o.particle_count; i++) | |
o.particle_pos[i] = vec3_add(o.particle_pos[i], vec3_mulf(o.particle_vel[i], tick)); | |
for (int i = 0; i < o.particle_count; i++) { | |
o.particle_time[i] -= tick; | |
if (o.particle_time[i] < 0) { | |
o.particle_count--; | |
o.particle_pos[i] = o.particle_pos[o.particle_count]; | |
o.particle_vel[i] = o.particle_vel[o.particle_count]; | |
o.particle_time[i] = o.particle_time[o.particle_count]; | |
i--; | |
} | |
} | |
} | |
int main(int argc, char **argv) { | |
// bench: init | |
srand(0xC0FFE); | |
// bench: warmup | |
#define WARMUP_RUNS 1000 | |
for (int benchmark_i = 0; benchmark_i < WARMUP_RUNS; benchmark_i++) { | |
particles_create(); | |
particles_update(); | |
} | |
// bench: run | |
#define RUNS 100000 | |
uint64_t time_start = ns(); | |
for (int benchmark_i = 0; benchmark_i < RUNS; benchmark_i++) { | |
particles_create(); | |
particles_update(); | |
} | |
// bench: time | |
uint64_t total_time = ns() - time_start; | |
printf("In-place move, spawn chance 1/50, SOA: %8.2f ms\n", (double)total_time/1000000.0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment