2007-08-02 23:10:38 +04:00
|
|
|
/*
|
|
|
|
* 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
|
2007-08-02 23:10:38 +04:00
|
|
|
FontCacheEntry*
|
|
|
|
FontCache::FontCacheEntryFor(const ServerFont& font)
|
|
|
|
{
|
2009-02-28 00:11:05 +03:00
|
|
|
static const size_t signatureSize = 512;
|
|
|
|
char signature[signatureSize];
|
|
|
|
FontCacheEntry::GenerateSignature(signature, signatureSize, font);
|
2007-08-02 23:10:38 +04:00
|
|
|
|
|
|
|
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);
|
2007-08-02 23:10:38 +04:00
|
|
|
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);
|
2007-08-02 23:10:38 +04:00
|
|
|
|
|
|
|
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);
|
2007-08-11 17:16:07 +04:00
|
|
|
if (!entry)
|
|
|
|
return;
|
2007-08-02 23:10:38 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|