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