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