Commit: 0163bc9079c9e3c72220ae82a3c40923f160e76e
Parent: 2a64126fc9cf1a5c754c1700a7dcc5d2f1bedcb3
Author: Randy Palamar
Date: Tue, 18 Jun 2024 12:40:45 -0600
start drawing rf data to the background
Diffstat:
M | beamformer.c | | | 74 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
M | build.sh | | | 2 | +- |
M | main.c | | | 176 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
A | os_unix.c | | | 61 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | util.c | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 387 insertions(+), 18 deletions(-)
diff --git a/beamformer.c b/beamformer.c
@@ -3,11 +3,81 @@
#include <raylib.h>
#include <rlgl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES 1
+#include <GL/glcorearb.h>
+#include <GL/glext.h>
+#endif
+
#include "util.c"
DEBUG_EXPORT void
-do_beamformer(BeamformerCtx *ctx)
+do_beamformer(BeamformerCtx *ctx, Arena arena, s8 rf_data)
{
- ClearBackground(DARKGRAY);
+ uv2 ws = ctx->window_size;
+ f32 dt = GetFrameTime();
+
+ static v2 pos = {.x = 32, .y = 128 };
+ static v2 scale = {.x = 1.0, .y = 1.0};
+ static u32 txt_idx = 0;
+ static char *txt[2] = { "-_-", "^_^" };
+
+ Font font = GetFontDefault();
+ v2 fs = { .rl = MeasureTextEx(font, txt[txt_idx], 60, 6) };
+
+ pos.x += 130 * dt * scale.x;
+ pos.y += 120 * dt * scale.y;
+
+ if (pos.x > (ws.w - fs.x) || pos.x < 0) {
+ txt_idx = !txt_idx;
+ fs = (v2){ .rl = MeasureTextEx(font, txt[txt_idx], 60, 6) };
+ CLAMP(pos.x, 0, ws.w - fs.x);
+ scale.x *= -1.0;
+ }
+
+ if (pos.y > (ws.h - fs.y) || pos.y < 0) {
+ txt_idx = !txt_idx;
+ fs = (v2){ .rl = MeasureTextEx(font, txt[txt_idx], 60, 6) };
+ CLAMP(pos.y, 0, ws.h - fs.y);
+ scale.y *= -1.0;
+ }
+
+ {
+ ComputeShaderCtx *csctx = &ctx->csctx;
+ glUseProgram(csctx->programs[CS_UFORCES]);
+
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, csctx->rf_data_ssbo);
+ glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, rf_data.len, rf_data.data);
+
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, csctx->rf_data_ssbo);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, csctx->out_img_ssbo);
+
+ glUniform3uiv(csctx->u_rf_dim_id, 1, csctx->rf_data_dim.E);
+ glUniform3ui(csctx->u_out_dim_id, ctx->out_img_dim.x, ctx->out_img_dim.y, 0);
+
+ glDispatchCompute(ctx->out_img_dim.x, ctx->out_img_dim.y, 1);
+ }
+
+ BeginDrawing();
+
+ ClearBackground(PINK);
+
+ BeginShaderMode(ctx->fsctx.shader);
+ glUseProgram(ctx->fsctx.shader.id);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ctx->csctx.out_img_ssbo);
+ glUniform2uiv(ctx->fsctx.u_out_dim_id, 1, ctx->out_img_dim.E);
+ ASSERT(ctx->fsctx.u_out_dim_id != -1);
+ DrawTexture(ctx->fsctx.output, 0, 0, WHITE);
+ EndShaderMode();
+
DrawFPS(20, 20);
+ DrawTextEx(font, txt[txt_idx], pos.rl, 60, 6, BLACK);
+
+ EndDrawing();
+
+ if (IsKeyPressed(KEY_R))
+ ctx->flags |= RELOAD_SHADERS;
}
diff --git a/build.sh b/build.sh
@@ -1,7 +1,7 @@
#!/bin/sh
cflags="-march=native -ggdb -O0 -Wall"
-ldflags="-lraylib"
+ldflags="-lraylib -lGL"
# Hot Reloading/Debugging
cflags="$cflags -D_DEBUG"
diff --git a/main.c b/main.c
@@ -3,7 +3,20 @@
#include <raylib.h>
#include <rlgl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define GL_GLEXT_PROTOTYPES 1
+#include <GL/glcorearb.h>
+#include <GL/glext.h>
+
#include "util.c"
+#include "os_unix.c"
+
+static char *compute_shader_paths[CS_LAST] = {
+ //[CS_MIN_MAX] = "shaders/min_max.glsl",
+ [CS_UFORCES] = "shaders/uforces.glsl",
+};
#ifndef _DEBUG
@@ -12,23 +25,19 @@ static void do_debug(void) { }
#else
#include <dlfcn.h>
-#include <sys/stat.h>
#include <time.h>
-#include <unistd.h>
-static const char *libname = "./beamformer.so";
+static char *libname = "./beamformer.so";
static void *libhandle;
-typedef void (do_beamformer_fn)(BeamformerCtx*);
+typedef void do_beamformer_fn(BeamformerCtx *, Arena, s8);
static do_beamformer_fn *do_beamformer;
-static struct timespec
-get_filetime(const char *name)
+static os_filetime
+get_filetime(char *name)
{
- struct stat sb;
- if (stat(name, &sb) < 0)
- return (struct timespec){0};
- return sb.st_mtim;
+ os_file_stats fstats = os_get_file_stats(name);
+ return fstats.timestamp;
}
static b32
@@ -55,8 +64,8 @@ load_library(const char *lib)
static void
do_debug(void)
{
- static struct timespec updated_time;
- struct timespec test_time = get_filetime(libname);
+ static os_filetime updated_time;
+ os_filetime test_time = get_filetime(libname);
if (filetime_is_newer(test_time, updated_time)) {
struct timespec sleep_time = {.tv_sec = 0, .tv_nsec = 100e6};
nanosleep(&sleep_time, &sleep_time);
@@ -67,12 +76,141 @@ do_debug(void)
#endif /* _DEBUG */
+#if 0
+static void
+update_output_image_dimensions(BeamformerCtx *ctx, uv2 new_size)
+{
+ UnloadTexture(ctx->fsctx.output);
+ rlUnloadShaderBuffer(ctx->csctx.out_img_ssbo);
+
+ size out_img_size = new_size.w * new_size.h * sizeof(f32);
+ ctx->csctx.out_img_ssbo = rlLoadShaderBuffer(out_img_size, NULL, GL_DYNAMIC_COPY);
+
+ Texture2D t = ctx->fsctx.output;
+ t.width = new_size.w;
+ t.height = new_size.h;
+ t.id = rlLoadTexture(0, t.width, t.height, t.format, t.mipmaps);
+ ctx->fsctx.output = t;
+}
+#endif
+
+static void
+init_compute_shader_ctx(ComputeShaderCtx *ctx, uv3 rf_data_dim, uv2 out_img_dim)
+{
+ for (u32 i = 0; i < ARRAY_COUNT(ctx->programs); i++) {
+ char *shader_text = LoadFileText(compute_shader_paths[i]);
+ u32 shader_id = rlCompileShader(shader_text, RL_COMPUTE_SHADER);
+ ctx->programs[i] = rlLoadComputeShaderProgram(shader_id);
+ glDeleteShader(shader_id);
+ UnloadFileText(shader_text);
+ }
+
+ ctx->u_rf_dim_id = glGetUniformLocation(ctx->programs[CS_UFORCES], "u_rf_dim");
+ ctx->u_out_dim_id = glGetUniformLocation(ctx->programs[CS_UFORCES], "u_out_dim");
+
+ ctx->rf_data_dim = rf_data_dim;
+
+ size rf_data_size = rf_data_dim.w * rf_data_dim.h * rf_data_dim.d * sizeof(f32);
+ size out_img_size = out_img_dim.w * out_img_dim.h * sizeof(f32);
+
+ ctx->rf_data_ssbo = rlLoadShaderBuffer(rf_data_size, NULL, GL_DYNAMIC_COPY);
+ ctx->out_img_ssbo = rlLoadShaderBuffer(out_img_size, NULL, GL_DYNAMIC_COPY);
+}
+
+static void
+init_fragment_shader_ctx(FragmentShaderCtx *ctx, uv2 window_size)
+{
+ ctx->shader = LoadShader(NULL, "shaders/render.glsl");
+ ctx->u_out_dim_id = glGetUniformLocation(ctx->shader.id, "u_out_img_dim");
+ glUniform2uiv(ctx->u_out_dim_id, 1, window_size.E);
+ /* TODO: add min max uniform */
+
+ /* output texture for image blitting */
+ Texture2D new;
+ new.width = window_size.w;
+ new.height = window_size.h;
+ new.mipmaps = 1;
+ new.format = RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32;
+ new.id = rlLoadTexture(0, new.width, new.height, new.format, new.mipmaps);
+ ctx->output = new;
+}
+
+static u32
+compile_shader(Arena a, u32 type, s8 shader)
+{
+ u32 sid = glCreateShader(type);
+ glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len);
+ glCompileShader(sid);
+
+ i32 res = 0;
+ glGetShaderiv(sid, GL_COMPILE_STATUS, &res);
+
+ char *stype;
+ switch (type) {
+ case GL_COMPUTE_SHADER: stype = "Compute"; break;
+ case GL_FRAGMENT_SHADER: stype = "Fragment"; break;
+ }
+
+ if (res == GL_FALSE) {
+ TraceLog(LOG_WARNING, "SHADER: [ID %u] %s shader failed to compile", sid, stype);
+ i32 len = 0;
+ glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len);
+ s8 err = s8alloc(&a, len);
+ glGetShaderInfoLog(sid, len, (int *)&err.len, (char *)err.data);
+ TraceLog(LOG_WARNING, "SHADER: [ID %u] Compile error: %s", sid, (char *)err.data);
+ glDeleteShader(sid);
+ } else {
+ TraceLog(LOG_INFO, "SHADER: [ID %u] %s shader compiled successfully", sid, stype);
+ }
+
+ return sid;
+}
+
+static void
+reload_shaders(Arena a, BeamformerCtx *ctx)
+{
+ u32 cs_programs[ARRAY_COUNT(((ComputeShaderCtx *)0)->programs)];
+
+ ComputeShaderCtx *csctx = &ctx->csctx;
+ for (u32 i = 0; i < ARRAY_COUNT(cs_programs); i++) {
+ Arena tmp = a;
+ os_file_stats fs = os_get_file_stats(compute_shader_paths[i]);
+ s8 shader_text = os_read_file(&tmp, compute_shader_paths[i], fs.filesize);
+ u32 shader_id = compile_shader(tmp, GL_COMPUTE_SHADER, shader_text);
+
+ if (shader_id != rlGetShaderIdDefault()) {
+ glDeleteProgram(csctx->programs[i]);
+ csctx->programs[i] = rlLoadComputeShaderProgram(shader_id);
+ }
+
+ glDeleteShader(shader_id);
+ }
+
+ csctx->u_rf_dim_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_rf_dim");
+ csctx->u_out_dim_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_out_dim");
+
+ Shader updated_fs = LoadShader(NULL, "shaders/render.glsl");
+ if (updated_fs.id != rlGetShaderIdDefault()) {
+ UnloadShader(ctx->fsctx.shader);
+ ctx->fsctx.shader = updated_fs;
+ ctx->fsctx.u_out_dim_id = GetShaderLocation(updated_fs, "u_out_img_dim");
+ glUniform2ui(ctx->fsctx.u_out_dim_id, ctx->out_img_dim.x, ctx->out_img_dim.y);
+ }
+}
+
int
main(void)
{
BeamformerCtx ctx = {0};
+ Arena temp_arena = os_new_arena(256 * MEGABYTE);
+
+ char *decoded_name = "/tmp/decoded.bin";
+ os_file_stats decoded_stats = os_get_file_stats(decoded_name);
+ s8 raw_rf_data = os_read_file(&temp_arena, decoded_name, decoded_stats.filesize);
+
ctx.window_size = (uv2){.w = 720, .h = 720};
+ ctx.out_img_dim = (uv2){.w = 720, .h = 720};
ctx.bg = DARKGRAY;
ctx.fg = (Color){ .r = 0xea, .g = 0xe1, .b = 0xb4, .a = 0xff };
@@ -80,11 +218,19 @@ main(void)
SetConfigFlags(FLAG_VSYNC_HINT);
InitWindow(ctx.window_size.w, ctx.window_size.h, "OGL Beamformer");
+ init_compute_shader_ctx(&ctx.csctx, (uv3){.w = 4093, .h = 128, .d = 1}, ctx.window_size);
+ init_fragment_shader_ctx(&ctx.fsctx, ctx.window_size);
+
+ ctx.flags |= RELOAD_SHADERS;
+
while(!WindowShouldClose()) {
do_debug();
- BeginDrawing();
- do_beamformer(&ctx);
- EndDrawing();
+ if (ctx.flags & RELOAD_SHADERS) {
+ ctx.flags &= ~RELOAD_SHADERS;
+ reload_shaders(temp_arena, &ctx);
+ }
+
+ do_beamformer(&ctx, temp_arena, raw_rf_data);
}
}
diff --git a/os_unix.c b/os_unix.c
@@ -0,0 +1,61 @@
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+typedef struct timespec os_filetime;
+
+typedef struct {
+ size filesize;
+ os_filetime timestamp;
+} os_file_stats;
+
+static Arena
+os_new_arena(size capacity)
+{
+ Arena a = {0};
+
+ size pagesize = sysconf(_SC_PAGESIZE);
+ if (capacity % pagesize != 0)
+ capacity += (pagesize - capacity % pagesize);
+
+ a.beg = mmap(0, capacity, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ if (a.beg == MAP_FAILED)
+ die("os_new_arena: couldn't allocate memory\n");
+ a.end = a.beg + capacity;
+ return a;
+}
+
+static s8
+os_read_file(Arena *a, char *fname, size fsize)
+{
+ i32 fd = open(fname, O_RDONLY);
+ if (fd < 0)
+ die("os_read_file: couldn't open file: %s\n", fname);
+
+ s8 ret = s8alloc(a, fsize);
+
+ size rlen = read(fd, ret.data, ret.len);
+ close(fd);
+
+ if (rlen != ret.len)
+ die("os_read_file: couldn't read file: %s\n", fname);
+
+ return ret;
+}
+
+static os_file_stats
+os_get_file_stats(char *fname)
+{
+ struct stat st;
+
+ if (stat(fname, &st) < 0) {
+ fputs("os_get_file_stats: couldn't stat file\n",stderr);
+ return (os_file_stats){0};
+ }
+
+ return (os_file_stats){
+ .filesize = st.st_size,
+ .timestamp = st.st_mtim,
+ };
+}
diff --git a/util.c b/util.c
@@ -14,6 +14,7 @@
#define DEBUG_EXPORT static
#endif
+typedef uint8_t u8;
typedef int32_t i32;
typedef uint32_t u32;
typedef uint32_t b32;
@@ -21,6 +22,10 @@ typedef float f32;
typedef double f64;
typedef ptrdiff_t size;
+typedef struct { u8 *beg, *end; } Arena;
+
+typedef struct { size len; u8 *data; } s8;
+
typedef union {
struct { u32 x, y; };
struct { u32 w, h; };
@@ -28,6 +33,12 @@ typedef union {
} uv2;
typedef union {
+ struct { u32 x, y, z; };
+ struct { u32 w, h, d; };
+ u32 E[3];
+} uv3;
+
+typedef union {
struct { f32 x, y; };
f32 E[2];
Vector2 rl;
@@ -40,15 +51,96 @@ typedef union {
Vector4 rl;
} v4;
+enum compute_shaders {
+// CS_FORCES,
+// CS_HADAMARD_DECODE,
+// CS_HERCULES,
+// CS_MIN_MAX,
+ CS_UFORCES,
+ CS_LAST
+};
+
+enum program_flags {
+ RELOAD_SHADERS = 1 << 0,
+};
+
+typedef struct {
+ u32 programs[CS_LAST];
+
+ u32 out_img_ssbo;
+
+ u32 rf_data_ssbo;
+ uv3 rf_data_dim;
+
+ i32 u_rf_dim_id;
+ i32 u_out_dim_id;
+} ComputeShaderCtx;
+
+typedef struct {
+ Shader shader;
+ Texture2D output;
+ i32 u_out_dim_id;
+} FragmentShaderCtx;
+
typedef struct {
uv2 window_size;
u32 flags;
Color bg, fg;
+
+ uv2 out_img_dim; /* shared output image dimension */
+
+ ComputeShaderCtx csctx;
+ FragmentShaderCtx fsctx;
} BeamformerCtx;
+#define MEGABYTE (1024ULL * 1024ULL)
+#define GIGABYTE (1024ULL * 1024ULL * 1024ULL)
+
#define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a))
#define ABS(x) ((x) < 0 ? (-x) : (x))
#define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
+static void __attribute__((noreturn))
+die(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+static void *
+mem_clear(u8 *p, u8 c, size len)
+{
+ while (len) p[--len] = c;
+ return p;
+}
+
+#define alloc(a, t, n) (t *)alloc_(a, sizeof(t), _Alignof(t), n)
+__attribute((malloc, alloc_size(4, 2), alloc_align(3)))
+static void *
+alloc_(Arena *a, size len, size align, size count)
+{
+ size padding = -(uintptr_t)a->beg & (align - 1);
+ size available = a->end - a->beg - padding;
+ if (available < 0 || count > available / len) {
+ ASSERT(0);
+ die("arena OOM\n");
+ }
+ void *p = a->beg + padding;
+ a->beg += padding + count * len;
+ /* TODO: Performance? */
+ return mem_clear(p, 0, count * len);
+}
+
+static s8
+s8alloc(Arena *a, size len)
+{
+ return (s8){ .data = alloc(a, u8, len), .len = len };
+}
+
#endif /*_UTIL_C_ */