1/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 *
7 * Classes for writing out bench results in various formats.
8 */
9
10#ifndef SkPictureResultsWriter_DEFINED
11#define SkPictureResultsWriter_DEFINED
12
13#include "BenchLogger.h"
14#include "ResultsWriter.h"
15#include "SkJSONCPP.h"
16#include "SkStream.h"
17#include "SkString.h"
18#include "SkTArray.h"
19#include "TimerData.h"
20
21/**
22 * Base class for writing picture bench results.
23 */
24class PictureResultsWriter : SkNoncopyable {
25public:
26    enum TileFlags {kPurging, kAvg};
27
28    PictureResultsWriter() {}
29    virtual ~PictureResultsWriter() {}
30
31    virtual void bench(const char name[], int32_t x, int32_t y) = 0;
32    virtual void tileConfig(SkString configName) = 0;
33    virtual void tileMeta(int x, int y, int tx, int ty) = 0;
34    virtual void addTileFlag(PictureResultsWriter::TileFlags flag) = 0;
35    virtual void tileData(
36            TimerData* data,
37            const char format[],
38            const TimerData::Result result,
39            uint32_t timerTypes,
40            int numInnerLoops = 1) = 0;
41   virtual void end() = 0;
42};
43
44/**
45 * This class allows bench data to be piped into multiple
46 * PictureResultWriter classes. It does not own any classes
47 * passed to it, so the owner is required to manage any classes
48 * passed to PictureResultsMultiWriter */
49class PictureResultsMultiWriter : public PictureResultsWriter {
50public:
51    PictureResultsMultiWriter()
52        : fWriters() {}
53    void add(PictureResultsWriter* newWriter) {
54        fWriters.push_back(newWriter);
55    }
56    virtual ~PictureResultsMultiWriter() {}
57    virtual void bench(const char name[], int32_t x, int32_t y) {
58        for(int i=0; i<fWriters.count(); ++i) {
59            fWriters[i]->bench(name, x, y);
60        }
61    }
62    virtual void tileConfig(SkString configName) {
63        for(int i=0; i<fWriters.count(); ++i) {
64            fWriters[i]->tileConfig(configName);
65        }
66    }
67    virtual void tileMeta(int x, int y, int tx, int ty) {
68        for(int i=0; i<fWriters.count(); ++i) {
69            fWriters[i]->tileMeta(x, y, tx, ty);
70        }
71    }
72    virtual void addTileFlag(PictureResultsWriter::TileFlags flag) {
73        for(int i=0; i<fWriters.count(); ++i) {
74            fWriters[i]->addTileFlag(flag);
75        }
76    }
77    virtual void tileData(
78            TimerData* data,
79            const char format[],
80            const TimerData::Result result,
81            uint32_t timerTypes,
82            int numInnerLoops = 1) {
83        for(int i=0; i<fWriters.count(); ++i) {
84            fWriters[i]->tileData(data, format, result, timerTypes,
85                                 numInnerLoops);
86        }
87    }
88   virtual void end() {
89        for(int i=0; i<fWriters.count(); ++i) {
90            fWriters[i]->end();
91        }
92   }
93private:
94    SkTArray<PictureResultsWriter*> fWriters;
95};
96
97/**
98 * Writes to BenchLogger to mimic original behavior
99 */
100class PictureResultsLoggerWriter : public PictureResultsWriter {
101private:
102    void logProgress(const char str[]) {
103        if(fLogger != NULL) {
104            fLogger->logProgress(str);
105        }
106    }
107public:
108    PictureResultsLoggerWriter(BenchLogger* log)
109          : fLogger(log), currentLine() {}
110    virtual void bench(const char name[], int32_t x, int32_t y) {
111        SkString result;
112        result.printf("running bench [%i %i] %s ", x, y, name);
113        this->logProgress(result.c_str());
114    }
115    virtual void tileConfig(SkString configName) {
116        currentLine = configName;
117    }
118    virtual void tileMeta(int x, int y, int tx, int ty) {
119        currentLine.appendf(": tile [%i,%i] out of [%i,%i]", x, y, tx, ty);
120    }
121    virtual void addTileFlag(PictureResultsWriter::TileFlags flag) {
122        if(flag == PictureResultsWriter::kPurging) {
123            currentLine.append(" <withPurging>");
124        } else if(flag == PictureResultsWriter::kAvg) {
125            currentLine.append(" <averaged>");
126        }
127    }
128    virtual void tileData(
129            TimerData* data,
130            const char format[],
131            const TimerData::Result result,
132            uint32_t timerTypes,
133            int numInnerLoops = 1) {
134        SkString results = data->getResult(format, result,
135                currentLine.c_str(), timerTypes, numInnerLoops);
136        results.append("\n");
137        this->logProgress(results.c_str());
138    }
139    virtual void end() {}
140private:
141    BenchLogger* fLogger;
142    SkString currentLine;
143};
144
145/**
146 * This PictureResultsWriter collects data in a JSON node
147 *
148 * The format is something like
149 * {
150 *      benches: [
151 *          {
152 *              name: "Name_of_test"
153 *              tilesets: [
154 *                  {
155 *                      name: "Name of the configuration"
156 *                      tiles: [
157 *                          {
158 *                              flags: {
159 *                                  purging: true //Flags for the current tile
160 *                                              // are put here
161 *                              }
162 *                              data: {
163 *                                  wsecs: [....] //Actual data ends up here
164 *                              }
165 *                          }
166 *                      ]
167 *                  }
168 *              ]
169 *          }
170 *      ]
171 * }*/
172
173class PictureJSONResultsWriter : public PictureResultsWriter {
174public:
175    PictureJSONResultsWriter(const char filename[])
176        : fFilename(filename),
177          fRoot(),
178          fCurrentBench(NULL),
179          fCurrentTileSet(NULL),
180          fCurrentTile(NULL) {}
181
182    virtual void bench(const char name[], int32_t x, int32_t y) {
183        SkString sk_name(name);
184        sk_name.append("_");
185        sk_name.appendS32(x);
186        sk_name.append("_");
187        sk_name.appendS32(y);
188        Json::Value* bench_node = SkFindNamedNode(&fRoot["benches"], sk_name.c_str());
189        fCurrentBench = &(*bench_node)["tileSets"];
190    }
191    virtual void tileConfig(SkString configName) {
192        SkASSERT(fCurrentBench != NULL);
193        fCurrentTileSet = SkFindNamedNode(fCurrentBench, configName.c_str());
194        fCurrentTile = &(*fCurrentTileSet)["tiles"][0];
195    }
196    virtual void tileMeta(int x, int y, int tx, int ty) {
197        SkASSERT(fCurrentTileSet != NULL);
198        (*fCurrentTileSet)["tx"] = tx;
199        (*fCurrentTileSet)["ty"] = ty;
200        fCurrentTile = &(*fCurrentTileSet)["tiles"][x+tx*y];
201    }
202    virtual void addTileFlag(PictureResultsWriter::TileFlags flag) {
203        SkASSERT(fCurrentTile != NULL);
204        if(flag == PictureResultsWriter::kPurging) {
205            (*fCurrentTile)["flags"]["purging"] = true;
206        } else if(flag == PictureResultsWriter::kAvg) {
207            (*fCurrentTile)["flags"]["averaged"] = true;
208        }
209    }
210    virtual void tileData(
211            TimerData* data,
212            const char format[],
213            const TimerData::Result result,
214            uint32_t timerTypes,
215            int numInnerLoops = 1) {
216        SkASSERT(fCurrentTile != NULL);
217        (*fCurrentTile)["data"] = data->getJSON(timerTypes, result, numInnerLoops);
218    }
219    virtual void end() {
220       SkFILEWStream stream(fFilename.c_str());
221       stream.writeText(Json::FastWriter().write(fRoot).c_str());
222       stream.flush();
223    }
224private:
225    SkString fFilename;
226    Json::Value fRoot;
227    Json::Value *fCurrentBench;
228    Json::Value *fCurrentTileSet;
229    Json::Value *fCurrentTile;
230};
231
232#endif
233