Clean up some dead code.

This cleans up tools/ code, or code that should have been in tools/.

The only interesting code change trims features off of PictureRenderer.
It's still in use by a few useful-looking tools.

BUG=skia:

Review URL: https://codereview.chromium.org/1416913003
This commit is contained in:
mtklein 2015-10-20 14:29:10 -07:00 committed by Commit bot
parent ef33b1e739
commit 0c768a2f33
21 changed files with 19 additions and 3340 deletions

View File

@ -52,9 +52,6 @@ SRCS = glob(
"src/gpu/gl/mesa/*", # Requires SK_MESA define.
"src/svg/parser/*", # Missing SkSVG.h.
# Dependency on files outside src.
"src/svg/skp2svg.cpp", # Depends on tools/LazyDecodeBitmap.h.
# Not used.
"src/animator/**/*",
"src/views/**/*",

View File

@ -17,12 +17,9 @@
'target_name': 'tools',
'type': 'none',
'dependencies': [
'bench_pictures',
'bitmap_region_decoder',
'chrome_fuzz',
'dump_record',
'filter',
'flatten',
'gpuveto',
'imgblur',
'imgconv',
@ -31,10 +28,8 @@
'lua_pictures',
'pinspect',
'render_pdfs',
'render_pictures',
'skdiff',
'skhello',
'skp2svg',
'skpdiff',
'skpinfo',
'skpmaker',
@ -344,50 +339,32 @@
],
},
{
'target_name': 'flatten',
'type': 'executable',
'sources': [
'../tools/flatten.cpp',
],
'dependencies': [
'skia_lib.gyp:skia_lib',
],
},
{
# Superseded by dm, should be removed.
'target_name': 'skp2svg',
'type': 'executable',
'sources': [
'../src/svg/skp2svg.cpp',
'../tools/LazyDecodeBitmap.cpp',
],
'include_dirs': [
'../include/private',
'../src/core/',
'../src/lazy/',
'../tools/',
],
'dependencies': [
'flags.gyp:flags',
'skia_lib.gyp:skia_lib',
'svg.gyp:svg',
'xml.gyp:xml',
],
'target_name': 'lazy_decode_bitmap',
'type': 'static_library',
'sources': [ '../tools/LazyDecodeBitmap.cpp' ],
'include_dirs': [
'../include/private',
'../src/core',
'../src/lazy',
],
'dependencies': [
'flags.gyp:flags',
'skia_lib.gyp:skia_lib'
],
},
{
'target_name': 'gpuveto',
'type': 'executable',
'sources': [
'../tools/gpuveto.cpp',
'../tools/LazyDecodeBitmap.cpp',
],
'include_dirs': [
'../include/private',
'../src/core/',
'../src/images',
'../src/lazy',
],
'dependencies': [
'lazy_decode_bitmap',
'flags.gyp:flags',
'skia_lib.gyp:skia_lib',
],
@ -429,103 +406,23 @@
'../src/core/',
],
'dependencies': [
'lazy_decode_bitmap',
'effects.gyp:effects',
'flags.gyp:flags',
'images.gyp:images',
'lua.gyp:lua',
'tools.gyp:picture_renderer',
'tools.gyp:picture_utils',
'pdf.gyp:pdf',
'ports.gyp:ports',
'skia_lib.gyp:skia_lib',
],
},
{
'target_name': 'render_pictures',
'type': 'executable',
'sources': [
'../tools/render_pictures_main.cpp',
],
'include_dirs': [
'../include/private',
'../src/core',
'../src/images',
'../src/lazy',
'../src/pipe/utils/',
],
'dependencies': [
'flags.gyp:flags',
'skia_lib.gyp:skia_lib',
'tools.gyp:picture_renderer',
'tools.gyp:picture_utils',
],
},
{
'target_name': 'bench_pictures',
'type': 'executable',
'sources': [
'../bench/BenchLogger.cpp',
'../bench/BenchLogger.h',
'../tools/PictureBenchmark.cpp',
'../tools/PictureResultsWriter.h',
'../tools/bench_pictures_main.cpp',
],
'include_dirs': [
'../include/private',
'../src/core/',
'../bench',
'../src/lazy/',
],
'dependencies': [
'timer',
'crash_handler',
'flags.gyp:flags',
'jsoncpp.gyp:jsoncpp',
'skia_lib.gyp:skia_lib',
'tools.gyp:picture_renderer',
'tools.gyp:picture_utils',
],
'conditions': [
['skia_android_framework == 1', {
'libraries': [ '-lskia' ],
}],
],
},
{
'target_name': 'dump_record',
'type': 'executable',
'sources': [
'../tools/dump_record.cpp',
'../tools/DumpRecord.cpp',
'../tools/LazyDecodeBitmap.cpp',
],
'include_dirs': [
'../include/private',
'../src/core/',
'../src/images',
'../src/lazy',
],
'dependencies': [
'timer',
'flags.gyp:flags',
'skia_lib.gyp:skia_lib',
],
},
{
'target_name': 'picture_renderer',
'type': 'static_library',
'sources': [
'../tools/image_expectations.h',
'../tools/image_expectations.cpp',
'../tools/LazyDecodeBitmap.cpp',
'../tools/PictureRenderer.h',
'../tools/PictureRenderer.cpp',
'../tools/PictureRenderingFlags.h',
'../tools/PictureRenderingFlags.cpp',
'../tools/CopyTilesRenderer.h',
'../tools/CopyTilesRenderer.cpp',
'../src/pipe/utils/SamplePipeControllers.h',
'../src/pipe/utils/SamplePipeControllers.cpp',
],
'include_dirs': [
'../include/private',
@ -535,14 +432,8 @@
'../src/pipe/utils/',
'../src/utils/',
],
'direct_dependent_settings': {
'include_dirs': [
# needed for JSON headers used within image_expectations.h
'../third_party/externals/jsoncpp-chromium/overrides/include/',
'../third_party/externals/jsoncpp/include/',
],
},
'dependencies': [
'lazy_decode_bitmap',
'flags.gyp:flags',
'jsoncpp.gyp:jsoncpp',
'skia_lib.gyp:skia_lib',
@ -642,9 +533,9 @@
'../tools/pinspect.cpp',
],
'dependencies': [
'lazy_decode_bitmap',
'flags.gyp:flags',
'skia_lib.gyp:skia_lib',
'tools.gyp:picture_renderer',
],
},
{

View File

@ -1,70 +0,0 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "LazyDecodeBitmap.h"
#include "SkCommandLineFlags.h"
#include "SkPicture.h"
#include "SkStream.h"
#include "SkSVGCanvas.h"
#include "SkXMLWriter.h"
DEFINE_string2(input, i, "", "input skp file");
DEFINE_string2(output, o, "", "output svg file (optional)");
// return codes:
static const int kSuccess = 0;
static const int kInvalidArgs = 1;
static const int kIOError = 2;
static const int kNotAnSKP = 3;
int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
SkCommandLineFlags::SetUsage("Converts an SKP file to SVG.");
SkCommandLineFlags::Parse(argc, argv);
if (FLAGS_input.count() != 1) {
SkDebugf("Missing input file\n");
return kInvalidArgs;
}
SkFILEStream stream(FLAGS_input[0]);
if (!stream.isValid()) {
SkDebugf("Couldn't open file: %s\n", FLAGS_input[0]);
return kIOError;
}
SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(&stream, &sk_tools::LazyDecodeBitmap));
if (!SkToBool(pic.get())) {
SkDebugf("Could not load SKP: %s\n", FLAGS_input[0]);
return kNotAnSKP;
}
SkAutoTDelete<SkWStream> outStream;
if (FLAGS_output.count() > 0) {
SkFILEWStream* fileStream = new SkFILEWStream(FLAGS_output[0]);
if (!fileStream->isValid()) {
SkDebugf("Couldn't open output file for writing: %s\n", FLAGS_output[0]);
return kIOError;
}
outStream.reset(fileStream);
} else {
outStream.reset(new SkDebugWStream);
}
SkAutoTDelete<SkXMLWriter> xmlWriter(new SkXMLStreamWriter(outStream.get()));
SkAutoTUnref<SkCanvas> svgCanvas(SkSVGCanvas::Create(pic->cullRect(), xmlWriter.get()));
pic->playback(svgCanvas);
return kSuccess;
}
#if !defined SK_BUILD_FOR_IOS
int main(int argc, char * const argv[]) {
return tool_main(argc, (char**) argv);
}
#endif

View File

@ -1,118 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "picture_utils.h"
#include "CopyTilesRenderer.h"
#include "SkCanvas.h"
#include "SkDevice.h"
#include "SkImageEncoder.h"
#include "SkMultiPictureDraw.h"
#include "SkPicture.h"
#include "SkPixelRef.h"
#include "SkRect.h"
#include "SkString.h"
namespace sk_tools {
#if SK_SUPPORT_GPU
CopyTilesRenderer::CopyTilesRenderer(const GrContextOptions& opts, int x, int y)
: INHERITED(opts)
, fXTilesPerLargeTile(x)
, fYTilesPerLargeTile(y) { }
#else
CopyTilesRenderer::CopyTilesRenderer(int x, int y)
: fXTilesPerLargeTile(x)
, fYTilesPerLargeTile(y) { }
#endif
void CopyTilesRenderer::init(const SkPicture* pict, const SkString* writePath,
const SkString* mismatchPath, const SkString* inputFilename,
bool useChecksumBasedFilenames, bool useMultiPictureDraw) {
// Do not call INHERITED::init(), which would create a (potentially large) canvas which is
// not used by bench_pictures.
SkASSERT(pict != nullptr);
// Only work with absolute widths (as opposed to percentages).
SkASSERT(this->getTileWidth() != 0 && this->getTileHeight() != 0);
fPicture.reset(SkRef(pict));
this->CopyString(&fWritePath, writePath);
this->CopyString(&fMismatchPath, mismatchPath);
this->CopyString(&fInputFilename, inputFilename);
fUseChecksumBasedFilenames = useChecksumBasedFilenames;
fUseMultiPictureDraw = useMultiPictureDraw;
this->buildBBoxHierarchy();
// In order to avoid allocating a large canvas (particularly important for GPU), create one
// canvas that is a multiple of the tile size, and draw portions of the picture.
fLargeTileWidth = fXTilesPerLargeTile * this->getTileWidth();
fLargeTileHeight = fYTilesPerLargeTile * this->getTileHeight();
fCanvas.reset(this->INHERITED::setupCanvas(fLargeTileWidth, fLargeTileHeight));
}
bool CopyTilesRenderer::render(SkBitmap** out) {
int i = 0;
bool success = true;
SkBitmap dst;
for (int x = 0; x < this->getViewWidth(); x += fLargeTileWidth) {
for (int y = 0; y < this->getViewHeight(); y += fLargeTileHeight) {
SkAutoCanvasRestore autoRestore(fCanvas, true);
// Translate so that we draw the correct portion of the picture.
// Perform a postTranslate so that the scaleFactor does not interfere with the
// positioning.
SkMatrix mat(fCanvas->getTotalMatrix());
mat.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
fCanvas->setMatrix(mat);
// Draw the picture
if (fUseMultiPictureDraw) {
SkMultiPictureDraw mpd;
mpd.add(fCanvas, fPicture);
mpd.draw();
} else {
fCanvas->drawPicture(fPicture);
}
// Now extract the picture into tiles
SkBitmap baseBitmap;
fCanvas->readPixels(SkIRect::MakeSize(fCanvas->getBaseLayerSize()), &baseBitmap);
SkIRect subset;
for (int tileY = 0; tileY < fLargeTileHeight; tileY += this->getTileHeight()) {
for (int tileX = 0; tileX < fLargeTileWidth; tileX += this->getTileWidth()) {
subset.set(tileX, tileY, tileX + this->getTileWidth(),
tileY + this->getTileHeight());
SkDEBUGCODE(bool extracted =)
baseBitmap.extractSubset(&dst, subset);
SkASSERT(extracted);
if (!fWritePath.isEmpty()) {
// Similar to write() in PictureRenderer.cpp, but just encodes
// a bitmap directly.
// TODO: Share more common code with write() to do this, to properly
// write out the JSON summary, etc.
SkString pathWithNumber = SkOSPath::Join(fWritePath.c_str(),
fInputFilename.c_str());
pathWithNumber.remove(pathWithNumber.size() - 4, 4);
pathWithNumber.appendf("%i.png", i++);
SkBitmap copy;
#if SK_SUPPORT_GPU
if (isUsingGpuDevice()) {
dst.pixelRef()->readPixels(&copy, &subset);
} else {
#endif
dst.copyTo(&copy);
#if SK_SUPPORT_GPU
}
#endif
success &= SkImageEncoder::EncodeFile(pathWithNumber.c_str(), copy,
SkImageEncoder::kPNG_Type, 100);
}
}
}
}
}
return success;
}
SkString CopyTilesRenderer::getConfigNameInternal() {
return SkString("copy_tiles");
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef CopyTilesRenderer_DEFINED
#define CopyTilesRenderer_DEFINED
#include "PictureRenderer.h"
#include "SkTypes.h"
struct GrContextOptions;
class SkPicture;
class SkString;
namespace sk_tools {
/**
* PictureRenderer that draws the picture and then extracts it into tiles. For large pictures,
* it will divide the picture into large tiles and draw the picture once for each large tile.
*/
class CopyTilesRenderer : public TiledPictureRenderer {
public:
#if SK_SUPPORT_GPU
CopyTilesRenderer(const GrContextOptions &opts, int x, int y);
#else
CopyTilesRenderer(int x, int y);
#endif
virtual void init(const SkPicture* pict,
const SkString* writePath,
const SkString* mismatchPath,
const SkString* inputFilename,
bool useChecksumBasedFilenames,
bool useMultiPictureDraw) override;
/**
* Similar to TiledPictureRenderer, this will draw a PNG for each tile. However, the
* numbering (and actual tiles) will be different.
*/
bool render(SkBitmap** out) override;
bool supportsTimingIndividualTiles() override { return false; }
private:
int fXTilesPerLargeTile;
int fYTilesPerLargeTile;
int fLargeTileWidth;
int fLargeTileHeight;
SkString getConfigNameInternal() override;
typedef TiledPictureRenderer INHERITED;
};
} // sk_tools
#endif // CopyTilesRenderer_DEFINED

View File

@ -1,246 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Timer.h"
#include "PictureBenchmark.h"
#include "SkCanvas.h"
#include "SkPicture.h"
#include "SkString.h"
#include "picture_utils.h"
namespace sk_tools {
PictureBenchmark::PictureBenchmark()
: fRepeats(1)
, fRenderer(nullptr)
, fTimerResult(TimerData::kAvg_Result)
, fTimerTypes(0)
, fTimeIndividualTiles(false)
, fPurgeDecodedTex(false)
, fWriter(nullptr) {
}
PictureBenchmark::~PictureBenchmark() {
SkSafeUnref(fRenderer);
}
void PictureBenchmark::setTimersToShow(bool wall,
bool truncatedWall,
bool cpu,
bool truncatedCpu,
bool gpu) {
fTimerTypes = 0;
fTimerTypes |= wall ? TimerData::kWall_Flag : 0;
fTimerTypes |= truncatedWall ? TimerData::kTruncatedWall_Flag : 0;
fTimerTypes |= cpu ? TimerData::kCpu_Flag : 0;
fTimerTypes |= truncatedCpu ? TimerData::kTruncatedCpu_Flag : 0;
fTimerTypes |= gpu ? TimerData::kGpu_Flag : 0;
}
Timer* PictureBenchmark::setupTimer(bool useGLTimer) {
#if SK_SUPPORT_GPU
if (useGLTimer && fRenderer != nullptr && fRenderer->isUsingGpuDevice()) {
return new Timer(fRenderer->getGLContext());
}
#endif
return new Timer(nullptr);
}
PictureRenderer* PictureBenchmark::setRenderer(sk_tools::PictureRenderer* renderer) {
SkRefCnt_SafeAssign(fRenderer, renderer);
return renderer;
}
void PictureBenchmark::run(SkPicture* pict, bool useMultiPictureDraw) {
SkASSERT(pict);
if (nullptr == pict) {
return;
}
SkASSERT(fRenderer != nullptr);
if (nullptr == fRenderer) {
return;
}
fRenderer->init(pict, nullptr, nullptr, nullptr, false, useMultiPictureDraw);
// We throw this away to remove first time effects (such as paging in this program)
fRenderer->setup();
fRenderer->render(nullptr);
fRenderer->resetState(true); // flush, swapBuffers and Finish
if (fPurgeDecodedTex) {
fRenderer->purgeTextures();
}
bool usingGpu = false;
#if SK_SUPPORT_GPU
usingGpu = fRenderer->isUsingGpuDevice();
#endif
uint32_t timerTypes = fTimerTypes;
if (!usingGpu) {
timerTypes &= ~TimerData::kGpu_Flag;
}
SkString timeFormat;
if (TimerData::kPerIter_Result == fTimerResult) {
timeFormat = fRenderer->getPerIterTimeFormat();
} else {
timeFormat = fRenderer->getNormalTimeFormat();
}
static const int kNumInnerLoops = 10;
int numOuterLoops = 1;
int numInnerLoops = fRepeats;
if (TimerData::kPerIter_Result == fTimerResult && fRepeats > 1) {
// interpret this flag combination to mean: generate 'fRepeats'
// numbers by averaging each rendering 'kNumInnerLoops' times
numOuterLoops = fRepeats;
numInnerLoops = kNumInnerLoops;
}
if (fTimeIndividualTiles) {
TiledPictureRenderer* tiledRenderer = fRenderer->getTiledRenderer();
SkASSERT(tiledRenderer && tiledRenderer->supportsTimingIndividualTiles());
if (nullptr == tiledRenderer || !tiledRenderer->supportsTimingIndividualTiles()) {
return;
}
int xTiles, yTiles;
if (!tiledRenderer->tileDimensions(xTiles, yTiles)) {
return;
}
int x, y;
while (tiledRenderer->nextTile(x, y)) {
// There are two timers, which will behave slightly differently:
// 1) longRunningTimer, along with perTileTimerData, will time how long it takes to draw
// one tile fRepeats times, and take the average. As such, it will not respect the
// logPerIter or printMin options, since it does not know the time per iteration. It
// will also be unable to call flush() for each tile.
// The goal of this timer is to make up for a system timer that is not precise enough to
// measure the small amount of time it takes to draw one tile once.
//
// 2) perTileTimer, along with perTileTimerData, will record each run separately, and
// then take the average. As such, it supports logPerIter and printMin options.
//
// Although "legal", having two gpu timers running at the same time
// seems to cause problems (i.e., INVALID_OPERATIONs) on several
// platforms. To work around this, we disable the gpu timer on the
// long running timer.
SkAutoTDelete<Timer> longRunningTimer(this->setupTimer());
TimerData longRunningTimerData(numOuterLoops);
for (int outer = 0; outer < numOuterLoops; ++outer) {
SkAutoTDelete<Timer> perTileTimer(this->setupTimer(false));
TimerData perTileTimerData(numInnerLoops);
longRunningTimer->start();
for (int inner = 0; inner < numInnerLoops; ++inner) {
perTileTimer->start();
tiledRenderer->drawCurrentTile();
perTileTimer->truncatedEnd();
tiledRenderer->resetState(false); // flush & swapBuffers, but don't Finish
perTileTimer->end();
SkAssertResult(perTileTimerData.appendTimes(perTileTimer.get()));
if (fPurgeDecodedTex) {
fRenderer->purgeTextures();
}
}
longRunningTimer->truncatedEnd();
tiledRenderer->resetState(true); // flush, swapBuffers and Finish
longRunningTimer->end();
SkAssertResult(longRunningTimerData.appendTimes(longRunningTimer.get()));
}
fWriter->logRenderer(tiledRenderer);
fWriter->tileMeta(x, y, xTiles, yTiles);
// TODO(borenet): Turn off per-iteration tile time reporting for now.
// Avoiding logging the time for every iteration for each tile cuts
// down on data file size by a significant amount. Re-enable this once
// we're loading the bench data directly into a data store and are no
// longer generating SVG graphs.
#if 0
fWriter->tileData(
&perTileTimerData,
timeFormat.c_str(),
fTimerResult,
timerTypes);
#endif
if (fPurgeDecodedTex) {
fWriter->addTileFlag(PictureResultsWriter::kPurging);
}
fWriter->addTileFlag(PictureResultsWriter::kAvg);
fWriter->tileData(
&longRunningTimerData,
tiledRenderer->getNormalTimeFormat().c_str(),
TimerData::kAvg_Result,
timerTypes,
numInnerLoops);
}
} else {
SkAutoTDelete<Timer> longRunningTimer(this->setupTimer());
TimerData longRunningTimerData(numOuterLoops);
for (int outer = 0; outer < numOuterLoops; ++outer) {
SkAutoTDelete<Timer> perRunTimer(this->setupTimer(false));
TimerData perRunTimerData(numInnerLoops);
longRunningTimer->start();
for (int inner = 0; inner < numInnerLoops; ++inner) {
fRenderer->setup();
perRunTimer->start();
fRenderer->render(nullptr);
perRunTimer->truncatedEnd();
fRenderer->resetState(false); // flush & swapBuffers, but don't Finish
perRunTimer->end();
SkAssertResult(perRunTimerData.appendTimes(perRunTimer.get()));
if (fPurgeDecodedTex) {
fRenderer->purgeTextures();
}
}
longRunningTimer->truncatedEnd();
fRenderer->resetState(true); // flush, swapBuffers and Finish
longRunningTimer->end();
SkAssertResult(longRunningTimerData.appendTimes(longRunningTimer.get()));
}
fWriter->logRenderer(fRenderer);
if (fPurgeDecodedTex) {
fWriter->addTileFlag(PictureResultsWriter::kPurging);
}
// Beware - since the per-run-timer doesn't ever include a glFinish it can
// report a lower time then the long-running-timer
#if 0
fWriter->tileData(
&perRunTimerData,
timeFormat.c_str(),
fTimerResult,
timerTypes);
#else
fWriter->tileData(
&longRunningTimerData,
timeFormat.c_str(),
fTimerResult,
timerTypes,
numInnerLoops);
#endif
}
fRenderer->end();
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef PictureBenchmark_DEFINED
#define PictureBenchmark_DEFINED
#include "PictureRenderer.h"
#include "PictureResultsWriter.h"
#include "SkTypes.h"
#include "TimerData.h"
class SkPicture;
class Timer;
namespace sk_tools {
class PictureBenchmark {
public:
PictureBenchmark();
~PictureBenchmark();
/**
* Draw the provided SkPicture fRepeats times while collecting timing data, and log the output
* via fWriter.
*/
void run(SkPicture* pict, bool useMultiPictureDraw);
void setRepeats(int repeats) {
fRepeats = repeats;
}
/**
* If true, tells run to log separate timing data for each individual tile. Each tile will be
* drawn fRepeats times. Requires the PictureRenderer set by setRenderer to be a
* TiledPictureRenderer.
*/
void setTimeIndividualTiles(bool indiv) { fTimeIndividualTiles = indiv; }
bool timeIndividualTiles() const { return fTimeIndividualTiles; }
void setPurgeDecodedTex(bool purgeDecodedTex) { fPurgeDecodedTex = purgeDecodedTex; }
bool purgeDecodedText() const { return fPurgeDecodedTex; }
PictureRenderer* setRenderer(PictureRenderer*);
PictureRenderer* renderer() { return fRenderer; }
void setTimerResultType(TimerData::Result resultType) { fTimerResult = resultType; }
void setTimersToShow(bool wall, bool truncatedWall, bool cpu, bool truncatedCpu, bool gpu);
void setWriter(PictureResultsWriter* writer) { fWriter = writer; }
private:
int fRepeats;
PictureRenderer* fRenderer;
TimerData::Result fTimerResult;
uint32_t fTimerTypes; // bitfield of TimerData::TimerFlags values
bool fTimeIndividualTiles;
bool fPurgeDecodedTex;
PictureResultsWriter* fWriter;
Timer* setupTimer(bool useGLTimer = true);
};
}
#endif // PictureBenchmark_DEFINED

View File

@ -283,87 +283,6 @@ void PictureRenderer::purgeTextures() {
#endif
}
/**
* Write the canvas to an image file and/or JSON summary.
*
* @param canvas Must be non-null. Canvas to be written to a file.
* @param writePath If nonempty, write the binary image to a file within this directory.
* @param mismatchPath If nonempty, write the binary image to a file within this directory,
* but only if the image does not match expectations.
* @param inputFilename If we are writing out a binary image, use this to build its filename.
* @param jsonSummaryPtr If not null, add image results (checksum) to this summary.
* @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
* @param tileNumberPtr If not null, which tile number this image contains.
*
* @return bool True if the operation completed successfully.
*/
static bool write(SkCanvas* canvas, const SkString& writePath, const SkString& mismatchPath,
const SkString& inputFilename, ImageResultsAndExpectations *jsonSummaryPtr,
bool useChecksumBasedFilenames, const int* tileNumberPtr=nullptr) {
SkASSERT(canvas != nullptr);
if (nullptr == canvas) {
return false;
}
SkBitmap bitmap;
SkISize size = canvas->getDeviceSize();
setup_bitmap(&bitmap, size.width(), size.height());
canvas->readPixels(&bitmap, 0, 0);
force_all_opaque(bitmap);
BitmapAndDigest bitmapAndDigest(bitmap);
SkString escapedInputFilename(inputFilename);
replace_char(&escapedInputFilename, '.', '_');
// TODO(epoger): what about including the config type within outputFilename? That way,
// we could combine results of different config types without conflicting filenames.
SkString outputFilename;
const char *outputSubdirPtr = nullptr;
if (useChecksumBasedFilenames) {
ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr();
outputSubdirPtr = escapedInputFilename.c_str();
outputFilename.set(imageDigestPtr->getHashType());
outputFilename.append("_");
outputFilename.appendU64(imageDigestPtr->getHashValue());
} else {
outputFilename.set(escapedInputFilename);
if (tileNumberPtr) {
outputFilename.append("-tile");
outputFilename.appendS32(*tileNumberPtr);
}
}
outputFilename.append(".png");
if (jsonSummaryPtr) {
ImageDigest *imageDigestPtr = bitmapAndDigest.getImageDigestPtr();
SkString outputRelativePath;
if (outputSubdirPtr) {
outputRelativePath.set(outputSubdirPtr);
outputRelativePath.append("/"); // always use "/", even on Windows
outputRelativePath.append(outputFilename);
} else {
outputRelativePath.set(outputFilename);
}
jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
*imageDigestPtr, tileNumberPtr);
if (!mismatchPath.isEmpty() &&
!jsonSummaryPtr->getExpectation(inputFilename.c_str(),
tileNumberPtr).matches(*imageDigestPtr)) {
if (!write_bitmap_to_disk(bitmap, mismatchPath, outputSubdirPtr, outputFilename)) {
return false;
}
}
}
if (writePath.isEmpty()) {
return true;
} else {
return write_bitmap_to_disk(bitmap, writePath, outputSubdirPtr, outputFilename);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
@ -417,12 +336,7 @@ bool PipePictureRenderer::render(SkBitmap** out) {
SkScalarCeilToInt(fPicture->cullRect().height()));
fCanvas->readPixels(*out, 0, 0);
}
if (fEnableWrites) {
return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
fUseChecksumBasedFilenames);
} else {
return true;
}
return true;
}
SkString PipePictureRenderer::getConfigNameInternal() {
@ -462,12 +376,7 @@ bool SimplePictureRenderer::render(SkBitmap** out) {
SkScalarCeilToInt(fPicture->cullRect().height()));
fCanvas->readPixels(*out, 0, 0);
}
if (fEnableWrites) {
return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
fUseChecksumBasedFilenames);
} else {
return true;
}
return true;
}
SkString SimplePictureRenderer::getConfigNameInternal() {
@ -674,10 +583,6 @@ bool TiledPictureRenderer::postRender(SkCanvas* canvas, const SkIRect& tileRect,
int tileNumber) {
bool success = true;
if (fEnableWrites) {
success &= write(canvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
fUseChecksumBasedFilenames, &tileNumber);
}
if (out) {
if (canvas->readPixels(tempBM, 0, 0)) {
// Add this tile to the entire bitmap.

View File

@ -26,8 +26,6 @@
#include "GrContext.h"
#endif
#include "image_expectations.h"
struct GrContextOptions;
class SkBitmap;
class SkCanvas;
@ -101,14 +99,6 @@ public:
bool useChecksumBasedFilenames,
bool useMultiPictureDraw);
/**
* TODO(epoger): Temporary hack, while we work on http://skbug.com/2584 ('bench_pictures is
* timing reading pixels and writing json files'), such that:
* - render_pictures can call this method and continue to work
* - any other callers (bench_pictures) will skip calls to write() by default
*/
void enableWrites() { fEnableWrites = true; }
/**
* Set the viewport so that only the portion listed gets drawn.
*/
@ -251,10 +241,6 @@ public:
BBoxHierarchyType getBBoxHierarchyType() { return fBBoxHierarchyType; }
void setJsonSummaryPtr(ImageResultsAndExpectations* jsonSummaryPtr) {
fJsonSummaryPtr = jsonSummaryPtr;
}
bool isUsingBitmapDevice() {
return kBitmap_DeviceType == fDeviceType;
}
@ -444,9 +430,7 @@ public:
#else
PictureRenderer()
#endif
: fJsonSummaryPtr(nullptr)
, fDeviceType(kBitmap_DeviceType)
, fEnableWrites(false)
: fDeviceType(kBitmap_DeviceType)
, fBBoxHierarchyType(kNone_BBoxHierarchyType)
, fHasDrawFilters(false)
, fScaleFactor(SK_Scalar1)
@ -472,9 +456,7 @@ protected:
SkAutoTUnref<const SkPicture> fPicture;
bool fUseChecksumBasedFilenames;
bool fUseMultiPictureDraw;
ImageResultsAndExpectations* fJsonSummaryPtr;
SkDeviceTypes fDeviceType;
bool fEnableWrites;
BBoxHierarchyType fBBoxHierarchyType;
bool fHasDrawFilters;
DrawFilterFlags fDrawFilters[SkDrawFilter::kTypeCount];

View File

@ -1,373 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "PictureRenderingFlags.h"
#include "CopyTilesRenderer.h"
#if SK_SUPPORT_GPU
#include "GrContextOptions.h"
#endif
#include "PictureRenderer.h"
#include "picture_utils.h"
#include "SkCommandLineFlags.h"
#include "SkData.h"
#include "SkImage.h"
#include "SkImageDecoder.h"
#include "SkString.h"
#include <stdlib.h>
// Alphabetized list of flags used by this file or bench_ and render_pictures.
DEFINE_string(bbh, "none", "bbhType [width height]: Set the bounding box hierarchy type to "
"be used. Accepted values are: none, rtree, grid. "
"Not compatible with --pipe. With value "
"'grid', width and height must be specified. 'grid' can "
"only be used with modes tile, record, and "
"playbackCreation.");
#if SK_SUPPORT_GPU
static const char kGpuAPINameGL[] = "gl";
static const char kGpuAPINameGLES[] = "gles";
#define GPU_CONFIG_STRING "|gpu|msaa4|msaa16|nvprmsaa4|nvprmsaa16|gpudft"
#else
#define GPU_CONFIG_STRING ""
#endif
#if SK_ANGLE
#define ANGLE_CONFIG_STRING "|angle"
#else
#define ANGLE_CONFIG_STRING ""
#endif
#if SK_COMMAND_BUFFER
#define COMMAND_BUFFER_CONFIG_STRING "|commandbuffer"
#else
#define COMMAND_BUFFER_CONFIG_STRING ""
#endif
#if SK_MESA
#define MESA_CONFIG_STRING "|mesa"
#else
#define MESA_CONFIG_STRING ""
#endif
// Although this config does not support all the same options as gm, the names should be kept
// consistent.
DEFINE_string(config, "8888", "["
"8888" GPU_CONFIG_STRING ANGLE_CONFIG_STRING COMMAND_BUFFER_CONFIG_STRING MESA_CONFIG_STRING
"]: Use the corresponding config.");
DEFINE_bool(deferImageDecoding, false, "Defer decoding until drawing images. "
"Has no effect if the provided skp does not have its images encoded.");
DEFINE_string(mode, "simple", "Run in the corresponding mode:\n"
"simple: Simple rendering.\n"
"tile width height: Use tiles with the given dimensions or percentages.\n"
"pow2tile minWidth height: Use tiles with widths that are all a power\n"
"\tof two such that they minimize the amount of wasted tile space.\n"
"\tminWidth must be a power of two.\n"
"copyTile width height: Draw the picture, then copy into tiles. If the\n"
"\tpicture is large enough, it is broken into larger tiles to avoid\n"
"\tcreating a large canvas.\n"
// TODO: If bench_pictures and render_pictures were two separate targets, we could use build flags
// to determine which modes to display.
"record: (Only in bench_pictures) Time recording from a picture to a new\n"
"\tpicture.\n"
"playbackCreation: (Only in bench_pictures) Time creation of the \n"
"\tSkPicturePlayback.\n"
"rerecord: (Only in render_pictures) Record the picture as a new skp,\n"
"\twith the bitmaps PNG encoded.\n");
DEFINE_bool(pipe, false, "Use SkGPipe rendering. Currently incompatible with \"mode\".");
DEFINE_string2(readPath, r, "", "skp files or directories of skp files to process.");
DEFINE_double(scale, 1, "Set the scale factor.");
DEFINE_string(tiles, "", "Used with --mode copyTile to specify number of tiles per larger tile "
"in the x and y directions.");
DEFINE_string(viewport, "", "width height: Set the viewport.");
#if SK_SUPPORT_GPU
DEFINE_string(gpuAPI, "", "Force use of specific gpu API. Using \"gl\" "
"forces OpenGL API. Using \"gles\" forces OpenGL ES API. "
"Defaults to empty string, which selects the API native to the "
"system.");
DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling back to "
"software path rendering.");
#endif
sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) {
error.reset();
bool useTiles = false;
const char* widthString = nullptr;
const char* heightString = nullptr;
bool isPowerOf2Mode = false;
bool isCopyMode = false;
const char* mode = nullptr;
#if SK_SUPPORT_GPU
GrContextOptions grContextOpts;
grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks;
#define RENDERER_ARGS (grContextOpts)
#else
#define RENDERER_ARGS ()
#endif
SkAutoTUnref<sk_tools::PictureRenderer> renderer;
if (FLAGS_mode.count() >= 1) {
mode = FLAGS_mode[0];
if (0 == strcmp(mode, "record")) {
renderer.reset(new sk_tools::RecordPictureRenderer RENDERER_ARGS);
} else if (0 == strcmp(mode, "tile") || 0 == strcmp(mode, "pow2tile")
|| 0 == strcmp(mode, "copyTile")) {
useTiles = true;
if (0 == strcmp(mode, "pow2tile")) {
isPowerOf2Mode = true;
} else if (0 == strcmp(mode, "copyTile")) {
isCopyMode = true;
}
if (FLAGS_mode.count() < 2) {
error.printf("Missing width for --mode %s\n", mode);
return nullptr;
}
widthString = FLAGS_mode[1];
if (FLAGS_mode.count() < 3) {
error.printf("Missing height for --mode %s\n", mode);
return nullptr;
}
heightString = FLAGS_mode[2];
} else if (0 == strcmp(mode, "playbackCreation") && kBench_PictureTool == tool) {
renderer.reset(new sk_tools::PlaybackCreationRenderer RENDERER_ARGS);
// undocumented
} else if (0 == strcmp(mode, "rerecord") && kRender_PictureTool == tool) {
renderer.reset(new sk_tools::RecordPictureRenderer RENDERER_ARGS);
} else if (0 == strcmp(mode, "simple")) {
// Allow 'mode' to be set to 'simple', but do not create a renderer, so we can
// ensure that pipe does not override a mode besides simple. The renderer will
// be created below.
} else {
error.printf("%s is not a valid mode for --mode\n", mode);
return nullptr;
}
}
if (useTiles) {
SkASSERT(nullptr == renderer);
SkAutoTUnref<sk_tools::TiledPictureRenderer> tiledRenderer;
if (isCopyMode) {
int xTiles = -1;
int yTiles = -1;
if (FLAGS_tiles.count() > 0) {
if (FLAGS_tiles.count() != 2) {
error.printf("--tiles requires an x value and a y value.\n");
return nullptr;
}
xTiles = atoi(FLAGS_tiles[0]);
yTiles = atoi(FLAGS_tiles[1]);
}
int x, y;
if (xTiles != -1 && yTiles != -1) {
x = xTiles;
y = yTiles;
if (x <= 0 || y <= 0) {
error.printf("--tiles must be given values > 0\n");
return nullptr;
}
} else {
x = y = 4;
}
#if SK_SUPPORT_GPU
tiledRenderer.reset(new sk_tools::CopyTilesRenderer(grContextOpts, x, y));
#else
tiledRenderer.reset(new sk_tools::CopyTilesRenderer(x, y));
#endif
} else {
tiledRenderer.reset(new sk_tools::TiledPictureRenderer RENDERER_ARGS);
}
if (isPowerOf2Mode) {
int minWidth = atoi(widthString);
if (!SkIsPow2(minWidth) || minWidth < 0) {
SkString err;
error.printf("-mode %s must be given a width"
" value that is a power of two\n", mode);
return nullptr;
}
tiledRenderer->setTileMinPowerOf2Width(minWidth);
} else if (sk_tools::is_percentage(widthString)) {
if (isCopyMode) {
error.printf("--mode %s does not support percentages.\n", mode);
return nullptr;
}
tiledRenderer->setTileWidthPercentage(atof(widthString));
if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
error.printf("--mode %s must be given a width percentage > 0\n", mode);
return nullptr;
}
} else {
tiledRenderer->setTileWidth(atoi(widthString));
if (!(tiledRenderer->getTileWidth() > 0)) {
error.printf("--mode %s must be given a width > 0\n", mode);
return nullptr;
}
}
if (sk_tools::is_percentage(heightString)) {
if (isCopyMode) {
error.printf("--mode %s does not support percentages.\n", mode);
return nullptr;
}
tiledRenderer->setTileHeightPercentage(atof(heightString));
if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
error.printf("--mode %s must be given a height percentage > 0\n", mode);
return nullptr;
}
} else {
tiledRenderer->setTileHeight(atoi(heightString));
if (!(tiledRenderer->getTileHeight() > 0)) {
SkString err;
error.printf("--mode %s must be given a height > 0\n", mode);
return nullptr;
}
}
renderer.reset(tiledRenderer.detach());
if (FLAGS_pipe) {
error.printf("Pipe rendering is currently not compatible with tiling.\n"
"Turning off pipe.\n");
}
} else { // useTiles
if (FLAGS_pipe) {
if (renderer != nullptr) {
error.printf("Pipe is incompatible with other modes.\n");
return nullptr;
}
renderer.reset(new sk_tools::PipePictureRenderer RENDERER_ARGS);
}
}
if (nullptr == renderer) {
renderer.reset(new sk_tools::SimplePictureRenderer RENDERER_ARGS);
}
if (FLAGS_viewport.count() > 0) {
if (FLAGS_viewport.count() != 2) {
error.printf("--viewport requires a width and a height.\n");
return nullptr;
}
SkISize viewport;
viewport.fWidth = atoi(FLAGS_viewport[0]);
viewport.fHeight = atoi(FLAGS_viewport[1]);
renderer->setViewport(viewport);
}
sk_tools::PictureRenderer::SkDeviceTypes deviceType =
sk_tools::PictureRenderer::kBitmap_DeviceType;
#if SK_SUPPORT_GPU
GrGLStandard gpuAPI = kNone_GrGLStandard;
if (1 == FLAGS_gpuAPI.count()) {
if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) {
gpuAPI = kGL_GrGLStandard;
} else if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) {
gpuAPI = kGLES_GrGLStandard;
} else {
error.printf("--gpuAPI invalid api value.\n");
return nullptr;
}
} else if (FLAGS_gpuAPI.count() > 1) {
error.printf("--gpuAPI invalid api value.\n");
return nullptr;
}
int sampleCount = 0;
bool useDFText = false;
#endif
if (FLAGS_config.count() > 0) {
if (0 == strcmp(FLAGS_config[0], "8888")) {
deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
}
#if SK_SUPPORT_GPU
else if (0 == strcmp(FLAGS_config[0], "gpu")) {
deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
}
else if (0 == strcmp(FLAGS_config[0], "msaa4")) {
deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
sampleCount = 4;
}
else if (0 == strcmp(FLAGS_config[0], "msaa16")) {
deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
sampleCount = 16;
}
else if (0 == strcmp(FLAGS_config[0], "nvprmsaa4")) {
deviceType = sk_tools::PictureRenderer::kNVPR_DeviceType;
sampleCount = 4;
}
else if (0 == strcmp(FLAGS_config[0], "nvprmsaa16")) {
deviceType = sk_tools::PictureRenderer::kNVPR_DeviceType;
sampleCount = 16;
}
else if (0 == strcmp(FLAGS_config[0], "gpudft")) {
deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
useDFText = true;
}
#if SK_ANGLE
else if (0 == strcmp(FLAGS_config[0], "angle")) {
deviceType = sk_tools::PictureRenderer::kAngle_DeviceType;
}
#endif
#if SK_COMMAND_BUFFER
else if (0 == strcmp(FLAGS_config[0], "commandbuffer")) {
deviceType = sk_tools::PictureRenderer::kCommandBuffer_DeviceType;
}
#endif
#if SK_MESA
else if (0 == strcmp(FLAGS_config[0], "mesa")) {
deviceType = sk_tools::PictureRenderer::kMesa_DeviceType;
}
#endif
#endif
else {
error.printf("%s is not a valid mode for --config\n", FLAGS_config[0]);
return nullptr;
}
#if SK_SUPPORT_GPU
if (!renderer->setDeviceType(deviceType, gpuAPI)) {
#else
if (!renderer->setDeviceType(deviceType)) {
#endif
error.printf("Could not create backend for --config %s\n", FLAGS_config[0]);
return nullptr;
}
#if SK_SUPPORT_GPU
renderer->setSampleCount(sampleCount);
renderer->setUseDFText(useDFText);
#endif
}
sk_tools::PictureRenderer::BBoxHierarchyType bbhType
= sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
if (FLAGS_bbh.count() > 0) {
const char* type = FLAGS_bbh[0];
if (0 == strcmp(type, "none")) {
bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
} else if (0 == strcmp(type, "rtree")) {
bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
} else {
error.printf("%s is not a valid value for --bbhType\n", type);
return nullptr;
}
if (FLAGS_pipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
error.printf("--pipe and --bbh cannot be used together\n");
return nullptr;
}
}
renderer->setBBoxHierarchyType(bbhType);
renderer->setScaleFactor(SkDoubleToScalar(FLAGS_scale));
return renderer.detach();
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef PICTURE_RENDERING_FLAGS
#define PICTURE_RENDERING_FLAGS
class SkString;
namespace sk_tools {
class PictureRenderer;
}
enum PictureTool {
kBench_PictureTool,
kRender_PictureTool,
};
/**
* Uses SkCommandLineFlags to parse the command line, and returns a PictureRenderer
* reflecting the flags used. Assumes that SkCommandLineFlags::Parse has
* been called.
* @param error If there is an error or warning, it will be stored in error.
* @param tool Which tool is being used.
* @return PictureRenderer A PictureRenderer with the settings specified
* on the command line, or nullptr if the command line is invalid.
*/
sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool);
#endif // PICTURE_RENDERING_FLAGS

View File

@ -1,312 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Classes for writing out bench results in various formats.
*/
#ifndef SkPictureResultsWriter_DEFINED
#define SkPictureResultsWriter_DEFINED
#include "PictureRenderer.h"
#include "BenchLogger.h"
#include "ResultsWriter.h"
#include "SkJSONCPP.h"
#include "SkStream.h"
#include "SkString.h"
#include "SkTArray.h"
#include "TimerData.h"
#include <stdlib.h>
/**
* Base class for writing picture bench results.
*/
class PictureResultsWriter : SkNoncopyable {
public:
enum TileFlags {kPurging, kAvg};
PictureResultsWriter() {}
virtual ~PictureResultsWriter() {}
virtual void bench(const char name[], int32_t x, int32_t y) = 0;
virtual void logRenderer(sk_tools::PictureRenderer *pr) = 0;
virtual void tileMeta(int x, int y, int tx, int ty) = 0;
virtual void addTileFlag(PictureResultsWriter::TileFlags flag) = 0;
virtual void tileData(
TimerData* data,
const char format[],
const TimerData::Result result,
uint32_t timerTypes,
int numInnerLoops = 1) = 0;
virtual void end() = 0;
};
/**
* This class allows bench data to be piped into multiple
* PictureResultWriter classes. It does not own any classes
* passed to it, so the owner is required to manage any classes
* passed to PictureResultsMultiWriter */
class PictureResultsMultiWriter : public PictureResultsWriter {
public:
PictureResultsMultiWriter()
: fWriters() {}
void add(PictureResultsWriter* newWriter) {
fWriters.push_back(newWriter);
}
virtual ~PictureResultsMultiWriter() {}
void bench(const char name[], int32_t x, int32_t y) override {
for(int i=0; i<fWriters.count(); ++i) {
fWriters[i]->bench(name, x, y);
}
}
void logRenderer(sk_tools::PictureRenderer *pr) override {
for(int i=0; i<fWriters.count(); ++i) {
fWriters[i]->logRenderer(pr);
}
}
void tileMeta(int x, int y, int tx, int ty) override {
for(int i=0; i<fWriters.count(); ++i) {
fWriters[i]->tileMeta(x, y, tx, ty);
}
}
void addTileFlag(PictureResultsWriter::TileFlags flag) override {
for(int i=0; i<fWriters.count(); ++i) {
fWriters[i]->addTileFlag(flag);
}
}
virtual void tileData(
TimerData* data,
const char format[],
const TimerData::Result result,
uint32_t timerTypes,
int numInnerLoops = 1) override {
for(int i=0; i<fWriters.count(); ++i) {
fWriters[i]->tileData(data, format, result, timerTypes,
numInnerLoops);
}
}
void end() override {
for(int i=0; i<fWriters.count(); ++i) {
fWriters[i]->end();
}
}
private:
SkTArray<PictureResultsWriter*> fWriters;
};
/**
* Writes to BenchLogger to mimic original behavior
*/
class PictureResultsLoggerWriter : public PictureResultsWriter {
private:
void logProgress(const char str[]) {
if(fLogger != nullptr) {
fLogger->logProgress(str);
}
}
public:
PictureResultsLoggerWriter(BenchLogger* log)
: fLogger(log), fCurrentLine() {}
void bench(const char name[], int32_t x, int32_t y) override {
SkString result;
result.printf("running bench [%i %i] %s ", x, y, name);
this->logProgress(result.c_str());
}
void logRenderer(sk_tools::PictureRenderer* renderer) override {
fCurrentLine = renderer->getConfigName();
}
void tileMeta(int x, int y, int tx, int ty) override {
fCurrentLine.appendf(": tile [%i,%i] out of [%i,%i]", x, y, tx, ty);
}
void addTileFlag(PictureResultsWriter::TileFlags flag) override {
if(flag == PictureResultsWriter::kPurging) {
fCurrentLine.append(" <withPurging>");
} else if(flag == PictureResultsWriter::kAvg) {
fCurrentLine.append(" <averaged>");
}
}
virtual void tileData(
TimerData* data,
const char format[],
const TimerData::Result result,
uint32_t timerTypes,
int numInnerLoops = 1) override {
SkString results = data->getResult(format, result,
fCurrentLine.c_str(), timerTypes, numInnerLoops);
results.append("\n");
this->logProgress(results.c_str());
}
void end() override {}
private:
BenchLogger* fLogger;
SkString fCurrentLine;
};
/**
* This PictureResultsWriter collects data in a JSON node
*
* The format is something like
* {
* benches: [
* {
* name: "Name_of_test"
* tilesets: [
* {
* name: "Name of the configuration"
* tiles: [
* {
* flags: {
* purging: true //Flags for the current tile
* // are put here
* }
* data: {
* wsecs: [....] //Actual data ends up here
* }
* }
* ]
* }
* ]
* }
* ]
* }*/
class PictureJSONResultsWriter : public PictureResultsWriter {
public:
PictureJSONResultsWriter(const char filename[],
const char builderName[],
int buildNumber,
int timestamp,
const char gitHash[],
int gitNumber)
: fStream(filename) {
fBuilderName = SkString(builderName);
fBuildNumber = buildNumber;
fTimestamp = timestamp;
fGitHash = SkString(gitHash);
fGitNumber = gitNumber;
fBuilderData = this->makeBuilderJson();
}
void bench(const char name[], int32_t x, int32_t y) override {
fBenchName = SkString(name);
}
void logRenderer(sk_tools::PictureRenderer* pr) override {
fParams = pr->getJSONConfig();
fConfigString = pr->getConfigName();
}
// Apparently tiles aren't used, so tileMeta is empty
void tileMeta(int x, int y, int tx, int ty) override {}
// Flags aren't used, so addTileFlag is empty
void addTileFlag(PictureResultsWriter::TileFlags flag) override {}
virtual void tileData(
TimerData* data,
const char format[],
const TimerData::Result result,
uint32_t timerTypes,
int numInnerLoops = 1) override {
Json::Value newData = data->getJSON(timerTypes, result, numInnerLoops);
Json::Value combinedParams(fBuilderData);
for(Json::ValueIterator iter = fParams.begin(); iter != fParams.end();
iter++) {
combinedParams[iter.key().asString()]= *iter;
}
// For each set of timer data
for(Json::ValueIterator iter = newData.begin(); iter != newData.end();
iter++) {
Json::Value data;
data["buildNumber"] = fBuildNumber;
data["timestamp"] = fTimestamp;
data["gitHash"] = fGitHash.c_str();
data["gitNumber"] = fGitNumber;
data["isTrybot"] = fBuilderName.endsWith("Trybot");
data["params"] = combinedParams;
data["params"]["benchName"] = fBenchName.c_str();
// Not including skpSize because that's deprecated?
data["key"] = this->makeKey(iter.key().asString().c_str()).c_str();
// Get the data
SkTArray<double> times;
Json::Value val = *iter;
for(Json::ValueIterator vals = val.begin(); vals != val.end();
vals++) {
times.push_back((*vals).asDouble());
}
qsort(static_cast<void*>(times.begin()), times.count(),
sizeof(double), PictureJSONResultsWriter::CompareDoubles);
data["value"] = times[static_cast<int>(times.count() * 0.25f)];
data["params"]["measurementType"] = iter.key().asString();
fStream.writeText(Json::FastWriter().write(data).c_str());
}
}
void end() override {
fStream.flush();
}
private:
Json::Value makeBuilderJson() const {
static const int kNumKeys = 6;
static const char* kKeys[kNumKeys] = {
"role", "os", "model", "gpu", "arch", "configuration"};
Json::Value builderData;
if (!fBuilderName.isEmpty()) {
SkTArray<SkString> splitBuilder;
SkStrSplit(fBuilderName.c_str(), "-", &splitBuilder);
SkASSERT(splitBuilder.count() >= kNumKeys);
for (int i = 0; i < kNumKeys && i < splitBuilder.count(); ++i) {
builderData[kKeys[i]] = splitBuilder[i].c_str();
}
builderData["builderName"] = fBuilderName.c_str();
if (kNumKeys < splitBuilder.count()) {
SkString extras;
for (int i = kNumKeys; i < splitBuilder.count(); ++i) {
extras.append(splitBuilder[i]);
if (i != splitBuilder.count() - 1) {
extras.append("-");
}
}
builderData["badParams"] = extras.c_str();
}
}
return builderData;
}
static int CompareDoubles(const void* p1, const void* p2) {
if(*static_cast<const double*>(p1) < *static_cast<const double*>(p2)) {
return -1;
} else if(*static_cast<const double*>(p1) ==
*static_cast<const double*>(p2)) {
return 0;
} else {
return 1;
}
}
SkString makeKey(const char measurementType[]) const {
SkString tmp(fBuilderName);
tmp.append("_");
tmp.append(fBenchName);
tmp.append("_");
tmp.append(fConfigString);
tmp.append("_");
tmp.append(measurementType);
return tmp;
}
SkFILEWStream fStream;
Json::Value fBuilderData;
SkString fBenchName;
Json::Value fParams;
SkString fConfigString;
SkString fBuilderName;
int fBuildNumber;
int fTimestamp;
SkString fGitHash;
int fGitNumber;
};
#endif

View File

@ -1,83 +0,0 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
This file defines the configurations in which bench_pictures should be run
on various platforms. The buildbots read these configurations from the
bench_pictures_cfg dictionary. Everything else in this file exists to help in
constructing that dictionary.
This code is executed directly on the buildbot so that convenient things like
variables and loops can be used to avoid unnecessary verbosity. With great power
comes great responsibility; don't put any nasty code here. To reiterate, code in
this file will be directly executed on the build slaves.
"""
import os
import sys
if 'import_path' in globals():
sys.path.append(import_path)
from bench_pictures_cfg_helper import *
# Default tile sizes
DEFAULT_TILE_X = '256'
DEFAULT_TILE_Y = '256'
# Default viewport size
DEFAULT_VIEWPORT_X = 1000
DEFAULT_VIEWPORT_Y = 1000
# Default scale factor for scaled configs.
DEFAULT_SCALE = 1.1
# Configs to run on most bots
default_configs = [
# Viewport CPU and GPU
ViewportBitmapConfig(DEFAULT_VIEWPORT_X, DEFAULT_VIEWPORT_Y),
ViewportGPUConfig(DEFAULT_VIEWPORT_X, DEFAULT_VIEWPORT_Y),
# Scaled viewport, CPU and GPU
ViewportBitmapConfig(DEFAULT_VIEWPORT_X, DEFAULT_VIEWPORT_Y,
scale=str(DEFAULT_SCALE)),
ViewportGPUConfig(DEFAULT_VIEWPORT_X, DEFAULT_VIEWPORT_Y,
scale=str(DEFAULT_SCALE)),
]
default_no_gpu = [cfg for cfg in default_configs if cfg['config'] != 'gpu']
msaa4 = Config(config='msaa4', viewport=[str(DEFAULT_VIEWPORT_X),
str(DEFAULT_VIEWPORT_Y)])
msaa16 = Config(config='msaa16', viewport=[str(DEFAULT_VIEWPORT_X),
str(DEFAULT_VIEWPORT_Y)])
viewport_angle = Config(config='angle', viewport=[str(DEFAULT_VIEWPORT_X),
str(DEFAULT_VIEWPORT_Y)])
# This dictionary defines the sets of configs for all platforms. Each config is
# a dictionary of key/value pairs directly corresponding to the command-line
# flags passed to bench_pictures.
bench_pictures_cfg = {
'angle': [viewport_angle],
'debug': [ViewportBitmapConfig(DEFAULT_VIEWPORT_X, DEFAULT_VIEWPORT_Y)],
'default': default_configs,
'no_gpu': default_no_gpu,
'nexus_s': default_no_gpu,
'xoom': default_configs,
'galaxy_nexus': default_configs,
'nexus_4': default_configs + [msaa4],
'nexus_7': default_configs,
'nexus_10': default_configs + [msaa4],
'razr_i': default_configs + [msaa4],
'intel_rhb': default_no_gpu,
'default_msaa16': default_configs + [msaa16],
}

View File

@ -1,107 +0,0 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Helper functions to be used in bench_pictures.cfg. """
def Config(**kwargs):
config = {}
for key in kwargs:
config[key] = kwargs[key]
return config
def TileArgs(tile_x, tile_y, timeIndividualTiles=True):
config = {'mode': ['tile', str(tile_x), str(tile_y)]}
if timeIndividualTiles:
config['timeIndividualTiles'] = True
return config
def BitmapConfig(**kwargs):
return Config(config='8888', **kwargs)
def GPUConfig(**kwargs):
return Config(config='gpu', **kwargs)
def TiledBitmapConfig(tile_x, tile_y, timeIndividualTiles=True, **kwargs):
return BitmapConfig(**dict(TileArgs(tile_x, tile_y,
timeIndividualTiles=timeIndividualTiles).items() + kwargs.items()))
def TiledGPUConfig(tile_x, tile_y, **kwargs):
return GPUConfig(**dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
def TiledConfig(tile_x, tile_y, timeIndividualTiles=True, **kwargs):
return Config(**dict(TileArgs(tile_x, tile_y,
timeIndividualTiles=timeIndividualTiles).items() + kwargs.items()))
def ViewportBitmapConfig(viewport_x, viewport_y, **kwargs):
return BitmapConfig(viewport=[str(viewport_x), str(viewport_y)], **kwargs)
def ViewportGPUConfig(viewport_x, viewport_y, **kwargs):
return GPUConfig(viewport=[str(viewport_x), str(viewport_y)], **kwargs)
def ViewportRTreeConfig(viewport_x, viewport_y, **kwargs):
return RTreeConfig(mode='simple', viewport=[str(viewport_x), str(viewport_y)],
**kwargs)
def ViewportGridConfig(viewport_x, viewport_y, **kwargs):
return GridConfig(viewport_x, viewport_y, mode='simple',
viewport=[str(viewport_x), str(viewport_y)], **kwargs)
def CopyTilesConfig(tile_x, tile_y, **kwargs):
return BitmapConfig(mode=['copyTile', str(tile_x), str(tile_y)], **kwargs)
def RecordConfig(**kwargs):
return BitmapConfig(mode='record', **kwargs)
def PlaybackCreationConfig(**kwargs):
return BitmapConfig(mode='playbackCreation', **kwargs)
def MultiThreadTileConfig(threads, tile_x, tile_y, **kwargs):
return TiledBitmapConfig(tile_x=tile_x, tile_y=tile_y,
timeIndividualTiles=False, multi=str(threads),
**kwargs)
def RTreeConfig(**kwargs):
return BitmapConfig(bbh='rtree', **kwargs)
def GridConfig(tile_x, tile_y, mode, **kwargs):
return BitmapConfig(mode=mode, bbh=['grid', str(tile_x), str(tile_y)],
**kwargs)
def RecordRTreeConfig(**kwargs):
return RTreeConfig(mode='record', **kwargs)
def PlaybackCreationRTreeConfig(**kwargs):
return RTreeConfig(mode='playbackCreation', **kwargs)
def TileRTreeConfig(tile_x, tile_y, **kwargs):
return RTreeConfig(**dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
def RecordGridConfig(tile_x, tile_y, **kwargs):
return GridConfig(tile_x=tile_x, tile_y=tile_y, mode='record', **kwargs)
def PlaybackCreationGridConfig(tile_x, tile_y, **kwargs):
return GridConfig(tile_x, tile_y, mode='playbackCreation')

View File

@ -1,481 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "BenchLogger.h"
#include "Timer.h"
#include "CopyTilesRenderer.h"
#include "CrashHandler.h"
#include "LazyDecodeBitmap.h"
#include "PictureBenchmark.h"
#include "PictureRenderingFlags.h"
#include "PictureResultsWriter.h"
#include "SkCommandLineFlags.h"
#include "SkData.h"
#include "SkDiscardableMemoryPool.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#include "SkMath.h"
#include "SkOSFile.h"
#include "SkPicture.h"
#include "SkStream.h"
#include "picture_utils.h"
BenchLogger gLogger;
PictureResultsLoggerWriter gLogWriter(&gLogger);
PictureResultsMultiWriter gWriter;
// Flags used by this file, in alphabetical order.
DEFINE_bool(countRAM, false, "Count the RAM used for bitmap pixels in each skp file");
DECLARE_bool(deferImageDecoding);
DEFINE_string(filter, "",
"type:flag : Enable canvas filtering to disable a paint flag, "
"use no blur or low quality blur, or use no hinting or "
"slight hinting. For all flags except AAClip, specify the "
"type of primitive to effect, or choose all. for AAClip "
"alone, the filter affects all clips independent of type. "
"Specific flags are listed above.");
DEFINE_string(logFile, "", "Destination for writing log output, in addition to stdout.");
DEFINE_bool(logPerIter, false, "Log each repeat timer instead of mean.");
DEFINE_string(jsonLog, "", "Destination for writing JSON data.");
DEFINE_bool(min, false, "Print the minimum times (instead of average).");
DECLARE_string(readPath);
DEFINE_int32(repeat, 1, "Set the number of times to repeat each test.");
DEFINE_bool(timeIndividualTiles, false, "Report times for drawing individual tiles, rather than "
"times for drawing the whole page. Requires tiled rendering.");
DEFINE_bool(purgeDecodedTex, false, "Purge decoded and GPU-uploaded textures "
"after each iteration.");
DEFINE_string(timers, "c", "[wcgWC]*: Display wall, cpu, gpu, truncated wall or truncated cpu time"
" for each picture.");
DEFINE_bool(trackDeferredCaching, false, "Only meaningful with --deferImageDecoding and "
"SK_LAZY_CACHE_STATS set to true. Report percentage of cache hits when using "
"deferred image decoding.");
#if GR_GPU_STATS
DEFINE_bool(gpuStats, false, "Only meaningful with gpu configurations. "
"Report some GPU call statistics.");
#endif
DEFINE_bool(mpd, false, "If true, use MultiPictureDraw to render.");
// Buildbot-specific parameters
DEFINE_string(builderName, "", "Name of the builder this is running on.");
DEFINE_int32(buildNumber, -1, "Build number of the build this test is running on");
DEFINE_int32(timestamp, 0, "Timestamp of the revision of Skia being tested.");
DEFINE_string(gitHash, "", "Commit hash of the revision of Skia being run.");
DEFINE_int32(gitNumber, -1, "Git number of the revision of Skia being run.");
static char const * const gFilterTypes[] = {
"paint",
"point",
"line",
"bitmap",
"rect",
"oval",
"path",
"text",
"all",
};
static const size_t kFilterTypesCount = sizeof(gFilterTypes) / sizeof(gFilterTypes[0]);
static char const * const gFilterFlags[] = {
"antiAlias",
"filterBitmap",
"dither",
"underlineText",
"strikeThruText",
"fakeBoldText",
"linearText",
"subpixelText",
"devKernText",
"LCDRenderText",
"embeddedBitmapText",
"autoHinting",
"verticalText",
"genA8FromLCD",
"blur",
"hinting",
"slightHinting",
"AAClip",
};
static const size_t kFilterFlagsCount = sizeof(gFilterFlags) / sizeof(gFilterFlags[0]);
static SkString filtersName(sk_tools::PictureRenderer::DrawFilterFlags* drawFilters) {
int all = drawFilters[0];
size_t tIndex;
for (tIndex = 1; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
all &= drawFilters[tIndex];
}
SkString result;
for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
SkString types;
if (all & (1 << fIndex)) {
types = gFilterTypes[SkDrawFilter::kTypeCount];
} else {
for (tIndex = 0; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
if (drawFilters[tIndex] & (1 << fIndex)) {
types += gFilterTypes[tIndex];
}
}
}
if (!types.size()) {
continue;
}
result += "_";
result += types;
result += ".";
result += gFilterFlags[fIndex];
}
return result;
}
static SkString filterTypesUsage() {
SkString result;
for (size_t index = 0; index < kFilterTypesCount; ++index) {
result += gFilterTypes[index];
if (index < kFilterTypesCount - 1) {
result += " | ";
}
}
return result;
}
static SkString filterFlagsUsage() {
SkString result;
size_t len = 0;
for (size_t index = 0; index < kFilterFlagsCount; ++index) {
result += gFilterFlags[index];
if (result.size() - len >= 72) {
result += "\n\t\t";
len = result.size();
}
if (index < kFilterFlagsCount - 1) {
result += " | ";
}
}
return result;
}
#if SK_LAZY_CACHE_STATS
static int32_t gTotalCacheHits;
static int32_t gTotalCacheMisses;
#endif
static bool run_single_benchmark(const SkString& inputPath,
sk_tools::PictureBenchmark& benchmark) {
SkFILEStream inputStream;
inputStream.setPath(inputPath.c_str());
if (!inputStream.isValid()) {
SkString err;
err.printf("Could not open file %s\n", inputPath.c_str());
gLogger.logError(err);
return false;
}
SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
// Since the old picture has been deleted, all pixels should be cleared.
SkASSERT(pool->getRAMUsed() == 0);
if (FLAGS_countRAM) {
pool->setRAMBudget(SK_MaxU32);
// Set the limit to max, so all pixels will be kept
}
SkPicture::InstallPixelRefProc proc;
if (FLAGS_deferImageDecoding) {
proc = &sk_tools::LazyDecodeBitmap;
} else {
proc = &SkImageDecoder::DecodeMemory;
}
SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, proc));
if (nullptr == picture.get()) {
SkString err;
err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
gLogger.logError(err);
return false;
}
SkString filename = SkOSPath::Basename(inputPath.c_str());
gWriter.bench(filename.c_str(),
SkScalarCeilToInt(picture->cullRect().width()),
SkScalarCeilToInt(picture->cullRect().height()));
benchmark.run(picture, FLAGS_mpd);
#if SK_LAZY_CACHE_STATS
if (FLAGS_trackDeferredCaching) {
int cacheHits = pool->getCacheHits();
int cacheMisses = pool->getCacheMisses();
pool->resetCacheHitsAndMisses();
SkString hitString;
hitString.printf("Cache hit rate: %f\n", (double) cacheHits / (cacheHits + cacheMisses));
gLogger.logProgress(hitString);
gTotalCacheHits += cacheHits;
gTotalCacheMisses += cacheMisses;
}
#endif
if (FLAGS_countRAM) {
SkString ramCount("RAM used for bitmaps: ");
size_t bytes = pool->getRAMUsed();
if (bytes > 1024) {
size_t kb = bytes / 1024;
if (kb > 1024) {
size_t mb = kb / 1024;
ramCount.appendf("%zi MB\n", mb);
} else {
ramCount.appendf("%zi KB\n", kb);
}
} else {
ramCount.appendf("%zi bytes\n", bytes);
}
gLogger.logProgress(ramCount);
}
return true;
}
static void setup_benchmark(sk_tools::PictureBenchmark* benchmark) {
sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
sk_bzero(drawFilters, sizeof(drawFilters));
if (FLAGS_filter.count() > 0) {
const char* filters = FLAGS_filter[0];
const char* colon = strchr(filters, ':');
if (colon) {
int32_t type = -1;
size_t typeLen = colon - filters;
for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
if (typeLen == strlen(gFilterTypes[tIndex])
&& !strncmp(filters, gFilterTypes[tIndex], typeLen)) {
type = SkToS32(tIndex);
break;
}
}
if (type < 0) {
SkString err;
err.printf("Unknown type for --filter %s\n", filters);
gLogger.logError(err);
exit(-1);
}
int flag = -1;
size_t flagLen = strlen(filters) - typeLen - 1;
for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
if (flagLen == strlen(gFilterFlags[fIndex])
&& !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
flag = 1 << fIndex;
break;
}
}
if (flag < 0) {
SkString err;
err.printf("Unknown flag for --filter %s\n", filters);
gLogger.logError(err);
exit(-1);
}
for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
if (type != SkDrawFilter::kTypeCount && index != type) {
continue;
}
drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
(drawFilters[index] | flag);
}
} else {
SkString err;
err.printf("Unknown arg for --filter %s : missing colon\n", filters);
gLogger.logError(err);
exit(-1);
}
}
if (FLAGS_timers.count() > 0) {
size_t index = 0;
bool timerWall = false;
bool truncatedTimerWall = false;
bool timerCpu = false;
bool truncatedTimerCpu = false;
bool timerGpu = false;
while (index < strlen(FLAGS_timers[0])) {
switch (FLAGS_timers[0][index]) {
case 'w':
timerWall = true;
break;
case 'c':
timerCpu = true;
break;
case 'W':
truncatedTimerWall = true;
break;
case 'C':
truncatedTimerCpu = true;
break;
case 'g':
timerGpu = true;
break;
default:
SkDebugf("mystery character\n");
break;
}
index++;
}
benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu,
timerGpu);
}
SkString errorString;
SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
kBench_PictureTool));
if (errorString.size() > 0) {
gLogger.logError(errorString);
}
if (nullptr == renderer.get()) {
exit(-1);
}
if (FLAGS_timeIndividualTiles) {
sk_tools::TiledPictureRenderer* tiledRenderer = renderer->getTiledRenderer();
if (nullptr == tiledRenderer) {
gLogger.logError("--timeIndividualTiles requires tiled rendering.\n");
exit(-1);
}
if (!tiledRenderer->supportsTimingIndividualTiles()) {
gLogger.logError("This renderer does not support --timeIndividualTiles.\n");
exit(-1);
}
benchmark->setTimeIndividualTiles(true);
}
benchmark->setPurgeDecodedTex(FLAGS_purgeDecodedTex);
if (FLAGS_readPath.count() < 1) {
gLogger.logError(".skp files or directories are required.\n");
exit(-1);
}
renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
if (FLAGS_logPerIter) {
benchmark->setTimerResultType(TimerData::kPerIter_Result);
} else if (FLAGS_min) {
benchmark->setTimerResultType(TimerData::kMin_Result);
} else {
benchmark->setTimerResultType(TimerData::kAvg_Result);
}
benchmark->setRenderer(renderer);
benchmark->setRepeats(FLAGS_repeat);
benchmark->setWriter(&gWriter);
}
static int process_input(const char* input,
sk_tools::PictureBenchmark& benchmark) {
SkString inputAsSkString(input);
SkOSFile::Iter iter(input, "skp");
SkString inputFilename;
int failures = 0;
if (iter.next(&inputFilename)) {
do {
SkString inputPath = SkOSPath::Join(input, inputFilename.c_str());
if (!run_single_benchmark(inputPath, benchmark)) {
++failures;
}
} while(iter.next(&inputFilename));
} else if (SkStrEndsWith(input, ".skp")) {
if (!run_single_benchmark(inputAsSkString, benchmark)) {
++failures;
}
} else {
SkString warning;
warning.printf("Warning: skipping %s\n", input);
gLogger.logError(warning);
}
return failures;
}
int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
SetupCrashHandler();
SkString usage;
usage.printf("Time drawing .skp files.\n"
"\tPossible arguments for --filter: [%s]\n\t\t[%s]",
filterTypesUsage().c_str(), filterFlagsUsage().c_str());
SkCommandLineFlags::SetUsage(usage.c_str());
SkCommandLineFlags::Parse(argc, argv);
if (FLAGS_repeat < 1) {
SkString error;
error.printf("--repeats must be >= 1. Was %i\n", FLAGS_repeat);
gLogger.logError(error);
exit(-1);
}
if (FLAGS_logFile.count() == 1) {
if (!gLogger.SetLogFile(FLAGS_logFile[0])) {
SkString str;
str.printf("Could not open %s for writing.\n", FLAGS_logFile[0]);
gLogger.logError(str);
// TODO(borenet): We're disabling this for now, due to
// write-protected Android devices. The very short-term
// solution is to ignore the fact that we have no log file.
//exit(-1);
}
}
SkAutoTDelete<PictureJSONResultsWriter> jsonWriter;
if (FLAGS_jsonLog.count() == 1) {
SkASSERT(FLAGS_builderName.count() == 1 && FLAGS_gitHash.count() == 1);
jsonWriter.reset(new PictureJSONResultsWriter(FLAGS_jsonLog[0], FLAGS_builderName[0],
FLAGS_buildNumber, FLAGS_timestamp,
FLAGS_gitHash[0], FLAGS_gitNumber));
gWriter.add(jsonWriter.get());
}
gWriter.add(&gLogWriter);
SkAutoGraphics ag;
sk_tools::PictureBenchmark benchmark;
setup_benchmark(&benchmark);
int failures = 0;
for (int i = 0; i < FLAGS_readPath.count(); ++i) {
failures += process_input(FLAGS_readPath[i], benchmark);
}
if (failures != 0) {
SkString err;
err.printf("Failed to run %i benchmarks.\n", failures);
gLogger.logError(err);
return 1;
}
#if SK_LAZY_CACHE_STATS
if (FLAGS_trackDeferredCaching) {
SkDebugf("Total cache hit rate: %f\n",
(double) gTotalCacheHits / (gTotalCacheHits + gTotalCacheMisses));
}
#endif
#if GR_GPU_STATS && SK_SUPPORT_GPU
if (FLAGS_gpuStats && benchmark.renderer()->isUsingGpuDevice()) {
benchmark.renderer()->getGrContext()->printGpuStats();
}
#endif
gWriter.end();
return 0;
}
#if !defined SK_BUILD_FOR_IOS
int main(int argc, char * const argv[]) {
return tool_main(argc, (char**) argv);
}
#endif

View File

@ -1,7 +0,0 @@
# This file contains references to Chrome CLs which are associated with Skia
# changes. If your Skia change requires a change in Chrome, add a line to this
# file which contains the codereview issue(s) for the Chrome change, eg:
# https://codereview.chromium.org/249493003/
# In the future, these changes will be automatically applied and committed as
# part of the DEPS roll which contains the Skia change.
https://codereview.chromium.org/265513005/

View File

@ -1,83 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stdio.h>
#include "SkCommandLineFlags.h"
#include "SkGraphics.h"
#include "SkPicture.h"
#include "SkRecordOpts.h"
#include "SkRecorder.h"
#include "SkStream.h"
#include "DumpRecord.h"
#include "LazyDecodeBitmap.h"
#include <stdlib.h>
DEFINE_string2(skps, r, "", ".SKPs to dump.");
DEFINE_string(match, "", "The usual filters on file names to dump.");
DEFINE_bool2(optimize, O, false, "Run SkRecordOptimize before dumping.");
DEFINE_int32(tile, 1000000000, "Simulated tile size.");
DEFINE_bool(timeWithCommand, false, "If true, print time next to command, else in first column.");
static void dump(const char* name, int w, int h, const SkRecord& record) {
SkBitmap bitmap;
bitmap.allocN32Pixels(w, h);
SkCanvas canvas(bitmap);
canvas.clipRect(SkRect::MakeWH(SkIntToScalar(FLAGS_tile),
SkIntToScalar(FLAGS_tile)));
printf("%s %s\n", FLAGS_optimize ? "optimized" : "not-optimized", name);
DumpRecord(record, &canvas, FLAGS_timeWithCommand);
}
int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
SkCommandLineFlags::Parse(argc, argv);
SkAutoGraphics ag;
for (int i = 0; i < FLAGS_skps.count(); i++) {
if (SkCommandLineFlags::ShouldSkip(FLAGS_match, FLAGS_skps[i])) {
continue;
}
SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(FLAGS_skps[i]));
if (!stream) {
SkDebugf("Could not read %s.\n", FLAGS_skps[i]);
exit(1);
}
SkAutoTUnref<SkPicture> src(
SkPicture::CreateFromStream(stream, sk_tools::LazyDecodeBitmap));
if (!src) {
SkDebugf("Could not read %s as an SkPicture.\n", FLAGS_skps[i]);
exit(1);
}
const int w = SkScalarCeilToInt(src->cullRect().width());
const int h = SkScalarCeilToInt(src->cullRect().height());
SkRecord record;
SkRecorder canvas(&record, w, h);
src->playback(&canvas);
if (FLAGS_optimize) {
SkRecordOptimize(&record);
}
dump(FLAGS_skps[i], w, h, record);
}
return 0;
}
#if !defined SK_BUILD_FOR_IOS
int main(int argc, char * const argv[]) {
return tool_main(argc, (char**) argv);
}
#endif

View File

@ -1,51 +0,0 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkData.h"
#include "SkForceLinking.h"
#include "SkImageGenerator.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
#include "SkStream.h"
#include <stdlib.h>
#include <stdio.h>
__SK_FORCE_IMAGE_DECODER_LINKING;
#define ASSERTF(cond, fmt, ...) if (!(cond)) { fprintf(stderr, fmt"\n", __VA_ARGS__); exit(1); }
static bool lazy_decode_bitmap(const void* src, size_t size, SkBitmap* dst) {
SkAutoTUnref<SkData> encoded(SkData::NewWithCopy(src, size));
return encoded && SkDEPRECATED_InstallDiscardablePixelRef(encoded, dst);
}
int main(int argc, char** argv) {
ASSERTF(argc == 3, "usage: %s nested.skp flat.skp", argv[0]);
const char *nestedPath = argv[1],
*flatPath = argv[2];
// Read nested.skp.
SkFILEStream stream(nestedPath);
ASSERTF(stream.isValid(), "Couldn't read %s.", nestedPath);
SkAutoTUnref<const SkPicture> nested(SkPicture::CreateFromStream(&stream, &lazy_decode_bitmap));
ASSERTF(nested, "Couldn't parse %s as a picture.", nestedPath);
// Play it back into a new picture using kPlaybackDrawPicture_RecordFlag.
SkPictureRecorder recorder;
uint32_t flags = SkPictureRecorder::kPlaybackDrawPicture_RecordFlag;
nested->playback(recorder.beginRecording(nested->cullRect(), nullptr, flags));
SkAutoTUnref<const SkPicture> flat(recorder.endRecordingAsPicture());
// Write out that flat.skp
SkFILEWStream wstream(flatPath);
ASSERTF(wstream.isValid(), "Could not open %s.", flatPath);
flat->serialize(&wstream);
wstream.flush();
return 0;
}

View File

@ -1,259 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBitmap.h"
#include "SkBitmapHasher.h"
#include "SkData.h"
#include "SkJSONCPP.h"
#include "SkOSFile.h"
#include "SkStream.h"
#include "SkTypes.h"
#include "image_expectations.h"
/*
* TODO(epoger): Make constant strings consistent instead of mixing hypenated and camel-caps.
*
* TODO(epoger): Similar constants are already maintained in 2 other places:
* gm/gm_json.py and gm/gm_expectations.cpp. We shouldn't add yet a third place.
* Figure out a way to share the definitions instead.
*
* Note that, as of https://codereview.chromium.org/226293002 , the JSON
* schema used here has started to differ from the one in gm_expectations.cpp .
* TODO(epoger): Consider getting GM and render_pictures to use the same JSON
* output module.
*/
const static char kJsonKey_ActualResults[] = "actual-results";
const static char kJsonKey_Descriptions[] = "descriptions";
const static char kJsonKey_ExpectedResults[] = "expected-results";
const static char kJsonKey_ImageBaseGSUrl[] = "image-base-gs-url";
const static char kJsonKey_Header[] = "header";
const static char kJsonKey_Header_Type[] = "type";
const static char kJsonKey_Header_Revision[] = "revision";
const static char kJsonKey_Image_ChecksumAlgorithm[] = "checksumAlgorithm";
const static char kJsonKey_Image_ChecksumValue[] = "checksumValue";
const static char kJsonKey_Image_ComparisonResult[] = "comparisonResult";
const static char kJsonKey_Image_Filepath[] = "filepath";
const static char kJsonKey_Image_IgnoreFailure[] = "ignoreFailure";
const static char kJsonKey_Source_TiledImages[] = "tiled-images";
const static char kJsonKey_Source_WholeImage[] = "whole-image";
// Values (not keys) that are written out by this JSON generator
const static char kJsonValue_Header_Type[] = "ChecksummedImages";
const static int kJsonValue_Header_Revision = 1;
const static char kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5[] = "bitmap-64bitMD5";
const static char kJsonValue_Image_ComparisonResult_Failed[] = "failed";
const static char kJsonValue_Image_ComparisonResult_FailureIgnored[] = "failure-ignored";
const static char kJsonValue_Image_ComparisonResult_NoComparison[] = "no-comparison";
const static char kJsonValue_Image_ComparisonResult_Succeeded[] = "succeeded";
namespace sk_tools {
// ImageDigest class...
ImageDigest::ImageDigest(const SkBitmap &bitmap) :
fBitmap(bitmap), fHashValue(0), fComputedHashValue(false) {}
ImageDigest::ImageDigest(const SkString &hashType, uint64_t hashValue) :
fBitmap(), fHashValue(hashValue), fComputedHashValue(true) {
if (!hashType.equals(kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5)) {
SkDebugf("unsupported hashType '%s'\n", hashType.c_str());
SkFAIL("unsupported hashType (see above)");
}
}
bool ImageDigest::equals(ImageDigest &other) {
// TODO(epoger): The current implementation assumes that this
// and other always have hashType kJsonKey_Hashtype_Bitmap_64bitMD5
return (this->getHashValue() == other.getHashValue());
}
SkString ImageDigest::getHashType() {
// TODO(epoger): The current implementation assumes that the
// result digest is always of type kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 .
return SkString(kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5);
}
uint64_t ImageDigest::getHashValue() {
if (!this->fComputedHashValue) {
if (!SkBitmapHasher::ComputeDigest(this->fBitmap, &this->fHashValue)) {
SkFAIL("unable to compute image digest");
}
this->fComputedHashValue = true;
}
return this->fHashValue;
}
// BitmapAndDigest class...
BitmapAndDigest::BitmapAndDigest(const SkBitmap &bitmap) :
fBitmap(bitmap), fImageDigest(bitmap) {}
const SkBitmap *BitmapAndDigest::getBitmapPtr() const {return &fBitmap;}
ImageDigest *BitmapAndDigest::getImageDigestPtr() {return &fImageDigest;}
// Expectation class...
// For when we need a valid ImageDigest, but we don't care what it is.
static const ImageDigest kDummyImageDigest(
SkString(kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5), 0);
Expectation::Expectation(bool ignoreFailure) :
fIsEmpty(true), fIgnoreFailure(ignoreFailure), fImageDigest(kDummyImageDigest) {}
Expectation::Expectation(const SkString &hashType, uint64_t hashValue, bool ignoreFailure) :
fIsEmpty(false), fIgnoreFailure(ignoreFailure), fImageDigest(hashType, hashValue) {}
Expectation::Expectation(const SkBitmap& bitmap, bool ignoreFailure) :
fIsEmpty(false), fIgnoreFailure(ignoreFailure), fImageDigest(bitmap) {}
bool Expectation::ignoreFailure() const { return this->fIgnoreFailure; }
bool Expectation::empty() const { return this->fIsEmpty; }
bool Expectation::matches(ImageDigest &imageDigest) {
return !(this->fIsEmpty) && (this->fImageDigest.equals(imageDigest));
}
// ImageResultsAndExpectations class...
bool ImageResultsAndExpectations::readExpectationsFile(const char *jsonPath) {
if (nullptr == jsonPath) {
SkDebugf("JSON expectations filename not specified\n");
return false;
}
SkFILE* filePtr = sk_fopen(jsonPath, kRead_SkFILE_Flag);
if (nullptr == filePtr) {
SkDebugf("JSON expectations file '%s' does not exist\n", jsonPath);
return false;
}
size_t size = sk_fgetsize(filePtr);
if (0 == size) {
SkDebugf("JSON expectations file '%s' is empty, so no expectations\n", jsonPath);
sk_fclose(filePtr);
fExpectedResults.clear();
return true;
}
bool parsedJson = Parse(filePtr, &fExpectedJsonRoot);
sk_fclose(filePtr);
if (!parsedJson) {
SkDebugf("Failed to parse JSON expectations file '%s'\n", jsonPath);
return false;
}
Json::Value header = fExpectedJsonRoot[kJsonKey_Header];
Json::Value headerType = header[kJsonKey_Header_Type];
Json::Value headerRevision = header[kJsonKey_Header_Revision];
if (strcmp(headerType.asCString(), kJsonValue_Header_Type)) {
SkDebugf("JSON expectations file '%s': expected headerType '%s', found '%s'\n",
jsonPath, kJsonValue_Header_Type, headerType.asCString());
return false;
}
if (headerRevision.asInt() != kJsonValue_Header_Revision) {
SkDebugf("JSON expectations file '%s': expected headerRevision %d, found %d\n",
jsonPath, kJsonValue_Header_Revision, headerRevision.asInt());
return false;
}
fExpectedResults = fExpectedJsonRoot[kJsonKey_ExpectedResults];
return true;
}
void ImageResultsAndExpectations::add(const char *sourceName, const char *fileName,
ImageDigest &digest, const int *tileNumber) {
// Get expectation, if any.
Expectation expectation = this->getExpectation(sourceName, tileNumber);
// Fill in info about the actual result.
Json::Value actualChecksumAlgorithm = digest.getHashType().c_str();
Json::Value actualChecksumValue = Json::UInt64(digest.getHashValue());
Json::Value actualImage;
actualImage[kJsonKey_Image_ChecksumAlgorithm] = actualChecksumAlgorithm;
actualImage[kJsonKey_Image_ChecksumValue] = actualChecksumValue;
actualImage[kJsonKey_Image_Filepath] = fileName;
// Compare against expectedImage to fill in comparisonResult.
Json::Value comparisonResult;
if (expectation.empty()) {
comparisonResult = kJsonValue_Image_ComparisonResult_NoComparison;
} else if (expectation.matches(digest)) {
comparisonResult = kJsonValue_Image_ComparisonResult_Succeeded;
} else if (expectation.ignoreFailure()) {
comparisonResult = kJsonValue_Image_ComparisonResult_FailureIgnored;
} else {
comparisonResult = kJsonValue_Image_ComparisonResult_Failed;
}
actualImage[kJsonKey_Image_ComparisonResult] = comparisonResult;
// Add this actual result to our collection.
if (nullptr == tileNumber) {
fActualResults[sourceName][kJsonKey_Source_WholeImage] = actualImage;
} else {
fActualResults[sourceName][kJsonKey_Source_TiledImages][*tileNumber] = actualImage;
}
}
void ImageResultsAndExpectations::addDescription(const char *key, const char *value) {
fDescriptions[key] = value;
}
void ImageResultsAndExpectations::setImageBaseGSUrl(const char *imageBaseGSUrl) {
fImageBaseGSUrl = imageBaseGSUrl;
}
Expectation ImageResultsAndExpectations::getExpectation(const char *sourceName,
const int *tileNumber) {
if (fExpectedResults.isNull()) {
return Expectation();
}
Json::Value expectedImage;
if (nullptr == tileNumber) {
expectedImage = fExpectedResults[sourceName][kJsonKey_Source_WholeImage];
} else {
expectedImage = fExpectedResults[sourceName][kJsonKey_Source_TiledImages][*tileNumber];
}
if (expectedImage.isNull()) {
return Expectation();
}
bool ignoreFailure = (expectedImage[kJsonKey_Image_IgnoreFailure] == true);
return Expectation(SkString(expectedImage[kJsonKey_Image_ChecksumAlgorithm].asCString()),
expectedImage[kJsonKey_Image_ChecksumValue].asUInt64(),
ignoreFailure);
}
void ImageResultsAndExpectations::writeToFile(const char *filename) const {
Json::Value header;
header[kJsonKey_Header_Type] = kJsonValue_Header_Type;
header[kJsonKey_Header_Revision] = kJsonValue_Header_Revision;
Json::Value root;
root[kJsonKey_ActualResults] = fActualResults;
root[kJsonKey_Descriptions] = fDescriptions;
root[kJsonKey_Header] = header;
root[kJsonKey_ImageBaseGSUrl] = fImageBaseGSUrl;
std::string jsonStdString = root.toStyledString();
SkFILEWStream stream(filename);
stream.write(jsonStdString.c_str(), jsonStdString.length());
}
/*static*/ bool ImageResultsAndExpectations::Parse(SkFILE *filePtr,
Json::Value *jsonRoot) {
SkAutoDataUnref dataRef(SkData::NewFromFILE(filePtr));
if (nullptr == dataRef.get()) {
return false;
}
const char *bytes = reinterpret_cast<const char *>(dataRef.get()->data());
size_t size = dataRef.get()->size();
Json::Reader reader;
if (!reader.parse(bytes, bytes+size, *jsonRoot)) {
return false;
}
return true;
}
} // namespace sk_tools

View File

@ -1,232 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef image_expectations_DEFINED
#define image_expectations_DEFINED
#include "SkBitmap.h"
#include "SkJSONCPP.h"
#include "SkOSFile.h"
namespace sk_tools {
/**
* The digest of an image (either an image we have generated locally, or an image expectation).
*
* Currently, this is always a uint64_t hash digest of an SkBitmap.
*/
class ImageDigest {
public:
/**
* Create an ImageDigest of a bitmap.
*
* Computes the hash of the bitmap lazily, since that is an expensive operation.
*
* @param bitmap image to get the digest of
*/
explicit ImageDigest(const SkBitmap &bitmap);
/**
* Create an ImageDigest using a hashType/hashValue pair.
*
* @param hashType the algorithm used to generate the hash; for now, only
* kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 is allowed.
* @param hashValue the value generated by the hash algorithm for a particular image.
*/
explicit ImageDigest(const SkString &hashType, uint64_t hashValue);
/**
* Returns true iff this and other ImageDigest represent identical images.
*/
bool equals(ImageDigest &other);
/**
* Returns the hash digest type as an SkString.
*
* For now, this always returns kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 .
*/
SkString getHashType();
/**
* Returns the hash digest value as a uint64_t.
*
* Since the hash is computed lazily, this may take some time, and it may modify
* some fields on this object.
*/
uint64_t getHashValue();
private:
const SkBitmap fBitmap;
uint64_t fHashValue;
bool fComputedHashValue;
};
/**
* Container that holds a reference to an SkBitmap and its ImageDigest.
*/
class BitmapAndDigest {
public:
explicit BitmapAndDigest(const SkBitmap &bitmap);
const SkBitmap *getBitmapPtr() const;
/**
* Returns a pointer to the ImageDigest.
*
* Since the hash is computed lazily within the ImageDigest object, we cannot mandate
* that it be held const.
*/
ImageDigest *getImageDigestPtr();
private:
const SkBitmap fBitmap;
ImageDigest fImageDigest;
};
/**
* Expected test result: expected image (if any), and whether we want to ignore failures on
* this test or not.
*
* This is just an ImageDigest (or lack thereof, if there is no expectation) and a boolean
* telling us whether to ignore failures.
*/
class Expectation {
public:
/**
* No expectation at all.
*/
explicit Expectation(bool ignoreFailure=kDefaultIgnoreFailure);
/**
* Expect an image, passed as hashType/hashValue.
*/
explicit Expectation(const SkString &hashType, uint64_t hashValue,
bool ignoreFailure=kDefaultIgnoreFailure);
/**
* Expect an image, passed as a bitmap.
*/
explicit Expectation(const SkBitmap& bitmap,
bool ignoreFailure=kDefaultIgnoreFailure);
/**
* Returns true iff we want to ignore failed expectations.
*/
bool ignoreFailure() const;
/**
* Returns true iff there are no allowed results.
*/
bool empty() const;
/**
* Returns true iff we are expecting a particular image, and imageDigest matches it.
*
* If empty() returns true, this will return false.
*
* If this expectation DOES contain an image, and imageDigest doesn't match it,
* this method will return false regardless of what ignoreFailure() would return.
* (The caller can check that separately.)
*/
bool matches(ImageDigest &imageDigest);
private:
static const bool kDefaultIgnoreFailure = false;
const bool fIsEmpty;
const bool fIgnoreFailure;
ImageDigest fImageDigest; // cannot be const, because it computes its hash lazily
};
/**
* Collects ImageDigests of actually rendered images, perhaps comparing to expectations.
*/
class ImageResultsAndExpectations {
public:
/**
* Adds expectations from a JSON file, returning true if successful.
*
* If the file exists but is empty, it succeeds, and there will be no expectations.
* If the file does not exist, this will fail.
*
* Reasoning:
* Generating expectations the first time can be a tricky chicken-and-egg
* proposition. "I need actual results to turn into expectations... but the only
* way to get actual results is to run the tool, and the tool won't run without
* expectations!"
* We could make the tool run even if there is no expectations file at all, but it's
* better for the tool to fail if the expectations file is not found--that will tell us
* quickly if files are not being copied around as they should be.
* Creating an empty file is an easy way to break the chicken-and-egg cycle and generate
* the first real expectations.
*/
bool readExpectationsFile(const char *jsonPath);
/**
* Adds this image to the summary of results.
*
* @param sourceName name of the source file that generated this result
* @param fileName relative path to the image output file on local disk
* @param digest description of the image's contents
* @param tileNumber if not nullptr, pointer to tile number
*/
void add(const char *sourceName, const char *fileName, ImageDigest &digest,
const int *tileNumber=nullptr);
/**
* Adds a key/value pair to the descriptions dict within the summary of results.
*
* @param key key within the descriptions dict
* @param value value to associate with that key
*/
void addDescription(const char *key, const char *value);
/**
* Adds the image base Google Storage URL to the summary of results.
*
* @param imageBaseGSUrl the image base Google Storage URL
*/
void setImageBaseGSUrl(const char *imageBaseGSUrl);
/**
* Returns the Expectation for this test.
*
* @param sourceName name of the source file that generated this result
* @param tileNumber if not nullptr, pointer to tile number
*
* TODO(stephana): To make this work for GMs, we will need to add parameters for
* config, and maybe renderMode/builder?
*/
Expectation getExpectation(const char *sourceName, const int *tileNumber=nullptr);
/**
* Writes the summary (as constructed so far) to a file.
*
* @param filename path to write the summary to
*/
void writeToFile(const char *filename) const;
private:
/**
* Read the file contents from filePtr and parse them into jsonRoot.
*
* It is up to the caller to close filePtr after this is done.
*
* Returns true if successful.
*/
static bool Parse(SkFILE* filePtr, Json::Value *jsonRoot);
Json::Value fActualResults;
Json::Value fDescriptions;
Json::Value fExpectedJsonRoot;
Json::Value fExpectedResults;
Json::Value fImageBaseGSUrl;
};
} // namespace sk_tools
#endif // image_expectations_DEFINED

View File

@ -1,511 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "LazyDecodeBitmap.h"
#include "CopyTilesRenderer.h"
#include "SkBitmap.h"
#include "SkDevice.h"
#include "SkCommandLineFlags.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkMath.h"
#include "SkOSFile.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
#include "SkStream.h"
#include "SkString.h"
#include "image_expectations.h"
#include "PictureRenderer.h"
#include "PictureRenderingFlags.h"
#include "picture_utils.h"
#include <stdlib.h>
// Flags used by this file, alphabetically:
DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture.");
DECLARE_bool(deferImageDecoding);
DEFINE_string(descriptions, "", "one or more key=value pairs to add to the descriptions section "
"of the JSON summary.");
DEFINE_string(imageBaseGSUrl, "", "The Google Storage image base URL the images are stored in.");
DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ "
"by more than this amount are considered errors, though all diffs are reported. "
"Requires --validate.");
DEFINE_string(mismatchPath, "", "Write images for tests that failed due to "
"pixel mismatches into this directory.");
#if GR_GPU_STATS
DEFINE_bool(gpuStats, false, "Only meaningful with gpu configurations. "
"Report some GPU call statistics.");
#endif
DEFINE_bool(mpd, false, "If true, use MultiPictureDraw for rendering.");
DEFINE_string(readJsonSummaryPath, "", "JSON file to read image expectations from.");
DECLARE_string(readPath);
DEFINE_bool(writeChecksumBasedFilenames, false,
"When writing out images, use checksum-based filenames.");
DEFINE_bool(writeEncodedImages, false, "Any time the skp contains an encoded image, write it to a "
"file rather than decoding it. Requires writePath to be set. Skips drawing the full "
"skp to a file. Not compatible with deferImageDecoding.");
DEFINE_string(writeJsonSummaryPath, "", "File to write a JSON summary of image results to.");
DEFINE_string2(writePath, w, "", "Directory to write the rendered images into.");
DEFINE_bool(writeWholeImage, false, "In tile mode, write the entire rendered image to a "
"file, instead of an image for each tile.");
DEFINE_bool(validate, false, "Verify that the rendered image contains the same pixels as "
"the picture rendered in simple mode. When used in conjunction with --bbh, results "
"are validated against the picture rendered in the same mode, but without the bbh.");
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Table for translating from format of data to a suffix.
*/
struct Format {
SkImageDecoder::Format fFormat;
const char* fSuffix;
};
static const Format gFormats[] = {
{ SkImageDecoder::kBMP_Format, ".bmp" },
{ SkImageDecoder::kGIF_Format, ".gif" },
{ SkImageDecoder::kICO_Format, ".ico" },
{ SkImageDecoder::kJPEG_Format, ".jpg" },
{ SkImageDecoder::kPNG_Format, ".png" },
{ SkImageDecoder::kWBMP_Format, ".wbmp" },
{ SkImageDecoder::kWEBP_Format, ".webp" },
{ SkImageDecoder::kUnknown_Format, "" },
};
/**
* Get an appropriate suffix for an image format.
*/
static const char* get_suffix_from_format(SkImageDecoder::Format format) {
for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) {
if (gFormats[i].fFormat == format) {
return gFormats[i].fSuffix;
}
}
return "";
}
/**
* Base name for an image file created from the encoded data in an skp.
*/
static SkString gInputFileName;
/**
* Number to be appended to the image file name so that it is unique.
*/
static uint32_t gImageNo;
/**
* Set up the name for writing encoded data to a file.
* Sets gInputFileName to name, minus any extension ".*"
* Sets gImageNo to 0, so images from file "X.skp" will
* look like "X_<gImageNo>.<suffix>", beginning with 0
* for each new skp.
*/
static void reset_image_file_base_name(const SkString& name) {
gImageNo = 0;
// Remove ".skp"
const char* cName = name.c_str();
const char* dot = strrchr(cName, '.');
if (dot != nullptr) {
gInputFileName.set(cName, dot - cName);
} else {
gInputFileName.set(name);
}
}
/**
* Write the raw encoded bitmap data to a file.
*/
static bool write_image_to_file(const void* buffer, size_t size, SkBitmap* bitmap) {
SkASSERT(!FLAGS_writePath.isEmpty());
SkMemoryStream memStream(buffer, size);
SkString outPath;
SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&memStream);
SkString name = SkStringPrintf("%s_%d%s", gInputFileName.c_str(), gImageNo++,
get_suffix_from_format(format));
SkString dir(FLAGS_writePath[0]);
outPath = SkOSPath::Join(dir.c_str(), name.c_str());
SkFILEWStream fileStream(outPath.c_str());
if (!(fileStream.isValid() && fileStream.write(buffer, size))) {
SkDebugf("Failed to write encoded data to \"%s\"\n", outPath.c_str());
}
// Put in a dummy bitmap.
return SkImageDecoder::DecodeStream(&memStream, bitmap, kUnknown_SkColorType,
SkImageDecoder::kDecodeBounds_Mode);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Called only by render_picture().
*/
static bool render_picture_internal(const SkString& inputPath, const SkString* writePath,
const SkString* mismatchPath,
sk_tools::PictureRenderer& renderer,
SkBitmap** out) {
SkString inputFilename = SkOSPath::Basename(inputPath.c_str());
SkString writePathString;
if (writePath && writePath->size() > 0 && !FLAGS_writeEncodedImages) {
writePathString.set(*writePath);
}
SkString mismatchPathString;
if (mismatchPath && mismatchPath->size() > 0) {
mismatchPathString.set(*mismatchPath);
}
SkFILEStream inputStream;
inputStream.setPath(inputPath.c_str());
if (!inputStream.isValid()) {
SkDebugf("Could not open file %s\n", inputPath.c_str());
return false;
}
SkPicture::InstallPixelRefProc proc;
if (FLAGS_deferImageDecoding) {
proc = &sk_tools::LazyDecodeBitmap;
} else if (FLAGS_writeEncodedImages) {
SkASSERT(!FLAGS_writePath.isEmpty());
reset_image_file_base_name(inputFilename);
proc = &write_image_to_file;
} else {
proc = &SkImageDecoder::DecodeMemory;
}
SkDebugf("deserializing... %s\n", inputPath.c_str());
SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, proc));
if (nullptr == picture) {
SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
return false;
}
while (FLAGS_bench_record) {
SkPictureRecorder recorder;
picture->playback(recorder.beginRecording(picture->cullRect().width(),
picture->cullRect().height(),
nullptr, 0));
SkAutoTUnref<SkPicture> other(recorder.endRecording());
}
SkDebugf("drawing... [%f %f %f %f] %s\n",
picture->cullRect().fLeft, picture->cullRect().fTop,
picture->cullRect().fRight, picture->cullRect().fBottom,
inputPath.c_str());
renderer.init(picture, &writePathString, &mismatchPathString, &inputFilename,
FLAGS_writeChecksumBasedFilenames, FLAGS_mpd);
renderer.setup();
renderer.enableWrites();
bool success = renderer.render(out);
if (!success) {
SkDebugf("Failed to render %s\n", inputFilename.c_str());
}
renderer.end();
return success;
}
static inline int getByte(uint32_t value, int index) {
SkASSERT(0 <= index && index < 4);
return (value >> (index * 8)) & 0xFF;
}
static int MaxByteDiff(uint32_t v1, uint32_t v2) {
return SkMax32(SkMax32(SkTAbs(getByte(v1, 0) - getByte(v2, 0)), SkTAbs(getByte(v1, 1) - getByte(v2, 1))),
SkMax32(SkTAbs(getByte(v1, 2) - getByte(v2, 2)), SkTAbs(getByte(v1, 3) - getByte(v2, 3))));
}
class AutoRestoreBbhType {
public:
AutoRestoreBbhType() {
fRenderer = nullptr;
fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
}
void set(sk_tools::PictureRenderer* renderer,
sk_tools::PictureRenderer::BBoxHierarchyType bbhType) {
fRenderer = renderer;
fSavedBbhType = renderer->getBBoxHierarchyType();
renderer->setBBoxHierarchyType(bbhType);
}
~AutoRestoreBbhType() {
if (fRenderer) {
fRenderer->setBBoxHierarchyType(fSavedBbhType);
}
}
private:
sk_tools::PictureRenderer* fRenderer;
sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType;
};
/**
* Render the SKP file(s) within inputPath.
*
* @param inputPath path to an individual SKP file, or a directory of SKP files
* @param writePath if not nullptr, write all image(s) generated into this directory
* @param mismatchPath if not nullptr, write any image(s) not matching expectations into this directory
* @param renderer PictureRenderer to use to render the SKPs
* @param jsonSummaryPtr if not nullptr, add the image(s) generated to this summary
*/
static bool render_picture(const SkString& inputPath, const SkString* writePath,
const SkString* mismatchPath, sk_tools::PictureRenderer& renderer,
sk_tools::ImageResultsAndExpectations *jsonSummaryPtr) {
int diffs[256] = {0};
SkBitmap* bitmap = nullptr;
renderer.setJsonSummaryPtr(jsonSummaryPtr);
bool success = render_picture_internal(inputPath,
FLAGS_writeWholeImage ? nullptr : writePath,
FLAGS_writeWholeImage ? nullptr : mismatchPath,
renderer,
FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : nullptr);
if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == nullptr)) {
SkDebugf("Failed to draw the picture.\n");
delete bitmap;
return false;
}
if (FLAGS_validate) {
SkBitmap* referenceBitmap = nullptr;
sk_tools::PictureRenderer* referenceRenderer;
// If the renderer uses a BBoxHierarchy, then the reference renderer
// will be the same renderer, without the bbh.
AutoRestoreBbhType arbbh;
if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType !=
renderer.getBBoxHierarchyType()) {
referenceRenderer = &renderer;
referenceRenderer->ref(); // to match auto unref below
arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
} else {
#if SK_SUPPORT_GPU
referenceRenderer = new sk_tools::SimplePictureRenderer(renderer.getGrContextOptions());
#else
referenceRenderer = new sk_tools::SimplePictureRenderer;
#endif
}
SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer);
success = render_picture_internal(inputPath, nullptr, nullptr, *referenceRenderer,
&referenceBitmap);
if (!success || nullptr == referenceBitmap || nullptr == referenceBitmap->getPixels()) {
SkDebugf("Failed to draw the reference picture.\n");
delete bitmap;
delete referenceBitmap;
return false;
}
if (success && (bitmap->width() != referenceBitmap->width())) {
SkDebugf("Expected image width: %i, actual image width %i.\n",
referenceBitmap->width(), bitmap->width());
delete bitmap;
delete referenceBitmap;
return false;
}
if (success && (bitmap->height() != referenceBitmap->height())) {
SkDebugf("Expected image height: %i, actual image height %i",
referenceBitmap->height(), bitmap->height());
delete bitmap;
delete referenceBitmap;
return false;
}
for (int y = 0; success && y < bitmap->height(); y++) {
for (int x = 0; success && x < bitmap->width(); x++) {
int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
*bitmap->getAddr32(x, y));
SkASSERT(diff >= 0 && diff <= 255);
diffs[diff]++;
if (diff > FLAGS_maxComponentDiff) {
SkDebugf("Expected pixel at (%i %i) exceedds maximum "
"component diff of %i: 0x%x, actual 0x%x\n",
x, y, FLAGS_maxComponentDiff,
*referenceBitmap->getAddr32(x, y),
*bitmap->getAddr32(x, y));
delete bitmap;
delete referenceBitmap;
return false;
}
}
}
delete referenceBitmap;
for (int i = 1; i <= 255; ++i) {
if(diffs[i] > 0) {
SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
}
}
}
if (FLAGS_writeWholeImage) {
sk_tools::force_all_opaque(*bitmap);
SkString inputFilename = SkOSPath::Basename(inputPath.c_str());
SkString outputFilename(inputFilename);
sk_tools::replace_char(&outputFilename, '.', '_');
outputFilename.append(".png");
if (jsonSummaryPtr) {
sk_tools::ImageDigest imageDigest(*bitmap);
jsonSummaryPtr->add(inputFilename.c_str(), outputFilename.c_str(), imageDigest);
if ((mismatchPath) && !mismatchPath->isEmpty() &&
!jsonSummaryPtr->getExpectation(inputFilename.c_str()).matches(imageDigest)) {
success &= sk_tools::write_bitmap_to_disk(*bitmap, *mismatchPath, nullptr,
outputFilename);
}
}
if ((writePath) && !writePath->isEmpty()) {
success &= sk_tools::write_bitmap_to_disk(*bitmap, *writePath, nullptr, outputFilename);
}
}
delete bitmap;
return success;
}
static int process_input(const char* input, const SkString* writePath,
const SkString* mismatchPath, sk_tools::PictureRenderer& renderer,
sk_tools::ImageResultsAndExpectations *jsonSummaryPtr) {
SkOSFile::Iter iter(input, "skp");
SkString inputFilename;
int failures = 0;
SkDebugf("process_input, %s\n", input);
if (iter.next(&inputFilename)) {
do {
SkString inputPath = SkOSPath::Join(input, inputFilename.c_str());
if (!render_picture(inputPath, writePath, mismatchPath, renderer, jsonSummaryPtr)) {
++failures;
}
} while(iter.next(&inputFilename));
} else if (SkStrEndsWith(input, ".skp")) {
SkString inputPath(input);
if (!render_picture(inputPath, writePath, mismatchPath, renderer, jsonSummaryPtr)) {
++failures;
}
} else {
SkString warning;
warning.printf("Warning: skipping %s\n", input);
SkDebugf("%s", warning.c_str());
}
return failures;
}
int tool_main(int argc, char** argv);
int tool_main(int argc, char** argv) {
SkCommandLineFlags::SetUsage("Render .skp files.");
SkCommandLineFlags::Parse(argc, argv);
if (FLAGS_readPath.isEmpty()) {
SkDebugf(".skp files or directories are required.\n");
exit(-1);
}
if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) {
SkDebugf("--maxComponentDiff must be between 0 and 256\n");
exit(-1);
}
if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) {
SkDebugf("--maxComponentDiff requires --validate\n");
exit(-1);
}
if (FLAGS_writeEncodedImages) {
if (FLAGS_writePath.isEmpty()) {
SkDebugf("--writeEncodedImages requires --writePath\n");
exit(-1);
}
if (FLAGS_deferImageDecoding) {
SkDebugf("--writeEncodedImages is not compatible with --deferImageDecoding\n");
exit(-1);
}
}
SkString errorString;
SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
kRender_PictureTool));
if (errorString.size() > 0) {
SkDebugf("%s\n", errorString.c_str());
}
if (renderer.get() == nullptr) {
exit(-1);
}
SkAutoGraphics ag;
SkString writePath;
if (FLAGS_writePath.count() == 1) {
writePath.set(FLAGS_writePath[0]);
}
SkString mismatchPath;
if (FLAGS_mismatchPath.count() == 1) {
mismatchPath.set(FLAGS_mismatchPath[0]);
}
sk_tools::ImageResultsAndExpectations jsonSummary;
sk_tools::ImageResultsAndExpectations* jsonSummaryPtr = nullptr;
if (FLAGS_writeJsonSummaryPath.count() == 1) {
jsonSummaryPtr = &jsonSummary;
if (FLAGS_readJsonSummaryPath.count() == 1) {
SkASSERT(jsonSummary.readExpectationsFile(FLAGS_readJsonSummaryPath[0]));
}
}
int failures = 0;
for (int i = 0; i < FLAGS_readPath.count(); i ++) {
failures += process_input(FLAGS_readPath[i], &writePath, &mismatchPath, *renderer.get(),
jsonSummaryPtr);
}
if (failures != 0) {
SkDebugf("Failed to render %i pictures.\n", failures);
return 1;
}
#if GR_CACHE_STATS && SK_SUPPORT_GPU
if (renderer->isUsingGpuDevice()) {
GrContext* ctx = renderer->getGrContext();
ctx->printCacheStats();
}
#endif
#if GR_GPU_STATS && SK_SUPPORT_GPU
if (FLAGS_gpuStats && renderer->isUsingGpuDevice()) {
renderer->getGrContext()->printGpuStats();
}
#endif
if (FLAGS_writeJsonSummaryPath.count() == 1) {
// If there were any descriptions on the command line, insert them now.
for (int i=0; i<FLAGS_descriptions.count(); i++) {
SkTArray<SkString> tokens;
SkStrSplit(FLAGS_descriptions[i], "=", &tokens);
SkASSERT(tokens.count() == 2);
jsonSummary.addDescription(tokens[0].c_str(), tokens[1].c_str());
}
if (FLAGS_imageBaseGSUrl.count() == 1) {
jsonSummary.setImageBaseGSUrl(FLAGS_imageBaseGSUrl[0]);
}
jsonSummary.writeToFile(FLAGS_writeJsonSummaryPath[0]);
}
return 0;
}
#if !defined SK_BUILD_FOR_IOS
int main(int argc, char * const argv[]) {
return tool_main(argc, (char**) argv);
}
#endif