97f85bb7fd
Chromium has been updated to use makeAnalysisCanvas directly and there are no more references to SkTextBlobDiffCanvas as a type in its code base. Since the GlyphTrackingDevice extends SkNoPixelsDevice, any SkCanvas that uses it is effectively a "no-draw" canvas. However, by returning a base SkCanvas the text tracking now automatically happens in the context of the base's AutoLayerForImageFilter handling it applies on every draw. This means that drawing a text blob with an image filter that modifies the transform state will now be analyzed in that context automatically (simplifying code in chrome after this lands). Another behavioral change is that all non-text draws will still go through the base SkCanvas' virtuals and invoke the device function. Since it's an SkNoPixelsDevice, it'll still be a no-op, it just happens a little later. This won't really impact performance because oop-r already inspects their operations and only plays back text and transform related ones to the analysis canvas, so we shouldn't really see non-text draws being invoked anyways. Bug: chromium:1187246 Change-Id: I83f86571300751f385b3065dfe889f218fa1edc6 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/405196 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
327 lines
9.6 KiB
C++
327 lines
9.6 KiB
C++
/*
|
|
* 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 "include/core/SkGraphics.h"
|
|
#include "include/core/SkSurface.h"
|
|
#include "src/core/SkRemoteGlyphCache.h"
|
|
#include "src/core/SkScalerContext.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,
|
|
sk_sp<SkColorSpace> colorSpace, int writeFd) {
|
|
const SkIRect bounds = pic.cullRect().round();
|
|
const SkSurfaceProps props(0, kRGB_H_SkPixelGeometry);
|
|
std::unique_ptr<SkCanvas> filter = strikeServer->makeAnalysisCanvas(
|
|
bounds.width(), bounds.height(), props, std::move(colorSpace), true);
|
|
pic.playback(filter.get());
|
|
|
|
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());
|
|
auto colorSpace = SkColorSpace::MakeSRGB();
|
|
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, &server, colorSpace, 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;
|
|
}
|