diff options
author | Robert Phillips <robertphillips@google.com> | 2018-05-29 16:13:26 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-05-30 10:12:08 +0000 |
commit | 96601084b3f7b108c1faf12a2ea12eb7ea8688a0 (patch) | |
tree | 222f8ec851bd0156b32a382f458226170f7b33c0 /tools | |
parent | 16f558ddeed33c816f3d3dad03997b2ea523c5b9 (diff) |
Add DDL to SKPBench
Most of this CL is just repackaging the promise image and tile
code from ViaDDL for reuse by SKPBench.
Change-Id: Ie5003c36fe85cc5be9639552f9488b8e92dcdbbf
Reviewed-on: https://skia-review.googlesource.com/129805
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/DDLPromiseImageHelper.cpp | 181 | ||||
-rw-r--r-- | tools/DDLPromiseImageHelper.h | 144 | ||||
-rw-r--r-- | tools/DDLTileHelper.cpp | 166 | ||||
-rw-r--r-- | tools/DDLTileHelper.h | 80 | ||||
-rw-r--r-- | tools/skpbench/skpbench.cpp | 98 |
5 files changed, 665 insertions, 4 deletions
diff --git a/tools/DDLPromiseImageHelper.cpp b/tools/DDLPromiseImageHelper.cpp new file mode 100644 index 0000000000..1eed565331 --- /dev/null +++ b/tools/DDLPromiseImageHelper.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "DDLPromiseImageHelper.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrGpu.h" +#include "SkDeferredDisplayListRecorder.h" + +DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() { + GrGpu* gpu = fContext->contextPriv().getGpu(); + + if (fBackendTexture.isValid()) { + gpu->deleteTestingOnlyBackendTexture(fBackendTexture); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) { + SkSerialProcs procs; + + procs.fImageCtx = this; + procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> { + auto helper = static_cast<DDLPromiseImageHelper*>(ctx); + + int id = helper->findOrDefineImage(image); + if (id >= 0) { + SkASSERT(helper->isValidID(id)); + return SkData::MakeWithCopy(&id, sizeof(id)); + } + + return nullptr; + }; + + return inputPicture->serialize(&procs); +} + +void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) { + GrGpu* gpu = context->contextPriv().getGpu(); + SkASSERT(gpu); + + for (int i = 0; i < fImageInfo.count(); ++i) { + sk_sp<PromiseImageCallbackContext> callbackContext( + new PromiseImageCallbackContext(context)); + + const PromiseImageInfo& info = fImageInfo[i]; + + // DDL TODO: how can we tell if we need mipmapping! + callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture( + info.fBitmap.getPixels(), + info.fBitmap.width(), + info.fBitmap.height(), + info.fBitmap.colorType(), + info.fBitmap.colorSpace(), + false, GrMipMapped::kNo)); + // The GMs sometimes request too large an image + //SkAssertResult(callbackContext->backendTexture().isValid()); + + // The fImageInfo array gets the creation ref + fImageInfo[i].fCallbackContext = std::move(callbackContext); + } +} + +sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP( + SkDeferredDisplayListRecorder* recorder, + SkData* compressedPictureData, + SkTArray<sk_sp<SkImage>>* promiseImages) const { + PerRecorderContext perRecorderContext { recorder, this, promiseImages }; + + SkDeserialProcs procs; + procs.fImageCtx = (void*) &perRecorderContext; + procs.fImageProc = PromiseImageCreator; + + return SkPicture::MakeFromData(compressedPictureData, &procs); +} + +// This generates promise images to replace the indices in the compressed picture. This +// reconstitution is performed separately in each thread so we end up with multiple +// promise images referring to the same GrBackendTexture. +sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData, + size_t length, void* ctxIn) { + PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn); + const DDLPromiseImageHelper* helper = perRecorderContext->fHelper; + SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder; + + SkASSERT(length == sizeof(int)); + + const int* indexPtr = static_cast<const int*>(rawData); + SkASSERT(helper->isValidID(*indexPtr)); + + const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr); + + if (!curImage.fCallbackContext->backendTexture().isValid()) { + // We weren't able to make a backend texture for this SkImage. In this case we create + // a separate bitmap-backed image for each thread. + // Note: we would like to share the same bitmap between all the threads but + // SkBitmap is not thread-safe. + return SkImage::MakeRasterCopy(curImage.fBitmap.pixmap()); + } + SkASSERT(curImage.fIndex == *indexPtr); + + GrBackendFormat backendFormat = curImage.fCallbackContext->backendTexture().format(); + + // Each DDL recorder gets its own ref on the promise callback context for the + // promise images it creates. + // DDL TODO: sort out mipmapping + sk_sp<SkImage> image = recorder->makePromiseTexture( + backendFormat, + curImage.fBitmap.width(), + curImage.fBitmap.height(), + GrMipMapped::kNo, + GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, + curImage.fBitmap.colorType(), + curImage.fBitmap.alphaType(), + curImage.fBitmap.refColorSpace(), + DDLPromiseImageHelper::PromiseImageFulfillProc, + DDLPromiseImageHelper::PromiseImageReleaseProc, + DDLPromiseImageHelper::PromiseImageDoneProc, + (void*) SkSafeRef(curImage.fCallbackContext.get())); + perRecorderContext->fPromiseImages->push_back(image); + SkASSERT(image); + return image; +} + +int DDLPromiseImageHelper::findImage(SkImage* image) const { + for (int i = 0; i < fImageInfo.count(); ++i) { + if (fImageInfo[i].fOriginalUniqueID == image->uniqueID()) { // trying to dedup here + SkASSERT(fImageInfo[i].fIndex == i); + SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].fIndex)); + return i; + } + } + return -1; +} + +int DDLPromiseImageHelper::addImage(SkImage* image) { + sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images + + SkImageInfo ii = SkImageInfo::Make(rasterImage->width(), rasterImage->height(), + rasterImage->colorType(), rasterImage->alphaType(), + rasterImage->refColorSpace()); + + SkBitmap bm; + bm.allocPixels(ii); + + if (!rasterImage->readPixels(bm.pixmap(), 0, 0)) { + return -1; + } + + bm.setImmutable(); + + PromiseImageInfo& newImageInfo = fImageInfo.push_back(); + newImageInfo.fIndex = fImageInfo.count()-1; + newImageInfo.fOriginalUniqueID = image->uniqueID(); + newImageInfo.fBitmap = bm; + /* fCallbackContext is filled in by uploadAllToGPU */ + + return fImageInfo.count()-1; +} + +int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) { + int preExistingID = this->findImage(image); + if (preExistingID >= 0) { + SkASSERT(this->isValidID(preExistingID)); + return preExistingID; + } + + int newID = this->addImage(image); + SkASSERT(this->isValidID(newID)); + return newID; +} + +#endif diff --git a/tools/DDLPromiseImageHelper.h b/tools/DDLPromiseImageHelper.h new file mode 100644 index 0000000000..b4b51a4f5b --- /dev/null +++ b/tools/DDLPromiseImageHelper.h @@ -0,0 +1,144 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef PromiseImageHelper_DEFINED +#define PromiseImageHelper_DEFINED + +#include "SkBitmap.h" +#include "SkTArray.h" + +#if SK_SUPPORT_GPU + +#include "GrBackendSurface.h" + +class GrContext; +class SkDeferredDisplayListRecorder; +class SkImage; +class SkPicture; + +// This class consolidates tracking & extraction of the original image data from an skp, +// the upload of said data to the GPU and the fulfillment of promise images. +// +// The way this works is: +// the original skp is converted to SkData and all its image info is extracted into this +// class and only indices into this class are left in the SkData (via deflateSKP) +// +// Prior to replaying in threads, all the images stored in this class are uploaded to the +// gpu and PromiseImageCallbackContexts are created for them (via uploadAllToGPU) +// +// Each thread reinflates the SkData into an SkPicture replacing all the indices w/ +// promise images (all using the same GrBackendTexture and getting a ref to the +// appropriate PromiseImageCallbackContext) (via reinflateSKP). +// +// This class is then reset - dropping all of its refs on the PromiseImageCallbackContexts +// +// Each done callback unrefs its PromiseImageCallbackContext so, once all the promise images +// are done the PromiseImageCallbackContext is freed and its GrBackendTexture removed +// from VRAM +// +// Note: if DDLs are going to be replayed multiple times, the reset call can be delayed until +// all the replaying is complete. This will pin the GrBackendTextures in VRAM. +class DDLPromiseImageHelper { +public: + DDLPromiseImageHelper() { } + + // Convert the SkPicture into SkData replacing all the SkImages with an index. + sk_sp<SkData> deflateSKP(const SkPicture* inputPicture); + + void uploadAllToGPU(GrContext* context); + + // reinflate a deflated SKP, replacing all the indices with promise images. + sk_sp<SkPicture> reinflateSKP(SkDeferredDisplayListRecorder*, + SkData* compressedPicture, + SkTArray<sk_sp<SkImage>>* promiseImages) const; + + // Remove this class' refs on the PromiseImageCallbackContexts + void reset() { fImageInfo.reset(); } + +private: + // This class acts as a proxy for the single GrBackendTexture representing an image. + // Whenever a promise image is created for the image, the promise image receives a ref to + // this object. Once all the promise images receive their done callbacks this object + // is deleted - removing the GrBackendTexture from VRAM. + // Note that while the DDLs are being created in the threads, the PromiseImageHelper holds + // a ref on all the PromiseImageCallbackContexts. However, once all the threads are done + // it drops all of its refs (via "reset"). + class PromiseImageCallbackContext : public SkRefCnt { + public: + PromiseImageCallbackContext(GrContext* context) : fContext(context) {} + + ~PromiseImageCallbackContext(); + + void setBackendTexture(const GrBackendTexture& backendTexture) { + fBackendTexture = backendTexture; + } + + const GrBackendTexture& backendTexture() const { return fBackendTexture; } + + private: + GrContext* fContext; + GrBackendTexture fBackendTexture; + + typedef SkRefCnt INHERITED; + }; + + // This is the information extracted into this class from the parsing of the skp file. + // Once it has all been uploaded to the GPU and distributed to the promise images, it + // is all dropped via "reset". + class PromiseImageInfo { + public: + int fIndex; // index in the 'fImageInfo' array + uint32_t fOriginalUniqueID; // original ID for deduping + SkBitmap fBitmap; // CPU-side cache of the contents + sk_sp<PromiseImageCallbackContext> fCallbackContext; + }; + + // This stack-based context allows each thread to re-inflate the image indices into + // promise images while still using the same GrBackendTexture. + struct PerRecorderContext { + SkDeferredDisplayListRecorder* fRecorder; + const DDLPromiseImageHelper* fHelper; + SkTArray<sk_sp<SkImage>>* fPromiseImages; + }; + + static void PromiseImageFulfillProc(void* textureContext, GrBackendTexture* outTexture) { + auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext); + SkASSERT(callbackContext->backendTexture().isValid()); + *outTexture = callbackContext->backendTexture(); + } + + static void PromiseImageReleaseProc(void* textureContext) { +#ifdef SK_DEBUG + auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext); + SkASSERT(callbackContext->backendTexture().isValid()); +#endif + } + + static void PromiseImageDoneProc(void* textureContext) { + auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext); + callbackContext->unref(); + } + + static sk_sp<SkImage> PromiseImageCreator(const void* rawData, size_t length, void* ctxIn); + + bool isValidID(int id) const { return id >= 0 && id < fImageInfo.count(); } + const PromiseImageInfo& getInfo(int id) const { return fImageInfo[id]; } + + // returns -1 if not found + int findImage(SkImage* image) const; + + // returns -1 on failure + int addImage(SkImage* image); + + // returns -1 on failure + int findOrDefineImage(SkImage* image); + + SkTArray<PromiseImageInfo> fImageInfo; +}; + +#endif +#endif diff --git a/tools/DDLTileHelper.cpp b/tools/DDLTileHelper.cpp new file mode 100644 index 0000000000..462915aafc --- /dev/null +++ b/tools/DDLTileHelper.cpp @@ -0,0 +1,166 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "DDLTileHelper.h" + +#if SK_SUPPORT_GPU + +#include "DDLPromiseImageHelper.h" +#include "SkCanvas.h" +#include "SkDeferredDisplayListRecorder.h" +#include "SkImage_Gpu.h" +#include "SkPicture.h" +#include "SkSurface.h" +#include "SkSurfaceCharacterization.h" +#include "SkTaskGroup.h" + +DDLTileHelper::TileData::TileData(sk_sp<SkSurface> s, const SkIRect& clip) + : fSurface(std::move(s)) + , fClip(clip) { + SkAssertResult(fSurface->characterize(&fCharacterization)); +} + +void DDLTileHelper::TileData::createTileSpecificSKP(SkData* compressedPictureData, + const DDLPromiseImageHelper& helper) { + SkASSERT(!fReconstitutedPicture); + + // This is bending the DDLRecorder contract! The promise images in the SKP should be + // created by the same recorder used to create the matching DDL. + SkDeferredDisplayListRecorder recorder(fCharacterization); + + fReconstitutedPicture = helper.reinflateSKP(&recorder, compressedPictureData, &fPromiseImages); +} + +void DDLTileHelper::TileData::createDDL() { + SkASSERT(fReconstitutedPicture); + SkASSERT(!fDisplayList); + + SkDeferredDisplayListRecorder recorder(fCharacterization); + + // DDL TODO: the DDLRecorder's GrContext isn't initialized until getCanvas is called. + // Maybe set it up in the ctor? + SkCanvas* subCanvas = recorder.getCanvas(); + + // Because we cheated in createTileSpecificSKP and used the wrong DDLRecorder, the GrContext's + // stored in fReconstitutedPicture's promise images are incorrect. Patch them with the correct + // one now. + for (int i = 0; i < fPromiseImages.count(); ++i) { + GrContext* newContext = subCanvas->getGrContext(); + + if (fPromiseImages[i]->isTextureBacked()) { + SkImage_Gpu* gpuImage = (SkImage_Gpu*) fPromiseImages[i].get(); + gpuImage->resetContext(sk_ref_sp(newContext)); + } + } + + subCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height())); + subCanvas->translate(-fClip.fLeft, -fClip.fTop); + + // Note: in this use case we only render a picture to the deferred canvas + // but, more generally, clients will use arbitrary draw calls. + subCanvas->drawPicture(fReconstitutedPicture); + + fDisplayList = recorder.detach(); +} + +void DDLTileHelper::TileData::draw() { + SkASSERT(fDisplayList); + + fSurface->draw(fDisplayList.get()); +} + +void DDLTileHelper::TileData::compose(SkCanvas* dst) { + sk_sp<SkImage> img = fSurface->makeImageSnapshot(); + dst->save(); + dst->clipRect(SkRect::Make(fClip)); + dst->drawImage(std::move(img), fClip.fLeft, fClip.fTop); + dst->restore(); +} + +void DDLTileHelper::TileData::reset() { + // TODO: when DDLs are re-renderable we don't need to do this + fDisplayList = nullptr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +DDLTileHelper::DDLTileHelper(SkCanvas* canvas, const SkIRect& viewport, int numDivisions) + : fNumDivisions(numDivisions) { + SkASSERT(fNumDivisions > 0); + fTiles.reserve(fNumDivisions*fNumDivisions); + + int xTileSize = viewport.width()/fNumDivisions; + int yTileSize = viewport.height()/fNumDivisions; + + // Create the destination tiles + for (int y = 0, yOff = 0; y < fNumDivisions; ++y, yOff += yTileSize) { + int ySize = (y < fNumDivisions-1) ? yTileSize : viewport.height()-yOff; + + for (int x = 0, xOff = 0; x < fNumDivisions; ++x, xOff += xTileSize) { + int xSize = (x < fNumDivisions-1) ? xTileSize : viewport.width()-xOff; + + SkIRect clip = SkIRect::MakeXYWH(xOff, yOff, xSize, ySize); + + SkASSERT(viewport.contains(clip)); + + SkImageInfo tileII = SkImageInfo::MakeN32Premul(xSize, ySize); + + sk_sp<SkSurface> tileSurface = canvas->makeSurface(tileII); + + // TODO: this is here to deal w/ a resource allocator bug (skbug.com/8007). If all + // the DDLs are flushed at the same time (w/o the composition draws) the allocator + // feels free to reuse the backing GrSurfaces! + tileSurface->flush(); + + fTiles.push_back(TileData(std::move(tileSurface), clip)); + } + } +} + +void DDLTileHelper::createSKPPerTile(SkData* compressedPictureData, + const DDLPromiseImageHelper& helper) { + for (int i = 0; i < fTiles.count(); ++i) { + fTiles[i].createTileSpecificSKP(compressedPictureData, helper); + } +} + +void DDLTileHelper::createDDLsInParallel() { +#if 1 + SkTaskGroup().batch(fTiles.count(), [&](int i) { + fTiles[i].createDDL(); + }); +#else + // Use this code path to debug w/o threads + for (int i = 0; i < fTiles.count(); ++i) { + fTiles[i].createDDL(); + } +#endif + +} + +void DDLTileHelper::drawAllTilesAndFlush(GrContext* context, bool flush) { + for (int i = 0; i < fTiles.count(); ++i) { + fTiles[i].draw(); + } + if (flush) { + context->flush(); + } +} + +void DDLTileHelper::composeAllTiles(SkCanvas* dstCanvas) { + for (int i = 0; i < fTiles.count(); ++i) { + fTiles[i].compose(dstCanvas); + } +} + +void DDLTileHelper::resetAllTiles() { + for (int i = 0; i < fTiles.count(); ++i) { + fTiles[i].reset(); + } +} + +#endif diff --git a/tools/DDLTileHelper.h b/tools/DDLTileHelper.h new file mode 100644 index 0000000000..fa6f8115bc --- /dev/null +++ b/tools/DDLTileHelper.h @@ -0,0 +1,80 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef DDLTileHelper_DEFINED +#define DDLTileHelper_DEFINED + +#include "SkRect.h" +#include "SkRefCnt.h" +#include "SkSurfaceCharacterization.h" + +#if SK_SUPPORT_GPU + +class DDLPromiseImageHelper; +class SkCanvas; +class SkData; +class SkDeferredDisplayList; +class SkPicture; +class SkSurface; +class SkSurfaceCharacterization; + +class DDLTileHelper { +public: + // TileData class encapsulates the information and behavior for a single tile/thread in + // a DDL rendering. + class TileData { + public: + TileData(sk_sp<SkSurface>, const SkIRect& clip); + + // This method can be invoked in parallel + // In each thread we will reconvert the compressedPictureData into an SkPicture + // replacing each image-index with a promise image. + void createTileSpecificSKP(SkData* compressedPictureData, + const DDLPromiseImageHelper& helper); + + // This method can be invoked in parallel + // Create the per-tile DDL from the per-tile SKP + void createDDL(); + + // This method operates serially and replays the recorded DDL into the tile surface. + void draw(); + + // This method also operates serially and composes the results of replaying the DDL into + // the final destination surface. + void compose(SkCanvas* dst); + + void reset(); + + private: + sk_sp<SkSurface> fSurface; + SkSurfaceCharacterization fCharacterization; + SkIRect fClip; // in the device space of the dest canvas + sk_sp<SkPicture> fReconstitutedPicture; + SkTArray<sk_sp<SkImage>> fPromiseImages; // All the promise images in the + // reconstituted picture + std::unique_ptr<SkDeferredDisplayList> fDisplayList; + }; + + DDLTileHelper(SkCanvas* canvas, const SkIRect& viewport, int numDivisions); + + void createSKPPerTile(SkData* compressedPictureData, const DDLPromiseImageHelper& helper); + + void createDDLsInParallel(); + + void drawAllTilesAndFlush(GrContext*, bool flush); + + void composeAllTiles(SkCanvas* dstCanvas); + + void resetAllTiles(); + +private: + int fNumDivisions; // number of tiles along a side + SkTArray<TileData> fTiles; +}; + +#endif +#endif diff --git a/tools/skpbench/skpbench.cpp b/tools/skpbench/skpbench.cpp index 452095fe06..f8a6e9b1a8 100644 --- a/tools/skpbench/skpbench.cpp +++ b/tools/skpbench/skpbench.cpp @@ -11,6 +11,8 @@ #include <chrono> #include <cmath> #include <vector> +#include "DDLPromiseImageHelper.h" +#include "DDLTileHelper.h" #include "GpuTimer.h" #include "GrCaps.h" #include "GrContextFactory.h" @@ -18,6 +20,8 @@ #include "SkCanvas.h" #include "SkCommonFlags.h" #include "SkCommonFlagsGpu.h" +#include "SkDeferredDisplayList.h" +#include "SkGraphics.h" #include "SkGr.h" #include "SkOSFile.h" #include "SkOSPath.h" @@ -27,6 +31,7 @@ #include "SkStream.h" #include "SkSurface.h" #include "SkSurfaceProps.h" +#include "SkTaskGroup.h" #include "flags/SkCommandLineFlags.h" #include "flags/SkCommonFlagsConfig.h" #include "picture_utils.h" @@ -43,6 +48,10 @@ * Currently, only GPU configs are supported. */ +DEFINE_bool(ddl, false, "record the skp into DDLs before rendering"); +DEFINE_int32(ddlNumAdditionalThreads, 0, "number of DDL recording threads in addition to main one"); +DEFINE_int32(ddlTilingWidthHeight, 0, "number of tiles along one edge when in DDL mode"); + DEFINE_int32(duration, 5000, "number of milliseconds to run the benchmark"); DEFINE_int32(sampleMs, 50, "minimum duration of a sample"); DEFINE_bool(gpuClock, false, "time on the gpu clock (gpu work only)"); @@ -100,17 +109,87 @@ static bool mkdir_p(const SkString& name); static SkString join(const SkCommandLineFlags::StringArray&); static void exitf(ExitErr, const char* format, ...); +static void ddl_sample(GrContext* context, DDLTileHelper* tiles, GpuSync* gpuSync, Sample* sample, + std::chrono::high_resolution_clock::time_point* startStopTime) { + using clock = std::chrono::high_resolution_clock; + + clock::time_point start = *startStopTime; + + tiles->createDDLsInParallel(); + + tiles->drawAllTilesAndFlush(context, true); + if (gpuSync) { + gpuSync->syncToPreviousFrame(); + } + + *startStopTime = clock::now(); + + tiles->resetAllTiles(); + + if (sample) { + SkASSERT(gpuSync); + sample->fDuration += *startStopTime - start; + sample->fFrames++; + } +} + +static void run_ddl_benchmark(const sk_gpu_test::FenceSync* fenceSync, + GrContext* context, SkCanvas* finalCanvas, + SkPicture* inputPicture, std::vector<Sample>* samples) { + using clock = std::chrono::high_resolution_clock; + const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs); + const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration); + + SkIRect viewport = finalCanvas->imageInfo().bounds(); + + DDLPromiseImageHelper promiseImageHelper; + sk_sp<SkData> compressedPictureData = promiseImageHelper.deflateSKP(inputPicture); + if (!compressedPictureData) { + exitf(ExitErr::kUnavailable, "DDL: conversion of skp failed"); + } + + promiseImageHelper.uploadAllToGPU(context); + + DDLTileHelper tiles(finalCanvas, viewport, FLAGS_ddlTilingWidthHeight); + + tiles.createSKPPerTile(compressedPictureData.get(), promiseImageHelper); + + clock::time_point startStopTime = clock::now(); + + ddl_sample(context, &tiles, nullptr, nullptr, &startStopTime); + GpuSync gpuSync(fenceSync); + ddl_sample(context, &tiles, &gpuSync, nullptr, &startStopTime); + + clock::duration cumulativeDuration = std::chrono::milliseconds(0); + + do { + samples->emplace_back(); + Sample& sample = samples->back(); + + do { + ddl_sample(context, &tiles, &gpuSync, &sample, &startStopTime); + } while (sample.fDuration < sampleDuration); + + cumulativeDuration += sample.fDuration; + } while (cumulativeDuration < benchDuration || 0 == samples->size() % 2); + + if (!FLAGS_png.isEmpty()) { + // The user wants to see the final result + tiles.composeAllTiles(finalCanvas); + } +} + static void run_benchmark(const sk_gpu_test::FenceSync* fenceSync, SkCanvas* canvas, const SkPicture* skp, std::vector<Sample>* samples) { using clock = std::chrono::high_resolution_clock; const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs); const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration); - draw_skp_and_flush(canvas, skp); + draw_skp_and_flush(canvas, skp); // draw1 GpuSync gpuSync(fenceSync); - draw_skp_and_flush(canvas, skp); - gpuSync.syncToPreviousFrame(); + draw_skp_and_flush(canvas, skp); // draw2 + gpuSync.syncToPreviousFrame(); // waits for draw1 to finish (after draw2's cpu work is done). clock::time_point now = clock::now(); const clock::time_point endTime = now + benchDuration; @@ -249,6 +328,10 @@ int main(int argc, char** argv) { exitf(ExitErr::kUsage, "invalid skp '%s': must specify a single skp file, or 'warmup'", join(FLAGS_skp).c_str()); } + + SkGraphics::Init(); + SkTaskGroup::Enabler enabled(FLAGS_ddlNumAdditionalThreads); + sk_sp<SkPicture> skp; SkString skpname; if (0 == strcmp(FLAGS_skp[0], "warmup")) { @@ -339,8 +422,15 @@ int main(int argc, char** argv) { SkCanvas* canvas = surface->getCanvas(); canvas->translate(-skp->cullRect().x(), -skp->cullRect().y()); if (!FLAGS_gpuClock) { - run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples); + if (FLAGS_ddl) { + run_ddl_benchmark(testCtx->fenceSync(), ctx, canvas, skp.get(), &samples); + } else { + run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples); + } } else { + if (FLAGS_ddl) { + exitf(ExitErr::kUnavailable, "DDL: GPU-only timing not supported"); + } if (!testCtx->gpuTimingSupport()) { exitf(ExitErr::kUnavailable, "GPU does not support timing"); } |