• 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(0, kRGB_H_SkPixelGeometry);
139      std::unique_ptr<SkCanvas> filter = strikeServer->makeAnalysisCanvas(
140              bounds.width(), bounds.height(), props, std::move(colorSpace), true);
141      pic.playback(filter.get());
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, &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