Add profiling to debugger
https://codereview.appspot.com/6817114/ git-svn-id: http://skia.googlecode.com/svn/trunk@6430 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
5dd85a45bc
commit
2bde91dcb6
@ -9,6 +9,12 @@
|
||||
#include "SkGraphics.h"
|
||||
#include "SkImageDecoder.h"
|
||||
#include <QListWidgetItem>
|
||||
#include "PictureBenchmark.h"
|
||||
#include "PictureRenderer.h"
|
||||
#include "SkBenchLogger.h"
|
||||
#include "SkPictureRecord.h"
|
||||
#include "SkPicturePlayback.h"
|
||||
#include "BenchTimer.h"
|
||||
|
||||
SkDebuggerGUI::SkDebuggerGUI(QWidget *parent) :
|
||||
QMainWindow(parent)
|
||||
@ -119,7 +125,167 @@ void SkDebuggerGUI::showDeletes() {
|
||||
}
|
||||
}
|
||||
|
||||
// The timed picture playback uses the SkPicturePlayback's profiling stubs
|
||||
// to time individual commands. The offsets are needed to map SkPicture
|
||||
// offsets to individual commands.
|
||||
class SkTimedPicturePlayback : public SkPicturePlayback {
|
||||
public:
|
||||
SkTimedPicturePlayback(SkStream* stream, const SkPictInfo& info, bool* isValid,
|
||||
SkSerializationHelpers::DecodeBitmap decoder,
|
||||
const SkTDArray<size_t>& offsets)
|
||||
: INHERITED(stream, info, isValid, decoder)
|
||||
, fTot(0.0)
|
||||
, fCurCommand(0)
|
||||
, fOffsets(offsets) {
|
||||
fTimes.setCount(fOffsets.count());
|
||||
for (int i = 0; i < fOffsets.count(); ++i) {
|
||||
fTimes[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int count() const { return fTimes.count(); }
|
||||
|
||||
double time(int index) const { return fTimes[index] / fTot; }
|
||||
|
||||
protected:
|
||||
BenchTimer fTimer;
|
||||
SkTDArray<size_t> fOffsets; // offset in the SkPicture for each command
|
||||
SkTDArray<double> fTimes; // sum of time consumed for each command
|
||||
double fTot; // total of all times in 'fTimes'
|
||||
size_t fCurOffset;
|
||||
int fCurCommand; // the current command being executed/timed
|
||||
|
||||
virtual void preDraw(size_t offset, int type) {
|
||||
// This search isn't as bad as it seems. In normal playback mode, the
|
||||
// base class steps through the commands in order and can only skip ahead
|
||||
// a bit on a clip. This class is only used during profiling so we
|
||||
// don't have to worry about forward/backward scrubbing through commands.
|
||||
for (int i = 0; offset != fOffsets[fCurCommand]; ++i) {
|
||||
fCurCommand = (fCurCommand+1) % fOffsets.count();
|
||||
SkASSERT(i <= fOffsets.count()); // should always find the offset in the list
|
||||
}
|
||||
|
||||
fCurOffset = offset;
|
||||
|
||||
fTimer.start();
|
||||
}
|
||||
|
||||
virtual void postDraw(size_t offset) {
|
||||
fTimer.end();
|
||||
|
||||
SkASSERT(offset == fCurOffset);
|
||||
|
||||
fTimes[fCurCommand] += fTimer.fWall;
|
||||
fTot += fTimer.fWall;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef SkPicturePlayback INHERITED;
|
||||
};
|
||||
|
||||
// Wrap SkPicture to allow installation of an SkTimedPicturePlayback object
|
||||
class SkTimedPicture : public SkPicture {
|
||||
public:
|
||||
explicit SkTimedPicture(SkStream* stream,
|
||||
bool* success,
|
||||
SkSerializationHelpers::DecodeBitmap decoder,
|
||||
const SkTDArray<size_t>& offsets) {
|
||||
if (success) {
|
||||
*success = false;
|
||||
}
|
||||
fRecord = NULL;
|
||||
fPlayback = NULL;
|
||||
fWidth = fHeight = 0;
|
||||
|
||||
SkPictInfo info;
|
||||
|
||||
if (!stream->read(&info, sizeof(info))) {
|
||||
return;
|
||||
}
|
||||
if (SkPicture::PICTURE_VERSION != info.fVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stream->readBool()) {
|
||||
bool isValid = false;
|
||||
fPlayback = SkNEW_ARGS(SkTimedPicturePlayback,
|
||||
(stream, info, &isValid, decoder, offsets));
|
||||
if (!isValid) {
|
||||
SkDELETE(fPlayback);
|
||||
fPlayback = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// do this at the end, so that they will be zero if we hit an error.
|
||||
fWidth = info.fWidth;
|
||||
fHeight = info.fHeight;
|
||||
if (success) {
|
||||
*success = true;
|
||||
}
|
||||
}
|
||||
|
||||
int count() const { return ((SkTimedPicturePlayback*) fPlayback)->count(); }
|
||||
|
||||
// return the fraction of the total time this command consumed
|
||||
double time(int index) const { return ((SkTimedPicturePlayback*) fPlayback)->time(index); }
|
||||
|
||||
private:
|
||||
typedef SkPicture INHERITED;
|
||||
};
|
||||
|
||||
void SkDebuggerGUI::actionProfile() {
|
||||
// In order to profile we pass the command offsets (that were read-in
|
||||
// in loadPicture by the SkOffsetPicture) to an SkTimedPlaybackPicture.
|
||||
// The SkTimedPlaybackPicture in turn passes the offsets to an
|
||||
// SkTimedPicturePlayback object which uses them to track the performance
|
||||
// of individual commands.
|
||||
if (fFileName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkFILEStream inputStream;
|
||||
|
||||
inputStream.setPath(fFileName.c_str());
|
||||
if (!inputStream.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
SkTimedPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream, fOffsets);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
sk_tools::PictureBenchmark benchmark;
|
||||
|
||||
sk_tools::TiledPictureRenderer* renderer = NULL;
|
||||
|
||||
renderer = SkNEW(sk_tools::TiledPictureRenderer);
|
||||
renderer->setTileWidth(256);
|
||||
renderer->setTileHeight(256);
|
||||
|
||||
|
||||
benchmark.setRepeats(2);
|
||||
benchmark.setRenderer(renderer);
|
||||
benchmark.setTimersToShow(true, false, true, false, false);
|
||||
|
||||
SkBenchLogger logger;
|
||||
|
||||
benchmark.setLogger(&logger);
|
||||
|
||||
benchmark.run(&picture);
|
||||
|
||||
SkASSERT(picture.count() == fListWidget.count());
|
||||
|
||||
// extract the individual command times from the SkTimedPlaybackPicture
|
||||
for (int i = 0; i < picture.count(); ++i) {
|
||||
double temp = picture.time(i);
|
||||
|
||||
QListWidgetItem* item = fListWidget.item(i);
|
||||
|
||||
item->setData(Qt::UserRole + 4, 100.0*temp);
|
||||
}
|
||||
}
|
||||
|
||||
void SkDebuggerGUI::actionCancel() {
|
||||
@ -585,20 +751,97 @@ void SkDebuggerGUI::setupDirectoryWidget() {
|
||||
}
|
||||
}
|
||||
|
||||
// SkOffsetPicturePlayback records the offset of each command in the picture.
|
||||
// These are needed by the profiling system.
|
||||
class SkOffsetPicturePlayback : public SkPicturePlayback {
|
||||
public:
|
||||
SkOffsetPicturePlayback(SkStream* stream, const SkPictInfo& info, bool* isValid,
|
||||
SkSerializationHelpers::DecodeBitmap decoder)
|
||||
: INHERITED(stream, info, isValid, decoder) {
|
||||
}
|
||||
|
||||
const SkTDArray<size_t>& offsets() const { return fOffsets; }
|
||||
|
||||
protected:
|
||||
SkTDArray<size_t> fOffsets;
|
||||
|
||||
virtual void preDraw(size_t offset, int type) {
|
||||
*fOffsets.append() = offset;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef SkPicturePlayback INHERITED;
|
||||
};
|
||||
|
||||
// Picture to wrap an SkOffsetPicturePlayback.
|
||||
class SkOffsetPicture : public SkPicture {
|
||||
public:
|
||||
SkOffsetPicture(SkStream* stream,
|
||||
bool* success,
|
||||
SkSerializationHelpers::DecodeBitmap decoder) {
|
||||
if (success) {
|
||||
*success = false;
|
||||
}
|
||||
fRecord = NULL;
|
||||
fPlayback = NULL;
|
||||
fWidth = fHeight = 0;
|
||||
|
||||
SkPictInfo info;
|
||||
|
||||
if (!stream->read(&info, sizeof(info))) {
|
||||
return;
|
||||
}
|
||||
if (PICTURE_VERSION != info.fVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stream->readBool()) {
|
||||
bool isValid = false;
|
||||
fPlayback = SkNEW_ARGS(SkOffsetPicturePlayback, (stream, info, &isValid, decoder));
|
||||
if (!isValid) {
|
||||
SkDELETE(fPlayback);
|
||||
fPlayback = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// do this at the end, so that they will be zero if we hit an error.
|
||||
fWidth = info.fWidth;
|
||||
fHeight = info.fHeight;
|
||||
if (success) {
|
||||
*success = true;
|
||||
}
|
||||
}
|
||||
|
||||
const SkTDArray<size_t>& offsets() const {
|
||||
return ((SkOffsetPicturePlayback*) fPlayback)->offsets();
|
||||
}
|
||||
|
||||
private:
|
||||
typedef SkPicture INHERITED;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void SkDebuggerGUI::loadPicture(const SkString& fileName) {
|
||||
fFileName = fileName;
|
||||
fLoading = true;
|
||||
SkStream* stream = SkNEW_ARGS(SkFILEStream, (fileName.c_str()));
|
||||
SkPicture* picture = SkNEW_ARGS(SkPicture, (stream, NULL, &SkImageDecoder::DecodeStream));
|
||||
SkOffsetPicture* picture = SkNEW_ARGS(SkOffsetPicture, (stream, NULL, &SkImageDecoder::DecodeStream));
|
||||
|
||||
fCanvasWidget.resetWidgetTransform();
|
||||
fDebugger.loadPicture(picture);
|
||||
|
||||
fOffsets = picture->offsets();
|
||||
|
||||
SkSafeUnref(stream);
|
||||
SkSafeUnref(picture);
|
||||
|
||||
// Will this automatically clear out due to nature of refcnt?
|
||||
SkTDArray<SkString*>* commands = fDebugger.getDrawCommands();
|
||||
|
||||
SkASSERT(commands->count() == fOffsets.count());
|
||||
|
||||
/* fDebugCanvas is reinitialized every load picture. Need it to retain value
|
||||
* of the visibility filter.
|
||||
* TODO(chudy): This should be deprecated since fDebugger is not
|
||||
|
@ -251,6 +251,7 @@ private:
|
||||
|
||||
QString fPath;
|
||||
SkString fFileName;
|
||||
SkTDArray<size_t> fOffsets; // the offset of each command in the SkPicture
|
||||
bool fDirectoryWidgetActive;
|
||||
|
||||
QMenuBar fMenuBar;
|
||||
|
@ -81,6 +81,8 @@
|
||||
'../debugger', # To pull SkDebugger.h
|
||||
'../debugger/QT', # For all the QT UI Goodies
|
||||
'../src/gpu', # To pull gl/GrGLUtil.h
|
||||
'../bench',
|
||||
'../tools',
|
||||
'<@(qt_includes)',
|
||||
],
|
||||
'sources': [
|
||||
@ -107,6 +109,14 @@
|
||||
'../debugger/QT/SkRasterWidget.h',
|
||||
'../debugger/QT/SkRasterWidget.cpp',
|
||||
|
||||
'../tools/PictureBenchmark.h',
|
||||
'../tools/PictureBenchmark.cpp',
|
||||
|
||||
'../bench/SkBenchLogger.h',
|
||||
'../bench/SkBenchLogger.cpp',
|
||||
'../bench/TimerData.h',
|
||||
'../bench/TimerData.cpp',
|
||||
|
||||
# To update this file edit SkIcons.qrc and rerun rcc to generate cpp
|
||||
'../debugger/QT/qrc_SkIcons.cpp',
|
||||
|
||||
@ -122,6 +132,8 @@
|
||||
'skia_base_libs.gyp:skia_base_libs',
|
||||
'images.gyp:images',
|
||||
'effects.gyp:effects',
|
||||
'bench.gyp:bench_timer',
|
||||
'tools.gyp:picture_renderer',
|
||||
'debugger_mocs',
|
||||
],
|
||||
'link_settings': {
|
||||
|
Loading…
Reference in New Issue
Block a user