ok, most features can be vias

This refactors most features out of ok's core into vias:
   -w    --> a .png dumping via, "png", opening the door to other types
   -m/-s --> a filtering via "filter"

Everything now can print a brief help message too.

Change-Id: I9e653aab98fd57182a6d458c7a80052130980284
Reviewed-on: https://skia-review.googlesource.com/10509
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Mike Klein <mtklein@chromium.org>
This commit is contained in:
Mike Klein 2017-03-29 12:41:13 -04:00 committed by Skia Commit-Bot
parent e0ff6ab272
commit e15a7b528e
6 changed files with 144 additions and 116 deletions

View File

@ -10,21 +10,20 @@
// * ok is entirely opt-in. No more maintaining huge --blacklists.
#include "SkGraphics.h"
#include "SkOSFile.h"
#include "ok.h"
#include <chrono>
#include <future>
#include <list>
#include <regex>
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <vector>
#if !defined(__has_include)
#define __has_include(x) 0
#endif
static thread_local const char* tls_name = "";
static thread_local const char* tls_currently_running = "";
#if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
#include <execinfo.h>
@ -53,7 +52,7 @@ static thread_local const char* tls_name = "";
#undef CASE
}
log(" while running '");
log(tls_name);
log(tls_currently_running);
log("'\n");
void* stack[128];
@ -81,7 +80,7 @@ static thread_local const char* tls_name = "";
void ok_log(const char* msg) {
lockf(log_fd, F_LOCK, 0);
log("[");
log(tls_name);
log(tls_currently_running);
log("]\t");
log(msg);
log("\n");
@ -97,8 +96,6 @@ static thread_local const char* tls_name = "";
}
#endif
enum class Status { OK, Failed, Crashed, Skipped, None };
struct Engine {
virtual ~Engine() {}
virtual bool spawn(std::function<Status(void)>) = 0;
@ -174,55 +171,54 @@ struct ThreadEngine : Engine {
#endif
struct StreamType {
const char* name;
const char *name, *help;
std::unique_ptr<Stream> (*factory)(Options);
};
static std::vector<StreamType> stream_types;
struct DstType {
const char* name;
const char *name, *help;
std::unique_ptr<Dst> (*factory)(Options);
};
static std::vector<DstType> dst_types;
struct ViaType {
const char* name;
const char *name, *help;
std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
};
static std::vector<ViaType> via_types;
template <typename T>
static std::string help_for(std::vector<T> registered) {
std::string help;
for (auto r : registered) {
help += "\n ";
help += r.name;
help += ": ";
help += r.help;
}
return help;
}
int main(int argc, char** argv) {
SkGraphics::Init();
setup_crash_handler();
int jobs {1};
std::regex match {".*"};
std::regex search {".*"};
std::string write_dir {""};
int jobs{1};
std::unique_ptr<Stream> stream;
std::function<std::unique_ptr<Dst>(void)> dst_factory;
std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
// A default Dst that's enough for unit tests and not much else.
struct : Dst {
Status draw(Src* src) override { return src->draw(nullptr); }
sk_sp<SkImage> image() override { return nullptr; }
} dst;
return move_unique(dst);
};
auto help = [&] {
std::string stream_names, dst_names, via_names;
for (auto s : stream_types) {
if (!stream_names.empty()) {
stream_names += ", ";
}
stream_names += s.name;
}
for (auto d : dst_types) {
if (!dst_names.empty()) {
dst_names += ", ";
}
dst_names += d.name;
}
for (auto v : via_types) {
if (!via_names.empty()) {
via_names += ", ";
}
via_names += v.name;
}
std::string stream_help = help_for(stream_types),
dst_help = help_for( dst_types),
via_help = help_for( via_types);
printf("%s [-j N] [-m regex] [-s regex] [-w dir] [-h] \n"
" src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...] \n"
@ -230,23 +226,17 @@ int main(int argc, char** argv) {
" If <0, use -N threads instead. \n"
" If 0, use one thread in one process. \n"
" If 1 (default) or -1, auto-detect N. \n"
" -m: Run only names matching regex exactly. \n"
" -s: Run only names matching regex anywhere. \n"
" -w: If set, write .pngs into dir. \n"
" -h: Print this message and exit. \n"
" src: content to draw: %s \n"
" dst: how to draw that content: %s \n"
" via: front-patches to the dst: %s \n"
" Some srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
argv[0], stream_names.c_str(), dst_names.c_str(), via_names.c_str());
" src: content to draw%s \n"
" dst: how to draw that content%s \n"
" via: wrappers around dst%s \n"
" Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
argv[0], stream_help.c_str(), dst_help.c_str(), via_help.c_str());
return 1;
};
for (int i = 1; i < argc; i++) {
if (0 == strcmp("-j", argv[i])) { jobs = atoi(argv[++i]); }
if (0 == strcmp("-m", argv[i])) { match = argv[++i] ; }
if (0 == strcmp("-s", argv[i])) { search = argv[++i] ; }
if (0 == strcmp("-w", argv[i])) { write_dir = argv[++i] ; }
if (0 == strcmp("-j", argv[i])) { jobs = atoi(argv[++i]); }
if (0 == strcmp("-h", argv[i])) { return help(); }
for (auto s : stream_types) {
@ -283,16 +273,6 @@ int main(int argc, char** argv) {
}
}
if (!stream) { return help(); }
if (!dst_factory) {
// A default Dst that's enough for unit tests and not much else.
dst_factory = []{
struct : Dst {
bool draw(Src* src) override { return src->draw(nullptr); }
sk_sp<SkImage> image() override { return nullptr; }
} dst;
return move_unique(dst);
};
}
std::unique_ptr<Engine> engine;
if (jobs == 0) { engine.reset(new SerialEngine); }
@ -301,10 +281,6 @@ int main(int argc, char** argv) {
if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }
if (!write_dir.empty()) {
sk_mkdir(write_dir.c_str());
}
int ok = 0, failed = 0, crashed = 0, skipped = 0;
auto update_stats = [&](Status s) {
@ -343,25 +319,10 @@ int main(int argc, char** argv) {
spawn([=] {
std::unique_ptr<Src> src{raw};
auto name = src->name();
tls_name = name.c_str();
if (!std::regex_match (name, match) ||
!std::regex_search(name, search)) {
return Status::Skipped;
}
std::string name = src->name();
tls_currently_running = name.c_str();
auto dst = dst_factory();
if (!dst->draw(src.get())) {
return Status::Failed;
}
if (!write_dir.empty()) {
auto image = dst->image();
sk_sp<SkData> png{image->encode()};
SkFILEWStream{(write_dir + "/" + name + ".png").c_str()}
.write(png->data(), png->size());
}
return Status::OK;
return dst_factory()->draw(src.get());
});
}
@ -374,15 +335,17 @@ int main(int argc, char** argv) {
}
Register::Register(const char* name, std::unique_ptr<Stream> (*factory)(Options)) {
stream_types.push_back(StreamType{name, factory});
Register::Register(const char* name, const char* help,
std::unique_ptr<Stream> (*factory)(Options)) {
stream_types.push_back(StreamType{name, help, factory});
}
Register::Register(const char* name, std::unique_ptr<Dst> (*factory)(Options)) {
dst_types.push_back(DstType{name, factory});
Register::Register(const char* name, const char* help,
std::unique_ptr<Dst> (*factory)(Options)) {
dst_types.push_back(DstType{name, help, factory});
}
Register::Register(const char* name,
Register::Register(const char* name, const char* help,
std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
via_types.push_back(ViaType{name, factory});
via_types.push_back(ViaType{name, help, factory});
}
Options::Options(std::string str) {

View File

@ -22,11 +22,13 @@ static std::unique_ptr<T> move_unique(T& v) {
void ok_log(const char*);
enum class Status { OK, Failed, Crashed, Skipped, None };
struct Src {
virtual ~Src() {}
virtual std::string name() = 0;
virtual SkISize size() = 0;
virtual bool draw(SkCanvas*) = 0;
virtual std::string name() = 0;
virtual SkISize size() = 0;
virtual Status draw(SkCanvas*) = 0;
};
struct Stream {
@ -36,7 +38,7 @@ struct Stream {
struct Dst {
virtual ~Dst() {}
virtual bool draw(Src*) = 0;
virtual Status draw(Src*) = 0;
virtual sk_sp<SkImage> image() = 0;
};
@ -50,9 +52,10 @@ public:
// Create globals to register your new type of Stream or Dst.
struct Register {
Register(const char* name, std::unique_ptr<Stream> (*factory)(Options));
Register(const char* name, std::unique_ptr<Dst> (*factory)(Options));
Register(const char* name, std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>));
Register(const char* name, const char* help, std::unique_ptr<Stream> (*factory)(Options));
Register(const char* name, const char* help, std::unique_ptr<Dst> (*factory)(Options));
Register(const char* name, const char* help,
std::unique_ptr<Dst>(*factory)(Options, std::unique_ptr<Dst>));
};
#endif//ok_DEFINED

View File

@ -22,7 +22,7 @@ struct SWDst : Dst {
return move_unique(dst);
}
bool draw(Src* src) override {
Status draw(Src* src) override {
auto size = src->size();
surface = SkSurface::MakeRaster(info.makeWH(size.width(), size.height()));
return src->draw(surface->getCanvas());
@ -32,9 +32,9 @@ struct SWDst : Dst {
return surface->makeImageSnapshot();
}
};
static Register sw{"sw", SWDst::Create};
static Register sw{"sw", "draw with the software backend", SWDst::Create};
static Register _565{"565", [](Options options) {
static Register _565{"565", "alias for sw:ct=565", [](Options options) {
options["ct"] = "565";
return SWDst::Create(options);
}};

View File

@ -38,12 +38,12 @@ struct GMStream : Stream {
return gm->getISize();
}
bool draw(SkCanvas* canvas) override {
Status draw(SkCanvas* canvas) override {
this->init();
canvas->clear(0xffffffff);
canvas->concat(gm->getInitialTransform());
gm->draw(canvas);
return true;
return Status::OK;
}
};
@ -57,7 +57,7 @@ struct GMStream : Stream {
return move_unique(src);
}
};
static Register gm{"gm", GMStream::Create};
static Register gm{"gm", "draw GMs linked into this binary", GMStream::Create};
struct SKPStream : Stream {
std::string dir;
@ -92,11 +92,11 @@ struct SKPStream : Stream {
return pic->cullRect().roundOut().size();
}
bool draw(SkCanvas* canvas) override {
Status draw(SkCanvas* canvas) override {
this->init();
canvas->clear(0xffffffff);
pic->playback(canvas);
return true;
return Status::OK;
}
};
@ -111,4 +111,4 @@ struct SKPStream : Stream {
return move_unique(src);
}
};
static Register skp{"skp", SKPStream::Create};
static Register skp{"skp", "draw SKPs from dir=skps", SKPStream::Create};

View File

@ -31,15 +31,15 @@ struct TestStream : Stream {
std::string name() override { return test.name; }
SkISize size() override { return {0,0}; }
bool draw(SkCanvas*) override {
Status draw(SkCanvas*) override {
struct : public skiatest::Reporter {
bool ok = true;
Status status = Status::OK;
bool extended, verbose_;
void reportFailed(const skiatest::Failure& failure) override {
ok_log(failure.toString().c_str());
ok = false;
status = Status::Failed;
}
bool allowExtendedTest() const override { return extended; }
bool verbose() const override { return verbose_; }
@ -55,7 +55,7 @@ struct TestStream : Stream {
#endif
test.proc(&reporter, factory);
return reporter.ok;
return reporter.status;
}
};
@ -71,7 +71,7 @@ struct TestStream : Stream {
return move_unique(src);
}
};
static Register test{"test", TestStream::Create};
static Register test{"test", "run unit tests linked into this binary", TestStream::Create};
// Hey, now why were these defined in DM.cpp? That's kind of weird.
namespace skiatest {

View File

@ -5,17 +5,19 @@
* found in the LICENSE file.
*/
#include "ok.h"
#include "SkOSFile.h"
#include "SkPictureRecorder.h"
#include "ok.h"
#include <regex>
static std::unique_ptr<Src> proxy(Src* original, std::function<bool(SkCanvas*)> fn) {
static std::unique_ptr<Src> proxy(Src* original, std::function<Status(SkCanvas*)> fn) {
struct : Src {
Src* original;
std::function<bool(SkCanvas*)> fn;
Src* original;
std::function<Status(SkCanvas*)> fn;
std::string name() override { return original->name(); }
SkISize size() override { return original->size(); }
bool draw(SkCanvas* canvas) override { return fn(canvas); }
Status draw(SkCanvas* canvas) override { return fn(canvas); }
} src;
src.original = original;
src.fn = fn;
@ -33,20 +35,20 @@ struct ViaPic : Dst {
return move_unique(via);
}
bool draw(Src* src) override {
Status draw(Src* src) override {
SkRTreeFactory factory;
SkPictureRecorder rec;
rec.beginRecording(SkRect::MakeSize(SkSize::Make(src->size())),
rtree ? &factory : nullptr);
if (!src->draw(rec.getRecordingCanvas())) {
return false;
for (auto status = src->draw(rec.getRecordingCanvas()); status != Status::OK; ) {
return status;
}
auto pic = rec.finishRecordingAsPicture();
return target->draw(proxy(src, [=](SkCanvas* canvas) {
pic->playback(canvas);
return true;
return Status::OK;
}).get());
}
@ -54,4 +56,64 @@ struct ViaPic : Dst {
return target->image();
}
};
static Register via_pic{"via_pic", ViaPic::Create};
static Register via_pic{"via_pic", "record then play back an SkPicture", ViaPic::Create};
struct Png : Dst {
std::unique_ptr<Dst> target;
std::string dir;
static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) {
Png via;
via.target = std::move(dst);
via.dir = options("dir", "ok");
return move_unique(via);
}
Status draw(Src* src) override {
for (auto status = target->draw(src); status != Status::OK; ) {
return status;
}
auto image = target->image();
sk_sp<SkData> png{image->encode()};
sk_mkdir(dir.c_str());
SkFILEWStream{(dir + "/" + src->name() + ".png").c_str()}
.write(png->data(), png->size());
return Status::OK;
}
sk_sp<SkImage> image() override {
return target->image();
}
};
static Register png{"png", "dump PNGs to dir=ok" , Png::Create};
struct Filter : Dst {
std::unique_ptr<Dst> target;
std::regex match, search;
static std::unique_ptr<Dst> Create(Options options, std::unique_ptr<Dst> dst) {
Filter via;
via.target = std::move(dst);
via.match = options("match", ".*");
via.search = options("search", ".*");
return move_unique(via);
}
Status draw(Src* src) override {
auto name = src->name();
if (!std::regex_match (name, match) ||
!std::regex_search(name, search)) {
return Status::Skipped;
}
return target->draw(src);
}
sk_sp<SkImage> image() override {
return target->image();
}
};
struct Register filter{"filter",
"run only srcs matching match=.* exactly and search=.* somewhere",
Filter::Create};