From 88b8d1124b7280d379f7545eda4b9097a4d8a292 Mon Sep 17 00:00:00 2001 From: Brian Salomon Date: Thu, 7 Mar 2019 15:25:34 +0000 Subject: [PATCH] Revert "Distinguish between "flushed" and "finished" idle state callbacks on GrTexture." This reverts commit 9ac040700659cefff3c9a7ec6d4ada98948c307c. Reason for revert: Breaking DDL Win10 skpbench bot Original change's description: > Distinguish between "flushed" and "finished" idle state callbacks on GrTexture. > > This is necessary to convert the promise image API to call Release when all > work is flushed and Done when all work is complete (future work). > > Change-Id: I9745952bb0978ca2aaa79aeed460730b2fea856e > Bug: skia:8800 > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/197163 > Commit-Queue: Brian Salomon > Reviewed-by: Robert Phillips TBR=egdaniel@google.com,bsalomon@google.com,robertphillips@google.com # Not skipping CQ checks because original CL landed > 1 day ago. Bug: skia:8800 Change-Id: I5e6c4ea072beb4fb67a53d2ea2b007a7d201799d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/198603 Reviewed-by: Brian Salomon Commit-Queue: Brian Salomon --- include/gpu/GrTexture.h | 38 ++++------ include/private/GrTypesPriv.h | 22 +++++- src/gpu/vk/GrVkImage.cpp | 23 ++---- src/gpu/vk/GrVkImage.h | 15 ++-- src/gpu/vk/GrVkTexture.cpp | 81 +++++----------------- src/gpu/vk/GrVkTexture.h | 6 +- src/image/SkImage_GpuBase.cpp | 29 +++++--- tests/GrSurfaceTest.cpp | 127 +++++++++++----------------------- 8 files changed, 126 insertions(+), 215 deletions(-) diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h index b5e7ff143247d..4a7935f680623 100644 --- a/include/gpu/GrTexture.h +++ b/include/gpu/GrTexture.h @@ -49,31 +49,17 @@ class SK_API GrTexture : virtual public GrSurface { } #endif - /** See addIdleProc. */ - enum class IdleState { - kFlushed, - kFinished - }; /** - * Installs a proc on this texture. It will be called when the texture becomes "idle". There - * are two types of idle states as indicated by IdleState. For managed backends (e.g. GL where - * a driver typically handles CPU/GPU synchronization of resource access) there is no difference - * between the two. They both mean "all work related to the resource has been flushed to the - * backend API and the texture is not owned outside the resource cache". - * - * If the API is unmanaged (e.g. Vulkan) then kFinished has the additional constraint that the - * work flushed to the GPU is finished. + * Installs a proc on this texture. It will be called when the texture becomes "idle". Idle is + * defined to mean that the texture has no refs or pending IOs and that GPU I/O operations on + * the texture are completed if the backend API disallows deletion of a texture before such + * operations occur (e.g. Vulkan). After the idle proc is called it is removed. The idle proc + * will always be called before the texture is destroyed, even in unusual shutdown scenarios + * (e.g. GrContext::abandonContext()). */ - virtual void addIdleProc(sk_sp idleProc, IdleState) { - // This is the default implementation for the managed case where the IdleState can be - // ignored. Unmanaged backends, e.g. Vulkan, must override this to consider IdleState. - fIdleProcs.push_back(std::move(idleProc)); - } - /** Helper version of addIdleProc that creates the ref-counted wrapper. */ - void addIdleProc(GrRefCntedCallback::Callback callback, - GrRefCntedCallback::Context context, - IdleState state) { - this->addIdleProc(sk_make_sp(callback, context), state); + virtual void addIdleProc(sk_sp callback) { + callback->addChild(std::move(fIdleCallback)); + fIdleCallback = std::move(callback); } /** Access methods that are only to be used within Skia code. */ @@ -85,11 +71,11 @@ class SK_API GrTexture : virtual public GrSurface { virtual bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) = 0; - SkTArray> fIdleProcs; + sk_sp fIdleCallback; void willRemoveLastRefOrPendingIO() override { - // We're about to be idle in the resource cache. Do our part to trigger the idle callbacks. - fIdleProcs.reset(); + // We're about to be idle in the resource cache. Do our part to trigger the idle callback. + fIdleCallback.reset(); } private: diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h index f6c3590f6dc60..d6ea362762687 100644 --- a/include/private/GrTypesPriv.h +++ b/include/private/GrTypesPriv.h @@ -1555,7 +1555,8 @@ static inline GrPixelConfig GrColorTypeToPixelConfig(GrColorType config, } /** - * Ref-counted object that calls a callback from its destructor. + * Ref-counted object that calls a callback from its destructor. These can be chained together. Any + * owner can cancel calling the callback via abandon(). */ class GrRefCntedCallback : public SkRefCnt { public: @@ -1567,9 +1568,28 @@ class GrRefCntedCallback : public SkRefCnt { } ~GrRefCntedCallback() override { fReleaseProc ? fReleaseProc(fReleaseCtx) : void(); } + /** + * After abandon is called the release proc will no longer be called in the destructor. This + * does not recurse on child release procs or unref them. + */ + void abandon() { + fReleaseProc = nullptr; + fReleaseCtx = nullptr; + } + + /** Adds another GrRefCntedCallback that this will unref in its destructor. */ + void addChild(sk_sp next) { + if (!fNext) { + fNext = std::move(next); + return; + } + fNext->addChild(std::move(next)); + } + Context context() const { return fReleaseCtx; } private: + sk_sp fNext; Callback fReleaseProc; Context fReleaseCtx; }; diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp index d90873ba9c55b..524c33e5675c8 100644 --- a/src/gpu/vk/GrVkImage.cpp +++ b/src/gpu/vk/GrVkImage.cpp @@ -270,37 +270,28 @@ void GrVkImage::Resource::freeGPUData(GrVkGpu* gpu) const { GrVkMemory::FreeImageMemory(gpu, isLinear, fAlloc); } -void GrVkImage::Resource::addIdleProc(GrVkTexture* owningTexture, - sk_sp idleProc) const { - SkASSERT(!fOwningTexture || fOwningTexture == owningTexture); - fOwningTexture = owningTexture; - fIdleProcs.push_back(std::move(idleProc)); +void GrVkImage::Resource::replaceIdleProc( + GrVkTexture* owner, sk_sp idleCallback) const { + fOwningTexture = owner; + fIdleCallback = std::move(idleCallback); } -int GrVkImage::Resource::idleProcCnt() const { return fIdleProcs.count(); } - -sk_sp GrVkImage::Resource::idleProc(int i) const { return fIdleProcs[i]; } - -void GrVkImage::Resource::resetIdleProcs() const { fIdleProcs.reset(); } - void GrVkImage::Resource::removeOwningTexture() const { fOwningTexture = nullptr; } void GrVkImage::Resource::notifyAddedToCommandBuffer() const { ++fNumCommandBufferOwners; } void GrVkImage::Resource::notifyRemovedFromCommandBuffer() const { SkASSERT(fNumCommandBufferOwners); - if (--fNumCommandBufferOwners || !fIdleProcs.count()) { + if (--fNumCommandBufferOwners || !fIdleCallback) { return; } if (fOwningTexture) { if (fOwningTexture->resourcePriv().hasRefOrPendingIO()) { - // Wait for the texture to become idle in the cache to call the procs. return; } - fOwningTexture->callIdleProcsOnBehalfOfResource(); - } else { - fIdleProcs.reset(); + fOwningTexture->removeIdleProc(); } + fIdleCallback.reset(); } void GrVkImage::BorrowedResource::freeGPUData(GrVkGpu* gpu) const { diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h index ec3c15d8fc5e2..2da325d1c2f49 100644 --- a/src/gpu/vk/GrVkImage.h +++ b/src/gpu/vk/GrVkImage.h @@ -187,15 +187,12 @@ class GrVkImage : SkNoncopyable { } /** - * These are used to coordinate calling the "finished" idle procs between the GrVkTexture - * and the Resource. If the GrVkTexture becomes purgeable and if there are no command - * buffers referring to the Resource then it calls the procs. Otherwise, the Resource calls - * them when the last command buffer reference goes away and the GrVkTexture is purgeable. + * These are used to coordinate calling the idle proc between the GrVkTexture and the + * Resource. If the GrVkTexture becomes purgeable and if there are no command buffers + * referring to the Resource then it calls the proc. Otherwise, the Resource calls it + * when the last command buffer reference goes away and the GrVkTexture is purgeable. */ - void addIdleProc(GrVkTexture*, sk_sp) const; - int idleProcCnt() const; - sk_sp idleProc(int) const; - void resetIdleProcs() const; + void replaceIdleProc(GrVkTexture* owner, sk_sp) const; void removeOwningTexture() const; /** @@ -228,7 +225,7 @@ class GrVkImage : SkNoncopyable { GrVkAlloc fAlloc; VkImageTiling fImageTiling; mutable int fNumCommandBufferOwners = 0; - mutable SkTArray> fIdleProcs; + mutable sk_sp fIdleCallback; mutable GrVkTexture* fOwningTexture = nullptr; typedef GrVkResource INHERITED; diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp index 8610a864f57d3..74f9b15688b2f 100644 --- a/src/gpu/vk/GrVkTexture.cpp +++ b/src/gpu/vk/GrVkTexture.cpp @@ -120,11 +120,11 @@ GrVkTexture::~GrVkTexture() { } void GrVkTexture::onRelease() { - // We're about to be severed from our GrVkResource. If there are "finish" idle procs we have to - // decide who will handle them. If the resource is still tied to a command buffer we let it - // handle them. Otherwise, we handle them. + // We're about to be severed from our GrVkResource. If there is an idle proc we have to decide + // who will handle it. If the resource is still tied to a command buffer we let it handle it. + // Otherwise, we handle it. if (this->hasResource() && this->resource()->isOwnedByCommandBuffer()) { - this->removeFinishIdleProcs(); + fIdleCallback.reset(); } // we create this and don't hand it off, so we should always destroy it @@ -139,11 +139,11 @@ void GrVkTexture::onRelease() { } void GrVkTexture::onAbandon() { - // We're about to be severed from our GrVkResource. If there are "finish" idle procs we have to - // decide who will handle them. If the resource is still tied to a command buffer we let it - // handle them. Otherwise, we handle them. + // We're about to be severed from our GrVkResource. If there is an idle proc we have to decide + // who will handle it. If the resource is still tied to a command buffer we let it handle it. + // Otherwise, we handle it. if (this->hasResource() && this->resource()->isOwnedByCommandBuffer()) { - this->removeFinishIdleProcs(); + fIdleCallback.reset(); } // we create this and don't hand it off, so we should always destroy it @@ -169,70 +169,25 @@ const GrVkImageView* GrVkTexture::textureView() { return fTextureView; } -void GrVkTexture::addIdleProc(sk_sp idleProc, IdleState type) { - INHERITED::addIdleProc(idleProc, type); - if (type == IdleState::kFinished) { - if (auto* resource = this->resource()) { - resource->addIdleProc(this, std::move(idleProc)); - } - } -} - -void GrVkTexture::callIdleProcsOnBehalfOfResource() { - // If we got here then the resource is being removed from its last command buffer and the - // texture is idle in the cache. Any kFlush idle procs should already have been called. So - // the texture and resource should have the same set of procs. - SkASSERT(this->resource()); - SkASSERT(this->resource()->idleProcCnt() == fIdleProcs.count()); -#ifdef SK_DEBUG - for (int i = 0; i < fIdleProcs.count(); ++i) { - SkASSERT(fIdleProcs[i] == this->resource()->idleProc(i)); +void GrVkTexture::addIdleProc(sk_sp callback) { + INHERITED::addIdleProc(callback); + if (auto* resource = this->resource()) { + resource->replaceIdleProc(this, fIdleCallback); } -#endif - fIdleProcs.reset(); - this->resource()->resetIdleProcs(); } void GrVkTexture::willRemoveLastRefOrPendingIO() { - if (!fIdleProcs.count()) { + if (!fIdleCallback) { return; } // This is called when the GrTexture is purgeable. However, we need to check whether the // Resource is still owned by any command buffers. If it is then it will call the proc. auto* resource = this->hasResource() ? this->resource() : nullptr; - bool callFinishProcs = !resource || !resource->isOwnedByCommandBuffer(); - if (callFinishProcs) { - // Everything must go! - fIdleProcs.reset(); - resource->resetIdleProcs(); - } else { - // The procs that should be called on flush but not finish are those that are owned - // by the GrVkTexture and not the Resource. We do this by copying the resource's array - // and thereby dropping refs to procs we own but the resource does not. - SkASSERT(resource); - fIdleProcs.reset(resource->idleProcCnt()); - for (int i = 0; i < fIdleProcs.count(); ++i) { - fIdleProcs[i] = resource->idleProc(i); - } - } -} - -void GrVkTexture::removeFinishIdleProcs() { - // This should only be called by onRelease/onAbandon when we have already checked for a - // resource. - const auto* resource = this->resource(); - SkASSERT(resource); - SkSTArray<4, sk_sp> procsToKeep; - int resourceIdx = 0; - // The idle procs that are common between the GrVkTexture and its Resource should be found in - // the same order. - for (int i = 0; i < fIdleProcs.count(); ++i) { - if (fIdleProcs[i] == resource->idleProc(resourceIdx)) { - ++resourceIdx; - } else { - procsToKeep.push_back(fIdleProcs[i]); + if (resource) { + if (resource->isOwnedByCommandBuffer()) { + return; } + resource->replaceIdleProc(this, nullptr); } - SkASSERT(resourceIdx == resource->idleProcCnt()); - fIdleProcs = procsToKeep; + fIdleCallback.reset(); } diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h index 50d4cae740f16..ffd4a3fd083c0 100644 --- a/src/gpu/vk/GrVkTexture.h +++ b/src/gpu/vk/GrVkTexture.h @@ -38,8 +38,8 @@ class GrVkTexture : public GrTexture, public virtual GrVkImage { const GrVkImageView* textureView(); - void addIdleProc(sk_sp, IdleState) override; - void callIdleProcsOnBehalfOfResource(); + void addIdleProc(sk_sp) override; + void removeIdleProc() { fIdleCallback.reset(); } protected: GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, const GrVkImageInfo&, sk_sp, @@ -71,8 +71,6 @@ class GrVkTexture : public GrTexture, public virtual GrVkImage { this->setResourceRelease(std::move(releaseHelper)); } - void removeFinishIdleProcs(); - const GrVkImageView* fTextureView; typedef GrTexture INHERITED; diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp index 2618dd4276eb9..ec2e6519eff6a 100644 --- a/src/image/SkImage_GpuBase.cpp +++ b/src/image/SkImage_GpuBase.cpp @@ -426,8 +426,10 @@ sk_sp SkImage_GpuBase::MakePromiseImageLazyProxy( PromiseImageTextureDoneProc doneProc, PromiseImageTextureContext context, GrPixelConfig config) - : fFulfillProc(fulfillProc), fReleaseProc(releaseProc), fConfig(config) { - fDoneCallback = sk_make_sp(doneProc, context); + : fFulfillProc(fulfillProc), fConfig(config) { + auto doneHelper = sk_make_sp(doneProc, context); + fIdleCallback = sk_make_sp(releaseProc, context); + fIdleCallback->addChild(std::move(doneHelper)); } PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default; PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) { @@ -441,20 +443,30 @@ sk_sp SkImage_GpuBase::MakePromiseImageLazyProxy( return *this; } + ~PromiseLazyInstantiateCallback() { + if (fIdleCallback) { + // We were never fulfilled. Pass false so done proc is still called. + fIdleCallback->abandon(); + } + } + sk_sp operator()(GrResourceProvider* resourceProvider) { - SkASSERT(fDoneCallback); - PromiseImageTextureContext textureContext = fDoneCallback->context(); + SkASSERT(fIdleCallback); + PromiseImageTextureContext textureContext = fIdleCallback->context(); sk_sp promiseTexture = fFulfillProc(textureContext); // From here on out our contract is that the release proc must be called, even if // the return from fulfill was invalid or we fail for some other reason. - auto releaseCallback = sk_make_sp(fReleaseProc, textureContext); if (!promiseTexture) { + // Make sure we explicitly reset this because our destructor assumes a non-null + // fIdleCallback means fulfill was never called. + fIdleCallback.reset(); return sk_sp(); } auto backendTexture = promiseTexture->backendTexture(); backendTexture.fConfig = fConfig; if (!backendTexture.isValid()) { + fIdleCallback.reset(); return sk_sp(); } @@ -477,19 +489,18 @@ sk_sp SkImage_GpuBase::MakePromiseImageLazyProxy( kRead_GrIOType))) { tex->resourcePriv().setUniqueKey(key); } else { + fIdleCallback.reset(); return sk_sp(); } } - tex->addIdleProc(std::move(releaseCallback), GrTexture::IdleState::kFinished); - tex->addIdleProc(std::move(fDoneCallback), GrTexture::IdleState::kFinished); + tex->addIdleProc(std::move(fIdleCallback)); promiseTexture->addKeyToInvalidate(tex->getContext()->priv().contextID(), key); return std::move(tex); } private: + sk_sp fIdleCallback; PromiseImageTextureFulfillProc fFulfillProc; - PromiseImageTextureReleaseProc fReleaseProc; - sk_sp fDoneCallback; GrPixelConfig fConfig; } callback(fulfillProc, releaseProc, doneProc, textureContext, config); diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp index 745f1deb0ac9a..9035cd7e56f68 100644 --- a/tests/GrSurfaceTest.cpp +++ b/tests/GrSurfaceTest.cpp @@ -423,8 +423,8 @@ DEF_GPUTEST(TextureIdleProcTest, reporter, options) { // Makes a texture, possibly adds a key, and sets the callback. auto make = [&m, &keyAdder, &proc, &idleIDs](GrContext* context, int num) { sk_sp texture = m(context); - texture->addIdleProc(proc, new Context{&idleIDs, num}, - GrTexture::IdleState::kFinished); + texture->addIdleProc( + sk_make_sp(proc, new Context{&idleIDs, num})); keyAdder(texture.get()); return texture; }; @@ -585,15 +585,12 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcCacheManipulationTest, reporter, con for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) { for (const auto& otherMaker : {make_wrapped_texture, make_normal_texture}) { - for (auto idleState : - {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) { - auto idleTexture = idleMaker(context, false); - auto otherTexture = otherMaker(context, false); - otherTexture->ref(); - idleTexture->addIdleProc(idleProc, otherTexture.get(), idleState); - otherTexture.reset(); - idleTexture.reset(); - } + auto idleTexture = idleMaker(context, false); + auto otherTexture = otherMaker(context, false); + otherTexture->ref(); + idleTexture->addIdleProc(sk_make_sp(idleProc, otherTexture.get())); + otherTexture.reset(); + idleTexture.reset(); } } } @@ -607,27 +604,25 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcFlushTest, reporter, contextInfo) { auto idleProc = [](void* context) { reinterpret_cast(context)->flush(); }; for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) { - for (auto idleState : {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) { - auto idleTexture = idleMaker(context, false); - idleTexture->addIdleProc(idleProc, context, idleState); - auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType); - auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 1, nullptr); - // We'll draw two images to the canvas. One is a normal texture-backed image. The other - // is a wrapped-texture backed image. - surf->getCanvas()->clear(SK_ColorWHITE); - auto img1 = surf->makeImageSnapshot(); - auto gpu = context->priv().getGpu(); - std::unique_ptr pixels(new uint32_t[info.width() * info.height()]); - auto backendTexture = gpu->createTestingOnlyBackendTexture( - pixels.get(), info.width(), info.height(), kRGBA_8888_SkColorType, false, - GrMipMapped::kNo); - auto img2 = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, - info.colorType(), info.alphaType(), nullptr); - surf->getCanvas()->drawImage(std::move(img1), 0, 0); - surf->getCanvas()->drawImage(std::move(img2), 1, 1); - idleTexture.reset(); - gpu->deleteTestingOnlyBackendTexture(backendTexture); - } + auto idleTexture = idleMaker(context, false); + idleTexture->addIdleProc(sk_make_sp(idleProc, context)); + auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 1, nullptr); + // We'll draw two images to the canvas. One is a normal texture-backed image. The other is + // a wrapped-texture backed image. + surf->getCanvas()->clear(SK_ColorWHITE); + auto img1 = surf->makeImageSnapshot(); + auto gpu = context->priv().getGpu(); + std::unique_ptr pixels(new uint32_t[info.width() * info.height()]); + auto backendTexture = gpu->createTestingOnlyBackendTexture( + pixels.get(), info.width(), info.height(), kRGBA_8888_SkColorType, false, + GrMipMapped::kNo); + auto img2 = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, + info.colorType(), info.alphaType(), nullptr); + surf->getCanvas()->drawImage(std::move(img1), 0, 0); + surf->getCanvas()->drawImage(std::move(img2), 1, 1); + idleTexture.reset(); + gpu->deleteTestingOnlyBackendTexture(backendTexture); } } @@ -637,59 +632,17 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcRerefTest, reporter, contextInfo) { auto idleProc = [](void* texture) { reinterpret_cast(texture)->ref(); }; // release proc to check whether the texture was released or not. auto releaseProc = [](void* isReleased) { *reinterpret_cast(isReleased) = true; }; - for (auto idleState : {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) { - bool isReleased = false; - auto idleTexture = make_normal_texture(context, false); - // This test assumes the texture won't be cached (or else the release proc doesn't get - // called). - idleTexture->resourcePriv().removeScratchKey(); - context->flush(); - idleTexture->addIdleProc(idleProc, idleTexture.get(), idleState); - idleTexture->setRelease(releaseProc, &isReleased); - auto* raw = idleTexture.get(); - idleTexture.reset(); - REPORTER_ASSERT(reporter, !isReleased); - raw->unref(); - REPORTER_ASSERT(reporter, isReleased); - } -} - -DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleStateTest, reporter, contextInfo) { - GrContext* context = contextInfo.grContext(); - for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) { - auto idleTexture = idleMaker(context, false); - - uint32_t flags = 0; - static constexpr uint32_t kFlushFlag = 0x1; - static constexpr uint32_t kFinishFlag = 0x2; - auto flushProc = [](void* flags) { *static_cast(flags) |= kFlushFlag; }; - auto finishProc = [](void* flags) { *static_cast(flags) |= kFinishFlag; }; - idleTexture->addIdleProc(flushProc, &flags, GrTexture::IdleState::kFlushed); - idleTexture->addIdleProc(finishProc, &flags, GrTexture::IdleState::kFinished); - - // Insert a copy from idleTexture to another texture so that we have some queued IO on - // idleTexture. - auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped( - std::move(idleTexture), kTopLeft_GrSurfaceOrigin); - SkImageInfo info = SkImageInfo::Make(proxy->width(), proxy->height(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType); - auto rt = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, nullptr); - auto rtc = rt->getCanvas()->internal_private_accessTopLayerRenderTargetContext(); - context->flush(); - rtc->copy(proxy.get()); - proxy.reset(); - REPORTER_ASSERT(reporter, flags == 0); - - // After a flush we expect idleTexture to have reached the kFlushed state on all backends. - // On "managed" backends we expect it to reach kFinished as well. On Vulkan, the only - // current "unmanaged" backend, we *may* need a sync to reach kFinished. - context->flush(); - if (contextInfo.backend() == kVulkan_GrBackend) { - REPORTER_ASSERT(reporter, flags & kFlushFlag); - } else { - REPORTER_ASSERT(reporter, flags == (kFlushFlag | kFinishFlag)); - } - context->priv().getGpu()->testingOnly_flushGpuAndSync(); - REPORTER_ASSERT(reporter, flags == (kFlushFlag | kFinishFlag)); - } + bool isReleased = false; + auto idleTexture = make_normal_texture(context, false); + // This test assumes the texture won't be cached (or else the release proc doesn't get + // called). + idleTexture->resourcePriv().removeScratchKey(); + context->flush(); + idleTexture->addIdleProc(sk_make_sp(idleProc, idleTexture.get())); + idleTexture->setRelease(releaseProc, &isReleased); + auto* raw = idleTexture.get(); + idleTexture.reset(); + REPORTER_ASSERT(reporter, !isReleased); + raw->unref(); + REPORTER_ASSERT(reporter, isReleased); }