You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
9.5 KiB
327 lines
9.5 KiB
/*
|
|
* 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 <chrono>
|
|
#include <err.h>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/wait.h>
|
|
#include <thread>
|
|
#include <unistd.h>
|
|
|
|
#include "SkGraphics.h"
|
|
#include "SkRemoteGlyphCache.h"
|
|
#include "SkScalerContext.h"
|
|
#include "SkSurface.h"
|
|
|
|
static std::string gSkpName;
|
|
static bool gUseGpu = true;
|
|
static bool gPurgeFontCaches = true;
|
|
static bool gUseProcess = true;
|
|
|
|
class ServerDiscardableManager : public SkStrikeServer::DiscardableHandleManager {
|
|
public:
|
|
ServerDiscardableManager() = default;
|
|
~ServerDiscardableManager() override = default;
|
|
|
|
SkDiscardableHandleId createHandle() override { return ++nextHandleId; }
|
|
bool lockHandle(SkDiscardableHandleId handleId) override {
|
|
return handleId > lastPurgedHandleId;
|
|
}
|
|
void purgeAll() { lastPurgedHandleId = nextHandleId; }
|
|
|
|
private:
|
|
SkDiscardableHandleId nextHandleId = 0u;
|
|
SkDiscardableHandleId lastPurgedHandleId = 0u;
|
|
};
|
|
|
|
class ClientDiscardableManager : public SkStrikeClient::DiscardableHandleManager {
|
|
public:
|
|
class ScopedPurgeCache {
|
|
public:
|
|
ScopedPurgeCache(ClientDiscardableManager* manager) : fManager(manager) {
|
|
if (fManager) fManager->allowPurging = true;
|
|
}
|
|
~ScopedPurgeCache() {
|
|
if (fManager) fManager->allowPurging = false;
|
|
}
|
|
|
|
private:
|
|
ClientDiscardableManager* fManager;
|
|
};
|
|
|
|
ClientDiscardableManager() = default;
|
|
~ClientDiscardableManager() override = default;
|
|
|
|
bool deleteHandle(SkDiscardableHandleId) override { return allowPurging; }
|
|
|
|
private:
|
|
bool allowPurging = false;
|
|
};
|
|
|
|
static bool write_SkData(int fd, const SkData& data) {
|
|
size_t size = data.size();
|
|
ssize_t bytesWritten = ::write(fd, &size, sizeof(size));
|
|
if (bytesWritten < 0) {
|
|
err(1,"Failed write %zu", size);
|
|
return false;
|
|
}
|
|
|
|
bytesWritten = ::write(fd, data.data(), data.size());
|
|
if (bytesWritten < 0) {
|
|
err(1,"Failed write %zu", size);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static sk_sp<SkData> read_SkData(int fd) {
|
|
size_t size;
|
|
ssize_t readSize = ::read(fd, &size, sizeof(size));
|
|
if (readSize <= 0) {
|
|
if (readSize < 0) {
|
|
err(1, "Failed read %zu", size);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
auto out = SkData::MakeUninitialized(size);
|
|
auto data = (uint8_t*)out->data();
|
|
|
|
size_t totalRead = 0;
|
|
while (totalRead < size) {
|
|
ssize_t sizeRead;
|
|
sizeRead = ::read(fd, &data[totalRead], size - totalRead);
|
|
if (sizeRead <= 0) {
|
|
if (readSize < 0) {
|
|
err(1, "Failed read %zu", size);
|
|
}
|
|
return nullptr;
|
|
}
|
|
totalRead += sizeRead;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
class Timer {
|
|
public:
|
|
void start() {
|
|
fStart = std::chrono::high_resolution_clock::now();
|
|
}
|
|
|
|
void stop() {
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
fElapsedSeconds += end - fStart;
|
|
}
|
|
|
|
double elapsedSeconds() {
|
|
return fElapsedSeconds.count();
|
|
}
|
|
|
|
private:
|
|
decltype(std::chrono::high_resolution_clock::now()) fStart;
|
|
std::chrono::duration<double> fElapsedSeconds{0.0};
|
|
};
|
|
|
|
static bool push_font_data(const SkPicture& pic, SkStrikeServer* strikeServer, int writeFd) {
|
|
SkMatrix deviceMatrix = SkMatrix::I();
|
|
const SkIRect bounds = pic.cullRect().round();
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
SkTextBlobCacheDiffCanvas filter(bounds.width(), bounds.height(), deviceMatrix, props,
|
|
strikeServer);
|
|
pic.playback(&filter);
|
|
|
|
std::vector<uint8_t> fontData;
|
|
strikeServer->writeStrikeData(&fontData);
|
|
auto data = SkData::MakeWithoutCopy(fontData.data(), fontData.size());
|
|
return write_SkData(writeFd, *data);
|
|
}
|
|
|
|
static void final_draw(std::string outFilename, SkData* picData, SkStrikeClient* client,
|
|
ClientDiscardableManager* discardableManager, int readFd, int writeFd) {
|
|
SkDeserialProcs procs;
|
|
auto decode = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
|
|
return reinterpret_cast<SkStrikeClient*>(ctx)->deserializeTypeface(data, length);
|
|
};
|
|
procs.fTypefaceProc = decode;
|
|
procs.fTypefaceCtx = client;
|
|
|
|
auto pic = SkPicture::MakeFromData(picData, &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, &procs);
|
|
|
|
Timer drawTime;
|
|
auto randomData = SkData::MakeUninitialized(1u);
|
|
for (int i = 0; i < 100; i++) {
|
|
if (gPurgeFontCaches) {
|
|
ClientDiscardableManager::ScopedPurgeCache purge(discardableManager);
|
|
SkGraphics::PurgeFontCache();
|
|
SkASSERT(SkGraphics::GetFontCacheUsed() == 0u);
|
|
}
|
|
|
|
drawTime.start();
|
|
if (client != nullptr) {
|
|
// Kick the renderer to send us the fonts.
|
|
write_SkData(writeFd, *randomData);
|
|
auto fontData = read_SkData(readFd);
|
|
if (fontData && !fontData->isEmpty()) {
|
|
if (!client->readStrikeData(fontData->data(), fontData->size()))
|
|
SK_ABORT("Bad serialization");
|
|
}
|
|
}
|
|
c->drawPicture(picUnderTest);
|
|
drawTime.stop();
|
|
}
|
|
|
|
std::cout << "useProcess: " << gUseProcess
|
|
<< " useGPU: " << gUseGpu
|
|
<< " purgeCache: " << gPurgeFontCaches << std::endl;
|
|
fprintf(stderr, "%s use GPU %s elapsed time %8.6f s\n", gSkpName.c_str(),
|
|
gUseGpu ? "true" : "false", drawTime.elapsedSeconds());
|
|
|
|
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) {
|
|
|
|
if (gUseGpu) {
|
|
auto picData = read_SkData(readFd);
|
|
if (picData == nullptr) {
|
|
return;
|
|
}
|
|
|
|
sk_sp<ClientDiscardableManager> discardableManager = sk_make_sp<ClientDiscardableManager>();
|
|
SkStrikeClient strikeClient(discardableManager);
|
|
|
|
final_draw("test.png", picData.get(), &strikeClient, discardableManager.get(), readFd,
|
|
writeFd);
|
|
}
|
|
|
|
::close(writeFd);
|
|
::close(readFd);
|
|
|
|
printf("GPU is exiting\n");
|
|
}
|
|
|
|
static int renderer(
|
|
const std::string& skpName, int readFd, int writeFd)
|
|
{
|
|
ServerDiscardableManager discardableManager;
|
|
SkStrikeServer server(&discardableManager);
|
|
auto closeAll = [readFd, writeFd]() {
|
|
::close(writeFd);
|
|
::close(readFd);
|
|
};
|
|
|
|
auto skpData = SkData::MakeFromFileName(skpName.c_str());
|
|
std::cout << "skp stream is " << skpData->size() << " bytes long " << std::endl;
|
|
|
|
sk_sp<SkData> stream;
|
|
if (gUseGpu) {
|
|
auto pic = SkPicture::MakeFromData(skpData.get());
|
|
SkSerialProcs procs;
|
|
auto encode = [](SkTypeface* tf, void* ctx) -> sk_sp<SkData> {
|
|
return reinterpret_cast<SkStrikeServer*>(ctx)->serializeTypeface(tf);
|
|
};
|
|
procs.fTypefaceProc = encode;
|
|
procs.fTypefaceCtx = &server;
|
|
|
|
stream = pic->serialize(&procs);
|
|
|
|
if (!write_SkData(writeFd, *stream)) {
|
|
closeAll();
|
|
return 1;
|
|
}
|
|
|
|
while (true) {
|
|
auto inBuffer = read_SkData(readFd);
|
|
if (inBuffer == nullptr) {
|
|
closeAll();
|
|
return 0;
|
|
}
|
|
if (gPurgeFontCaches) discardableManager.purgeAll();
|
|
push_font_data(*pic.get(), &server, writeFd);
|
|
}
|
|
} else {
|
|
stream = skpData;
|
|
final_draw("test-correct.png", stream.get(), nullptr, nullptr, -1, -1);
|
|
closeAll();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
std::string skpName = argc > 1 ? std::string{argv[1]} : std::string{"skps/desk_nytimes.skp"};
|
|
int mode = argc > 2 ? atoi(argv[2]) : -1;
|
|
printf("skp: %s\n", skpName.c_str());
|
|
|
|
gSkpName = skpName;
|
|
|
|
enum direction : int {kRead = 0, kWrite = 1};
|
|
|
|
|
|
int render_to_gpu[2],
|
|
gpu_to_render[2];
|
|
|
|
for (int m = 0; m < 8; m++) {
|
|
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;
|
|
}
|
|
|
|
gPurgeFontCaches = (m & 4) == 4;
|
|
gUseGpu = (m & 2) == 2;
|
|
gUseProcess = (m & 1) == 1;
|
|
|
|
if (mode >= 0 && mode < 8 && mode != m) {
|
|
continue;
|
|
}
|
|
|
|
if (gUseProcess) {
|
|
pid_t child = fork();
|
|
SkGraphics::Init();
|
|
|
|
if (child == 0) {
|
|
close(gpu_to_render[kRead]);
|
|
close(render_to_gpu[kWrite]);
|
|
gpu(render_to_gpu[kRead], gpu_to_render[kWrite]);
|
|
} else {
|
|
close(render_to_gpu[kRead]);
|
|
close(gpu_to_render[kWrite]);
|
|
renderer(skpName, gpu_to_render[kRead], render_to_gpu[kWrite]);
|
|
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;
|
|
}
|
|
|