build.c (80029B)
1 /* See LICENSE for license details. */ 2 /* NOTE: inspired by nob: https://github.com/tsoding/nob.h */ 3 4 /* TODO(rnp): 5 * [ ]: refactor: "base" shaders should only be reloadable shaders 6 * - internally when a shader with no file is encountered it should 7 * not get pushed as a "base" shader. 8 * [ ]: bug: column indicator for compile error is off 9 * [ ]: bake shaders and font data into binary 10 * - for shaders there is a way of making a separate data section and referring 11 * to it with extern from the C source (bake both data and size) 12 * - use objcopy, maybe need linker script maybe command line flags for ld will work 13 * [ ]: cross compile/override baked compiler 14 * [ ]: msvc build doesn't detect out of date files correctly 15 * [ ]: seperate dwarf debug info 16 */ 17 #include <stdarg.h> 18 #include <setjmp.h> 19 #include <stdio.h> 20 21 #include "util.h" 22 23 #define BeamformerShaderKind_ComputeCount (1) 24 #include "beamformer_parameters.h" 25 26 global char *g_argv0; 27 28 #define OUTDIR "out" 29 #define OUTPUT(s) OUTDIR "/" s 30 31 #if COMPILER_MSVC 32 #define COMMON_FLAGS "-nologo", "-std:c11", "-Fo:" OUTDIR "\\", "-Z7", "-Zo" 33 #define DEBUG_FLAGS "-Od", "-D_DEBUG" 34 #define OPTIMIZED_FLAGS "-O2" 35 #define EXTRA_FLAGS "" 36 #else 37 #define COMMON_FLAGS "-std=c11", "-pipe", "-Wall" 38 #define DEBUG_FLAGS "-O0", "-D_DEBUG", "-Wno-unused-function" 39 #define OPTIMIZED_FLAGS "-O3" 40 #define EXTRA_FLAGS_BASE "-Werror", "-Wextra", "-Wshadow", "-Wconversion", "-Wno-unused-parameter", \ 41 "-Wno-error=unused-function", "-funsafe-math-optimizations", "-fno-math-errno" 42 #if COMPILER_GCC 43 #define EXTRA_FLAGS EXTRA_FLAGS_BASE, "-Wno-unused-variable" 44 #else 45 #define EXTRA_FLAGS EXTRA_FLAGS_BASE 46 #endif 47 #endif 48 49 #define is_aarch64 ARCH_ARM64 50 #define is_amd64 ARCH_X64 51 #define is_unix OS_LINUX 52 #define is_w32 OS_WINDOWS 53 #define is_clang COMPILER_CLANG 54 #define is_gcc COMPILER_GCC 55 #define is_msvc COMPILER_MSVC 56 57 #if OS_LINUX 58 59 #include <errno.h> 60 #include <string.h> 61 #include <sys/select.h> 62 #include <sys/wait.h> 63 64 #include "os_linux.c" 65 66 #define W32_DECL(x) 67 68 #define OS_SHARED_LINK_LIB(s) "lib" s ".so" 69 #define OS_SHARED_LIB(s) s ".so" 70 #define OS_STATIC_LIB(s) s ".a" 71 #define OS_MAIN "main_linux.c" 72 73 #elif OS_WINDOWS 74 75 #include "os_win32.c" 76 77 #define W32_DECL(x) x 78 79 #define OS_SHARED_LINK_LIB(s) s ".dll" 80 #define OS_SHARED_LIB(s) s ".dll" 81 #define OS_STATIC_LIB(s) s ".lib" 82 #define OS_MAIN "main_w32.c" 83 84 #else 85 #error Unsupported Platform 86 #endif 87 88 #if COMPILER_CLANG 89 #define COMPILER "clang" 90 #elif COMPILER_MSVC 91 #define COMPILER "cl" 92 #else 93 #define COMPILER "cc" 94 #endif 95 96 #if COMPILER_MSVC 97 #define LINK_LIB(name) name ".lib" 98 #define OBJECT(name) name ".obj" 99 #define OUTPUT_DLL(name) "/LD", "/Fe:", name 100 #define OUTPUT_LIB(name) "/out:" OUTPUT(name) 101 #define OUTPUT_EXE(name) "/Fe:", name 102 #define STATIC_LIBRARY_BEGIN(name) "lib", "/nologo", name 103 #else 104 #define LINK_LIB(name) "-l" name 105 #define OBJECT(name) name ".o" 106 #define OUTPUT_DLL(name) "-fPIC", "-shared", "-o", name 107 #define OUTPUT_LIB(name) OUTPUT(name) 108 #define OUTPUT_EXE(name) "-o", name 109 #define STATIC_LIBRARY_BEGIN(name) "ar", "rc", name 110 #endif 111 112 #define shift(list, count) ((count)--, *(list)++) 113 114 #define cmd_append_count da_append_count 115 #define cmd_append(a, s, ...) da_append_count(a, s, ((char *[]){__VA_ARGS__}), \ 116 (iz)(sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 117 118 DA_STRUCT(char *, Command); 119 120 typedef struct { 121 b32 debug; 122 b32 generic; 123 b32 sanitize; 124 b32 tests; 125 b32 time; 126 } Options; 127 128 #define BUILD_LOG_KINDS \ 129 X(Error, "\x1B[31m[ERROR]\x1B[0m ") \ 130 X(Warning, "\x1B[33m[WARNING]\x1B[0m ") \ 131 X(Generate, "\x1B[32m[GENERATE]\x1B[0m ") \ 132 X(Info, "\x1B[33m[INFO]\x1B[0m ") \ 133 X(Command, "\x1B[36m[COMMAND]\x1B[0m ") 134 #define X(t, ...) BuildLogKind_##t, 135 typedef enum {BUILD_LOG_KINDS BuildLogKind_Count} BuildLogKind; 136 #undef X 137 138 function void 139 build_log_base(BuildLogKind kind, char *format, va_list args) 140 { 141 #define X(t, pre) pre, 142 read_only local_persist char *prefixes[BuildLogKind_Count + 1] = {BUILD_LOG_KINDS "[INVALID] "}; 143 #undef X 144 FILE *out = kind == BuildLogKind_Error? stderr : stdout; 145 fputs(prefixes[MIN(kind, BuildLogKind_Count)], out); 146 vfprintf(out, format, args); 147 fputc('\n', out); 148 } 149 150 #define build_log_failure(format, ...) build_log(BuildLogKind_Error, \ 151 "failed to build: " format, ##__VA_ARGS__) 152 #define build_log_generate(...) build_log(BuildLogKind_Generate, ##__VA_ARGS__) 153 #define build_log_info(...) build_log(BuildLogKind_Info, ##__VA_ARGS__) 154 #define build_log_command(...) build_log(BuildLogKind_Command, ##__VA_ARGS__) 155 #define build_log_warning(...) build_log(BuildLogKind_Warning, ##__VA_ARGS__) 156 function void 157 build_log(BuildLogKind kind, char *format, ...) 158 { 159 va_list ap; 160 va_start(ap, format); 161 build_log_base(kind, format, ap); 162 va_end(ap); 163 } 164 165 #define build_fatal(fmt, ...) build_fatal_("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) 166 function no_return void 167 build_fatal_(char *format, ...) 168 { 169 va_list ap; 170 va_start(ap, format); 171 build_log_base(BuildLogKind_Error, format, ap); 172 va_end(ap); 173 os_exit(1); 174 } 175 176 function b32 177 s8_contains(s8 s, u8 byte) 178 { 179 b32 result = 0; 180 for (iz i = 0 ; !result && i < s.len; i++) 181 result |= s.data[i] == byte; 182 return result; 183 } 184 185 function void 186 stream_push_command(Stream *s, CommandList *c) 187 { 188 if (!s->errors) { 189 for (iz i = 0; i < c->count; i++) { 190 s8 item = c_str_to_s8(c->data[i]); 191 if (item.len) { 192 b32 escape = s8_contains(item, ' ') || s8_contains(item, '"'); 193 if (escape) stream_append_byte(s, '\''); 194 stream_append_s8(s, item); 195 if (escape) stream_append_byte(s, '\''); 196 if (i != c->count - 1) stream_append_byte(s, ' '); 197 } 198 } 199 } 200 } 201 202 #if OS_LINUX 203 204 function b32 205 os_rename_file(char *name, char *new) 206 { 207 b32 result = rename(name, new) != -1; 208 return result; 209 } 210 211 function b32 212 os_remove_file(char *name) 213 { 214 b32 result = remove(name) != -1; 215 return result; 216 } 217 218 function void 219 os_make_directory(char *name) 220 { 221 mkdir(name, 0770); 222 } 223 224 function u64 225 os_get_filetime(char *file) 226 { 227 struct stat sb; 228 u64 result = (u64)-1; 229 if (stat(file, &sb) != -1) 230 result = (u64)sb.st_mtim.tv_sec; 231 return result; 232 } 233 234 function iptr 235 os_spawn_process(CommandList *cmd, Stream sb) 236 { 237 pid_t result = fork(); 238 switch (result) { 239 case -1: build_fatal("failed to fork command: %s: %s", cmd->data[0], strerror(errno)); break; 240 case 0: { 241 if (execvp(cmd->data[0], cmd->data) == -1) 242 build_fatal("failed to exec command: %s: %s", cmd->data[0], strerror(errno)); 243 unreachable(); 244 } break; 245 } 246 return (iptr)result; 247 } 248 249 function b32 250 os_wait_close_process(iptr handle) 251 { 252 b32 result = 0; 253 for (;;) { 254 i32 status; 255 iptr wait_pid = (iptr)waitpid((i32)handle, &status, 0); 256 if (wait_pid == -1) 257 build_fatal("failed to wait on child process: %s", strerror(errno)); 258 if (wait_pid == handle) { 259 if (WIFEXITED(status)) { 260 status = WEXITSTATUS(status); 261 /* TODO(rnp): logging */ 262 result = status == 0; 263 break; 264 } 265 if (WIFSIGNALED(status)) { 266 /* TODO(rnp): logging */ 267 result = 0; 268 break; 269 } 270 } else { 271 /* TODO(rnp): handle multiple children */ 272 InvalidCodePath; 273 } 274 } 275 return result; 276 } 277 278 #elif OS_WINDOWS 279 280 enum { 281 MOVEFILE_REPLACE_EXISTING = 0x01, 282 }; 283 284 W32(b32) CreateDirectoryA(c8 *, void *); 285 W32(b32) CreateProcessA(u8 *, u8 *, iptr, iptr, b32, u32, iptr, u8 *, iptr, iptr); 286 W32(b32) GetExitCodeProcess(iptr handle, u32 *); 287 W32(b32) GetFileTime(iptr, iptr, iptr, iptr); 288 W32(b32) MoveFileExA(c8 *, c8 *, u32); 289 290 function void 291 os_make_directory(char *name) 292 { 293 CreateDirectoryA(name, 0); 294 } 295 296 function b32 297 os_rename_file(char *name, char *new) 298 { 299 b32 result = MoveFileExA(name, new, MOVEFILE_REPLACE_EXISTING) != 0; 300 return result; 301 } 302 303 function b32 304 os_remove_file(char *name) 305 { 306 b32 result = DeleteFileA(name); 307 return result; 308 } 309 310 function u64 311 os_get_filetime(char *file) 312 { 313 u64 result = (u64)-1; 314 iptr h = CreateFileA(file, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); 315 if (h != INVALID_FILE) { 316 struct { u32 low, high; } w32_filetime; 317 GetFileTime(h, 0, 0, (iptr)&w32_filetime); 318 result = (u64)w32_filetime.high << 32ULL | w32_filetime.low; 319 CloseHandle(h); 320 } 321 return result; 322 } 323 324 function iptr 325 os_spawn_process(CommandList *cmd, Stream sb) 326 { 327 struct { 328 u32 cb; 329 u8 *reserved, *desktop, *title; 330 u32 x, y, x_size, y_size, x_count_chars, y_count_chars; 331 u32 fill_attr, flags; 332 u16 show_window, reserved_2; 333 u8 *reserved_3; 334 iptr std_input, std_output, std_error; 335 } w32_startup_info = { 336 .cb = sizeof(w32_startup_info), 337 .flags = 0x100, 338 .std_input = GetStdHandle(STD_INPUT_HANDLE), 339 .std_output = GetStdHandle(STD_OUTPUT_HANDLE), 340 .std_error = GetStdHandle(STD_ERROR_HANDLE), 341 }; 342 343 struct { 344 iptr phandle, thandle; 345 u32 pid, tid; 346 } w32_process_info = {0}; 347 348 /* TODO(rnp): warn if we need to clamp last string */ 349 sb.widx = MIN(sb.widx, (i32)(KB(32) - 1)); 350 if (sb.widx < sb.cap) sb.data[sb.widx] = 0; 351 else sb.data[sb.widx - 1] = 0; 352 353 iptr result = INVALID_FILE; 354 if (CreateProcessA(0, sb.data, 0, 0, 1, 0, 0, 0, (iptr)&w32_startup_info, 355 (iptr)&w32_process_info)) 356 { 357 CloseHandle(w32_process_info.thandle); 358 result = w32_process_info.phandle; 359 } 360 return result; 361 } 362 363 function b32 364 os_wait_close_process(iptr handle) 365 { 366 b32 result = WaitForSingleObject(handle, (u32)-1) != 0xFFFFFFFFUL; 367 if (result) { 368 u32 status; 369 GetExitCodeProcess(handle, &status); 370 result = status == 0; 371 } 372 CloseHandle(handle); 373 return result; 374 } 375 376 #endif 377 378 #define needs_rebuild(b, ...) needs_rebuild_(b, ((char *[]){__VA_ARGS__}), \ 379 (sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 380 function b32 381 needs_rebuild_(char *binary, char *deps[], iz deps_count) 382 { 383 u64 binary_filetime = os_get_filetime(binary); 384 u64 argv0_filetime = os_get_filetime(g_argv0); 385 b32 result = (binary_filetime == (u64)-1) | (argv0_filetime > binary_filetime); 386 for (iz i = 0; i < deps_count; i++) { 387 u64 filetime = os_get_filetime(deps[i]); 388 result |= (filetime == (u64)-1) | (filetime > binary_filetime); 389 } 390 return result; 391 } 392 393 function b32 394 run_synchronous(Arena a, CommandList *command) 395 { 396 Stream sb = arena_stream(a); 397 stream_push_command(&sb, command); 398 build_log_command("%.*s", (i32)sb.widx, sb.data); 399 return os_wait_close_process(os_spawn_process(command, sb)); 400 } 401 402 function CommandList 403 cmd_base(Arena *a, Options *o) 404 { 405 CommandList result = {0}; 406 cmd_append(a, &result, COMPILER); 407 408 if (!is_msvc) { 409 /* TODO(rnp): support cross compiling with clang */ 410 if (!o->generic) cmd_append(a, &result, "-march=native"); 411 else if (is_amd64) cmd_append(a, &result, "-march=x86-64-v3"); 412 else if (is_aarch64) cmd_append(a, &result, "-march=armv8"); 413 } 414 415 cmd_append(a, &result, COMMON_FLAGS, "-Iexternal/include"); 416 if (o->debug) cmd_append(a, &result, DEBUG_FLAGS); 417 else cmd_append(a, &result, OPTIMIZED_FLAGS); 418 419 /* NOTE: ancient gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80454 */ 420 if (is_gcc) cmd_append(a, &result, "-Wno-missing-braces"); 421 422 if (is_w32 && is_clang) cmd_append(a, &result, "-fms-extensions"); 423 424 if (o->debug && is_unix) cmd_append(a, &result, "-gdwarf-4"); 425 426 /* NOTE(rnp): need to avoid w32-gcc for ci */ 427 b32 sanitize = o->sanitize && !is_msvc && !(is_w32 && is_gcc); 428 if (sanitize) { 429 cmd_append(a, &result, "-fsanitize=address,undefined"); 430 /* NOTE(rnp): impossible to autodetect on GCC versions < 14 (ci has 13) */ 431 cmd_append(a, &result, "-DASAN_ACTIVE=1"); 432 } else { 433 cmd_append(a, &result, "-DASAN_ACTIVE=0"); 434 } 435 if (!sanitize && o->sanitize) build_log_warning("santizers not supported with this compiler"); 436 437 return result; 438 } 439 440 function void 441 check_rebuild_self(Arena arena, i32 argc, char *argv[]) 442 { 443 char *binary = shift(argv, argc); 444 if (needs_rebuild(binary, __FILE__, "os_win32.c", "os_linux.c", "util.c", "util.h", "beamformer_parameters.h")) { 445 Stream name_buffer = arena_stream(arena); 446 stream_append_s8s(&name_buffer, c_str_to_s8(binary), s8(".old")); 447 char *old_name = (char *)arena_stream_commit_zero(&arena, &name_buffer).data; 448 449 if (!os_rename_file(binary, old_name)) 450 build_fatal("failed to move: %s -> %s", binary, old_name); 451 452 Options options = {0}; 453 CommandList c = cmd_base(&arena, &options); 454 cmd_append(&arena, &c, EXTRA_FLAGS); 455 if (!is_msvc) cmd_append(&arena, &c, "-Wno-unused-function"); 456 cmd_append(&arena, &c, __FILE__, OUTPUT_EXE(binary)); 457 if (is_msvc) cmd_append(&arena, &c, "/link", "-incremental:no", "-opt:ref"); 458 cmd_append(&arena, &c, (void *)0); 459 if (!run_synchronous(arena, &c)) { 460 os_rename_file(old_name, binary); 461 build_fatal("failed to rebuild self"); 462 } 463 os_remove_file(old_name); 464 465 c.count = 0; 466 cmd_append(&arena, &c, binary); 467 cmd_append_count(&arena, &c, argv, argc); 468 cmd_append(&arena, &c, (void *)0); 469 if (!run_synchronous(arena, &c)) 470 os_exit(1); 471 472 os_exit(0); 473 } 474 } 475 476 function b32 477 s8_equal(s8 a, s8 b) 478 { 479 b32 result = a.len == b.len; 480 for (iz i = 0; result && i < a.len; i++) 481 result = a.data[i] == b.data[i]; 482 return result; 483 } 484 485 function void 486 usage(char *argv0) 487 { 488 printf("%s [--debug] [--sanitize] [--time]\n" 489 " --debug: dynamically link and build with debug symbols\n" 490 " --generic: compile for a generic target (x86-64-v3 or armv8 with NEON)\n" 491 " --sanitize: build with ASAN and UBSAN\n" 492 " --tests: also build programs in tests/\n" 493 " --time: print build time\n" 494 , argv0); 495 os_exit(0); 496 } 497 498 function Options 499 parse_options(i32 argc, char *argv[]) 500 { 501 Options result = {0}; 502 503 char *argv0 = shift(argv, argc); 504 while (argc > 0) { 505 char *arg = shift(argv, argc); 506 s8 str = c_str_to_s8(arg); 507 if (s8_equal(str, s8("--debug"))) { 508 result.debug = 1; 509 } else if (s8_equal(str, s8("--generic"))) { 510 result.generic = 1; 511 } else if (s8_equal(str, s8("--sanitize"))) { 512 result.sanitize = 1; 513 } else if (s8_equal(str, s8("--tests"))) { 514 result.tests = 1; 515 } else if (s8_equal(str, s8("--time"))) { 516 result.time = 1; 517 } else { 518 usage(argv0); 519 } 520 } 521 522 return result; 523 } 524 525 /* NOTE(rnp): produce pdbs on w32 */ 526 function void 527 cmd_pdb(Arena *a, CommandList *cmd, char *name) 528 { 529 if (is_w32 && is_clang) { 530 cmd_append(a, cmd, "-fuse-ld=lld", "-g", "-gcodeview", "-Wl,--pdb="); 531 } else if (is_msvc) { 532 Stream sb = arena_stream(*a); 533 stream_append_s8s(&sb, s8("-PDB:"), c_str_to_s8(name), s8(".pdb")); 534 char *pdb = (char *)arena_stream_commit_zero(a, &sb).data; 535 cmd_append(a, cmd, "/link", "-incremental:no", "-opt:ref", "-DEBUG", pdb); 536 } 537 } 538 539 function void 540 git_submodule_update(Arena a, char *name) 541 { 542 Stream sb = arena_stream(a); 543 stream_append_s8s(&sb, c_str_to_s8(name), s8(OS_PATH_SEPARATOR), s8(".git")); 544 arena_stream_commit_zero(&a, &sb); 545 546 CommandList git = {0}; 547 /* NOTE(rnp): cryptic bs needed to get a simple exit code if name is dirty */ 548 cmd_append(&a, &git, "git", "diff-index", "--quiet", "HEAD", "--", name, (void *)0); 549 if (!os_file_exists((c8 *)sb.data) || !run_synchronous(a, &git)) { 550 git.count = 1; 551 cmd_append(&a, &git, "submodule", "update", "--init", "--depth=1", name, (void *)0); 552 if (!run_synchronous(a, &git)) 553 build_fatal("failed to clone required module: %s", name); 554 } 555 } 556 557 function b32 558 build_shared_library(Arena a, CommandList cc, char *name, char *output, char **libs, iz libs_count, char **srcs, iz srcs_count) 559 { 560 cmd_append_count(&a, &cc, srcs, srcs_count); 561 cmd_append(&a, &cc, OUTPUT_DLL(output)); 562 cmd_pdb(&a, &cc, name); 563 cmd_append_count(&a, &cc, libs, libs_count); 564 cmd_append(&a, &cc, (void *)0); 565 b32 result = run_synchronous(a, &cc); 566 if (!result) build_log_failure("%s", output); 567 return result; 568 } 569 570 function b32 571 cc_single_file(Arena a, CommandList cc, char *exe, char *src, char *dest, char **tail, iz tail_count) 572 { 573 char *executable[] = {src, is_msvc? "/Fe:" : "-o", dest}; 574 char *object[] = {is_msvc? "/c" : "-c", src, is_msvc? "/Fo:" : "-o", dest}; 575 576 577 cmd_append_count(&a, &cc, exe? executable : object, 578 exe? countof(executable) : countof(object)); 579 if (exe) cmd_pdb(&a, &cc, exe); 580 cmd_append_count(&a, &cc, tail, tail_count); 581 cmd_append(&a, &cc, (void *)0); 582 b32 result = run_synchronous(a, &cc); 583 if (!result) build_log_failure("%s", dest); 584 return result; 585 } 586 587 function b32 588 build_static_library_from_objects(Arena a, char *name, char **flags, iz flags_count, char **objects, iz count) 589 { 590 CommandList ar = {0}; 591 cmd_append(&a, &ar, STATIC_LIBRARY_BEGIN(name)); 592 cmd_append_count(&a, &ar, flags, flags_count); 593 cmd_append_count(&a, &ar, objects, count); 594 cmd_append(&a, &ar, (void *)0); 595 b32 result = run_synchronous(a, &ar); 596 if (!result) build_log_failure("%s", name); 597 return result; 598 } 599 600 function b32 601 build_static_library(Arena a, CommandList cc, char *name, char **deps, char **outputs, iz count) 602 { 603 /* TODO(rnp): refactor to not need outputs */ 604 b32 result = 1; 605 for (iz i = 0; i < count; i++) 606 result &= cc_single_file(a, cc, 0, deps[i], outputs[i], 0, 0); 607 if (result) result = build_static_library_from_objects(a, name, 0, 0, outputs, count); 608 return result; 609 } 610 611 function b32 612 check_build_raylib(Arena a, CommandList cc, b32 shared) 613 { 614 b32 result = 1; 615 char *libraylib = shared ? OS_SHARED_LINK_LIB("raylib") : OUTPUT_LIB(OS_STATIC_LIB("raylib")); 616 if (needs_rebuild(libraylib, __FILE__, "external/include/rlgl.h", "external/raylib")) { 617 git_submodule_update(a, "external/raylib"); 618 os_copy_file("external/raylib/src/rlgl.h", "external/include/rlgl.h"); 619 620 if (is_unix) cmd_append(&a, &cc, "-D_GLFW_X11"); 621 cmd_append(&a, &cc, "-DPLATFORM_DESKTOP_GLFW"); 622 if (!is_msvc) cmd_append(&a, &cc, "-Wno-unused-but-set-variable"); 623 cmd_append(&a, &cc, "-Iexternal/raylib/src", "-Iexternal/raylib/src/external/glfw/include"); 624 #define RAYLIB_SOURCES \ 625 X(rglfw) \ 626 X(rshapes) \ 627 X(rtext) \ 628 X(rtextures) \ 629 X(utils) 630 #define X(name) "external/raylib/src/" #name ".c", 631 char *srcs[] = {"external/rcore_extended.c", RAYLIB_SOURCES}; 632 #undef X 633 #define X(name) OUTPUT(OBJECT(#name)), 634 char *outs[] = {OUTPUT(OBJECT("rcore_extended")), RAYLIB_SOURCES}; 635 #undef X 636 637 if (shared) { 638 char *libs[] = {LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), LINK_LIB("winmm")}; 639 iz libs_count = is_w32 ? countof(libs) : 0; 640 cmd_append(&a, &cc, "-DBUILD_LIBTYPE_SHARED", "-D_GLFW_BUILD_DLL"); 641 result = build_shared_library(a, cc, "raylib", libraylib, libs, libs_count, srcs, countof(srcs)); 642 } else { 643 result = build_static_library(a, cc, libraylib, srcs, outs, countof(srcs)); 644 } 645 } 646 return result; 647 } 648 649 function b32 650 build_helper_library(Arena arena, CommandList cc) 651 { 652 ///////////// 653 // library 654 char *library = OUTPUT(OS_SHARED_LIB("ogl_beamformer_lib")); 655 char *libs[] = {LINK_LIB("Synchronization")}; 656 iz libs_count = is_w32 ? countof(libs) : 0; 657 658 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 659 b32 result = build_shared_library(arena, cc, "ogl_beamformer_lib", library, 660 libs, libs_count, 661 arg_list(char *, "helpers/ogl_beamformer_lib.c")); 662 return result; 663 } 664 665 function b32 666 build_beamformer_as_library(Arena arena, CommandList cc) 667 { 668 char *library = OS_SHARED_LIB("beamformer"); 669 char *libs[] = {!is_msvc? "-L." : "", LINK_LIB("raylib"), LINK_LIB("gdi32"), 670 LINK_LIB("shell32"), LINK_LIB("user32"), LINK_LIB("opengl32"), 671 LINK_LIB("winmm"), LINK_LIB("Synchronization"), OUTPUT("main.lib")}; 672 iz libs_count = is_w32 ? countof(libs) : 0; 673 cmd_append(&arena, &cc, "-D_BEAMFORMER_DLL"); 674 b32 result = build_shared_library(arena, cc, "beamformer", library, 675 libs, libs_count, arg_list(char *, "beamformer.c")); 676 return result; 677 } 678 679 function b32 680 build_tests(Arena arena, CommandList cc) 681 { 682 #define TEST_PROGRAMS \ 683 X("throughput", LINK_LIB("zstd"), W32_DECL(LINK_LIB("Synchronization"))) 684 685 os_make_directory(OUTPUT("tests")); 686 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 687 cmd_append(&arena, &cc, "-I.", "-Ihelpers"); 688 689 b32 result = 1; 690 iz cc_count = cc.count; 691 #define X(prog, ...) \ 692 result &= cc_single_file(arena, cc, prog, "tests/" prog ".c", \ 693 OUTPUT("tests/" prog), \ 694 arg_list(char *, ##__VA_ARGS__)); \ 695 cc.count = cc_count; 696 TEST_PROGRAMS 697 #undef X 698 return result; 699 } 700 701 typedef struct { 702 s8 *data; 703 iz count; 704 iz capacity; 705 } s8_list; 706 707 function s8 708 s8_chop(s8 *in, iz count) 709 { 710 count = CLAMP(count, 0, in->len); 711 s8 result = {.data = in->data, .len = count}; 712 in->data += count; 713 in->len -= count; 714 return result; 715 } 716 717 function void 718 s8_split(s8 str, s8 *left, s8 *right, u8 byte) 719 { 720 iz i; 721 for (i = 0; i < str.len; i++) if (str.data[i] == byte) break; 722 723 if (left) *left = (s8){.data = str.data, .len = i}; 724 if (right) { 725 right->data = str.data + i + 1; 726 right->len = MAX(0, str.len - (i + 1)); 727 } 728 } 729 730 function s8 731 s8_trim(s8 in) 732 { 733 s8 result = in; 734 for (iz i = 0; i < in.len && *result.data == ' '; i++) result.data++; 735 result.len -= result.data - in.data; 736 for (; result.len > 0 && result.data[result.len - 1] == ' '; result.len--); 737 return result; 738 } 739 740 function void 741 s8_list_from_s8(s8_list *list, Arena *arena, s8 str) 742 { 743 s8 right = str, left; 744 while (right.len > 0) { 745 s8_split(right, &left, &right, ' '); 746 left = s8_trim(left); 747 if (left.len > 0) { *da_push(arena, list) = left; } 748 } 749 } 750 751 typedef struct { 752 Stream stream; 753 Arena scratch; 754 i32 indentation_level; 755 } MetaprogramContext; 756 757 function b32 758 meta_write_and_reset(MetaprogramContext *m, char *file) 759 { 760 b32 result = os_write_new_file(file, stream_to_s8(&m->stream)); 761 if (!result) build_log_failure("%s", file); 762 m->stream.widx = 0; 763 m->indentation_level = 0; 764 return result; 765 } 766 767 #define meta_push(m, ...) meta_push_(m, arg_list(s8, __VA_ARGS__)) 768 function void 769 meta_push_(MetaprogramContext *m, s8 *items, iz count) 770 { 771 stream_append_s8s_(&m->stream, items, count); 772 } 773 774 #define meta_pad(m, b, n) stream_pad(&(m)->stream, (b), (n)) 775 #define meta_indent(m) meta_pad((m), '\t', (m)->indentation_level) 776 #define meta_begin_line(m, ...) do { meta_indent(m); meta_push(m, __VA_ARGS__); } while(0) 777 #define meta_end_line(m, ...) meta_push(m, __VA_ARGS__, s8("\n")) 778 #define meta_push_line(m, ...) do { meta_indent(m); meta_push(m, __VA_ARGS__, s8("\n")); } while(0) 779 #define meta_begin_scope(m, ...) do { meta_push_line(m, __VA_ARGS__); (m)->indentation_level++; } while(0) 780 #define meta_end_scope(m, ...) do { (m)->indentation_level--; meta_push_line(m, __VA_ARGS__); } while(0) 781 #define meta_push_u64(m, n) stream_append_u64(&(m)->stream, (n)) 782 #define meta_push_u64_hex(m, n) stream_append_hex_u64(&(m)->stream, (n)) 783 784 #define meta_begin_matlab_class_cracker(_1, _2, FN, ...) FN 785 #define meta_begin_matlab_class_1(m, name) meta_begin_scope(m, s8("classdef " name)) 786 #define meta_begin_matlab_class_2(m, name, type) \ 787 meta_begin_scope(m, s8("classdef " name " < " type)) 788 789 #define meta_begin_matlab_class(m, ...) \ 790 meta_begin_matlab_class_cracker(__VA_ARGS__, \ 791 meta_begin_matlab_class_2, \ 792 meta_begin_matlab_class_1)(m, __VA_ARGS__) 793 794 function void 795 meta_push_matlab_property(MetaprogramContext *m, s8 name, u64 length, s8 kind) 796 { 797 meta_begin_line(m, name, s8("(1,")); 798 meta_push_u64(m, (u64)length); 799 meta_end_line(m, s8(")"), kind.len > 0 ? s8(" ") : s8(""), kind); 800 } 801 802 function void 803 meta_push_matlab_enum_with_value(MetaprogramContext *m, s8 name, i32 value) 804 { 805 meta_indent(m); 806 stream_append_s8s(&m->stream, name, s8(" (")); 807 stream_append_i64(&m->stream, value); 808 stream_append_s8(&m->stream, s8(")\n")); 809 } 810 811 function b32 812 meta_end_and_write_matlab(MetaprogramContext *m, char *path) 813 { 814 while (m->indentation_level > 0) meta_end_scope(m, s8("end")); 815 b32 result = meta_write_and_reset(m, path); 816 return result; 817 } 818 819 #define META_ENTRY_KIND_LIST \ 820 X(Invalid) \ 821 X(BeginScope) \ 822 X(EndScope) \ 823 X(Enumeration) \ 824 X(Flags) \ 825 X(Permute) \ 826 X(PermuteFlags) \ 827 X(Shader) \ 828 X(ShaderGroup) \ 829 X(SubShader) 830 831 typedef enum { 832 #define X(k, ...) MetaEntryKind_## k, 833 META_ENTRY_KIND_LIST 834 #undef X 835 MetaEntryKind_Count, 836 } MetaEntryKind; 837 838 #define X(k, ...) #k, 839 read_only global char *meta_entry_kind_strings[] = {META_ENTRY_KIND_LIST}; 840 #undef X 841 842 typedef struct { u32 line, column; } MetaLocation; 843 844 #define META_ENTRY_ARGUMENT_KIND_LIST \ 845 X(None) \ 846 X(String) \ 847 X(Array) 848 849 #define X(k, ...) MetaEntryArgumentKind_## k, 850 typedef enum {META_ENTRY_ARGUMENT_KIND_LIST} MetaEntryArgumentKind; 851 #undef X 852 853 typedef struct { 854 MetaEntryArgumentKind kind; 855 MetaLocation location; 856 union { 857 s8 string; 858 struct { 859 s8 *strings; 860 u64 count; 861 }; 862 }; 863 } MetaEntryArgument; 864 865 typedef struct { 866 MetaEntryKind kind; 867 u32 argument_count; 868 MetaEntryArgument *arguments; 869 s8 name; 870 MetaLocation location; 871 } MetaEntry; 872 873 typedef struct { 874 MetaEntry *data; 875 iz count; 876 iz capacity; 877 s8 raw; 878 } MetaEntryStack; 879 880 #define META_PARSE_TOKEN_LIST \ 881 X('@', Entry) \ 882 X('(', BeginArgs) \ 883 X(')', EndArgs) \ 884 X('[', BeginArray) \ 885 X(']', EndArray) \ 886 X('{', BeginScope) \ 887 X('}', EndScope) 888 889 typedef enum { 890 MetaParseToken_EOF, 891 MetaParseToken_String, 892 #define X(__1, kind, ...) MetaParseToken_## kind, 893 META_PARSE_TOKEN_LIST 894 #undef X 895 MetaParseToken_Count, 896 } MetaParseToken; 897 898 typedef union { 899 MetaEntryKind kind; 900 s8 string; 901 } MetaParseUnion; 902 903 typedef struct { 904 s8 s; 905 MetaLocation location; 906 } MetaParsePoint; 907 908 typedef struct { 909 MetaParsePoint p; 910 MetaParseUnion u; 911 MetaParsePoint save_point; 912 } MetaParser; 913 914 global char *compiler_file; 915 global jmp_buf compiler_jmp_buf; 916 917 #define meta_parser_save(v) (v)->save_point = (v)->p 918 #define meta_parser_restore(v) swap((v)->p, (v)->save_point) 919 #define meta_parser_commit(v) meta_parser_restore(v) 920 921 #define meta_compiler_error_message(loc, format, ...) \ 922 fprintf(stderr, "%s:%u:%u: error: "format, compiler_file, \ 923 loc.line + 1, loc.column + 1, ##__VA_ARGS__) 924 925 #define meta_compiler_error(loc, format, ...) do { \ 926 meta_compiler_error_message(loc, format, ##__VA_ARGS__); \ 927 meta_error(); \ 928 } while (0) 929 930 #define meta_entry_error(e, ...) meta_entry_error_column((e), (i32)(e)->location.column, __VA_ARGS__) 931 #define meta_entry_error_column(e, column, ...) do { \ 932 meta_compiler_error_message((e)->location, __VA_ARGS__); \ 933 meta_entry_print((e), 1, (column)); \ 934 meta_error(); \ 935 } while(0) 936 937 #define meta_entry_error_location(e, loc, ...) do { \ 938 meta_compiler_error_message((loc), __VA_ARGS__); \ 939 meta_entry_print((e), 1, (i32)(loc).column); \ 940 meta_error(); \ 941 } while (0) 942 943 function no_return void 944 meta_error(void) 945 { 946 assert(0); 947 longjmp(compiler_jmp_buf, 1); 948 } 949 950 function void 951 meta_entry_print(MetaEntry *e, i32 depth, i32 caret) 952 { 953 char *kind = meta_entry_kind_strings[e->kind]; 954 if (e->kind == MetaEntryKind_BeginScope) kind = "{"; 955 if (e->kind == MetaEntryKind_EndScope) kind = "}"; 956 957 fprintf(stderr, "%*s@%s", depth * 2, "", kind); 958 959 if (e->argument_count) { 960 fprintf(stderr, "("); 961 for (u32 i = 0; i < e->argument_count; i++) { 962 MetaEntryArgument *a = e->arguments + i; 963 if (i != 0) fprintf(stderr, " "); 964 if (a->kind == MetaEntryArgumentKind_Array) { 965 fprintf(stderr, "["); 966 for (u64 j = 0; j < a->count; j++) { 967 if (j != 0) fprintf(stderr, " "); 968 fprintf(stderr, "%.*s", (i32)a->strings[j].len, a->strings[j].data); 969 } 970 fprintf(stderr, "]"); 971 } else { 972 fprintf(stderr, "%.*s", (i32)a->string.len, a->string.data); 973 } 974 } 975 fprintf(stderr, ")"); 976 } 977 if (e->name.len) fprintf(stderr, " %.*s", (i32)e->name.len, e->name.data); 978 979 if (caret >= 0) fprintf(stderr, "\n%.*s^", depth * 2 + caret, ""); 980 981 fprintf(stderr, "\n"); 982 } 983 984 function MetaEntryKind 985 meta_entry_kind_from_string(s8 s) 986 { 987 #define X(k, ...) s8_comp(#k), 988 read_only local_persist s8 kinds[] = {META_ENTRY_KIND_LIST}; 989 #undef X 990 MetaEntryKind result = MetaEntryKind_Invalid; 991 for EachNonZeroEnumValue(MetaEntryKind, kind) { 992 if (s8_equal(kinds[kind], s)) { 993 result = kind; 994 break; 995 } 996 } 997 return result; 998 } 999 1000 function void 1001 meta_parser_trim(MetaParser *p) 1002 { 1003 u8 *s, *end = p->p.s.data + p->p.s.len; 1004 b32 done = 0; 1005 b32 comment = 0; 1006 for (s = p->p.s.data; !done && s != end;) { 1007 switch (*s) { 1008 case '\r': case '\t': case ' ': 1009 { 1010 p->p.location.column++; 1011 }break; 1012 case '\n':{ p->p.location.line++; p->p.location.column = 0; comment = 0; }break; 1013 case '/':{ 1014 comment = ((s + 1) != end && s[1] == '/'); 1015 } /* FALLTHROUGH */ 1016 default:{done = !comment;}break; 1017 } 1018 if (!done) s++; 1019 } 1020 p->p.s.data = s; 1021 p->p.s.len = end - s; 1022 } 1023 1024 function s8 1025 meta_parser_extract_string(MetaParser *p) 1026 { 1027 s8 result = {.data = p->p.s.data}; 1028 for (; result.len < p->p.s.len; result.len++) { 1029 b32 done = 0; 1030 switch (p->p.s.data[result.len]) { 1031 #define X(t, ...) case t: 1032 META_PARSE_TOKEN_LIST 1033 #undef X 1034 case ' ': case '\n': case '\r': case '\t': 1035 {done = 1;}break; 1036 case '/':{ 1037 done = (result.len + 1 < p->p.s.len) && (p->p.s.data[result.len + 1] == '/'); 1038 }break; 1039 default:{}break; 1040 } 1041 if (done) break; 1042 } 1043 p->p.location.column += (u32)result.len; 1044 p->p.s.data += result.len; 1045 p->p.s.len -= result.len; 1046 return result; 1047 } 1048 1049 function s8 1050 meta_parser_token_name(MetaParser *p, MetaParseToken t) 1051 { 1052 s8 result = s8("\"invalid\""); 1053 read_only local_persist s8 names[MetaParseToken_Count] = { 1054 [MetaParseToken_EOF] = s8_comp("\"EOF\""), 1055 #define X(k, v, ...) [MetaParseToken_## v] = s8_comp(#k), 1056 META_PARSE_TOKEN_LIST 1057 #undef X 1058 }; 1059 if (t >= 0 && t < countof(names)) result = names[t]; 1060 if (t == MetaParseToken_String) result = p->u.string; 1061 return result; 1062 } 1063 1064 function MetaParseToken 1065 meta_parser_token(MetaParser *p) 1066 { 1067 MetaParseToken result = MetaParseToken_EOF; 1068 meta_parser_save(p); 1069 if (p->p.s.len > 0) { 1070 b32 chop = 1; 1071 switch (p->p.s.data[0]) { 1072 #define X(t, kind, ...) case t:{ result = MetaParseToken_## kind; }break; 1073 META_PARSE_TOKEN_LIST 1074 #undef X 1075 default:{ result = MetaParseToken_String; chop = 0; }break; 1076 } 1077 if (chop) { s8_chop(&p->p.s, 1); p->p.location.column++; } 1078 1079 meta_parser_trim(p); 1080 switch (result) { 1081 case MetaParseToken_String:{ p->u.string = meta_parser_extract_string(p); }break; 1082 1083 /* NOTE(rnp): '{' and '}' are shorthand for @BeginScope and @EndScope */ 1084 case MetaParseToken_BeginScope:{ p->u.kind = MetaEntryKind_BeginScope; }break; 1085 case MetaParseToken_EndScope:{ p->u.kind = MetaEntryKind_EndScope; }break; 1086 1087 case MetaParseToken_Entry:{ 1088 s8 kind = meta_parser_extract_string(p); 1089 p->u.kind = meta_entry_kind_from_string(kind); 1090 if (p->u.kind == MetaEntryKind_Invalid) { 1091 meta_compiler_error(p->p.location, "invalid keyword: @%.*s\n", (i32)kind.len, kind.data); 1092 } 1093 }break; 1094 default:{}break; 1095 } 1096 meta_parser_trim(p); 1097 } 1098 1099 return result; 1100 } 1101 1102 function MetaParseToken 1103 meta_parser_peek_token(MetaParser *p) 1104 { 1105 MetaParseToken result = meta_parser_token(p); 1106 meta_parser_restore(p); 1107 return result; 1108 } 1109 1110 function void 1111 meta_parser_unexpected_token(MetaParser *p, MetaParseToken t) 1112 { 1113 meta_parser_restore(p); 1114 s8 token_name = meta_parser_token_name(p, t); 1115 meta_compiler_error(p->p.location, "unexpected token: %.*s\n", (i32)token_name.len, token_name.data); 1116 } 1117 1118 function void 1119 meta_parser_arguments(MetaParser *p, MetaEntry *e, Arena *arena) 1120 { 1121 if (meta_parser_peek_token(p) == MetaParseToken_BeginArgs) { 1122 meta_parser_commit(p); 1123 1124 MetaEntryArgument *arg = e->arguments = push_struct(arena, MetaEntryArgument); 1125 b32 array = 0; 1126 for (MetaParseToken token = meta_parser_token(p); 1127 token != MetaParseToken_EndArgs; 1128 token = meta_parser_token(p)) 1129 { 1130 if (!arg) arg = push_struct(arena, MetaEntryArgument); 1131 switch (token) { 1132 case MetaParseToken_String:{ 1133 if (array) { 1134 assert((u8 *)(arg->strings + arg->count) == arena->beg); 1135 *push_struct(arena, s8) = p->u.string; 1136 arg->count++; 1137 } else { 1138 e->argument_count++; 1139 arg->kind = MetaEntryArgumentKind_String; 1140 arg->string = p->u.string; 1141 arg->location = p->p.location; 1142 arg = 0; 1143 } 1144 }break; 1145 case MetaParseToken_BeginArray:{ 1146 arg->kind = MetaEntryArgumentKind_Array; 1147 arg->strings = (s8 *)arena_aligned_start(*arena, alignof(s8)); 1148 arg->location = p->p.location; 1149 array = 1; 1150 }break; 1151 case MetaParseToken_EndArray:{ 1152 e->argument_count++; 1153 array = 0; 1154 arg = 0; 1155 }break; 1156 default:{ meta_parser_unexpected_token(p, token); }break; 1157 } 1158 } 1159 } 1160 } 1161 1162 function MetaEntryStack 1163 meta_entry_stack_from_file(Arena *arena, Arena scratch, char *file) 1164 { 1165 MetaParser parser = {.p.s = os_read_whole_file(arena, file)}; 1166 MetaEntryStack result = {.raw = parser.p.s}; 1167 1168 compiler_file = file; 1169 1170 meta_parser_trim(&parser); 1171 1172 for (MetaParseToken token = meta_parser_token(&parser); 1173 token != MetaParseToken_EOF; 1174 token = meta_parser_token(&parser)) 1175 { 1176 MetaEntry *e = da_push(arena, &result); 1177 switch (token) { 1178 case MetaParseToken_BeginScope: 1179 case MetaParseToken_EndScope: 1180 case MetaParseToken_Entry: 1181 { 1182 e->kind = parser.u.kind; 1183 e->location = parser.save_point.location; 1184 1185 if (token == MetaParseToken_Entry) 1186 meta_parser_arguments(&parser, e, arena); 1187 1188 if (meta_parser_peek_token(&parser) == MetaParseToken_String) { 1189 meta_parser_commit(&parser); 1190 e->name = parser.u.string; 1191 } 1192 }break; 1193 1194 default:{ meta_parser_unexpected_token(&parser, token); }break; 1195 } 1196 } 1197 1198 return result; 1199 } 1200 1201 #define meta_entry_argument_expected(e, ...) \ 1202 meta_entry_argument_expected_((e), arg_list(s8, __VA_ARGS__)) 1203 function void 1204 meta_entry_argument_expected_(MetaEntry *e, s8 *args, uz count) 1205 { 1206 if (e->argument_count != count) { 1207 meta_compiler_error_message(e->location, "incorrect argument count for entry %s() got: %u expected: %u\n", 1208 meta_entry_kind_strings[e->kind], e->argument_count, (u32)count); 1209 fprintf(stderr, " format: @%s(", meta_entry_kind_strings[e->kind]); 1210 for (uz i = 0; i < count; i++) { 1211 if (i != 0) fprintf(stderr, ", "); 1212 fprintf(stderr, "%.*s", (i32)args[i].len, args[i].data); 1213 } 1214 fprintf(stderr, ")\n"); 1215 meta_error(); 1216 } 1217 } 1218 1219 function MetaEntryArgument 1220 meta_entry_argument_expect(MetaEntry *e, u32 index, MetaEntryArgumentKind kind) 1221 { 1222 #define X(k, ...) #k, 1223 read_only local_persist char *kinds[] = {META_ENTRY_ARGUMENT_KIND_LIST}; 1224 #undef X 1225 1226 assert(e->argument_count > index); 1227 MetaEntryArgument result = e->arguments[index]; 1228 1229 if (result.kind != kind) { 1230 meta_entry_error_location(e, result.location, "unexpected argument kind: expected %s but got: %s\n", 1231 kinds[kind], kinds[result.kind]); 1232 } 1233 1234 if (kind == MetaEntryArgumentKind_Array && result.count == 0) 1235 meta_entry_error_location(e, result.location, "array arguments must have at least 1 element\n"); 1236 1237 return result; 1238 } 1239 1240 typedef struct { 1241 s8_list *data; 1242 iz count; 1243 iz capacity; 1244 } s8_list_table; 1245 1246 typedef struct { 1247 iz kind; 1248 iz variation; 1249 } MetaEnumeration; 1250 1251 typedef struct { 1252 u32 *data; 1253 iz count; 1254 iz capacity; 1255 } MetaIDList; 1256 1257 typedef struct { 1258 u32 *global_flags; 1259 u16 local_flags; 1260 u16 global_flags_count; 1261 } MetaShaderPermutation; 1262 DA_STRUCT(MetaShaderPermutation, MetaShaderPermutation); 1263 1264 typedef struct { 1265 MetaShaderPermutationList permutations; 1266 MetaIDList global_flag_ids; 1267 MetaIDList global_enumeration_ids; 1268 u32 base_name_id; 1269 u32 flag_list_id; 1270 } MetaShader; 1271 DA_STRUCT(MetaShader, MetaShader); 1272 1273 typedef struct { 1274 MetaShader *shader; 1275 MetaIDList sub_shaders; 1276 s8 file; 1277 } MetaBaseShader; 1278 DA_STRUCT(MetaBaseShader, MetaBaseShader); 1279 1280 typedef struct { 1281 i32 first_match_vector_index; 1282 i32 one_past_last_match_vector_index; 1283 i32 sub_field_count; 1284 b32 has_local_flags; 1285 } MetaShaderDescriptor; 1286 1287 typedef struct { 1288 s8 name; 1289 MetaIDList shaders; 1290 } MetaShaderGroup; 1291 DA_STRUCT(MetaShaderGroup, MetaShaderGroup); 1292 1293 typedef struct { 1294 Arena *arena, scratch; 1295 1296 s8_list enumeration_kinds; 1297 s8_list_table enumeration_members; 1298 1299 s8_list_table flags_for_shader; 1300 1301 MetaShaderGroupList shader_groups; 1302 MetaShaderList shaders; 1303 MetaBaseShaderList base_shaders; 1304 s8_list shader_names; 1305 1306 MetaShaderDescriptor *shader_descriptors; 1307 } MetaContext; 1308 1309 1310 function u32 1311 metagen_pack_permutation(MetaContext *ctx, MetaEnumeration e) 1312 { 1313 u32 result = ((u32)(e.kind & 0xFFFFu) << 16u) | (u32)(e.variation & 0xFFFFu); 1314 return result; 1315 } 1316 1317 function MetaEnumeration 1318 metagen_unpack_permutation(MetaContext *ctx, u32 packed) 1319 { 1320 MetaEnumeration result; 1321 result.kind = (iz)(packed >> 16u); 1322 result.variation = (iz)(packed & 0xFFFFu); 1323 assert(result.kind < ctx->enumeration_kinds.count); 1324 assert(result.variation < ctx->enumeration_members.data[result.kind].count); 1325 return result; 1326 } 1327 1328 function s8 1329 metagen_permutation_kind(MetaContext *ctx, u32 packed) 1330 { 1331 MetaEnumeration p = metagen_unpack_permutation(ctx, packed); 1332 s8 result = ctx->enumeration_kinds.data[p.kind]; 1333 return result; 1334 } 1335 1336 function s8 1337 metagen_permutation_variation(MetaContext *ctx, u32 packed) 1338 { 1339 MetaEnumeration p = metagen_unpack_permutation(ctx, packed); 1340 s8 result = ctx->enumeration_members.data[p.kind].data[p.variation]; 1341 return result; 1342 } 1343 1344 function iz 1345 meta_lookup_string_slow(s8_list *sv, s8 s) 1346 { 1347 // TODO(rnp): obviously this is slow 1348 iz result = -1; 1349 for (iz i = 0; i < sv->count; i++) { 1350 if (s8_equal(s, sv->data[i])) { 1351 result = i; 1352 break; 1353 } 1354 } 1355 return result; 1356 } 1357 1358 function iz 1359 meta_lookup_id_slow(MetaIDList *v, u32 id) 1360 { 1361 // TODO(rnp): obviously this is slow 1362 iz result = -1; 1363 for (iz i = 0; i < v->count; i++) { 1364 if (id == v->data[i]) { 1365 result = i; 1366 break; 1367 } 1368 } 1369 return result; 1370 } 1371 1372 function iz 1373 meta_intern_string(MetaContext *ctx, s8_list *sv, s8 s) 1374 { 1375 iz result = meta_lookup_string_slow(sv, s); 1376 if (result < 0) { 1377 *da_push(ctx->arena, sv) = s; 1378 result = sv->count - 1; 1379 } 1380 return result; 1381 } 1382 1383 function iz 1384 meta_intern_id(MetaContext *ctx, MetaIDList *v, u32 id) 1385 { 1386 iz result = meta_lookup_id_slow(v, id); 1387 if (result < 0) { 1388 *da_push(ctx->arena, v) = id; 1389 result = v->count - 1; 1390 } 1391 return result; 1392 } 1393 1394 function iz 1395 meta_enumeration_id(MetaContext *ctx, s8 kind) 1396 { 1397 iz result = meta_intern_string(ctx, &ctx->enumeration_kinds, kind); 1398 if (ctx->enumeration_kinds.count != ctx->enumeration_members.count) { 1399 da_push(ctx->arena, &ctx->enumeration_members); 1400 assert(result == (ctx->enumeration_members.count - 1)); 1401 } 1402 return result; 1403 } 1404 1405 function MetaEnumeration 1406 meta_commit_enumeration(MetaContext *ctx, s8 kind, s8 variation) 1407 { 1408 iz kidx = meta_enumeration_id(ctx, kind); 1409 iz vidx = meta_intern_string(ctx, ctx->enumeration_members.data + kidx, variation); 1410 MetaEnumeration result = {.kind = kidx, .variation = vidx}; 1411 return result; 1412 } 1413 1414 function u16 1415 meta_pack_shader_name(MetaContext *ctx, s8 base_name, MetaLocation loc) 1416 { 1417 iz result = meta_intern_string(ctx, &ctx->shader_names, base_name); 1418 if (result > (iz)U16_MAX) 1419 meta_compiler_error(loc, "maximum base shaders exceeded: limit: %lu\n", U16_MAX); 1420 return (u16)result; 1421 } 1422 1423 function u8 1424 meta_commit_shader_flag(MetaContext *ctx, u32 flag_list_id, s8 flag, MetaEntry *e) 1425 { 1426 assert(flag_list_id < ctx->flags_for_shader.count); 1427 iz index = meta_intern_string(ctx, ctx->flags_for_shader.data + flag_list_id, flag); 1428 if (index > 7) meta_entry_error(e, "Shaders only support 8 local flags\n"); 1429 u8 result = (u8)index; 1430 return result; 1431 } 1432 1433 typedef struct { 1434 u16 entry_id; 1435 struct {u8 current; u8 target;} cursor; 1436 u32 permutation_id; 1437 } MetaShaderPermutationStackFrame; 1438 1439 typedef struct { 1440 MetaEntry *base_entry; 1441 1442 MetaShaderPermutationStackFrame *data; 1443 iz count; 1444 iz capacity; 1445 } MetaShaderPermutationStack; 1446 1447 function void 1448 meta_pack_shader_permutation(MetaContext *ctx, MetaShaderPermutation *sp, MetaShader *base_shader, 1449 MetaShaderPermutationStack *stack, MetaEntry *last, u32 frame_cursor) 1450 { 1451 //////////////////////////////////// 1452 // NOTE: fill ids from up the stack 1453 u32 global_flag_index = 0; 1454 for (iz i = 0; i < stack->count; i++) { 1455 MetaShaderPermutationStackFrame *f = stack->data + i; 1456 MetaEntry *e = stack->base_entry + f->entry_id; 1457 MetaEntryArgument *a = e->arguments; 1458 u32 cursor = f->cursor.current; 1459 switch (e->kind) { 1460 case MetaEntryKind_PermuteFlags:{ 1461 if (f->permutation_id == U32_MAX) { 1462 u32 test = cursor, packed = 0; 1463 for EachBit(test, flag) { 1464 u32 flag_index = meta_commit_shader_flag(ctx, base_shader->flag_list_id, a->strings[flag], e); 1465 packed |= (1u << flag_index); 1466 } 1467 f->permutation_id = packed; 1468 } 1469 sp->local_flags |= (u8)f->permutation_id; 1470 }break; 1471 case MetaEntryKind_Permute:{ 1472 if (f->permutation_id == U32_MAX) { 1473 MetaEnumeration p = meta_commit_enumeration(ctx, a[0].string, a[1].strings[cursor]); 1474 f->permutation_id = ((u32)(p.kind & 0xFFFFu) << 16) | (u32)(p.variation & 0xFFFFu); 1475 meta_intern_id(ctx, &base_shader->global_flag_ids, (u32)p.kind); 1476 } 1477 sp->global_flags[global_flag_index++] = f->permutation_id; 1478 }break; 1479 InvalidDefaultCase; 1480 } 1481 } 1482 1483 /////////////////////////////////// 1484 // NOTE: fill ids from stack frame 1485 MetaEntryArgument *a = last->arguments; 1486 switch (last->kind) { 1487 case MetaEntryKind_PermuteFlags:{ 1488 u32 packed = 0, test = frame_cursor; 1489 for EachBit(test, flag) { 1490 u32 flag_index = meta_commit_shader_flag(ctx, base_shader->flag_list_id, a->strings[flag], last); 1491 packed |= (1u << flag_index); 1492 } 1493 sp->local_flags |= (u8)packed; 1494 }break; 1495 case MetaEntryKind_Permute:{ 1496 MetaEnumeration p = meta_commit_enumeration(ctx, a[0].string, a[1].strings[frame_cursor]); 1497 sp->global_flags[global_flag_index++] = metagen_pack_permutation(ctx, p); 1498 meta_intern_id(ctx, &base_shader->global_flag_ids, (u32)p.kind); 1499 }break; 1500 InvalidDefaultCase; 1501 } 1502 } 1503 1504 function void 1505 meta_pop_and_pack_shader_permutations(MetaContext *ctx, MetaShader *base_shader, u32 local_flags, 1506 MetaShaderPermutationStack *stack) 1507 { 1508 assert(stack->count > 0); 1509 1510 u32 global_flag_count = 0; 1511 for (iz i = 0; i < stack->count; i++) { 1512 switch (stack->base_entry[stack->data[i].entry_id].kind) { 1513 case MetaEntryKind_PermuteFlags:{}break; 1514 case MetaEntryKind_Permute:{ global_flag_count++; }break; 1515 InvalidDefaultCase; 1516 } 1517 } 1518 1519 MetaShaderPermutationStackFrame *f = stack->data + (--stack->count); 1520 MetaEntry *last = stack->base_entry + f->entry_id; 1521 assert(f->cursor.current == 0); 1522 for (; f->cursor.current < f->cursor.target; f->cursor.current++) { 1523 MetaShaderPermutation *sp = da_push(ctx->arena, &base_shader->permutations); 1524 sp->global_flags_count = (u8)global_flag_count; 1525 sp->global_flags = push_array(ctx->arena, typeof(*sp->global_flags), global_flag_count); 1526 sp->local_flags = (u16)local_flags; 1527 1528 meta_pack_shader_permutation(ctx, sp, base_shader, stack, last, f->cursor.current); 1529 } 1530 } 1531 1532 function void 1533 meta_emit_shader_permutations(MetaContext *ctx, Arena scratch, MetaShader *s, u32 local_flags, 1534 MetaEntry *entries, iz entry_count) 1535 { 1536 assert(entry_count > 0); 1537 assert(entries[0].kind == MetaEntryKind_Permute || 1538 entries[0].kind == MetaEntryKind_PermuteFlags || 1539 entries[0].kind == MetaEntryKind_SubShader); 1540 1541 MetaShaderPermutationStack stack = {.base_entry = entries}; 1542 da_reserve(&scratch, &stack, 32); 1543 1544 b32 done = 0; 1545 for (iz i = 0; i < entry_count && !done; i++) { 1546 MetaEntry *e = entries + i; 1547 switch (e->kind) { 1548 case MetaEntryKind_PermuteFlags: 1549 case MetaEntryKind_Permute: 1550 { 1551 if (stack.count && stack.data[stack.count - 1].entry_id == (u16)i) { 1552 MetaShaderPermutationStackFrame *f = stack.data + (stack.count - 1); 1553 f->permutation_id = U32_MAX; 1554 f->cursor.current++; 1555 if (f->cursor.current == f->cursor.target) { 1556 stack.count--; 1557 done = stack.count == 0; 1558 } 1559 } else { 1560 u8 target; 1561 if (e->kind == MetaEntryKind_Permute) { 1562 meta_entry_argument_expected(e, s8("kind"), s8("[id ...]")); 1563 target = (u8)meta_entry_argument_expect(e, 1, MetaEntryArgumentKind_Array).count; 1564 } else { 1565 meta_entry_argument_expected(e, s8("[id ...]")); 1566 u32 count = (u32)meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_Array).count; 1567 target = (u8)(2u << (count - 1)); 1568 } 1569 *da_push(&scratch, &stack) = (MetaShaderPermutationStackFrame){ 1570 .entry_id = (u16)i, 1571 .permutation_id = U32_MAX, 1572 .cursor.target = target, 1573 }; 1574 } 1575 }break; 1576 case MetaEntryKind_SubShader:{}break; 1577 case MetaEntryKind_BeginScope:{}break; 1578 case MetaEntryKind_EndScope:{ 1579 meta_pop_and_pack_shader_permutations(ctx, s, local_flags, &stack); 1580 if (stack.count != 0) 1581 i = stack.data[stack.count - 1].entry_id - 1; 1582 }break; 1583 InvalidDefaultCase; 1584 } 1585 } 1586 if (stack.count) { 1587 assert(stack.count == 1); 1588 meta_pop_and_pack_shader_permutations(ctx, s, local_flags, &stack); 1589 } 1590 } 1591 1592 function iz 1593 meta_pack_shader(MetaContext *ctx, MetaShaderGroup *sg, Arena scratch, MetaEntry *entries, iz entry_count) 1594 { 1595 assert(entries[0].kind == MetaEntryKind_Shader); 1596 1597 MetaBaseShader *base_shader = da_push(ctx->arena, &ctx->base_shaders); 1598 MetaShader *s = da_push(ctx->arena, &ctx->shaders); 1599 *da_push(ctx->arena, &sg->shaders) = (u32)da_index(s, &ctx->shaders); 1600 { 1601 s8_list *flag_list = da_push(ctx->arena, &ctx->flags_for_shader); 1602 s->flag_list_id = (u32)da_index(flag_list, &ctx->flags_for_shader); 1603 } 1604 1605 base_shader->shader = s; 1606 if (entries->argument_count > 1) { 1607 meta_entry_argument_expected(entries, s8("[file_name]")); 1608 } else if (entries->argument_count == 1) { 1609 base_shader->file = meta_entry_argument_expect(entries, 0, MetaEntryArgumentKind_String).string; 1610 } 1611 s->base_name_id = meta_pack_shader_name(ctx, entries->name, entries->location); 1612 1613 i32 stack_items[32]; 1614 struct { i32 *data; iz capacity; iz count; } stack = {stack_items, countof(stack_items), 0}; 1615 1616 iz result; 1617 b32 in_sub_shader = 0; 1618 for (result = 0; result < entry_count; result++) { 1619 MetaEntry *e = entries + result; 1620 switch (e->kind) { 1621 case MetaEntryKind_BeginScope:{}break; 1622 case MetaEntryKind_SubShader:{ 1623 if (in_sub_shader) goto error; 1624 in_sub_shader = 1; 1625 } /* FALLTHROUGH */ 1626 case MetaEntryKind_PermuteFlags: 1627 case MetaEntryKind_Permute: 1628 case MetaEntryKind_Shader: 1629 { 1630 *da_push(&scratch, &stack) = (i32)result; 1631 if ((result + 1 < entry_count) && entries[result + 1].kind == MetaEntryKind_BeginScope) 1632 break; 1633 } /* FALLTHROUGH */ 1634 case MetaEntryKind_EndScope:{ 1635 i32 index = stack.data[--stack.count]; 1636 MetaEntry *ended = entries + index; 1637 if (index == 0) { 1638 assert(stack.count == 0 && ended->kind == MetaEntryKind_Shader); 1639 // NOTE(rnp): emit an empty single permutation 1640 if (s->permutations.count == 0) 1641 da_push(ctx->arena, &s->permutations); 1642 } else { 1643 u32 local_flags = 0; 1644 if (stack.count > 0 && entries[stack.data[stack.count - 1]].kind == MetaEntryKind_Shader) { 1645 MetaShader *fill = s; 1646 if (ended->kind == MetaEntryKind_SubShader) { 1647 fill = da_push(ctx->arena, &ctx->shaders); 1648 u32 sid = (u32)da_index(fill, &ctx->shaders); 1649 *da_push(ctx->arena, &sg->shaders) = sid; 1650 *da_push(ctx->arena, &base_shader->sub_shaders) = sid; 1651 1652 fill->flag_list_id = s->flag_list_id; 1653 fill->base_name_id = meta_pack_shader_name(ctx, ended->name, ended->location); 1654 local_flags = 1u << meta_commit_shader_flag(ctx, s->flag_list_id, ended->name, ended); 1655 in_sub_shader = 0; 1656 } 1657 meta_emit_shader_permutations(ctx, scratch, fill, local_flags, ended, result - index + 1); 1658 } 1659 } 1660 }break; 1661 case MetaEntryKind_Enumeration:{ 1662 meta_entry_argument_expected(e, s8("kind")); 1663 s8 kind = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 1664 iz kid = meta_enumeration_id(ctx, kind); 1665 meta_intern_id(ctx, &s->global_enumeration_ids, (u32)kid); 1666 }break; 1667 case MetaEntryKind_Flags:{ 1668 meta_entry_argument_expected(e, s8("[flag ...]")); 1669 MetaEntryArgument flags = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_Array); 1670 for (u32 index = 0; index < flags.count; index++) 1671 meta_commit_shader_flag(ctx, s->flag_list_id, flags.strings[index], e); 1672 }break; 1673 1674 default: 1675 error: 1676 { 1677 meta_entry_error(e, "invalid nested @%s() in @%s()\n", 1678 meta_entry_kind_strings[e->kind], 1679 meta_entry_kind_strings[MetaEntryKind_Shader]); 1680 }break; 1681 } 1682 if (stack.count == 0) 1683 break; 1684 } 1685 1686 return result; 1687 } 1688 1689 function void 1690 metagen_push_table(MetaprogramContext *m, Arena scratch, s8 row_start, s8 row_end, 1691 s8 **column_strings, uz rows, uz columns) 1692 { 1693 u32 *column_widths = 0; 1694 if (columns > 1) { 1695 column_widths = push_array(&scratch, u32, (iz)columns - 1); 1696 for (uz column = 0; column < columns - 1; column++) { 1697 s8 *strings = column_strings[column]; 1698 for (uz row = 0; row < rows; row++) 1699 column_widths[column] = MAX(column_widths[column], (u32)strings[row].len); 1700 } 1701 } 1702 1703 for (uz row = 0; row < rows; row++) { 1704 meta_begin_line(m, row_start); 1705 for (uz column = 0; column < columns; column++) { 1706 s8 text = column_strings[column][row]; 1707 meta_push(m, text); 1708 i32 pad = columns > 1 ? 1 : 0; 1709 if (column_widths && column < columns - 1) 1710 pad += (i32)column_widths[column] - (i32)text.len; 1711 if (column < columns - 1) meta_pad(m, ' ', pad); 1712 } 1713 meta_end_line(m, row_end); 1714 } 1715 } 1716 1717 function void 1718 metagen_push_c_struct(MetaprogramContext *m, s8 kind, s8 *types, uz types_count, s8 *fields, uz fields_count) 1719 { 1720 assert(fields_count == types_count); 1721 meta_begin_scope(m, s8("typedef struct {")); 1722 metagen_push_table(m, m->scratch, s8(""), s8(";"), (s8 *[]){types, fields}, fields_count, 2); 1723 meta_end_scope(m, s8("} "), kind, s8(";\n")); 1724 } 1725 1726 function void 1727 metagen_push_counted_enum_body(MetaprogramContext *m, s8 kind, s8 prefix, s8 mid, s8 suffix, s8 *ids, iz ids_count) 1728 { 1729 iz max_id_length = 0; 1730 for (iz id = 0; id < ids_count; id++) 1731 max_id_length = MAX(max_id_length, ids[id].len); 1732 1733 for (iz id = 0; id < ids_count; id++) { 1734 meta_begin_line(m, prefix, kind, ids[id]); 1735 meta_pad(m, ' ', 1 + (i32)(max_id_length - ids[id].len)); 1736 meta_push(m, mid); 1737 meta_push_u64(m, (u64)id); 1738 meta_end_line(m, suffix); 1739 } 1740 } 1741 1742 function void 1743 metagen_push_c_enum(MetaprogramContext *m, Arena scratch, s8 kind, s8 *ids, iz ids_count) 1744 { 1745 s8 kind_full = push_s8_from_parts(&scratch, s8(""), kind, s8("_")); 1746 meta_begin_scope(m, s8("typedef enum {")); 1747 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= "), s8(","), ids, ids_count); 1748 meta_push_line(m, kind_full, s8("Count,")); 1749 meta_end_scope(m, s8("} "), kind, s8(";\n")); 1750 } 1751 1752 function void 1753 metagen_push_c_flag_enum(MetaprogramContext *m, Arena scratch, s8 kind, s8 *ids, iz ids_count) 1754 { 1755 s8 kind_full = push_s8_from_parts(&scratch, s8(""), kind, s8("_")); 1756 meta_begin_scope(m, s8("typedef enum {")); 1757 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= (1 << "), s8("),"), ids, ids_count); 1758 meta_end_scope(m, s8("} "), kind, s8(";\n")); 1759 } 1760 1761 function void 1762 metagen_push_shader_derivative_vectors(MetaContext *ctx, MetaprogramContext *m, MetaShader *s, 1763 i32 sub_field_count, b32 has_local_flags) 1764 { 1765 meta_push_line(m, s8("// "), ctx->shader_names.data[s->base_name_id]); 1766 for (iz perm = 0; perm < s->permutations.count; perm++) { 1767 MetaShaderPermutation *p = s->permutations.data + perm; 1768 if (!has_local_flags && sub_field_count == 0) { 1769 meta_push_line(m, s8("0,")); 1770 } else { 1771 meta_begin_line(m, s8("(i32 []){")); 1772 for (u8 id = 0; id < p->global_flags_count; id++) { 1773 s8 kind = metagen_permutation_kind(ctx, p->global_flags[id]); 1774 s8 variation = metagen_permutation_variation(ctx, p->global_flags[id]); 1775 if (id != 0) meta_push(m, s8(", ")); 1776 meta_push(m, s8("Beamformer"), kind, s8("_"), variation); 1777 } 1778 1779 for (i32 id = p->global_flags_count; id < sub_field_count; id++) 1780 meta_push(m, s8(", -1")); 1781 1782 if (has_local_flags) { 1783 meta_push(m, s8(", 0x")); 1784 meta_push_u64_hex(m, p->local_flags); 1785 } 1786 meta_end_line(m, s8("},")); 1787 } 1788 } 1789 } 1790 1791 function void 1792 meta_push_shader_descriptors_table(MetaprogramContext *m, MetaContext *ctx) 1793 { 1794 Arena scratch_start = m->scratch; 1795 s8 *columns[5]; 1796 for EachElement(columns, it) 1797 columns[it] = push_array(&m->scratch, s8, ctx->shaders.count); 1798 1799 Stream sb = arena_stream(m->scratch); 1800 for (iz shader = 0; shader < ctx->shaders.count; shader++) { 1801 MetaShaderDescriptor *sd = ctx->shader_descriptors + shader; 1802 MetaShader *s = ctx->shaders.data + shader; 1803 1804 stream_append_u64(&sb, (u64)sd->first_match_vector_index); 1805 stream_append_byte(&sb, ','); 1806 columns[0][shader] = arena_stream_commit_and_reset(&m->scratch, &sb); 1807 1808 stream_append_u64(&sb, (u64)sd->one_past_last_match_vector_index); 1809 stream_append_byte(&sb, ','); 1810 columns[1][shader] = arena_stream_commit_and_reset(&m->scratch, &sb); 1811 1812 stream_append_u64(&sb, (u64)sd->sub_field_count); 1813 stream_append_byte(&sb, ','); 1814 columns[2][shader] = arena_stream_commit_and_reset(&m->scratch, &sb); 1815 1816 stream_append_u64(&sb, (u64)sd->sub_field_count + (u64)s->global_enumeration_ids.count); 1817 stream_append_byte(&sb, ','); 1818 columns[3][shader] = arena_stream_commit_and_reset(&m->scratch, &sb); 1819 1820 columns[4][shader] = sd->has_local_flags ? s8("1") : s8 ("0"); 1821 } 1822 1823 meta_begin_scope(m, s8("read_only global BeamformerShaderDescriptor beamformer_shader_descriptors[] = {")); 1824 metagen_push_table(m, m->scratch, s8("{"), s8("},"), columns, (u32)ctx->shaders.count, countof(columns)); 1825 meta_end_scope(m, s8("};\n")); 1826 1827 m->scratch = scratch_start; 1828 } 1829 1830 function void 1831 meta_push_shader_reload_info(MetaprogramContext *m, MetaContext *ctx) 1832 { 1833 /////////////////////////////// 1834 // NOTE(rnp): reloadable infos 1835 i32 max_shader_name_length = 0; 1836 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 1837 if (ctx->base_shaders.data[shader].file.len == 0) continue; 1838 s8 name = ctx->shader_names.data[ctx->base_shaders.data[shader].shader->base_name_id]; 1839 max_shader_name_length = MAX((i32)name.len, max_shader_name_length); 1840 } 1841 1842 meta_begin_scope(m, s8("read_only global BeamformerReloadableShaderInfo beamformer_reloadable_shader_infos[] = {")); 1843 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 1844 MetaBaseShader *bs = ctx->base_shaders.data + shader; 1845 MetaShader *s = bs->shader; 1846 1847 if (bs->file.len == 0) continue; 1848 1849 s8 name = ctx->shader_names.data[s->base_name_id]; 1850 meta_begin_line(m, s8("{BeamformerShaderKind_"), name, s8(", ")); 1851 meta_pad(m, ' ', max_shader_name_length - (i32)name.len); 1852 meta_push_u64(m, (u64)bs->sub_shaders.count); 1853 1854 if (bs->sub_shaders.count) { 1855 meta_push(m, s8(", (i32 []){")); 1856 for (iz sub_shader = 0; sub_shader < bs->sub_shaders.count; sub_shader++) { 1857 if (sub_shader != 0) meta_push(m, s8(", ")); 1858 meta_push_u64(m, bs->sub_shaders.data[sub_shader]); 1859 } 1860 meta_push(m, s8("}")); 1861 } else { 1862 meta_push(m, s8(", 0")); 1863 } 1864 meta_end_line(m, s8("},")); 1865 } 1866 meta_end_scope(m, s8("};\n")); 1867 1868 meta_begin_scope(m, s8("read_only global s8 beamformer_reloadable_shader_files[] = {")); 1869 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 1870 MetaBaseShader *bs = ctx->base_shaders.data + shader; 1871 if (bs->file.len == 0) continue; 1872 meta_push_line(m, s8("s8_comp(\""), bs->file, s8("\"),")); 1873 } 1874 meta_end_scope(m, s8("};\n")); 1875 1876 { 1877 u32 info_index = 0; 1878 for (iz group = 0; group < ctx->shader_groups.count; group++) { 1879 MetaShaderGroup *sg = ctx->shader_groups.data + group; 1880 meta_begin_line(m, s8("read_only global i32 beamformer_reloadable_")); 1881 for (iz i = 0; i < sg->name.len; i++) 1882 stream_append_byte(&m->stream, TOLOWER(sg->name.data[i])); 1883 meta_begin_scope(m, s8("_shader_info_indices[] = {")); 1884 1885 for (iz shader = 0; shader < sg->shaders.count; shader++) { 1886 MetaShader *s = ctx->shaders.data + sg->shaders.data[shader]; 1887 /* TODO(rnp): store base shader list in a better format */ 1888 for (iz base_shader = 0; base_shader < ctx->base_shaders.count; base_shader++) { 1889 MetaBaseShader *bs = ctx->base_shaders.data + base_shader; 1890 if (bs->file.len && bs->shader == s) { 1891 meta_indent(m); 1892 meta_push_u64(m, info_index++); 1893 meta_end_line(m, s8(",")); 1894 break; 1895 } 1896 } 1897 } 1898 meta_end_scope(m, s8("};\n")); 1899 } 1900 } 1901 1902 //////////////////////////////////// 1903 // NOTE(rnp): shader header strings 1904 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_global_header_strings[] = {")); 1905 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 1906 s8_list *sub_list = ctx->enumeration_members.data + kind; 1907 s8 kind_name = push_s8_from_parts(&m->scratch, s8(""), ctx->enumeration_kinds.data[kind], s8("_")); 1908 meta_push_line(m, s8("s8_comp(\"\"")); 1909 metagen_push_counted_enum_body(m, kind_name, s8("\"#define "), s8(""), s8("\\n\""), 1910 sub_list->data, sub_list->count); 1911 meta_push_line(m, s8("\"\\n\"),")); 1912 m->scratch = ctx->scratch; 1913 } 1914 meta_end_scope(m, s8("};\n")); 1915 1916 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_local_header_strings[] = {")); 1917 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 1918 if (ctx->base_shaders.data[shader].file.len == 0) continue; 1919 1920 MetaShader *s = ctx->base_shaders.data[shader].shader; 1921 s8_list *flag_list = ctx->flags_for_shader.data + s->flag_list_id; 1922 1923 if (flag_list->count) { 1924 meta_push_line(m, s8("s8_comp(\"\"")); 1925 metagen_push_counted_enum_body(m, s8("ShaderFlags_"), s8("\"#define "), s8("(1 << "), s8(")\\n\""), 1926 flag_list->data, flag_list->count); 1927 meta_push_line(m, s8("\"\\n\"),")); 1928 } else { 1929 meta_push_line(m, s8("{0},")); 1930 } 1931 } 1932 meta_end_scope(m, s8("};\n")); 1933 1934 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_descriptor_header_strings[] = {")); 1935 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) 1936 meta_push_line(m, s8("s8_comp(\""), ctx->enumeration_kinds.data[kind], s8("\"),")); 1937 meta_end_scope(m, s8("};\n")); 1938 } 1939 1940 function void 1941 meta_push_shader_match_helper(MetaprogramContext *m, MetaContext *ctx, MetaShader *s, MetaShaderDescriptor *sd) 1942 { 1943 s8 name = ctx->shader_names.data[s->base_name_id]; 1944 meta_push_line(m, s8("function iz")); 1945 meta_begin_line(m, s8("beamformer_shader_")); 1946 for (iz i = 0; i < name.len; i++) 1947 stream_append_byte(&m->stream, TOLOWER(name.data[i])); 1948 meta_push(m, s8("_match(")); 1949 1950 assert(s->global_flag_ids.count < 27); 1951 for (iz flag = 0; flag < s->global_flag_ids.count; flag++) { 1952 if (flag != 0) meta_push(m, s8(", ")); 1953 u32 index = s->global_flag_ids.data[flag]; 1954 meta_push(m, s8("Beamformer"), ctx->enumeration_kinds.data[index], s8(" ")); 1955 stream_append_byte(&m->stream, (u8)((iz)'a' + flag)); 1956 } 1957 if (sd->has_local_flags) { 1958 if (s->global_flag_ids.count) meta_push(m, s8(", ")); 1959 meta_push(m, s8("i32 flags")); 1960 } 1961 meta_end_line(m, s8(")")); 1962 1963 meta_begin_scope(m, s8("{")); 1964 meta_begin_line(m, s8("iz result = beamformer_shader_match((i32 []){(i32)")); 1965 for (iz flag = 0; flag < s->global_flag_ids.count; flag++) { 1966 if (flag != 0) meta_push(m, s8(", (i32)")); 1967 stream_append_byte(&m->stream, (u8)((iz)'a' + flag)); 1968 } 1969 if (sd->has_local_flags) { 1970 if (s->global_flag_ids.count) meta_push(m, s8(", ")); 1971 meta_push(m, s8("flags")); 1972 } 1973 meta_push(m, s8("}, ")); 1974 meta_push_u64(m, (u64)sd->first_match_vector_index); 1975 meta_push(m, s8(", ")); 1976 meta_push_u64(m, (u64)sd->one_past_last_match_vector_index); 1977 meta_push(m, s8(", ")); 1978 meta_push_u64(m, (u64)sd->sub_field_count + sd->has_local_flags); 1979 meta_end_line(m, s8(");")); 1980 meta_push_line(m, s8("return result;")); 1981 meta_end_scope(m, s8("}\n")); 1982 } 1983 1984 function b32 1985 metagen_emit_c_code(MetaContext *ctx, Arena arena) 1986 { 1987 b32 result = 1; 1988 1989 os_make_directory("generated"); 1990 char *out = "generated/beamformer.meta.c"; 1991 if (!needs_rebuild(out, "beamformer.meta")) 1992 return result; 1993 1994 build_log_generate("Core C Code"); 1995 1996 MetaprogramContext meta_program = {.stream = arena_stream(arena), .scratch = ctx->scratch}; 1997 MetaprogramContext *m = &meta_program; 1998 1999 meta_push_line(m, s8("/* See LICENSE for license details. */\n")); 2000 meta_push_line(m, s8("// GENERATED CODE\n")); 2001 2002 ///////////////////////// 2003 // NOTE(rnp): enumarents 2004 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 2005 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("Beamformer"), ctx->enumeration_kinds.data[kind]); 2006 metagen_push_c_enum(m, m->scratch, enum_name, ctx->enumeration_members.data[kind].data, 2007 ctx->enumeration_members.data[kind].count); 2008 m->scratch = ctx->scratch; 2009 } 2010 2011 for (iz shader = 0; shader < ctx->base_shaders.count; shader++) { 2012 MetaShader *s = ctx->base_shaders.data[shader].shader; 2013 s8_list flag_list = ctx->flags_for_shader.data[s->flag_list_id]; 2014 if (flag_list.count) { 2015 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("BeamformerShader"), 2016 ctx->shader_names.data[s->base_name_id], s8("Flags")); 2017 metagen_push_c_flag_enum(m, m->scratch, enum_name, flag_list.data, flag_list.count); 2018 m->scratch = ctx->scratch; 2019 } 2020 } 2021 2022 { 2023 s8 kind = s8("BeamformerShaderKind"); 2024 s8 kind_full = s8("BeamformerShaderKind_"); 2025 meta_begin_scope(m, s8("typedef enum {")); 2026 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= "), s8(","), 2027 ctx->shader_names.data, ctx->shader_names.count); 2028 meta_push_line(m, kind_full, s8("Count,\n")); 2029 2030 s8 *columns[2]; 2031 columns[0] = push_array(&m->scratch, s8, ctx->shader_groups.count * 3); 2032 columns[1] = push_array(&m->scratch, s8, ctx->shader_groups.count * 3); 2033 2034 for (iz group = 0; group < ctx->shader_groups.count; group++) { 2035 MetaShaderGroup *sg = ctx->shader_groups.data + group; 2036 2037 s8 first_name = ctx->shader_names.data[ctx->shaders.data[sg->shaders.data[0]].base_name_id]; 2038 s8 last_name = ctx->shader_names.data[ctx->shaders.data[sg->shaders.data[sg->shaders.count - 1]].base_name_id]; 2039 2040 columns[0][3 * group + 0] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("First")); 2041 columns[1][3 * group + 0] = push_s8_from_parts(&m->scratch, s8(""), s8("= "), kind, s8("_"), first_name); 2042 2043 columns[0][3 * group + 1] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("Last")); 2044 columns[1][3 * group + 1] = push_s8_from_parts(&m->scratch, s8(""),s8("= "), kind, s8("_"), last_name); 2045 2046 columns[0][3 * group + 2] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), sg->name, s8("Count")); 2047 Stream sb = arena_stream(m->scratch); 2048 stream_append_s8(&sb, s8("= ")); 2049 stream_append_u64(&sb, (u64)sg->shaders.count); 2050 columns[1][3 * group + 2] = arena_stream_commit(&m->scratch, &sb); 2051 } 2052 metagen_push_table(m, m->scratch, s8(""), s8(","), columns, (uz)ctx->shader_groups.count * 3, 2); 2053 2054 meta_end_scope(m, s8("} "), kind, s8(";\n")); 2055 m->scratch = ctx->scratch; 2056 } 2057 2058 ////////////////////// 2059 // NOTE(rnp): structs 2060 { 2061 s8 name = s8_comp("BeamformerShaderDescriptor"); 2062 s8 types[] = {s8_comp("i32"), s8_comp("i32"), s8_comp("i16"), s8_comp("i16"), s8_comp("b32")}; 2063 s8 names[] = { 2064 s8_comp("first_match_vector_index"), 2065 s8_comp("one_past_last_match_vector_index"), 2066 s8_comp("match_vector_length"), 2067 s8_comp("header_vector_length"), 2068 s8_comp("has_local_flags"), 2069 }; 2070 metagen_push_c_struct(m, name, types, countof(types), names, countof(names)); 2071 } 2072 2073 { 2074 s8 name = s8_comp("BeamformerReloadableShaderInfo"); 2075 s8 types[] = {s8_comp("BeamformerShaderKind"), s8_comp("i32"), s8_comp("i32 *")}; 2076 s8 names[] = { 2077 s8_comp("kind"), 2078 s8_comp("sub_shader_descriptor_index_count"), 2079 s8_comp("sub_shader_descriptor_indices"), 2080 }; 2081 metagen_push_c_struct(m, name, types, countof(types), names, countof(names)); 2082 } 2083 2084 /////////////////////////////////////// 2085 // NOTE(rnp): shader descriptor tables 2086 i32 match_vectors_count = 0; 2087 meta_begin_scope(m, s8("read_only global i32 *beamformer_shader_match_vectors[] = {")); 2088 for (iz shader = 0; shader < ctx->shaders.count; shader++) { 2089 MetaShader *s = ctx->shaders.data + shader; 2090 MetaShaderDescriptor *sd = ctx->shader_descriptors + shader; 2091 metagen_push_shader_derivative_vectors(ctx, m, s, sd->sub_field_count, sd->has_local_flags); 2092 match_vectors_count += (i32)s->permutations.count; 2093 } 2094 meta_end_scope(m, s8("};")); 2095 meta_begin_line(m, s8("#define beamformer_match_vectors_count (")); 2096 meta_push_u64(m, (u64)match_vectors_count); 2097 meta_end_line(m, s8(")\n")); 2098 2099 meta_push_shader_descriptors_table(m, ctx); 2100 2101 ///////////////////////////////// 2102 // NOTE(rnp): shader info tables 2103 meta_begin_scope(m, s8("read_only global s8 beamformer_shader_names[] = {")); 2104 metagen_push_table(m, m->scratch, s8("s8_comp(\""), s8("\"),"), &ctx->shader_names.data, 2105 (uz)ctx->shader_names.count, 1); 2106 meta_end_scope(m, s8("};\n")); 2107 2108 meta_push_shader_reload_info(m, ctx); 2109 2110 meta_begin_scope(m, s8("read_only global i32 *beamformer_shader_header_vectors[] = {")); 2111 for (iz shader = 0; shader < ctx->shaders.count; shader++) { 2112 MetaShader *s = ctx->shaders.data + shader; 2113 2114 if (s->global_flag_ids.count) { 2115 meta_begin_line(m, s8("(i32 []){")); 2116 for (iz id = 0; id < s->global_flag_ids.count; id++) { 2117 if (id != 0) meta_push(m, s8(", ")); 2118 meta_push_u64(m, s->global_flag_ids.data[id]); 2119 } 2120 for (iz id = 0; id < s->global_enumeration_ids.count; id++) { 2121 if (id != 0 || s->global_flag_ids.count) meta_push(m, s8(", ")); 2122 meta_push_u64(m, s->global_enumeration_ids.data[id]); 2123 } 2124 meta_end_line(m, s8("},")); 2125 } else { 2126 meta_push_line(m, s8("0,")); 2127 } 2128 } 2129 meta_end_scope(m, s8("};\n")); 2130 2131 ////////////////////////////////////// 2132 // NOTE(rnp): shader matching helpers 2133 meta_push_line(m, s8("function iz")); 2134 meta_push_line(m, s8("beamformer_shader_match(i32 *match_vector, i32 first_index, i32 one_past_last_index, i32 vector_length)")); 2135 meta_begin_scope(m, s8("{")); 2136 meta_push_line(m, s8("iz result = first_index;")); 2137 meta_push_line(m, s8("i32 best_score = 0;")); 2138 meta_push_line(m, s8("for (i32 index = first_index; index < one_past_last_index; index++)")); 2139 meta_begin_scope(m, s8("{")); 2140 meta_push_line(m, s8("i32 score = 0;")); 2141 meta_push_line(m, s8("i32 *v = beamformer_shader_match_vectors[index];")); 2142 meta_begin_scope(m, s8("for (i32 i = 0; i < vector_length; i++) {")); 2143 meta_begin_scope(m, s8("if (match_vector[i] == v[i]) {")); 2144 meta_push_line(m, s8("score++;")); 2145 meta_end_scope(m, s8("}")); 2146 meta_end_scope(m, s8("}")); 2147 meta_begin_scope(m, s8("if (best_score < score) {")); 2148 meta_push_line(m, s8("result = index;")); 2149 meta_push_line(m, s8("best_score = score;")); 2150 meta_end_scope(m, s8("}")); 2151 meta_end_scope(m, s8("}")); 2152 meta_push_line(m, s8("return result;")); 2153 meta_end_scope(m, s8("}\n")); 2154 2155 for (iz shader = 0; shader < ctx->shaders.count; shader++) { 2156 MetaShader *s = ctx->shaders.data + shader; 2157 MetaShaderDescriptor *sd = ctx->shader_descriptors + shader; 2158 if (sd->sub_field_count || sd->has_local_flags) 2159 meta_push_shader_match_helper(m, ctx, s, sd); 2160 } 2161 2162 //fprintf(stderr, "%.*s\n", (i32)m.stream.widx, m.stream.data); 2163 2164 result = meta_write_and_reset(m, out); 2165 2166 return result; 2167 } 2168 2169 function b32 2170 metagen_emit_matlab_code(MetaContext *ctx, Arena arena) 2171 { 2172 b32 result = 1; 2173 if (!needs_rebuild(OUTPUT("matlab/OGLBeamformerFilterKind.m"), "beamformer_parameters.h")) 2174 return result; 2175 2176 build_log_generate("MATLAB Bindings"); 2177 /* TODO(rnp): recreate/clear directory incase these file names change */ 2178 os_make_directory(OUTPUT("matlab")); 2179 2180 MetaprogramContext meta_program = {.stream = arena_stream(arena), .scratch = ctx->scratch}; 2181 MetaprogramContext *m = &meta_program; 2182 2183 #define X(name, flag, ...) meta_push_line(m, s8(#name " (" str(flag) ")")); 2184 meta_begin_matlab_class(m, "OGLBeamformerLiveFeedbackFlags", "int32"); 2185 meta_begin_scope(m, s8("enumeration")); 2186 BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST 2187 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveFeedbackFlags.m")); 2188 #undef X 2189 2190 #define X(kind, ...) meta_push_matlab_enum_with_value(m, s8(#kind), BeamformerFilterKind_## kind); 2191 meta_begin_matlab_class(m, "OGLBeamformerFilterKind", "int32"); 2192 meta_begin_scope(m, s8("enumeration")); 2193 BEAMFORMER_FILTER_KIND_LIST(,) 2194 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerFilterKind.m")); 2195 #undef X 2196 2197 os_make_directory(OUTPUT("matlab/+OGLBeamformerFilter")); 2198 #define X(kind, ...) {OUTPUT("matlab/+OGLBeamformerFilter/" #kind ".m"), s8_comp(#kind), s8_comp(#__VA_ARGS__)}, 2199 read_only local_persist struct {char *out; s8 class, args;} filter_table[] = { 2200 BEAMFORMER_FILTER_KIND_LIST(,) 2201 }; 2202 #undef X 2203 2204 s8_list members = {0}; 2205 for EachNonZeroEnumValue(BeamformerFilterKind, filter) { 2206 typeof(*filter_table) *f = filter_table + filter; 2207 members.count = 0; 2208 s8_list_from_s8(&members, &m->scratch, f->args); 2209 meta_begin_scope(m, s8("classdef "), f->class, s8(" < OGLBeamformerFilter.BaseFilter")); 2210 2211 meta_begin_scope(m, s8("properties")); 2212 for (iz it = 0; it < members.count; it++) 2213 meta_push_matlab_property(m, members.data[it], 1, s8("single")); 2214 meta_end_scope(m, s8("end")); 2215 2216 meta_begin_scope(m, s8("methods")); 2217 meta_begin_line(m, s8("function obj = "), f->class, s8("(")); 2218 for (iz it = 0; it < members.count; it++) 2219 meta_push(m, it > 0 ? s8(", ") : s8(""), members.data[it]); 2220 meta_end_line(m, s8(")")); 2221 2222 m->indentation_level++; 2223 for (iz it = 0; it < members.count; it++) 2224 meta_push_line(m, s8("obj."), members.data[it], s8(" = "), members.data[it], s8(";")); 2225 result &= meta_end_and_write_matlab(m, f->out); 2226 } 2227 m->scratch = ctx->scratch; 2228 2229 meta_begin_matlab_class(m, "BaseFilter"); 2230 meta_begin_scope(m, s8("methods")); 2231 meta_begin_scope(m, s8("function out = Flatten(obj)")); 2232 meta_push_line(m, s8("fields = struct2cell(struct(obj));")); 2233 meta_push_line(m, s8("out = zeros(1, numel(fields));")); 2234 meta_begin_scope(m, s8("for i = 1:numel(fields)")); 2235 meta_push_line(m, s8("out(i) = fields{i};")); 2236 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/+OGLBeamformerFilter/BaseFilter.m")); 2237 2238 #define X(name, __t, __s, kind, elements, ...) meta_push_matlab_property(m, s8(#name), (u64)elements, s8(#kind)); 2239 meta_begin_matlab_class(m, "OGLBeamformerParameters"); 2240 meta_begin_scope(m, s8("properties")); 2241 BEAMFORMER_PARAMS_HEAD 2242 BEAMFORMER_UI_PARAMS 2243 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParameters.m")); 2244 2245 meta_begin_matlab_class(m, "OGLBeamformerParametersHead"); 2246 meta_begin_scope(m, s8("properties")); 2247 BEAMFORMER_PARAMS_HEAD 2248 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParametersHead.m")); 2249 2250 meta_begin_matlab_class(m, "OGLBeamformerParametersUI"); 2251 meta_begin_scope(m, s8("properties")); 2252 BEAMFORMER_UI_PARAMS 2253 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParametersUI.m")); 2254 2255 meta_begin_matlab_class(m, "OGLBeamformerSimpleParameters"); 2256 meta_begin_scope(m, s8("properties")); 2257 BEAMFORMER_PARAMS_HEAD 2258 BEAMFORMER_UI_PARAMS 2259 BEAMFORMER_SIMPLE_PARAMS 2260 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerSimpleParameters.m")); 2261 #undef X 2262 2263 #define X(name, __t, __s, elements, ...) meta_push_matlab_property(m, s8(#name), elements, s8("")); 2264 meta_begin_matlab_class(m, "OGLBeamformerLiveImagingParameters"); 2265 meta_begin_scope(m, s8("properties")); 2266 BEAMFORMER_LIVE_IMAGING_PARAMETERS_LIST 2267 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveImagingParameters.m")); 2268 #undef X 2269 2270 meta_begin_matlab_class(m, "OGLBeamformerShaderStage", "int32"); 2271 meta_begin_scope(m, s8("enumeration")); 2272 { 2273 iz index = -1; 2274 for (iz group = 0; group < ctx->shader_groups.count; group++) { 2275 if (s8_equal(ctx->shader_groups.data[group].name, s8("Compute"))) { 2276 index = group; 2277 break; 2278 } 2279 } 2280 if (index != -1) { 2281 MetaShaderGroup *sg = ctx->shader_groups.data + index; 2282 /* TODO(rnp): this assumes that the shaders are sequential */ 2283 s8 *names = ctx->shader_names.data + ctx->shaders.data[0].base_name_id; 2284 metagen_push_counted_enum_body(m, s8(""), s8(""), s8("("), s8(")"), names, sg->shaders.count); 2285 } else { 2286 build_log_failure("failed to find Compute shader group in meta info\n"); 2287 } 2288 result &= index != -1; 2289 } 2290 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerShaderStage.m")); 2291 2292 for (iz kind = 0; kind < ctx->enumeration_kinds.count; kind++) { 2293 Arena scratch = ctx->scratch; 2294 s8 name = ctx->enumeration_kinds.data[kind]; 2295 s8 output = push_s8_from_parts(&scratch, s8(""), s8(OUTPUT("matlab/OGLBeamformer")), name, s8(".m")); 2296 s8_list *kinds = ctx->enumeration_members.data + kind; 2297 meta_begin_scope(m, s8("classdef OGLBeamformer"), name, s8(" < int32")); 2298 meta_begin_scope(m, s8("enumeration")); 2299 s8 prefix = s8(""); 2300 if (kinds->count > 0 && ISDIGIT(kinds->data[0].data[0])) prefix = s8("m"); 2301 metagen_push_counted_enum_body(m, s8(""), prefix, s8("("), s8(")"), kinds->data, kinds->count); 2302 result &= meta_end_and_write_matlab(m, (c8 *)output.data); 2303 } 2304 2305 return result; 2306 } 2307 2308 function b32 2309 metagen_emit_helper_library_header(MetaContext *ctx, Arena arena) 2310 { 2311 b32 result = 1; 2312 char *out = OUTPUT("ogl_beamformer_lib.h"); 2313 if (!needs_rebuild(out, "helpers/ogl_beamformer_lib_base.h", "beamformer.meta")) 2314 return result; 2315 2316 build_log_generate("Helper Library Header"); 2317 2318 s8 parameters_header = os_read_whole_file(&arena, "beamformer_parameters.h"); 2319 s8 base_header = os_read_whole_file(&arena, "helpers/ogl_beamformer_lib_base.h"); 2320 2321 MetaprogramContext meta_program = {.stream = arena_stream(arena), .scratch = ctx->scratch}; 2322 MetaprogramContext *m = &meta_program; 2323 2324 meta_push_line(m, s8("/* See LICENSE for license details. */\n")); 2325 meta_push_line(m, s8("// GENERATED CODE\n")); 2326 2327 { 2328 iz index = meta_lookup_string_slow(&ctx->enumeration_kinds, s8("DataKind")); 2329 if (index != -1) { 2330 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8("Beamformer"), ctx->enumeration_kinds.data[index]); 2331 metagen_push_c_enum(m, m->scratch, enum_name, ctx->enumeration_members.data[index].data, 2332 ctx->enumeration_members.data[index].count); 2333 m->scratch = ctx->scratch; 2334 } else { 2335 build_log_failure("failed to find DataKind in meta info\n"); 2336 } 2337 } 2338 2339 { 2340 iz index = -1; 2341 for (iz group = 0; group < ctx->shader_groups.count; group++) { 2342 if (s8_equal(ctx->shader_groups.data[group].name, s8("Compute"))) { 2343 index = group; 2344 break; 2345 } 2346 } 2347 if (index != -1) { 2348 MetaShaderGroup *sg = ctx->shader_groups.data + index; 2349 meta_begin_line(m, s8("#define BeamformerShaderKind_ComputeCount (")); 2350 meta_push_u64(m, (u64)sg->shaders.count); 2351 meta_end_line(m, s8(")\n")); 2352 } else { 2353 build_log_failure("failed to find Compute shader group in meta info\n"); 2354 } 2355 } 2356 2357 meta_push(m, parameters_header, base_header); 2358 result &= meta_write_and_reset(m, out); 2359 2360 return result; 2361 } 2362 2363 function MetaContext * 2364 metagen_load_context(Arena *arena) 2365 { 2366 if (setjmp(compiler_jmp_buf)) { 2367 /* NOTE(rnp): compiler error */ 2368 return 0; 2369 } 2370 2371 MetaContext *ctx = push_struct(arena, MetaContext); 2372 ctx->scratch = sub_arena(arena, MB(1), 16); 2373 ctx->arena = arena; 2374 2375 MetaContext *result = ctx; 2376 2377 Arena scratch = ctx->scratch; 2378 MetaEntryStack entries = meta_entry_stack_from_file(ctx->arena, scratch, "beamformer.meta"); 2379 2380 i32 stack_items[32]; 2381 struct { i32 *data; iz capacity; iz count; } stack = {stack_items, countof(stack_items), 0}; 2382 2383 MetaShaderGroup *current_shader_group = 0; 2384 for (iz i = 0; i < entries.count; i++) { 2385 MetaEntry *e = entries.data + i; 2386 //if (e->kind == MetaEntryKind_EndScope) depth--; 2387 //meta_entry_print(e, depth, -1); 2388 //if (e->kind == MetaEntryKind_BeginScope) depth++; 2389 //continue; 2390 2391 switch (e->kind) { 2392 case MetaEntryKind_BeginScope:{ *da_push(&scratch, &stack) = (i32)(i - 1); }break; 2393 case MetaEntryKind_EndScope:{ 2394 i32 index = stack.data[--stack.count]; 2395 MetaEntry *ended = entries.data + index; 2396 switch (ended->kind) { 2397 case MetaEntryKind_ShaderGroup:{ current_shader_group = 0; }break; 2398 default:{}break; 2399 } 2400 }break; 2401 case MetaEntryKind_Enumeration:{ 2402 meta_entry_argument_expected(e, s8("kind"), s8("[id ...]")); 2403 s8 kind = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2404 MetaEntryArgument ids = meta_entry_argument_expect(e, 1, MetaEntryArgumentKind_Array); 2405 for (u32 id = 0; id < ids.count; id++) 2406 meta_commit_enumeration(ctx, kind, ids.strings[id]); 2407 }break; 2408 case MetaEntryKind_ShaderGroup:{ 2409 MetaShaderGroup *sg = da_push(ctx->arena, &ctx->shader_groups); 2410 sg->name = e->name; 2411 current_shader_group = sg; 2412 }break; 2413 case MetaEntryKind_Shader:{ 2414 if (!current_shader_group) goto error; 2415 i += meta_pack_shader(ctx, current_shader_group, scratch, e, entries.count - i); 2416 }break; 2417 2418 error: 2419 default: 2420 { 2421 meta_entry_error(e, "invalid @%s() in global scope\n", meta_entry_kind_strings[e->kind]); 2422 }break; 2423 } 2424 } 2425 2426 ctx->shader_descriptors = push_array(ctx->arena, MetaShaderDescriptor, ctx->shaders.count); 2427 { 2428 i32 match_vectors_count = 0; 2429 for (iz shader = 0; shader < ctx->shaders.count; shader++) { 2430 MetaShader *s = ctx->shaders.data + shader; 2431 MetaShaderDescriptor *sd = ctx->shader_descriptors + shader; 2432 2433 sd->has_local_flags = ctx->flags_for_shader.data[s->flag_list_id].count > 0; 2434 sd->sub_field_count = (i32)s->global_flag_ids.count; 2435 sd->first_match_vector_index = match_vectors_count; 2436 match_vectors_count += (i32)s->permutations.count; 2437 sd->one_past_last_match_vector_index = match_vectors_count; 2438 } 2439 } 2440 2441 result->arena = 0; 2442 return result; 2443 } 2444 2445 i32 2446 main(i32 argc, char *argv[]) 2447 { 2448 u64 start_time = os_get_timer_counter(); 2449 g_argv0 = argv[0]; 2450 2451 b32 result = 1; 2452 Arena arena = os_alloc_arena(MB(8)); 2453 check_rebuild_self(arena, argc, argv); 2454 2455 os_make_directory(OUTDIR); 2456 2457 MetaContext *meta = metagen_load_context(&arena); 2458 if (!meta) return 1; 2459 2460 result &= metagen_emit_c_code(meta, arena); 2461 result &= metagen_emit_helper_library_header(meta, arena); 2462 result &= metagen_emit_matlab_code(meta, arena); 2463 2464 Options options = parse_options(argc, argv); 2465 2466 CommandList c = cmd_base(&arena, &options); 2467 if (!check_build_raylib(arena, c, options.debug)) return 1; 2468 2469 ///////////////////////////////////// 2470 // extra flags (unusable for raylib) 2471 cmd_append(&arena, &c, EXTRA_FLAGS); 2472 2473 ///////////////// 2474 // helpers/tests 2475 result &= build_helper_library(arena, c); 2476 if (options.tests) result &= build_tests(arena, c); 2477 2478 ////////////////// 2479 // static portion 2480 iz c_count = c.count; 2481 cmd_append(&arena, &c, OS_MAIN, OUTPUT_EXE("ogl")); 2482 cmd_pdb(&arena, &c, "ogl"); 2483 if (options.debug) { 2484 if (!is_w32) cmd_append(&arena, &c, "-Wl,--export-dynamic", "-Wl,-rpath,."); 2485 if (!is_msvc) cmd_append(&arena, &c, "-L."); 2486 cmd_append(&arena, &c, LINK_LIB("raylib")); 2487 } else { 2488 cmd_append(&arena, &c, OUTPUT(OS_STATIC_LIB("raylib"))); 2489 } 2490 if (!is_msvc) cmd_append(&arena, &c, "-lm"); 2491 if (is_unix) cmd_append(&arena, &c, "-lGL"); 2492 if (is_w32) { 2493 cmd_append(&arena, &c, LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), 2494 LINK_LIB("opengl32"), LINK_LIB("winmm"), LINK_LIB("Synchronization")); 2495 if (!is_msvc) cmd_append(&arena, &c, "-Wl,--out-implib," OUTPUT(OS_STATIC_LIB("main"))); 2496 } 2497 cmd_append(&arena, &c, (void *)0); 2498 2499 result &= run_synchronous(arena, &c); 2500 c.count = c_count; 2501 2502 ///////////////////////// 2503 // hot reloadable portion 2504 // 2505 // NOTE: this is built after main because on w32 we need to export 2506 // gl function pointers for the reloadable portion to import 2507 if (options.debug) { 2508 if (is_msvc) { 2509 build_static_library_from_objects(arena, OUTPUT_LIB(OS_STATIC_LIB("main")), 2510 arg_list(char *, "/def", "/name:ogl.exe"), 2511 arg_list(char *, OUTPUT(OBJECT("main_w32")))); 2512 } 2513 result &= build_beamformer_as_library(arena, c); 2514 } 2515 2516 if (options.time) { 2517 f64 seconds = (f64)(os_get_timer_counter() - start_time) / (f64)os_get_timer_frequency(); 2518 build_log_info("took %0.03f [s]", seconds); 2519 } 2520 2521 return result != 1; 2522 }