xref: /third_party/skia/bench/FontCacheBench.cpp (revision cb93a386)
1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bench/Benchmark.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkFont.h"
11#include "include/core/SkPaint.h"
12#include "include/core/SkPath.h"
13#include "include/core/SkString.h"
14#include "include/private/SkChecksum.h"
15#include "include/private/SkTemplates.h"
16
17#include "bench/gUniqueGlyphIDs.h"
18
19#define gUniqueGlyphIDs_Sentinel    0xFFFF
20
21static int count_glyphs(const uint16_t start[]) {
22    const uint16_t* curr = start;
23    while (*curr != gUniqueGlyphIDs_Sentinel) {
24        curr += 1;
25    }
26    return static_cast<int>(curr - start);
27}
28
29class FontCacheBench : public Benchmark {
30public:
31    FontCacheBench()  {}
32
33protected:
34    const char* onGetName() override {
35        return "fontcache";
36    }
37
38    void onDraw(int loops, SkCanvas* canvas) override {
39        SkFont font;
40        font.setEdging(SkFont::Edging::kAntiAlias);
41
42        const uint16_t* array = gUniqueGlyphIDs;
43        while (*array != gUniqueGlyphIDs_Sentinel) {
44            int count = count_glyphs(array);
45            for (int i = 0; i < loops; ++i) {
46                (void)font.measureText(array, count * sizeof(uint16_t), SkTextEncoding::kGlyphID);
47            }
48            array += count + 1;    // skip the sentinel
49        }
50    }
51
52private:
53    using INHERITED = Benchmark;
54};
55
56///////////////////////////////////////////////////////////////////////////////
57
58static uint32_t rotr(uint32_t value, unsigned bits) {
59    return (value >> bits) | (value << (32 - bits));
60}
61
62typedef uint32_t (*HasherProc)(uint32_t);
63
64static uint32_t hasher0(uint32_t value) {
65    value = value ^ (value >> 16);
66    return value ^ (value >> 8);
67}
68
69static const struct {
70    const char* fName;
71    HasherProc  fHasher;
72} gRec[] = {
73    { "hasher0",  hasher0 },
74    { "hasher2",  SkChecksum::Mix },
75};
76
77#define kMaxHashBits   12
78#define kMaxHashCount  (1 << kMaxHashBits)
79
80static int count_collisions(const uint16_t array[], int count, HasherProc proc,
81                            unsigned hashMask) {
82    char table[kMaxHashCount];
83    sk_bzero(table, sizeof(table));
84
85    int collisions = 0;
86    for (int i = 0; i < count; ++i) {
87        int index = proc(array[i]) & hashMask;
88        collisions += table[index];
89        table[index] = 1;
90    }
91    return collisions;
92}
93
94static void dump_array(const uint16_t array[], int count) {
95    for (int i = 0; i < count; ++i) {
96        SkDebugf(" %d,", array[i]);
97    }
98    SkDebugf("\n");
99}
100
101class FontCacheEfficiency : public Benchmark {
102public:
103    FontCacheEfficiency()  {
104        if (false) dump_array(nullptr, 0);
105        if (false) rotr(0, 0);
106    }
107
108protected:
109    const char* onGetName() override {
110        return "fontefficiency";
111    }
112
113    void onDraw(int loops, SkCanvas* canvas) override {
114        static bool gDone;
115        if (gDone) {
116            return;
117        }
118        gDone = true;
119
120        for (int hashBits = 6; hashBits <= 12; hashBits += 1) {
121            int hashMask = ((1 << hashBits) - 1);
122            for (int limit = 32; limit <= 1024; limit <<= 1) {
123                for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
124                    int collisions = 0;
125                    int glyphs = 0;
126                    const uint16_t* array = gUniqueGlyphIDs;
127                    while (*array != gUniqueGlyphIDs_Sentinel) {
128                        int count = std::min(count_glyphs(array), limit);
129                        collisions += count_collisions(array, count, gRec[i].fHasher, hashMask);
130                        glyphs += count;
131                        array += count + 1;    // skip the sentinel
132                    }
133                    SkDebugf("hashBits [%d] limit [%d] collisions [%d / %d = %1.2g%%] using %s\n", hashBits, limit, collisions, glyphs,
134                             collisions * 100.0 / glyphs, gRec[i].fName);
135                }
136            }
137        }
138    }
139
140private:
141    using INHERITED = Benchmark;
142};
143DEF_BENCH( return new FontCacheBench(); )
144
145// undefine this to run the efficiency test
146//DEF_BENCH( return new FontCacheEfficiency(); )
147
148///////////////////////////////////////////////////////////////////////////////
149
150class FontPathBench : public Benchmark {
151    SkFont fFont;
152    uint16_t fGlyphs[100];
153    SkString fName;
154    const bool fOneAtATime;
155
156public:
157    FontPathBench(bool oneAtATime) : fOneAtATime(oneAtATime) {
158        fName.printf("font-path-%s", oneAtATime ? "loop" : "batch");
159    }
160
161protected:
162    const char* onGetName() override {
163        return fName.c_str();
164    }
165
166    bool isSuitableFor(Backend backend) override {
167        return backend == kNonRendering_Backend;
168    }
169
170    void onDelayedSetup() override {
171        fFont.setSize(32);
172        for (size_t i = 0; i < SK_ARRAY_COUNT(fGlyphs); ++i) {
173            fGlyphs[i] = i;
174        }
175    }
176
177    void onDraw(int loops, SkCanvas* canvas) override {
178        SkPath path;
179        for (int loop = 0; loop < loops; ++loop) {
180            if (fOneAtATime) {
181                for (size_t i = 0; i < SK_ARRAY_COUNT(fGlyphs); ++i) {
182                    fFont.getPath(fGlyphs[i], &path);
183                }
184            } else {
185                fFont.getPaths(fGlyphs, SK_ARRAY_COUNT(fGlyphs),
186                               [](const SkPath* src, const SkMatrix& mx, void* ctx) {
187                                   if (src) {
188                                       src->transform(mx, static_cast<SkPath*>(ctx));
189                                   }
190                               }, &path);
191            }
192        }
193    }
194
195private:
196    using INHERITED = Benchmark;
197};
198DEF_BENCH( return new FontPathBench(true); )
199DEF_BENCH( return new FontPathBench(false); )
200