better hash and fix cache

master
Alessandro Mauri 1 year ago
parent a65662d0fc
commit 0e13fd34fb
  1. 5
      text_rendering/font.c
  2. 47
      text_rendering/generic_cache.h
  3. 106
      text_rendering/generic_hash.h
  4. 40
      text_rendering/main.c
  5. 4
      text_rendering/ren.c
  6. 2
      text_rendering/ren.h

@ -11,7 +11,6 @@
#include "stb_image_write.h" #include "stb_image_write.h"
#include "util.h" #include "util.h"
// generic cache type
#include "generic_cache.h" #include "generic_cache.h"
static inline unsigned int hash(unsigned int code) static inline unsigned int hash(unsigned int code)
{ {
@ -106,8 +105,8 @@ int font_free(struct font_atlas *atlas)
} }
// FIXME: when generating the sdf I only use the height, so to not encounter memory // TODO: time and take the median of the time it takes to generate the cache and
// errors height and width must be equal // the time it takes to draw the glyph
const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code, int *updated) const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code, int *updated)
{ {
int _u = 0; int _u = 0;

@ -22,55 +22,53 @@
} }
#define CACHE_DECL(cachename, type, hashfn, cmpfn) \ #define CACHE_DECL(name, type, hashfn, cmpfn) \
HASH_DECL(cachename##table, unsigned int, void *, hashfn, cmpfn) \ HASH_DECL(name##table, uint32_t, void *, hashfn, cmpfn) \
struct cachename { \ struct name { \
struct cachename##table_ref *table; \ struct name##table_ref *table; \
type *array; \ type *array; \
uint64_t *bitmap; \ uint64_t *bitmap; \
int cycles; \ int cycles; \
}; \ }; \
\ \
\ \
struct cachename cachename##_init(void) \ struct name name##_init(void) \
{ \ { \
struct cachename##table_ref *t = cachename##table_create(CACHE_SIZE); \ struct name##table_ref *t = name##table_create(CACHE_SIZE); \
type *a = malloc(sizeof(type)*CACHE_SIZE); \ type *a = malloc(sizeof(type)*CACHE_SIZE); \
uint64_t *b = malloc(sizeof(uint64_t)*CACHE_BSIZE); \ uint64_t *b = malloc(sizeof(uint64_t)*CACHE_BSIZE); \
CACHE_BRESET(b); \ CACHE_BRESET(b); \
return (struct cachename){ .table = t, .array = a, .bitmap = b, 0}; \ return (struct name){ .table = t, .array = a, .bitmap = b, 0}; \
} \ } \
\ \
\ \
void cachename##_free(struct cachename *cache) \ void name##_free(struct name *cache) \
{ \ { \
if (cache) { \ if (cache) { \
cachename##table_destroy(cache->table); \ name##table_destroy(cache->table); \
free(cache->array); \ free(cache->array); \
free(cache->bitmap); \ free(cache->bitmap); \
} \ } \
} \ } \
\ \
\ \
const type * cachename##_search(struct cachename *cache, unsigned int code) \ const type * name##_search(struct name *cache, uint32_t code) \
{ \ { \
if (!cache) \ if (!cache) return NULL; \
return NULL; \ struct name##table_entry *r; \
struct cachename##table_entry *r; \ r = name##table_search(cache->table, code); \
r = cachename##table_search(cache->table, code); \
/* MISS */ \ /* MISS */ \
if (!r) \ if (!r || !cmpfn(code, r->code)) \
return NULL; \ return NULL; \
/* HIT */ \ /* HIT, set as recently used */ \
CACHE_SET(cache, (type *)(r->data)-cache->array); \ CACHE_SET(cache, (type *)(r->data)-(cache->array)); \
return (const type *)(r->data); \ return (const type *)(r->data); \
} \ } \
\ \
\ \
int cachename##_get(struct cachename *cache) \ int name##_get(struct name *cache) \
{ \ { \
if (!cache) \ if (!cache) return -1; \
return -1; \
int x = 0; \ int x = 0; \
for (int b = 0; b < CACHE_BSIZE; b++) { \ for (int b = 0; b < CACHE_BSIZE; b++) { \
if (cache->bitmap[b] == 0) x = 64; \ if (cache->bitmap[b] == 0) x = 64; \
@ -83,10 +81,9 @@ int cachename##_get(struct cachename *cache) \
} \ } \
\ \
\ \
const type * cachename##_insert(struct cachename *cache, const type *g, unsigned int code, int x) \ const type * name##_insert(struct name *cache, const type *g, uint32_t code, int x)\
{ \ { \
if (!cache) \ if (!cache) return NULL; \
return NULL; \
type *spot = NULL; \ type *spot = NULL; \
/* x is the index to the cache array, it has to come from the user */ \ /* x is the index to the cache array, it has to come from the user */ \
/* check if the spot is free */ \ /* check if the spot is free */ \
@ -95,8 +92,8 @@ const type * cachename##_insert(struct cachename *cache, const type *g, unsigned
CACHE_SET(cache, x) \ CACHE_SET(cache, x) \
spot = &(cache->array[x]); \ spot = &(cache->array[x]); \
*spot = *g; \ *spot = *g; \
struct cachename##table_entry e = { .code = code, .data = spot}; \ struct name##table_entry e = { .code = code, .data = spot}; \
if (!cachename##table_insert(cache->table, &e)) \ if (!name##table_insert(cache->table, &e)) \
return NULL; \ return NULL; \
return spot; \ return spot; \
} \ } \

@ -3,35 +3,36 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#define HASH_MAXSIZE 4096 #define HASH_MAXSIZE 4096
// for fibonacci hashing, 2^{32,64}/<golden ratio> // for fibonacci hashing, 2^{32,64}/<golden ratio>
#define HASH_RATIO32 (unsigned int)2654435769u #define HASH_RATIO32 ((uint64_t)2654435769u)
#define HASH_RATIO64 (unsigned long long int)11400714819322457583u #define HASH_RATIO64 ((uint64_t)11400714819322457583u)
// salt for string hashing // salt for string hashing
#define HASH_STRSALT 0xbabb0cac #define HASH_SALT ((uint64_t)0xbabb0cac)
/* Ready-made compares */ /* Ready-made compares */
static inline int hash_cmp_u32(unsigned int a, unsigned int b) { return a == b; } static inline int hash_cmp_u32(uint32_t a, uint32_t b) { return a == b; }
static inline int hash_cmp_u64(unsigned long long int a, unsigned long long int b) { return a == b; } static inline int hash_cmp_u64(uint64_t a, uint64_t b) { return a == b; }
static inline int hash_cmp_str(const char *a, const char *b) { return strcmp(a, b) == 0; } static inline int hash_cmp_str(const char *a, const char *b) { return strcmp(a, b) == 0; }
/* Ready-made hashes */ /* Ready-made hashes */
static inline unsigned int hash_u64(unsigned long long int c) static inline uint32_t hash_u64(uint64_t c)
{ {
return (unsigned long long int)((unsigned long long int)(c*HASH_RATIO64)>>32); return (uint64_t)((((uint64_t)c+HASH_SALT)*HASH_RATIO64)>>32);
} }
static inline unsigned int hash_u32(unsigned int c) static inline uint32_t hash_u32(uint32_t c)
{ {
return (unsigned int)((unsigned long long int)(c*HASH_RATIO32)>>32); return (uint32_t)((((uint64_t)c<<31)*HASH_RATIO64)>>32);
} }
static inline unsigned int hash_str(const char *s) static inline uint32_t hash_str(const char *s)
{ {
unsigned int h = HASH_STRSALT; uint32_t h = HASH_SALT;
const unsigned char *v = (const unsigned char *)(s); const uint8_t *v = (const uint8_t *)(s);
for (int x = *s; x; x--) { for (int x = *s; x; x--) {
h += v[x-1]; h += v[x-1];
h += h << 10; h += h << 10;
@ -44,74 +45,75 @@ static inline unsigned int hash_str(const char *s)
} }
#define HASH_DECL(hashname, codetype, datatype, hashfn, cmpfn) \ #define HASH_DECL(htname, codetype, datatype, hashfn, cmpfn) \
struct hashname##_entry { \ struct htname##_entry { \
codetype code; \ codetype code; \
datatype data; \ datatype data; \
}; \ }; \
\ \
struct hashname##_ref { \ struct htname##_ref { \
unsigned int items, size; \ uint32_t items, size, exp; \
struct hashname##_entry bucket[]; \ struct htname##_entry bucket[]; \
}; \ }; \
\ \
\ \
struct hashname##_ref * hashname##_create(unsigned int size) \ struct htname##_ref * htname##_create(uint32_t size) \
{ \ { \
if (!size || size > HASH_MAXSIZE) \ if (!size || size > HASH_MAXSIZE) \
return NULL; \ return NULL; \
/* round to the greater power of two */ \ /* round to the greater power of two */ \
/* FIXME: check for intger overflow here */ \ /* FIXME: check for intger overflow here */ \
size = 1<<__builtin_clz(size); \ uint32_t exp = 32-__builtin_clz(size-1); \
size = 1<<(exp); \
/* FIXME: check for intger overflow here */ \ /* FIXME: check for intger overflow here */ \
struct hashname##_ref *ht = malloc(sizeof(struct hashname##_ref)+sizeof(struct hashname##_entry)*size); \ struct htname##_ref *ht = malloc(sizeof(struct htname##_ref)+sizeof(struct htname##_entry)*size); \
if (ht) { \ if (ht) { \
ht->items = 0; \ ht->items = 0; \
ht->size = size; \ ht->size = size; \
memset(ht->bucket, 0, sizeof(struct hashname##_entry)*size); \ ht->exp = exp; \
memset(ht->bucket, 0, sizeof(struct htname##_entry)*size); \
} \ } \
return ht; \ return ht; \
} \ } \
\ \
\ \
void hashname##_destroy(struct hashname##_ref *ht) \ void htname##_destroy(struct htname##_ref *ht) \
{ \ { \
if (ht) \ if (ht) free(ht); \
free(ht); \
} \ } \
\ \
\ \
static struct hashname##_entry * hashname##lookup(struct hashname##_ref *ht, codetype code) \ static uint32_t htname##_lookup(struct htname##_ref *ht, uint32_t hash, uint32_t idx) \
{ \ { \
if (!ht) \ if (!ht) return 0; \
return NULL; \ uint32_t mask = ht->size-1; \
/* fast modulo operation for power-of-2 size */ \ uint32_t step = (hash >> (32 - ht->exp)) | 1; \
unsigned int mask = ht->size - 1; \ return (idx + step) & mask; \
unsigned int i = hashfn(code); \
for (unsigned int j = 1; ; i += j++) { \
if (!ht->bucket[i&mask].code || cmpfn(ht->bucket[i].code, code)) \
return &(ht->bucket[i&mask]); \
} \
return NULL; \
} \ } \
\ \
\ \
/* Find and return the element by code */ \ /* Find and return the element by code */ \
struct hashname##_entry * hashname##_search(struct hashname##_ref *ht, codetype code) \ struct htname##_entry * htname##_search(struct htname##_ref *ht, codetype code)\
{ \ { \
if (!ht) \ if (!ht) return NULL; \
return NULL; \ uint32_t h = hashfn(code); \
struct hashname##_entry *r = hashname##lookup(ht, code); \ for (uint32_t i=h, x=0; ; x++) { \
if (r && cmpfn(r->code, code)) \ i = htname##_lookup(ht, h, i); \
return r; \ if (x > (ht->size<<1) || \
!ht->bucket[i].code || \
cmpfn(ht->bucket[i].code, code) \
) { \
return &(ht->bucket[i]); \
} \
} \
return NULL; \ return NULL; \
} \ } \
\ \
\ \
/* FIXME: this simply overrides the found item */ \ /* FIXME: this simply overrides the found item */ \
struct hashname##_entry * hashname##_insert(struct hashname##_ref *ht, struct hashname##_entry *entry) \ struct htname##_entry * htname##_insert(struct htname##_ref *ht, struct htname##_entry *entry) \
{ \ { \
struct hashname##_entry *r = hashname##lookup(ht, entry->code); \ struct htname##_entry *r = htname##_search(ht, entry->code); \
if (r) { \ if (r) { \
if (!r->code) \ if (!r->code) \
ht->items++; \ ht->items++; \
@ -121,24 +123,26 @@ struct hashname##_entry * hashname##_insert(struct hashname##_ref *ht, struct ha
} \ } \
\ \
\ \
#if 0
/* returns the number of removed items */ \ /* returns the number of removed items */ \
int hashname##_remove(struct hashname##_ref *ht, codetype code) \ int htname##_remove(struct htname##_ref *ht, codetype code) \
{ \ { \
if (!ht) \ if (!ht) \
return -1; \ return -1; \
unsigned int mask = ht->size - 1; \ uint32_t mask = ht->size - 1; \
unsigned int s = hashfn(code)&mask, inc = 0; \ uint32_t s = hashfn(code)&mask, inc = 0; \
struct hashname##_entry *r; \ struct htname##_entry *r; \
/* Flag for removal */ \ /* Flag for removal */ \
while (ht->items > 0 && (r = hashname##lookup(ht, code)) && r->code) { \ while (ht->items > 0 && (r = htname##lookup(ht, code)) && r->code) { \
/* FIXME: this cast may not work */ \ /* FIXME: this cast may not work */ \
r->code = (codetype)(-1); \ r->code = (codetype)(-1); \
ht->items--; \ ht->items--; \
} \ } \
/* Remove */ \ /* Remove */ \
for (unsigned int i = s; i < ht->items; i++) { \ for (uint32_t i = s; i < ht->items; i++) { \
if (ht->bucket[i].code == (codetype)(-1)) { \ if (ht->bucket[i].code == (codetype)(-1)) { \
ht->bucket[i] = (struct hashname##_entry){0}; \ ht->bucket[i] = (struct htname##_entry){0}; \
inc++; \ inc++; \
} \ } \
} \ } \
@ -146,3 +150,5 @@ int hashname##_remove(struct hashname##_ref *ht, codetype code) \
} \ } \
#endif #endif
#endif

@ -2,6 +2,7 @@
#include <SDL2/SDL_events.h> #include <SDL2/SDL_events.h>
#include <SDL2/SDL_video.h> #include <SDL2/SDL_video.h>
#include <stdlib.h> #include <stdlib.h>
#include <locale.h>
#include <stdio.h> #include <stdio.h>
#include "ren.h" #include "ren.h"
@ -10,7 +11,12 @@
//const char *str1 = "Ciao Mamma!\nprova: òçà°ù§|¬³¼$£ì\t"; //const char *str1 = "Ciao Mamma!\nprova: òçà°ù§|¬³¼$£ì\t";
const char *str1 = "ciao\tmamma"; const char *str1 = "ciao\tmamma";
const char *str2 = "gmt"; const char *str2 = "The quick brown fox jumps over the lazy dog\n"
"чащах юга жил бы цитрус? Да, но фальшивый экземпляр!\n"
"Pijamalı hasta, yağız şoföre çabucak güvendi\n"
"Pchnąć w tę łódź jeża lub ośm skrzyń fig\n"
"イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン\n"
"Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa";
SDL_Window *win; SDL_Window *win;
@ -19,20 +25,31 @@ void draw(void)
static unsigned int frame = 0; static unsigned int frame = 0;
printf("frame: %d\n", frame++); printf("frame: %d\n", frame++);
ren_clear(); ren_clear();
ren_render_box(10, 10, 400, 50, 0xffff0000); //ren_render_box(10, 10, 400, 50, 0xffff0000);
if (ren_render_text(str1, 10, 10, 400, 50, 20)) //if (ren_render_text(str1, 10, 10, 400, 50, 20))
printf("text: %s\n", ren_strerror()); // printf("text: %s\n", ren_strerror());
int w, h; int w, h;
ren_get_text_box(str2, &w, &h, 40); ren_get_text_box(str2, &w, &h, 20);
printf("box for: %s -> (%d, %d)\n", str2, w, h); //printf("box for: %s -> (%d, %d)\n", str2, w, h);
ren_render_box(200, 40, w, h, 0xffff0000); ren_render_box(0, 0, w, h, 0xffff0000);
ren_render_text(str2, 200, 40, 300, 300, 40); ren_render_text(str2, 0, 0, w, h, 20);
// fixme: this causes a bug
const char *s = "ciao mamma";
ren_get_text_box(s, &w, &h, 12);
s = "stuff that was not in font size 12 -> чащаx";
ren_render_box(0, 200, w, h, 0xff0000ff);
if (ren_render_text(s, 0, 200, 0xffff, 0xffff, 12))
printf("BUG\n");
SDL_GL_SwapWindow(win); SDL_GL_SwapWindow(win);
} }
int main(void) int main(void)
{ {
setlocale(LC_ALL, "en_US.UTF-8");
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
@ -50,13 +67,6 @@ int main(void)
return 1; return 1;
} }
int w, h;
const char *s = "ciao mamma";
ren_get_text_box(s, &w, &h, 12);
printf("box for: %s -> (%d, %d)\n", s, w, h);
SDL_Event e; SDL_Event e;
while(1) { while(1) {
SDL_WaitEvent(&e); SDL_WaitEvent(&e);

@ -314,7 +314,7 @@ static int update_font_texture(int idx)
GL_RED, GL_RED,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
ren.fonts[idx].font->atlas)) ren.fonts[idx].font->atlas))
font_dump(ren.fonts[idx].font, "./atlas.png"); //font_dump(ren.fonts[idx].font, "./atlas.png");
GL(glUseProgram(0)); GL(glUseProgram(0));
return 0; return 0;
} }
@ -336,7 +336,7 @@ static int ren_load_font(int size, const char *path)
if (font_load(f, path, size)) if (font_load(f, path, size))
REN_RET(-1, REN_FONT) REN_RET(-1, REN_FONT)
font_dump(f, "./atlas.png"); //font_dump(f, "./atlas.png");
// load font texture (atlas) // load font texture (atlas)
ren.fonts[idx].texture = ren_texturer_rect( ren.fonts[idx].texture = ren_texturer_rect(

@ -4,7 +4,7 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#define DEFAULT_FONT "/usr/share/fonts/TTF/DejaVuSansMono.ttf" #define DEFAULT_FONT "/usr/share/fonts/TTF/FiraCode-Regular.ttf"
#define FONT_VERSHADER "./font_vertshader.glsl" #define FONT_VERSHADER "./font_vertshader.glsl"
#define FONT_FRAGSHADER "./font_fragshader.glsl" #define FONT_FRAGSHADER "./font_fragshader.glsl"
#define BOX_VERSHADER "./box_vertshader.glsl" #define BOX_VERSHADER "./box_vertshader.glsl"

Loading…
Cancel
Save