From 0f6cca8a2070b974368652ec04b6003e9205a8a3 Mon Sep 17 00:00:00 2001 From: cdalton Date: Tue, 24 Nov 2015 08:54:29 -0800 Subject: [PATCH] Optimize visualbench offscreen blits Modifies NvprWrappedBenchmark to present its offscreen canvas via GrContext::copySurface rather than going through the SkCanvas API. Only copies a subrectangle the size of the benchmark, not the entire canvas. BUG=skia: Review URL: https://codereview.chromium.org/1443263002 --- tools/VisualBench/VisualBenchmarkStream.cpp | 10 ++++- tools/VisualBench/VisualSKPBench.cpp | 15 +++++++ tools/VisualBench/VisualSKPBench.h | 2 + tools/VisualBench/WrappedBenchmark.h | 69 +++++++++++++++++++++++------ 4 files changed, 81 insertions(+), 15 deletions(-) diff --git a/tools/VisualBench/VisualBenchmarkStream.cpp b/tools/VisualBench/VisualBenchmarkStream.cpp index cfeff75..7f072e8 100644 --- a/tools/VisualBench/VisualBenchmarkStream.cpp +++ b/tools/VisualBench/VisualBenchmarkStream.cpp @@ -39,9 +39,15 @@ class WarmupBench : public Benchmark { public: WarmupBench() { sk_tool_utils::make_big_path(fPath); + fPerlinRect = SkRect::MakeLTRB(0., 0., 400., 400.); } private: const char* onGetName() override { return "warmupbench"; } + SkIPoint onGetSize() override { + int w = SkScalarCeilToInt(SkTMax(fPath.getBounds().right(), fPerlinRect.right())); + int h = SkScalarCeilToInt(SkTMax(fPath.getBounds().bottom(), fPerlinRect.bottom())); + return SkIPoint::Make(w, h); + } void onDraw(int loops, SkCanvas* canvas) override { // We draw a big path to warm up the cpu, and then use perlin noise shader to warm up the // gpu @@ -52,10 +58,9 @@ private: SkPaint perlinPaint; perlinPaint.setShader(SkPerlinNoiseShader::CreateTurbulence(0.1f, 0.1f, 1, 0, nullptr))->unref(); - SkRect rect = SkRect::MakeLTRB(0., 0., 400., 400.); for (int i = 0; i < loops; i++) { canvas->drawPath(fPath, paint); - canvas->drawRect(rect, perlinPaint); + canvas->drawRect(fPerlinRect, perlinPaint); #if SK_SUPPORT_GPU // Ensure the GrContext doesn't batch across draw loops. if (GrContext* context = canvas->getGrContext()) { @@ -65,6 +70,7 @@ private: } } SkPath fPath; + SkRect fPerlinRect; }; VisualBenchmarkStream::VisualBenchmarkStream(const SkSurfaceProps& surfaceProps) diff --git a/tools/VisualBench/VisualSKPBench.cpp b/tools/VisualBench/VisualSKPBench.cpp index 628265e..5d15d7e 100644 --- a/tools/VisualBench/VisualSKPBench.cpp +++ b/tools/VisualBench/VisualSKPBench.cpp @@ -14,6 +14,7 @@ VisualSKPBench::VisualSKPBench(const char* name, const SkPicture* pic) : fPic(SkRef(pic)) + , fCullRect(fPic->cullRect().roundOut()) , fName(name) { fUniqueName.printf("%s", name); } @@ -30,7 +31,17 @@ bool VisualSKPBench::isSuitableFor(Backend backend) { return backend != kNonRendering_Backend; } +SkIPoint VisualSKPBench::onGetSize() { + return SkIPoint::Make(fCullRect.width(), fCullRect.height()); +} + void VisualSKPBench::onDraw(int loops, SkCanvas* canvas) { + bool isOffset = SkToBool(fCullRect.left() | fCullRect.top()); + if (isOffset) { + canvas->save(); + canvas->translate(SkIntToScalar(-fCullRect.left()), SkIntToScalar(-fCullRect.top())); + } + for (int i = 0; i < loops; i++) { canvas->drawPicture(fPic); #if SK_SUPPORT_GPU @@ -40,4 +51,8 @@ void VisualSKPBench::onDraw(int loops, SkCanvas* canvas) { } #endif } + + if (isOffset) { + canvas->restore(); + } } diff --git a/tools/VisualBench/VisualSKPBench.h b/tools/VisualBench/VisualSKPBench.h index 6b9593b..dc22fec 100644 --- a/tools/VisualBench/VisualSKPBench.h +++ b/tools/VisualBench/VisualSKPBench.h @@ -24,10 +24,12 @@ protected: const char* onGetName() override; const char* onGetUniqueName() override; bool isSuitableFor(Backend backend) override; + SkIPoint onGetSize() override; void onDraw(int loops, SkCanvas* canvas) override; private: SkAutoTUnref fPic; + SkIRect fCullRect; SkString fName; SkString fUniqueName; diff --git a/tools/VisualBench/WrappedBenchmark.h b/tools/VisualBench/WrappedBenchmark.h index ffa0da3..dba6134 100644 --- a/tools/VisualBench/WrappedBenchmark.h +++ b/tools/VisualBench/WrappedBenchmark.h @@ -9,7 +9,10 @@ #define WrappedBenchmark_DEFINED #include "Benchmark.h" +#include "SkDevice.h" #include "SkSurface.h" +#include "GrContext.h" +#include "GrRenderTarget.h" // Wrap some other benchmark to allow specialization to either // cpu or gpu backends. The derived class will override 'setupOffScreen' @@ -28,7 +31,8 @@ public: void onDelayedSetup() override { fBench->delayedSetup(); } void onPerCanvasPreDraw(SkCanvas* canvas) override { - fOffScreen.reset(this->setupOffScreen(canvas)); + this->setupOffScreen(canvas); + fOffScreen->getCanvas()->clear(SK_ColorWHITE); fBench->perCanvasPreDraw(fOffScreen->getCanvas()); } void onPreDraw(SkCanvas* canvas) override { @@ -47,14 +51,21 @@ public: void onDraw(int loops, SkCanvas* canvas) override { SkASSERT(fOffScreen.get()); fBench->draw(loops, fOffScreen->getCanvas()); - SkAutoTUnref image(fOffScreen->newImageSnapshot()); - canvas->drawImage(image, 0,0); + this->blitToScreen(canvas); } virtual SkIPoint onGetSize() override { return fBench->getSize(); } -private: - virtual SkSurface* setupOffScreen(SkCanvas*)=0; +protected: + virtual void setupOffScreen(SkCanvas*)=0; + + void blitToScreen(SkCanvas* canvas) { + int w = SkTMin(fBench->getSize().fX, fOffScreen->width()); + int h = SkTMin(fBench->getSize().fY, fOffScreen->width()); + this->onBlitToScreen(canvas, w, h); + } + + virtual void onBlitToScreen(SkCanvas* canvas, int w, int h) = 0; SkSurfaceProps fSurfaceProps; SkAutoTUnref fOffScreen; @@ -68,8 +79,16 @@ public: : INHERITED(surfaceProps, bench) {} private: - SkSurface* setupOffScreen(SkCanvas* canvas) override { - return SkSurface::NewRaster(canvas->imageInfo(), &this->surfaceProps()); + void setupOffScreen(SkCanvas* canvas) override { + fOffScreen.reset(SkSurface::NewRaster(canvas->imageInfo(), &this->surfaceProps())); + } + + void onBlitToScreen(SkCanvas* canvas, int w, int h) override { + SkAutoTUnref image(fOffScreen->newImageSnapshot()); + SkPaint blitPaint; + blitPaint.setXfermodeMode(SkXfermode::kSrc_Mode); + canvas->drawImageRect(image, SkIRect::MakeWH(w, h), + SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)), &blitPaint); } typedef WrappedBenchmark INHERITED; @@ -84,12 +103,36 @@ public: , fNumSamples(numSamples) {} private: - SkSurface* setupOffScreen(SkCanvas* canvas) override { - return SkSurface::NewRenderTarget(canvas->getGrContext(), - SkSurface::kNo_Budgeted, - canvas->imageInfo(), - fNumSamples, - &this->surfaceProps()); + void setupOffScreen(SkCanvas* canvas) override { + fOffScreen.reset(SkSurface::NewRenderTarget(canvas->getGrContext(), + SkSurface::kNo_Budgeted, + canvas->imageInfo(), + fNumSamples, + &this->surfaceProps())); + } + + void onBlitToScreen(SkCanvas* canvas, int w, int h) override { + // We call copySurface directly on the underlying GPU surfaces for a more efficient blit. + GrRenderTarget* dst, *src; + + SkCanvas::LayerIter canvasIter(canvas, false); + SkAssertResult((dst = canvasIter.device()->accessRenderTarget())); + + SkCanvas::LayerIter offscreenIter(fOffScreen->getCanvas(), false); + SkAssertResult((src = offscreenIter.device()->accessRenderTarget())); + + SkASSERT(dst->getContext() == src->getContext()); + + dst->getContext()->copySurface(dst, src, SkIRect::MakeWH(w, h), SkIPoint::Make(0, 0)); + +#ifdef SK_DEBUG + // This method should not be called while layers are saved. + canvasIter.next(); + SkASSERT(canvasIter.done()); + + offscreenIter.next(); + SkASSERT(offscreenIter.done()); +#endif } int fNumSamples; -- 2.7.4