/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkCanvas.h" #include "SkPathEffect.h" #include "SkMaskFilter.h" #include "SkData.h" #include "SkDescriptor.h" #include "SkGraphics.h" #include "SkSemaphore.h" #include "SkPictureRecorder.h" #include "SkSerialProcs.h" #include "SkSurface.h" #include "SkTypeface.h" #include "SkWriteBuffer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "SkTypeface_remote.h" static const size_t kPageSize = 4096; struct WireTypeface { std::thread::id thread_id; SkFontID typeface_id; SkFontStyle style; bool is_fixed; }; class ScalerContextRecDescriptor { public: explicit ScalerContextRecDescriptor(const SkScalerContextRec& rec) { auto desc = reinterpret_cast(&fDescriptor); desc->init(); desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); } const SkDescriptor& desc() const { return *reinterpret_cast(&fDescriptor); } struct Hash { size_t operator()(ScalerContextRecDescriptor const& s) const { return SkOpts::hash_fn(&s.desc(), sizeof(s), 0); } }; struct Equal { bool operator()( const ScalerContextRecDescriptor& lhs, const ScalerContextRecDescriptor& rhs ) const { return lhs.desc() == rhs.desc(); } }; private: // The system only passes descriptors without effects. That is why it uses a fixed size // descriptor. storageFor is needed because some of the constructors below are private. template using storageFor = typename std::aligned_storage::type; struct { storageFor dummy1; storageFor dummy2; storageFor dummy3; } fDescriptor; }; class Op { public: explicit Op(const SkScalerContextRec& rec) : descriptor{rec} {} int32_t op; SkFontID typeface_id; union { // op 0 SkPaint::FontMetrics fontMetrics; // op 1 and 2 SkGlyph glyph; // op 3 struct { SkGlyphID glyphId; size_t pathSize; }; }; ScalerContextRecDescriptor descriptor; }; class RemoteScalerContextPassThread : public SkRemoteScalerContext { public: explicit RemoteScalerContextPassThread(int readFd, int writeFd) : fReadFd{readFd} , fWriteFd{writeFd} { } void generateFontMetrics(const SkTypefaceProxy& tf, const SkScalerContextRec& rec, SkPaint::FontMetrics* metrics) override { Op* op = this->createOp(0, tf, rec); write(fWriteFd, fBuffer, sizeof(*op)); read(fReadFd, fBuffer, sizeof(fBuffer)); memcpy(metrics, &op->fontMetrics, sizeof(op->fontMetrics)); op->~Op(); } void generateMetrics(const SkTypefaceProxy& tf, const SkScalerContextRec& rec, SkGlyph* glyph) override { Op* op = this->createOp(1, tf, rec); memcpy(&op->glyph, glyph, sizeof(*glyph)); write(fWriteFd, fBuffer, sizeof(*op)); read(fReadFd, fBuffer, sizeof(fBuffer)); memcpy(glyph, &op->glyph, sizeof(op->glyph)); op->~Op(); } void generateImage(const SkTypefaceProxy& tf, const SkScalerContextRec& rec, const SkGlyph& glyph) override { Op* op = this->createOp(2, tf, rec); memcpy(&op->glyph, &glyph, sizeof(glyph)); write(fWriteFd, fBuffer, sizeof(*op)); read(fReadFd, fBuffer, sizeof(fBuffer)); memcpy(glyph.fImage, fBuffer + sizeof(Op), glyph.rowBytes() * glyph.fHeight); op->~Op(); } void generatePath(const SkTypefaceProxy& tf, const SkScalerContextRec& rec, SkGlyphID glyph, SkPath* path) override { Op* op = this->createOp(3, tf, rec); op->glyphId = glyph; write(fWriteFd, fBuffer, sizeof(*op)); read(fReadFd, fBuffer, sizeof(fBuffer)); path->readFromMemory(fBuffer + sizeof(Op), op->pathSize); op->~Op(); } private: Op* createOp(uint32_t opID, const SkTypefaceProxy& tf, const SkScalerContextRec& rec) { Op* op = new (fBuffer) Op(rec); op->op = opID; op->typeface_id = tf.fontID(); return op; } const int fReadFd, fWriteFd; uint8_t fBuffer[1024 * kPageSize]; }; static sk_sp gpu_from_renderer_by_ID(const void* buf, size_t len, void* ctx) { static std::unordered_map> mapIdToTypeface; WireTypeface wire; if (len >= sizeof(wire)) { memcpy(&wire, buf, sizeof(wire)); auto i = mapIdToTypeface.find(wire.typeface_id); if (i == mapIdToTypeface.end()) { auto newTypeface = sk_make_sp( wire.typeface_id, wire.thread_id, wire.style, wire.is_fixed, (SkRemoteScalerContext*)ctx); i = mapIdToTypeface.emplace_hint(i, wire.typeface_id, newTypeface); } return i->second; } SK_ABORT("Bad data"); return nullptr; } std::unordered_map> gTypefaceMap; // TODO: Figure out how to manage the entries. std::unordered_map, ScalerContextRecDescriptor::Hash, ScalerContextRecDescriptor::Equal> gScalerContextMap(16, ScalerContextRecDescriptor::Hash(), ScalerContextRecDescriptor::Equal()); static SkScalerContext* scaler_context_from_op(Op* op) { SkScalerContext* sc; auto j = gScalerContextMap.find(op->descriptor); if (j != gScalerContextMap.end()) { sc = j->second.get(); } else { auto i = gTypefaceMap.find(op->typeface_id); if (i == gTypefaceMap.end()) { std::cerr << "bad typeface id: " << op->typeface_id << std::endl; SK_ABORT("unknown type face"); } auto tf = i->second; SkScalerContextEffects effects; auto mapSc = tf->createScalerContext(effects, &op->descriptor.desc(), false); sc = mapSc.get(); gScalerContextMap.emplace_hint(j, op->descriptor, std::move(mapSc)); } return sc; } static sk_sp renderer_to_gpu_by_ID(SkTypeface* tf, void* ctx) { WireTypeface wire = { std::this_thread::get_id(), SkTypeface::UniqueID(tf), tf->fontStyle(), tf->isFixedPitch() }; auto i = gTypefaceMap.find(SkTypeface::UniqueID(tf)); if (i == gTypefaceMap.end()) { gTypefaceMap.insert({SkTypeface::UniqueID(tf), sk_ref_sp(tf)}); } return SkData::MakeWithCopy(&wire, sizeof(wire)); } static void final_draw(std::string outFilename, SkDeserialProcs* procs, uint8_t* picData, size_t picSize) { auto pic = SkPicture::MakeFromData(picData, picSize, procs); auto cullRect = pic->cullRect(); auto r = cullRect.round(); auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height()); auto c = s->getCanvas(); auto picUnderTest = SkPicture::MakeFromData(picData, picSize, procs); auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < 40; i++) { c->drawPicture(picUnderTest); } auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed_seconds = end-start; std::cout << "elapsed time: " << elapsed_seconds.count() << "s\n"; auto i = s->makeImageSnapshot(); auto data = i->encodeToData(); SkFILEWStream f(outFilename.c_str()); f.write(data->data(), data->size()); } static void gpu(int readFd, int writeFd) { size_t picSize = 0; read(readFd, &picSize, sizeof(picSize)); static constexpr size_t kBufferSize = 10 * 1024 * kPageSize; std::unique_ptr picBuffer{new uint8_t[kBufferSize]}; size_t readSoFar = 0; while (readSoFar < picSize) { ssize_t readSize; if((readSize = read(readFd, &picBuffer[readSoFar], kBufferSize - readSoFar)) <= 0) { if (readSize == 0) return; err(1, "gpu pic read error %d", errno); } readSoFar += readSize; } SkDeserialProcs procs; std::unique_ptr rsc{ new RemoteScalerContextPassThread{readFd, writeFd}}; procs.fTypefaceProc = gpu_from_renderer_by_ID; procs.fTypefaceCtx = rsc.get(); final_draw("test.png", &procs, picBuffer.get(), picSize); /* auto pic = SkPicture::MakeFromData(picBuffer.get(), picSize, &procs); auto cullRect = pic->cullRect(); auto r = cullRect.round(); auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height()); auto c = s->getCanvas(); c->drawPicture(pic); auto i = s->makeImageSnapshot(); auto data = i->encodeToData(); SkFILEWStream f("test.png"); f.write(data->data(), data->size()); */ close(writeFd); close(readFd); } static int renderer(const std::string& skpName, int readFd, int writeFd) { std::string prefix{"skps/"}; std::string fileName{prefix + skpName + ".skp"}; auto skp = SkData::MakeFromFileName(fileName.c_str()); auto pic = SkPicture::MakeFromData(skp.get()); bool toGpu = true; SkSerialProcs procs; if (toGpu) { procs.fTypefaceProc = renderer_to_gpu_by_ID; } auto stream = pic->serialize(&procs); std::cerr << "stream is " << stream->size() << " bytes long" << std::endl; size_t picSize = stream->size(); uint8_t* picBuffer = (uint8_t*) stream->data(); if (!toGpu) { final_draw("test-direct.png", nullptr, picBuffer, picSize); close(writeFd); close(readFd); return 0; } write(writeFd, &picSize, sizeof(picSize)); size_t writeSoFar = 0; while (writeSoFar < picSize) { ssize_t writeSize = write(writeFd, &picBuffer[writeSoFar], picSize - writeSoFar); if (writeSize <= 0) { if (writeSize == 0) { std::cerr << "Exit" << std::endl; return 1; } perror("Can't write picture from render to GPU "); return 1; } writeSoFar += writeSize; } std::cerr << "Waiting for scaler context ops." << std::endl; static constexpr size_t kBufferSize = 1024 * kPageSize; std::unique_ptr glyphBuffer{new uint8_t[kBufferSize]}; Op* op = (Op*)glyphBuffer.get(); while (true) { ssize_t size = read(readFd, glyphBuffer.get(), sizeof(*op)); if (size <= 0) { std::cerr << "Exit op loop" << std::endl; break;} size_t writeSize = sizeof(*op); auto sc = scaler_context_from_op(op); switch (op->op) { case 0: { sc->getFontMetrics(&op->fontMetrics); break; } case 1: { sc->getMetrics(&op->glyph); break; } case 2: { // TODO: check for buffer overflow. op->glyph.fImage = &glyphBuffer[sizeof(Op)]; sc->getImage(op->glyph); writeSize += op->glyph.rowBytes() * op->glyph.fHeight; break; } case 3: { // TODO: check for buffer overflow. SkPath path; sc->getPath(op->glyphId, &path); op->pathSize = path.writeToMemory(&glyphBuffer[sizeof(Op)]); writeSize += op->pathSize; break; } default: SkASSERT("Bad op"); } write(writeFd, glyphBuffer.get(), writeSize); } close(readFd); close(writeFd); std::cerr << "Returning from render" << std::endl; return 0; } enum direction : int {kRead = 0, kWrite = 1}; static void start_gpu(int render_to_gpu[2], int gpu_to_render[2]) { std::cout << "gpu - Starting GPU" << std::endl; close(gpu_to_render[kRead]); close(render_to_gpu[kWrite]); gpu(render_to_gpu[kRead], gpu_to_render[kWrite]); } static void start_render(std::string& skpName, int render_to_gpu[2], int gpu_to_render[2]) { std::cout << "renderer - Starting Renderer" << std::endl; close(render_to_gpu[kRead]); close(gpu_to_render[kWrite]); renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]); } int main(int argc, char** argv) { std::string skpName = argc > 1 ? std::string{argv[1]} : std::string{"desk_nytimes"}; printf("skp: %s\n", skpName.c_str()); int render_to_gpu[2], gpu_to_render[2]; int r = pipe(render_to_gpu); if (r < 0) { perror("Can't write picture from render to GPU "); return 1; } r = pipe(gpu_to_render); if (r < 0) { perror("Can't write picture from render to GPU "); return 1; } bool useProcess = true; if (useProcess) { pid_t child = fork(); SkGraphics::Init(); if (child == 0) { start_render(skpName, render_to_gpu, gpu_to_render); } else { start_gpu(render_to_gpu, gpu_to_render); std::cerr << "Waiting for renderer." << std::endl; waitpid(child, nullptr, 0); } } else { SkGraphics::Init(); std::thread(gpu, render_to_gpu[kRead], gpu_to_render[kWrite]).detach(); renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]); } return 0; }