1 /*
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 <chrono>
9 #include <err.h>
10 #include <iostream>
11 #include <memory>
12 #include <string>
13 #include <sys/types.h>
14 #include <sys/uio.h>
15 #include <sys/wait.h>
16 #include <thread>
17 #include <unistd.h>
18
19 #include "include/core/SkGraphics.h"
20 #include "include/core/SkSurface.h"
21 #include "src/core/SkRemoteGlyphCache.h"
22 #include "src/core/SkScalerContext.h"
23
24 static std::string gSkpName;
25 static bool gUseGpu = true;
26 static bool gPurgeFontCaches = true;
27 static bool gUseProcess = true;
28
29 class ServerDiscardableManager : public SkStrikeServer::DiscardableHandleManager {
30 public:
31 ServerDiscardableManager() = default;
32 ~ServerDiscardableManager() override = default;
33
createHandle()34 SkDiscardableHandleId createHandle() override { return ++nextHandleId; }
lockHandle(SkDiscardableHandleId handleId)35 bool lockHandle(SkDiscardableHandleId handleId) override {
36 return handleId > lastPurgedHandleId;
37 }
purgeAll()38 void purgeAll() { lastPurgedHandleId = nextHandleId; }
39
40 private:
41 SkDiscardableHandleId nextHandleId = 0u;
42 SkDiscardableHandleId lastPurgedHandleId = 0u;
43 };
44
45 class ClientDiscardableManager : public SkStrikeClient::DiscardableHandleManager {
46 public:
47 class ScopedPurgeCache {
48 public:
ScopedPurgeCache(ClientDiscardableManager * manager)49 ScopedPurgeCache(ClientDiscardableManager* manager) : fManager(manager) {
50 if (fManager) fManager->allowPurging = true;
51 }
~ScopedPurgeCache()52 ~ScopedPurgeCache() {
53 if (fManager) fManager->allowPurging = false;
54 }
55
56 private:
57 ClientDiscardableManager* fManager;
58 };
59
60 ClientDiscardableManager() = default;
61 ~ClientDiscardableManager() override = default;
62
deleteHandle(SkDiscardableHandleId)63 bool deleteHandle(SkDiscardableHandleId) override { return allowPurging; }
64
65 private:
66 bool allowPurging = false;
67 };
68
write_SkData(int fd,const SkData & data)69 static bool write_SkData(int fd, const SkData& data) {
70 size_t size = data.size();
71 ssize_t bytesWritten = ::write(fd, &size, sizeof(size));
72 if (bytesWritten < 0) {
73 err(1,"Failed write %zu", size);
74 return false;
75 }
76
77 bytesWritten = ::write(fd, data.data(), data.size());
78 if (bytesWritten < 0) {
79 err(1,"Failed write %zu", size);
80 return false;
81 }
82
83 return true;
84 }
85
read_SkData(int fd)86 static sk_sp<SkData> read_SkData(int fd) {
87 size_t size;
88 ssize_t readSize = ::read(fd, &size, sizeof(size));
89 if (readSize <= 0) {
90 if (readSize < 0) {
91 err(1, "Failed read %zu", size);
92 }
93 return nullptr;
94 }
95
96 auto out = SkData::MakeUninitialized(size);
97 auto data = (uint8_t*)out->data();
98
99 size_t totalRead = 0;
100 while (totalRead < size) {
101 ssize_t sizeRead;
102 sizeRead = ::read(fd, &data[totalRead], size - totalRead);
103 if (sizeRead <= 0) {
104 if (readSize < 0) {
105 err(1, "Failed read %zu", size);
106 }
107 return nullptr;
108 }
109 totalRead += sizeRead;
110 }
111
112 return out;
113 }
114
115 class Timer {
116 public:
start()117 void start() {
118 fStart = std::chrono::high_resolution_clock::now();
119 }
120
stop()121 void stop() {
122 auto end = std::chrono::high_resolution_clock::now();
123 fElapsedSeconds += end - fStart;
124 }
125
elapsedSeconds()126 double elapsedSeconds() {
127 return fElapsedSeconds.count();
128 }
129
130 private:
131 decltype(std::chrono::high_resolution_clock::now()) fStart;
132 std::chrono::duration<double> fElapsedSeconds{0.0};
133 };
134
push_font_data(const SkPicture & pic,SkStrikeServer * strikeServer,sk_sp<SkColorSpace> colorSpace,int writeFd)135 static bool push_font_data(const SkPicture& pic, SkStrikeServer* strikeServer,
136 sk_sp<SkColorSpace> colorSpace, int writeFd) {
137 const SkIRect bounds = pic.cullRect().round();
138 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
139 SkTextBlobCacheDiffCanvas filter(bounds.width(), bounds.height(), props,
140 strikeServer, std::move(colorSpace), true);
141 pic.playback(&filter);
142
143 std::vector<uint8_t> fontData;
144 strikeServer->writeStrikeData(&fontData);
145 auto data = SkData::MakeWithoutCopy(fontData.data(), fontData.size());
146 return write_SkData(writeFd, *data);
147 }
148
final_draw(std::string outFilename,SkData * picData,SkStrikeClient * client,ClientDiscardableManager * discardableManager,int readFd,int writeFd)149 static void final_draw(std::string outFilename, SkData* picData, SkStrikeClient* client,
150 ClientDiscardableManager* discardableManager, int readFd, int writeFd) {
151 SkDeserialProcs procs;
152 auto decode = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
153 return reinterpret_cast<SkStrikeClient*>(ctx)->deserializeTypeface(data, length);
154 };
155 procs.fTypefaceProc = decode;
156 procs.fTypefaceCtx = client;
157
158 auto pic = SkPicture::MakeFromData(picData, &procs);
159
160 auto cullRect = pic->cullRect();
161 auto r = cullRect.round();
162
163 auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height());
164 auto c = s->getCanvas();
165 auto picUnderTest = SkPicture::MakeFromData(picData, &procs);
166
167 Timer drawTime;
168 auto randomData = SkData::MakeUninitialized(1u);
169 for (int i = 0; i < 100; i++) {
170 if (gPurgeFontCaches) {
171 ClientDiscardableManager::ScopedPurgeCache purge(discardableManager);
172 SkGraphics::PurgeFontCache();
173 SkASSERT(SkGraphics::GetFontCacheUsed() == 0u);
174 }
175
176 drawTime.start();
177 if (client != nullptr) {
178 // Kick the renderer to send us the fonts.
179 write_SkData(writeFd, *randomData);
180 auto fontData = read_SkData(readFd);
181 if (fontData && !fontData->isEmpty()) {
182 if (!client->readStrikeData(fontData->data(), fontData->size()))
183 SK_ABORT("Bad serialization");
184 }
185 }
186 c->drawPicture(picUnderTest);
187 drawTime.stop();
188 }
189
190 std::cout << "useProcess: " << gUseProcess
191 << " useGPU: " << gUseGpu
192 << " purgeCache: " << gPurgeFontCaches << std::endl;
193 fprintf(stderr, "%s use GPU %s elapsed time %8.6f s\n", gSkpName.c_str(),
194 gUseGpu ? "true" : "false", drawTime.elapsedSeconds());
195
196 auto i = s->makeImageSnapshot();
197 auto data = i->encodeToData();
198 SkFILEWStream f(outFilename.c_str());
199 f.write(data->data(), data->size());
200 }
201
gpu(int readFd,int writeFd)202 static void gpu(int readFd, int writeFd) {
203
204 if (gUseGpu) {
205 auto picData = read_SkData(readFd);
206 if (picData == nullptr) {
207 return;
208 }
209
210 sk_sp<ClientDiscardableManager> discardableManager = sk_make_sp<ClientDiscardableManager>();
211 SkStrikeClient strikeClient(discardableManager);
212
213 final_draw("test.png", picData.get(), &strikeClient, discardableManager.get(), readFd,
214 writeFd);
215 }
216
217 ::close(writeFd);
218 ::close(readFd);
219
220 printf("GPU is exiting\n");
221 }
222
renderer(const std::string & skpName,int readFd,int writeFd)223 static int renderer(
224 const std::string& skpName, int readFd, int writeFd)
225 {
226 ServerDiscardableManager discardableManager;
227 SkStrikeServer server(&discardableManager);
228 auto closeAll = [readFd, writeFd]() {
229 ::close(writeFd);
230 ::close(readFd);
231 };
232
233 auto skpData = SkData::MakeFromFileName(skpName.c_str());
234 std::cout << "skp stream is " << skpData->size() << " bytes long " << std::endl;
235
236 sk_sp<SkData> stream;
237 if (gUseGpu) {
238 auto pic = SkPicture::MakeFromData(skpData.get());
239 auto colorSpace = SkColorSpace::MakeSRGB();
240 SkSerialProcs procs;
241 auto encode = [](SkTypeface* tf, void* ctx) -> sk_sp<SkData> {
242 return reinterpret_cast<SkStrikeServer*>(ctx)->serializeTypeface(tf);
243 };
244 procs.fTypefaceProc = encode;
245 procs.fTypefaceCtx = &server;
246
247 stream = pic->serialize(&procs);
248
249 if (!write_SkData(writeFd, *stream)) {
250 closeAll();
251 return 1;
252 }
253
254 while (true) {
255 auto inBuffer = read_SkData(readFd);
256 if (inBuffer == nullptr) {
257 closeAll();
258 return 0;
259 }
260 if (gPurgeFontCaches) discardableManager.purgeAll();
261 push_font_data(*pic.get(), &server, colorSpace, writeFd);
262 }
263 } else {
264 stream = skpData;
265 final_draw("test-correct.png", stream.get(), nullptr, nullptr, -1, -1);
266 closeAll();
267 return 0;
268 }
269 }
270
main(int argc,char ** argv)271 int main(int argc, char** argv) {
272 std::string skpName = argc > 1 ? std::string{argv[1]} : std::string{"skps/desk_nytimes.skp"};
273 int mode = argc > 2 ? atoi(argv[2]) : -1;
274 printf("skp: %s\n", skpName.c_str());
275
276 gSkpName = skpName;
277
278 enum direction : int {kRead = 0, kWrite = 1};
279
280
281 int render_to_gpu[2],
282 gpu_to_render[2];
283
284 for (int m = 0; m < 8; m++) {
285 int r = pipe(render_to_gpu);
286 if (r < 0) {
287 perror("Can't write picture from render to GPU ");
288 return 1;
289 }
290 r = pipe(gpu_to_render);
291 if (r < 0) {
292 perror("Can't write picture from render to GPU ");
293 return 1;
294 }
295
296 gPurgeFontCaches = (m & 4) == 4;
297 gUseGpu = (m & 2) == 2;
298 gUseProcess = (m & 1) == 1;
299
300 if (mode >= 0 && mode < 8 && mode != m) {
301 continue;
302 }
303
304 if (gUseProcess) {
305 pid_t child = fork();
306 SkGraphics::Init();
307
308 if (child == 0) {
309 close(gpu_to_render[kRead]);
310 close(render_to_gpu[kWrite]);
311 gpu(render_to_gpu[kRead], gpu_to_render[kWrite]);
312 } else {
313 close(render_to_gpu[kRead]);
314 close(gpu_to_render[kWrite]);
315 renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]);
316 waitpid(child, nullptr, 0);
317 }
318 } else {
319 SkGraphics::Init();
320 std::thread(gpu, render_to_gpu[kRead], gpu_to_render[kWrite]).detach();
321 renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]);
322 }
323 }
324
325 return 0;
326 }
327
328