Cleanup timing state machine
BUG=skia: Review URL: https://codereview.chromium.org/1386933002
This commit is contained in:
parent
1562855b19
commit
cb54e8ed45
@ -111,6 +111,19 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool isVisual() { return false; }
|
virtual bool isVisual() { return false; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VisualBench frequently resets the canvas. As a result we need to bulk call all of the hooks
|
||||||
|
*/
|
||||||
|
void preTimingHooks(SkCanvas* canvas) {
|
||||||
|
this->perCanvasPreDraw(canvas);
|
||||||
|
this->preDraw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void postTimingHooks(SkCanvas* canvas) {
|
||||||
|
this->postDraw(canvas);
|
||||||
|
this->perCanvasPostDraw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void setupPaint(SkPaint* paint);
|
virtual void setupPaint(SkPaint* paint);
|
||||||
|
|
||||||
|
@ -18,53 +18,63 @@ TimingStateMachine::TimingStateMachine()
|
|||||||
: fCurrentFrame(0)
|
: fCurrentFrame(0)
|
||||||
, fLoops(1)
|
, fLoops(1)
|
||||||
, fLastMeasurement(0.)
|
, fLastMeasurement(0.)
|
||||||
, fState(kPreWarmLoopsPerCanvasPreDraw_State) {
|
, fState(kPreWarm_State)
|
||||||
|
, fInnerState(kTuning_InnerState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TimingStateMachine::ParentEvents TimingStateMachine::nextFrame(SkCanvas* canvas,
|
TimingStateMachine::ParentEvents TimingStateMachine::nextFrame(bool preWarmBetweenSamples) {
|
||||||
Benchmark* benchmark) {
|
ParentEvents parentEvent = kTiming_ParentEvents;
|
||||||
switch (fState) {
|
switch (fState) {
|
||||||
case kPreWarmLoopsPerCanvasPreDraw_State:
|
case kPreWarm_State: {
|
||||||
return this->perCanvasPreDraw(canvas, benchmark, kPreWarmLoops_State);
|
|
||||||
case kPreWarmLoops_State:
|
|
||||||
return this->preWarm(kTuneLoops_State);
|
|
||||||
case kTuneLoops_State:
|
|
||||||
return this->tuneLoops();
|
|
||||||
case kPreWarmTimingPerCanvasPreDraw_State:
|
|
||||||
return this->perCanvasPreDraw(canvas, benchmark, kPreWarmTiming_State);
|
|
||||||
case kPreWarmTiming_State:
|
|
||||||
return this->preWarm(kTiming_State);
|
|
||||||
case kTiming_State:
|
|
||||||
return this->timing(canvas, benchmark);
|
|
||||||
}
|
|
||||||
SkFAIL("Incomplete switch\n");
|
|
||||||
return kTiming_ParentEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void TimingStateMachine::nextState(State nextState) {
|
|
||||||
fState = nextState;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimingStateMachine::ParentEvents TimingStateMachine::perCanvasPreDraw(SkCanvas* canvas,
|
|
||||||
Benchmark* benchmark,
|
|
||||||
State nextState) {
|
|
||||||
benchmark->perCanvasPreDraw(canvas);
|
|
||||||
benchmark->preDraw(canvas);
|
|
||||||
fCurrentFrame = 0;
|
|
||||||
this->nextState(nextState);
|
|
||||||
return kTiming_ParentEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimingStateMachine::ParentEvents TimingStateMachine::preWarm(State nextState) {
|
|
||||||
if (fCurrentFrame >= FLAGS_gpuFrameLag) {
|
if (fCurrentFrame >= FLAGS_gpuFrameLag) {
|
||||||
// we currently time across all frames to make sure we capture all GPU work
|
|
||||||
this->nextState(nextState);
|
|
||||||
fCurrentFrame = 0;
|
fCurrentFrame = 0;
|
||||||
fTimer.start();
|
fTimer.start();
|
||||||
|
fState = kTiming_State;
|
||||||
} else {
|
} else {
|
||||||
fCurrentFrame++;
|
fCurrentFrame++;
|
||||||
}
|
}
|
||||||
return kTiming_ParentEvents;
|
break;
|
||||||
|
}
|
||||||
|
case kTiming_State: {
|
||||||
|
switch (fInnerState) {
|
||||||
|
case kTuning_InnerState: {
|
||||||
|
if (1 << 30 == fLoops) {
|
||||||
|
// We're about to wrap. Something's wrong with the bench.
|
||||||
|
SkDebugf("InnerLoops wrapped\n");
|
||||||
|
fLoops = 1;
|
||||||
|
} else {
|
||||||
|
double elapsedMs = this->elapsed();
|
||||||
|
if (elapsedMs < FLAGS_loopMs) {
|
||||||
|
fLoops *= 2;
|
||||||
|
} else {
|
||||||
|
fInnerState = kTiming_InnerState;
|
||||||
|
fState = kPreWarm_State;
|
||||||
|
}
|
||||||
|
this->resetTimingState();
|
||||||
|
parentEvent = kReset_ParentEvents;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kTiming_InnerState: {
|
||||||
|
if (fCurrentFrame >= FLAGS_frames) {
|
||||||
|
this->recordMeasurement();
|
||||||
|
this->resetTimingState();
|
||||||
|
parentEvent = kTimingFinished_ParentEvents;
|
||||||
|
if (preWarmBetweenSamples) {
|
||||||
|
fState = kPreWarm_State;
|
||||||
|
} else {
|
||||||
|
fTimer.start(); // start timing again, don't change state
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fCurrentFrame++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return parentEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double TimingStateMachine::elapsed() {
|
inline double TimingStateMachine::elapsed() {
|
||||||
@ -77,52 +87,14 @@ void TimingStateMachine::resetTimingState() {
|
|||||||
fTimer = WallTimer();
|
fTimer = WallTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline TimingStateMachine::ParentEvents TimingStateMachine::tuneLoops() {
|
|
||||||
if (1 << 30 == fLoops) {
|
|
||||||
// We're about to wrap. Something's wrong with the bench.
|
|
||||||
SkDebugf("InnerLoops wrapped\n");
|
|
||||||
fLoops = 1;
|
|
||||||
return kTiming_ParentEvents;
|
|
||||||
} else {
|
|
||||||
double elapsedMs = this->elapsed();
|
|
||||||
if (elapsedMs > FLAGS_loopMs) {
|
|
||||||
this->nextState(kPreWarmTimingPerCanvasPreDraw_State);
|
|
||||||
} else {
|
|
||||||
fLoops *= 2;
|
|
||||||
this->nextState(kPreWarmLoops_State);
|
|
||||||
}
|
|
||||||
this->resetTimingState();
|
|
||||||
return kReset_ParentEvents;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimingStateMachine::recordMeasurement() {
|
void TimingStateMachine::recordMeasurement() {
|
||||||
fLastMeasurement = this->elapsed() / (FLAGS_frames * fLoops);
|
fLastMeasurement = this->elapsed() / (FLAGS_frames * fLoops);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline TimingStateMachine::ParentEvents TimingStateMachine::timing(SkCanvas* canvas,
|
|
||||||
Benchmark* benchmark) {
|
|
||||||
if (fCurrentFrame >= FLAGS_frames) {
|
|
||||||
this->recordMeasurement();
|
|
||||||
this->resetTimingState();
|
|
||||||
return kTimingFinished_ParentEvents;
|
|
||||||
} else {
|
|
||||||
fCurrentFrame++;
|
|
||||||
return kTiming_ParentEvents;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimingStateMachine::nextBenchmark(SkCanvas* canvas, Benchmark* benchmark) {
|
void TimingStateMachine::nextBenchmark(SkCanvas* canvas, Benchmark* benchmark) {
|
||||||
benchmark->postDraw(canvas);
|
benchmark->postDraw(canvas);
|
||||||
benchmark->perCanvasPostDraw(canvas);
|
benchmark->perCanvasPostDraw(canvas);
|
||||||
fLoops = 1;
|
fLoops = 1;
|
||||||
this->nextState(kPreWarmLoopsPerCanvasPreDraw_State);
|
fInnerState = kTuning_InnerState;
|
||||||
}
|
fState = kPreWarm_State;
|
||||||
|
|
||||||
void TimingStateMachine::nextSampleWithPrewarm() {
|
|
||||||
this->nextState(kPreWarmTimingPerCanvasPreDraw_State);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimingStateMachine::nextSample() {
|
|
||||||
fTimer.start();
|
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,7 @@ public:
|
|||||||
// reset
|
// reset
|
||||||
};
|
};
|
||||||
|
|
||||||
ParentEvents nextFrame(SkCanvas* canvas, Benchmark* benchmark);
|
ParentEvents nextFrame(bool preWarmBetweenSamples);
|
||||||
|
|
||||||
/*
|
|
||||||
* Before taking another sample, the owner can choose to prewarm or not
|
|
||||||
*/
|
|
||||||
void nextSampleWithPrewarm();
|
|
||||||
void nextSample();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The caller should call this when they are ready to move to the next benchmark. The caller
|
* The caller should call this when they are ready to move to the next benchmark. The caller
|
||||||
@ -50,7 +44,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void nextBenchmark(SkCanvas*, Benchmark*);
|
void nextBenchmark(SkCanvas*, Benchmark*);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When TimingStateMachine returns kTimingFinished_ParentEvents, then the owner can call
|
* When TimingStateMachine returns kTimingFinished_ParentEvents, then the owner can call
|
||||||
* lastMeasurement() to get the time
|
* lastMeasurement() to get the time
|
||||||
@ -60,43 +53,17 @@ public:
|
|||||||
int loops() const { return fLoops; }
|
int loops() const { return fLoops; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
|
||||||
* The heart of the timing state machine is an event driven timing loop.
|
|
||||||
* kPreWarmLoopsPerCanvasPreDraw_State: Before we begin timing, Benchmarks have a hook to
|
|
||||||
* access the canvas. Then we prewarm before the autotune
|
|
||||||
* loops step.
|
|
||||||
* kPreWarmLoops_State: We prewarm the gpu before auto tuning to enter a steady
|
|
||||||
* work state
|
|
||||||
* kTuneLoops_State: Then we tune the loops of the benchmark to ensure we
|
|
||||||
* are doing a measurable amount of work
|
|
||||||
* kPreWarmTimingPerCanvasPreDraw_State: Because reset the context after tuning loops to ensure
|
|
||||||
* coherent state, we need to give the benchmark
|
|
||||||
* another hook
|
|
||||||
* kPreWarmTiming_State: We prewarm the gpu again to enter a steady state
|
|
||||||
* kTiming_State: Finally we time the benchmark. When finished timing
|
|
||||||
* if we have enough samples then we'll start the next
|
|
||||||
* benchmark in the kPreWarmLoopsPerCanvasPreDraw_State.
|
|
||||||
* otherwise, we enter the
|
|
||||||
* kPreWarmTimingPerCanvasPreDraw_State for another sample
|
|
||||||
* In either case we reset the context.
|
|
||||||
*/
|
|
||||||
enum State {
|
enum State {
|
||||||
kPreWarmLoopsPerCanvasPreDraw_State,
|
kPreWarm_State,
|
||||||
kPreWarmLoops_State,
|
|
||||||
kTuneLoops_State,
|
|
||||||
kPreWarmTimingPerCanvasPreDraw_State,
|
|
||||||
kPreWarmTiming_State,
|
|
||||||
kTiming_State,
|
kTiming_State,
|
||||||
};
|
};
|
||||||
|
enum InnerState {
|
||||||
|
kTuning_InnerState,
|
||||||
|
kTiming_InnerState,
|
||||||
|
};
|
||||||
|
|
||||||
inline void nextState(State);
|
|
||||||
ParentEvents perCanvasPreDraw(SkCanvas*, Benchmark*, State);
|
|
||||||
ParentEvents preWarm(State nextState);
|
|
||||||
inline ParentEvents tuneLoops();
|
|
||||||
inline ParentEvents timing(SkCanvas*, Benchmark*);
|
|
||||||
inline double elapsed();
|
inline double elapsed();
|
||||||
void resetTimingState();
|
void resetTimingState();
|
||||||
void postDraw(SkCanvas*, Benchmark*);
|
|
||||||
void recordMeasurement();
|
void recordMeasurement();
|
||||||
|
|
||||||
int fCurrentFrame;
|
int fCurrentFrame;
|
||||||
@ -104,6 +71,7 @@ private:
|
|||||||
double fLastMeasurement;
|
double fLastMeasurement;
|
||||||
WallTimer fTimer;
|
WallTimer fTimer;
|
||||||
State fState;
|
State fState;
|
||||||
|
InnerState fInnerState;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -27,6 +27,7 @@ VisualInteractiveModule::VisualInteractiveModule(VisualBench* owner)
|
|||||||
: fCurrentMeasurement(0)
|
: fCurrentMeasurement(0)
|
||||||
, fBenchmark(nullptr)
|
, fBenchmark(nullptr)
|
||||||
, fAdvance(false)
|
, fAdvance(false)
|
||||||
|
, fHasBeenReset(false)
|
||||||
, fOwner(SkRef(owner)) {
|
, fOwner(SkRef(owner)) {
|
||||||
fBenchmarkStream.reset(new VisualBenchmarkStream);
|
fBenchmarkStream.reset(new VisualBenchmarkStream);
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ bool VisualInteractiveModule::advanceRecordIfNecessary(SkCanvas* canvas) {
|
|||||||
fOwner->clear(canvas, SK_ColorWHITE, 2);
|
fOwner->clear(canvas, SK_ColorWHITE, 2);
|
||||||
|
|
||||||
fBenchmark->delayedSetup();
|
fBenchmark->delayedSetup();
|
||||||
|
fBenchmark->preTimingHooks(canvas);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#include "GrGpu.h"
|
#include "GrGpu.h"
|
||||||
@ -104,10 +105,18 @@ void VisualInteractiveModule::draw(SkCanvas* canvas) {
|
|||||||
fOwner->closeWindow();
|
fOwner->closeWindow();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fHasBeenReset) {
|
||||||
|
fHasBeenReset = false;
|
||||||
|
fBenchmark->preTimingHooks(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
this->renderFrame(canvas);
|
this->renderFrame(canvas);
|
||||||
TimingStateMachine::ParentEvents event = fTSM.nextFrame(canvas, fBenchmark);
|
TimingStateMachine::ParentEvents event = fTSM.nextFrame(false);
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case TimingStateMachine::kReset_ParentEvents:
|
case TimingStateMachine::kReset_ParentEvents:
|
||||||
|
fBenchmark->postTimingHooks(canvas);
|
||||||
|
fHasBeenReset = true;
|
||||||
fOwner->reset();
|
fOwner->reset();
|
||||||
break;
|
break;
|
||||||
case TimingStateMachine::kTiming_ParentEvents:
|
case TimingStateMachine::kTiming_ParentEvents:
|
||||||
@ -121,10 +130,10 @@ void VisualInteractiveModule::draw(SkCanvas* canvas) {
|
|||||||
if (fAdvance) {
|
if (fAdvance) {
|
||||||
fAdvance = false;
|
fAdvance = false;
|
||||||
fTSM.nextBenchmark(canvas, fBenchmark);
|
fTSM.nextBenchmark(canvas, fBenchmark);
|
||||||
|
fBenchmark->postTimingHooks(canvas);
|
||||||
fBenchmark.reset(nullptr);
|
fBenchmark.reset(nullptr);
|
||||||
fOwner->reset();
|
fOwner->reset();
|
||||||
} else {
|
fHasBeenReset = true;
|
||||||
fTSM.nextSample();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ private:
|
|||||||
SkAutoTUnref<Benchmark> fBenchmark;
|
SkAutoTUnref<Benchmark> fBenchmark;
|
||||||
TimingStateMachine fTSM;
|
TimingStateMachine fTSM;
|
||||||
bool fAdvance;
|
bool fAdvance;
|
||||||
|
bool fHasBeenReset;
|
||||||
|
|
||||||
// support framework
|
// support framework
|
||||||
SkAutoTUnref<VisualBench> fOwner;
|
SkAutoTUnref<VisualBench> fOwner;
|
||||||
|
@ -44,6 +44,7 @@ static SkString humanize(double ms) {
|
|||||||
|
|
||||||
VisualLightweightBenchModule::VisualLightweightBenchModule(VisualBench* owner)
|
VisualLightweightBenchModule::VisualLightweightBenchModule(VisualBench* owner)
|
||||||
: fCurrentSample(0)
|
: fCurrentSample(0)
|
||||||
|
, fHasBeenReset(false)
|
||||||
, fOwner(SkRef(owner))
|
, fOwner(SkRef(owner))
|
||||||
, fResults(new ResultsWriter) {
|
, fResults(new ResultsWriter) {
|
||||||
fBenchmarkStream.reset(new VisualBenchmarkStream);
|
fBenchmarkStream.reset(new VisualBenchmarkStream);
|
||||||
@ -132,12 +133,14 @@ bool VisualLightweightBenchModule::advanceRecordIfNecessary(SkCanvas* canvas) {
|
|||||||
|
|
||||||
fOwner->clear(canvas, SK_ColorWHITE, 2);
|
fOwner->clear(canvas, SK_ColorWHITE, 2);
|
||||||
|
|
||||||
fBenchmark->delayedSetup();
|
|
||||||
fRecords.push_back();
|
fRecords.push_back();
|
||||||
|
|
||||||
// Log bench name
|
// Log bench name
|
||||||
fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX,
|
fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX,
|
||||||
fBenchmark->getSize().fY);
|
fBenchmark->getSize().fY);
|
||||||
|
|
||||||
|
fBenchmark->delayedSetup();
|
||||||
|
fBenchmark->preTimingHooks(canvas);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,15 +150,24 @@ void VisualLightweightBenchModule::draw(SkCanvas* canvas) {
|
|||||||
fOwner->closeWindow();
|
fOwner->closeWindow();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fHasBeenReset) {
|
||||||
|
fHasBeenReset = false;
|
||||||
|
fBenchmark->preTimingHooks(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
this->renderFrame(canvas);
|
this->renderFrame(canvas);
|
||||||
TimingStateMachine::ParentEvents event = fTSM.nextFrame(canvas, fBenchmark);
|
TimingStateMachine::ParentEvents event = fTSM.nextFrame(true);
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case TimingStateMachine::kReset_ParentEvents:
|
case TimingStateMachine::kReset_ParentEvents:
|
||||||
|
fBenchmark->postTimingHooks(canvas);
|
||||||
fOwner->reset();
|
fOwner->reset();
|
||||||
|
fHasBeenReset = true;
|
||||||
break;
|
break;
|
||||||
case TimingStateMachine::kTiming_ParentEvents:
|
case TimingStateMachine::kTiming_ParentEvents:
|
||||||
break;
|
break;
|
||||||
case TimingStateMachine::kTimingFinished_ParentEvents:
|
case TimingStateMachine::kTimingFinished_ParentEvents:
|
||||||
|
fBenchmark->postTimingHooks(canvas);
|
||||||
fOwner->reset();
|
fOwner->reset();
|
||||||
fRecords.back().fMeasurements.push_back(fTSM.lastMeasurement());
|
fRecords.back().fMeasurements.push_back(fTSM.lastMeasurement());
|
||||||
if (++fCurrentSample > FLAGS_samples) {
|
if (++fCurrentSample > FLAGS_samples) {
|
||||||
@ -164,7 +176,7 @@ void VisualLightweightBenchModule::draw(SkCanvas* canvas) {
|
|||||||
fCurrentSample = 0;
|
fCurrentSample = 0;
|
||||||
fBenchmark.reset(nullptr);
|
fBenchmark.reset(nullptr);
|
||||||
} else {
|
} else {
|
||||||
fTSM.nextSampleWithPrewarm();
|
fHasBeenReset = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ private:
|
|||||||
SkAutoTDelete<VisualBenchmarkStream> fBenchmarkStream;
|
SkAutoTDelete<VisualBenchmarkStream> fBenchmarkStream;
|
||||||
SkAutoTUnref<Benchmark> fBenchmark;
|
SkAutoTUnref<Benchmark> fBenchmark;
|
||||||
TimingStateMachine fTSM;
|
TimingStateMachine fTSM;
|
||||||
|
bool fHasBeenReset;
|
||||||
|
|
||||||
// support framework
|
// support framework
|
||||||
SkAutoTUnref<VisualBench> fOwner;
|
SkAutoTUnref<VisualBench> fOwner;
|
||||||
|
Loading…
Reference in New Issue
Block a user