Revert "Revert "Distinguish between "flushed" and "finished" idle state callbacks on GrTexture.""
This reverts commit 88b8d1124b
.
Bug: skia:8800
Change-Id: I27f5da73b651b91af0c5440557f5986e493a1559
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/199080
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
aea65338f3
commit
e80b809e8a
@ -49,17 +49,31 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
/** See addIdleProc. */
|
||||
enum class IdleState {
|
||||
kFlushed,
|
||||
kFinished
|
||||
};
|
||||
/**
|
||||
* 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()).
|
||||
* 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.
|
||||
*/
|
||||
virtual void addIdleProc(sk_sp<GrRefCntedCallback> callback) {
|
||||
callback->addChild(std::move(fIdleCallback));
|
||||
fIdleCallback = std::move(callback);
|
||||
virtual void addIdleProc(sk_sp<GrRefCntedCallback> 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<GrRefCntedCallback>(callback, context), state);
|
||||
}
|
||||
|
||||
/** Access methods that are only to be used within Skia code. */
|
||||
@ -71,11 +85,11 @@ protected:
|
||||
|
||||
virtual bool onStealBackendTexture(GrBackendTexture*, SkImage::BackendTextureReleaseProc*) = 0;
|
||||
|
||||
sk_sp<GrRefCntedCallback> fIdleCallback;
|
||||
SkTArray<sk_sp<GrRefCntedCallback>> fIdleProcs;
|
||||
|
||||
void willRemoveLastRefOrPendingIO() override {
|
||||
// We're about to be idle in the resource cache. Do our part to trigger the idle callback.
|
||||
fIdleCallback.reset();
|
||||
// We're about to be idle in the resource cache. Do our part to trigger the idle callbacks.
|
||||
fIdleProcs.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -1481,8 +1481,7 @@ static inline GrPixelConfig GrColorTypeToPixelConfig(GrColorType config,
|
||||
}
|
||||
|
||||
/**
|
||||
* Ref-counted object that calls a callback from its destructor. These can be chained together. Any
|
||||
* owner can cancel calling the callback via abandon().
|
||||
* Ref-counted object that calls a callback from its destructor.
|
||||
*/
|
||||
class GrRefCntedCallback : public SkRefCnt {
|
||||
public:
|
||||
@ -1494,28 +1493,9 @@ public:
|
||||
}
|
||||
~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<GrRefCntedCallback> next) {
|
||||
if (!fNext) {
|
||||
fNext = std::move(next);
|
||||
return;
|
||||
}
|
||||
fNext->addChild(std::move(next));
|
||||
}
|
||||
|
||||
Context context() const { return fReleaseCtx; }
|
||||
|
||||
private:
|
||||
sk_sp<GrRefCntedCallback> fNext;
|
||||
Callback fReleaseProc;
|
||||
Context fReleaseCtx;
|
||||
};
|
||||
|
@ -270,28 +270,37 @@ void GrVkImage::Resource::freeGPUData(GrVkGpu* gpu) const {
|
||||
GrVkMemory::FreeImageMemory(gpu, isLinear, fAlloc);
|
||||
}
|
||||
|
||||
void GrVkImage::Resource::replaceIdleProc(
|
||||
GrVkTexture* owner, sk_sp<GrRefCntedCallback> idleCallback) const {
|
||||
fOwningTexture = owner;
|
||||
fIdleCallback = std::move(idleCallback);
|
||||
void GrVkImage::Resource::addIdleProc(GrVkTexture* owningTexture,
|
||||
sk_sp<GrRefCntedCallback> idleProc) const {
|
||||
SkASSERT(!fOwningTexture || fOwningTexture == owningTexture);
|
||||
fOwningTexture = owningTexture;
|
||||
fIdleProcs.push_back(std::move(idleProc));
|
||||
}
|
||||
|
||||
int GrVkImage::Resource::idleProcCnt() const { return fIdleProcs.count(); }
|
||||
|
||||
sk_sp<GrRefCntedCallback> 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 || !fIdleCallback) {
|
||||
if (--fNumCommandBufferOwners || !fIdleProcs.count()) {
|
||||
return;
|
||||
}
|
||||
if (fOwningTexture) {
|
||||
if (fOwningTexture->resourcePriv().hasRefOrPendingIO()) {
|
||||
// Wait for the texture to become idle in the cache to call the procs.
|
||||
return;
|
||||
}
|
||||
fOwningTexture->removeIdleProc();
|
||||
fOwningTexture->callIdleProcsOnBehalfOfResource();
|
||||
} else {
|
||||
fIdleProcs.reset();
|
||||
}
|
||||
fIdleCallback.reset();
|
||||
}
|
||||
|
||||
void GrVkImage::BorrowedResource::freeGPUData(GrVkGpu* gpu) const {
|
||||
|
@ -187,12 +187,15 @@ private:
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
void replaceIdleProc(GrVkTexture* owner, sk_sp<GrRefCntedCallback>) const;
|
||||
void addIdleProc(GrVkTexture*, sk_sp<GrRefCntedCallback>) const;
|
||||
int idleProcCnt() const;
|
||||
sk_sp<GrRefCntedCallback> idleProc(int) const;
|
||||
void resetIdleProcs() const;
|
||||
void removeOwningTexture() const;
|
||||
|
||||
/**
|
||||
@ -225,7 +228,7 @@ private:
|
||||
GrVkAlloc fAlloc;
|
||||
VkImageTiling fImageTiling;
|
||||
mutable int fNumCommandBufferOwners = 0;
|
||||
mutable sk_sp<GrRefCntedCallback> fIdleCallback;
|
||||
mutable SkTArray<sk_sp<GrRefCntedCallback>> fIdleProcs;
|
||||
mutable GrVkTexture* fOwningTexture = nullptr;
|
||||
|
||||
typedef GrVkResource INHERITED;
|
||||
|
@ -120,11 +120,11 @@ GrVkTexture::~GrVkTexture() {
|
||||
}
|
||||
|
||||
void GrVkTexture::onRelease() {
|
||||
// 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.
|
||||
// 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.
|
||||
if (this->hasResource() && this->resource()->isOwnedByCommandBuffer()) {
|
||||
fIdleCallback.reset();
|
||||
this->removeFinishIdleProcs();
|
||||
}
|
||||
|
||||
// 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 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.
|
||||
// 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.
|
||||
if (this->hasResource() && this->resource()->isOwnedByCommandBuffer()) {
|
||||
fIdleCallback.reset();
|
||||
this->removeFinishIdleProcs();
|
||||
}
|
||||
|
||||
// we create this and don't hand it off, so we should always destroy it
|
||||
@ -169,25 +169,70 @@ const GrVkImageView* GrVkTexture::textureView() {
|
||||
return fTextureView;
|
||||
}
|
||||
|
||||
void GrVkTexture::addIdleProc(sk_sp<GrRefCntedCallback> callback) {
|
||||
INHERITED::addIdleProc(callback);
|
||||
if (auto* resource = this->resource()) {
|
||||
resource->replaceIdleProc(this, fIdleCallback);
|
||||
void GrVkTexture::addIdleProc(sk_sp<GrRefCntedCallback> 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));
|
||||
}
|
||||
#endif
|
||||
fIdleProcs.reset();
|
||||
this->resource()->resetIdleProcs();
|
||||
}
|
||||
|
||||
void GrVkTexture::willRemoveLastRefOrPendingIO() {
|
||||
if (!fIdleCallback) {
|
||||
if (!fIdleProcs.count()) {
|
||||
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;
|
||||
if (resource) {
|
||||
if (resource->isOwnedByCommandBuffer()) {
|
||||
return;
|
||||
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);
|
||||
}
|
||||
resource->replaceIdleProc(this, nullptr);
|
||||
}
|
||||
fIdleCallback.reset();
|
||||
}
|
||||
|
||||
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<GrRefCntedCallback>> 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]);
|
||||
}
|
||||
}
|
||||
SkASSERT(resourceIdx == resource->idleProcCnt());
|
||||
fIdleProcs = procsToKeep;
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ public:
|
||||
|
||||
const GrVkImageView* textureView();
|
||||
|
||||
void addIdleProc(sk_sp<GrRefCntedCallback>) override;
|
||||
void removeIdleProc() { fIdleCallback.reset(); }
|
||||
void addIdleProc(sk_sp<GrRefCntedCallback>, IdleState) override;
|
||||
void callIdleProcsOnBehalfOfResource();
|
||||
|
||||
protected:
|
||||
GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, const GrVkImageInfo&, sk_sp<GrVkImageLayout>,
|
||||
@ -71,6 +71,8 @@ private:
|
||||
this->setResourceRelease(std::move(releaseHelper));
|
||||
}
|
||||
|
||||
void removeFinishIdleProcs();
|
||||
|
||||
const GrVkImageView* fTextureView;
|
||||
|
||||
typedef GrTexture INHERITED;
|
||||
|
@ -426,10 +426,8 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
|
||||
PromiseImageTextureDoneProc doneProc,
|
||||
PromiseImageTextureContext context,
|
||||
GrPixelConfig config)
|
||||
: fFulfillProc(fulfillProc), fConfig(config) {
|
||||
auto doneHelper = sk_make_sp<GrRefCntedCallback>(doneProc, context);
|
||||
fIdleCallback = sk_make_sp<GrRefCntedCallback>(releaseProc, context);
|
||||
fIdleCallback->addChild(std::move(doneHelper));
|
||||
: fFulfillProc(fulfillProc), fReleaseProc(releaseProc), fConfig(config) {
|
||||
fDoneCallback = sk_make_sp<GrRefCntedCallback>(doneProc, context);
|
||||
}
|
||||
PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
|
||||
PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
|
||||
@ -444,14 +442,8 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
|
||||
}
|
||||
|
||||
~PromiseLazyInstantiateCallback() {
|
||||
if (fIdleCallback) {
|
||||
SkASSERT(!fTexture);
|
||||
// We were never fulfilled. Pass false so done proc is still called.
|
||||
fIdleCallback->abandon();
|
||||
}
|
||||
// Our destructor can run on any thread. We trigger the unref of fTexture by message.
|
||||
if (fTexture) {
|
||||
SkASSERT(!fIdleCallback);
|
||||
SkMessageBus<GrGpuResourceFreedMessage>::Post({fTexture, fTextureContextID});
|
||||
}
|
||||
}
|
||||
@ -462,22 +454,19 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
|
||||
if (fTexture) {
|
||||
return sk_ref_sp(fTexture);
|
||||
}
|
||||
SkASSERT(fIdleCallback);
|
||||
PromiseImageTextureContext textureContext = fIdleCallback->context();
|
||||
SkASSERT(fDoneCallback);
|
||||
PromiseImageTextureContext textureContext = fDoneCallback->context();
|
||||
sk_sp<SkPromiseImageTexture> 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<GrRefCntedCallback>(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<GrTexture>();
|
||||
}
|
||||
|
||||
auto backendTexture = promiseTexture->backendTexture();
|
||||
backendTexture.fConfig = fConfig;
|
||||
if (!backendTexture.isValid()) {
|
||||
fIdleCallback.reset();
|
||||
return sk_sp<GrTexture>();
|
||||
}
|
||||
|
||||
@ -500,11 +489,11 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
|
||||
kRead_GrIOType))) {
|
||||
tex->resourcePriv().setUniqueKey(key);
|
||||
} else {
|
||||
fIdleCallback.reset();
|
||||
return sk_sp<GrTexture>();
|
||||
}
|
||||
}
|
||||
tex->addIdleProc(std::move(fIdleCallback));
|
||||
tex->addIdleProc(std::move(releaseCallback), GrTexture::IdleState::kFinished);
|
||||
tex->addIdleProc(std::move(fDoneCallback), GrTexture::IdleState::kFinished);
|
||||
promiseTexture->addKeyToInvalidate(tex->getContext()->priv().contextID(), key);
|
||||
fTexture = tex.get();
|
||||
// We need to hold on to the GrTexture in case our proxy gets reinstantiated. However,
|
||||
@ -518,10 +507,11 @@ sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
|
||||
}
|
||||
|
||||
private:
|
||||
PromiseImageTextureFulfillProc fFulfillProc;
|
||||
PromiseImageTextureReleaseProc fReleaseProc;
|
||||
sk_sp<GrRefCntedCallback> fDoneCallback;
|
||||
GrTexture* fTexture = nullptr;
|
||||
uint32_t fTextureContextID = SK_InvalidUniqueID;
|
||||
sk_sp<GrRefCntedCallback> fIdleCallback;
|
||||
PromiseImageTextureFulfillProc fFulfillProc;
|
||||
GrPixelConfig fConfig;
|
||||
} callback(fulfillProc, releaseProc, doneProc, textureContext, config);
|
||||
|
||||
|
@ -424,8 +424,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<GrTexture> texture = m(context);
|
||||
texture->addIdleProc(
|
||||
sk_make_sp<GrRefCntedCallback>(proc, new Context{&idleIDs, num}));
|
||||
texture->addIdleProc(proc, new Context{&idleIDs, num},
|
||||
GrTexture::IdleState::kFinished);
|
||||
keyAdder(texture.get());
|
||||
return texture;
|
||||
};
|
||||
@ -608,12 +608,15 @@ 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}) {
|
||||
auto idleTexture = idleMaker(context, false);
|
||||
auto otherTexture = otherMaker(context, false);
|
||||
otherTexture->ref();
|
||||
idleTexture->addIdleProc(sk_make_sp<GrRefCntedCallback>(idleProc, otherTexture.get()));
|
||||
otherTexture.reset();
|
||||
idleTexture.reset();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -627,25 +630,27 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcFlushTest, reporter, contextInfo) {
|
||||
auto idleProc = [](void* context) { reinterpret_cast<GrContext*>(context)->flush(); };
|
||||
|
||||
for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
|
||||
auto idleTexture = idleMaker(context, false);
|
||||
idleTexture->addIdleProc(sk_make_sp<GrRefCntedCallback>(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<uint32_t[]> 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);
|
||||
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<uint32_t[]> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,17 +660,59 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcRerefTest, reporter, contextInfo) {
|
||||
auto idleProc = [](void* texture) { reinterpret_cast<GrTexture*>(texture)->ref(); };
|
||||
// release proc to check whether the texture was released or not.
|
||||
auto releaseProc = [](void* isReleased) { *reinterpret_cast<bool*>(isReleased) = true; };
|
||||
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<GrRefCntedCallback>(idleProc, idleTexture.get()));
|
||||
idleTexture->setRelease(releaseProc, &isReleased);
|
||||
auto* raw = idleTexture.get();
|
||||
idleTexture.reset();
|
||||
REPORTER_ASSERT(reporter, !isReleased);
|
||||
raw->unref();
|
||||
REPORTER_ASSERT(reporter, isReleased);
|
||||
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<uint32_t*>(flags) |= kFlushFlag; };
|
||||
auto finishProc = [](void* flags) { *static_cast<uint32_t*>(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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user