#if 0 gcc -fsanitize=address,undefined,leak gen_gl.c -o gen_gl && ./gen_gl exit #endif #define KIT_IMPLEMENTATION #include "include/kit.inl.h" #define GL_FOLDER ".gl_registry" #define GL_REPO "https://github.com/KhronosGroup/OpenGL-Registry" typedef struct { str_t name; str_t value; } enum_t; typedef struct { str_t name; str_builder_t return_type; DA(str_builder_t) arguments; } proc_t; typedef struct { str_t name; DA(str_t) procs; } feature_t; int main(int argc, char **argv) { if (path_type(SZ("." PATH_DELIM GL_FOLDER)) == PATH_NONE) { int s = system("git clone --quiet --depth 1 " GL_REPO " " GL_FOLDER); int code = s & 0xff; if (code != 0) { printf("`git clone` failed.\n"); return code; } } if (path_type(SZ("." PATH_DELIM GL_FOLDER)) != PATH_FOLDER) { printf("OpenGL registry folder not found.\n"); return 1; } FILE *f = fopen("." PATH_DELIM GL_FOLDER PATH_DELIM "xml" PATH_DELIM "gl.xml", "rb"); if (f == NULL) { printf("Failed to read OpenGL registry.\n"); return 1; } struct timespec t0, t1; is_handle_t is = is_wrap_file(f, NULL); timespec_get(&t0, TIME_UTC); xml_parse_result_t res = xml_parse(is, NULL); timespec_get(&t1, TIME_UTC); is_destroy(is); fclose(f); if (res.status != KIT_OK) { printf("XML parse failed.\n"); return 1; } i64 diff = 1000 * (t1.tv_sec - t0.tv_sec) + (t1.tv_nsec - t0.tv_nsec + 500000) / 1000000; printf("XML parsed in %lld milliseconds.\n", diff); DA(enum_t) enums; DA(proc_t) procs; DA(feature_t) features; #define DESTROY_ALL_ \ for (i64 i_ = 0; i_ < procs.size; i_++) { \ for (i64 j_ = 0; j_ < procs.values[i_].arguments.size; j_++) \ DA_DESTROY(procs.values[i_].arguments.values[j_]); \ DA_DESTROY(procs.values[i_].return_type); \ DA_DESTROY(procs.values[i_].arguments); \ } \ for (i64 i_ = 0; i_ < features.size; i_++) \ DA_DESTROY(features.values[i_].procs); \ DA_DESTROY(enums); \ DA_DESTROY(procs); \ DA_DESTROY(features) DA_INIT(enums, 0, NULL); DA_INIT(procs, 0, NULL); DA_INIT(features, 0, NULL); xml_t *registry = res.xml.children.values + 1; for (i64 i = 0; i < registry->children.size; i++) { xml_t *tag_enums = registry->children.values + i; if (!AR_EQUAL(tag_enums->tag, SZ("enums"))) continue; for (i64 j = 0; j < tag_enums->children.size; j++) { xml_t *tag_enum = tag_enums->children.values + j; if (!AR_EQUAL(tag_enum->tag, SZ("enum"))) continue; xml_property_t *pro_name = NULL; xml_property_t *pro_value = NULL; for (i64 k = 0; k < tag_enum->properties.size; k++) { xml_property_t *pro = tag_enum->properties.values + k; if (AR_EQUAL(pro->name, SZ("name"))) pro_name = pro; else if (AR_EQUAL(pro->name, SZ("value"))) pro_value = pro; } if (pro_name == NULL || pro_value == NULL) continue; i8 found = 0; for (i64 k = 0; k < enums.size; k++) if (AR_EQUAL(pro_name->value, enums.values[k].name)) { found = 1; break; } if (found) continue; i64 n = enums.size; DA_RESIZE(enums, n + 1); if (enums.size != n + 1) { printf("Bad alloc.\n"); DESTROY_ALL_; return 1; } enums.values[n].name = WRAP_STR(pro_name->value); enums.values[n].value = WRAP_STR(pro_value->value); } } for (i64 i = 0; i < registry->children.size; i++) { xml_t *tag_commands = registry->children.values + i; if (!AR_EQUAL(tag_commands->tag, SZ("commands"))) continue; for (i64 j = 0; j < tag_commands->children.size; j++) { xml_t *tag_command = tag_commands->children.values + j; if (!AR_EQUAL(tag_command->tag, SZ("command"))) continue; xml_t *tag_proto = NULL; xml_t *tag_name = NULL; for (i64 k = 0; k < tag_command->children.size; k++) { tag_proto = tag_command->children.values + k; if (!AR_EQUAL(tag_proto->tag, SZ("proto"))) continue; for (i64 l = 0; l < tag_proto->children.size; l++) if (AR_EQUAL(tag_proto->children.values[l].tag, SZ("name"))) { tag_name = tag_proto->children.values + l; break; } break; } if (tag_proto == NULL || tag_name == NULL) continue; i64 n = procs.size; DA_RESIZE(procs, n + 1); if (procs.size != n + 1) { printf("Bad alloc.\n"); DESTROY_ALL_; return 1; } procs.values[n].name = WRAP_STR(tag_name->text); procs.values[n].return_type = xml_full_text(tag_proto, NULL).text; procs.values[n].return_type.size -= procs.values[n].name.size; DA_INIT(procs.values[n].arguments, tag_command->children.size - 1, NULL); if (procs.values[n].arguments.size != tag_command->children.size - 1) { printf("Bad alloc.\n"); DESTROY_ALL_; return 1; } procs.values[n].arguments.size = 0; for (i64 k = 0; k < tag_command->children.size; k++) { xml_t *tag_param = tag_command->children.values + k; if (AR_EQUAL(tag_param->tag, SZ("param"))) procs.values[n] .arguments.values[procs.values[n].arguments.size++] = xml_full_text(tag_param, NULL).text; } } } for (i64 i = 0; i < registry->children.size; i++) { xml_t *tag_feature = registry->children.values + i; if (!AR_EQUAL(tag_feature->tag, SZ("feature"))) continue; xml_property_t *pro_name = NULL; for (i64 k = 0; k < tag_feature->properties.size; k++) { xml_property_t *pro = tag_feature->properties.values + k; if (AR_EQUAL(pro->name, SZ("name"))) pro_name = pro; } if (pro_name == NULL) continue; i64 n = features.size; DA_RESIZE(features, n + 1); if (features.size != n + 1) { printf("Bad alloc.\n"); DESTROY_ALL_; return 1; } features.values[n].name = WRAP_STR(pro_name->value); DA_INIT(features.values[n].procs, 0, NULL); for (i64 j = 0; j < tag_feature->children.size; j++) { xml_t *tag_require = tag_feature->children.values + j; if (!AR_EQUAL(tag_require->tag, SZ("require"))) continue; i64 m = features.values[n].procs.size; DA_RESIZE(features.values[n].procs, m + tag_require->children.size); if (features.values[n].procs.size != m + tag_require->children.size) { printf("Bad alloc.\n"); DESTROY_ALL_; return 1; } features.values[n].procs.size = m; for (i64 k = 0; k < tag_require->children.size; k++) { xml_t *tag_command = tag_require->children.values + k; if (!AR_EQUAL(tag_command->tag, SZ("command"))) continue; pro_name = NULL; for (i64 k = 0; k < tag_command->properties.size; k++) { xml_property_t *pro = tag_command->properties.values + k; if (AR_EQUAL(pro->name, SZ("name"))) pro_name = pro; } if (pro_name == NULL) continue; features.values[n] .procs.values[features.values[n].procs.size++] = WRAP_STR( pro_name->value); } } if (features.values[n].procs.size == 0) { DA_DESTROY(features.values[n].procs); --features.size; } } for (i64 r = 0; r < registry->children.size; r++) { xml_t *tag_extensions = registry->children.values + r; if (!AR_EQUAL(tag_extensions->tag, SZ("extensions"))) continue; for (i64 i = 0; i < tag_extensions->children.size; i++) { xml_t *tag_extension = tag_extensions->children.values + i; if (!AR_EQUAL(tag_extension->tag, SZ("extension"))) continue; xml_property_t *pro_name = NULL; for (i64 k = 0; k < tag_extension->properties.size; k++) { xml_property_t *pro = tag_extension->properties.values + k; if (AR_EQUAL(pro->name, SZ("name"))) pro_name = pro; } if (pro_name == NULL) continue; i64 n = features.size; DA_RESIZE(features, n + 1); if (features.size != n + 1) { printf("Bad alloc.\n"); DESTROY_ALL_; return 1; } features.values[n].name = WRAP_STR(pro_name->value); DA_INIT(features.values[n].procs, 0, NULL); for (i64 j = 0; j < tag_extension->children.size; j++) { xml_t *tag_require = tag_extension->children.values + j; if (!AR_EQUAL(tag_require->tag, SZ("require"))) continue; i64 m = features.values[n].procs.size; DA_RESIZE(features.values[n].procs, m + tag_require->children.size); if (features.values[n].procs.size != m + tag_require->children.size) { printf("Bad alloc.\n"); DESTROY_ALL_; return 1; } features.values[n].procs.size = m; for (i64 k = 0; k < tag_require->children.size; k++) { xml_t *tag_command = tag_require->children.values + k; if (!AR_EQUAL(tag_command->tag, SZ("command"))) continue; pro_name = NULL; for (i64 k = 0; k < tag_command->properties.size; k++) { xml_property_t *pro = tag_command->properties.values + k; if (AR_EQUAL(pro->name, SZ("name"))) pro_name = pro; } if (pro_name == NULL) continue; features.values[n] .procs.values[features.values[n].procs.size++] = WRAP_STR(pro_name->value); } } if (features.values[n].procs.size == 0) { DA_DESTROY(features.values[n].procs); --features.size; } } } f = fopen("kit_gl.h", "wb"); if (f == NULL) { printf("Failed to write kit_gl.inl.h.\n"); DESTROY_ALL_; return 1; } fprintf(f, "#include \n"); fprintf(f, "#include \n"); fprintf(f, "#ifdef _WIN32\n"); fprintf(f, "# define GL_API_ENTRY __stdcall\n"); fprintf(f, "#else\n"); fprintf(f, "# define GL_API_ENTRY\n"); fprintf(f, "#endif\n"); fprintf(f, "typedef unsigned GLenum;\n"); fprintf(f, "typedef unsigned char GLboolean;\n"); fprintf(f, "typedef unsigned GLbitfield;\n"); fprintf(f, "typedef signed char GLbyte;\n"); fprintf(f, "typedef unsigned char GLubyte;\n"); fprintf(f, "typedef signed short GLshort;\n"); fprintf(f, "typedef unsigned short GLushort;\n"); fprintf(f, "typedef int GLint;\n"); fprintf(f, "typedef unsigned GLuint;\n"); fprintf(f, "typedef int GLclampx;\n"); fprintf(f, "typedef int GLsizei;\n"); fprintf(f, "typedef float GLfloat;\n"); fprintf(f, "typedef float GLclampf;\n"); fprintf(f, "typedef double GLdouble;\n"); fprintf(f, "typedef double GLclampd;\n"); fprintf(f, "typedef void * GLeglClientBufferEXT;\n"); fprintf(f, "typedef void * GLeglImageOES;\n"); fprintf(f, "typedef char GLchar;\n"); fprintf(f, "typedef char GLcharARB;\n"); fprintf(f, "#ifdef __APPLE__\n"); fprintf(f, " typedef void * GLhandleARB;\n"); fprintf(f, "#else\n"); fprintf(f, " typedef unsigned GLhandleARB;\n"); fprintf(f, "#endif\n"); fprintf(f, "typedef unsigned short GLhalf;\n"); fprintf(f, "typedef unsigned short GLhalfARB;\n"); fprintf(f, "typedef int GLfixed;\n"); fprintf(f, "typedef intptr_t GLintptr;\n"); fprintf(f, "typedef intptr_t GLintptrARB;\n"); fprintf(f, "typedef intptr_t GLsizeiptr;\n"); fprintf(f, "typedef intptr_t GLsizeiptrARB;\n"); fprintf(f, "typedef long long GLint64;\n"); fprintf(f, "typedef long long GLint64EXT;\n"); fprintf(f, "typedef unsigned long long GLuint64;\n"); fprintf(f, "typedef unsigned long long GLuint64EXT;\n"); fprintf(f, "typedef unsigned short GLhalfNV;\n"); fprintf(f, "typedef intptr_t GLvdpauSurfaceNV;\n"); fprintf(f, "typedef struct __GLsync * GLsync;\n"); fprintf(f, "struct _cl_context;\n"); fprintf(f, "struct _cl_event;\n"); fprintf(f, "typedef void (GL_API_ENTRY *GLDEBUGPROC)(GLenum " "source,GLenum type,GLuint id,GLenum severity,GLsizei " "length,const GLchar *message,const void *userParam);\n"); fprintf(f, "typedef void (GL_API_ENTRY *GLDEBUGPROCARB)(GLenum " "source,GLenum type,GLuint id,GLenum severity,GLsizei " "length,const GLchar *message,const void *userParam);\n"); fprintf(f, "typedef void (GL_API_ENTRY *GLDEBUGPROCKHR)(GLenum " "source,GLenum type,GLuint id,GLenum severity,GLsizei " "length,const GLchar *message,const void *userParam);\n"); fprintf(f, "typedef void (GL_API_ENTRY *GLDEBUGPROCAMD)(GLuint " "id,GLenum category,GLenum severity,GLsizei " "length,const GLchar *message,void *userParam);\n"); fprintf(f, "typedef void (GL_API_ENTRY *GLVULKANPROCNV)(void);\n"); for (i64 i = 0; i < enums.size; i++) fprintf(f, "#define %-80s %s\n", BS(enums.values[i].name), BS(enums.values[i].value)); for (i64 i = 0; i < procs.size; i++) { fprintf(f, "typedef %-20s(* proc_%-56s)(", BS(procs.values[i].return_type), BS(procs.values[i].name)); for (i64 j = 0; j < procs.values[i].arguments.size; j++) { if (j > 0) fprintf(f, ", "); fprintf(f, "%s", BS(procs.values[i].arguments.values[j])); } fprintf(f, ");\n"); } for (i64 i = 0; i < procs.size; i++) fprintf(f, "extern proc_%-56s %s;\n", BS(procs.values[i].name), BS(procs.values[i].name)); fprintf(f, "int gl_load_procs(void);\n"); fclose(f); f = fopen("kit_gl.inl.h", "wb"); fprintf(f, "#include \"kit_gl.h\"\n"); for (i64 i = 0; i < procs.size; i++) fprintf(f, "proc_%-56s %-56s= NULL;\n", BS(procs.values[i].name), BS(procs.values[i].name)); fprintf(f, "#ifndef GL_LOAD_PROC\n"); fprintf(f, "# define GL_LOAD_PROC(name_)\n"); fprintf(f, "#endif\n"); fprintf(f, "int gl_load_procs(void) {\n"); for (i64 i = 0; i < features.size; i++) { fprintf(f, "#ifdef %s\n", BS(features.values[i].name)); for (i64 j = 0; j < features.values[i].procs.size; j++) fprintf(f, " GL_LOAD_PROC(%s);\n", BS(features.values[i].procs.values[j])); fprintf(f, "#endif\n"); } fprintf(f, " return 1;\n"); fprintf(f, "}\n"); fclose(f); DESTROY_ALL_; xml_destroy(&res.xml); return 0; }