[haiku-commits] r38154 - haiku/trunk/src/servers/app

  • From: superstippi@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 16 Aug 2010 21:24:20 +0200 (CEST)

Author: stippi
Date: 2010-08-16 21:24:20 +0200 (Mon, 16 Aug 2010)
New Revision: 38154
Changeset: http://dev.haiku-os.org/changeset/38154

Modified:
   haiku/trunk/src/servers/app/GlyphLayoutEngine.h
Log:
Rewrote the fake font overlay support, as the previous implementation
had several problems: First of all it duplicated the FontCacheEntry
retrieval and locking code. Second it was really slow to do the whole
lookup for every single glyph. Third is used the FontManager without
locking, which could result to app_server crashes, mostly at startup,
while the FontManager thread was scanning the fonts and the glyph
layout was already using it. Forth it dereferenced a FontStyle
pointer without checking it against NULL. And lastly it didn't use
the font size of the original font for the fallback font.

The new algorithm addresses not only these, but also puts the glyphs
which are unsupported by a font and retrieved from the alternative
font, into the FontCacheEntry of the original font, so that the
HasGlyphs() check succeeds the next time around and does not need to
use the fallback algorithm anymore. There is also less manual management
of locking and FontCacheEntry recycling, this is now taking care of by
FontCacheReference objects on the stack.


Modified: haiku/trunk/src/servers/app/GlyphLayoutEngine.h
===================================================================
--- haiku/trunk/src/servers/app/GlyphLayoutEngine.h     2010-08-16 19:17:11 UTC 
(rev 38153)
+++ haiku/trunk/src/servers/app/GlyphLayoutEngine.h     2010-08-16 19:24:20 UTC 
(rev 38154)
@@ -19,44 +19,70 @@
 #include <ctype.h>
 
 class FontCacheReference {
- public:
-                                                               
FontCacheReference()
-                                                                       : 
fCacheEntry(NULL)
-                                                                       , 
fWriteLocked(false)
-                                                               {
-                                                               }
-                                                               
~FontCacheReference()
-                                                               {
-                                                                       if 
(fCacheEntry) {
-                                                                               
if (fWriteLocked)
-                                                                               
        fCacheEntry->WriteUnlock();
-                                                                               
else
-                                                                               
        fCacheEntry->ReadUnlock();
-                                                                       
-                                                                               
FontCache::Default()->Recycle(
-                                                                               
        fCacheEntry);
-                                                                       }
-                                                               }
-                       void                            SetTo(FontCacheEntry* 
entry,
-                                                                       bool 
writeLocked)
-                                                               {
-                                                                       
fCacheEntry = entry;
-                                                                       
fWriteLocked = writeLocked;
-                                                               }
-       inline  FontCacheEntry*         Entry() const
-                                                                       { 
return fCacheEntry; }
-       inline  bool                            WriteLocked() const
-                                                                       { 
return fWriteLocked; }
+public:
+       FontCacheReference()
+               :
+               fCacheEntry(NULL),
+               fWriteLocked(false)
+       {
+       }
 
- private:
+       ~FontCacheReference()
+       {
+               Unset();
+       }
+
+       void SetTo(FontCacheEntry* entry, bool writeLocked)
+       {
+               // NOTE: If the semantics are changed such
+               // that the reference to a previous entry
+               // is properly released, then don't forget
+               // to adapt existing which transfers
+               // responsibility of entries between
+               // references!
+               fCacheEntry = entry;
+               fWriteLocked = writeLocked;
+       }
+
+       void Unset()
+       {
+               if (fCacheEntry == NULL)
+                       return;
+
+               if (fWriteLocked)
+                       fCacheEntry->WriteUnlock();
+               else
+                       fCacheEntry->ReadUnlock();
+       
+               FontCache::Default()->Recycle(fCacheEntry);
+       }
+
+       inline FontCacheEntry* Entry() const
+       {
+               return fCacheEntry;
+       }
+
+       inline bool WriteLocked() const
+       {
+               return fWriteLocked;
+       }
+
+private:
                        FontCacheEntry*         fCacheEntry;
                        bool                            fWriteLocked;
 };
 
 
 class GlyphLayoutEngine {
- public:
+public:
+       static  bool                            IsWhiteSpace(uint32 glyphCode);
 
+       static  FontCacheEntry*         FontCacheEntryFor(const ServerFont& 
font,
+                                                                       const 
FontCacheEntry* disallowedEntry,
+                                                                       const 
char* utf8String, int32 length,
+                                                                       
FontCacheReference& cacheReference,
+                                                                       bool 
needsWriteLock);
+
                        template<class GlyphConsumer>
        static  bool                            LayoutGlyphs(GlyphConsumer& 
consumer,
                                                                        const 
ServerFont& font,
@@ -68,15 +94,12 @@
                                                                        const 
BPoint* offsets = NULL,
                                                                        
FontCacheReference* cacheReference = NULL);
 
-       static  bool                            IsWhiteSpace(uint32 glyphCode);
-
- private:
+private:
                                                                
GlyphLayoutEngine();
        virtual                                         ~GlyphLayoutEngine();
 };
 
 
-// IsWhiteSpace
 inline bool
 GlyphLayoutEngine::IsWhiteSpace(uint32 charCode)
 {
@@ -97,42 +120,118 @@
 }
 
 
-// LayoutGlyphs
+inline FontCacheEntry*
+GlyphLayoutEngine::FontCacheEntryFor(const ServerFont& font,
+       const FontCacheEntry* disallowedEntry, const char* utf8String, int32 
length,
+       FontCacheReference& cacheReference, bool needsWriteLock)
+{
+       ASSERT(cacheReference.Entry() == NULL);
+
+       FontCache* cache = FontCache::Default();
+       FontCacheEntry* entry = cache->FontCacheEntryFor(font);
+       if (entry == NULL)
+               return NULL;
+
+       if (entry == disallowedEntry) {
+               cache->Recycle(entry);
+               return NULL;
+       }
+
+       if (needsWriteLock) {
+               if (!entry->WriteLock()) {
+                       cache->Recycle(entry);
+                       return NULL;
+               }
+       } else {
+               if (!entry->ReadLock()) {
+                       cache->Recycle(entry);
+                       return NULL;
+               }
+       }
+
+       // At this point, we have a valid FontCacheEntry and it is locked in the
+       // proper mode. We can setup the FontCacheReference so it takes care of
+       // the locking and recycling from now and return the entry.
+       cacheReference.SetTo(entry, needsWriteLock);
+       return entry;
+}
+
+
 template<class GlyphConsumer>
 inline bool
 GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer,
        const ServerFont& font,
        const char* utf8String, int32 length,
        const escapement_delta* delta, bool kerning, uint8 spacing,
-       const BPoint* offsets, FontCacheReference* cacheReference)
+       const BPoint* offsets, FontCacheReference* _cacheReference)
 {
        // TODO: implement spacing modes
-
        FontCacheEntry* entry = NULL;
+       FontCacheReference cacheReference;
        FontCacheEntry* fallbackEntry = NULL;
-       bool needsWriteLock = false;
-       if (cacheReference) {
-               entry = cacheReference->Entry();
-               needsWriteLock = cacheReference->WriteLocked();
+       FontCacheReference fallbackCacheReference;
+       if (_cacheReference != NULL) {
+               entry = _cacheReference->Entry();
+               // When there is already a cacheReference, it means there was 
already
+               // an iteration over the glyphs. The use-case is for example to 
do
+               // a layout pass to get the string width for the bounding box, 
then a
+               // second layout pass to actually render the glyphs to the 
screen.
+               // This means that the fallback entry mechanism will not do any 
good
+               // for the second pass, since the fallback glyphs have been 
stored in
+               // the original entry.
        }
 
-       if (!entry) {
-               FontCache* cache = FontCache::Default();
-               entry = cache->FontCacheEntryFor(font);
-       
-               if (!entry || !entry->ReadLock()) {
-                       cache->Recycle(entry);
+       if (entry == NULL) {
+               entry = FontCacheEntryFor(font, NULL, utf8String, length,
+                       cacheReference, false);
+
+               if (entry == NULL)
                        return false;
-               }
-       
-               needsWriteLock = !entry->HasGlyphs(utf8String, length);
-       
+
+               // 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()) {
-                               cache->Recycle(entry);
-                               return false;
+                               FontCache::Default()->Recycle(entry);
+                               return NULL;
                        }
+
+                       // Update the FontCacheReference, since the locking 
kind changed.
+                       cacheReference.SetTo(entry, needsWriteLock);
                }
        } // else the entry was already used and is still locked
 
@@ -172,68 +271,18 @@
                                x += IsWhiteSpace(charCode) ? delta->space : 
delta->nonspace;
                }
 
-               const GlyphCache* glyph = entry->Glyph(charCode);
+               const GlyphCache* glyph = entry->Glyph(charCode, fallbackEntry);
                if (glyph == NULL) {
-                       // Try to find a suitable glyph in another font
-                       FontCache* cache = FontCache::Default();
-                       bool needsWriteLock = false;
-                       ServerFont 
fallbackFont(*(gFontManager->GetStyleByIndex("VL Gothic",
-                               0)));
-                               // We always try to get the glyph from VL 
Gothic, so we can
-                               // display japanese characters. Other scripts 
(indian, ...)
-                               // should be handled too, perhaps with a 
charcode > font
-                               // mapping.
-                               // TODO : the font should not be hardcoded, but 
somehow derived
-                               // from the one that missed a glyph.
-                       bool consumed = true;
-                       fallbackEntry = cache->FontCacheEntryFor(fallbackFont);
-                       if (fallbackEntry != entry)
-                       {
-                               if (!fallbackEntry || 
!fallbackEntry->ReadLock()) {
-                                       cache->Recycle(fallbackEntry);
-                                       continue;
-                               }
-
-                               needsWriteLock = 
!fallbackEntry->HasGlyphs(utf8String, length);
-
-                               if (needsWriteLock) {
-                                       fallbackEntry->ReadUnlock();
-                                       if (!fallbackEntry->WriteLock()) {
-                                               cache->Recycle(fallbackEntry);
-                                               continue;
-                                       }
-                               }
-
-                               glyph = fallbackEntry->Glyph(charCode);
-                               if (glyph != NULL && 
!consumer.ConsumeGlyph(index, charCode,
-                                               glyph, fallbackEntry, x, y)) {
-                                       advanceX = 0;
-                                       advanceY = 0;
-                                       consumed = false;
-                               }
-
-                               if (needsWriteLock)
-                                       fallbackEntry->WriteUnlock();
-                               else
-                                       fallbackEntry->ReadUnlock();
+                       consumer.ConsumeEmptyGlyph(index, charCode, x, y);
+                       continue;
+               } else {
+                       if (!consumer.ConsumeGlyph(index, charCode, glyph, 
entry, x, y)) {
+                               advanceX = 0;
+                               advanceY = 0;
+                               break;
                        }
-                       FontCache::Default()->Recycle(fallbackEntry);
-
-                       if (glyph == NULL) {
-                               consumer.ConsumeEmptyGlyph(index, charCode, x, 
y);
-                               continue;
-                       }
-
-                       if (!consumed)
-                               break;
                }
 
-               if (!consumer.ConsumeGlyph(index, charCode, glyph, entry, x, 
y)) {
-                       advanceX = 0;
-                       advanceY = 0;
-                       break;
-               }
-
                // get next increment for pen position
                advanceX = glyph->advance_x;
                advanceY = glyph->advance_y;
@@ -248,17 +297,15 @@
        y += advanceY;
        consumer.Finish(x, y);
 
-       if (!cacheReference) {
-               if (needsWriteLock)
-                       entry->WriteUnlock();
-               else
-                       entry->ReadUnlock();
-       
-               FontCache::Default()->Recycle(entry);
-       } else {
-               // the reference will take care of locking
-               if (!cacheReference->Entry())
-                       cacheReference->SetTo(entry, needsWriteLock);
+       if (_cacheReference != NULL && _cacheReference->Entry() == NULL) {
+               // The caller passed a FontCacheReference, but this is the first
+               // iteration -> switch the ownership from the stack allocated
+               // FontCacheReference to the one passed by the caller. The 
fallback
+               // FontCacheReference is not affected by this. Eventually, the 
fallback
+               // glyphs shall be cached in the original FontCacheEntry, so 
that it is
+               // not even used in this situation.
+               _cacheReference->SetTo(entry, cacheReference.WriteLocked());
+               cacheReference.SetTo(NULL, false);
        }
        return true;
 }


Other related posts: