int main_run(void* main_arg) { int result = RENDERCOMPILE_RESULT_OK; rendercompile_input_t input = rendercompile_parse_command_line(environment_command_line()); FOUNDATION_UNUSED(main_arg); if (input.display_help) { rendercompile_print_usage(); goto exit; } resource_source_set_path(STRING_ARGS(input.source_path)); resource_compile_register(render_compile); size_t ifile, fsize; for (ifile = 0, fsize = array_size(input.input_files); ifile < fsize; ++ifile) { uuid_t uuid = string_to_uuid(STRING_ARGS(input.input_files[ifile])); if (uuid_is_null(uuid)) { char buffer[BUILD_MAX_PATHLEN]; string_t pathstr = string_copy(buffer, sizeof(buffer), STRING_ARGS(input.input_files[ifile])); pathstr = path_clean(STRING_ARGS(pathstr), sizeof(buffer)); pathstr = path_absolute(STRING_ARGS(pathstr), sizeof(buffer)); uuid = resource_import_map_lookup(STRING_ARGS(pathstr)); } if (uuid_is_null(uuid)) { log_warnf(HASH_RESOURCE, WARNING_INVALID_VALUE, STRING_CONST("Failed to lookup: %.*s"), STRING_FORMAT(input.input_files[ifile])); result = RENDERCOMPILE_RESULT_INVALID_INPUT; break; } if (resource_compile(uuid, RESOURCE_PLATFORM_ALL)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Successfully compiled: %.*s (%.*s)"), STRING_FORMAT(uuidstr), STRING_FORMAT(input.input_files[ifile])); } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_UNSUPPORTED, STRING_CONST("Failed to compile: %.*s (%.*s)"), STRING_FORMAT(uuidstr), STRING_FORMAT(input.input_files[ifile])); } } exit: array_deallocate(input.input_files); return result; }
DECLARE_TEST( uuid, string ) { uuid_t uuid, uuidref; char* str; uuidref = uuid_generate_random(); EXPECT_FALSE( uuid_is_null( uuidref ) ); str = string_from_uuid( uuidref ); EXPECT_NE( str, 0 ); uuid = string_to_uuid( str ); EXPECT_FALSE( uuid_is_null( uuid ) ); EXPECT_TRUE( uuid_equal( uuid, uuidref ) ); string_deallocate( str ); uuid = string_to_uuid( "" ); EXPECT_EQ_MSGFORMAT( uuid_is_null( uuid ), true, "empty string did not convert to null uuid: %s", string_from_uuid_static( uuid ) ); uuid = string_to_uuid( "0" ); EXPECT_EQ_MSGFORMAT( uuid_is_null( uuid ), true, "\"0\" string did not convert to null uuid: %s", string_from_uuid_static( uuid ) ); uuid = string_to_uuid( string_from_uuid_static( uuid_null() ) ); EXPECT_EQ_MSGFORMAT( uuid_is_null( uuid ), true, "null uuid reconvert through string did not convert to null uuid: %s", string_from_uuid_static( uuid ) ); return 0; }
static int render_import_glsl_shader(stream_t* stream, const uuid_t uuid, const char* type, size_t type_length) { resource_source_t source; void* blob = 0; size_t size; size_t read; size_t begin; size_t next; hash_t checksum; tick_t timestamp; uint64_t platform; size_t maxtokens; size_t parameter; string_const_t* token = 0; string_const_t valstr; char buffer[128]; int ret = 0; resource_platform_t platformdecl = { -1, -1, RENDERAPIGROUP_OPENGL, -1, -1, -1}; resource_source_initialize(&source); resource_source_read(&source, uuid); read = 0; size = stream_size(stream); blob = memory_allocate(HASH_RESOURCE, size, 0, MEMORY_PERSISTENT); size = stream_read(stream, blob, size); platform = resource_platform(platformdecl); timestamp = time_system(); checksum = hash(blob, size); if (resource_source_write_blob(uuid, timestamp, HASH_SOURCE, platform, checksum, blob, size)) { resource_source_set_blob(&source, timestamp, HASH_SOURCE, platform, checksum, size); } else { ret = -1; goto finalize; } //Parse source and set parameters maxtokens = 256; token = memory_allocate(HASH_RESOURCE, sizeof(string_const_t) * maxtokens, 0, MEMORY_PERSISTENT); begin = 0; parameter = 0; do { string_const_t tokens[64], line; size_t itok, ntokens; next = string_find_first_of(blob, size, STRING_CONST("\n\r"), begin); line = string_substr(blob, size, begin, next - begin); ntokens = string_explode(STRING_ARGS(line), STRING_CONST(GLSL_TOKEN_DELIM), tokens, maxtokens, false); for (itok = 0; itok < ntokens; ++itok) { if ((string_equal(STRING_ARGS(tokens[itok]), STRING_CONST("attribute")) || string_equal(STRING_ARGS(tokens[itok]), STRING_CONST("uniform"))) && (itok + 2 < ntokens)) { char typebuf[16], dimbuf[16]; string_const_t typestr = tokens[itok + 1]; string_const_t namestr = tokens[itok + 2]; string_const_t dimstr; int parameter_type = glsl_type_to_parameter_type(typestr); if (parameter_type < 0) continue; int parameter_dim = glsl_dim_from_token(typestr); if (parameter_dim == 1) parameter_dim = glsl_dim_from_token(namestr); namestr = glsl_name_from_token(namestr); typestr = string_to_const(string_from_uint(typebuf, sizeof(typebuf), (unsigned int)parameter_type, false, 0, 0)); dimstr = string_to_const(string_from_uint(dimbuf, sizeof(dimbuf), (unsigned int)parameter_dim, false, 0, 0)); log_debugf(HASH_RESOURCE, STRING_CONST("parameter: %.*s type %.*s dim %.*s"), STRING_FORMAT(namestr), STRING_FORMAT(typestr), STRING_FORMAT(dimstr)); string_t param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_type_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(typestr)); param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_name_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(namestr)); param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_dim_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(dimstr)); ++parameter; } } begin = string_find_first_not_of(blob, size, STRING_CONST(STRING_WHITESPACE), next); } while (next != STRING_NPOS); valstr = string_from_uint_static(parameter, false, 0, 0); resource_source_set(&source, timestamp, HASH_PARAMETER_COUNT, platform, STRING_ARGS(valstr)); resource_source_set(&source, timestamp, HASH_RESOURCE_TYPE, 0, type, type_length); if (!resource_source_write(&source, uuid, false)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Failed writing imported GLSL shader: %.*s"), STRING_FORMAT(uuidstr)); ret = -1; goto finalize; } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Wrote imported GLSL shader: %.*s"), STRING_FORMAT(uuidstr)); } finalize: memory_deallocate(blob); memory_deallocate(token); resource_source_finalize(&source); return ret; }
static int render_import_program(stream_t* stream, const uuid_t uuid) { char buffer[1024]; char pathbuf[BUILD_MAX_PATHLEN]; resource_source_t source; resource_platform_t platformdecl = {-1, -1, -1, -1, -1, -1}; uint64_t platform; tick_t timestamp; int ret = 0; resource_source_initialize(&source); resource_source_read(&source, uuid); platform = resource_platform(platformdecl); timestamp = time_system(); while (!stream_eos(stream)) { string_const_t type, ref; string_const_t fullpath; string_const_t uuidstr; uuid_t shaderuuid; hash_t typehash; string_t line = stream_read_line_buffer(stream, buffer, sizeof(buffer), '\n'); string_split(STRING_ARGS(line), STRING_CONST(" \t"), &type, &ref, false); type = string_strip(STRING_ARGS(type), STRING_CONST(STRING_WHITESPACE)); ref = string_strip(STRING_ARGS(ref), STRING_CONST(STRING_WHITESPACE)); if (!type.length || !ref.length) continue; typehash = hash(STRING_ARGS(type)); if ((typehash != HASH_VERTEXSHADER) && (typehash != HASH_PIXELSHADER)) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Ignore invalid line: %.*s"), STRING_FORMAT(line)); continue; } shaderuuid = string_to_uuid(STRING_ARGS(ref)); if (uuid_is_null(shaderuuid)) { if (path_is_absolute(STRING_ARGS(ref))) { fullpath = ref; } else { string_t full; string_const_t path = stream_path(stream); path = path_directory_name(STRING_ARGS(path)); full = path_concat(pathbuf, sizeof(pathbuf), STRING_ARGS(path), STRING_ARGS(ref)); full = path_absolute(STRING_ARGS(full), sizeof(pathbuf)); fullpath = string_const(STRING_ARGS(full)); } resource_signature_t sig = resource_import_lookup(STRING_ARGS(fullpath)); if (uuid_is_null(sig.uuid)) { if (!resource_import(STRING_ARGS(fullpath), uuid_null())) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Unable to import linked shader: %.*s"), STRING_FORMAT(fullpath)); ret = -1; goto finalize; } sig = resource_import_lookup(STRING_ARGS(fullpath)); if (uuid_is_null(sig.uuid)) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Import linked shader gave no UUID: %.*s"), STRING_FORMAT(fullpath)); ret = -1; goto finalize; } } shaderuuid = sig.uuid; } if (!uuid_is_null(shaderuuid)) { uuidstr = string_from_uuid_static(shaderuuid); resource_source_set(&source, timestamp, typehash, platform, STRING_ARGS(uuidstr)); } } resource_source_set(&source, timestamp, HASH_RESOURCE_TYPE, 0, STRING_CONST("program")); if (!resource_source_write(&source, uuid, false)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Failed writing imported program: %.*s"), STRING_FORMAT(uuidstr)); ret = -1; goto finalize; } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Wrote imported program: %.*s"), STRING_FORMAT(uuidstr)); } finalize: resource_source_finalize(&source); return ret; }
static int render_import_shader(stream_t* stream, const uuid_t uuid) { char buffer[1024]; char pathbuf[BUILD_MAX_PATHLEN]; resource_source_t source; resource_platform_t platformdecl = {-1, -1, -1, -1, -1, -1}; uint64_t platform; tick_t timestamp; int ret = 0; resource_source_initialize(&source); resource_source_read(&source, uuid); platform = resource_platform(platformdecl); timestamp = time_system(); while (!stream_eos(stream)) { uuid_t shaderuuid; string_const_t target, ref, fullpath; string_t line = stream_read_line_buffer(stream, buffer, sizeof(buffer), '\n'); string_split(STRING_ARGS(line), STRING_CONST(" \t"), &target, &ref, false); ref = string_strip(STRING_ARGS(ref), STRING_CONST(STRING_WHITESPACE)); shaderuuid = string_to_uuid(STRING_ARGS(ref)); if (uuid_is_null(shaderuuid)) { if (path_is_absolute(STRING_ARGS(ref))) { fullpath = ref; } else { string_t full; string_const_t path = stream_path(stream); path = path_directory_name(STRING_ARGS(path)); full = path_concat(pathbuf, sizeof(pathbuf), STRING_ARGS(path), STRING_ARGS(ref)); full = path_absolute(STRING_ARGS(full), sizeof(pathbuf)); fullpath = string_const(STRING_ARGS(full)); } resource_signature_t sig = resource_import_lookup(STRING_ARGS(fullpath)); if (uuid_is_null(sig.uuid)) { if (!resource_import(STRING_ARGS(fullpath), uuid_null())) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Unable to import linked shader: %.*s"), STRING_FORMAT(fullpath)); ret = -1; goto finalize; } sig = resource_import_lookup(STRING_ARGS(fullpath)); if (uuid_is_null(sig.uuid)) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Import linked shader gave no UUID: %.*s"), STRING_FORMAT(fullpath)); ret = -1; goto finalize; } } shaderuuid = sig.uuid; } if (!uuid_is_null(shaderuuid)) { resource_platform_t targetplatformdecl = render_import_parse_target(STRING_ARGS(target), platformdecl); uint64_t targetplatform = resource_platform(targetplatformdecl); if (resource_autoimport_need_update(shaderuuid, targetplatform)) resource_autoimport(shaderuuid); const string_const_t uuidstr = string_from_uuid_static(shaderuuid); resource_source_set(&source, timestamp, HASH_SHADER, targetplatform, STRING_ARGS(uuidstr)); resource_source_set_dependencies(uuid, targetplatform, &shaderuuid, 1); } } resource_source_set(&source, timestamp, HASH_RESOURCE_TYPE, 0, STRING_CONST("shader")); if (!resource_source_write(&source, uuid, false)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Failed writing imported shader: %.*s"), STRING_FORMAT(uuidstr)); ret = -1; goto finalize; } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Wrote imported shader: %.*s"), STRING_FORMAT(uuidstr)); } finalize: resource_source_finalize(&source); return 0; }
int lua_module_reload(lua_t* lua, const uuid_t uuid) { int ret = -1; lua_State* state = lua_state(lua); lua_modulemap_entry_t* entry = lua_module_registry_lookup(state, uuid); if (entry) { int stacksize = lua_gettop(state); lua_module_t module = lua_module_load_resource(uuid); if (module.size) { string_const_t uuidstr = string_from_uuid_static(uuid); log_debugf(HASH_LUA, STRING_CONST("Reloading module: %.*s"), STRING_FORMAT(uuidstr)); if (lua_module_upload(state, module.bytecode, module.size) == 0) { //Check if module loaded as a table if (lua_istable(state, -1)) { lua_pushlstring(state, STRING_CONST(BUILD_REGISTRY_LOADED_MODULES)); lua_gettable(state, LUA_REGISTRYINDEX); //Get and replace the old loaded module table lua_pushlightuserdata(state, entry); lua_gettable(state, -2); lua_replace(state, -2); //Get rid of loaded-modules registry table from stack if (lua_istable(state, -1)) { lua_pushlstring(state, STRING_CONST("__modulename")); lua_gettable(state, -2); size_t name_length = 0; const char* modulename = lua_isnil(state, -1) ? nullptr : luaL_checklstring(state, -1, &name_length); log_debugf(HASH_LUA, STRING_CONST("Replacing module table: %.*s (%.*s)"), STRING_FORMAT(uuidstr), (int)name_length, modulename); lua_pop(state, 1); //Get rid of name from stack //Clear previous loaded-modules table lua_pushnil(state); while (lua_next(state, -2) != 0) { lua_pop(state, 1); //Old value lua_pushnil(state); //Replace with nil lua_settable(state, -3); //Erase in previous loaded-modules table lua_pushnil(state); //Restart lua_next } //Copy new module table to previous loaded-modules table lua_pushnil(state); while (lua_next(state, -3) != 0) { //Lookup in new module table lua_pushvalue(state, -2); //Copy key lua_pushvalue(state, -2); //Copy value lua_settable(state, -5); //Set in previous loaded-modules table lua_pop(state, 1); //Pop value, leaving key for lua_next iteration } lua_pushlstring(state, STRING_CONST("__modulename")); lua_pushlstring(state, modulename, name_length); lua_settable(state, -3); } } ret = 0; } else { uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_LUA, WARNING_RESOURCE, STRING_CONST("Unable to reload module '%.*s'"), STRING_FORMAT(uuidstr)); } } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_LUA, WARNING_RESOURCE, STRING_CONST("Unable to load module '%.*s'"), STRING_FORMAT(uuidstr)); } lua_settop(state, stacksize); } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_debugf(HASH_LUA, STRING_CONST("Reloading module ignored, not loaded: %.*s"), STRING_FORMAT(uuidstr)); } return ret; }
static int lua_module_upload(lua_State* state, const void* bytecode, size_t size) { string_const_t errmsg = {0, 0}; lua_readbuffer_t read_buffer = { .buffer = bytecode, .size = size, .offset = 0 }; log_debugf(HASH_LUA, STRING_CONST("Loading %u bytes of module bytecode"), read_buffer.size); if (lua_load(state, lua_read_buffer, &read_buffer, "module") != 0) { errmsg.str = lua_tolstring(state, -1, &errmsg.length); log_errorf(HASH_LUA, ERROR_INTERNAL_FAILURE, STRING_CONST("Lua load failed (module): %.*s"), STRING_FORMAT(errmsg)); lua_pop(state, 1); return -1; } if (lua_pcall(state, 0, 1, 0) != 0) { errmsg.str = lua_tolstring(state, -1, &errmsg.length); log_errorf(HASH_LUA, ERROR_INTERNAL_FAILURE, STRING_CONST("Lua pcall failed (module): %.*s"), STRING_FORMAT(errmsg)); return -1; } return 0; } int lua_module_loader(lua_State* state) { lua_modulemap_entry_t* entry = lua_touserdata(state, lua_upvalueindex(2)); if (!entry) return 0; size_t name_length = 0; const char* name = luaL_checklstring(state, lua_upvalueindex(1), &name_length); int stacksize = lua_gettop(state); if (entry->preload) entry->preload(); lua_module_t module = lua_module_load_resource(entry->uuid); if (module.size) { if (lua_module_upload(state, module.bytecode, module.size) == 0) { lua_module_registry_add(state, entry); if (lua_istable(state, -1)) { /* Modules are never garbage collected anyway, since lib_package keeps a global registry reference in _LOADED table, and we keep a reference in loaded-modules table for reload functionality lua_pushlstring(state, STRING_CONST("__gcproxy")); //Proxy table index lua_newuserdata(state, 8); //New proxy object lua_newtable(state); //New metatable for proxy lua_pushlstring(state, STRING_CONST("__gc")); lua_pushlightuserdata(state, entry); lua_pushcclosure(state, lua_module_gc, 1); lua_rawset(state, -3); //Set __gc metatable method lua_setmetatable(state, -2); //Set metatable on proxy lua_rawset(state, -3); //Set proxy in table index __gcproxy */ lua_pushlstring(state, STRING_CONST(BUILD_REGISTRY_LOADED_MODULES)); lua_gettable(state, LUA_REGISTRYINDEX); lua_pushlightuserdata(state, entry); //entry is key lua_pushvalue(state, -3); //copy the loaded module table as value lua_pushlstring(state, STRING_CONST("__modulename")); lua_pushlstring(state, name, name_length); lua_settable(state, -3); //set moduletable["__modulename"] = name lua_settable(state, -3); //set table loaded-modules[entry] = moduletable lua_pop(state, 1); } } memory_deallocate(module.bytecode); } else { string_const_t uuidstr = string_from_uuid_static(entry->uuid); lua_pushfstring(state, "unable to load module '%s'", uuidstr.str); lua_error(state); } return lua_gettop(state) - stacksize; }