main.c (8998B)
1 /* See LICENSE for license details. */ 2 #include "beamformer.h" 3 4 static char *compute_shader_paths[CS_LAST] = { 5 [CS_HADAMARD] = "shaders/hadamard.glsl", 6 [CS_HERCULES] = "shaders/hercules.glsl", 7 [CS_DEMOD] = "shaders/demod.glsl", 8 [CS_MIN_MAX] = "shaders/min_max.glsl", 9 [CS_UFORCES] = "shaders/uforces.glsl", 10 }; 11 12 #ifndef _DEBUG 13 14 #include "beamformer.c" 15 static void do_debug(void) { } 16 17 #else 18 static os_library_handle libhandle; 19 20 typedef void do_beamformer_fn(BeamformerCtx *, Arena); 21 static do_beamformer_fn *do_beamformer; 22 23 static void 24 do_debug(void) 25 { 26 static os_filetime updated_time; 27 os_file_stats test_stats = os_get_file_stats(OS_DEBUG_LIB_NAME); 28 if (test_stats.filesize > 32 && os_filetime_is_newer(test_stats.timestamp, updated_time)) { 29 os_unload_library(libhandle); 30 libhandle = os_load_library(OS_DEBUG_LIB_NAME, OS_DEBUG_LIB_TEMP_NAME); 31 do_beamformer = os_lookup_dynamic_symbol(libhandle, "do_beamformer"); 32 updated_time = test_stats.timestamp; 33 } 34 } 35 36 #endif /* _DEBUG */ 37 38 /* NOTE: cuda lib stubs */ 39 INIT_CUDA_CONFIGURATION_FN(init_cuda_configuration_stub) {} 40 REGISTER_CUDA_BUFFERS_FN(register_cuda_buffers_stub) {} 41 CUDA_DECODE_FN(cuda_decode_stub) {} 42 CUDA_HILBERT_FN(cuda_hilbert_stub) {} 43 44 static void 45 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *userctx) 46 { 47 (void)src; (void)type; (void)id; (void)userctx; 48 fputs("[GL DEBUG ", stderr); 49 switch (lvl) { 50 case GL_DEBUG_SEVERITY_HIGH: fputs("HIGH]: ", stderr); break; 51 case GL_DEBUG_SEVERITY_MEDIUM: fputs("MEDIUM]: ", stderr); break; 52 case GL_DEBUG_SEVERITY_LOW: fputs("LOW]: ", stderr); break; 53 case GL_DEBUG_SEVERITY_NOTIFICATION: fputs("NOTIFICATION]: ", stderr); break; 54 default: fputs("INVALID]: ", stderr); break; 55 } 56 fwrite(msg, 1, len, stderr); 57 fputc('\n', stderr); 58 } 59 60 static u32 61 compile_shader(Arena a, u32 type, s8 shader) 62 { 63 u32 sid = glCreateShader(type); 64 glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len); 65 glCompileShader(sid); 66 67 i32 res = 0; 68 glGetShaderiv(sid, GL_COMPILE_STATUS, &res); 69 70 char *stype; 71 switch (type) { 72 case GL_COMPUTE_SHADER: stype = "Compute"; break; 73 case GL_FRAGMENT_SHADER: stype = "Fragment"; break; 74 } 75 76 if (res == GL_FALSE) { 77 TraceLog(LOG_WARNING, "SHADER: [ID %u] %s shader failed to compile", sid, stype); 78 i32 len = 0; 79 glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len); 80 s8 err = s8alloc(&a, len); 81 glGetShaderInfoLog(sid, len, (int *)&err.len, (char *)err.data); 82 TraceLog(LOG_WARNING, "SHADER: [ID %u] Compile error: %s", sid, (char *)err.data); 83 glDeleteShader(sid); 84 } else { 85 TraceLog(LOG_INFO, "SHADER: [ID %u] %s shader compiled successfully", sid, stype); 86 } 87 88 return sid; 89 } 90 91 static void 92 init_fragment_shader_ctx(FragmentShaderCtx *ctx, uv4 out_data_dim) 93 { 94 ctx->output = LoadRenderTexture(out_data_dim.x, out_data_dim.y); 95 ctx->db = -50.0f; 96 } 97 98 static void 99 reload_shaders(BeamformerCtx *ctx, Arena a) 100 { 101 ComputeShaderCtx *csctx = &ctx->csctx; 102 for (u32 i = 0; i < ARRAY_COUNT(csctx->programs); i++) { 103 if (!compute_shader_paths[i]) 104 continue; 105 106 Arena tmp = a; 107 os_file_stats fs = os_get_file_stats(compute_shader_paths[i]); 108 s8 shader_text = os_read_file(&tmp, compute_shader_paths[i], fs.filesize); 109 u32 shader_id = compile_shader(tmp, GL_COMPUTE_SHADER, shader_text); 110 111 if (shader_id) { 112 glDeleteProgram(csctx->programs[i]); 113 csctx->programs[i] = rlLoadComputeShaderProgram(shader_id); 114 ctx->flags |= DO_COMPUTE; 115 } 116 117 glDeleteShader(shader_id); 118 } 119 120 ctx->export_ctx.volume_texture_id = glGetUniformLocation(csctx->programs[CS_HERCULES], 121 "u_out_volume_tex"); 122 csctx->volume_export_pass_id = glGetUniformLocation(csctx->programs[CS_HERCULES], 123 "u_volume_export_pass"); 124 csctx->volume_export_dim_offset_id = glGetUniformLocation(csctx->programs[CS_HERCULES], 125 "u_volume_export_dim_offset"); 126 127 csctx->out_data_tex_id = glGetUniformLocation(csctx->programs[CS_UFORCES], "u_out_data_tex"); 128 csctx->mip_view_tex_id = glGetUniformLocation(csctx->programs[CS_MIN_MAX], "u_mip_view_tex"); 129 csctx->mips_level_id = glGetUniformLocation(csctx->programs[CS_MIN_MAX], "u_mip_map"); 130 131 Shader updated_fs = LoadShader(NULL, "shaders/render.glsl"); 132 if (updated_fs.id != rlGetShaderIdDefault()) { 133 UnloadShader(ctx->fsctx.shader); 134 ctx->fsctx.shader = updated_fs; 135 ctx->fsctx.out_data_tex_id = GetShaderLocation(updated_fs, "u_out_data_tex"); 136 ctx->fsctx.db_cutoff_id = GetShaderLocation(updated_fs, "u_db_cutoff"); 137 } 138 } 139 140 static void 141 validate_cuda_lib(CudaLib *cl) 142 { 143 if (!cl->init_cuda_configuration) cl->init_cuda_configuration = init_cuda_configuration_stub; 144 if (!cl->register_cuda_buffers) cl->register_cuda_buffers = register_cuda_buffers_stub; 145 if (!cl->cuda_decode) cl->cuda_decode = cuda_decode_stub; 146 if (!cl->cuda_hilbert) cl->cuda_hilbert = cuda_hilbert_stub; 147 } 148 149 static void 150 check_and_load_cuda_lib(CudaLib *cl) 151 { 152 os_file_stats current = os_get_file_stats(OS_CUDA_LIB_NAME); 153 if (!os_filetime_is_newer(current.timestamp, cl->timestamp) || current.filesize < 32) 154 return; 155 156 TraceLog(LOG_INFO, "Loading CUDA lib: %s", OS_CUDA_LIB_NAME); 157 158 cl->timestamp = current.timestamp; 159 os_unload_library(cl->lib); 160 cl->lib = os_load_library(OS_CUDA_LIB_NAME, OS_CUDA_LIB_TEMP_NAME); 161 162 cl->init_cuda_configuration = os_lookup_dynamic_symbol(cl->lib, "init_cuda_configuration"); 163 cl->register_cuda_buffers = os_lookup_dynamic_symbol(cl->lib, "register_cuda_buffers"); 164 cl->cuda_decode = os_lookup_dynamic_symbol(cl->lib, "cuda_decode"); 165 cl->cuda_hilbert = os_lookup_dynamic_symbol(cl->lib, "cuda_hilbert"); 166 167 validate_cuda_lib(cl); 168 } 169 170 int 171 main(void) 172 { 173 BeamformerCtx ctx = {0}; 174 175 Arena temp_memory = os_alloc_arena((Arena){0}, 8 * MEGABYTE); 176 177 ctx.window_size = (uv2){.w = 1280, .h = 840}; 178 179 ctx.out_data_dim = (uv4){.x = 1, .y = 1, .z = 1}; 180 ctx.export_ctx.volume_dim = (uv4){.x = 1, .y = 1, .z = 1}; 181 182 SetConfigFlags(FLAG_VSYNC_HINT); 183 InitWindow(ctx.window_size.w, ctx.window_size.h, "OGL Beamformer"); 184 /* NOTE: do this after initing so that the window starts out floating in tiling wm */ 185 SetWindowState(FLAG_WINDOW_RESIZABLE); 186 SetWindowMinSize(INFO_COLUMN_WIDTH * 2, ctx.window_size.h); 187 188 /* TODO: build these into the binary */ 189 ctx.font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 28, 0, 0); 190 ctx.small_font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 22, 0, 0); 191 192 ctx.is.cursor_blink_t = 1; 193 194 init_fragment_shader_ctx(&ctx.fsctx, ctx.out_data_dim); 195 196 ctx.data_pipe = os_open_named_pipe(OS_PIPE_NAME); 197 ctx.params = os_open_shared_memory_area(OS_SMEM_NAME); 198 /* TODO: properly handle this? */ 199 ASSERT(ctx.data_pipe.file != OS_INVALID_FILE); 200 ASSERT(ctx.params); 201 202 ctx.params->raw.output_points = ctx.out_data_dim; 203 /* NOTE: default compute shader pipeline */ 204 ctx.params->compute_stages[0] = CS_HADAMARD; 205 ctx.params->compute_stages[1] = CS_DEMOD; 206 ctx.params->compute_stages[2] = CS_UFORCES; 207 ctx.params->compute_stages[3] = CS_MIN_MAX; 208 ctx.params->compute_stages_count = 4; 209 210 /* NOTE: Determine which graphics vendor we are running on */ 211 { 212 const u8 *vendor = glGetString(GL_VENDOR); 213 if (!vendor) 214 die("Failed to determine GL Vendor\n"); 215 switch (vendor[0]) { 216 case 'A': ctx.gl_vendor_id = GL_VENDOR_AMD; break; 217 case 'I': ctx.gl_vendor_id = GL_VENDOR_INTEL; break; 218 case 'N': ctx.gl_vendor_id = GL_VENDOR_NVIDIA; break; 219 default: die("Unknown GL Vendor: %s\n", vendor); break; 220 } 221 } 222 223 /* NOTE: make sure function pointers are valid even if we are not using the cuda lib */ 224 validate_cuda_lib(&ctx.cuda_lib); 225 226 /* NOTE: set up OpenGL debug logging */ 227 glDebugMessageCallback(gl_debug_logger, NULL); 228 #ifdef _DEBUG 229 glEnable(GL_DEBUG_OUTPUT); 230 #endif 231 232 /* NOTE: allocate space for Uniform Buffer but don't send anything yet */ 233 glCreateBuffers(1, &ctx.csctx.shared_ubo); 234 glNamedBufferStorage(ctx.csctx.shared_ubo, sizeof(BeamformerParameters), 0, GL_DYNAMIC_STORAGE_BIT); 235 236 glGenQueries(ARRAY_COUNT(ctx.csctx.timer_fences) * CS_LAST, (u32 *)ctx.csctx.timer_ids); 237 glGenQueries(ARRAY_COUNT(ctx.export_ctx.timer_ids), ctx.export_ctx.timer_ids); 238 239 /* NOTE: do not DO_COMPUTE on first frame */ 240 reload_shaders(&ctx, temp_memory); 241 ctx.flags &= ~DO_COMPUTE; 242 243 while(!WindowShouldClose()) { 244 do_debug(); 245 if (ctx.gl_vendor_id == GL_VENDOR_NVIDIA) 246 check_and_load_cuda_lib(&ctx.cuda_lib); 247 248 if (ctx.flags & RELOAD_SHADERS) { 249 ctx.flags &= ~RELOAD_SHADERS; 250 reload_shaders(&ctx, temp_memory); 251 } 252 253 do_beamformer(&ctx, temp_memory); 254 } 255 256 /* NOTE: make sure this will get cleaned up after external 257 * programs release their references */ 258 os_remove_shared_memory(OS_SMEM_NAME); 259 260 /* NOTE: garbage code needed for Linux */ 261 os_close_named_pipe(ctx.data_pipe); 262 }