| DM is like GM, but multithreaded.  It doesn't do everything GM does. | 
 |  | 
 | 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 | 
 |  |