Support sharing promise images between DDLs
- Migrate our code to SkImage::MakePromiseTexture
- Have DDLTileHelper share one SKP and one set of promise images across all tiles.
- Disallow on-the-fly allocation of mips for promise textures.
Bug: skia:10286
Change-Id: Ie35976958454fc520f3c9d860e6285441260c9f7
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/291938
Commit-Queue: Adlai Holler <adlai@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 8a8b354..67c7a20 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -1790,7 +1790,7 @@
SkTaskGroup* recordingTaskGroup,
SkTaskGroup* gpuTaskGroup,
sk_gpu_test::TestContext* gpuTestCtx,
- GrDirectContext* gpuThreadCtx) const {
+ GrDirectContext* dContext) const {
// We have to do this here bc characterization can hit the SkGpuDevice's thread guard (i.e.,
// leaving it until the DDLTileHelper ctor will result in multiple threads trying to use the
@@ -1800,8 +1800,8 @@
auto size = src.size();
SkPictureRecorder recorder;
- Result result = src.draw(gpuThreadCtx, recorder.beginRecording(SkIntToScalar(size.width()),
- SkIntToScalar(size.height())));
+ Result result = src.draw(dContext, recorder.beginRecording(SkIntToScalar(size.width()),
+ SkIntToScalar(size.height())));
if (!result.isOk()) {
return result;
}
@@ -1810,14 +1810,14 @@
// this is our ultimate final drawing area/rect
SkIRect viewport = SkIRect::MakeWH(size.fWidth, size.fHeight);
- SkYUVAPixmapInfo::SupportedDataTypes supportedYUVADataTypes(*gpuThreadCtx);
+ SkYUVAPixmapInfo::SupportedDataTypes supportedYUVADataTypes(*dContext);
DDLPromiseImageHelper promiseImageHelper(supportedYUVADataTypes);
sk_sp<SkData> compressedPictureData = promiseImageHelper.deflateSKP(inputPicture.get());
if (!compressedPictureData) {
return Result::Fatal("GPUDDLSink: Couldn't deflate SkPicture");
}
- promiseImageHelper.createCallbackContexts(gpuThreadCtx);
+ promiseImageHelper.createCallbackContexts(dContext);
// 'gpuTestCtx/gpuThreadCtx' is being shifted to the gpuThread. Leave the main (this)
// thread w/o a context.
@@ -1827,21 +1827,21 @@
gpuTaskGroup->add([gpuTestCtx] { gpuTestCtx->makeCurrent(); });
// TODO: move the image upload to the utility thread
- promiseImageHelper.uploadAllToGPU(gpuTaskGroup, gpuThreadCtx);
+ promiseImageHelper.uploadAllToGPU(gpuTaskGroup, dContext);
// 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(gpuThreadCtx, dstCharacterization, viewport, kNumDivisions,
+ DDLTileHelper tiles(dContext, dstCharacterization, viewport, kNumDivisions,
/* addRandomPaddingToDst */ false);
- tiles.createBackendTextures(gpuTaskGroup, gpuThreadCtx);
+ tiles.createBackendTextures(gpuTaskGroup, dContext);
- // Reinflate the compressed picture individually for each thread.
- tiles.createSKPPerTile(compressedPictureData.get(), promiseImageHelper);
+ // Reinflate the compressed picture.
+ tiles.createSKP(dContext->threadSafeProxy(), compressedPictureData.get(), promiseImageHelper);
- tiles.kickOffThreadedWork(recordingTaskGroup, gpuTaskGroup, gpuThreadCtx);
+ tiles.kickOffThreadedWork(recordingTaskGroup, gpuTaskGroup, dContext);
// 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
@@ -1859,23 +1859,22 @@
});
// This should be the only explicit flush for the entire DDL draw.
- // TODO: remove the flushes in do_gpu_stuff
- gpuTaskGroup->add([gpuThreadCtx]() {
+ gpuTaskGroup->add([dContext]() {
// We need to ensure all the GPU work is finished so
// the following 'deleteAllFromGPU' call will work
// on Vulkan.
// TODO: switch over to using the promiseImage callbacks
// to free the backendTextures. This is complicated a
// bit by which thread possesses the direct context.
- gpuThreadCtx->flush();
- gpuThreadCtx->submit(true);
+ dContext->flush();
+ dContext->submit(true);
});
// The backend textures are created on the gpuThread by the 'uploadAllToGPU' call.
// It is simpler to also delete them at this point on the gpuThread.
- promiseImageHelper.deleteAllFromGPU(gpuTaskGroup, gpuThreadCtx);
+ promiseImageHelper.deleteAllFromGPU(gpuTaskGroup, dContext);
- tiles.deleteBackendTextures(gpuTaskGroup, gpuThreadCtx);
+ tiles.deleteBackendTextures(gpuTaskGroup, dContext);
// A flush has already been scheduled on the gpu thread along with the clean up of the backend
// textures so it is safe to schedule making 'gpuTestCtx' not current on the gpuThread.
@@ -2293,11 +2292,12 @@
tiles.createBackendTextures(nullptr, direct);
- // Second, reinflate the compressed picture individually for each thread
+ // Second, reinflate the compressed picture.
// This recreates the promise SkImages on each replay iteration. We are currently
// relying on this to test using a SkPromiseImageTexture to fulfill different
- // SkImages. On each replay the promise SkImages are recreated in createSKPPerTile.
- tiles.createSKPPerTile(compressedPictureData.get(), promiseImageHelper);
+ // SkImages. On each replay the promise SkImages are recreated in createSKP.
+ tiles.createSKP(direct->threadSafeProxy(), compressedPictureData.get(),
+ promiseImageHelper);
// Third, create the DDLs in parallel
tiles.createDDLsInParallel();
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index 8e4bc2a..9562f07 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -689,17 +689,19 @@
// We pass kReadOnly here since we should treat content of the client's texture as immutable.
// The promise API provides no way for the client to indicate that the texture is protected.
- return sk_sp<GrTextureProxy>(new GrTextureProxy(std::move(callback),
- format,
- dimensions,
- mipMapped,
- mipmapStatus,
- SkBackingFit::kExact,
- SkBudgeted::kNo,
- GrProtected::kNo,
- GrInternalSurfaceFlags::kReadOnly,
- GrSurfaceProxy::UseAllocator::kYes,
- GrDDLProvider::kYes));
+ auto proxy = sk_sp<GrTextureProxy>(new GrTextureProxy(std::move(callback),
+ format,
+ dimensions,
+ mipMapped,
+ mipmapStatus,
+ SkBackingFit::kExact,
+ SkBudgeted::kNo,
+ GrProtected::kNo,
+ GrInternalSurfaceFlags::kReadOnly,
+ GrSurfaceProxy::UseAllocator::kYes,
+ GrDDLProvider::kYes));
+ proxy->priv().setIsPromiseProxy();
+ return proxy;
}
sk_sp<GrTextureProxy> GrProxyProvider::createLazyProxy(LazyInstantiateCallback&& callback,
diff --git a/src/gpu/GrSurfaceProxy.h b/src/gpu/GrSurfaceProxy.h
index b6dcad0..f99a67e 100644
--- a/src/gpu/GrSurfaceProxy.h
+++ b/src/gpu/GrSurfaceProxy.h
@@ -337,6 +337,8 @@
GrProtected isProtected() const { return fIsProtected; }
+ bool isPromiseProxy() { return fIsPromiseProxy; }
+
protected:
// Deferred version - takes a new UniqueID from the shared resource/proxy pool.
GrSurfaceProxy(const GrBackendFormat&,
@@ -433,6 +435,7 @@
bool fIgnoredByResourceAllocator = false;
bool fIsDDLTarget = false;
+ bool fIsPromiseProxy = false;
GrProtected fIsProtected;
// This entry is lazily evaluated so, when the proxy wraps a resource, the resource
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index aa98f96..9fe0d1a 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -42,6 +42,8 @@
void setIsDDLTarget() { fProxy->fIsDDLTarget = true; }
+ void setIsPromiseProxy() { fProxy->fIsPromiseProxy = true; }
+
private:
explicit GrSurfaceProxyPriv(GrSurfaceProxy* proxy) : fProxy(proxy) {}
GrSurfaceProxyPriv(const GrSurfaceProxyPriv&) = delete;
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 750630e..6d85242 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -98,13 +98,18 @@
SkBudgeted budgeted) {
SkASSERT(baseProxy);
+ // We don't allow this for promise proxies i.e. if they need mips they need to give them
+ // to us upfront.
+ if (baseProxy->isPromiseProxy()) {
+ return nullptr;
+ }
if (!ctx->priv().caps()->isFormatCopyable(baseProxy->backendFormat())) {
- return {};
+ return nullptr;
}
auto copy = GrSurfaceProxy::Copy(ctx, std::move(baseProxy), origin, GrMipmapped::kYes,
SkBackingFit::kExact, budgeted);
if (!copy) {
- return {};
+ return nullptr;
}
SkASSERT(copy->asTextureProxy());
return copy;
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index d11a617..6a4d23e 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -35,13 +35,6 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
-#if GR_TEST_UTILS
-void SkImage_GpuBase::resetContext(sk_sp<GrImageContext> newContext) {
- SkASSERT(fContext->priv().matches(newContext.get()));
- fContext = newContext;
-}
-#endif
-
bool SkImage_GpuBase::ValidateBackendTexture(const GrCaps* caps, const GrBackendTexture& tex,
GrColorType grCT, SkColorType ct, SkAlphaType at,
sk_sp<SkColorSpace> cs) {
diff --git a/src/image/SkImage_GpuBase.h b/src/image/SkImage_GpuBase.h
index d38f577..05c2123 100644
--- a/src/image/SkImage_GpuBase.h
+++ b/src/image/SkImage_GpuBase.h
@@ -37,10 +37,6 @@
bool onIsValid(GrRecordingContext*) const final;
-#if GR_TEST_UTILS
- void resetContext(sk_sp<GrImageContext> newContext);
-#endif
-
static bool ValidateBackendTexture(const GrCaps*, const GrBackendTexture& tex,
GrColorType grCT, SkColorType ct, SkAlphaType at,
sk_sp<SkColorSpace> cs);
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index a12e1a7..5b8f802 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -893,11 +893,6 @@
REPORTER_ASSERT(reporter, !image);
}
-static sk_sp<SkPromiseImageTexture> dummy_fulfill_proc(void*) {
- SkASSERT(0);
- return nullptr;
-}
-
////////////////////////////////////////////////////////////////////////////////
// Test out the behavior of an invalid DDLRecorder
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLInvalidRecorder, reporter, ctxInfo) {
@@ -923,23 +918,6 @@
REPORTER_ASSERT(reporter, !c.isValid());
REPORTER_ASSERT(reporter, !recorder.getCanvas());
REPORTER_ASSERT(reporter, !recorder.detach());
-
- GrBackendFormat format = dContext->defaultBackendFormat(kRGBA_8888_SkColorType,
- GrRenderable::kNo);
- SkASSERT(format.isValid());
-
- sk_sp<SkImage> image = recorder.makePromiseTexture(
- format,
- 32, 32,
- GrMipmapped::kNo,
- kTopLeft_GrSurfaceOrigin,
- kRGBA_8888_SkColorType,
- kPremul_SkAlphaType,
- nullptr,
- dummy_fulfill_proc,
- /*release proc*/ nullptr,
- nullptr);
- REPORTER_ASSERT(reporter, !image);
}
}
@@ -1093,13 +1071,21 @@
GrRenderable::kNo);
SkASSERT(format.isValid());
- sk_sp<SkImage> promiseImage = recorder.makePromiseTexture(
- format, 32, 32, GrMipmapped::kNo, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType,
- kPremul_SkAlphaType, nullptr, tracking_fulfill_proc, tracking_release_proc,
- &fulfillInfo);
-
SkCanvas* canvas = recorder.getCanvas();
+ sk_sp<SkImage> promiseImage = SkImage::MakePromiseTexture(
+ canvas->recordingContext()->threadSafeProxy(),
+ format,
+ SkISize::Make(32, 32),
+ GrMipmapped::kNo,
+ kTopLeft_GrSurfaceOrigin,
+ kRGBA_8888_SkColorType,
+ kPremul_SkAlphaType,
+ nullptr,
+ tracking_fulfill_proc,
+ tracking_release_proc,
+ &fulfillInfo);
+
canvas->clear(SK_ColorRED);
canvas->drawImage(promiseImage, 0, 0);
ddl = recorder.detach();
@@ -1185,6 +1171,12 @@
}
#ifdef SK_GL
+
+static sk_sp<SkPromiseImageTexture> dummy_fulfill_proc(void*) {
+ SkASSERT(0);
+ return nullptr;
+}
+
////////////////////////////////////////////////////////////////////////////////
// Check that the texture-specific flags (i.e., for external & rectangle textures) work
// for promise images. As such, this is a GL-only test.
@@ -1203,17 +1195,18 @@
for (auto mipMapped : { GrMipmapped::kNo, GrMipmapped::kYes }) {
GrBackendFormat format = GrBackendFormat::MakeGL(GR_GL_RGBA8, target);
- sk_sp<SkImage> image = recorder.makePromiseTexture(
+ sk_sp<SkImage> image = SkImage::MakePromiseTexture(
+ recorder.getCanvas()->recordingContext()->threadSafeProxy(),
format,
- 32, 32,
+ SkISize::Make(32, 32),
mipMapped,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
- nullptr,
+ /*color space*/nullptr,
dummy_fulfill_proc,
/*release proc*/ nullptr,
- nullptr);
+ /*context*/nullptr);
if (GR_GL_TEXTURE_2D != target && mipMapped == GrMipmapped::kYes) {
REPORTER_ASSERT(reporter, !image);
continue;
diff --git a/tools/DDLPromiseImageHelper.cpp b/tools/DDLPromiseImageHelper.cpp
index 66af4cb..1a4119c 100644
--- a/tools/DDLPromiseImageHelper.cpp
+++ b/tools/DDLPromiseImageHelper.cpp
@@ -278,13 +278,13 @@
}
sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
- SkDeferredDisplayListRecorder* recorder,
+ sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
SkData* compressedPictureData,
SkTArray<sk_sp<SkImage>>* promiseImages) const {
- PerRecorderContext perRecorderContext { recorder, this, promiseImages };
+ DeserialImageProcContext procContext { std::move(threadSafeProxy), this, promiseImages };
SkDeserialProcs procs;
- procs.fImageCtx = (void*) &perRecorderContext;
+ procs.fImageCtx = (void*) &procContext;
procs.fImageProc = CreatePromiseImages;
return SkPicture::MakeFromData(compressedPictureData, &procs);
@@ -295,9 +295,8 @@
// promise images referring to the same GrBackendTexture.
sk_sp<SkImage> DDLPromiseImageHelper::CreatePromiseImages(const void* rawData,
size_t length, void* ctxIn) {
- PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
- const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
- SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
+ DeserialImageProcContext* procContext = static_cast<DeserialImageProcContext*>(ctxIn);
+ const DDLPromiseImageHelper* helper = procContext->fHelper;
SkASSERT(length == sizeof(int));
@@ -331,13 +330,13 @@
backendFormats,
GrMipmapped::kNo,
kTopLeft_GrSurfaceOrigin);
-
- image = recorder->makeYUVAPromiseTexture(
- yuvaBackendTextures,
- curImage.refOverallColorSpace(),
- PromiseImageCallbackContext::PromiseImageFulfillProc,
- PromiseImageCallbackContext::PromiseImageReleaseProc,
- contexts);
+ image = SkImage::MakePromiseYUVATexture(
+ procContext->fThreadSafeProxy,
+ yuvaBackendTextures,
+ curImage.refOverallColorSpace(),
+ PromiseImageCallbackContext::PromiseImageFulfillProc,
+ PromiseImageCallbackContext::PromiseImageReleaseProc,
+ contexts);
if (!image) {
return nullptr;
}
@@ -349,22 +348,20 @@
const GrBackendFormat& backendFormat = curImage.backendFormat(0);
SkASSERT(backendFormat.isValid());
- // Each DDL recorder gets its own ref on the promise callback context for the
- // promise images it creates.
- image = recorder->makePromiseTexture(backendFormat,
- curImage.overallWidth(),
- curImage.overallHeight(),
- curImage.mipMapped(0),
- GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
- curImage.overallColorType(),
- curImage.overallAlphaType(),
- curImage.refOverallColorSpace(),
- PromiseImageCallbackContext::PromiseImageFulfillProc,
- PromiseImageCallbackContext::PromiseImageReleaseProc,
- (void*)curImage.refCallbackContext(0).release());
+ image = SkImage::MakePromiseTexture(procContext->fThreadSafeProxy,
+ backendFormat,
+ curImage.overallDimensions(),
+ curImage.mipMapped(0),
+ GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
+ curImage.overallColorType(),
+ curImage.overallAlphaType(),
+ curImage.refOverallColorSpace(),
+ PromiseImageCallbackContext::PromiseImageFulfillProc,
+ PromiseImageCallbackContext::PromiseImageReleaseProc,
+ (void*)curImage.refCallbackContext(0).release());
curImage.callbackContext(0)->wasAddedToImage();
}
- perRecorderContext->fPromiseImages->push_back(image);
+ procContext->fPromiseImages->push_back(image);
SkASSERT(image);
return image;
}
diff --git a/tools/DDLPromiseImageHelper.h b/tools/DDLPromiseImageHelper.h
index cd65a7f..bdeaa75 100644
--- a/tools/DDLPromiseImageHelper.h
+++ b/tools/DDLPromiseImageHelper.h
@@ -119,7 +119,7 @@
void deleteAllFromGPU(SkTaskGroup*, GrDirectContext*);
// reinflate a deflated SKP, replacing all the indices with promise images.
- sk_sp<SkPicture> reinflateSKP(SkDeferredDisplayListRecorder*,
+ sk_sp<SkPicture> reinflateSKP(sk_sp<GrContextThreadSafeProxy>,
SkData* compressedPicture,
SkTArray<sk_sp<SkImage>>* promiseImages) const;
@@ -140,8 +140,7 @@
uint32_t originalUniqueID() const { return fOriginalUniqueID; }
bool isYUV() const { return fYUVAPixmaps.isValid(); }
- int overallWidth() const { return fImageInfo.width(); }
- int overallHeight() const { return fImageInfo.height(); }
+ SkISize overallDimensions() const { return fImageInfo.dimensions(); }
SkColorType overallColorType() const { return fImageInfo.colorType(); }
SkAlphaType overallAlphaType() const { return fImageInfo.alphaType(); }
sk_sp<SkColorSpace> refOverallColorSpace() const { return fImageInfo.refColorSpace(); }
@@ -212,12 +211,10 @@
sk_sp<PromiseImageCallbackContext> fCallbackContexts[SkYUVAInfo::kMaxPlanes];
};
- // 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;
+ struct DeserialImageProcContext {
+ sk_sp<GrContextThreadSafeProxy> fThreadSafeProxy;
+ const DDLPromiseImageHelper* fHelper;
+ SkTArray<sk_sp<SkImage>>* fPromiseImages;
};
static void CreateBETexturesForPromiseImage(GrDirectContext*, PromiseImageInfo*);
diff --git a/tools/DDLTileHelper.cpp b/tools/DDLTileHelper.cpp
index 1ebf6f4..7122bc1 100644
--- a/tools/DDLTileHelper.cpp
+++ b/tools/DDLTileHelper.cpp
@@ -43,66 +43,25 @@
DDLTileHelper::TileData::TileData() {}
DDLTileHelper::TileData::~TileData() {}
-void DDLTileHelper::TileData::createTileSpecificSKP(SkData* compressedPictureData,
- const DDLPromiseImageHelper& helper) {
- if (!this->initialized()) {
- return;
- }
-
- SkASSERT(!fReconstitutedPicture);
-
- auto recordingChar = fPlaybackChar.createResized(fClip.width(), fClip.height());
- SkASSERT(recordingChar.isValid());
-
- // 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(recordingChar);
-
- fReconstitutedPicture = helper.reinflateSKP(&recorder, compressedPictureData, &fPromiseImages);
-
- auto ddl = recorder.detach();
- if (ddl && ddl->priv().numRenderTasks()) {
- // TODO: remove this once skbug.com/8424 is fixed. If the DDL resulting from the
- // reinflation of the SKPs contains opsTasks that means some image subset operation
- // created a draw.
- fReconstitutedPicture.reset();
- }
-}
-
-void DDLTileHelper::TileData::createDDL() {
- if (!this->initialized()) {
- return;
- }
-
- SkASSERT(!fDisplayList && fReconstitutedPicture);
+void DDLTileHelper::TileData::createDDL(const SkPicture* picture) {
+ SkASSERT(!fDisplayList && picture);
auto recordingChar = fPlaybackChar.createResized(fClip.width(), fClip.height());
SkASSERT(recordingChar.isValid());
SkDeferredDisplayListRecorder recorder(recordingChar);
- // DDL TODO: the DDLRecorder's GrContext isn't initialized until getCanvas is called.
+ // DDL TODO: the DDLRecorder's rContext isn't initialized until getCanvas is called.
// Maybe set it up in the ctor?
SkCanvas* recordingCanvas = 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) {
- if (fPromiseImages[i]->isTextureBacked()) {
- auto rContext = recordingCanvas->recordingContext();
- SkImage_GpuBase* gpuImage = (SkImage_GpuBase*) fPromiseImages[i].get();
- gpuImage->resetContext(sk_ref_sp(rContext));
- }
- }
-
// We always record the DDL in the (0,0) .. (clipWidth, clipHeight) coordinates
recordingCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height()));
recordingCanvas->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.
- recordingCanvas->drawPicture(fReconstitutedPicture);
+ recordingCanvas->drawPicture(picture);
fDisplayList = recorder.detach();
}
@@ -120,7 +79,8 @@
continue;
}
- sk_sp<SkImage> promiseImage = tile->makePromiseImageForDst(&recorder);
+ sk_sp<SkImage> promiseImage = tile->makePromiseImageForDst(
+ recordingCanvas->recordingContext()->threadSafeProxy());
SkRect dstRect = SkRect::Make(tile->clipRect());
SkIRect srcRect = tile->clipRect();
@@ -150,7 +110,7 @@
}
}
-sk_sp<SkSurface> DDLTileHelper::TileData::makeWrappedTileDest(GrRecordingContext* context) {
+sk_sp<SkSurface> DDLTileHelper::TileData::makeWrappedTileDest(GrRecordingContext* rContext) {
SkASSERT(fCallbackContext && fCallbackContext->promiseImageTexture());
auto promiseImageTexture = fCallbackContext->promiseImageTexture();
@@ -161,7 +121,7 @@
// 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,
+ return SkSurface::MakeFromBackendTexture(rContext,
promiseImageTexture->backendTexture(),
fPlaybackChar.origin(),
fPlaybackChar.sampleCount(),
@@ -170,10 +130,11 @@
&fPlaybackChar.surfaceProps());
}
-void DDLTileHelper::TileData::drawSKPDirectly(GrRecordingContext* context) {
- SkASSERT(!fDisplayList && !fTileSurface && fReconstitutedPicture);
+void DDLTileHelper::TileData::drawSKPDirectly(GrDirectContext* dContext,
+ const SkPicture* picture) {
+ SkASSERT(!fDisplayList && !fTileSurface && picture);
- fTileSurface = this->makeWrappedTileDest(context);
+ fTileSurface = this->makeWrappedTileDest(dContext);
if (fTileSurface) {
SkCanvas* tileCanvas = fTileSurface->getCanvas();
@@ -181,7 +142,7 @@
tileCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height()));
tileCanvas->translate(-fClip.fLeft, -fClip.fTop);
- tileCanvas->drawPicture(fReconstitutedPicture);
+ tileCanvas->drawPicture(picture);
// We can't snap an image here bc, since we're using wrapped backend textures for the
// surfaces, that would incur a copy.
@@ -208,22 +169,22 @@
}
sk_sp<SkImage> DDLTileHelper::TileData::makePromiseImageForDst(
- SkDeferredDisplayListRecorder* recorder) {
+ sk_sp<GrContextThreadSafeProxy> threadSafeProxy) {
SkASSERT(fCallbackContext);
// The promise image gets a ref on the promise callback context
sk_sp<SkImage> promiseImage =
- recorder->makePromiseTexture(fCallbackContext->backendFormat(),
- this->paddedRectSize().width(),
- this->paddedRectSize().height(),
- GrMipmapped::kNo,
- GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin,
- fPlaybackChar.colorType(),
- kPremul_SkAlphaType,
- fPlaybackChar.refColorSpace(),
- PromiseImageCallbackContext::PromiseImageFulfillProc,
- PromiseImageCallbackContext::PromiseImageReleaseProc,
- (void*)this->refCallbackContext().release());
+ SkImage::MakePromiseTexture(std::move(threadSafeProxy),
+ fCallbackContext->backendFormat(),
+ this->paddedRectSize(),
+ GrMipmapped::kNo,
+ GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin,
+ fPlaybackChar.colorType(),
+ kPremul_SkAlphaType,
+ fPlaybackChar.refColorSpace(),
+ PromiseImageCallbackContext::PromiseImageFulfillProc,
+ PromiseImageCallbackContext::PromiseImageReleaseProc,
+ (void*)this->refCallbackContext().release());
fCallbackContext->wasAddedToImage();
return promiseImage;
@@ -297,22 +258,26 @@
}
}
-void DDLTileHelper::createSKPPerTile(SkData* compressedPictureData,
- const DDLPromiseImageHelper& helper) {
- for (int i = 0; i < this->numTiles(); ++i) {
- fTiles[i].createTileSpecificSKP(compressedPictureData, helper);
- }
+void DDLTileHelper::createSKP(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
+ SkData* compressedPictureData,
+ const DDLPromiseImageHelper& helper) {
+ SkASSERT(!fReconstitutedPicture);
+
+ fReconstitutedPicture = helper.reinflateSKP(std::move(threadSafeProxy), compressedPictureData,
+ &fPromiseImages);
}
void DDLTileHelper::createDDLsInParallel() {
#if 1
- SkTaskGroup().batch(this->numTiles(), [&](int i) { fTiles[i].createDDL(); });
+ SkTaskGroup().batch(this->numTiles(), [&](int i) {
+ fTiles[i].createDDL(fReconstitutedPicture.get());
+ });
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();
+ fTiles[i].createDDL(fReconstitutedPicture.get());
}
this->createComposeDDL();
#endif
@@ -349,8 +314,8 @@
// schedule gpu-thread processing of the DDL
// Note: a finer grained approach would be add a scheduling task which would evaluate
// which DDLs were ready to be rendered based on their prerequisites
- recordingTaskGroup->add([tile, gpuTaskGroup, dContext]() {
- tile->createDDL();
+ recordingTaskGroup->add([this, tile, gpuTaskGroup, dContext]() {
+ tile->createDDL(fReconstitutedPicture.get());
gpuTaskGroup->add([dContext, tile]() {
do_gpu_stuff(dContext, tile);
@@ -372,7 +337,7 @@
// Only called from skpbench
void DDLTileHelper::interleaveDDLCreationAndDraw(GrDirectContext* direct) {
for (int i = 0; i < this->numTiles(); ++i) {
- fTiles[i].createDDL();
+ fTiles[i].createDDL(fReconstitutedPicture.get());
fTiles[i].draw(direct);
}
}
@@ -380,7 +345,7 @@
// Only called from skpbench
void DDLTileHelper::drawAllTilesDirectly(GrDirectContext* context) {
for (int i = 0; i < this->numTiles(); ++i) {
- fTiles[i].drawSKPDirectly(context);
+ fTiles[i].drawSKPDirectly(context, fReconstitutedPicture.get());
}
}
diff --git a/tools/DDLTileHelper.h b/tools/DDLTileHelper.h
index dee5eeb..8d893be 100644
--- a/tools/DDLTileHelper.h
+++ b/tools/DDLTileHelper.h
@@ -12,6 +12,7 @@
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSurfaceCharacterization.h"
+#include "src/core/SkSpan.h"
class DDLPromiseImageHelper;
class PromiseImageCallbackContext;
@@ -40,13 +41,8 @@
const SkIRect& clip,
const SkIRect& paddingOutsets);
- // Convert the compressedPictureData into an SkPicture replacing each image-index
- // with a promise image.
- void createTileSpecificSKP(SkData* compressedPictureData,
- const DDLPromiseImageHelper& helper);
-
// Create the DDL for this tile (i.e., fill in 'fDisplayList').
- void createDDL();
+ void createDDL(const SkPicture*);
void dropDDL() { fDisplayList.reset(); }
@@ -56,7 +52,7 @@
// Just draw the re-inflated per-tile SKP directly into this tile w/o going through a DDL
// first. This is used for determining the overhead of using DDLs (i.e., it replaces
// a 'createDDL' and 'draw' pair.
- void drawSKPDirectly(GrRecordingContext*);
+ void drawSKPDirectly(GrDirectContext*, const SkPicture*);
// Replay the recorded DDL into the tile surface - filling in 'fBackendTexture'.
void draw(GrDirectContext*);
@@ -73,7 +69,7 @@
SkDeferredDisplayList* ddl() { return fDisplayList.get(); }
- sk_sp<SkImage> makePromiseImageForDst(SkDeferredDisplayListRecorder*);
+ sk_sp<SkImage> makePromiseImageForDst(sk_sp<GrContextThreadSafeProxy>);
void dropCallbackContext() { fCallbackContext.reset(); }
static void CreateBackendTexture(GrDirectContext*, TileData*);
@@ -99,9 +95,6 @@
// 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
sk_sp<SkDeferredDisplayList> fDisplayList;
};
@@ -111,7 +104,11 @@
int numDivisions,
bool addRandomPaddingToDst);
- void createSKPPerTile(SkData* compressedPictureData, const DDLPromiseImageHelper&);
+ // TODO: Move this to PromiseImageHelper and have one method that does all the work and
+ // returns the shared SkP.
+ void createSKP(sk_sp<GrContextThreadSafeProxy>,
+ SkData* compressedPictureData,
+ const DDLPromiseImageHelper&);
void kickOffThreadedWork(SkTaskGroup* recordingTaskGroup,
SkTaskGroup* gpuTaskGroup,
@@ -151,6 +148,9 @@
sk_sp<SkDeferredDisplayList> fComposeDDL;
const SkSurfaceCharacterization fDstCharacterization;
+ sk_sp<SkPicture> fReconstitutedPicture;
+ SkTArray<sk_sp<SkImage>> fPromiseImages; // All the promise images in the
+ // reconstituted picture
};
#endif
diff --git a/tools/skpbench/skpbench.cpp b/tools/skpbench/skpbench.cpp
index 34ab43b..49fbb92 100644
--- a/tools/skpbench/skpbench.cpp
+++ b/tools/skpbench/skpbench.cpp
@@ -276,7 +276,7 @@
tiles.createBackendTextures(nullptr, context);
- tiles.createSKPPerTile(compressedPictureData.get(), promiseImageHelper);
+ tiles.createSKP(context->threadSafeProxy(), compressedPictureData.get(), promiseImageHelper);
// In comparable modes, there is no GPU thread. The following pointers are all null.
// Otherwise, we transfer testContext onto the GPU thread until after the bench.