Create DDL for final composition step in DDL test harness

Besides better matching Viz's behavior this also reduces a lot of choppiness in the composition RenderTask DAG.

In the previous approach DDL draws and compositing draws would be interleaved resulting in a lot of render target swaps.

This necessitated some reorganization bc I wanted to reuse PromiseImageCallbackContext to manage the tiles' promiseImages.

Change-Id: I513bf060a69ff2bfe0e7b82ae72f149dfede632e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/285056
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2020-04-23 15:10:03 -04:00 committed by Skia Commit-Bot
parent 92247633ff
commit 11c6767a17
7 changed files with 251 additions and 177 deletions

View File

@ -1660,8 +1660,11 @@ Result GPUDDLSink::ddlDraw(const Src& src,
// TODO: move the image upload to the utility thread
promiseImageHelper.uploadAllToGPU(gpuTaskGroup, gpuThreadCtx);
// Care must be taken when using 'gpuThreadCtx' bc it moves between the gpu-thread and this
// one. About all it can be consistently used for is GrCaps access and 'defaultBackendFormat'
// calls.
constexpr int kNumDivisions = 3;
DDLTileHelper tiles(dstSurface, dstCharacterization, viewport, kNumDivisions);
DDLTileHelper tiles(gpuThreadCtx, dstCharacterization, viewport, kNumDivisions);
tiles.createBackendTextures(gpuTaskGroup, gpuThreadCtx);
@ -1669,10 +1672,22 @@ Result GPUDDLSink::ddlDraw(const Src& src,
tiles.createSKPPerTile(compressedPictureData.get(), promiseImageHelper);
tiles.kickOffThreadedWork(recordingTaskGroup, gpuTaskGroup, gpuThreadCtx);
// Apparently adding to a taskGroup isn't thread safe. Wait for the recording task group
// to add all its gpuThread work before adding the flush
// We have to wait for the recording threads to schedule all their work on the gpu thread
// before we can schedule the composition draw and the flush. Note that the gpu thread
// is not blocked at this point and this thread is borrowing recording work.
recordingTaskGroup->wait();
// Note: at this point the recording thread(s) are stalled out w/ nothing to do.
// The recording threads have already scheduled the drawing of each tile's DDL on the gpu
// thread. The composition DDL must be scheduled last bc it relies on the result of all
// the tiles' rendering. Additionally, bc we're aliasing the tiles' backend textures,
// there is nothing in the DAG to automatically force the required order.
gpuTaskGroup->add([dstSurface, ddl = tiles.composeDDL()]() {
dstSurface->draw(ddl);
});
// This should be the only explicit flush for the entire DDL draw
gpuTaskGroup->add([gpuThreadCtx]() {
// We need to ensure all the GPU work is finished so
@ -2111,7 +2126,7 @@ Result ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkStrin
canvas->clear(SK_ColorTRANSPARENT);
}
// First, create all the tiles (including their individual dest surfaces)
DDLTileHelper tiles(dstSurface, dstCharacterization, viewport, fNumDivisions);
DDLTileHelper tiles(context, dstCharacterization, viewport, fNumDivisions);
tiles.createBackendTextures(nullptr, context);
@ -2125,9 +2140,12 @@ Result ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkStrin
tiles.createDDLsInParallel();
if (replay == fNumReplays - 1) {
// This drops the promiseImageHelper's refs on all the promise images if we're in
// the last run.
// All the DDLs are created and they ref any created promise images which,
// in turn, ref the callback contexts. If it is the last run, drop the
// promise image helper's refs on the callback contexts.
promiseImageHelper.reset();
// Note: we cannot drop the tiles' callback contexts here bc they are needed
// to create each tile's destination surface.
}
// Fourth, synchronously render the display lists into the dest tiles
@ -2135,18 +2153,20 @@ Result ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkStrin
// drawing to the GPU and composing to the final surface
tiles.precompileAndDrawAllTiles(context);
// Finally, compose the drawn tiles into the result
// Note: the separation between the tiles and the final composition better
// matches Chrome but costs us a copy
tiles.composeAllTiles(context);
if (replay == fNumReplays - 1) {
// At this point the compose DDL holds refs to the composition promise images
// which, in turn, hold refs on the tile callback contexts. If it is the last run,
// drop the refs on tile callback contexts.
tiles.dropCallbackContexts();
}
// We need to ensure all the GPU work is finished so the following
// 'deleteBackendTextures' call will work on Vulkan.
dstSurface->draw(tiles.composeDDL());
// We need to ensure all the GPU work is finished so the promise image callback
// contexts will delete all the backend textures.
GrFlushInfo flushInfoSyncCpu;
flushInfoSyncCpu.fFlags = kSyncCpu_GrFlushFlag;
context->flush(flushInfoSyncCpu);
tiles.deleteBackendTextures(nullptr, context);
}
return Result::Ok();
};

View File

@ -156,7 +156,8 @@ bool SkDeferredDisplayListRecorder::init() {
[lazyProxyData](GrResourceProvider* resourceProvider,
const GrSurfaceProxy::LazySurfaceDesc&) {
// The proxy backing the destination surface had better have been instantiated
// prior to the proxy backing the DLL's surface. Steal its GrRenderTarget.
// prior to the this one (i.e., the proxy backing the DLL's surface).
// Fulfill this lazy proxy with the destination surface's GrRenderTarget.
SkASSERT(lazyProxyData->fReplayDest->peekSurface());
auto surface = sk_ref_sp<GrSurface>(lazyProxyData->fReplayDest->peekSurface());
return GrSurfaceProxy::LazyCallbackResult(std::move(surface));

View File

@ -71,7 +71,7 @@ void DDLPromiseImageHelper::PromiseImageInfo::setMipLevels(const SkBitmap& baseL
}
///////////////////////////////////////////////////////////////////////////////////////////////////
DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
PromiseImageCallbackContext::~PromiseImageCallbackContext() {
SkASSERT(fDoneCnt == fNumImages);
SkASSERT(!fUnreleasedFulfills);
SkASSERT(fTotalReleases == fTotalFulfills);
@ -82,8 +82,7 @@ DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext
}
}
void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
const GrBackendTexture& backendTexture) {
void PromiseImageCallbackContext::setBackendTexture(const GrBackendTexture& backendTexture) {
SkASSERT(!fPromiseImageTexture);
SkASSERT(fBackendFormat == backendTexture.getBackendFormat());
fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
@ -231,7 +230,6 @@ void DDLPromiseImageHelper::createCallbackContexts(GrContext* context) {
continue;
}
sk_sp<PromiseImageCallbackContext> callbackContext(
new PromiseImageCallbackContext(context, backendFormat));
@ -339,9 +337,9 @@ sk_sp<SkImage> DDLPromiseImageHelper::CreatePromiseImages(const void* rawData,
curImage.overallHeight(),
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
curImage.refOverallColorSpace(),
DDLPromiseImageHelper::PromiseImageFulfillProc,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
PromiseImageCallbackContext::PromiseImageFulfillProc,
PromiseImageCallbackContext::PromiseImageReleaseProc,
PromiseImageCallbackContext::PromiseImageDoneProc,
contexts,
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
for (int i = 0; i < textureCount; ++i) {
@ -363,7 +361,6 @@ sk_sp<SkImage> DDLPromiseImageHelper::CreatePromiseImages(const void* rawData,
// Each DDL recorder gets its own ref on the promise callback context for the
// promise images it creates.
// DDL TODO: sort out mipmapping
image = recorder->makePromiseTexture(
backendFormat,
curImage.overallWidth(),
@ -373,9 +370,9 @@ sk_sp<SkImage> DDLPromiseImageHelper::CreatePromiseImages(const void* rawData,
curImage.overallColorType(),
curImage.overallAlphaType(),
curImage.refOverallColorSpace(),
DDLPromiseImageHelper::PromiseImageFulfillProc,
DDLPromiseImageHelper::PromiseImageReleaseProc,
DDLPromiseImageHelper::PromiseImageDoneProc,
PromiseImageCallbackContext::PromiseImageFulfillProc,
PromiseImageCallbackContext::PromiseImageReleaseProc,
PromiseImageCallbackContext::PromiseImageDoneProc,
(void*)curImage.refCallbackContext(0).release(),
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
curImage.callbackContext(0)->wasAddedToImage();

View File

@ -24,6 +24,87 @@ class SkMipMap;
class SkPicture;
struct SkYUVAIndex;
// This class acts as a proxy for a GrBackendTexture that backs an image.
// Whenever a promise image is created for the image, the promise image receives a ref to
// potentially several of these objects. 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, GrBackendFormat backendFormat)
: fContext(context)
, fBackendFormat(backendFormat) {}
~PromiseImageCallbackContext();
const GrBackendFormat& backendFormat() const { return fBackendFormat; }
void setBackendTexture(const GrBackendTexture& backendTexture);
void destroyBackendTexture() {
SkASSERT(!fPromiseImageTexture || fPromiseImageTexture->unique());
if (fPromiseImageTexture) {
fContext->deleteBackendTexture(fPromiseImageTexture->backendTexture());
}
fPromiseImageTexture = nullptr;
}
sk_sp<SkPromiseImageTexture> fulfill() {
SkASSERT(fUnreleasedFulfills >= 0);
++fUnreleasedFulfills;
++fTotalFulfills;
return fPromiseImageTexture;
}
void release() {
SkASSERT(fUnreleasedFulfills > 0);
--fUnreleasedFulfills;
++fTotalReleases;
}
void done() {
++fDoneCnt;
SkASSERT(fDoneCnt <= fNumImages);
}
void wasAddedToImage() { fNumImages++; }
const SkPromiseImageTexture* promiseImageTexture() const {
return fPromiseImageTexture.get();
}
static sk_sp<SkPromiseImageTexture> PromiseImageFulfillProc(void* textureContext) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
return callbackContext->fulfill();
}
static void PromiseImageReleaseProc(void* textureContext) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
callbackContext->release();
}
static void PromiseImageDoneProc(void* textureContext) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
callbackContext->done();
callbackContext->unref();
}
private:
GrContext* fContext;
GrBackendFormat fBackendFormat;
sk_sp<SkPromiseImageTexture> fPromiseImageTexture;
int fNumImages = 0;
int fTotalFulfills = 0;
int fTotalReleases = 0;
int fUnreleasedFulfills = 0;
int fDoneCnt = 0;
typedef SkRefCnt INHERITED;
};
// 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.
//
@ -68,71 +149,6 @@ public:
void reset() { fImageInfo.reset(); }
private:
// This class acts as a proxy for a GrBackendTexture that is part of an image.
// Whenever a promise image is created for the image, the promise image receives a ref to
// potentially several of these objects. 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, GrBackendFormat backendFormat)
: fContext(context)
, fBackendFormat(backendFormat) {}
~PromiseImageCallbackContext();
const GrBackendFormat& backendFormat() const { return fBackendFormat; }
void setBackendTexture(const GrBackendTexture& backendTexture);
void destroyBackendTexture() {
SkASSERT(!fPromiseImageTexture || fPromiseImageTexture->unique());
if (fPromiseImageTexture) {
fContext->deleteBackendTexture(fPromiseImageTexture->backendTexture());
}
fPromiseImageTexture = nullptr;
}
sk_sp<SkPromiseImageTexture> fulfill() {
SkASSERT(fUnreleasedFulfills >= 0);
++fUnreleasedFulfills;
++fTotalFulfills;
return fPromiseImageTexture;
}
void release() {
SkASSERT(fUnreleasedFulfills > 0);
--fUnreleasedFulfills;
++fTotalReleases;
}
void done() {
++fDoneCnt;
SkASSERT(fDoneCnt <= fNumImages);
}
void wasAddedToImage() { fNumImages++; }
const SkPromiseImageTexture* promiseImageTexture() const {
return fPromiseImageTexture.get();
}
private:
GrContext* fContext;
GrBackendFormat fBackendFormat;
sk_sp<SkPromiseImageTexture> fPromiseImageTexture;
int fNumImages = 0;
int fTotalFulfills = 0;
int fTotalReleases = 0;
int fUnreleasedFulfills = 0;
int fDoneCnt = 0;
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".
@ -249,22 +265,6 @@ private:
static void CreateBETexturesForPromiseImage(GrContext*, PromiseImageInfo*);
static void DeleteBETexturesForPromiseImage(GrContext*, PromiseImageInfo*);
static sk_sp<SkPromiseImageTexture> PromiseImageFulfillProc(void* textureContext) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
return callbackContext->fulfill();
}
static void PromiseImageReleaseProc(void* textureContext) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
callbackContext->release();
}
static void PromiseImageDoneProc(void* textureContext) {
auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
callbackContext->done();
callbackContext->unref();
}
static sk_sp<SkImage> CreatePromiseImages(const void* rawData, size_t length, void* ctxIn);
bool isValidID(int id) const { return id >= 0 && id < fImageInfo.count(); }

View File

@ -19,16 +19,21 @@
#include "tools/DDLPromiseImageHelper.h"
void DDLTileHelper::TileData::init(int id,
sk_sp<SkSurface> dstSurface,
GrContext* context,
const SkSurfaceCharacterization& dstSurfaceCharacterization,
const SkIRect& clip) {
fID = id;
fDstSurface = dstSurface;
fClip = clip;
fCharacterization = dstSurfaceCharacterization.createResized(clip.width(), clip.height());
SkASSERT(fCharacterization.isValid());
SkASSERT(!fBackendTexture.isValid());
GrBackendFormat backendFormat = context->defaultBackendFormat(fCharacterization.colorType(),
GrRenderable::kYes);
SkDEBUGCODE(const GrCaps* caps = context->priv().caps());
SkASSERT(caps->isFormatTexturable(backendFormat));
fCallbackContext.reset(new PromiseImageCallbackContext(context, backendFormat));
}
DDLTileHelper::TileData::~TileData() {}
@ -83,6 +88,29 @@ void DDLTileHelper::TileData::createDDL() {
fDisplayList = recorder.detach();
}
void DDLTileHelper::createComposeDDL() {
SkASSERT(!fComposeDDL);
SkDeferredDisplayListRecorder recorder(fDstCharacterization);
SkCanvas* recordingCanvas = recorder.getCanvas();
for (int i = 0; i < this->numTiles(); ++i) {
TileData* tile = &fTiles[i];
sk_sp<SkImage> promiseImage = tile->makePromiseImage(&recorder);
SkIRect clipRect = tile->clipRect();
SkASSERT(clipRect.width() == promiseImage->width() &&
clipRect.height() == promiseImage->height());
recordingCanvas->drawImage(promiseImage, clipRect.fLeft, clipRect.fTop);
}
fComposeDDL = recorder.detach();
}
void DDLTileHelper::TileData::precompile(GrContext* context) {
SkASSERT(fDisplayList);
@ -93,12 +121,18 @@ void DDLTileHelper::TileData::precompile(GrContext* context) {
}
sk_sp<SkSurface> DDLTileHelper::TileData::makeWrappedTileDest(GrContext* context) {
if (!fBackendTexture.isValid()) {
SkASSERT(fCallbackContext && fCallbackContext->promiseImageTexture());
auto promiseImageTexture = fCallbackContext->promiseImageTexture();
if (!promiseImageTexture->backendTexture().isValid()) {
return nullptr;
}
// Here we are, unfortunately, aliasing the backend texture held by the SkPromiseImageTexture.
// Both the tile's destination surface and the promise image used to draw the tile will be
// backed by the same backendTexture - unbeknownst to Ganesh.
return SkSurface::MakeFromBackendTexture(context,
fBackendTexture,
promiseImageTexture->backendTexture(),
fCharacterization.origin(),
fCharacterization.sampleCount(),
fCharacterization.colorType(),
@ -126,7 +160,10 @@ void DDLTileHelper::TileData::drawSKPDirectly(GrContext* context) {
void DDLTileHelper::TileData::draw(GrContext* context) {
SkASSERT(fDisplayList && !fTileSurface);
// The tile's surface needs to be held until after the DDL is flushed
// The tile's surface needs to be held until after the DDL is flushed bc the DDL doesn't take
// a ref on its destination proxy.
// TODO: make the DDL (or probably the drawing manager) take a ref on the destination proxy
// (maybe in GrDrawingManager::addDDLTarget).
fTileSurface = this->makeWrappedTileDest(context);
if (fTileSurface) {
fTileSurface->draw(fDisplayList.get());
@ -136,65 +173,67 @@ void DDLTileHelper::TileData::draw(GrContext* context) {
}
}
// TODO: We should create a single DDL for the composition step and just add replaying it
// as the last GPU task
void DDLTileHelper::TileData::compose(GrContext* context) {
SkASSERT(context->priv().asDirectContext());
SkASSERT(fDstSurface);
if (!fBackendTexture.isValid()) {
return;
}
// Here we are, unfortunately, aliasing 'fBackendTexture'. It is backing both 'fTileSurface'
// and 'tmp'.
sk_sp<SkImage> tmp = SkImage::MakeFromTexture(context,
fBackendTexture,
fCharacterization.origin(),
fCharacterization.colorType(),
kPremul_SkAlphaType,
fCharacterization.refColorSpace());
SkCanvas* canvas = fDstSurface->getCanvas();
canvas->save();
canvas->clipRect(SkRect::Make(fClip));
canvas->drawImage(tmp, fClip.fLeft, fClip.fTop);
canvas->restore();
}
void DDLTileHelper::TileData::reset() {
// TODO: when DDLs are re-renderable we don't need to do this
fDisplayList = nullptr;
fTileSurface = nullptr;
}
sk_sp<SkImage> DDLTileHelper::TileData::makePromiseImage(SkDeferredDisplayListRecorder* recorder) {
SkASSERT(fCallbackContext);
// The promise image gets a ref on the promise callback context
sk_sp<SkImage> promiseImage = recorder->makePromiseTexture(
fCallbackContext->backendFormat(),
fClip.width(),
fClip.height(),
GrMipMapped::kNo,
GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin,
fCharacterization.colorType(),
kPremul_SkAlphaType,
fCharacterization.refColorSpace(),
PromiseImageCallbackContext::PromiseImageFulfillProc,
PromiseImageCallbackContext::PromiseImageReleaseProc,
PromiseImageCallbackContext::PromiseImageDoneProc,
(void*)this->refCallbackContext().release(),
SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
fCallbackContext->wasAddedToImage();
return promiseImage;
}
void DDLTileHelper::TileData::CreateBackendTexture(GrContext* context, TileData* tile) {
SkASSERT(context->priv().asDirectContext());
SkASSERT(!tile->fBackendTexture.isValid());
SkASSERT(tile->fCallbackContext && !tile->fCallbackContext->promiseImageTexture());
tile->fBackendTexture = context->createBackendTexture(tile->fCharacterization);
// TODO: it seems that, on the Linux bots, backend texture creation is failing
// a lot (skbug.com/10142)
//SkASSERT(tile->fBackendTexture.isValid());
GrBackendTexture beTex = context->createBackendTexture(tile->fCharacterization);
tile->fCallbackContext->setBackendTexture(beTex);
}
void DDLTileHelper::TileData::DeleteBackendTexture(GrContext* context, TileData* tile) {
SkASSERT(context->priv().asDirectContext());
SkASSERT(tile->fCallbackContext);
// TODO: it seems that, on the Linux bots, backend texture creation is failing
// a lot (skbug.com/10142)
//SkASSERT(tile->fBackendTexture.isValid());
SkASSERT(!tile->fCallbackContext->promiseImageTexture() ||
tile->fCallbackContext->promiseImageTexture()->backendTexture().isValid());
tile->fTileSurface = nullptr;
context->deleteBackendTexture(tile->fBackendTexture);
SkASSERT(tile->fCallbackContext->unique());
tile->fCallbackContext.reset();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
DDLTileHelper::DDLTileHelper(sk_sp<SkSurface> dstSurface,
DDLTileHelper::DDLTileHelper(GrContext* context,
const SkSurfaceCharacterization& dstChar,
const SkIRect& viewport,
int numDivisions)
: fNumDivisions(numDivisions) {
: fNumDivisions(numDivisions)
, fDstCharacterization(dstChar) {
SkASSERT(fNumDivisions > 0);
fTiles = new TileData[this->numTiles()];
@ -212,7 +251,7 @@ DDLTileHelper::DDLTileHelper(sk_sp<SkSurface> dstSurface,
SkASSERT(viewport.contains(clip));
fTiles[y*fNumDivisions+x].init(y*fNumDivisions+x, dstSurface, dstChar, clip);
fTiles[y*fNumDivisions+x].init(y*fNumDivisions+x, context, dstChar, clip);
}
}
}
@ -227,12 +266,14 @@ void DDLTileHelper::createSKPPerTile(SkData* compressedPictureData,
void DDLTileHelper::createDDLsInParallel() {
#if 1
SkTaskGroup().batch(this->numTiles(), [&](int i) { fTiles[i].createDDL(); });
SkTaskGroup().add([this]{ this->createComposeDDL(); });
SkTaskGroup().wait();
#else
// Use this code path to debug w/o threads
for (int i = 0; i < this->numTiles(); ++i) {
fTiles[i].createDDL();
}
this->createComposeDDL();
#endif
}
@ -246,10 +287,6 @@ static void do_gpu_stuff(GrContext* context, DDLTileHelper::TileData* tile) {
tile->precompile(context);
tile->draw(context);
// TODO: we should actually have a separate DDL that does
// the final composition draw
tile->compose(context);
}
// We expect to have more than one recording thread but just one gpu thread
@ -274,6 +311,8 @@ void DDLTileHelper::kickOffThreadedWork(SkTaskGroup* recordingTaskGroup,
});
});
}
recordingTaskGroup->add([this] { this->createComposeDDL(); });
}
void DDLTileHelper::precompileAndDrawAllTiles(GrContext* context) {
@ -296,9 +335,9 @@ void DDLTileHelper::drawAllTilesDirectly(GrContext* context) {
}
}
void DDLTileHelper::composeAllTiles(GrContext* context) {
void DDLTileHelper::dropCallbackContexts() {
for (int i = 0; i < this->numTiles(); ++i) {
fTiles[i].compose(context);
fTiles[i].dropCallbackContext();
}
}
@ -306,6 +345,7 @@ void DDLTileHelper::resetAllTiles() {
for (int i = 0; i < this->numTiles(); ++i) {
fTiles[i].reset();
}
fComposeDDL.reset();
}
void DDLTileHelper::createBackendTextures(SkTaskGroup* taskGroup, GrContext* context) {

View File

@ -13,9 +13,11 @@
#include "include/core/SkSurfaceCharacterization.h"
class DDLPromiseImageHelper;
class PromiseImageCallbackContext;
class SkCanvas;
class SkData;
class SkDeferredDisplayList;
class SkDeferredDisplayListRecorder;
class SkPicture;
class SkSurface;
class SkSurfaceCharacterization;
@ -30,7 +32,7 @@ public:
~TileData();
void init(int id,
sk_sp<SkSurface> dstSurface,
GrContext* context,
const SkSurfaceCharacterization& dstChar,
const SkIRect& clip);
@ -50,42 +52,48 @@ public:
// a 'createDDL' and 'draw' pair.
void drawSKPDirectly(GrContext*);
// Replay the recorded DDL into the tile surface - creating 'fImage'.
// Replay the recorded DDL into the tile surface - filling in 'fBackendTexture'.
void draw(GrContext*);
// Draw the result of replaying the DDL (i.e., 'fImage') into the
// final destination surface ('fDstSurface').
void compose(GrContext*);
void reset();
int id() const { return fID; }
SkIRect clipRect() const { return fClip; }
SkDeferredDisplayList* ddl() { return fDisplayList.get(); }
sk_sp<SkImage> makePromiseImage(SkDeferredDisplayListRecorder*);
void dropCallbackContext() { fCallbackContext.reset(); }
static void CreateBackendTexture(GrContext*, TileData*);
static void DeleteBackendTexture(GrContext*, TileData*);
private:
sk_sp<SkSurface> makeWrappedTileDest(GrContext* context);
sk_sp<PromiseImageCallbackContext> refCallbackContext() { return fCallbackContext; }
int fID = -1;
sk_sp<SkSurface> fDstSurface; // the ultimate target for composition
GrBackendTexture fBackendTexture; // destination for this tile's content
SkIRect fClip; // in the device space of the final SkSurface
SkSurfaceCharacterization fCharacterization; // characterization for the tile's surface
SkIRect fClip; // in the device space of the 'fDstSurface'
// 'fTileSurface' wraps 'fBackendTexture' and must exist until after 'fDisplayList'
// has been flushed.
// The callback context holds (via its SkPromiseImageTexture) the backend texture
// that is both wrapped in 'fTileSurface' and backs this tile's promise image
// (i.e., the one returned by 'makePromiseImage').
sk_sp<PromiseImageCallbackContext> fCallbackContext;
// 'fTileSurface' wraps the backend texture in 'fCallbackContext' and must exist until
// after 'fDisplayList' has been flushed (bc it owns the proxy the DDL's destination
// trampoline points at).
// TODO: fix the ref-order so we don't need 'fTileSurface' here
sk_sp<SkSurface> fTileSurface;
sk_sp<SkPicture> fReconstitutedPicture;
SkTArray<sk_sp<SkImage>> fPromiseImages; // All the promise images in the
// reconstituted picture
std::unique_ptr<SkDeferredDisplayList> fDisplayList;
};
DDLTileHelper(sk_sp<SkSurface> dstSurface,
DDLTileHelper(GrContext* context,
const SkSurfaceCharacterization& dstChar,
const SkIRect& viewport,
int numDivisions);
@ -101,6 +109,10 @@ public:
void createDDLsInParallel();
// Create the DDL that will compose all the tile images into a final result.
void createComposeDDL();
SkDeferredDisplayList* composeDDL() const { return fComposeDDL.get(); }
void precompileAndDrawAllTiles(GrContext*);
// For each tile, create its DDL and then draw it - all on a single thread. This is to allow
@ -114,8 +126,7 @@ public:
// DDLs first - all on a single thread.
void drawAllTilesDirectly(GrContext*);
void composeAllTiles(GrContext*);
void dropCallbackContexts();
void resetAllTiles();
int numTiles() const { return fNumDivisions * fNumDivisions; }
@ -124,8 +135,12 @@ public:
void deleteBackendTextures(SkTaskGroup*, GrContext*);
private:
int fNumDivisions; // number of tiles along a side
TileData* fTiles; // 'fNumDivisions' x 'fNumDivisions'
int fNumDivisions; // number of tiles along a side
TileData* fTiles; // 'fNumDivisions' x 'fNumDivisions'
std::unique_ptr<SkDeferredDisplayList> fComposeDDL;
const SkSurfaceCharacterization fDstCharacterization;
};
#endif

View File

@ -235,16 +235,16 @@ static void ddl_sample(GrContext* context, DDLTileHelper* tiles, GpuSync& gpuSyn
}
}
static void run_ddl_benchmark(GrContext* context, sk_sp<SkSurface> surface,
static void run_ddl_benchmark(GrContext* context, sk_sp<SkSurface> dstSurface,
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);
SkSurfaceCharacterization dstCharacterization;
SkAssertResult(surface->characterize(&dstCharacterization));
SkAssertResult(dstSurface->characterize(&dstCharacterization));
SkIRect viewport = surface->imageInfo().bounds();
SkIRect viewport = dstSurface->imageInfo().bounds();
DDLPromiseImageHelper promiseImageHelper;
sk_sp<SkData> compressedPictureData = promiseImageHelper.deflateSKP(inputPicture);
@ -256,7 +256,7 @@ static void run_ddl_benchmark(GrContext* context, sk_sp<SkSurface> surface,
promiseImageHelper.uploadAllToGPU(nullptr, context);
DDLTileHelper tiles(surface, dstCharacterization, viewport, FLAGS_ddlTilingWidthHeight);
DDLTileHelper tiles(context, dstCharacterization, viewport, FLAGS_ddlTilingWidthHeight);
tiles.createBackendTextures(nullptr, context);
@ -285,7 +285,8 @@ static void run_ddl_benchmark(GrContext* context, sk_sp<SkSurface> surface,
if (!FLAGS_png.isEmpty()) {
// The user wants to see the final result
tiles.composeAllTiles(context);
dstSurface->draw(tiles.composeDDL());
dstSurface->flush();
}
tiles.resetAllTiles();