haiku/src/servers/app/FontCache.cpp

161 lines
3.7 KiB
C++
Raw Normal View History

/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "FontCache.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <Entry.h>
#include <Path.h>
#include "AutoLocker.h"
using std::nothrow;
FontCache
FontCache::sDefaultInstance;
// #pragma mark -
// constructor
FontCache::FontCache()
: MultiLocker("FontCache lock")
, fFontCacheEntries()
{
}
// destructor
FontCache::~FontCache()
{
FontMap::Iterator iterator = fFontCacheEntries.GetIterator();
while (iterator.HasNext())
iterator.Next().value->RemoveReference();
}
// Default
/*static*/ FontCache*
FontCache::Default()
{
return &sDefaultInstance;
}
* after my last changes to font rendering, it was about 15% slower than before (although there should be much less lock contention) * with this change, there is quite a bit of cleanup, text drawing is now about 20% faster than before the original changes to font caching, mostly due to turning off the kerning feature, which at the moment gives absolutely no benefit. The correct way of doing it might be to use kerning depending on the provided glyph spacing mode * removed fPenLocation from Painter, the usage should be more correct now, since it is now consistently applied before the coordinate transformation from view to screen (also for DrawShape() now, before any view scaling and origin offset) * Painter no longer has it's own instance of a ServerFont, instead it uses its AGGTextRenderer instance, which was per Painter again after the last change, and not global anymore, made _UpdateFont() useless * When using GlyphLayoutEngine, it supports a second pass with the same FontCacheEntry through the introduction of a FontCacheReference. This speeds up DrawString a little, since it needs to calculate the bounding box for the string, and then draw the string in a second pass. This is now done with only one FontCacheEntry lookup * I also tried to optimize the on-the-fly conversion of UTF8->CharCode away, since it was done four times per DrawString, but surprisingly, it proofed to be a slight slowdown. * introduced a shortcut in DrawingEngine::DrawString() which avoids calculating the bounding box, we are now a tiny bit faster to figure out that we don't need to draw any string than Dano In the test environment (drawing to offscreen bitmap and blitting to screen eventually), text rendering is now about 3.7 times _faster_ than Dano text rendering (directly to screen), for untransformed text. Unfortunately I cannot test on the same machine in accelerant using version of the test environment. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21822 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-08-04 15:37:16 +04:00
// FontCacheEntryFor
FontCacheEntry*
FontCache::FontCacheEntryFor(const ServerFont& font)
{
char signature[512];
FontCacheEntry::GenerateSignature(signature, font);
AutoReadLocker readLocker(this);
FontCacheEntry* entry = fFontCacheEntries.Get(signature);
if (entry) {
// the entry was already there
entry->AddReference();
* after my last changes to font rendering, it was about 15% slower than before (although there should be much less lock contention) * with this change, there is quite a bit of cleanup, text drawing is now about 20% faster than before the original changes to font caching, mostly due to turning off the kerning feature, which at the moment gives absolutely no benefit. The correct way of doing it might be to use kerning depending on the provided glyph spacing mode * removed fPenLocation from Painter, the usage should be more correct now, since it is now consistently applied before the coordinate transformation from view to screen (also for DrawShape() now, before any view scaling and origin offset) * Painter no longer has it's own instance of a ServerFont, instead it uses its AGGTextRenderer instance, which was per Painter again after the last change, and not global anymore, made _UpdateFont() useless * When using GlyphLayoutEngine, it supports a second pass with the same FontCacheEntry through the introduction of a FontCacheReference. This speeds up DrawString a little, since it needs to calculate the bounding box for the string, and then draw the string in a second pass. This is now done with only one FontCacheEntry lookup * I also tried to optimize the on-the-fly conversion of UTF8->CharCode away, since it was done four times per DrawString, but surprisingly, it proofed to be a slight slowdown. * introduced a shortcut in DrawingEngine::DrawString() which avoids calculating the bounding box, we are now a tiny bit faster to figure out that we don't need to draw any string than Dano In the test environment (drawing to offscreen bitmap and blitting to screen eventually), text rendering is now about 3.7 times _faster_ than Dano text rendering (directly to screen), for untransformed text. Unfortunately I cannot test on the same machine in accelerant using version of the test environment. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21822 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-08-04 15:37:16 +04:00
//printf("FontCacheEntryFor(%ld): %p\n", font.GetFamilyAndStyle(), entry);
return entry;
}
readLocker.Unlock();
AutoWriteLocker locker(this);
if (!locker.IsLocked())
return NULL;
// prevent getting screwed by a race condition:
// when we released the readlock above, another thread might have
// gotten the writelock before we have, and might have already
// inserted a cache entry for this font. So we look again if there
// is an entry now, and only then create it if it's still not there,
// all while holding the writelock
entry = fFontCacheEntries.Get(signature);
if (!entry) {
// remove old entries, keep entries below certain count
_ConstrainEntryCount();
entry = new (nothrow) FontCacheEntry();
if (!entry || !entry->Init(font)
|| fFontCacheEntries.Put(signature, entry) < B_OK) {
fprintf(stderr, "FontCache::FontCacheEntryFor() - "
"out of memory or no font file\n");
delete entry;
return NULL;
}
}
* after my last changes to font rendering, it was about 15% slower than before (although there should be much less lock contention) * with this change, there is quite a bit of cleanup, text drawing is now about 20% faster than before the original changes to font caching, mostly due to turning off the kerning feature, which at the moment gives absolutely no benefit. The correct way of doing it might be to use kerning depending on the provided glyph spacing mode * removed fPenLocation from Painter, the usage should be more correct now, since it is now consistently applied before the coordinate transformation from view to screen (also for DrawShape() now, before any view scaling and origin offset) * Painter no longer has it's own instance of a ServerFont, instead it uses its AGGTextRenderer instance, which was per Painter again after the last change, and not global anymore, made _UpdateFont() useless * When using GlyphLayoutEngine, it supports a second pass with the same FontCacheEntry through the introduction of a FontCacheReference. This speeds up DrawString a little, since it needs to calculate the bounding box for the string, and then draw the string in a second pass. This is now done with only one FontCacheEntry lookup * I also tried to optimize the on-the-fly conversion of UTF8->CharCode away, since it was done four times per DrawString, but surprisingly, it proofed to be a slight slowdown. * introduced a shortcut in DrawingEngine::DrawString() which avoids calculating the bounding box, we are now a tiny bit faster to figure out that we don't need to draw any string than Dano In the test environment (drawing to offscreen bitmap and blitting to screen eventually), text rendering is now about 3.7 times _faster_ than Dano text rendering (directly to screen), for untransformed text. Unfortunately I cannot test on the same machine in accelerant using version of the test environment. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21822 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-08-04 15:37:16 +04:00
//printf("FontCacheEntryFor(%ld): %p (insert)\n", font.GetFamilyAndStyle(), entry);
entry->AddReference();
return entry;
}
// Recycle
void
FontCache::Recycle(FontCacheEntry* entry)
{
* after my last changes to font rendering, it was about 15% slower than before (although there should be much less lock contention) * with this change, there is quite a bit of cleanup, text drawing is now about 20% faster than before the original changes to font caching, mostly due to turning off the kerning feature, which at the moment gives absolutely no benefit. The correct way of doing it might be to use kerning depending on the provided glyph spacing mode * removed fPenLocation from Painter, the usage should be more correct now, since it is now consistently applied before the coordinate transformation from view to screen (also for DrawShape() now, before any view scaling and origin offset) * Painter no longer has it's own instance of a ServerFont, instead it uses its AGGTextRenderer instance, which was per Painter again after the last change, and not global anymore, made _UpdateFont() useless * When using GlyphLayoutEngine, it supports a second pass with the same FontCacheEntry through the introduction of a FontCacheReference. This speeds up DrawString a little, since it needs to calculate the bounding box for the string, and then draw the string in a second pass. This is now done with only one FontCacheEntry lookup * I also tried to optimize the on-the-fly conversion of UTF8->CharCode away, since it was done four times per DrawString, but surprisingly, it proofed to be a slight slowdown. * introduced a shortcut in DrawingEngine::DrawString() which avoids calculating the bounding box, we are now a tiny bit faster to figure out that we don't need to draw any string than Dano In the test environment (drawing to offscreen bitmap and blitting to screen eventually), text rendering is now about 3.7 times _faster_ than Dano text rendering (directly to screen), for untransformed text. Unfortunately I cannot test on the same machine in accelerant using version of the test environment. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21822 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-08-04 15:37:16 +04:00
//printf("Recycle(%p)\n", entry);
if (!entry)
return;
entry->UpdateUsage();
entry->RemoveReference();
}
static const int32 kMaxEntryCount = 30;
static inline double
usage_index(uint64 useCount, bigtime_t age)
{
return 100.0 * useCount / age;
}
// _ConstrainEntryCount
void
FontCache::_ConstrainEntryCount()
{
// this function is only ever called with the WriteLock held
if (fFontCacheEntries.Size() < kMaxEntryCount)
return;
//printf("FontCache::_ConstrainEntryCount()\n");
FontMap::Iterator iterator = fFontCacheEntries.GetIterator();
// NOTE: if kMaxEntryCount has a sane value, there has got to be
// some entries, so using the iterator like that should be ok
FontCacheEntry* leastUsedEntry = iterator.Next().value;
bigtime_t now = system_time();
bigtime_t age = now - leastUsedEntry->LastUsed();
uint64 useCount = leastUsedEntry->UsedCount();
double leastUsageIndex = usage_index(useCount, age);
//printf(" leastUsageIndex: %f\n", leastUsageIndex);
while (iterator.HasNext()) {
FontCacheEntry* entry = iterator.Next().value;
age = now - entry->LastUsed();
useCount = entry->UsedCount();
double usageIndex = usage_index(useCount, age);
//printf(" usageIndex: %f\n", usageIndex);
if (usageIndex < leastUsageIndex) {
leastUsedEntry = entry;
leastUsageIndex = usageIndex;
}
}
iterator = fFontCacheEntries.GetIterator();
while (iterator.HasNext()) {
if (iterator.Next().value == leastUsedEntry) {
iterator.Remove();
leastUsedEntry->RemoveReference();
break;
}
}
}