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