main.c (11501B)
1 /* See LICENSE for license details. */ 2 #include "beamformer.h" 3 4 static s8 compute_shader_paths[CS_LAST] = { 5 [CS_HADAMARD] = s8("shaders/hadamard.glsl"), 6 [CS_HERCULES] = s8("shaders/hercules.glsl"), 7 [CS_DEMOD] = s8("shaders/demod.glsl"), 8 [CS_MIN_MAX] = s8("shaders/min_max.glsl"), 9 [CS_SUM] = s8("shaders/sum.glsl"), 10 [CS_UFORCES] = s8("shaders/uforces.glsl"), 11 }; 12 13 #ifndef _DEBUG 14 15 #include "beamformer.c" 16 static void do_debug(void) { } 17 18 #else 19 static os_library_handle libhandle; 20 21 typedef void do_beamformer_fn(BeamformerCtx *, Arena); 22 static do_beamformer_fn *do_beamformer; 23 24 static void 25 do_debug(void) 26 { 27 static f32 updated_time; 28 FileStats test_stats = os_get_file_stats(OS_DEBUG_LIB_NAME); 29 if (test_stats.filesize > 32 && test_stats.timestamp > updated_time) { 30 os_unload_library(libhandle); 31 libhandle = os_load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME); 32 do_beamformer = os_lookup_dynamic_symbol(libhandle, "do_beamformer"); 33 updated_time = test_stats.timestamp; 34 } 35 } 36 37 #endif /* _DEBUG */ 38 39 /* NOTE: cuda lib stubs */ 40 INIT_CUDA_CONFIGURATION_FN(init_cuda_configuration_stub) {} 41 REGISTER_CUDA_BUFFERS_FN(register_cuda_buffers_stub) {} 42 CUDA_DECODE_FN(cuda_decode_stub) {} 43 CUDA_HILBERT_FN(cuda_hilbert_stub) {} 44 45 static void 46 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *userctx) 47 { 48 (void)src; (void)type; (void)id; (void)userctx; 49 50 u8 buf[128]; 51 Stream s = {.data = buf, .cap = ARRAY_COUNT(buf)}; 52 stream_append_s8(&s, s8("[GL DEBUG ")); 53 switch (lvl) { 54 case GL_DEBUG_SEVERITY_HIGH: stream_append_s8(&s, s8("HIGH]: ")); break; 55 case GL_DEBUG_SEVERITY_MEDIUM: stream_append_s8(&s, s8("MEDIUM]: ")); break; 56 case GL_DEBUG_SEVERITY_LOW: stream_append_s8(&s, s8("LOW]: ")); break; 57 case GL_DEBUG_SEVERITY_NOTIFICATION: stream_append_s8(&s, s8("NOTIFICATION]: ")); break; 58 default: stream_append_s8(&s, s8("INVALID]: ")); break; 59 } 60 os_write_err_msg(stream_to_s8(s)); 61 os_write_err_msg((s8){.len = len, .data = (u8 *)msg}); 62 os_write_err_msg(s8("\n")); 63 } 64 65 static void 66 get_gl_params(GLParams *gl) 67 { 68 char *vendor = (char *)glGetString(GL_VENDOR); 69 if (!vendor) { 70 os_write_err_msg(s8("Failed to determine GL Vendor\n")); 71 os_fail(); 72 } 73 switch (vendor[0]) { 74 case 'A': gl->vendor_id = GL_VENDOR_AMD; break; 75 case 'I': gl->vendor_id = GL_VENDOR_INTEL; break; 76 case 'N': gl->vendor_id = GL_VENDOR_NVIDIA; break; 77 default: { 78 os_write_err_msg(s8("Unknown GL Vendor: ")); 79 os_write_err_msg(cstr_to_s8(vendor)); 80 os_write_err_msg(s8("\n")); 81 os_fail(); 82 } break; 83 } 84 85 glGetIntegerv(GL_MAJOR_VERSION, &gl->version_major); 86 glGetIntegerv(GL_MINOR_VERSION, &gl->version_minor); 87 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl->max_2d_texture_dim); 88 glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl->max_3d_texture_dim); 89 glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &gl->max_ssbo_size); 90 glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &gl->max_ubo_size); 91 } 92 93 static void 94 validate_gl_requirements(GLParams *gl) 95 { 96 ASSERT(gl->max_ubo_size >= sizeof(BeamformerParameters)); 97 if (gl->version_major < 4 || (gl->version_major == 4 && gl->version_minor < 5)) { 98 os_write_err_msg(s8("Only OpenGL Versions 4.5 or newer are supported!\n")); 99 os_fail(); 100 } 101 } 102 103 static void 104 dump_gl_params(GLParams *gl, Arena a) 105 { 106 (void)gl; (void)a; 107 #ifdef _DEBUG 108 Stream s = stream_alloc(&a, 1 * MEGABYTE); 109 stream_append_s8(&s, s8("---- GL Parameters ----\n")); 110 switch (gl->vendor_id) { 111 case GL_VENDOR_AMD: stream_append_s8(&s, s8("Vendor: AMD\n")); break; 112 case GL_VENDOR_INTEL: stream_append_s8(&s, s8("Vendor: Intel\n")); break; 113 case GL_VENDOR_NVIDIA: stream_append_s8(&s, s8("Vendor: nVidia\n")); break; 114 } 115 stream_append_s8(&s, s8("Version: ")); 116 stream_append_i64(&s, gl->version_major); 117 stream_append_s8(&s, s8(".")); 118 stream_append_i64(&s, gl->version_minor); 119 stream_append_s8(&s, s8("\nMax 1D/2D Texture Dimension: ")); 120 stream_append_i64(&s, gl->max_2d_texture_dim); 121 stream_append_s8(&s, s8("\nMax 3D Texture Dimension: ")); 122 stream_append_i64(&s, gl->max_3d_texture_dim); 123 stream_append_s8(&s, s8("\nMax SSBO Size: ")); 124 stream_append_i64(&s, gl->max_ssbo_size); 125 stream_append_s8(&s, s8("\nMax UBO Size: ")); 126 stream_append_i64(&s, gl->max_ubo_size); 127 stream_append_s8(&s, s8("\n-----------------------\n")); 128 if (!s.errors) 129 os_write_err_msg(stream_to_s8(s)); 130 #endif 131 } 132 133 static u32 134 compile_shader(Arena a, u32 type, s8 shader) 135 { 136 u32 sid = glCreateShader(type); 137 glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len); 138 glCompileShader(sid); 139 140 i32 res = 0; 141 glGetShaderiv(sid, GL_COMPILE_STATUS, &res); 142 143 char *stype; 144 switch (type) { 145 case GL_COMPUTE_SHADER: stype = "Compute"; break; 146 case GL_FRAGMENT_SHADER: stype = "Fragment"; break; 147 } 148 149 if (res == GL_FALSE) { 150 TraceLog(LOG_WARNING, "SHADER: [ID %u] %s shader failed to compile", sid, stype); 151 i32 len = 0; 152 glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len); 153 s8 err = s8alloc(&a, len); 154 glGetShaderInfoLog(sid, len, (int *)&err.len, (char *)err.data); 155 TraceLog(LOG_WARNING, "SHADER: [ID %u] Compile error: %s", sid, (char *)err.data); 156 glDeleteShader(sid); 157 } else { 158 TraceLog(LOG_INFO, "SHADER: [ID %u] %s shader compiled successfully", sid, stype); 159 } 160 161 return sid; 162 } 163 164 static void 165 init_fragment_shader_ctx(FragmentShaderCtx *ctx, uv4 out_data_dim) 166 { 167 ctx->output = LoadRenderTexture(out_data_dim.x, out_data_dim.y); 168 ctx->db = -50.0f; 169 } 170 171 static void 172 reload_shaders(BeamformerCtx *ctx, Arena a) 173 { 174 ComputeShaderCtx *csctx = &ctx->csctx; 175 for (u32 i = 0; i < ARRAY_COUNT(csctx->programs); i++) { 176 if (!compute_shader_paths[i].len) 177 continue; 178 179 Arena tmp = a; 180 FileStats fs = os_get_file_stats((char *)compute_shader_paths[i].data); 181 s8 shader_text = os_read_file(&tmp, (char *)compute_shader_paths[i].data, fs.filesize); 182 if (shader_text.len == -1) { 183 os_write_err_msg(s8("failed to read shader: ")); 184 os_write_err_msg(compute_shader_paths[i]); 185 os_write_err_msg(s8("\n")); 186 os_fail(); 187 } 188 u32 shader_id = compile_shader(tmp, GL_COMPUTE_SHADER, shader_text); 189 190 if (shader_id) { 191 glDeleteProgram(csctx->programs[i]); 192 csctx->programs[i] = rlLoadComputeShaderProgram(shader_id); 193 ctx->flags |= DO_COMPUTE; 194 } 195 196 glDeleteShader(shader_id); 197 } 198 199 csctx->volume_export_pass_id = glGetUniformLocation(csctx->programs[CS_HERCULES], 200 "u_volume_export_pass"); 201 csctx->volume_export_dim_offset_id = glGetUniformLocation(csctx->programs[CS_HERCULES], 202 "u_volume_export_dim_offset"); 203 csctx->xdc_transform_id = glGetUniformLocation(csctx->programs[CS_UFORCES], 204 "u_xdc_transform"); 205 csctx->xdc_index_id = glGetUniformLocation(csctx->programs[CS_UFORCES], 206 "u_xdc_index"); 207 208 csctx->mips_level_id = glGetUniformLocation(csctx->programs[CS_MIN_MAX], "u_mip_map"); 209 210 csctx->sum_prescale_id = glGetUniformLocation(csctx->programs[CS_SUM], "u_prescale"); 211 212 Shader updated_fs = LoadShader(NULL, "shaders/render.glsl"); 213 if (updated_fs.id != rlGetShaderIdDefault()) { 214 UnloadShader(ctx->fsctx.shader); 215 ctx->fsctx.shader = updated_fs; 216 ctx->fsctx.db_cutoff_id = GetShaderLocation(updated_fs, "u_db_cutoff"); 217 } 218 } 219 220 static void 221 validate_cuda_lib(CudaLib *cl) 222 { 223 if (!cl->init_cuda_configuration) cl->init_cuda_configuration = init_cuda_configuration_stub; 224 if (!cl->register_cuda_buffers) cl->register_cuda_buffers = register_cuda_buffers_stub; 225 if (!cl->cuda_decode) cl->cuda_decode = cuda_decode_stub; 226 if (!cl->cuda_hilbert) cl->cuda_hilbert = cuda_hilbert_stub; 227 } 228 229 static void 230 check_and_load_cuda_lib(CudaLib *cl) 231 { 232 FileStats current = os_get_file_stats(OS_CUDA_LIB_NAME); 233 if (cl->timestamp == current.timestamp || current.filesize < 32) 234 return; 235 236 TraceLog(LOG_INFO, "Loading CUDA lib: %s", OS_CUDA_LIB_NAME); 237 238 cl->timestamp = current.timestamp; 239 os_unload_library(cl->lib); 240 cl->lib = os_load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME); 241 242 cl->init_cuda_configuration = os_lookup_dynamic_symbol(cl->lib, "init_cuda_configuration"); 243 cl->register_cuda_buffers = os_lookup_dynamic_symbol(cl->lib, "register_cuda_buffers"); 244 cl->cuda_decode = os_lookup_dynamic_symbol(cl->lib, "cuda_decode"); 245 cl->cuda_hilbert = os_lookup_dynamic_symbol(cl->lib, "cuda_hilbert"); 246 247 validate_cuda_lib(cl); 248 } 249 250 int 251 main(void) 252 { 253 BeamformerCtx ctx = {0}; 254 255 Arena temp_memory = os_alloc_arena((Arena){0}, 8 * MEGABYTE); 256 257 ctx.window_size = (uv2){.w = 1280, .h = 840}; 258 259 ctx.out_data_dim = (uv4){.x = 1, .y = 1, .z = 1}; 260 ctx.export_ctx.volume_dim = (uv4){.x = 1, .y = 1, .z = 1}; 261 262 SetConfigFlags(FLAG_VSYNC_HINT); 263 InitWindow(ctx.window_size.w, ctx.window_size.h, "OGL Beamformer"); 264 /* NOTE: do this after initing so that the window starts out floating in tiling wm */ 265 SetWindowState(FLAG_WINDOW_RESIZABLE); 266 SetWindowMinSize(INFO_COLUMN_WIDTH * 2, ctx.window_size.h); 267 268 /* NOTE: Gather information about the GPU */ 269 get_gl_params(&ctx.gl); 270 dump_gl_params(&ctx.gl, temp_memory); 271 validate_gl_requirements(&ctx.gl); 272 273 /* TODO: build these into the binary */ 274 ctx.font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 28, 0, 0); 275 ctx.small_font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 22, 0, 0); 276 277 ctx.is.cursor_blink_t = 1; 278 279 init_fragment_shader_ctx(&ctx.fsctx, ctx.out_data_dim); 280 281 ctx.data_pipe = os_open_named_pipe(OS_PIPE_NAME); 282 ctx.params = os_open_shared_memory_area(OS_SMEM_NAME); 283 /* TODO: properly handle this? */ 284 ASSERT(ctx.data_pipe.file != OS_INVALID_FILE); 285 ASSERT(ctx.params); 286 287 ctx.params->raw.output_points = ctx.out_data_dim; 288 /* NOTE: default compute shader pipeline */ 289 ctx.params->compute_stages[0] = CS_HADAMARD; 290 ctx.params->compute_stages[1] = CS_DEMOD; 291 ctx.params->compute_stages[2] = CS_UFORCES; 292 ctx.params->compute_stages[3] = CS_MIN_MAX; 293 ctx.params->compute_stages_count = 4; 294 295 /* NOTE: make sure function pointers are valid even if we are not using the cuda lib */ 296 validate_cuda_lib(&ctx.cuda_lib); 297 298 /* NOTE: set up OpenGL debug logging */ 299 glDebugMessageCallback(gl_debug_logger, NULL); 300 #ifdef _DEBUG 301 glEnable(GL_DEBUG_OUTPUT); 302 #endif 303 304 /* NOTE: allocate space for Uniform Buffer but don't send anything yet */ 305 glCreateBuffers(1, &ctx.csctx.shared_ubo); 306 glNamedBufferStorage(ctx.csctx.shared_ubo, sizeof(BeamformerParameters), 0, GL_DYNAMIC_STORAGE_BIT); 307 308 glGenQueries(ARRAY_COUNT(ctx.csctx.timer_fences) * CS_LAST, (u32 *)ctx.csctx.timer_ids); 309 glGenQueries(ARRAY_COUNT(ctx.export_ctx.timer_ids), ctx.export_ctx.timer_ids); 310 311 /* NOTE: do not DO_COMPUTE on first frame */ 312 reload_shaders(&ctx, temp_memory); 313 ctx.flags &= ~DO_COMPUTE; 314 315 while(!WindowShouldClose()) { 316 do_debug(); 317 if (ctx.gl.vendor_id == GL_VENDOR_NVIDIA) 318 check_and_load_cuda_lib(&ctx.cuda_lib); 319 320 if (ctx.flags & RELOAD_SHADERS) { 321 ctx.flags &= ~RELOAD_SHADERS; 322 reload_shaders(&ctx, temp_memory); 323 } 324 325 do_beamformer(&ctx, temp_memory); 326 } 327 328 /* NOTE: make sure this will get cleaned up after external 329 * programs release their references */ 330 os_remove_shared_memory(OS_SMEM_NAME); 331 332 /* NOTE: garbage code needed for Linux */ 333 os_close_named_pipe(ctx.data_pipe); 334 }