dm is like gm, but faster and with fewer features.

This is sort of the near-minimal proof-of-concept skeleton.

  - It can run existing GMs.
  - It supports most configs (just not PDF).
  - --replay is the only "fancy" feature it currently supports

Hopefully you will be disturbed by its speed.

BUG=
R=epoger@google.com

Review URL: https://codereview.chromium.org/22839016

git-svn-id: http://skia.googlecode.com/svn/trunk@11802 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
mtklein@google.com 2013-10-16 13:02:15 +00:00
parent beede90eae
commit d36522d12d
20 changed files with 852 additions and 1 deletions

166
dm/DM.cpp Normal file
View File

@ -0,0 +1,166 @@
// Main binary for DM.
// For a high-level overview, please see dm/README.
#include "GrContext.h"
#include "GrContextFactory.h"
#include "SkCommandLineFlags.h"
#include "SkForceLinking.h"
#include "SkGraphics.h"
#include "gm.h"
#include "DMReporter.h"
#include "DMTask.h"
#include "DMTaskRunner.h"
#include "DMCpuTask.h"
#include "DMGpuTask.h"
#include <string.h>
using skiagm::GM;
using skiagm::GMRegistry;
using skiagm::Expectations;
using skiagm::ExpectationsSource;
using skiagm::JsonExpectationsSource;
DEFINE_int32(cpuThreads, -1, "Threads for CPU work. Default NUM_CPUS.");
DEFINE_int32(gpuThreads, 1, "Threads for GPU work.");
DEFINE_string(expectations, "", "Compare generated images against JSON expectations at this path.");
DEFINE_string(resources, "resources", "Path to resources directory.");
DEFINE_string(match, "", "[~][^]substring[$] [...] of GM name to run.\n"
"Multiple matches may be separated by spaces.\n"
"~ causes a matching GM to always be skipped\n"
"^ requires the start of the GM to match\n"
"$ requires the end of the GM to match\n"
"^ and $ requires an exact match\n"
"If a GM does not match any list entry,\n"
"it is skipped unless some list entry starts with ~");
DEFINE_string(config, "8888 gpu",
"Options: 565 8888 gpu msaa4 msaa16 gpunull gpudebug angle mesa"); // TODO(mtklein): pdf
__SK_FORCE_IMAGE_DECODER_LINKING;
// Split str on any characters in delimiters into out. (Think, strtok with a sane API.)
static void split(const char* str, const char* delimiters, SkTArray<SkString>* out) {
const char* end = str + strlen(str);
while (str != end) {
// Find a token.
const size_t len = strcspn(str, delimiters);
out->push_back().set(str, len);
str += len;
// Skip any delimiters.
str += strspn(str, delimiters);
}
}
// "FooBar" -> "foobar". Obviously, ASCII only.
static SkString lowercase(SkString s) {
for (size_t i = 0; i < s.size(); i++) {
s[i] = tolower(s[i]);
}
return s;
}
static void kick_off_tasks(const SkTDArray<GMRegistry::Factory>& gms,
const SkTArray<SkString>& configs,
const ExpectationsSource& expectations,
DM::Reporter* reporter,
DM::TaskRunner* tasks) {
const SkBitmap::Config _565 = SkBitmap::kRGB_565_Config;
const SkBitmap::Config _8888 = SkBitmap::kARGB_8888_Config;
const GrContextFactory::GLContextType native = GrContextFactory::kNative_GLContextType;
const GrContextFactory::GLContextType null = GrContextFactory::kNull_GLContextType;
const GrContextFactory::GLContextType debug = GrContextFactory::kDebug_GLContextType;
const GrContextFactory::GLContextType angle =
#if SK_ANGLE
GrContextFactory::kANGLE_GLContextType;
#else
native;
#endif
const GrContextFactory::GLContextType mesa =
#if SK_MESA
GLContextFactory::kMESA_GLContextType;
#else
native;
#endif
for (int i = 0; i < gms.count(); i++) {
SkAutoTDelete<GM> gmForName(gms[i](NULL));
if (SkCommandLineFlags::ShouldSkip(FLAGS_match, gmForName->shortName())) continue;
#define START(name, type, ...) \
if (lowercase(configs[j]).equals(name)) { \
tasks->add(SkNEW_ARGS(DM::type, \
(name, reporter, tasks, expectations, gms[i], __VA_ARGS__))); \
}
for (int j = 0; j < configs.count(); j++) {
START("565", CpuTask, _565);
START("8888", CpuTask, _8888);
START("gpu", GpuTask, _8888, native, 0);
START("msaa4", GpuTask, _8888, native, 4);
START("msaa16", GpuTask, _8888, native, 16);
START("gpunull", GpuTask, _8888, null, 0);
START("gpudebug", GpuTask, _8888, debug, 0);
START("angle", GpuTask, _8888, angle, 0);
START("mesa", GpuTask, _8888, mesa, 0);
//START("pdf", PdfTask, _8888);
}
}
#undef START
}
static void report_failures(const DM::Reporter& reporter) {
SkTArray<SkString> failures;
reporter.getFailures(&failures);
if (failures.count() == 0) {
return;
}
SkDebugf("Failures:\n");
for (int i = 0; i < failures.count(); i++) {
SkDebugf(" %s\n", failures[i].c_str());
}
}
class NoExpectations : public ExpectationsSource {
public:
Expectations get(const char* /*testName*/) const SK_OVERRIDE {
return Expectations();
}
};
int main(int argc, char** argv) {
SkGraphics::Init();
SkCommandLineFlags::Parse(argc, argv);
GM::SetResourcePath(FLAGS_resources[0]);
SkTArray<SkString> configs;
for (int i = 0; i < FLAGS_config.count(); i++) {
split(FLAGS_config[i], ", ", &configs);
}
SkTDArray<GMRegistry::Factory> gms;
for (const GMRegistry* reg = GMRegistry::Head(); reg != NULL; reg = reg->next()) {
*gms.append() = reg->factory();
}
SkDebugf("%d GMs x %d configs\n", gms.count(), configs.count());
SkAutoTUnref<ExpectationsSource> expectations(SkNEW(NoExpectations));
if (FLAGS_expectations.count() > 0) {
expectations.reset(SkNEW_ARGS(JsonExpectationsSource, (FLAGS_expectations[0])));
}
DM::Reporter reporter;
DM::TaskRunner tasks(FLAGS_cpuThreads, FLAGS_gpuThreads);
kick_off_tasks(gms, configs, *expectations, &reporter, &tasks);
tasks.wait();
reporter.updateStatusLine();
SkDebugf("\n");
report_failures(reporter);
SkGraphics::Term();
return reporter.failed() > 0;
}

22
dm/DMComparisonTask.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "DMComparisonTask.h"
#include "DMUtil.h"
namespace DM {
ComparisonTask::ComparisonTask(const Task& parent,
skiagm::Expectations expectations,
SkBitmap bitmap)
: Task(parent)
, fName(parent.name()) // Masquerade as parent so failures are attributed to it.
, fExpectations(expectations)
, fBitmap(bitmap)
{}
void ComparisonTask::draw() {
const skiagm::GmResultDigest digest(fBitmap);
if (!meetsExpectations(fExpectations, digest)) {
this->fail();
}
}
} // namespace DM

31
dm/DMComparisonTask.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef DMComparisonTask_DEFINED
#define DMComparisonTask_DEFINED
#include "DMTask.h"
#include "SkBitmap.h"
#include "SkString.h"
#include "gm_expectations.h"
namespace DM {
// We use ComparisonTask to move CPU-bound comparison work of GpuTasks back to
// the main thread pool, where we probably have more threads available.
class ComparisonTask : public Task {
public:
ComparisonTask(const Task& parent, skiagm::Expectations, SkBitmap);
virtual void draw() SK_OVERRIDE;
virtual bool usesGpu() const SK_OVERRIDE { return false; }
virtual bool shouldSkip() const SK_OVERRIDE { return false; }
virtual SkString name() const SK_OVERRIDE { return fName; }
private:
const SkString fName;
const skiagm::Expectations fExpectations;
const SkBitmap fBitmap;
};
} // namespace DM
#endif // DMComparisonTask_DEFINED

57
dm/DMCpuTask.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "DMCpuTask.h"
#include "DMReplayTask.h"
#include "DMUtil.h"
#include "SkCommandLineFlags.h"
DEFINE_bool(replay, false, "If true, run replay tests for each CpuTask.");
// TODO(mtklein): add the other various options
namespace DM {
CpuTask::CpuTask(const char* name,
Reporter* reporter,
TaskRunner* taskRunner,
const skiagm::ExpectationsSource& expectations,
skiagm::GMRegistry::Factory gmFactory,
SkBitmap::Config config)
: Task(reporter, taskRunner)
, fGMFactory(gmFactory)
, fGM(fGMFactory(NULL))
, fName(underJoin(fGM->shortName(), name))
, fExpectations(expectations.get(png(fName).c_str()))
, fConfig(config)
{}
void CpuTask::draw() {
SkBitmap bitmap;
bitmap.setConfig(fConfig, fGM->width(), fGM->height());
bitmap.allocPixels();
bitmap.eraseColor(0x00000000);
SkCanvas canvas(bitmap);
canvas.concat(fGM->getInitialTransform());
fGM->draw(&canvas);
canvas.flush();
const skiagm::GmResultDigest digest(bitmap);
if (!meetsExpectations(fExpectations, digest)) {
this->fail();
}
if (FLAGS_replay) {
this->spawnChild(SkNEW_ARGS(ReplayTask,
("replay", *this, fGMFactory(NULL), digest, fConfig)));
}
}
bool CpuTask::shouldSkip() const {
if (SkBitmap::kRGB_565_Config == fConfig && (fGM->getFlags() & skiagm::GM::kSkip565_Flag)) {
return true;
}
if (fGM->getFlags() & skiagm::GM::kGPUOnly_Flag) {
return true;
}
return false;
}
} // namespace DM

44
dm/DMCpuTask.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef DMCpuTask_DEFINED
#define DMCpuTask_DEFINED
#include "DMReporter.h"
#include "DMTask.h"
#include "DMTaskRunner.h"
#include "SkBitmap.h"
#include "SkString.h"
#include "SkTemplates.h"
#include "gm.h"
#include "gm_expectations.h"
// This is the main entry point for drawing GMs with the CPU. Commandline
// flags control whether this kicks off various comparison tasks when done.
// Currently:
// --replay: spawn a DMReplayTask to record into a picture, draw the picture, and compare.
namespace DM {
class CpuTask : public Task {
public:
CpuTask(const char* name,
Reporter*,
TaskRunner*,
const skiagm::ExpectationsSource&,
skiagm::GMRegistry::Factory,
SkBitmap::Config);
virtual void draw() SK_OVERRIDE;
virtual bool usesGpu() const SK_OVERRIDE { return false; }
virtual bool shouldSkip() const SK_OVERRIDE;
virtual SkString name() const SK_OVERRIDE { return fName; }
private:
skiagm::GMRegistry::Factory fGMFactory;
SkAutoTDelete<skiagm::GM> fGM;
const SkString fName;
const skiagm::Expectations fExpectations;
const SkBitmap::Config fConfig;
};
} // namespace DM
#endif // DMCpuTask_DEFINED

63
dm/DMGpuTask.cpp Normal file
View File

@ -0,0 +1,63 @@
#include "DMGpuTask.h"
#include "DMComparisonTask.h"
#include "DMUtil.h"
#include "SkCommandLineFlags.h"
#include "SkGpuDevice.h"
#include "SkTLS.h"
namespace DM {
GpuTask::GpuTask(const char* name,
Reporter* reporter,
TaskRunner* taskRunner,
const skiagm::ExpectationsSource& expectations,
skiagm::GMRegistry::Factory gmFactory,
SkBitmap::Config config,
GrContextFactory::GLContextType contextType,
int sampleCount)
: Task(reporter, taskRunner)
, fGM(gmFactory(NULL))
, fName(underJoin(fGM->shortName(), name))
, fExpectations(expectations.get(png(fName).c_str()))
, fConfig(config)
, fContextType(contextType)
, fSampleCount(sampleCount)
{}
static void* new_gr_context_factory() {
return SkNEW(GrContextFactory);
}
static void delete_gr_context_factory(void* factory) {
return SkDELETE((GrContextFactory*) factory);
}
static GrContextFactory* get_gr_factory() {
return reinterpret_cast<GrContextFactory*>(SkTLS::Get(&new_gr_context_factory,
&delete_gr_context_factory));
}
void GpuTask::draw() {
GrContext* gr = get_gr_factory()->get(fContextType); // Will be owned by device.
SkGpuDevice device(gr, fConfig, fGM->width(), fGM->height(), fSampleCount);
SkCanvas canvas(&device);
canvas.concat(fGM->getInitialTransform());
fGM->draw(&canvas);
canvas.flush();
SkBitmap bitmap;
bitmap.setConfig(fConfig, fGM->width(), fGM->height());
canvas.readPixels(&bitmap, 0, 0);
// We offload checksum comparison to the main CPU threadpool.
// This cuts run time by about 30%.
this->spawnChild(SkNEW_ARGS(ComparisonTask, (*this, fExpectations, bitmap)));
}
bool GpuTask::shouldSkip() const {
return fGM->getFlags() & skiagm::GM::kSkipGPU_Flag;
}
} // namespace DM

45
dm/DMGpuTask.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef DMGpuTask_DEFINED
#define DMGpuTask_DEFINED
#include "DMReporter.h"
#include "DMTask.h"
#include "DMTaskRunner.h"
#include "GrContextFactory.h"
#include "SkBitmap.h"
#include "SkString.h"
#include "SkTemplates.h"
#include "gm.h"
#include "gm_expectations.h"
// This is the main entry point for drawing GMs with the GPU.
namespace DM {
class GpuTask : public Task {
public:
GpuTask(const char* name,
Reporter*,
TaskRunner*,
const skiagm::ExpectationsSource&,
skiagm::GMRegistry::Factory,
SkBitmap::Config,
GrContextFactory::GLContextType,
int sampleCount);
virtual void draw() SK_OVERRIDE;
virtual bool usesGpu() const SK_OVERRIDE { return true; }
virtual bool shouldSkip() const SK_OVERRIDE;
virtual SkString name() const SK_OVERRIDE { return fName; }
private:
SkAutoTDelete<skiagm::GM> fGM;
const SkString fName;
const skiagm::Expectations fExpectations;
const SkBitmap::Config fConfig;
const GrContextFactory::GLContextType fContextType;
const int fSampleCount;
};
} // namespace DM
#endif // DMGpuTask_DEFINED

50
dm/DMReplayTask.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "DMReplayTask.h"
#include "DMUtil.h"
#include "SkPicture.h"
namespace DM {
ReplayTask::ReplayTask(const char* suffix,
const Task& parent,
skiagm::GM* gm,
skiagm::GmResultDigest reference,
SkBitmap::Config config)
: Task(parent)
, fName(underJoin(parent.name().c_str(), suffix))
, fGM(gm)
, fReference(reference)
, fConfig(config)
{}
void ReplayTask::draw() {
SkPicture picture;
SkCanvas* canvas = picture.beginRecording(fGM->width(), fGM->height(), 0 /*flags*/);
canvas->concat(fGM->getInitialTransform());
fGM->draw(canvas);
canvas->flush();
picture.endRecording();
SkBitmap bitmap;
bitmap.setConfig(fConfig, fGM->width(), fGM->height());
bitmap.allocPixels();
bitmap.eraseColor(0x00000000);
SkCanvas replay(bitmap);
replay.drawPicture(picture);
replay.flush();
const skiagm::GmResultDigest replayDigest(bitmap);
if (!replayDigest.equals(fReference)) {
this->fail();
}
}
bool ReplayTask::shouldSkip() const {
return fGM->getFlags() & skiagm::GM::kGPUOnly_Flag ||
fGM->getFlags() & skiagm::GM::kSkipPicture_Flag;
}
} // namespace

40
dm/DMReplayTask.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef DMReplayTask_DEFINED
#define DMReplayTask_DEFINED
#include "DMReporter.h"
#include "DMTask.h"
#include "DMTaskRunner.h"
#include "SkBitmap.h"
#include "SkString.h"
#include "SkTemplates.h"
#include "gm.h"
#include "gm_expectations.h"
// Records a GM through an SkPicture, draws it, and compares against the reference checksum.
namespace DM {
class ReplayTask : public Task {
public:
ReplayTask(const char* name,
const Task& parent,
skiagm::GM*,
skiagm::GmResultDigest reference,
SkBitmap::Config);
virtual void draw() SK_OVERRIDE;
virtual bool usesGpu() const SK_OVERRIDE { return false; }
virtual bool shouldSkip() const SK_OVERRIDE;
virtual SkString name() const SK_OVERRIDE { return fName; }
private:
const SkString fName;
SkAutoTDelete<skiagm::GM> fGM;
const skiagm::GmResultDigest fReference;
const SkBitmap::Config fConfig;
};
} // namespace DM
#endif // DMReplayTask_DEFINED

24
dm/DMReporter.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "DMReporter.h"
namespace DM {
void Reporter::updateStatusLine() const {
SkDebugf("\r\033[K%d / %d, %d failed", this->finished(), this->started(), this->failed());
}
int32_t Reporter::failed() const {
SkAutoMutexAcquire reader(&fMutex);
return fFailures.count();
}
void Reporter::fail(SkString name) {
SkAutoMutexAcquire writer(&fMutex);
fFailures.push_back(name);
}
void Reporter::getFailures(SkTArray<SkString>* failures) const {
SkAutoMutexAcquire reader(&fMutex);
*failures = fFailures;
}
} // namespace DM

39
dm/DMReporter.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef DMReporter_DEFINED
#define DMReporter_DEFINED
#include "SkString.h"
#include "SkTArray.h"
#include "SkThread.h"
#include "SkTypes.h"
// Used to report status changes including failures. All public methods are threadsafe.
namespace DM {
class Reporter : SkNoncopyable {
public:
Reporter() : fStarted(0), fFinished(0) {}
void start() { sk_atomic_inc(&fStarted); }
void finish() { sk_atomic_inc(&fFinished); }
void fail(SkString name);
int32_t started() const { return fStarted; }
int32_t finished() const { return fFinished; }
int32_t failed() const;
void updateStatusLine() const;
void getFailures(SkTArray<SkString>*) const;
private:
int32_t fStarted, fFinished;
mutable SkMutex fMutex; // Guards fFailures.
SkTArray<SkString> fFailures;
};
} // namespace DM
#endif // DMReporter_DEFINED

42
dm/DMTask.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "DMTask.h"
#include "DMTaskRunner.h"
#include "DMUtil.h"
#include "SkBitmap.h"
#include "SkCommandLineFlags.h"
namespace DM {
Task::Task(Reporter* reporter, TaskRunner* taskRunner)
: fReporter(reporter), fTaskRunner(taskRunner) {
fReporter->start();
}
Task::Task(const Task& that) : fReporter(that.fReporter), fTaskRunner(that.fTaskRunner) {
fReporter->start();
}
Task::~Task() {}
void Task::run() {
if (!this->shouldSkip()) {
this->draw();
}
fReporter->finish();
fReporter->updateStatusLine();
delete this;
}
void Task::spawnChild(Task* task) {
if (!task->usesGpu()) {
fTaskRunner->add(task);
} else {
SkDEBUGFAIL("Sorry, we can't spawn GPU tasks. :( See comment in TaskRunner::wait().");
}
}
void Task::fail() {
fReporter->fail(this->name());
}
} // namespace DM

43
dm/DMTask.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef DMTask_DEFINED
#define DMTask_DEFINED
#include "DMReporter.h"
#include "SkRunnable.h"
#include "SkThreadPool.h"
// DM will run() these tasks on one of two threadpools, depending on the result
// of usesGpu(). The subclasses can call fail() to mark this task as failed,
// or make any number of spawnChild() calls to kick off dependent tasks.
//
// Task deletes itself when run.
namespace DM {
class TaskRunner;
class Task : public SkRunnable {
public:
Task(Reporter* reporter, TaskRunner* taskRunner);
Task(const Task& that);
virtual ~Task();
void run();
virtual void draw() = 0;
virtual bool usesGpu() const = 0;
virtual bool shouldSkip() const = 0;
virtual SkString name() const = 0;
protected:
void spawnChild(Task* task);
void fail();
private:
// Both unowned.
Reporter* fReporter;
TaskRunner* fTaskRunner;
};
} // namespace DM
#endif // DMTask_DEFINED

28
dm/DMTaskRunner.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "DMTaskRunner.h"
#include "DMTask.h"
namespace DM {
TaskRunner::TaskRunner(int cputhreads, int gpuThreads)
: fMain(cputhreads)
, fGpu(gpuThreads)
{}
void TaskRunner::add(Task* task) {
if (task->usesGpu()) {
fGpu.add(task);
} else {
fMain.add(task);
}
}
void TaskRunner::wait() {
// These wait calls block until the threadpool is done. We don't allow
// children to spawn new GPU tasks so we can wait for that first knowing
// we'll never try to add to it later. Same can't be said of fMain: fGpu
// and fMain can both add tasks to fMain, so we have to wait for that last.
fGpu.wait();
fMain.wait();
}
} // namespace DM

28
dm/DMTaskRunner.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef DMTaskRunner_DEFINED
#define DMTaskRunner_DEFINED
#include "SkThreadPool.h"
#include "SkTypes.h"
// TaskRunner runs Tasks on one of two threadpools depending on the Task's usesGpu() method.
// This lets us drive the GPU with a small number of threads (e.g. 2 or 4 can be faster than 1)
// while not swamping it with requests from the full fleet of threads that CPU-bound tasks run on.
namespace DM {
class Task;
class TaskRunner : SkNoncopyable {
public:
TaskRunner(int cputhreads, int gpuThreads);
void add(Task* task);
void wait();
private:
SkThreadPool fMain, fGpu;
};
} // namespace DM
#endif // DMTaskRunner_DEFINED

23
dm/DMUtil.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "DMUtil.h"
namespace DM {
SkString underJoin(const char* a, const char* b) {
SkString s;
s.appendf("%s_%s", a, b);
return s;
}
SkString png(SkString s) {
s.appendf(".png");
return s;
}
bool meetsExpectations(const skiagm::Expectations& expectations,
const skiagm::GmResultDigest& digest) {
return expectations.ignoreFailure()
|| expectations.empty()
|| expectations.match(digest);
}
} // namespace DM

23
dm/DMUtil.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef DMUtil_DEFINED
#define DMUtil_DEFINED
#include "SkString.h"
#include "gm_expectations.h"
// Small free functions used in more than one place in DM.
namespace DM {
// underJoin("a", "b") -> "a_b"
SkString underJoin(const char* a, const char* b);
// png("a") -> "a.png"
SkString png(SkString s);
// Roughly, expectations.match(digest), but only does it if we're not ignoring the result.
bool meetsExpectations(const skiagm::Expectations& expectations,
const skiagm::GmResultDigest& digest);
} // namespace DM
#endif // DMUtil_DEFINED

37
dm/README Normal file
View File

@ -0,0 +1,37 @@
DM is like GM, but multithreaded. It doesn't do everything GM does yet.
Current approximate list of missing features:
--mismatchPath
--missingExpectationsPath
--writePath
--writePicturePath
--deferred / --pipe
--rtree
--serialize
--tiledGrid
DM's design is based around Tasks and a TaskRunner.
A Task represents an independent unit of work that might fail. We make a task
for each GM/configuration pair we want to run. Tasks can kick off new tasks
themselves. For example, a CpuTask can kick off a ReplayTask to make sure
recording and playing back an SkPicture gives the same result as direct
rendering.
The TaskRunner runs all tasks on one of two threadpools, whose sizes are
configurable by --cpuThreads and --gpuThreads. Ideally we'd run these on a
single threadpool but it can swamp the GPU if we shove too much work into it at
once. --cpuThreads defaults to the number of cores on the machine.
--gpuThreads defaults to 1, but you may find 2 or 4 runs a little faster.
So the main flow of DM is:
for each GM:
for each configuration:
kick off a new task
< tasks run, maybe fail, and maybe kick off new tasks >
wait for all tasks to finish
report failures

43
gyp/dm.gyp Normal file
View File

@ -0,0 +1,43 @@
# GYP for "dm" (Diamond Master, a.k.a Dungeon master, a.k.a GM 2).
# vim: set expandtab tabstop=4 shiftwidth=4
{
'includes': [ 'apptype_console.gypi' ],
'targets': [{
'target_name': 'dm',
'type': 'executable',
'include_dirs': [
'../dm',
'../gm',
'../src/core',
'../src/effects',
'../src/utils',
'../src/utils/debugger',
],
'includes': [ 'gmslides.gypi' ],
'sources': [
'../dm/DM.cpp',
'../dm/DMComparisonTask.cpp',
'../dm/DMCpuTask.cpp',
'../dm/DMGpuTask.cpp',
'../dm/DMReplayTask.cpp',
'../dm/DMReporter.cpp',
'../dm/DMTask.cpp',
'../dm/DMTaskRunner.cpp',
'../dm/DMUtil.cpp',
'../gm/gm.cpp',
'../gm/gm_expectations.cpp',
# TODO: split these out as a library in src/utils/debugger.
'../src/utils/debugger/SkDebugCanvas.cpp',
'../src/utils/debugger/SkDrawCommand.cpp',
'../src/utils/debugger/SkObjectParser.cpp',
],
'dependencies': [
'skia_lib.gyp:skia_lib',
'flags.gyp:flags',
'jsoncpp.gyp:jsoncpp',
'gputest.gyp:skgputest',
],
}]
}

View File

@ -12,7 +12,10 @@
{
'target_name': 'everything',
'type': 'none',
'dependencies': ['most.gyp:most'],
'dependencies': [
'most.gyp:most',
'dm.gyp:dm',
],
'conditions': [
['skia_os in ("ios", "android", "chromeos") or (skia_os == "mac" and skia_arch_width == 32)', {
# debugger is not supported on this platform