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:
parent
92247633ff
commit
11c6767a17
@ -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();
|
||||
};
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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(); }
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user