| Index: tools/kilobench/kilobench.cpp | 
| diff --git a/tools/kilobench/kilobench.cpp b/tools/kilobench/kilobench.cpp | 
| index 438e582324fb2e8c97f1da061e7d9a3f8760d870..8c844f47e0169ac26f72cae33d22b03fb52d8e50 100644 | 
| --- a/tools/kilobench/kilobench.cpp | 
| +++ b/tools/kilobench/kilobench.cpp | 
| @@ -8,6 +8,7 @@ | 
| #include "GrCaps.h" | 
| #include "GrContextFactory.h" | 
| #include "Benchmark.h" | 
| +#include "ResultsWriter.h" | 
| #include "SkCommandLineFlags.h" | 
| #include "SkOSFile.h" | 
| #include "SkStream.h" | 
| @@ -18,6 +19,11 @@ | 
| #include "VisualSKPBench.h" | 
| #include "gl/GrGLDefines.h" | 
|  | 
| +// posix only for now | 
| +#include <unistd.h> | 
| +#include <sys/types.h> | 
| +#include <sys/wait.h> | 
| + | 
| /* | 
| * This is an experimental GPU only benchmarking program.  The initial implementation will only | 
| * support SKPs. | 
| @@ -63,6 +69,11 @@ DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str()); | 
| DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU."); | 
| DEFINE_string2(writePath, w, "", "If set, write bitmaps here as .pngs."); | 
|  | 
| +static SkString humanize(double ms) { | 
| +    return HumanizeMs(ms); | 
| +} | 
| +#define HUMANIZE(ms) humanize(ms).c_str() | 
| + | 
| namespace kilobench { | 
| class BenchmarkStream { | 
| public: | 
| @@ -322,76 +333,135 @@ static int setup_gpu_bench(GPUTarget* target, Benchmark* bench, int maxGpuFrameL | 
| return loops; | 
| } | 
|  | 
| -static SkString humanize(double ms) { | 
| -    return HumanizeMs(ms); | 
| -} | 
| -#define HUMANIZE(ms) humanize(ms).c_str() | 
| +struct AutoSetupContextBenchAndTarget { | 
| +    AutoSetupContextBenchAndTarget(Benchmark* bench) : fBenchmark(bench) { | 
| +        GrContextOptions grContextOpts; | 
| +        fCtxFactory.reset(new GrContextFactory(grContextOpts)); | 
|  | 
| -void benchmark_inner_loop(Benchmark* bench, GrContextFactory* ctxFactory) { | 
| -    SkTArray<double> samples; | 
| -    GPUTarget target; | 
| -    SkAssertResult(target.init(bench, ctxFactory, false, | 
| -                               GrContextFactory::kNative_GLContextType, | 
| -                               GrContextFactory::kNone_GLContextOptions, 0)); | 
| +        SkAssertResult(fTarget.init(bench, fCtxFactory, false, | 
| +                                    GrContextFactory::kNative_GLContextType, | 
| +                                    GrContextFactory::kNone_GLContextOptions, 0)); | 
|  | 
| -    SkCanvas* canvas = target.getCanvas(); | 
| -    target.setup(); | 
| +        fCanvas = fTarget.getCanvas(); | 
| +        fTarget.setup(); | 
|  | 
| -    bench->perCanvasPreDraw(canvas); | 
| -    int maxFrameLag; | 
| -    target.needsFrameTiming(&maxFrameLag); | 
| -    int loops = setup_gpu_bench(&target, bench, maxFrameLag); | 
| +        bench->perCanvasPreDraw(fCanvas); | 
| +        fTarget.needsFrameTiming(&fMaxFrameLag); | 
| +    } | 
| + | 
| +    int getLoops() { return setup_gpu_bench(&fTarget, fBenchmark, fMaxFrameLag); } | 
|  | 
| -    samples.reset(FLAGS_samples); | 
| -    for (int s = 0; s < FLAGS_samples; s++) { | 
| -        samples[s] = time(loops, bench, &target) / loops; | 
| +    double timeSample(int loops) { | 
| +        for (int i = 0; i < fMaxFrameLag; i++) { | 
| +            time(loops, fBenchmark, &fTarget); | 
| +        } | 
| + | 
| +        return time(loops, fBenchmark, &fTarget) / loops; | 
| } | 
| +    void teardownBench() { fBenchmark->perCanvasPostDraw(fCanvas); } | 
| + | 
| +    SkAutoTDelete<GrContextFactory> fCtxFactory; | 
| +    GPUTarget fTarget; | 
| +    SkCanvas* fCanvas; | 
| +    Benchmark* fBenchmark; | 
| +    int fMaxFrameLag; | 
| +}; | 
|  | 
| -    bench->perCanvasPostDraw(canvas); | 
| - | 
| -    Stats stats(samples); | 
| -    const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; | 
| -    SkDebugf("%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n" | 
| -            , loops | 
| -            , HUMANIZE(stats.min) | 
| -            , HUMANIZE(stats.median) | 
| -            , HUMANIZE(stats.mean) | 
| -            , HUMANIZE(stats.max) | 
| -            , stddev_percent | 
| -            , stats.plot.c_str() | 
| -            , "gpu" | 
| -            , bench->getUniqueName() | 
| -            ); | 
| +int setup_loops(Benchmark* bench) { | 
| +    AutoSetupContextBenchAndTarget ascbt(bench); | 
| +    int loops = ascbt.getLoops(); | 
| +    ascbt.teardownBench(); | 
|  | 
| if (!FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { | 
| SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], "gpu"); | 
| pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName()); | 
| pngFilename.append(".png"); | 
| -        write_canvas_png(&target, pngFilename); | 
| +        write_canvas_png(&ascbt.fTarget, pngFilename); | 
| } | 
| +    return loops; | 
| } | 
|  | 
| -} // namespace kilobench | 
| +double time_sample(Benchmark* bench, int loops) { | 
| +    AutoSetupContextBenchAndTarget ascbt(bench); | 
| +    double sample = ascbt.timeSample(loops); | 
| +    ascbt.teardownBench(); | 
|  | 
| -int kilobench_main() { | 
| -    SkAutoTDelete<GrContextFactory> ctxFactory; | 
| +    return sample; | 
| +} | 
|  | 
| -    GrContextOptions grContextOpts; | 
| -    ctxFactory.reset(new GrContextFactory(grContextOpts)); | 
| +} // namespace kilobench | 
| + | 
| +static const int kOutResultSize = 1024; | 
|  | 
| +int kilobench_main() { | 
| kilobench::BenchmarkStream benchStream; | 
|  | 
| SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n", | 
| FLAGS_samples, "samples"); | 
|  | 
| +    int descriptors[2]; | 
| +    if (pipe(descriptors) != 0) { | 
| +        SkFAIL("Failed to open a pipe\n"); | 
| +    } | 
| + | 
| while (Benchmark* b = benchStream.next()) { | 
| SkAutoTDelete<Benchmark> bench(b); | 
| -        kilobench::benchmark_inner_loop(bench.get(), ctxFactory.get()); | 
| -    } | 
|  | 
| -    // Make sure we clean up the global GrContextFactory here, otherwise we might race with the | 
| -    // SkEventTracer destructor | 
| -    ctxFactory.reset(nullptr); | 
| +        int loops; | 
| +        SkTArray<double> samples; | 
| +        for (int i = 0; i < FLAGS_samples + 1; i++) { | 
| +            // We fork off a new process to setup the grcontext and run the test while we wait | 
| +            int childPid = fork(); | 
| +            if (childPid > 0) { | 
| +                char result[kOutResultSize]; | 
| +                if (read(descriptors[0], result, kOutResultSize) < 0) { | 
| +                     SkFAIL("Failed to read from pipe\n"); | 
| +                } | 
| + | 
| +                // if samples == 0 then parse # of loops | 
| +                // else parse float | 
| +                if (i == 0) { | 
| +                    sscanf(result, "%d", &loops); | 
| +                } else { | 
| +                    sscanf(result, "%lf", &samples.push_back()); | 
| +                } | 
| + | 
| +                // wait until exit | 
| +                int status; | 
| +                waitpid(childPid, &status, 0); | 
| +            } else if (0 == childPid) { | 
| +                char result[kOutResultSize]; | 
| +                if (i == 0) { | 
| +                    sprintf(result, "%d", kilobench::setup_loops(bench)); | 
| +                } else { | 
| +                    sprintf(result, "%lf", kilobench::time_sample(bench, loops)); | 
| +                } | 
| + | 
| +                // Make sure to write the null terminator | 
| +                if (write(descriptors[1], result, strlen(result) + 1) < 0) { | 
| +                    SkFAIL("Failed to write to pipe\n"); | 
| +                } | 
| +                return 0; | 
| +            } else { | 
| +                SkFAIL("Fork failed\n"); | 
| +            } | 
| +        } | 
| + | 
| +        Stats stats(samples); | 
| +        const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; | 
| +        SkDebugf("%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n" | 
| +                , loops | 
| +                , HUMANIZE(stats.min) | 
| +                , HUMANIZE(stats.median) | 
| +                , HUMANIZE(stats.mean) | 
| +                , HUMANIZE(stats.max) | 
| +                , stddev_percent | 
| +                , stats.plot.c_str() | 
| +                , "gpu" | 
| +                , bench->getUniqueName() | 
| +                ); | 
| + | 
| +    } | 
| return 0; | 
| } | 
|  | 
|  |