Add core/map hash table; rm lib/map;

This commit is contained in:
bjorn 2019-09-07 15:07:07 -07:00
parent 15e9fd49e7
commit d21911d010
33 changed files with 628 additions and 677 deletions

View File

@ -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)

View File

@ -2,6 +2,8 @@
#include <lauxlib.h>
#include <lualib.h>
#include <stdint.h>
#include <string.h>
#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")

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -2,6 +2,7 @@
#include "graphics/buffer.h"
#include "graphics/shader.h"
#include <stdlib.h>
#include <string.h>
static int l_lovrShaderBlockGetType(lua_State* L) {
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);

View File

@ -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) {

View File

@ -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,\

12
src/core/hash.h Normal file
View File

@ -0,0 +1,12 @@
#include "util.h"
#include <stdint.h>
// 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;
}

100
src/core/map.c Normal file
View File

@ -0,0 +1,100 @@
#include "map.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
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--;
}

18
src/core/map.h Normal file
View File

@ -0,0 +1,18 @@
#include <stdint.h>
#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);

View File

@ -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.

View File

@ -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 <stdlib.h>
#include <string.h>
#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);
}

View File

@ -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 <string.h>
#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

View File

@ -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);
}

View File

@ -1,5 +1,5 @@
#include "util.h"
#include "lib/map/map.h"
#include "core/map.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
@ -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);

View File

@ -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);
}

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <float.h>
@ -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);

View File

@ -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 <string.h>
#include <stdlib.h>
#include <stdio.h>
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);

View File

@ -1,15 +1,12 @@
#include "data/rasterizer.h"
#include "lib/map/map.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#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);

View File

@ -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,

View File

@ -196,8 +196,8 @@ typedef struct {
} GpuLimits;
typedef struct {
int shaderSwitches;
int drawCalls;
uint32_t shaderSwitches;
uint32_t drawCalls;
} GpuStats;
typedef struct {

View File

@ -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 <stdlib.h>
@ -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;
}
}

View File

@ -1,6 +1,6 @@
#include "data/modelData.h"
#include "graphics/opengl.h"
#include "lib/map/map.h"
#include "core/map.h"
#include <stdbool.h>
#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;

View File

@ -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 <math.h>
#include <limits.h>
#include <string.h>
@ -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);

View File

@ -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 <stdlib.h>
#include <stdio.h>
@ -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) {

View File

@ -1,7 +1,6 @@
#include "graphics/texture.h"
#include "graphics/opengl.h"
#include "core/arr.h"
#include "lib/map/map.h"
#include <stdbool.h>
#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;

View File

@ -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 <stdbool.h>
#include <OVR_CAPI.h>
#include <OVR_CAPI_GL.h>
#include <stdlib.h>
#include <stdbool.h>
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);

View File

@ -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) {

View File

@ -1,5 +1,4 @@
#include "core/arr.h"
#include "lib/map/map.h"
#include <stdint.h>
#include <stdbool.h>
#include <ode/ode.h>
@ -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);

View File

@ -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 <stdlib.h>
#include <string.h>
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) {