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