blob: dbe36d1e6abb894b4e10fd4579c230ad45a24989 [file] [log] [blame]
Khushal38a08432018-05-02 10:29:37 -07001/*
2 * Copyright 2018 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 "SkGraphics.h"
9#include "SkMutex.h"
10#include "SkRemoteGlyphCache.h"
11#include "SkStrikeCache.h"
12#include "SkSurface.h"
13#include "SkTextBlob.h"
14#include "SkTypeface_remote.h"
15#include "Test.h"
16
17class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
18 public SkStrikeClient::DiscardableHandleManager {
19public:
20 DiscardableManager() = default;
21 ~DiscardableManager() override = default;
22
23 // Server implementation.
24 SkDiscardableHandleId createHandle() override {
25 // Handles starts as locked.
26 fLockedHandles.add(++fNextHandleId);
27 return fNextHandleId;
28 }
29 bool lockHandle(SkDiscardableHandleId id) override {
30 if (id <= fLastDeletedHandleId) return false;
31 fLockedHandles.add(id);
32 return true;
33 }
34
35 // Client implementation.
36 bool deleteHandle(SkDiscardableHandleId id) override { return id <= fLastDeletedHandleId; }
37
38 void unlockAll() { fLockedHandles.reset(); }
39 void unlockAndDeleteAll() {
40 unlockAll();
41 fLastDeletedHandleId = fNextHandleId;
42 }
43 const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { return fLockedHandles; }
44 SkDiscardableHandleId handleCount() { return fNextHandleId; }
45
46private:
47 SkDiscardableHandleId fNextHandleId = 0u;
48 SkDiscardableHandleId fLastDeletedHandleId = 0u;
49 SkTHashSet<SkDiscardableHandleId> fLockedHandles;
50};
51
52sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) {
53 SkPaint font;
54 font.setTypeface(tf);
55 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
56 font.setTextAlign(SkPaint::kLeft_Align);
57 font.setStyle(SkPaint::kFill_Style);
58 font.setHinting(SkPaint::kNormal_Hinting);
59 font.setTextSize(1u);
60
61 SkTextBlobBuilder builder;
62 SkRect bounds = SkRect::MakeWH(10, 10);
63 const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds);
64 SkASSERT(runBuffer.utf8text == nullptr);
65 SkASSERT(runBuffer.clusters == nullptr);
66
67 for (int i = 0; i < glyphCount; i++) {
68 runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
69 runBuffer.pos[i] = SkIntToScalar(i);
70 }
71 return builder.make();
72}
73
74SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height) {
75 auto surface = SkSurface::MakeRasterN32Premul(width, height);
76 SkPaint paint;
77 surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint);
78 SkBitmap bitmap;
79 bitmap.allocN32Pixels(width, height);
80 surface->readPixels(bitmap, 0, 0);
81 return bitmap;
82}
83
84DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) {
85 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
86 SkStrikeServer server(discardableManager.get());
87 SkStrikeClient client(discardableManager);
88
89 auto server_tf = SkTypeface::MakeDefault();
90 auto tf_data = server.serializeTypeface(server_tf.get());
91
92 auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size());
93 REPORTER_ASSERT(reporter, client_tf);
94 REPORTER_ASSERT(reporter, SkTypefaceProxy::DownCast(client_tf.get())->remoteTypefaceID() ==
95 server_tf->uniqueID());
96}
97
98DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) {
99 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
100 SkStrikeServer server(discardableManager.get());
101 SkStrikeClient client(discardableManager);
102
103 // Server.
104 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
105 auto serverTfData = server.serializeTypeface(serverTf.get());
106
107 int glyphCount = 10;
108 auto serverBlob = buildTextBlob(serverTf, glyphCount);
109 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
110 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
111 SkPaint paint;
112 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
113
114 std::vector<uint8_t> serverStrikeData;
115 server.writeStrikeData(&serverStrikeData);
116
117 // Client.
118 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
119 REPORTER_ASSERT(reporter,
120 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
121 auto clientBlob = buildTextBlob(clientTf, glyphCount);
122
123 SkBitmap expected = RasterBlob(serverBlob, 10, 10);
124 SkBitmap actual = RasterBlob(clientBlob, 10, 10);
125 for (int i = 0; i < expected.width(); ++i) {
126 for (int j = 0; j < expected.height(); ++j) {
127 REPORTER_ASSERT(reporter, expected.getColor(i, j) == actual.getColor(i, j));
128 }
129 }
130}
131
132DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
133 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
134 SkStrikeServer server(discardableManager.get());
135 SkStrikeClient client(discardableManager);
136
137 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
138 server.serializeTypeface(serverTf.get());
139 int glyphCount = 10;
140 auto serverBlob = buildTextBlob(serverTf, glyphCount);
141
142 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
143 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
144 SkPaint paint;
145 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
146
147 // The strike from the blob should be locked after it has been drawn on the canvas.
148 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
149 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
150
151 // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
152 // again.
153 std::vector<uint8_t> fontData;
154 server.writeStrikeData(&fontData);
155 discardableManager->unlockAll();
156 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
157
158 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
159 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
160 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
161}
162
163DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
164 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
165 SkStrikeServer server(discardableManager.get());
166 SkStrikeClient client(discardableManager);
167
168 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
169 server.serializeTypeface(serverTf.get());
170 int glyphCount = 10;
171 auto serverBlob = buildTextBlob(serverTf, glyphCount);
172
173 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
174 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
175 SkPaint paint;
176 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
177 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
178
179 // Write the strike data and delete all the handles. Re-analyzing the blob should create new
180 // handles.
181 std::vector<uint8_t> fontData;
182 server.writeStrikeData(&fontData);
183 discardableManager->unlockAndDeleteAll();
184 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
Khushal38a08432018-05-02 10:29:37 -0700185 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
186}
187
188DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) {
189 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
190 SkStrikeServer server(discardableManager.get());
191 SkStrikeClient client(discardableManager);
192
193 // Server.
194 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
195 auto serverTfData = server.serializeTypeface(serverTf.get());
196
197 int glyphCount = 10;
198 auto serverBlob = buildTextBlob(serverTf, glyphCount);
199
200 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
201 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
202 SkPaint paint;
203 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
204
205 std::vector<uint8_t> serverStrikeData;
206 server.writeStrikeData(&serverStrikeData);
207
208 // Client.
209 REPORTER_ASSERT(reporter,
210 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
211 auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
212
213 // The cache remains alive until it is pinned in the discardable manager.
214 SkGraphics::PurgeFontCache();
215 REPORTER_ASSERT(reporter, !clientTf->unique());
216
217 // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
218 // clientTf.
219 discardableManager->unlockAndDeleteAll();
220 SkGraphics::PurgeFontCache();
221 REPORTER_ASSERT(reporter, clientTf->unique());
222}
Khushalb2e71272018-05-15 12:59:48 -0700223
224DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) {
225 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
226 SkStrikeServer server(discardableManager.get());
227 SkStrikeClient client(discardableManager);
228
229 // Server.
230 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
231 auto serverTfData = server.serializeTypeface(serverTf.get());
232
233 int glyphCount = 10;
234 auto serverBlob = buildTextBlob(serverTf, glyphCount);
235
236 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
237 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
238 SkPaint paint;
239 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
240
241 std::vector<uint8_t> serverStrikeData;
242 server.writeStrikeData(&serverStrikeData);
243
244 // Client.
245 REPORTER_ASSERT(reporter,
246 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
247 SkStrikeCache::Validate();
248}