From ec39c408e9d60f6282036b8cf0f46522d5db8ff4 Mon Sep 17 00:00:00 2001 From: HW Date: Sat, 19 May 2012 00:35:09 +0200 Subject: [PATCH] add new generic global cache --- cache.lua | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++ rendertext.lua | 62 ++++++++++++-------------------------- 2 files changed, 102 insertions(+), 42 deletions(-) create mode 100644 cache.lua diff --git a/cache.lua b/cache.lua new file mode 100644 index 000000000..88e922a6a --- /dev/null +++ b/cache.lua @@ -0,0 +1,82 @@ +--[[ +Inheritable abstraction for cache items +]]-- +CacheItem = { + size = 64, -- some reasonable default for simple Lua values / small tables +} + +function CacheItem:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o +end + +function CacheItem:onFree() +end + +--[[ +A global LRU cache +]]-- +Cache = { + -- cache configuration: + max_memsize = 1024*1024*5, -- 5MB cache size + -- cache state: + current_memsize = 0, + -- associative cache + cache = {}, + -- this will hold the LRU order of the cache + cache_order = {} +} + +function Cache:insert(key, object) + -- guarantee that we have enough memory in cache + if(object.size > self.max_memsize) then + -- we're not allowed to claim this much at all + error("too much memory claimed") + end + -- delete objects that least recently used + -- (they are at the end of the cache_order array) + while self.current_memsize + object.size > self.max_memsize do + local removed_key = table.remove(self.cache_order) + self.current_memsize = self.current_memsize - self.cache[removed_key].size + self.cache[removed_key]:onFree() + self.cache[removed_key] = nil + end + -- insert new object in front of the LRU order + table.insert(self.cache_order, 1, key) + self.cache[key] = object + self.current_memsize = self.current_memsize + object.size +end + +function Cache:check(key) + if self.cache[key] then + if self.cache_order[1] ~= key then + -- put key in front of the LRU list + for k, v in ipairs(self.cache_order) do + if v == key then + table.remove(self.cache_order, k) + end + end + table.insert(self.cache_order, 1, key) + end + return self.cache[key] + end +end + +function Cache:willAccept(size) + -- we only allow single objects to fill 75% of the cache + if size*4 < self.max_memsize*3 then + return true + end +end + +-- blank the cache +function Cache:clear() + for k, _ in pairs(self.cache) do + self.cache[k]:onFree() + end + self.cache = {} + self.cache_order = {} + self.current_memsize = 0 +end diff --git a/rendertext.lua b/rendertext.lua index 2e70c3bf4..32eaba389 100644 --- a/rendertext.lua +++ b/rendertext.lua @@ -1,47 +1,25 @@ -glyphcache_max_memsize = 256*1024 -- 256kB glyphcache -glyphcache_current_memsize = 0 -glyphcache = {} -glyphcache_max_age = 4096 -function glyphCacheClaim(size) - if(size > glyphcache_max_memsize) then - error("too much memory claimed") - return false - end - while glyphcache_current_memsize + size > glyphcache_max_memsize do - for k, _ in pairs(glyphcache) do - if glyphcache[k].age > 0 then - glyphcache[k].age = glyphcache[k].age - 1 - else - glyphcache_current_memsize = glyphcache_current_memsize - glyphcache[k].size - glyphcache[k].glyph.bb:free() - glyphcache[k] = nil - end - end - end - glyphcache_current_memsize = glyphcache_current_memsize + size - return true -end +require "cache" + +--[[ +TODO: all these functions should probably be methods on Face objects +]]-- + function getGlyph(face, charcode) - local hash = glyphCacheHash(face.hash, charcode) - if glyphcache[hash] == nil then - local glyph = face.ftface:renderGlyph(charcode) - local size = glyph.bb:getWidth() * glyph.bb:getHeight() / 2 + 32 - glyphCacheClaim(size); - glyphcache[hash] = { - age = glyphcache_max_age, - size = size, - g = glyph - } - else - glyphcache[hash].age = glyphcache_max_age + local hash = "glyph|"..face.hash.."|"..charcode + local glyph = Cache:check(hash) + if glyph then + -- cache hit + return glyph[1] end - return glyphcache[hash].g -end -function glyphCacheHash(face, charcode) - return face..'_'..charcode; -end -function clearGlyphCache() - glyphcache = {} + local rendered_glyph = face.ftface:renderGlyph(charcode) + if not rendered_glyph then + debug("error rendering glyph (charcode=", charcode, ") for face", face) + return + end + glyph = CacheItem:new{rendered_glyph} + glyph.size = glyph[1].bb:getWidth() * glyph[1].bb:getHeight() / 2 + 32 + Cache:insert(hash, glyph) + return glyph[1] end function getSubTextByWidth(text, face, width, kerning)