diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c54610a..0c35e6f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,13 +320,13 @@ set(LOVR_SRC src/main.c src/core/arr.c src/core/maf.c + src/core/map.c src/core/platform.c src/core/ref.c src/core/utf.c src/core/util.c src/api/api.c src/api/l_lovr.c - src/lib/map/map.c ) if (LOVR_BUILD_SHARED) diff --git a/src/api/api.h b/src/api/api.h index 430ccba8..d87c6bc2 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -2,6 +2,8 @@ #include #include #include +#include +#include "core/hash.h" #include "util.h" #pragma once @@ -97,27 +99,19 @@ extern const char* WrapModes[]; struct Color; typedef struct { - uint32_t hash; + uint64_t hash; void* object; } Proxy; -static inline uint32_t hash(const char* str) { - uint32_t x = 0; - while (*str) { - x = (x * 65599) + *str++; - } - return x; -} - #ifndef LUA_RIDX_MAINTHERAD #define LUA_RIDX_MAINTHREAD 1 #endif #define luax_len(L, i) (int) lua_objlen(L, i) #define luax_registertype(L, T) _luax_registertype(L, #T, lovr ## T, lovr ## T ## Destroy) -#define luax_totype(L, i, T) (T*) _luax_totype(L, i, hash(#T)) -#define luax_checktype(L, i, T) (T*) _luax_checktype(L, i, hash(#T), #T) -#define luax_pushtype(L, T, o) _luax_pushtype(L, #T, hash(#T), o) +#define luax_totype(L, i, T) (T*) _luax_totype(L, i, hash64(#T, strlen(#T))) +#define luax_checktype(L, i, T) (T*) _luax_checktype(L, i, hash64(#T, strlen(#T)), #T) +#define luax_pushtype(L, T, o) _luax_pushtype(L, #T, hash64(#T, strlen(#T)), o) #define luax_checkfloat(L, i) (float) luaL_checknumber(L, i) #define luax_optfloat(L, i, x) (float) luaL_optnumber(L, i, x) #define luax_geterror(L) lua_getfield(L, LUA_REGISTRYINDEX, "_lovrerror") diff --git a/src/api/l_collider.c b/src/api/l_collider.c index 9513a081..01d27849 100644 --- a/src/api/l_collider.c +++ b/src/api/l_collider.c @@ -495,7 +495,7 @@ static int l_lovrColliderSetTag(lua_State* L) { } const char* tag = luaL_checkstring(L, 2); - if (lovrColliderSetTag(collider, tag)) { + if (!lovrColliderSetTag(collider, tag)) { return luaL_error(L, "Invalid tag %s", tag); } diff --git a/src/api/l_event.c b/src/api/l_event.c index c2b9eac6..a0d2355a 100644 --- a/src/api/l_event.c +++ b/src/api/l_event.c @@ -76,7 +76,7 @@ int luax_pushvariant(lua_State* L, Variant* variant) { case TYPE_BOOLEAN: lua_pushboolean(L, variant->value.boolean); return 1; case TYPE_NUMBER: lua_pushnumber(L, variant->value.number); return 1; case TYPE_STRING: lua_pushstring(L, variant->value.string); return 1; - case TYPE_OBJECT: _luax_pushtype(L, variant->value.object.type, hash(variant->value.object.type), variant->value.object.pointer); return 1; + case TYPE_OBJECT: _luax_pushtype(L, variant->value.object.type, hash64(variant->value.object.type, strlen(variant->value.object.type)), variant->value.object.pointer); return 1; default: return 0; } } diff --git a/src/api/l_font.c b/src/api/l_font.c index 893a74cd..278f22b8 100644 --- a/src/api/l_font.c +++ b/src/api/l_font.c @@ -1,5 +1,6 @@ #include "api.h" #include "graphics/font.h" +#include "data/rasterizer.h" static int l_lovrFontGetWidth(lua_State* L) { Font* font = luax_checktype(L, 1, Font); @@ -7,9 +8,10 @@ static int l_lovrFontGetWidth(lua_State* L) { const char* string = luaL_checklstring(L, 2, &length); float wrap = luax_optfloat(L, 3, 0.f); float width; + float height; uint32_t lineCount; uint32_t glyphCount; - lovrFontMeasure(font, string, length, wrap, &width, &lineCount, &glyphCount); + lovrFontMeasure(font, string, length, wrap, &width, &height, &lineCount, &glyphCount); lua_pushnumber(L, width); lua_pushnumber(L, lineCount + 1); return 2; diff --git a/src/api/l_joints.c b/src/api/l_joints.c index e4ab0dc2..2c31ce43 100644 --- a/src/api/l_joints.c +++ b/src/api/l_joints.c @@ -12,11 +12,24 @@ void luax_pushjoint(lua_State* L, Joint* joint) { Joint* luax_checkjoint(lua_State* L, int index) { Proxy* p = lua_touserdata(L, index); - if (!p || (p->hash != hash("BallJoint") && p->hash != hash("DistanceJoint") && p->hash != hash("HingeJoint") && p->hash != hash("SliderJoint"))) { - luaL_typerror(L, index, "Joint"); - return NULL; + + if (p) { + const uint64_t hashes[] = { + hash64("BallJoint", strlen("BallJoint")), + hash64("DistanceJoint", strlen("DistanceJoint")), + hash64("HingeJoint", strlen("HingeJoint")), + hash64("SliderJoint", strlen("SliderJoint")) + }; + + for (size_t i = 0; i < sizeof(hashes) / sizeof(hashes[0]); i++) { + if (p->hash == hashes[i]) { + return p->object; + } + } } - return p->object; + + luaL_typerror(L, index, "Joint"); + return NULL; } static int l_lovrJointDestroy(lua_State* L) { diff --git a/src/api/l_model.c b/src/api/l_model.c index 292fead3..bdbef5fa 100644 --- a/src/api/l_model.c +++ b/src/api/l_model.c @@ -4,29 +4,15 @@ #include "data/modelData.h" #include "core/maf.h" -static int luax_reverseModelDataNameMap(lua_State *L, ModelData *modelData, int idx, map_u32_t *t, int count, const char *noun) { - lovrAssert(idx > 0 && idx <= count, "Model has no %s at index %d", idx, noun); - map_iter_t iter = map_iter(t); - const char* key; - while ((key = map_next(t, &iter)) != NULL) { - uint32_t iterIdx = *map_get(t, key); - if (iterIdx == (uint32_t) idx - 1) { // Map is 0 indexed - lua_pushstring(L, key); - return 1; - } - } - lua_pushnil(L); - return 1; -} - static uint32_t luax_checkanimation(lua_State* L, int index, Model* model) { switch (lua_type(L, index)) { case LUA_TSTRING: { - const char* name = lua_tostring(L, index); + size_t length; + const char* name = lua_tolstring(L, index, &length); ModelData* modelData = lovrModelGetModelData(model); - uint32_t* index = map_get(&modelData->animationMap, name); - lovrAssert(index, "Model has no animation named '%s'", name); - return *index; + uint64_t index = map_get(&modelData->animationMap, hash64(name, length)); + lovrAssert(index != MAP_NIL, "Model has no animation named '%s'", name); + return (uint32_t) index; } case LUA_TNUMBER: return lua_tointeger(L, index) - 1; default: return luaL_typerror(L, index, "number or string"); @@ -64,11 +50,12 @@ static int l_lovrModelPose(lua_State* L) { node = lua_tointeger(L, 2) - 1; break; case LUA_TSTRING: { - const char* name = lua_tostring(L, 2); + size_t length; + const char* name = lua_tolstring(L, 2, &length); ModelData* modelData = lovrModelGetModelData(model); - uint32_t* index = map_get(&modelData->nodeMap, name); - lovrAssert(index, "Model has no node named '%s'", name); - node = *index; + uint64_t index = map_get(&modelData->nodeMap, hash64(name, length)); + lovrAssert(index != MAP_NIL, "Model has no node named '%s'", name); + node = (uint32_t) index; break; } default: @@ -93,11 +80,12 @@ static int l_lovrModelGetMaterial(lua_State* L) { material = lua_tointeger(L, 2) - 1; break; case LUA_TSTRING: { - const char* name = lua_tostring(L, 2); + size_t length; + const char* name = lua_tolstring(L, 2, &length); ModelData* modelData = lovrModelGetModelData(model); - uint32_t* index = map_get(&modelData->materialMap, name); - lovrAssert(index, "Model has no material named '%s'", name); - material = *index; + uint64_t index = map_get(&modelData->materialMap, hash64(name, length)); + lovrAssert(index != MAP_NIL, "Model has no material named '%s'", name); + material = (uint32_t) index; break; } default: @@ -126,11 +114,12 @@ static int l_lovrModelGetNodePose(lua_State* L) { node = lua_tointeger(L, 2) - 1; break; case LUA_TSTRING: { - const char* name = lua_tostring(L, 2); + size_t length; + const char* name = lua_tolstring(L, 2, &length); ModelData* modelData = lovrModelGetModelData(model); - uint32_t* index = map_get(&modelData->nodeMap, name); - lovrAssert(index, "Model has no node named '%s'", name); - node = *index; + uint64_t index = map_get(&modelData->nodeMap, hash64(name, length)); + lovrAssert(index != MAP_NIL, "Model has no node named '%s'", name); + node = (uint32_t) index; break; } default: @@ -153,23 +142,29 @@ static int l_lovrModelGetNodePose(lua_State* L) { static int l_lovrModelGetAnimationName(lua_State* L) { Model* model = luax_checktype(L, 1, Model); - int idx = luaL_checknumber(L, 2); + uint32_t index = luaL_checkinteger(L, 2); ModelData* modelData = lovrModelGetModelData(model); - return luax_reverseModelDataNameMap(L, modelData, idx, &modelData->animationMap, modelData->animationCount, "animation"); + lovrAssert(index > 0 && index <= modelData->animationCount, "Model has no animation at index %d", index); + lua_pushstring(L, modelData->animations[index - 1].name); + return 1; } static int l_lovrModelGetMaterialName(lua_State* L) { Model* model = luax_checktype(L, 1, Model); - int idx = luaL_checknumber(L, 2); + uint32_t index = luaL_checkinteger(L, 2); ModelData* modelData = lovrModelGetModelData(model); - return luax_reverseModelDataNameMap(L, modelData, idx, &modelData->materialMap, modelData->materialCount, "material"); + lovrAssert(index > 0 && index <= modelData->materialCount, "Model has no material at index %d", index); + lua_pushstring(L, modelData->materials[index - 1].name); + return 1; } static int l_lovrModelGetNodeName(lua_State* L) { Model* model = luax_checktype(L, 1, Model); - int idx = luaL_checknumber(L, 2); + uint32_t index = luaL_checkinteger(L, 2); ModelData* modelData = lovrModelGetModelData(model); - return luax_reverseModelDataNameMap(L, modelData, idx, &modelData->nodeMap, modelData->nodeCount, "node"); + lovrAssert(index > 0 && index <= modelData->nodeCount, "Model has no node at index %d", index); + lua_pushstring(L, modelData->nodes[index - 1].name); + return 1; } static int l_lovrModelGetAnimationCount(lua_State* L) { diff --git a/src/api/l_shaderBlock.c b/src/api/l_shaderBlock.c index c57cecb4..fe2f92be 100644 --- a/src/api/l_shaderBlock.c +++ b/src/api/l_shaderBlock.c @@ -2,6 +2,7 @@ #include "graphics/buffer.h" #include "graphics/shader.h" #include +#include static int l_lovrShaderBlockGetType(lua_State* L) { ShaderBlock* block = luax_checktype(L, 1, ShaderBlock); diff --git a/src/api/l_shapes.c b/src/api/l_shapes.c index f297d633..92d4071e 100644 --- a/src/api/l_shapes.c +++ b/src/api/l_shapes.c @@ -12,11 +12,24 @@ void luax_pushshape(lua_State* L, Shape* shape) { Shape* luax_checkshape(lua_State* L, int index) { Proxy* p = lua_touserdata(L, index); - if (!p || (p->hash != hash("SphereShape") && p->hash != hash("BoxShape") && p->hash != hash("CapsuleShape") && p->hash != hash("CylinderShape"))) { - luaL_typerror(L, index, "Shape"); - return NULL; + + if (p) { + const uint64_t hashes[] = { + hash64("SphereShape", strlen("SphereShape")), + hash64("BoxShape", strlen("BoxShape")), + hash64("CapsuleShape", strlen("CapsuleShape")), + hash64("CylinderShape", strlen("CylinderShape")) + }; + + for (size_t i = 0; i < sizeof(hashes) / sizeof(hashes[0]); i++) { + if (p->hash == hashes[i]) { + return p->object; + } + } } - return p->object; + + luaL_typerror(L, index, "Shape"); + return NULL; } static int l_lovrShapeDestroy(lua_State* L) { diff --git a/src/core/arr.h b/src/core/arr.h index a366970a..64158694 100644 --- a/src/core/arr.h +++ b/src/core/arr.h @@ -3,7 +3,7 @@ #pragma once #define arr_t(T)\ - struct { T *data; size_t length, capacity; } + struct { T* data; size_t length, capacity; } #define arr_init(a)\ (a)->data = NULL,\ diff --git a/src/core/hash.h b/src/core/hash.h new file mode 100644 index 00000000..8d92aef6 --- /dev/null +++ b/src/core/hash.h @@ -0,0 +1,12 @@ +#include "util.h" +#include + +// FNV1a +static LOVR_INLINE uint64_t hash64(const void* data, size_t length) { + const uint8_t* bytes = data; + uint64_t hash = 0xcbf29ce484222325; + for (size_t i = 0; i < length; i++) { + hash = (hash ^ bytes[i]) * 0x100000001b3; + } + return hash; +} diff --git a/src/core/map.c b/src/core/map.c new file mode 100644 index 00000000..32496ea3 --- /dev/null +++ b/src/core/map.c @@ -0,0 +1,100 @@ +#include "map.h" +#include "util.h" +#include +#include + +static uint32_t prevpo2(uint32_t x) { + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x - (x >> 1); +} + +static void map_rehash(map_t* map) { + map_t old = *map; + map->size <<= 1; + map->hashes = malloc(2 * map->size * sizeof(uint64_t)); + map->values = map->hashes + map->size; + lovrAssert(map->size && map->hashes, "Out of memory"); + memset(map->hashes, 0xff, 2 * map->size * sizeof(uint64_t)); + + if (old.hashes) { + uint64_t mask = map->size - 1; + for (uint32_t i = 0; i < old.size; i++) { + if (old.hashes[i] != MAP_NIL) { + uint64_t index = old.hashes[i] & mask; + while (map->hashes[index] != MAP_NIL) { + index = (index + 1) & mask; + } + map->hashes[index] = old.hashes[i]; + map->values[index] = old.values[i]; + } + } + free(old.hashes); + } +} + +static LOVR_INLINE uint64_t map_find(map_t* map, uint64_t hash) { + uint64_t mask = map->size - 1; + uint64_t h = hash & mask; + + while (map->hashes[h] != hash && map->hashes[h] != MAP_NIL) { + h = (h + 1) & mask; + } + + return h; +} + +void map_init(map_t* map, uint32_t n) { + map->size = prevpo2(n) + !n; + map->used = 0; + map->hashes = NULL; + map_rehash(map); +} + +void map_free(map_t* map) { + free(map->hashes); +} + +uint64_t map_get(map_t* map, uint64_t hash) { + return map->values[map_find(map, hash)]; +} + +void map_set(map_t* map, uint64_t hash, uint64_t value) { + if (map->used >= (map->size >> 1) + (map->size >> 2)) { + map_rehash(map); + } + + uint64_t h = map_find(map, hash); + map->used += map->hashes[h] == MAP_NIL; + map->hashes[h] = hash; + map->values[h] = value; +} + +void map_remove(map_t* map, uint64_t hash) { + uint64_t h = map_find(map, hash); + + if (map->hashes[h] == MAP_NIL) { + return; + } + + uint64_t mask = map->size - 1; + uint64_t i = h; + + do { + i = (i + 1) & mask; + uint64_t x = map->hashes[i] & mask; + // Removing a key from an open-addressed hash table is complicated + if ((i > h && (x <= h || x > i)) || (i < h && (x <= h && x > i))) { + map->hashes[h] = map->hashes[i]; + map->values[h] = map->values[i]; + h = i; + } + } while (map->hashes[i] != MAP_NIL); + + map->hashes[i] = MAP_NIL; + map->values[i] = MAP_NIL; + map->used--; +} diff --git a/src/core/map.h b/src/core/map.h new file mode 100644 index 00000000..3e604a93 --- /dev/null +++ b/src/core/map.h @@ -0,0 +1,18 @@ +#include + +#pragma once + +#define MAP_NIL UINT64_MAX + +typedef struct { + uint32_t size; + uint32_t used; + uint64_t* hashes; + uint64_t* values; +} map_t; + +void map_init(map_t* map, uint32_t n); +void map_free(map_t* map); +uint64_t map_get(map_t* map, uint64_t hash); +void map_set(map_t* map, uint64_t hash, uint64_t value); +void map_remove(map_t* map, uint64_t hash); diff --git a/src/lib/map/LICENSE b/src/lib/map/LICENSE deleted file mode 100644 index 03b65555..00000000 --- a/src/lib/map/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2014 rxi - - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/lib/map/map.c b/src/lib/map/map.c deleted file mode 100644 index c8d83a97..00000000 --- a/src/lib/map/map.c +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright (c) 2014 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include "map.h" - -struct map_node_t { - unsigned hash; - void *value; - map_node_t *next; - /* char key[]; */ - /* char value[]; */ -}; - - -static unsigned map_hash(const char *str) { - unsigned hash = 5381; - while (*str) { - hash = ((hash << 5) + hash) ^ *str++; - } - return hash; -} - - -static map_node_t *map_newnode(const char *key, void *value, int vsize) { - map_node_t *node; - int ksize = (int) strlen(key) + 1; - int voffset = ksize + ((sizeof(void*) - ksize) % sizeof(void*)); - node = malloc(sizeof(*node) + voffset + vsize); - if (!node) return NULL; - memcpy(node + 1, key, ksize); - node->hash = map_hash(key); - node->value = ((char*) (node + 1)) + voffset; - memcpy(node->value, value, vsize); - return node; -} - - -static int map_bucketidx(map_base_t *m, unsigned hash) { - /* If the implementation is changed to allow a non-power-of-2 bucket count, - * the line below should be changed to use mod instead of AND */ - return hash & (m->nbuckets - 1); -} - - -static void map_addnode(map_base_t *m, map_node_t *node) { - int n = map_bucketidx(m, node->hash); - node->next = m->buckets[n]; - m->buckets[n] = node; -} - - -static int map_resize(map_base_t *m, int nbuckets) { - map_node_t *nodes, *node, *next; - map_node_t **buckets; - int i; - /* Chain all nodes together */ - nodes = NULL; - i = m->nbuckets; - while (i--) { - node = (m->buckets)[i]; - while (node) { - next = node->next; - node->next = nodes; - nodes = node; - node = next; - } - } - /* Reset buckets */ - buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets); - if (buckets != NULL) { - m->buckets = buckets; - m->nbuckets = nbuckets; - } - if (m->buckets) { - memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets); - /* Re-add nodes to buckets */ - node = nodes; - while (node) { - next = node->next; - map_addnode(m, node); - node = next; - } - } - /* Return error code if realloc() failed */ - return (buckets == NULL) ? -1 : 0; -} - - -static map_node_t **map_getref(map_base_t *m, const char *key) { - unsigned hash = map_hash(key); - map_node_t **next; - if (m->nbuckets > 0) { - next = &m->buckets[map_bucketidx(m, hash)]; - while (*next) { - if ((*next)->hash == hash && !strcmp((char*) (*next + 1), key)) { - return next; - } - next = &(*next)->next; - } - } - return NULL; -} - - -void map_deinit_(map_base_t *m) { - map_node_t *next, *node; - int i; - i = m->nbuckets; - while (i--) { - node = m->buckets[i]; - while (node) { - next = node->next; - free(node); - node = next; - } - } - free(m->buckets); -} - - -void *map_get_(map_base_t *m, const char *key) { - map_node_t **next = map_getref(m, key); - return next ? (*next)->value : NULL; -} - - -int map_set_(map_base_t *m, const char *key, void *value, int vsize) { - int n, err; - map_node_t **next, *node; - /* Find & replace existing node */ - next = map_getref(m, key); - if (next) { - memcpy((*next)->value, value, vsize); - return 0; - } - /* Add new node */ - node = map_newnode(key, value, vsize); - if (node == NULL) goto fail; - if (m->nnodes >= m->nbuckets) { - n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1; - err = map_resize(m, n); - if (err) goto fail; - } - map_addnode(m, node); - m->nnodes++; - return 0; - fail: - if (node) free(node); - return -1; -} - - -void map_remove_(map_base_t *m, const char *key) { - map_node_t *node; - map_node_t **next = map_getref(m, key); - if (next) { - node = *next; - *next = (*next)->next; - free(node); - m->nnodes--; - } -} - - -map_iter_t map_iter_(void) { - map_iter_t iter; - iter.bucketidx = -1; - iter.node = NULL; - return iter; -} - - -const char *map_next_(map_base_t *m, map_iter_t *iter) { - if (iter->node) { - iter->node = iter->node->next; - if (iter->node == NULL) goto nextBucket; - } else { - nextBucket: - do { - if (++iter->bucketidx >= m->nbuckets) { - return NULL; - } - iter->node = m->buckets[iter->bucketidx]; - } while (iter->node == NULL); - } - return (char*) (iter->node + 1); -} diff --git a/src/lib/map/map.h b/src/lib/map/map.h deleted file mode 100644 index 71af7105..00000000 --- a/src/lib/map/map.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2014 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef MAP_H -#define MAP_H - -#include - -#define MAP_VERSION "0.1.0" - -struct map_node_t; -typedef struct map_node_t map_node_t; - -typedef struct { - map_node_t **buckets; - unsigned nbuckets, nnodes; -} map_base_t; - -typedef struct { - unsigned bucketidx; - map_node_t *node; -} map_iter_t; - - -#define map_t(T)\ - struct { map_base_t base; T *ref; T tmp; } - - -#define map_init(m)\ - memset(m, 0, sizeof(*(m))) - - -#define map_deinit(m)\ - map_deinit_(&(m)->base) - - -#define map_get(m, key)\ - ( (m)->ref = map_get_(&(m)->base, key) ) - - -#define map_set(m, key, value)\ - ( (m)->tmp = (value),\ - map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) ) - - -#define map_remove(m, key)\ - map_remove_(&(m)->base, key) - - -#define map_iter(m)\ - map_iter_() - - -#define map_next(m, iter)\ - map_next_(&(m)->base, iter) - - -void map_deinit_(map_base_t *m); -void *map_get_(map_base_t *m, const char *key); -int map_set_(map_base_t *m, const char *key, void *value, int vsize); -void map_remove_(map_base_t *m, const char *key); -map_iter_t map_iter_(void); -const char *map_next_(map_base_t *m, map_iter_t *iter); - - -typedef map_t(void*) map_void_t; -typedef map_t(char*) map_str_t; -typedef map_t(int) map_int_t; -typedef map_t(char) map_char_t; -typedef map_t(float) map_float_t; -typedef map_t(double) map_double_t; - -#endif diff --git a/src/modules/data/modelData.c b/src/modules/data/modelData.c index 9d798dd0..6b716a91 100644 --- a/src/modules/data/modelData.c +++ b/src/modules/data/modelData.c @@ -23,9 +23,9 @@ void lovrModelDataDestroy(void* ref) { for (uint32_t i = 0; i < model->textureCount; i++) { lovrRelease(TextureData, model->textures[i]); } - map_deinit(&model->animationMap); - map_deinit(&model->materialMap); - map_deinit(&model->nodeMap); + map_free(&model->animationMap); + map_free(&model->materialMap); + map_free(&model->nodeMap); free(model->data); } @@ -45,6 +45,7 @@ void lovrModelDataAllocate(ModelData* model) { totalSize += sizes[9] = model->channelCount * sizeof(ModelAnimationChannel); totalSize += sizes[10] = model->childCount * sizeof(uint32_t); totalSize += sizes[11] = model->jointCount * sizeof(uint32_t); + totalSize += sizes[12] = model->charCount * sizeof(char); size_t offset = 0; char* p = model->data = calloc(1, totalSize); @@ -61,8 +62,9 @@ void lovrModelDataAllocate(ModelData* model) { model->channels = (ModelAnimationChannel*) (p + offset), offset += sizes[9]; model->children = (uint32_t*) (p + offset), offset += sizes[10]; model->joints = (uint32_t*) (p + offset), offset += sizes[11]; + model->chars = (char*) (p + offset), offset += sizes[12]; - map_init(&model->animationMap); - map_init(&model->materialMap); - map_init(&model->nodeMap); + map_init(&model->animationMap, model->animationCount); + map_init(&model->materialMap, model->materialCount); + map_init(&model->nodeMap, model->nodeCount); } diff --git a/src/modules/data/modelData.h b/src/modules/data/modelData.h index b1bcd95d..c510efbc 100644 --- a/src/modules/data/modelData.h +++ b/src/modules/data/modelData.h @@ -1,5 +1,5 @@ #include "util.h" -#include "lib/map/map.h" +#include "core/map.h" #include #include #include @@ -133,12 +133,14 @@ typedef struct { } ModelAnimationChannel; typedef struct { + const char* name; ModelAnimationChannel* channels; uint32_t channelCount; float duration; } ModelAnimation; typedef struct { + const char* name; float scalars[MAX_MATERIAL_SCALARS]; Color colors[MAX_MATERIAL_COLORS]; uint32_t textures[MAX_MATERIAL_TEXTURES]; @@ -154,6 +156,7 @@ typedef struct { } ModelPrimitive; typedef struct { + const char* name; float transform[16]; float translation[4]; float rotation[4]; @@ -172,8 +175,6 @@ typedef struct { float* inverseBindMatrices; } ModelSkin; -typedef map_t(uint32_t) map_u32_t; - typedef struct ModelData { void* data; struct Blob** blobs; @@ -200,13 +201,15 @@ typedef struct ModelData { ModelAnimationChannel* channels; uint32_t* children; uint32_t* joints; + char* chars; uint32_t channelCount; uint32_t childCount; uint32_t jointCount; + uint32_t charCount; - map_u32_t animationMap; - map_u32_t materialMap; - map_u32_t nodeMap; + map_t animationMap; + map_t materialMap; + map_t nodeMap; } ModelData; ModelData* lovrModelDataInit(ModelData* model, struct Blob* blob); diff --git a/src/modules/data/modelData_gltf.c b/src/modules/data/modelData_gltf.c index 78b05b82..92e8441e 100644 --- a/src/modules/data/modelData_gltf.c +++ b/src/modules/data/modelData_gltf.c @@ -2,6 +2,7 @@ #include "data/blob.h" #include "data/textureData.h" #include "filesystem/filesystem.h" +#include "core/hash.h" #include "core/maf.h" #include "core/ref.h" #include "lib/jsmn/jsmn.h" @@ -128,22 +129,6 @@ static void* decodeBase64(char* str, size_t length, size_t decodedSize) { return data; } -// Kinda like count += sum(map(arr, obj => #obj[key])) -static jsmntok_t* aggregate(const char* json, jsmntok_t* token, const char* target, uint32_t* count) { - for (int i = (token++)->size; i > 0; i--) { - if (token->size > 0) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, target)) { - *count += token->size; - } - token += NOM_VALUE(json, token); - } - } - } - return token; -} - static jsmntok_t* resolveTexture(const char* json, jsmntok_t* token, ModelMaterial* material, MaterialTexture textureType, gltfTexture* textures, gltfSampler* samplers) { for (int k = (token++)->size; k > 0; k--) { gltfString key = NOM_STR(json, token); @@ -271,6 +256,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { gltfString key = NOM_STR(json, t); if (STR_EQ(key, "channels")) { model->channelCount += t->size; } else if (STR_EQ(key, "samplers")) { samplerCount += t->size; } + else if (STR_EQ(key, "name")) { model->charCount += t->end - t->start + 1; } t += NOM_VALUE(json, t); } } @@ -390,7 +376,13 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { } else if (STR_EQ(key, "materials")) { info.materials = token; model->materialCount = token->size; - token += NOM_VALUE(json, token); + for (int i = (token++)->size; i > 0; i--) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "name")) { model->charCount += token->end - token->start + 1; } + token += NOM_VALUE(json, token); + } + } } else if (STR_EQ(key, "meshes")) { info.meshes = token; @@ -414,7 +406,16 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { } else if (STR_EQ(key, "nodes")) { info.nodes = token; model->nodeCount = token->size; - token = aggregate(json, token, "children", &model->childCount); + for (int i = (token++)->size; i > 0; i--) { + if (token->size > 0) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "children")) { model->childCount += token->size; } + else if (STR_EQ(key, "name")) { model->charCount += token->end - token->start + 1; } + token += NOM_VALUE(json, token); + } + } + } } else if (STR_EQ(key, "scene")) { rootScene = NOM_INT(json, token); @@ -440,7 +441,13 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { } else if (STR_EQ(key, "skins")) { info.skins = token; model->skinCount = token->size; - token = aggregate(json, token, "joints", &model->jointCount); + for (int i = (token++)->size; i > 0; i--) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "joints")) { model->jointCount += token->size; } + token += NOM_VALUE(json, token); + } + } } else { token += NOM_VALUE(json, token); @@ -633,9 +640,10 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { token += NOM_VALUE(json, token); } else if (STR_EQ(key, "name")) { gltfString name = NOM_STR(json, token); - name.data[name.length] = '\0'; - map_set(&model->animationMap, name.data, model->animationCount - i); - name.data[name.length] = '"'; + map_set(&model->animationMap, hash64(name.data, name.length), model->animationCount - i); + memcpy(model->chars, name.data, name.length); + animation->name = model->chars; + model->chars += name.length + 1; } else { token += NOM_VALUE(json, token); } @@ -726,9 +734,10 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { material->colors[COLOR_EMISSIVE].b = NOM_FLOAT(json, token); } else if (STR_EQ(key, "name")) { gltfString name = NOM_STR(json, token); - name.data[name.length] = '\0'; - map_set(&model->materialMap, name.data, model->materialCount - i); - name.data[name.length] = '"'; + map_set(&model->materialMap, hash64(name.data, name.length), model->materialCount - i); + memcpy(model->chars, name.data, name.length); + material->name = model->chars; + model->chars += name.length + 1; } else { token += NOM_VALUE(json, token); } @@ -846,9 +855,10 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) { scale[2] = NOM_FLOAT(json, token); } else if (STR_EQ(key, "name")) { gltfString name = NOM_STR(json, token); - name.data[name.length] = '\0'; - map_set(&model->nodeMap, name.data, model->nodeCount - i); - name.data[name.length] = '"'; + map_set(&model->nodeMap, hash64(name.data, name.length), model->nodeCount - i); + memcpy(model->chars, name.data, name.length); + node->name = model->chars; + model->chars += name.length + 1; } else { token += NOM_VALUE(json, token); } diff --git a/src/modules/data/modelData_obj.c b/src/modules/data/modelData_obj.c index c749da11..6d801fa6 100644 --- a/src/modules/data/modelData_obj.c +++ b/src/modules/data/modelData_obj.c @@ -3,10 +3,11 @@ #include "data/textureData.h" #include "filesystem/filesystem.h" #include "core/arr.h" +#include "core/hash.h" #include "core/maf.h" +#include "core/map.h" #include "core/ref.h" #include "core/util.h" -#include "lib/map/map.h" #include #include #include @@ -24,7 +25,7 @@ typedef arr_t(objGroup) arr_group_t; #define STARTS_WITH(a, b) !strncmp(a, b, strlen(b)) -static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* materials, map_u32_t* names, char* base) { +static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* materials, map_t* names, char* base) { size_t length = 0; char* data = lovrFilesystemRead(path, -1, &length); lovrAssert(data && length > 0, "Unable to read mtl from '%s'", path); @@ -35,9 +36,9 @@ static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* ma if (STARTS_WITH(s, "newmtl ")) { char name[128]; - bool hasName = sscanf(s + 7, "%s\n%n", name, &lineLength); - lovrAssert(hasName, "Bad OBJ: Expected a material name"); - map_set(names, name, (uint32_t) materials->length); + size_t length = sscanf(s + 7, "%s\n%n", name, &lineLength); + lovrAssert(length > 0, "Bad OBJ: Expected a material name"); + map_set(names, hash64(name, length), materials->length); arr_push(materials, ((ModelMaterial) { .scalars[SCALAR_METALNESS] = 1.f, .scalars[SCALAR_ROUGHNESS] = 1.f, @@ -102,8 +103,8 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) { arr_material_t materials; arr_t(float) vertexBlob; arr_t(int) indexBlob; - map_u32_t materialMap; - map_int_t vertexMap; + map_t materialMap; + map_t vertexMap; arr_t(float) positions; arr_t(float) normals; arr_t(float) uvs; @@ -111,10 +112,10 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) { arr_init(&groups); arr_init(&textures); arr_init(&materials); - map_init(&materialMap); + map_init(&materialMap, 0); arr_init(&vertexBlob); arr_init(&indexBlob); - map_init(&vertexMap); + map_init(&vertexMap, 0); arr_init(&positions); arr_init(&normals); arr_init(&uvs); @@ -158,15 +159,15 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) { char terminator = i == 2 ? '\n' : ' '; char* space = strchr(s, terminator); if (space) { - *space = '\0'; // I'll be back - int* index = map_get(&vertexMap, s); - if (index) { - arr_push(&indexBlob, *index); + uint64_t hash = hash64(s, space - s); + uint64_t index = map_get(&vertexMap, hash); + if (index != MAP_NIL) { + arr_push(&indexBlob, index); } else { int v, vt, vn; - int newIndex = (int) vertexBlob.length / 8; + uint64_t newIndex = vertexBlob.length / 8; arr_push(&indexBlob, newIndex); - map_set(&vertexMap, s, newIndex); + map_set(&vertexMap, hash, newIndex); // Can be improved if (sscanf(s, "%d/%d/%d", &v, &vt, &vn) == 3) { @@ -188,7 +189,6 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) { lovrThrow("Bad OBJ: Unknown face format"); } } - *space = terminator; s = space + 1; } } @@ -203,21 +203,21 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) { parseMtl(path, &textures, &materials, &materialMap, base); } else if (STARTS_WITH(data, "usemtl ")) { char name[128]; - bool hasName = sscanf(data + 7, "%s\n%n", name, &lineLength); - uint32_t* material = map_get(&materialMap, name); - lovrAssert(hasName, "Bad OBJ: Expected a valid material name"); + uint64_t length = sscanf(data + 7, "%s\n%n", name, &lineLength); + uint64_t material = map_get(&materialMap, hash64(name, length)); + lovrAssert(length > 0, "Bad OBJ: Expected a valid material name"); // If the last group didn't have any faces, just reuse it, otherwise make a new group objGroup* group = &groups.data[groups.length - 1]; if (group->count > 0) { int start = group->start + group->count; // Don't put this in the compound literal (realloc) arr_push(&groups, ((objGroup) { - .material = material ? *material : -1, + .material = material == MAP_NIL ? -1 : material, .start = start, .count = 0 })); } else { - group->material = material ? *material : -1; + group->material = material == MAP_NIL ? -1 : material; } } else { char* newline = memchr(data, '\n', length); @@ -332,7 +332,7 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) { arr_free(&groups); arr_free(&textures); arr_free(&materials); - map_deinit(&vertexMap); + map_free(&vertexMap); arr_free(&positions); arr_free(&normals); arr_free(&uvs); diff --git a/src/modules/graphics/font.c b/src/modules/graphics/font.c index 15e0b415..e5c15b91 100644 --- a/src/modules/graphics/font.c +++ b/src/modules/graphics/font.c @@ -1,12 +1,37 @@ #include "graphics/font.h" #include "graphics/texture.h" +#include "data/rasterizer.h" #include "data/textureData.h" +#include "core/arr.h" +#include "core/hash.h" +#include "core/map.h" #include "core/ref.h" #include "core/utf.h" #include #include #include +typedef struct { + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint32_t rowHeight; + uint32_t padding; + arr_t(Glyph) glyphs; + map_t glyphMap; +} FontAtlas; + +struct Font { + Rasterizer* rasterizer; + Texture* texture; + FontAtlas atlas; + map_t kerning; + float lineHeight; + float pixelDensity; + bool flip; +}; + static float* lovrFontAlignLine(float* x, float* lineEnd, float width, HorizontalAlign halign) { while (x < lineEnd) { if (halign == ALIGN_CENTER) { @@ -21,12 +46,18 @@ static float* lovrFontAlignLine(float* x, float* lineEnd, float width, Horizonta return x; } -Font* lovrFontInit(Font* font, Rasterizer* rasterizer) { +static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint); +static void lovrFontAddGlyph(Font* font, Glyph* glyph); +static void lovrFontExpandTexture(Font* font); +static void lovrFontCreateTexture(Font* font); + +Font* lovrFontCreate(Rasterizer* rasterizer) { + Font* font = lovrAlloc(Font); lovrRetain(rasterizer); font->rasterizer = rasterizer; font->lineHeight = 1.f; font->pixelDensity = (float) font->rasterizer->height; - map_init(&font->kerning); + map_init(&font->kerning, 0); // Atlas uint32_t padding = 1; @@ -35,7 +66,8 @@ Font* lovrFontInit(Font* font, Rasterizer* rasterizer) { font->atlas.width = 128; font->atlas.height = 128; font->atlas.padding = padding; - map_init(&font->atlas.glyphs); + arr_init(&font->atlas.glyphs); + map_init(&font->atlas.glyphMap, 0); // Set initial atlas size while (font->atlas.height < 4 * rasterizer->size) { @@ -52,20 +84,21 @@ void lovrFontDestroy(void* ref) { Font* font = ref; lovrRelease(Rasterizer, font->rasterizer); lovrRelease(Texture, font->texture); - const char* key; - map_iter_t iter = map_iter(&font->atlas.glyphs); - while ((key = map_next(&font->atlas.glyphs, &iter)) != NULL) { - Glyph* glyph = map_get(&font->atlas.glyphs, key); - lovrRelease(TextureData, glyph->data); + for (size_t i = 0; i < font->atlas.glyphs.length; i++) { + lovrRelease(TextureData, font->atlas.glyphs.data[i].data); } - map_deinit(&font->atlas.glyphs); - map_deinit(&font->kerning); + map_free(&font->atlas.glyphMap); + map_free(&font->kerning); } Rasterizer* lovrFontGetRasterizer(Font* font) { return font->rasterizer; } +Texture* lovrFontGetTexture(Font* font) { + return font->texture; +} + void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, float* vertices, uint16_t* indices, uint16_t baseVertex) { FontAtlas* atlas = &font->atlas; bool flip = font->flip; @@ -154,7 +187,7 @@ void lovrFontRender(Font* font, const char* str, size_t length, float wrap, Hori lovrFontAlignLine(lineStart, vertexCursor, cx, halign); } -void lovrFontMeasure(Font* font, const char* str, size_t length, float wrap, float* width, uint32_t* lineCount, uint32_t* glyphCount) { +void lovrFontMeasure(Font* font, const char* str, size_t length, float wrap, float* width, float* height, uint32_t* lineCount, uint32_t* glyphCount) { float x = 0.f; const char* end = str + length; size_t bytes; @@ -195,6 +228,7 @@ void lovrFontMeasure(Font* font, const char* str, size_t length, float wrap, flo } *width = MAX(*width, x * scale); + *height = ((*lineCount + 1) * font->rasterizer->height * font->lineHeight) * (font->flip ? -1 : 1); } float lovrFontGetHeight(Font* font) { @@ -229,17 +263,16 @@ void lovrFontSetFlipEnabled(Font* font, bool flip) { font->flip = flip; } -int32_t lovrFontGetKerning(Font* font, unsigned int left, unsigned int right) { - char key[12]; - snprintf(key, 12, "%d,%d", left, right); +int32_t lovrFontGetKerning(Font* font, uint32_t left, uint32_t right) { + uint64_t key = ((uint64_t) left << 32) + right; + uint64_t hash = hash64(&key, sizeof(key)); // TODO improve number hashing + uint64_t kerning = map_get(&font->kerning, hash); - int* entry = map_get(&font->kerning, key); - if (entry) { - return *entry; + if (kerning == MAP_NIL) { + kerning = lovrRasterizerGetKerning(font->rasterizer, left, right); + map_set(&font->kerning, hash, kerning); } - int32_t kerning = lovrRasterizerGetKerning(font->rasterizer, left, right); - map_set(&font->kerning, key, kerning); return kerning; } @@ -255,27 +288,24 @@ void lovrFontSetPixelDensity(Font* font, float pixelDensity) { font->pixelDensity = pixelDensity; } -Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) { - char key[6]; - snprintf(key, 6, "%d", codepoint); - +static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) { FontAtlas* atlas = &font->atlas; - map_glyph_t* glyphs = &atlas->glyphs; - Glyph* glyph = map_get(glyphs, key); + uint64_t hash = hash64(&codepoint, sizeof(codepoint)); + uint64_t index = map_get(&atlas->glyphMap, hash); // Add the glyph to the atlas if it isn't there - if (!glyph) { - Glyph g; - lovrRasterizerLoadGlyph(font->rasterizer, codepoint, &g); - map_set(glyphs, key, g); - glyph = map_get(glyphs, key); - lovrFontAddGlyph(font, glyph); + if (index == MAP_NIL) { + index = atlas->glyphs.length; + arr_reserve(&atlas->glyphs, atlas->glyphs.length + 1); + lovrRasterizerLoadGlyph(font->rasterizer, codepoint, &atlas->glyphs.data[atlas->glyphs.length++]); + map_set(&atlas->glyphMap, hash, index); + lovrFontAddGlyph(font, &atlas->glyphs.data[index]); } - return glyph; + return &atlas->glyphs.data[index]; } -void lovrFontAddGlyph(Font* font, Glyph* glyph) { +static void lovrFontAddGlyph(Font* font, Glyph* glyph) { FontAtlas* atlas = &font->atlas; // Don't waste space on empty glyphs @@ -308,7 +338,7 @@ void lovrFontAddGlyph(Font* font, Glyph* glyph) { atlas->rowHeight = MAX(atlas->rowHeight, glyph->th); } -void lovrFontExpandTexture(Font* font) { +static void lovrFontExpandTexture(Font* font) { FontAtlas* atlas = &font->atlas; if (atlas->width == atlas->height) { @@ -330,17 +360,14 @@ void lovrFontExpandTexture(Font* font) { atlas->rowHeight = 0; // Re-pack all the glyphs - const char* key; - map_iter_t iter = map_iter(&atlas->glyphs); - while ((key = map_next(&atlas->glyphs, &iter)) != NULL) { - Glyph* glyph = map_get(&atlas->glyphs, key); - lovrFontAddGlyph(font, glyph); + for (size_t i = 0; i < atlas->glyphs.length; i++) { + lovrFontAddGlyph(font, &atlas->glyphs.data[i]); } } // TODO we only need the TextureData here to clear the texture, but it's a big waste of memory. // Could look into using glClearTexImage when supported to make this more efficient. -void lovrFontCreateTexture(Font* font) { +static void lovrFontCreateTexture(Font* font) { lovrRelease(Texture, font->texture); TextureData* textureData = lovrTextureDataCreate(font->atlas.width, font->atlas.height, 0x0, FORMAT_RGB); font->texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, false, false, 0); diff --git a/src/modules/graphics/font.h b/src/modules/graphics/font.h index e5c96ae4..41562a82 100644 --- a/src/modules/graphics/font.h +++ b/src/modules/graphics/font.h @@ -1,15 +1,12 @@ -#include "data/rasterizer.h" -#include "lib/map/map.h" #include #include +#include #pragma once struct Rasterizer; struct Texture; -typedef map_t(Glyph) map_glyph_t; - typedef enum { ALIGN_LEFT, ALIGN_CENTER, @@ -22,32 +19,13 @@ typedef enum { ALIGN_BOTTOM } VerticalAlign; -typedef struct { - uint32_t x; - uint32_t y; - uint32_t width; - uint32_t height; - uint32_t rowHeight; - uint32_t padding; - map_glyph_t glyphs; -} FontAtlas; - -typedef struct Font { - struct Rasterizer* rasterizer; - struct Texture* texture; - FontAtlas atlas; - map_int_t kerning; - float lineHeight; - float pixelDensity; - bool flip; -} Font; - -Font* lovrFontInit(Font* font, struct Rasterizer* rasterizer); -#define lovrFontCreate(...) lovrFontInit(lovrAlloc(Font), __VA_ARGS__) +typedef struct Font Font; +Font* lovrFontCreate(struct Rasterizer* rasterizer); void lovrFontDestroy(void* ref); struct Rasterizer* lovrFontGetRasterizer(Font* font); +struct Texture* lovrFontGetTexture(Font* font); void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, float* vertices, uint16_t* indices, uint16_t baseVertex); -void lovrFontMeasure(Font* font, const char* string, size_t length, float wrap, float* width, uint32_t* lineCount, uint32_t* glyphCount); +void lovrFontMeasure(Font* font, const char* string, size_t length, float wrap, float* width, float* height, uint32_t* lineCount, uint32_t* glyphCount); float lovrFontGetHeight(Font* font); float lovrFontGetAscent(Font* font); float lovrFontGetDescent(Font* font); @@ -59,7 +37,3 @@ void lovrFontSetFlipEnabled(Font* font, bool flip); int32_t lovrFontGetKerning(Font* font, unsigned int a, unsigned int b); float lovrFontGetPixelDensity(Font* font); void lovrFontSetPixelDensity(Font* font, float pixelDensity); -Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint); -void lovrFontAddGlyph(Font* font, Glyph* glyph); -void lovrFontExpandTexture(Font* font); -void lovrFontCreateTexture(Font* font); diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index 76420071..dcdc0810 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -1201,15 +1201,15 @@ void lovrGraphicsSkybox(Texture* texture) { void lovrGraphicsPrint(const char* str, size_t length, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign) { float width; + float height; uint32_t lineCount; uint32_t glyphCount; Font* font = lovrGraphicsGetFont(); - lovrFontMeasure(font, str, length, wrap, &width, &lineCount, &glyphCount); + lovrFontMeasure(font, str, length, wrap, &width, &height, &lineCount, &glyphCount); - float scale = 1.f / font->pixelDensity; - float offsetY = ((lineCount + 1) * font->rasterizer->height * font->lineHeight) * (valign / 2.f) * (font->flip ? -1 : 1); + float scale = 1.f / lovrFontGetPixelDensity(font); mat4_scale(transform, scale, scale, scale); - mat4_translate(transform, 0.f, offsetY, 0.f); + mat4_translate(transform, 0.f, height * (valign / 2.f), 0.f); Pipeline pipeline = state.pipeline; pipeline.blendMode = pipeline.blendMode == BLEND_NONE ? BLEND_ALPHA : pipeline.blendMode; @@ -1223,7 +1223,7 @@ void lovrGraphicsPrint(const char* str, size_t length, mat4 transform, float wra .shader = SHADER_FONT, .pipeline = &pipeline, .transform = transform, - .texture = font->texture, + .texture = lovrFontGetTexture(font), .vertexCount = glyphCount * 4, .indexCount = glyphCount * 6, .vertices = &vertices, diff --git a/src/modules/graphics/graphics.h b/src/modules/graphics/graphics.h index 0defcebf..c9512a13 100644 --- a/src/modules/graphics/graphics.h +++ b/src/modules/graphics/graphics.h @@ -196,8 +196,8 @@ typedef struct { } GpuLimits; typedef struct { - int shaderSwitches; - int drawCalls; + uint32_t shaderSwitches; + uint32_t drawCalls; } GpuStats; typedef struct { diff --git a/src/modules/graphics/mesh.c b/src/modules/graphics/mesh.c index f419e8ad..0b057baa 100644 --- a/src/modules/graphics/mesh.c +++ b/src/modules/graphics/mesh.c @@ -2,6 +2,7 @@ #include "graphics/buffer.h" #include "graphics/graphics.h" #include "graphics/material.h" +#include "core/hash.h" #include "core/ref.h" #include @@ -26,55 +27,60 @@ size_t lovrMeshGetIndexSize(Mesh* mesh) { } void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute) { - lovrAssert(!map_get(&mesh->attributeMap, name), "Mesh already has an attribute named '%s'", name); + uint64_t hash = hash64(name, strlen(name)); + lovrAssert(map_get(&mesh->attributeMap, hash) == MAP_NIL, "Mesh already has an attribute named '%s'", name); lovrAssert(mesh->attributeCount < MAX_ATTRIBUTES, "Mesh already has the max number of attributes (%d)", MAX_ATTRIBUTES); lovrAssert(strlen(name) < MAX_ATTRIBUTE_NAME_LENGTH, "Mesh attribute name '%s' is too long (max is %d)", name, MAX_ATTRIBUTE_NAME_LENGTH); lovrGraphicsFlushMesh(mesh); - int index = mesh->attributeCount++; + uint64_t index = mesh->attributeCount++; mesh->attributes[index] = *attribute; strcpy(mesh->attributeNames[index], name); - map_set(&mesh->attributeMap, name, index); + map_set(&mesh->attributeMap, hash, index); lovrRetain(attribute->buffer); } void lovrMeshDetachAttribute(Mesh* mesh, const char* name) { - int* index = map_get(&mesh->attributeMap, name); - lovrAssert(index, "No attached attribute named '%s' was found", name); - MeshAttribute* attribute = &mesh->attributes[*index]; + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&mesh->attributeMap, hash); + lovrAssert(index != MAP_NIL, "No attached attribute named '%s' was found", name); + MeshAttribute* attribute = &mesh->attributes[index]; lovrGraphicsFlushMesh(mesh); lovrRelease(Buffer, attribute->buffer); - map_remove(&mesh->attributeMap, name); - mesh->attributeNames[*index][0] = '\0'; - memmove(mesh->attributeNames + *index, mesh->attributeNames + *index + 1, (mesh->attributeCount - *index - 1) * MAX_ATTRIBUTE_NAME_LENGTH * sizeof(char)); - memmove(mesh->attributes + *index, mesh->attributes + *index + 1, (mesh->attributeCount - *index - 1) * sizeof(MeshAttribute)); + map_remove(&mesh->attributeMap, hash); + mesh->attributeNames[index][0] = '\0'; + memmove(mesh->attributeNames + index, mesh->attributeNames + index + 1, (mesh->attributeCount - index - 1) * MAX_ATTRIBUTE_NAME_LENGTH * sizeof(char)); + memmove(mesh->attributes + index, mesh->attributes + index + 1, (mesh->attributeCount - index - 1) * sizeof(MeshAttribute)); mesh->attributeCount--; for (uint32_t i = 0; i < MAX_ATTRIBUTES; i++) { - if (mesh->locations[i] > *index) { + if (mesh->locations[i] > index) { mesh->locations[i]--; - } else if (mesh->locations[i] == *index) { + } else if (mesh->locations[i] == index) { mesh->locations[i] = 0xff; } } } const MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, const char* name) { - int* index = map_get(&mesh->attributeMap, name); - return index ? &mesh->attributes[*index] : NULL; + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&mesh->attributeMap, hash); + return index == MAP_NIL ? NULL : &mesh->attributes[index]; } bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) { - int* index = map_get(&mesh->attributeMap, name); - lovrAssert(index, "Mesh does not have an attribute named '%s'", name); - return !mesh->attributes[*index].disabled; + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&mesh->attributeMap, hash); + lovrAssert(index != MAP_NIL, "Mesh does not have an attribute named '%s'", name); + return !mesh->attributes[index].disabled; } void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) { bool disable = !enable; - int* index = map_get(&mesh->attributeMap, name); - lovrAssert(index, "Mesh does not have an attribute named '%s'", name); - if (mesh->attributes[*index].disabled != disable) { + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&mesh->attributeMap, hash); + lovrAssert(index != MAP_NIL, "Mesh does not have an attribute named '%s'", name); + if (mesh->attributes[index].disabled != disable) { lovrGraphicsFlushMesh(mesh); - mesh->attributes[*index].disabled = disable; + mesh->attributes[index].disabled = disable; } } diff --git a/src/modules/graphics/mesh.h b/src/modules/graphics/mesh.h index 5f9b2e20..5a0b150e 100644 --- a/src/modules/graphics/mesh.h +++ b/src/modules/graphics/mesh.h @@ -1,6 +1,6 @@ #include "data/modelData.h" #include "graphics/opengl.h" -#include "lib/map/map.h" +#include "core/map.h" #include #pragma once @@ -30,7 +30,7 @@ typedef struct Mesh { uint8_t locations[MAX_ATTRIBUTES]; uint16_t enabledLocations; uint16_t divisors[MAX_ATTRIBUTES]; - map_int_t attributeMap; + map_t attributeMap; uint32_t attributeCount; struct Buffer* vertexBuffer; struct Buffer* indexBuffer; diff --git a/src/modules/graphics/opengl.c b/src/modules/graphics/opengl.c index b788a9b9..0c230db7 100644 --- a/src/modules/graphics/opengl.c +++ b/src/modules/graphics/opengl.c @@ -8,8 +8,8 @@ #include "graphics/texture.h" #include "resources/shaders.h" #include "data/modelData.h" +#include "core/hash.h" #include "core/ref.h" -#include "lib/map/map.h" #include #include #include @@ -47,11 +47,17 @@ typedef struct { } BlockBuffer; typedef struct { - size_t next; - size_t oldest; - GLuint timers[4]; - uint64_t ns; -} TimerList; + GLuint* queries; + uint32_t* chain; + uint32_t next; + uint32_t count; +} QueryPool; + +typedef struct { + uint32_t head; + uint32_t tail; + uint64_t nanoseconds; +} Timer; static struct { Texture* defaultTexture; @@ -83,7 +89,10 @@ static struct { float viewports[2][4]; uint32_t viewportCount; arr_t(void*) incoherents[MAX_BARRIERS]; - map_t(TimerList) timers; + QueryPool queryPool; + arr_t(Timer) timers; + uint32_t activeTimer; + map_t timerMap; GpuFeatures features; GpuLimits limits; GpuStats stats; @@ -1099,6 +1108,10 @@ void lovrGpuInit(getProcAddressProc getProcAddress) { lovrTextureSetFilter(state.defaultTexture, (TextureFilter) { .mode = FILTER_NEAREST }); lovrTextureSetWrap(state.defaultTexture, (TextureWrap) { WRAP_CLAMP, WRAP_CLAMP, WRAP_CLAMP }); lovrRelease(TextureData, textureData); + + map_init(&state.timerMap, 4); + state.queryPool.next = ~0u; + state.activeTimer = ~0u; } void lovrGpuDestroy() { @@ -1112,6 +1125,10 @@ void lovrGpuDestroy() { for (int i = 0; i < MAX_BARRIERS; i++) { arr_free(&state.incoherents[i]); } + glDeleteQueries(state.queryPool.count, state.queryPool.queries); + free(state.queryPool.queries); + arr_free(&state.timers); + map_free(&state.timerMap); memset(&state, 0, sizeof(state)); } @@ -1264,42 +1281,102 @@ void lovrGpuDirtyTexture() { void lovrGpuTick(const char* label) { #ifndef LOVR_WEBGL - TimerList* timer = map_get(&state.timers, label); + lovrAssert(state.activeTimer == ~0u, "Attempt to start a new GPU timer while one is already active!"); + QueryPool* pool = &state.queryPool; + uint64_t hash = hash64(label, strlen(label)); + uint64_t index = map_get(&state.timerMap, hash); - if (!timer) { - map_set(&state.timers, label, ((TimerList) { .oldest = 0 })); - timer = map_get(&state.timers, label); - glGenQueries(sizeof(timer->timers) / sizeof(timer->timers[0]), timer->timers); + // Create new timer + if (index == MAP_NIL) { + index = state.timers.length++; + map_set(&state.timerMap, hash, index); + arr_reserve(&state.timers, state.timers.length); + state.timers.data[index].head = ~0u; + state.timers.data[index].tail = ~0u; } - glBeginQuery(GL_TIME_ELAPSED, timer->timers[timer->next]); + Timer* timer = &state.timers.data[index]; + state.activeTimer = index; - size_t next = (timer->next + 1) % 4; - if (next != timer->oldest) { - timer->next = next; + // Expand pool if no unused timers are available. + // The pool manages one memory allocation split into two chunks. + // - The first chunk contains OpenGL query objects (GLuint). + // - The second chunk is a linked list of query indices (uint32_t), used for two purposes: + // - For inactive queries, pool->chain[query] points to the next inactive query (freelist). + // - For active queries, pool->chain[query] points to the next active query for that timer. + // When resizing the query pool allocation, the second half of the old allocation needs to be + // copied to the second half of the new allocation. + if (pool->next == ~0u) { + uint32_t n = pool->count; + pool->count = n == 0 ? 4 : (n << 1); + pool->queries = realloc(pool->queries, pool->count * (sizeof(GLuint) + sizeof(uint32_t))); + lovrAssert(pool->queries, "Out of memory"); + pool->chain = pool->queries + pool->count; + memcpy(pool->chain, pool->queries + n, n * sizeof(uint32_t)); + glGenQueries(n ? n : pool->count, pool->queries + n); + for (uint32_t i = n; i < pool->count - 1; i++) { + pool->chain[i] = i + 1; + } + pool->chain[pool->count - 1] = ~0u; + pool->next = n; } + + // Start query, update linked list pointers + uint32_t query = pool->next; + glBeginQuery(GL_TIME_ELAPSED, pool->queries[query]); + if (timer->tail != ~0u) { pool->chain[timer->tail] = query; } + if (timer->head == ~0u) { timer->head = query; } + pool->next = pool->chain[query]; + pool->chain[query] = ~0u; + timer->tail = query; #endif } double lovrGpuTock(const char* label) { #ifndef LOVR_WEBGL - TimerList* timer = map_get(&state.timers, label); - if (!timer) return 0.; + QueryPool* pool = &state.queryPool; + uint64_t hash = hash64(label, strlen(label)); + uint64_t index = map_get(&state.timerMap, hash); + + if (index == MAP_NIL) { + return 0.; + } + + Timer* timer = &state.timers.data[index]; + + if (state.activeTimer != index) { + return timer->nanoseconds / 1e9; + } glEndQuery(GL_TIME_ELAPSED); + state.activeTimer = ~0u; + + // Repeatedly check timer's oldest pending query for completion + for (;;) { + int query = timer->head; - while (timer->oldest != timer->next) { GLuint available; - glGetQueryObjectuiv(timer->timers[timer->oldest], GL_QUERY_RESULT_AVAILABLE, &available); + glGetQueryObjectuiv(pool->queries[query], GL_QUERY_RESULT_AVAILABLE, &available); + if (!available) { break; } - glGetQueryObjectui64v(timer->timers[timer->oldest], GL_QUERY_RESULT, &timer->ns); - timer->oldest = (timer->oldest + 1) % 4; + // Update timer result + glGetQueryObjectui64v(pool->queries[query], GL_QUERY_RESULT, &timer->nanoseconds); + + // Update timer's head pointer and return the completed query back to the pool + timer->head = pool->chain[query]; + pool->chain[query] = pool->next; + pool->next = query; + + if (timer->head == ~0u) { + timer->tail = ~0u; + break; + } } - return timer->ns / 1e9; + return timer->nanoseconds / 1e9; #endif return 0.; } @@ -1848,7 +1925,7 @@ static void lovrShaderSetupUniforms(Shader* shader) { int32_t blockCount; glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &blockCount); lovrAssert((size_t) blockCount <= MAX_BLOCK_BUFFERS, "Shader has too many uniform blocks (%d) the max is %d", blockCount, MAX_BLOCK_BUFFERS); - map_init(&shader->blockMap); + map_init(&shader->blockMap, blockCount); arr_block_t* uniformBlocks = &shader->blocks[BLOCK_UNIFORM]; arr_init(uniformBlocks); arr_reserve(uniformBlocks, (size_t) blockCount); @@ -1856,10 +1933,11 @@ static void lovrShaderSetupUniforms(Shader* shader) { UniformBlock block = { .slot = i, .source = NULL }; glUniformBlockBinding(program, i, block.slot); + GLsizei length; char name[LOVR_MAX_UNIFORM_LENGTH]; - glGetActiveUniformBlockName(program, i, LOVR_MAX_UNIFORM_LENGTH, NULL, name); + glGetActiveUniformBlockName(program, i, LOVR_MAX_UNIFORM_LENGTH, &length, name); int blockId = (i << 1) + BLOCK_UNIFORM; - map_set(&shader->blockMap, name, blockId); + map_set(&shader->blockMap, hash64(name, length), blockId); arr_push(uniformBlocks, block); arr_init(&uniformBlocks->data[uniformBlocks->length - 1].uniforms); } @@ -1880,10 +1958,11 @@ static void lovrShaderSetupUniforms(Shader* shader) { glShaderStorageBlockBinding(program, i, block.slot); arr_init(&block.uniforms); + GLsizei length; char name[LOVR_MAX_UNIFORM_LENGTH]; - glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, i, LOVR_MAX_UNIFORM_LENGTH, NULL, name); + glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, i, LOVR_MAX_UNIFORM_LENGTH, &length, name); int blockId = (i << 1) + BLOCK_COMPUTE; - map_set(&shader->blockMap, name, blockId); + map_set(&shader->blockMap, hash64(name, length), blockId); arr_push(computeBlocks, block); } @@ -1917,14 +1996,14 @@ static void lovrShaderSetupUniforms(Shader* shader) { int32_t uniformCount; int textureSlot = 0; int imageSlot = 0; - map_init(&shader->uniformMap); - arr_init(&shader->uniforms); - arr_reserve(&shader->uniforms, 4); glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformCount); + map_init(&shader->uniformMap, 0); + arr_init(&shader->uniforms); for (uint32_t i = 0; i < (uint32_t) uniformCount; i++) { Uniform uniform; GLenum glType; - glGetActiveUniform(program, i, LOVR_MAX_UNIFORM_LENGTH, NULL, &uniform.count, &glType, uniform.name); + GLsizei length; + glGetActiveUniform(program, i, LOVR_MAX_UNIFORM_LENGTH, &length, &uniform.count, &glType, uniform.name); char* subscript = strchr(uniform.name, '['); if (subscript) { @@ -2025,7 +2104,7 @@ static void lovrShaderSetupUniforms(Shader* shader) { offset += uniform.components * (uniform.type == UNIFORM_MATRIX ? uniform.components : 1); } - map_set(&shader->uniformMap, uniform.name, (int) shader->uniforms.length); + map_set(&shader->uniformMap, hash64(uniform.name, length), shader->uniforms.length); arr_push(&shader->uniforms, uniform); textureSlot += uniform.type == UNIFORM_SAMPLER ? uniform.count : 0; imageSlot += uniform.type == UNIFORM_IMAGE ? uniform.count : 0; @@ -2130,13 +2209,14 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c // Attribute cache int32_t attributeCount; glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attributeCount); - map_init(&shader->attributes); + map_init(&shader->attributes, 0); for (int i = 0; i < attributeCount; i++) { char name[LOVR_MAX_ATTRIBUTE_LENGTH]; GLint size; GLenum type; - glGetActiveAttrib(program, i, LOVR_MAX_ATTRIBUTE_LENGTH, NULL, &size, &type, name); - map_set(&shader->attributes, name, glGetAttribLocation(program, name)); + GLsizei length; + glGetActiveAttrib(program, i, LOVR_MAX_ATTRIBUTE_LENGTH, &length, &size, &type, name); + map_set(&shader->attributes, hash64(name, length), glGetAttribLocation(program, name)); } shader->multiview = multiview; @@ -2180,9 +2260,9 @@ void lovrShaderDestroy(void* ref) { arr_free(&shader->uniforms); arr_free(&shader->blocks[BLOCK_UNIFORM]); arr_free(&shader->blocks[BLOCK_COMPUTE]); - map_deinit(&shader->attributes); - map_deinit(&shader->uniformMap); - map_deinit(&shader->blockMap); + map_free(&shader->attributes); + map_free(&shader->uniformMap); + map_free(&shader->blockMap); } // Mesh @@ -2193,7 +2273,7 @@ Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, Buffer* vertexBuffer, uint32_t ver mesh->vertexCount = vertexCount; lovrRetain(mesh->vertexBuffer); glGenVertexArrays(1, &mesh->vao); - map_init(&mesh->attributeMap); + map_init(&mesh->attributeMap, MAX_ATTRIBUTES); memset(mesh->locations, 0xff, MAX_ATTRIBUTES * sizeof(uint8_t)); return mesh; } @@ -2205,7 +2285,7 @@ void lovrMeshDestroy(void* ref) { for (uint32_t i = 0; i < mesh->attributeCount; i++) { lovrRelease(Buffer, mesh->attributes[i].buffer); } - map_deinit(&mesh->attributeMap); + map_free(&mesh->attributeMap); lovrRelease(Buffer, mesh->vertexBuffer); lovrRelease(Buffer, mesh->indexBuffer); lovrRelease(Material, mesh->material); diff --git a/src/modules/graphics/shader.c b/src/modules/graphics/shader.c index 696df802..1633d749 100644 --- a/src/modules/graphics/shader.c +++ b/src/modules/graphics/shader.c @@ -3,6 +3,7 @@ #include "graphics/buffer.h" #include "math/math.h" #include "resources/shaders.h" +#include "core/hash.h" #include "core/ref.h" #include #include @@ -77,30 +78,26 @@ ShaderType lovrShaderGetType(Shader* shader) { } int lovrShaderGetAttributeLocation(Shader* shader, const char* name) { - int* location = map_get(&shader->attributes, name); - return location ? *location : -1; + uint64_t location = map_get(&shader->attributes, hash64(name, strlen(name))); + return location == MAP_NIL ? -1 : (int) location; } bool lovrShaderHasUniform(Shader* shader, const char* name) { - return map_get(&shader->uniformMap, name) != NULL; + return map_get(&shader->uniformMap, hash64(name, strlen(name))) != MAP_NIL; } const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) { - size_t* index = map_get(&shader->uniformMap, name); - if (!index) { - return false; - } - - return &shader->uniforms.data[*index]; + uint64_t index = map_get(&shader->uniformMap, hash64(name, strlen(name))); + return index == MAP_NIL ? NULL : &shader->uniforms.data[index]; } static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int start, int count, int size, const char* debug) { - size_t* index = map_get(&shader->uniformMap, name); - if (!index) { + uint64_t index = map_get(&shader->uniformMap, hash64(name, strlen(name))); + if (index == MAP_NIL) { return; } - Uniform* uniform = &shader->uniforms.data[*index]; + Uniform* uniform = &shader->uniforms.data[index]; lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name); lovrAssert((start + count) * size <= uniform->size, "Too many %ss for uniform %s, maximum is %d", debug, name, uniform->size / size); @@ -140,11 +137,11 @@ void lovrShaderSetColor(Shader* shader, const char* name, Color color) { } void lovrShaderSetBlock(Shader* shader, const char* name, Buffer* buffer, size_t offset, size_t size, UniformAccess access) { - int* id = map_get(&shader->blockMap, name); - if (!id) return; + uint64_t id = map_get(&shader->blockMap, hash64(name, strlen(name))); + if (id == MAP_NIL) return; - int type = *id & 1; - int index = *id >> 1; + int type = id & 1; + int index = id >> 1; UniformBlock* block = &shader->blocks[type].data[index]; if (block->source != buffer || block->offset != offset || block->size != size) { @@ -181,12 +178,13 @@ size_t lovrShaderComputeUniformLayout(arr_uniform_t* uniforms) { ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, BlockType type, Buffer* buffer, arr_uniform_t* uniforms) { arr_init(&block->uniforms); - map_init(&block->uniformMap); + map_init(&block->uniformMap, uniforms->length); arr_append(&block->uniforms, uniforms->data, uniforms->length); for (size_t i = 0; i < block->uniforms.length; i++) { - map_set(&block->uniformMap, block->uniforms.data[i].name, i); + Uniform* uniform = &block->uniforms.data[i]; + map_set(&block->uniformMap, hash64(uniform->name, strlen(uniform->name)), i); } block->type = type; @@ -199,7 +197,7 @@ void lovrShaderBlockDestroy(void* ref) { ShaderBlock* block = ref; lovrRelease(Buffer, block->buffer); arr_free(&block->uniforms); - map_deinit(&block->uniformMap); + map_free(&block->uniformMap); } BlockType lovrShaderBlockGetType(ShaderBlock* block) { @@ -248,10 +246,8 @@ char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, si } const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) { - size_t* index = map_get(&block->uniformMap, name); - if (!index) return NULL; - - return &block->uniforms.data[*index]; + uint64_t index = map_get(&block->uniformMap, hash64(name, strlen(name))); + return index == MAP_NIL ? NULL : &block->uniforms.data[index]; } Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) { diff --git a/src/modules/graphics/shader.h b/src/modules/graphics/shader.h index 23137f2c..94adb239 100644 --- a/src/modules/graphics/shader.h +++ b/src/modules/graphics/shader.h @@ -1,7 +1,6 @@ #include "graphics/texture.h" #include "graphics/opengl.h" #include "core/arr.h" -#include "lib/map/map.h" #include #pragma once @@ -95,7 +94,7 @@ typedef arr_t(Uniform) arr_uniform_t; typedef struct { BlockType type; arr_uniform_t uniforms; - map_t(size_t) uniformMap; + map_t uniformMap; struct Buffer* buffer; } ShaderBlock; @@ -114,9 +113,9 @@ typedef struct Shader { ShaderType type; arr_uniform_t uniforms; arr_block_t blocks[2]; - map_int_t attributes; - map_t(size_t) uniformMap; - map_int_t blockMap; + map_t attributes; + map_t uniformMap; + map_t blockMap; bool multiview; GPU_SHADER_FIELDS } Shader; diff --git a/src/modules/headset/oculus.c b/src/modules/headset/oculus.c index 88c9e969..7ef04550 100644 --- a/src/modules/headset/oculus.c +++ b/src/modules/headset/oculus.c @@ -3,12 +3,15 @@ #include "graphics/graphics.h" #include "graphics/canvas.h" #include "graphics/texture.h" +#include "core/arr.h" +#include "core/hash.h" #include "core/maf.h" +#include "core/map.h" #include "core/ref.h" - -#include #include #include +#include +#include static struct { bool needRefreshTracking; @@ -21,19 +24,21 @@ static struct { Canvas* canvas; ovrTextureSwapChain chain; ovrMirrorTexture mirror; - map_t(Texture*) textureLookup; + arr_t(Texture*) textures; + map_t textureLookup; } state; static Texture* lookupTexture(uint32_t handle) { - char key[4 + 1] = { 0 }; - lovrAssert(handle < 9999, "Texture handle overflow"); - sprintf(key, "%d", handle); - Texture** texture = map_get(&state.textureLookup, key); - if (!texture) { - map_set(&state.textureLookup, key, lovrTextureCreateFromHandle(handle, TEXTURE_2D, 1)); - texture = map_get(&state.textureLookup, key); + uint64_t hash = hash64(&handle, sizeof(handle)); + uint64_t index = map_get(&state.textureLookup, hash); + + if (index == MAP_NIL) { + index = state.textures.length; + map_set(&state.textureLookup, hash, index); + arr_push(&state.textures, lovrTextureCreateFromHandle(handle, TEXTURE_2D, 1)); } - return *texture; + + return state.textures.data[index]; } static ovrTrackingState *refreshTracking(void) { @@ -85,20 +90,17 @@ static bool oculus_init(float offset, uint32_t msaa) { state.clipNear = 0.1f; state.clipFar = 30.f; - map_init(&state.textureLookup); + map_init(&state.textureLookup, 4); ovr_SetTrackingOriginType(state.session, ovrTrackingOrigin_FloorLevel); return true; } static void oculus_destroy(void) { - const char* key; - map_iter_t iter = map_iter(&state.textureLookup); - while ((key = map_next(&state.textureLookup, &iter)) != NULL) { - Texture* texture = *map_get(&state.textureLookup, key); - lovrRelease(Texture, texture); + for (size_t i = 0; i < state.textures.length; i++) { + lovrRelease(Texture, state.textures.data[i]); } - map_deinit(&state.textureLookup); + map_free(&state.textureLookup); if (state.mirror) { ovr_DestroyMirrorTexture(state.session, state.mirror); diff --git a/src/modules/physics/physics.c b/src/modules/physics/physics.c index 992f4f1f..7dc1e4ce 100644 --- a/src/modules/physics/physics.c +++ b/src/modules/physics/physics.c @@ -31,6 +31,16 @@ static void raycastCallback(void* data, dGeomID a, dGeomID b) { } } +// XXX slow, but probably fine (tag names are not on any critical path), could switch to hashing if needed +static uint32_t findTag(World* world, const char* name) { + for (uint32_t i = 0; i < MAX_TAGS && world->tags[i]; i++) { + if (!strcmp(world->tags[i], name)) { + return i; + } + } + return NO_TAG; +} + static bool initialized = false; bool lovrPhysicsInit() { @@ -45,7 +55,7 @@ void lovrPhysicsDestroy() { initialized = false; } -World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep, const char** tags, int tagCount) { +World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep, const char** tags, uint32_t tagCount) { world->id = dWorldCreate(); world->space = dHashSpaceCreate(0); dHashSpaceSetLevels(world->space, -4, 8); @@ -53,15 +63,12 @@ World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep arr_init(&world->overlaps); lovrWorldSetGravity(world, xg, yg, zg); lovrWorldSetSleepingAllowed(world, allowSleep); - map_init(&world->tags); - for (int i = 0; i < tagCount; i++) { - map_set(&world->tags, tags[i], i); + for (uint32_t i = 0; i < tagCount; i++) { + size_t size = strlen(tags[i]) + 1; + world->tags[i] = malloc(size); + memcpy(world->tags[i], tags[i], size); } - - for (int i = 0; i < MAX_TAGS; i++) { - world->masks[i] = ~0; - } - + memset(world->masks, 0xff, sizeof(world->masks)); return world; } @@ -69,7 +76,9 @@ void lovrWorldDestroy(void* ref) { World* world = ref; lovrWorldDestroyData(world); arr_free(&world->overlaps); - map_deinit(&world->tags); + for (uint32_t i = 0; i < MAX_TAGS && world->tags[i]; i++) { + free(world->tags[i]); + } } void lovrWorldDestroyData(World* world) { @@ -132,10 +141,10 @@ int lovrWorldCollide(World* world, Shape* a, Shape* b, float friction, float res Collider* colliderA = a->collider; Collider* colliderB = b->collider; - int tag1 = colliderA->tag; - int tag2 = colliderB->tag; + uint32_t i = colliderA->tag; + uint32_t j = colliderB->tag; - if (tag1 != NO_TAG && tag2 != NO_TAG && !((world->masks[tag1] & (1 << tag2)) && (world->masks[tag2] & (1 << tag1)))) { + if (i != NO_TAG && j != NO_TAG && !((world->masks[i] & (1 << j)) && (world->masks[j] & (1 << i)))) { return false; } @@ -221,54 +230,42 @@ void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, floa dGeomDestroy(ray); } -const char* lovrWorldGetTagName(World* world, int tag) { - if (tag == NO_TAG) { - return NULL; - } - - const char* key; - map_iter_t iter = map_iter(&world->tags); - while ((key = map_next(&world->tags, &iter))) { - if (*map_get(&world->tags, key) == tag) { - return key; - } - } - - return NULL; +const char* lovrWorldGetTagName(World* world, uint32_t tag) { + return (tag == NO_TAG) ? NULL : world->tags[tag]; } int lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2) { - int* index1 = map_get(&world->tags, tag1); - int* index2 = map_get(&world->tags, tag2); - if (!index1 || !index2) { + uint32_t i = findTag(world, tag1); + uint32_t j = findTag(world, tag2); + if (i == NO_TAG || j == NO_TAG) { return NO_TAG; } - world->masks[*index1] &= ~(1 << *index2); - world->masks[*index2] &= ~(1 << *index1); + world->masks[i] &= ~(1 << j); + world->masks[j] &= ~(1 << i); return 0; } int lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2) { - int* index1 = map_get(&world->tags, tag1); - int* index2 = map_get(&world->tags, tag2); - if (!index1 || !index2) { + uint32_t i = findTag(world, tag1); + uint32_t j = findTag(world, tag2); + if (i == NO_TAG || j == NO_TAG) { return NO_TAG; } - world->masks[*index1] |= (1 << *index2); - world->masks[*index2] |= (1 << *index1); + world->masks[i] |= (1 << j); + world->masks[j] |= (1 << i); return 0; } int lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag2) { - int* index1 = map_get(&world->tags, tag1); - int* index2 = map_get(&world->tags, tag2); - if (!index1 || !index2) { + uint32_t i = findTag(world, tag1); + uint32_t j = findTag(world, tag2); + if (i == NO_TAG || j == NO_TAG) { return NO_TAG; } - return (world->masks[*index1] & (1 << *index2)) && (world->masks[*index2] & (1 << *index1)); + return (world->masks[i] & (1 << j)) && (world->masks[j] & (1 << i)); } Collider* lovrColliderInit(Collider* collider, World* world, float x, float y, float z) { @@ -396,20 +393,14 @@ const char* lovrColliderGetTag(Collider* collider) { return lovrWorldGetTagName(collider->world, collider->tag); } -int lovrColliderSetTag(Collider* collider, const char* tag) { - if (tag == NULL) { +bool lovrColliderSetTag(Collider* collider, const char* tag) { + if (!tag) { collider->tag = NO_TAG; - return 0; + return true; } - int* index = map_get(&collider->world->tags, tag); - - if (!index) { - return NO_TAG; - } - - collider->tag = *index; - return 0; + collider->tag = findTag(collider->world, tag); + return collider->tag != NO_TAG; } float lovrColliderGetFriction(Collider* collider) { diff --git a/src/modules/physics/physics.h b/src/modules/physics/physics.h index 30beaa24..77b2838b 100644 --- a/src/modules/physics/physics.h +++ b/src/modules/physics/physics.h @@ -1,5 +1,4 @@ #include "core/arr.h" -#include "lib/map/map.h" #include #include #include @@ -8,7 +7,7 @@ #define MAX_CONTACTS 4 #define MAX_TAGS 16 -#define NO_TAG ~0 +#define NO_TAG ~0u typedef enum { SHAPE_SPHERE, @@ -33,7 +32,7 @@ typedef struct { dSpaceID space; dJointGroupID contactGroup; arr_t(Shape*) overlaps; - map_int_t tags; + char* tags[MAX_TAGS]; uint16_t masks[MAX_TAGS]; Collider* head; } World; @@ -44,7 +43,7 @@ struct Collider { Collider* prev; Collider* next; void* userdata; - int tag; + uint32_t tag; arr_t(Shape*) shapes; arr_t(Joint*) joints; float friction; @@ -85,7 +84,7 @@ typedef struct { bool lovrPhysicsInit(void); void lovrPhysicsDestroy(void); -World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep, const char** tags, int tagCount); +World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep, const char** tags, uint32_t tagCount); #define lovrWorldCreate(...) lovrWorldInit(lovrAlloc(World), __VA_ARGS__) void lovrWorldDestroy(void* ref); void lovrWorldDestroyData(World* world); @@ -102,7 +101,7 @@ void lovrWorldSetAngularDamping(World* world, float damping, float threshold); bool lovrWorldIsSleepingAllowed(World* world); void lovrWorldSetSleepingAllowed(World* world, bool allowed); void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, float y2, float z2, RaycastCallback callback, void* userdata); -const char* lovrWorldGetTagName(World* world, int tag); +const char* lovrWorldGetTagName(World* world, uint32_t tag); int lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2); int lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2); int lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag); @@ -119,7 +118,7 @@ Joint** lovrColliderGetJoints(Collider* collider, size_t* count); void* lovrColliderGetUserData(Collider* collider); void lovrColliderSetUserData(Collider* collider, void* data); const char* lovrColliderGetTag(Collider* collider); -int lovrColliderSetTag(Collider* collider, const char* tag); +bool lovrColliderSetTag(Collider* collider, const char* tag); float lovrColliderGetFriction(Collider* collider); void lovrColliderSetFriction(Collider* collider, float friction); float lovrColliderGetRestitution(Collider* collider); diff --git a/src/modules/thread/thread.c b/src/modules/thread/thread.c index 46deace4..63bc7a85 100644 --- a/src/modules/thread/thread.c +++ b/src/modules/thread/thread.c @@ -1,43 +1,47 @@ #include "thread/thread.h" #include "thread/channel.h" +#include "core/arr.h" +#include "core/hash.h" +#include "core/map.h" #include "core/ref.h" #include "util.h" -#include "lib/map/map.h" #include +#include static struct { bool initialized; - map_void_t channels; + arr_t(Channel*) channels; + map_t channelMap; } state; bool lovrThreadModuleInit() { if (state.initialized) return false; - map_init(&state.channels); + arr_init(&state.channels); + map_init(&state.channelMap, 0); return state.initialized = true; } void lovrThreadModuleDestroy() { if (!state.initialized) return; - const char* key; - map_iter_t iter = map_iter(&state.channels); - while ((key = map_next(&state.channels, &iter)) != NULL) { - Channel* channel = *(Channel**) map_get(&state.channels, key); - lovrRelease(Channel, channel); + for (size_t i = 0; i < state.channels.length; i++) { + lovrRelease(Channel, state.channels.data[i]); } - map_deinit(&state.channels); + arr_free(&state.channels); + map_free(&state.channelMap); state.initialized = false; } Channel* lovrThreadGetChannel(const char* name) { - Channel** channel = (Channel**) map_get(&state.channels, name); + uint64_t hash = hash64(name, strlen(name)); + uint64_t index = map_get(&state.channelMap, hash); - if (channel) { - return *channel; - } else { - Channel* channel = lovrChannelCreate(); - map_set(&state.channels, name, channel); - return channel; + if (index == MAP_NIL) { + index = state.channels.length; + map_set(&state.channelMap, hash, index); + arr_push(&state.channels, lovrChannelCreate()); } + + return state.channels.data[index]; } Thread* lovrThreadInit(Thread* thread, int (*runner)(void*), Blob* body) {