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:
parent
ef33b1e739
commit
0c768a2f33
@ -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/**/*",
|
||||
|
141
gyp/tools.gyp
141
gyp/tools.gyp
@ -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',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -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
|
@ -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(©, &subset);
|
||||
} else {
|
||||
#endif
|
||||
dst.copyTo(©);
|
||||
#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");
|
||||
}
|
||||
}
|
@ -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
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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
|
@ -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.
|
||||
|
@ -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];
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
@ -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
|
@ -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],
|
||||
}
|
@ -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')
|
@ -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
|
@ -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/
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user