Flutter Engine
The Flutter Engine
SkStrikeTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 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
12#include "include/core/SkFont.h"
16#include "include/core/SkPath.h"
18#include "include/core/SkRect.h"
28#include "src/base/SkZip.h"
29#include "src/core/SkGlyph.h"
30#include "src/core/SkMask.h"
33#include "src/core/SkStrike.h"
39#include "tests/Test.h"
40#include "tools/ToolUtils.h"
42
43#include <atomic>
44#include <cstddef>
45#include <cstdint>
46#include <functional>
47#include <initializer_list>
48#include <memory>
49#include <tuple>
50#include <vector>
51
52using namespace skia_private;
53
54using namespace sktext;
55using namespace skglyph;
56
57class Barrier {
58public:
59 Barrier(int threadCount) : fThreadCount(threadCount) { }
60 void waitForAll() {
61 fThreadCount -= 1;
62 while (fThreadCount > 0) { }
63 }
64
65private:
66 std::atomic<int> fThreadCount;
67};
68
69// This should stay in sync with the implementation from SubRunContainer.
70static
71std::tuple<SkZip<const SkPackedGlyphID, const SkPoint>,
73 SkRect>
75 StrikeForGPU* strike,
78 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
79 SkGlyphRect boundingRect = skglyph::empty_rect();
80 int acceptedSize = 0,
81 rejectedSize = 0;
83 for (auto [glyphID, pos] : source) {
84 if (!SkIsFinite(pos.x(), pos.y())) {
85 continue;
86 }
87 const SkPackedGlyphID packedID{glyphID};
88 switch (const SkGlyphDigest digest = strike->digestFor(kDirectMask, packedID);
89 digest.actionFor(kDirectMask)) {
90 case GlyphAction::kAccept: {
91 const SkGlyphRect glyphBounds = digest.bounds().offset(pos);
92 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
93 acceptedBuffer[acceptedSize++] = std::make_tuple(packedID, glyphBounds.leftTop());
94 break;
95 }
96 case GlyphAction::kReject:
97 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
98 break;
99 default:
100 break;
101 }
102 }
103
104 return {acceptedBuffer.first(acceptedSize),
105 rejectedBuffer.first(rejectedSize),
106 boundingRect.rect()};
107}
108
109DEF_TEST(SkStrikeMultiThread, Reporter) {
110 sk_sp<SkTypeface> typeface =
112 static constexpr int kThreadCount = 4;
113
114 Barrier barrier{kThreadCount};
115
116 SkFont font;
118 font.setSubpixel(true);
119 font.setTypeface(typeface);
120
121 SkGlyphID glyphs['z'];
122 SkPoint pos['z'];
123 for (int c = ' '; c < 'z'; c++) {
124 glyphs[c] = font.unicharToGlyph(c);
125 pos[c] = {30.0f * c + 30, 30.0f};
126 }
127 constexpr size_t glyphCount = 'z' - ' ';
128 auto data = SkMakeZip(glyphs, pos).subspan(SkTo<int>(' '), glyphCount);
129
130 SkPaint defaultPaint;
134
135 SkStrikeCache strikeCache;
136
137 // Make our own executor so the --threads parameter doesn't mess things up.
139 for (int tries = 0; tries < 100; tries++) {
140 SkStrike strike{&strikeCache, strikeSpec, strikeSpec.createScalerContext(), nullptr,
141 nullptr};
142
143 auto perThread = [&](int threadIndex) {
144 barrier.waitForAll();
145
146 auto local = data.subspan(threadIndex * 2, data.size() - kThreadCount * 2);
147 for (int i = 0; i < 100; i++) {
148 // Accepted buffers.
149 STArray<64, SkPackedGlyphID> acceptedPackedGlyphIDs;
150 STArray<64, SkPoint> acceptedPositions;
151 STArray<64, SkMask::Format> acceptedFormats;
152 acceptedPackedGlyphIDs.resize(glyphCount);
153 acceptedPositions.resize(glyphCount);
154 const auto acceptedBuffer = SkMakeZip(acceptedPackedGlyphIDs, acceptedPositions);
155
156 // Rejected buffers.
157 STArray<64, SkGlyphID> rejectedGlyphIDs;
158 STArray<64, SkPoint> rejectedPositions;
159 rejectedGlyphIDs.resize(glyphCount);
160 rejectedPositions.resize(glyphCount);
161 const auto rejectedBuffer = SkMakeZip(rejectedGlyphIDs, rejectedPositions);
162
164
165 auto [accepted, rejected, bounds] =
166 prepare_for_mask_drawing(&strike, source, acceptedBuffer, rejectedBuffer);
167 source = rejected;
168 }
169 };
170
171 SkTaskGroup(*executor).batch(kThreadCount, perThread);
172 }
173}
174
175class SkGlyphTestPeer {
176public:
177 static void SetGlyph(SkGlyph* glyph) {
178 // Tweak the bounds to make them unique based on glyph id.
179 const SkGlyphID uniquify = glyph->getGlyphID();
180 glyph->fAdvanceX = 10;
181 glyph->fAdvanceY = 11;
182 glyph->fLeft = -1 - uniquify;
183 glyph->fTop = -2;
184 glyph->fWidth = 8;
185 glyph->fHeight = 9;
186 glyph->fMaskFormat = SkMask::Format::kA8_Format;
187 }
188};
189
191public:
192 static SkGlyph* GetGlyph(SkStrike* strike, SkPackedGlyphID packedID) {
193 SkAutoMutexExclusive m{strike->fStrikeLock};
194 return strike->glyph(packedID);
195 }
196};
197
198DEF_TEST(SkStrike_FlattenByType, reporter) {
199 std::vector<SkGlyph> imagesToSend;
200 std::vector<SkGlyph> pathsToSend;
201 std::vector<SkGlyph> drawablesToSend;
202 SkArenaAlloc alloc{256};
203
204 // Make a mask glyph and put it in the glyphs to send.
205 const SkPackedGlyphID maskPackedGlyphID((SkGlyphID)10);
206 SkGlyph maskGlyph{maskPackedGlyphID};
207 SkGlyphTestPeer::SetGlyph(&maskGlyph);
208
209 static constexpr uint8_t X = 0xff;
210 static constexpr uint8_t O = 0x00;
211 uint8_t imageData[][8] = {
212 {X,X,X,X,X,X,X,X},
213 {X,O,O,O,O,O,O,X},
214 {X,O,O,O,O,O,O,X},
215 {X,O,O,O,O,O,O,X},
216 {X,O,O,X,X,O,O,X},
217 {X,O,O,O,O,O,O,X},
218 {X,O,O,O,O,O,O,X},
219 {X,O,O,O,O,O,O,X},
220 {X,X,X,X,X,X,X,X},
221 };
222 maskGlyph.setImage(&alloc, imageData);
223 imagesToSend.emplace_back(maskGlyph);
224
225 // Make a path glyph and put it in the glyphs to send.
226 const SkPackedGlyphID pathPackedGlyphID((SkGlyphID)11);
227 SkGlyph pathGlyph{pathPackedGlyphID};
228 SkGlyphTestPeer::SetGlyph(&pathGlyph);
229 SkPath path;
230 path.addRect(pathGlyph.rect());
231 pathGlyph.setPath(&alloc, &path, false);
232 pathsToSend.emplace_back(pathGlyph);
233
234 // Make a drawable glyph and put it in the glyphs to send.
235 const SkPackedGlyphID drawablePackedGlyphID((SkGlyphID)12);
236 SkGlyph drawableGlyph{drawablePackedGlyphID};
237 SkGlyphTestPeer::SetGlyph(&drawableGlyph);
238 class TestDrawable final : public SkDrawable {
239 public:
240 TestDrawable(SkRect rect) : fRect(rect) {}
241
242 private:
243 const SkRect fRect;
244 SkRect onGetBounds() override { return fRect; }
245 size_t onApproximateBytesUsed() override {
246 return 0;
247 }
248 void onDraw(SkCanvas* canvas) override {
250 canvas->drawRect(fRect, paint);
251 }
252 };
253
254 sk_sp<SkDrawable> drawable = sk_make_sp<TestDrawable>(drawableGlyph.rect());
255 REPORTER_ASSERT(reporter, !drawableGlyph.setDrawableHasBeenCalled());
256 drawableGlyph.setDrawable(&alloc, drawable);
257 REPORTER_ASSERT(reporter, drawableGlyph.setDrawableHasBeenCalled());
258 REPORTER_ASSERT(reporter, drawableGlyph.drawable() != nullptr);
259 drawablesToSend.emplace_back(drawableGlyph);
260
261 // Test the FlattenGlyphsByType method.
262 SkBinaryWriteBuffer writeBuffer({});
263 SkStrike::FlattenGlyphsByType(writeBuffer, imagesToSend, pathsToSend, drawablesToSend);
264 auto data = writeBuffer.snapshotAsData();
265
266 // Make a strike to merge into.
267 SkStrikeCache strikeCache;
268 auto dstTypeface = ToolUtils::CreateTestTypeface("monospace", SkFontStyle());
269 SkFont font{dstTypeface};
271 sk_sp<SkStrike> strike = spec.findOrCreateStrike(&strikeCache);
272
273 // Test the mergeFromBuffer method.
274 SkReadBuffer readBuffer{data->data(), data->size()};
275 strike->mergeFromBuffer(readBuffer);
276
277 // Check mask glyph.
278 SkGlyph* dstMaskGlyph = SkStrikeTestingPeer::GetGlyph(strike.get(), maskPackedGlyphID);
279 REPORTER_ASSERT(reporter, maskGlyph.rect() == dstMaskGlyph->rect());
281 REPORTER_ASSERT(reporter, dstMaskGlyph->image() != nullptr);
282
283 // Check path glyph.
284 SkGlyph* dstPathGlyph = SkStrikeTestingPeer::GetGlyph(strike.get(), pathPackedGlyphID);
285 REPORTER_ASSERT(reporter, pathGlyph.rect() == dstPathGlyph->rect());
287 REPORTER_ASSERT(reporter, dstPathGlyph->path() != nullptr);
288
289 // Check drawable glyph.
290 SkGlyph* dstDrawableGlyph = SkStrikeTestingPeer::GetGlyph(strike.get(),drawablePackedGlyphID);
291 REPORTER_ASSERT(reporter, drawableGlyph.rect() == dstDrawableGlyph->rect());
293 REPORTER_ASSERT(reporter, dstDrawableGlyph->drawable() != nullptr);
294}
SkRect fRect
Definition: FillRRectOp.cpp:73
reporter
Definition: FontMgrTest.cpp:39
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
SkPoint pos
static bool SkIsFinite(T x, Pack... values)
DEF_TEST(SkStrikeMultiThread, Reporter)
static std::tuple< SkZip< const SkPackedGlyphID, const SkPoint >, SkZip< SkGlyphID, SkPoint >, SkRect > prepare_for_mask_drawing(StrikeForGPU *strike, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkPackedGlyphID, SkPoint > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
@ kUnknown_SkPixelGeometry
uint16_t SkGlyphID
Definition: SkTypes.h:179
constexpr auto SkMakeZip(Ts &&... ts)
Definition: SkZip.h:212
static const SkScalar X
Definition: StrokeBench.cpp:54
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
Barrier(int threadCount)
void waitForAll()
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
virtual SkRect onGetBounds()=0
virtual void onDraw(SkCanvas *)=0
virtual size_t onApproximateBytesUsed()
Definition: SkDrawable.cpp:78
static std::unique_ptr< SkExecutor > MakeFIFOThreadPool(int threads=0, bool allowBorrowing=true)
Definition: SkExecutor.cpp:146
static constexpr SkFontStyle Italic()
Definition: SkFontStyle.h:72
Definition: SkFont.h:35
@ kAntiAlias
may have transparent pixels on glyph edges
skglyph::GlyphAction actionFor(skglyph::ActionType actionType) const
Definition: SkGlyph.h:343
SkPoint leftTop() const
Definition: SkGlyph.h:275
SkRect rect() const
Definition: SkGlyph.h:259
SkGlyphRect offset(SkScalar x, SkScalar y) const
Definition: SkGlyph.h:262
static void SetGlyph(SkGlyph *glyph)
bool setPathHasBeenCalled() const
Definition: SkGlyph.h:486
SkGlyphID getGlyphID() const
Definition: SkGlyph.h:429
bool setDrawableHasBeenCalled() const
Definition: SkGlyph.h:495
bool setImageHasBeenCalled() const
Definition: SkGlyph.h:459
const SkPath * path() const
Definition: SkGlyph.cpp:284
SkRect rect() const
Definition: SkGlyph.h:506
SkDrawable * drawable() const
Definition: SkGlyph.cpp:327
const void * image() const
Definition: SkGlyph.h:465
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
Definition: SkPath.h:59
static SkStrikeSpec MakeMask(const SkFont &font, const SkPaint &paint, const SkSurfaceProps &surfaceProps, SkScalerContextFlags scalerContextFlags, const SkMatrix &deviceMatrix)
static SkStrikeSpec MakeWithNoDevice(const SkFont &font, const SkPaint *paint=nullptr)
std::unique_ptr< SkScalerContext > createScalerContext() const
Definition: SkStrikeSpec.h:91
sk_sp< SkStrike > findOrCreateStrike() const
static SkGlyph * GetGlyph(SkStrike *strike, SkPackedGlyphID packedID)
static void FlattenGlyphsByType(SkWriteBuffer &buffer, SkSpan< SkGlyph > images, SkSpan< SkGlyph > paths, SkSpan< SkGlyph > drawables)
Definition: SkStrike.cpp:87
bool mergeFromBuffer(SkReadBuffer &buffer) SK_EXCLUDES(fStrikeLock)
Definition: SkStrike.cpp:117
SkGlyph * glyph(SkGlyphDigest) SK_REQUIRES(fStrikeLock)
Definition: SkStrike.cpp:322
void batch(int N, std::function< void(int)> fn)
Definition: SkTaskGroup.cpp:24
Definition: SkZip.h:25
constexpr SkZip first(size_t n) const
Definition: SkZip.h:86
T * get() const
Definition: SkRefCnt.h:303
void resize(size_t count)
Definition: SkTArray.h:423
virtual SkGlyphDigest digestFor(skglyph::ActionType, SkPackedGlyphID)=0
const Paint & paint
Definition: color_source.cc:38
SkBitmap source
Definition: examples.cpp:28
static constexpr uint64_t kThreadCount
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
sk_sp< SkTypeface > CreatePortableTypeface(const char *name, SkFontStyle style)
sk_sp< SkTypeface > CreateTestTypeface(const char *name, SkFontStyle style)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
font
Font Metadata and Metrics.
SkGlyphRect empty_rect()
Definition: SkGlyph.h:289
SkGlyphRect rect_union(SkGlyphRect, SkGlyphRect)
Definition: SkGlyph.h:297
@ kDirectMask
Definition: SkGlyph.h:313
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63