• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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