Create uncached glyphs on demand instead of checking glyph availability upfront.

This safes a HasGlyphs() call which would convert the whole string to glyph
codes and look each of the glyphs up in the cache entry, just to do the same
again during the loop where they are actually used. Instead we now simply switch
to the write lock and look up the fallback entry when hitting the first uncached
glyph. This benefits the normal case of having all glyphs cached without any
drawbacks.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40186 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2011-01-09 21:24:09 +00:00
parent 27997edca9
commit 427427bc82
4 changed files with 86 additions and 49 deletions

View File

@ -256,7 +256,15 @@ render_as_zero_width(uint32 glyphCode)
const GlyphCache*
FontCacheEntry::Glyph(uint32 glyphCode, FontCacheEntry* fallbackEntry)
FontCacheEntry::CachedGlyph(uint32 glyphCode)
{
// Only requires a read lock.
return fGlyphCache->FindGlyph(glyphCode);
}
const GlyphCache*
FontCacheEntry::CreateGlyph(uint32 glyphCode, FontCacheEntry* fallbackEntry)
{
// We cache the glyph by the requested glyphCode. The FontEngine of this
// FontCacheEntry may not contain a glyph for the given code, in which case

View File

@ -103,7 +103,8 @@ class FontCacheEntry : public MultiLocker, public BReferenceable {
bool HasGlyphs(const char* utf8String,
ssize_t glyphCount) const;
const GlyphCache* Glyph(uint32 glyphCode,
const GlyphCache* CachedGlyph(uint32 glyphCode);
const GlyphCache* CreateGlyph(uint32 glyphCode,
FontCacheEntry* fallbackEntry = NULL);
void InitAdaptors(const GlyphCache* glyph,

View File

@ -97,6 +97,14 @@ public:
FontCacheReference* cacheReference = NULL);
private:
static bool _WriteLockAndAcquireFallbackEntry(
FontCacheReference& cacheReference,
FontCacheEntry* entry,
const ServerFont& font,
const char* utf8String, int32 length,
FontCacheReference& fallbackCacheReference,
FontCacheEntry*& fallbackEntry);
GlyphLayoutEngine();
virtual ~GlyphLayoutEngine();
};
@ -189,52 +197,6 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
if (entry == NULL)
return false;
// See if the entry already has the glyphs cached. Switch to a write
// lock if not.
bool needsWriteLock = !entry->HasGlyphs(utf8String, length);
if (needsWriteLock) {
// This also means we need the fallback font, since potentially,
// we have to obtain missing glyphs from it. We need to obtain
// the fallback font while we have not locked anything, since
// locking the FontManager with the write-lock held can obvisouly
// lead to a deadlock.
cacheReference.SetTo(NULL, false);
entry->ReadUnlock();
if (gFontManager->Lock()) {
// TODO: We always get the fallback glyphs from VL Gothic at the
// moment, but of course the fallback font should a) contain the
// missing glyphs at all and b) be similar to the original font.
// So there should be a mapping of some kind to know the most
// suitable fallback font.
FontStyle* fallbackStyle = gFontManager->GetStyleByIndex(
"VL Gothic", 0);
if (fallbackStyle != NULL) {
ServerFont fallbackFont(*fallbackStyle, font.Size());
gFontManager->Unlock();
// Force the write-lock on the fallback entry, since we
// don't transfer or copy GlyphCache objects from one cache
// to the other, but create new glyphs which are stored in
// "entry" in any case, which requires the write cache for
// sure (used FontEngine of fallbackEntry).
fallbackEntry = FontCacheEntryFor(fallbackFont, entry,
utf8String, length, fallbackCacheReference, true);
// NOTE: We don't care if fallbackEntry is NULL, fetching
// alternate glyphs will simply not work.
} else
gFontManager->Unlock();
}
if (!entry->WriteLock()) {
FontCache::Default()->Recycle(entry);
return false;
}
// Update the FontCacheReference, since the locking kind changed.
cacheReference.SetTo(entry, needsWriteLock);
}
} // else the entry was already used and is still locked
consumer.Start();
@ -252,6 +214,7 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
uint32 lastCharCode = 0;
uint32 charCode;
int32 index = 0;
bool writeLocked = false;
const char* start = utf8String;
while ((charCode = UTF8ToCharCode(&utf8String))) {
@ -273,7 +236,22 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
x += IsWhiteSpace(charCode) ? delta->space : delta->nonspace;
}
const GlyphCache* glyph = entry->Glyph(charCode, fallbackEntry);
const GlyphCache* glyph = entry->CachedGlyph(charCode);
if (glyph == NULL) {
// The glyph has not been cached yet, switch to a write lock,
// acquire the fallback entry and create the glyph. Note that
// the write lock will persist (in the cacheReference) so that
// we only have to do this switch once for the whole string.
if (!writeLocked) {
writeLocked = _WriteLockAndAcquireFallbackEntry(cacheReference,
entry, font, utf8String, length, fallbackCacheReference,
fallbackEntry);
}
if (writeLocked)
glyph = entry->CreateGlyph(charCode, fallbackEntry);
}
if (glyph == NULL) {
consumer.ConsumeEmptyGlyph(index++, charCode, x, y);
advanceX = 0;
@ -312,4 +290,53 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
}
inline bool
GlyphLayoutEngine::_WriteLockAndAcquireFallbackEntry(
FontCacheReference& cacheReference, FontCacheEntry* entry,
const ServerFont& font, const char* utf8String, int32 length,
FontCacheReference& fallbackCacheReference, FontCacheEntry*& fallbackEntry)
{
// We need the fallback font, since potentially, we have to obtain missing
// glyphs from it. We need to obtain the fallback font while we have not
// locked anything, since locking the FontManager with the write-lock held
// can obvisouly lead to a deadlock.
cacheReference.SetTo(NULL, false);
entry->ReadUnlock();
if (gFontManager->Lock()) {
// TODO: We always get the fallback glyphs from VL Gothic at the
// moment, but of course the fallback font should a) contain the
// missing glyphs at all and b) be similar to the original font.
// So there should be a mapping of some kind to know the most
// suitable fallback font.
FontStyle* fallbackStyle = gFontManager->GetStyleByIndex(
"VL Gothic", 0);
if (fallbackStyle != NULL) {
ServerFont fallbackFont(*fallbackStyle, font.Size());
gFontManager->Unlock();
// Force the write-lock on the fallback entry, since we
// don't transfer or copy GlyphCache objects from one cache
// to the other, but create new glyphs which are stored in
// "entry" in any case, which requires the write cache for
// sure (used FontEngine of fallbackEntry).
fallbackEntry = FontCacheEntryFor(fallbackFont, entry,
utf8String, length, fallbackCacheReference, true);
// NOTE: We don't care if fallbackEntry is NULL, fetching
// alternate glyphs will simply not work.
} else
gFontManager->Unlock();
}
if (!entry->WriteLock()) {
FontCache::Default()->Recycle(entry);
return false;
}
// Update the FontCacheReference, since the locking kind changed.
cacheReference.SetTo(entry, true);
return true;
}
#endif // GLYPH_LAYOUT_ENGINE_H

View File

@ -146,6 +146,7 @@ public:
fRenderer.fRasterizer.reset();
fRenderer.fSubpixRasterizer.reset();
}
void Finish(double x, double y)
{
if (fVector) {