| /* | |
| * Copyright 2013 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "bench/Benchmark.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkFont.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPath.h" | |
| #include "include/core/SkString.h" | |
| #include "include/private/SkTemplates.h" | |
| #include "src/core/SkChecksum.h" | |
| #include "tools/fonts/FontToolUtils.h" | |
| #include "bench/gUniqueGlyphIDs.h" | |
| #include <array> | |
| #define gUniqueGlyphIDs_Sentinel 0xFFFF | |
| static int count_glyphs(const uint16_t start[]) { | |
| const uint16_t* curr = start; | |
| while (*curr != gUniqueGlyphIDs_Sentinel) { | |
| curr += 1; | |
| } | |
| return static_cast<int>(curr - start); | |
| } | |
| class FontCacheBench : public Benchmark { | |
| public: | |
| FontCacheBench() {} | |
| protected: | |
| const char* onGetName() override { | |
| return "fontcache"; | |
| } | |
| void onDraw(int loops, SkCanvas* canvas) override { | |
| SkFont font = ToolUtils::DefaultFont(); | |
| font.setEdging(SkFont::Edging::kAntiAlias); | |
| const uint16_t* array = gUniqueGlyphIDs; | |
| while (*array != gUniqueGlyphIDs_Sentinel) { | |
| int count = count_glyphs(array); | |
| for (int i = 0; i < loops; ++i) { | |
| (void)font.measureText(array, count * sizeof(uint16_t), SkTextEncoding::kGlyphID); | |
| } | |
| array += count + 1; // skip the sentinel | |
| } | |
| } | |
| private: | |
| using INHERITED = Benchmark; | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| static uint32_t rotr(uint32_t value, unsigned bits) { | |
| return (value >> bits) | (value << (32 - bits)); | |
| } | |
| typedef uint32_t (*HasherProc)(uint32_t); | |
| static uint32_t hasher0(uint32_t value) { | |
| value = value ^ (value >> 16); | |
| return value ^ (value >> 8); | |
| } | |
| struct HashTestCase { | |
| const char* fName; | |
| HasherProc fHasher; | |
| }; | |
| static constexpr auto gRec = std::to_array<HashTestCase>({ | |
| HashTestCase{"hasher0", hasher0}, | |
| HashTestCase{"hasher2", SkChecksum::Mix}, | |
| }); | |
| #define kMaxHashBits 12 | |
| #define kMaxHashCount (1 << kMaxHashBits) | |
| static int count_collisions(const uint16_t array[], int count, HasherProc proc, | |
| unsigned hashMask) { | |
| char table[kMaxHashCount]; | |
| sk_bzero(table, sizeof(table)); | |
| int collisions = 0; | |
| for (int i = 0; i < count; ++i) { | |
| int index = proc(array[i]) & hashMask; | |
| collisions += table[index]; | |
| table[index] = 1; | |
| } | |
| return collisions; | |
| } | |
| static void dump_array(const uint16_t array[], int count) { | |
| for (int i = 0; i < count; ++i) { | |
| SkDebugf(" %d,", array[i]); | |
| } | |
| SkDebugf("\n"); | |
| } | |
| class FontCacheEfficiency : public Benchmark { | |
| public: | |
| FontCacheEfficiency() { | |
| if ((false)) dump_array(nullptr, 0); | |
| if ((false)) rotr(0, 0); | |
| } | |
| protected: | |
| const char* onGetName() override { | |
| return "fontefficiency"; | |
| } | |
| void onDraw(int loops, SkCanvas* canvas) override { | |
| static bool gDone; | |
| if (gDone) { | |
| return; | |
| } | |
| gDone = true; | |
| for (int hashBits = 6; hashBits <= 12; hashBits += 1) { | |
| int hashMask = ((1 << hashBits) - 1); | |
| for (int limit = 32; limit <= 1024; limit <<= 1) { | |
| for (size_t i = 0; i < std::size(gRec); ++i) { | |
| int collisions = 0; | |
| int glyphs = 0; | |
| const uint16_t* array = gUniqueGlyphIDs; | |
| while (*array != gUniqueGlyphIDs_Sentinel) { | |
| int count = std::min(count_glyphs(array), limit); | |
| collisions += count_collisions(array, count, gRec[i].fHasher, hashMask); | |
| glyphs += count; | |
| array += count + 1; // skip the sentinel | |
| } | |
| SkDebugf("hashBits [%d] limit [%d] collisions [%d / %d = %1.2g%%] using %s\n", hashBits, limit, collisions, glyphs, | |
| collisions * 100.0 / glyphs, gRec[i].fName); | |
| } | |
| } | |
| } | |
| } | |
| private: | |
| using INHERITED = Benchmark; | |
| }; | |
| DEF_BENCH( return new FontCacheBench(); ) | |
| // undefine this to run the efficiency test | |
| //DEF_BENCH( return new FontCacheEfficiency(); ) | |
| /////////////////////////////////////////////////////////////////////////////// | |
| class FontPathBench : public Benchmark { | |
| SkFont fFont; | |
| std::array<SkGlyphID, 100> fGlyphs; | |
| SkString fName; | |
| const bool fOneAtATime; | |
| public: | |
| FontPathBench(bool oneAtATime) : fOneAtATime(oneAtATime) { | |
| fName.printf("font-path-%s", oneAtATime ? "loop" : "batch"); | |
| } | |
| protected: | |
| const char* onGetName() override { | |
| return fName.c_str(); | |
| } | |
| bool isSuitableFor(Backend backend) override { | |
| return backend == Backend::kNonRendering; | |
| } | |
| void onDelayedSetup() override { | |
| fFont.setSize(32); | |
| for (size_t i = 0; i < std::size(fGlyphs); ++i) { | |
| fGlyphs[i] = i; | |
| } | |
| } | |
| void onDraw(int loops, SkCanvas* canvas) override { | |
| for (int loop = 0; loop < loops; ++loop) { | |
| if (fOneAtATime) { | |
| for (size_t i = 0; i < std::size(fGlyphs); ++i) { | |
| (void)fFont.getPath(fGlyphs[i]); | |
| } | |
| } else { | |
| fFont.getPaths(fGlyphs, | |
| [](const SkPath* src, const SkMatrix& mx, void*) { | |
| if (src) { | |
| (void)src->makeTransform(mx); | |
| } | |
| }, nullptr); | |
| } | |
| } | |
| } | |
| private: | |
| using INHERITED = Benchmark; | |
| }; | |
| DEF_BENCH( return new FontPathBench(true); ) | |
| DEF_BENCH( return new FontPathBench(false); ) |