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