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