Replace bool purgeImmediately with enum GrWrapCacheable.

Store budgeted status as a tri-state enum on GrGpuResource:
kBudgeted
kUnbudgetedCacheable
kUnbudgetedUncacheable

Uncacheable vs. Cacheable captures the current distinction between
wrapped resources created with purgeImmediately or !purgeImmediately.

Non-wrapped and unbudgeted resources are all kUnbudgetedCacheable to
match current behavior.

This change just introduces the new types. No behavior is changed.

Bug: chromium:922851

Change-Id: Ic2387bf321cf9b56b4c9ffd9dbef8ade60f9cb98
Reviewed-on: https://skia-review.googlesource.com/c/185003
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Brian Salomon 2019-01-24 15:58:58 -05:00 committed by Skia Commit-Bot
parent 607c44fa25
commit fa2ebeaf33
36 changed files with 187 additions and 160 deletions

View File

@ -141,7 +141,6 @@ private:
*/
class SK_API GrGpuResource : public GrIORef<GrGpuResource> {
public:
/**
* Tests whether a object has been abandoned or released. All objects will
* be in this state after their creating GrContext is destroyed or has
@ -265,7 +264,7 @@ protected:
// This must be called by every GrGpuObject that references any wrapped backend objects. It
// should be called once the object is fully initialized (i.e. only from the constructors of the
// final class).
void registerWithCacheWrapped(bool purgeImmediately = false);
void registerWithCacheWrapped(GrWrapCacheable = GrWrapCacheable::kYes);
GrGpuResource(GrGpu*);
virtual ~GrGpuResource();
@ -349,11 +348,10 @@ private:
// This is not ref'ed but abandon() or release() will be called before the GrGpu object
// is destroyed. Those calls set will this to NULL.
GrGpu* fGpu;
mutable size_t fGpuMemorySize;
mutable size_t fGpuMemorySize = kInvalidGpuMemorySize;
SkBudgeted fBudgeted;
bool fShouldPurgeImmediately;
bool fRefsWrappedObjects;
GrBudgetedType fBudgetedType = GrBudgetedType::kUnbudgetedCacheable;
bool fRefsWrappedObjects = false;
const UniqueID fUniqueID;
typedef GrIORef<GrGpuResource> INHERITED;

View File

@ -197,6 +197,36 @@ enum GrWrapOwnership {
kAdopt_GrWrapOwnership,
};
enum class GrWrapCacheable : bool {
/**
* The wrapped resource will be removed from the cache as soon as it becomes purgeable. It may
* still be assigned and found by a unique key, but the presence of the key will not be used to
* keep the resource alive when it has no references.
*/
kNo = false,
/**
* The wrapped resource is allowed to remain in the GrResourceCache when it has no references
* but has a unique key. Such resources should only be given unique keys when it is known that
* the key will eventually be removed from the resource or invalidated via the message bus.
*/
kYes = true
};
enum class GrBudgetedType : uint8_t {
/** The resource is budgeted and is subject to purging under budget pressure. */
kBudgeted,
/**
* The resource is unbudgeted and is purged as soon as it has no refs regardless of whether
* it has a unique or scratch key.
*/
kUnbudgetedUncacheable,
/**
* The resource is unbudgeted and is allowed to remain in the cache with no refs if it
* has a unique key. Scratch keys are ignored.
*/
kUnbudgetedCacheable,
};
/**
* Clips are composed from these objects.
*/

View File

@ -167,8 +167,10 @@ sk_sp<GrTextureProxy> GrBackendTextureImageGenerator::onGenerateTexture(
// informs us that the context is done with it. This is unfortunate - we'll have
// two texture objects referencing the same GPU object. However, no client can
// ever see the original texture, so this should be safe.
// We make the texture uncacheable so that the release proc is called ASAP.
tex = resourceProvider->wrapBackendTexture(
backendTexture, kBorrow_GrWrapOwnership, kRead_GrIOType, true);
backendTexture, kBorrow_GrWrapOwnership, kRead_GrIOType,
GrWrapCacheable::kNo);
if (!tex) {
return sk_sp<GrTexture>();
}

View File

@ -148,9 +148,8 @@ sk_sp<GrTexture> GrGpu::createTexture(const GrSurfaceDesc& desc, SkBudgeted budg
}
sk_sp<GrTexture> GrGpu::wrapBackendTexture(const GrBackendTexture& backendTex,
GrWrapOwnership ownership,
GrIOType ioType,
bool purgeImmediately) {
GrWrapOwnership ownership, GrWrapCacheable cacheable,
GrIOType ioType) {
SkASSERT(ioType != kWrite_GrIOType);
this->handleDirtyContext();
SkASSERT(this->caps());
@ -161,7 +160,7 @@ sk_sp<GrTexture> GrGpu::wrapBackendTexture(const GrBackendTexture& backendTex,
backendTex.height() > this->caps()->maxTextureSize()) {
return nullptr;
}
return this->onWrapBackendTexture(backendTex, ownership, ioType, purgeImmediately);
return this->onWrapBackendTexture(backendTex, ownership, cacheable, ioType);
}
sk_sp<GrTexture> GrGpu::wrapRenderableBackendTexture(const GrBackendTexture& backendTex,

View File

@ -104,8 +104,8 @@ public:
/**
* Implements GrResourceProvider::wrapBackendTexture
*/
sk_sp<GrTexture> wrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrIOType,
bool purgeImmediately);
sk_sp<GrTexture> wrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrWrapCacheable,
GrIOType);
/**
* Implements GrResourceProvider::wrapRenderableBackendTexture
@ -458,7 +458,7 @@ private:
const GrMipLevel texels[], int mipLevelCount) = 0;
virtual sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership,
GrIOType, bool purgeImmediately) = 0;
GrWrapCacheable, GrIOType) = 0;
virtual sk_sp<GrTexture> onWrapRenderableBackendTexture(const GrBackendTexture&,
int sampleCnt,
GrWrapOwnership) = 0;

View File

@ -21,28 +21,23 @@ static inline GrResourceCache* get_resource_cache(GrGpu* gpu) {
return gpu->getContext()->contextPriv().getResourceCache();
}
GrGpuResource::GrGpuResource(GrGpu* gpu)
: fGpu(gpu)
, fGpuMemorySize(kInvalidGpuMemorySize)
, fBudgeted(SkBudgeted::kNo)
, fShouldPurgeImmediately(false)
, fRefsWrappedObjects(false)
, fUniqueID(CreateUniqueID()) {
GrGpuResource::GrGpuResource(GrGpu* gpu) : fGpu(gpu), fUniqueID(CreateUniqueID()) {
SkDEBUGCODE(fCacheArrayIndex = -1);
}
void GrGpuResource::registerWithCache(SkBudgeted budgeted) {
SkASSERT(fBudgeted == SkBudgeted::kNo);
SkASSERT(!fShouldPurgeImmediately);
fBudgeted = budgeted;
SkASSERT(fBudgetedType == GrBudgetedType::kUnbudgetedCacheable);
fBudgetedType = budgeted == SkBudgeted::kYes ? GrBudgetedType::kBudgeted
: GrBudgetedType::kUnbudgetedCacheable;
this->computeScratchKey(&fScratchKey);
get_resource_cache(fGpu)->resourceAccess().insertResource(this);
}
void GrGpuResource::registerWithCacheWrapped(bool purgeImmediately) {
SkASSERT(fBudgeted == SkBudgeted::kNo);
// Currently resources referencing wrapped objects are not budgeted.
fShouldPurgeImmediately = purgeImmediately;
void GrGpuResource::registerWithCacheWrapped(GrWrapCacheable wrapType) {
SkASSERT(fBudgetedType == GrBudgetedType::kUnbudgetedCacheable);
// Resources referencing wrapped objects are never budgeted. They may be cached or uncached.
fBudgetedType = wrapType == GrWrapCacheable::kNo ? GrBudgetedType::kUnbudgetedUncacheable
: GrBudgetedType::kUnbudgetedCacheable;
fRefsWrappedObjects = true;
get_resource_cache(fGpu)->resourceAccess().insertResource(this);
}
@ -137,7 +132,8 @@ void GrGpuResource::setUniqueKey(const GrUniqueKey& key) {
// resources are a special case: the unique keys give us a weak ref so that we can reuse the
// same resource (rather than re-wrapping). When a wrapped resource is no longer referenced,
// it will always be released - it is never converted to a scratch resource.
if (SkBudgeted::kNo == this->resourcePriv().isBudgeted() && !this->fRefsWrappedObjects) {
if (this->resourcePriv().budgetedType() != GrBudgetedType::kBudgeted &&
!this->fRefsWrappedObjects) {
return;
}
@ -190,18 +186,21 @@ void GrGpuResource::removeScratchKey() {
}
void GrGpuResource::makeBudgeted() {
if (!this->wasDestroyed() && SkBudgeted::kNo == fBudgeted) {
// We should never make a wrapped resource budgeted.
SkASSERT(!fRefsWrappedObjects);
// Only wrapped resources can be in the kUnbudgetedUncacheable state.
SkASSERT(fBudgetedType != GrBudgetedType::kUnbudgetedUncacheable);
if (!this->wasDestroyed() && fBudgetedType == GrBudgetedType::kUnbudgetedCacheable) {
// Currently resources referencing wrapped objects are not budgeted.
SkASSERT(!fRefsWrappedObjects);
fBudgeted = SkBudgeted::kYes;
fBudgetedType = GrBudgetedType::kBudgeted;
get_resource_cache(fGpu)->resourceAccess().didChangeBudgetStatus(this);
}
}
void GrGpuResource::makeUnbudgeted() {
if (!this->wasDestroyed() && SkBudgeted::kYes == fBudgeted &&
if (!this->wasDestroyed() && fBudgetedType == GrBudgetedType::kBudgeted &&
!fUniqueKey.isValid()) {
fBudgeted = SkBudgeted::kNo;
fBudgetedType = GrBudgetedType::kUnbudgetedCacheable;
get_resource_cache(fGpu)->resourceAccess().didChangeBudgetStatus(this);
}
}

View File

@ -26,14 +26,9 @@ private:
*/
bool isScratch() const {
return !fResource->getUniqueKey().isValid() && fResource->fScratchKey.isValid() &&
SkBudgeted::kYes == fResource->resourcePriv().isBudgeted();
GrBudgetedType::kBudgeted == fResource->resourcePriv().budgetedType();
}
/**
* Even if the resource has a unique key should we still try to purge it as soon as possible.
*/
bool shouldPurgeImmediately() const { return fResource->fShouldPurgeImmediately; }
/**
* Called by GrResourceCache when a resource becomes purgeable regardless of whether the cache
* has decided to keep the resource ot purge it immediately.

View File

@ -43,12 +43,13 @@ public:
void makeUnbudgeted() { fResource->makeUnbudgeted(); }
/**
* Does the resource count against the resource budget?
* Get the resource's budgeted-type which indicates whether it counts against the resource cache
* budget and if not whether it is allowed to be cached.
*/
SkBudgeted isBudgeted() const {
bool ret = SkBudgeted::kYes == fResource->fBudgeted;
SkASSERT(ret || !fResource->getUniqueKey().isValid() || fResource->fRefsWrappedObjects);
return SkBudgeted(ret);
GrBudgetedType budgetedType() const {
SkASSERT(GrBudgetedType::kBudgeted == fResource->fBudgetedType ||
!fResource->getUniqueKey().isValid() || fResource->fRefsWrappedObjects);
return fResource->fBudgetedType;
}
/**

View File

@ -464,7 +464,8 @@ sk_sp<GrTextureProxy> GrProxyProvider::wrapBackendTexture(const GrBackendTexture
return nullptr;
}
sk_sp<GrTexture> tex = fResourceProvider->wrapBackendTexture(backendTex, ownership, ioType);
sk_sp<GrTexture> tex = fResourceProvider->wrapBackendTexture(backendTex, ownership, ioType,
GrWrapCacheable::kYes);
if (!tex) {
return nullptr;
}
@ -478,7 +479,7 @@ sk_sp<GrTextureProxy> GrProxyProvider::wrapBackendTexture(const GrBackendTexture
SkASSERT(!tex->asRenderTarget()); // Strictly a GrTexture
// Make sure we match how we created the proxy with SkBudgeted::kNo
SkASSERT(SkBudgeted::kNo == tex->resourcePriv().isBudgeted());
SkASSERT(GrBudgetedType::kBudgeted != tex->resourcePriv().budgetedType());
return sk_sp<GrTextureProxy>(new GrTextureProxy(std::move(tex), origin));
}
@ -508,7 +509,7 @@ sk_sp<GrTextureProxy> GrProxyProvider::wrapRenderableBackendTexture(
SkASSERT(tex->asRenderTarget()); // A GrTextureRenderTarget
// Make sure we match how we created the proxy with SkBudgeted::kNo
SkASSERT(SkBudgeted::kNo == tex->resourcePriv().isBudgeted());
SkASSERT(GrBudgetedType::kBudgeted != tex->resourcePriv().budgetedType());
return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(std::move(tex), origin));
}
@ -531,7 +532,7 @@ sk_sp<GrSurfaceProxy> GrProxyProvider::wrapBackendRenderTarget(
SkASSERT(!rt->asTexture()); // A GrRenderTarget that's not textureable
SkASSERT(!rt->getUniqueKey().isValid());
// Make sure we match how we created the proxy with SkBudgeted::kNo
SkASSERT(SkBudgeted::kNo == rt->resourcePriv().isBudgeted());
SkASSERT(GrBudgetedType::kBudgeted != rt->resourcePriv().budgetedType());
return sk_sp<GrRenderTargetProxy>(new GrRenderTargetProxy(std::move(rt), origin));
}
@ -555,7 +556,7 @@ sk_sp<GrSurfaceProxy> GrProxyProvider::wrapBackendTextureAsRenderTarget(
SkASSERT(!rt->asTexture()); // A GrRenderTarget that's not textureable
SkASSERT(!rt->getUniqueKey().isValid());
// This proxy should be unbudgeted because we're just wrapping an external resource
SkASSERT(SkBudgeted::kNo == rt->resourcePriv().isBudgeted());
SkASSERT(GrBudgetedType::kBudgeted != rt->resourcePriv().budgetedType());
return sk_sp<GrSurfaceProxy>(new GrRenderTargetProxy(std::move(rt), origin));
}
@ -580,7 +581,7 @@ sk_sp<GrRenderTargetProxy> GrProxyProvider::wrapVulkanSecondaryCBAsRenderTarget(
SkASSERT(!rt->asTexture()); // A GrRenderTarget that's not textureable
SkASSERT(!rt->getUniqueKey().isValid());
// This proxy should be unbudgeted because we're just wrapping an external resource
SkASSERT(SkBudgeted::kNo == rt->resourcePriv().isBudgeted());
SkASSERT(GrBudgetedType::kBudgeted != rt->resourcePriv().budgetedType());
// All Vulkan surfaces uses top left origins.
return sk_sp<GrRenderTargetProxy>(

View File

@ -266,9 +266,9 @@ sk_sp<GrSurface> GrResourceAllocator::findSurfaceFor(const GrSurfaceProxy* proxy
sk_sp<GrSurface> surface(fFreePool.findAndRemove(key, filter));
if (surface) {
if (SkBudgeted::kYes == proxy->isBudgeted() &&
SkBudgeted::kNo == surface->resourcePriv().isBudgeted()) {
GrBudgetedType::kBudgeted != surface->resourcePriv().budgetedType()) {
// This gets the job done but isn't quite correct. It would be better to try to
// match budgeted proxies w/ budgeted surface and unbudgeted w/ unbudgeted.
// match budgeted proxies w/ budgeted surfaces and unbudgeted w/ unbudgeted.
surface->resourcePriv().makeBudgeted();
}

View File

@ -125,7 +125,7 @@ void GrResourceCache::insertResource(GrGpuResource* resource) {
fHighWaterCount = SkTMax(this->getResourceCount(), fHighWaterCount);
fHighWaterBytes = SkTMax(fBytes, fHighWaterBytes);
#endif
if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
if (GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType()) {
++fBudgetedCount;
fBudgetedBytes += size;
TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
@ -159,7 +159,7 @@ void GrResourceCache::removeResource(GrGpuResource* resource) {
SkDEBUGCODE(--fCount;)
fBytes -= size;
if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
if (GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType()) {
--fBudgetedCount;
fBudgetedBytes -= size;
TRACE_COUNTER2("skia.gpu.cache", "skia budget", "used",
@ -423,10 +423,18 @@ void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t fla
{
SkScopeExit notifyPurgeable([resource] { resource->cacheAccess().becamePurgeable(); });
if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
auto budgetedType = resource->resourcePriv().budgetedType();
if (budgetedType == GrBudgetedType::kBudgeted) {
// Purge the resource immediately if we're over budget
// Also purge if the resource has neither a valid scratch key nor a unique key.
bool hasKey = resource->resourcePriv().getScratchKey().isValid() || hasUniqueKey;
if (!this->overBudget() && hasKey) {
return;
}
} else {
// We keep unbudgeted resources with a unique key in the purgable queue of the cache so
// they can be reused again by the image connected to the unique key.
if (hasUniqueKey && !resource->cacheAccess().shouldPurgeImmediately()) {
if (hasUniqueKey && budgetedType == GrBudgetedType::kUnbudgetedCacheable) {
return;
}
// Check whether this resource could still be used as a scratch resource.
@ -439,13 +447,6 @@ void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t fla
return;
}
}
} else {
// Purge the resource immediately if we're over budget
// Also purge if the resource has neither a valid scratch key nor a unique key.
bool hasKey = resource->resourcePriv().getScratchKey().isValid() || hasUniqueKey;
if (!this->overBudget() && hasKey) {
return;
}
}
}
SkDEBUGCODE(int beforeCount = this->getResourceCount();)
@ -462,7 +463,7 @@ void GrResourceCache::didChangeBudgetStatus(GrGpuResource* resource) {
size_t size = resource->gpuMemorySize();
if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
if (GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType()) {
++fBudgetedCount;
fBudgetedBytes += size;
#if GR_CACHE_STATS
@ -752,7 +753,7 @@ void GrResourceCache::validate() const {
SkASSERT(fScratchMap->countForKey(scratchKey));
SkASSERT(!resource->resourcePriv().refsWrappedObjects());
} else if (scratchKey.isValid()) {
SkASSERT(SkBudgeted::kNo == resource->resourcePriv().isBudgeted() ||
SkASSERT(GrBudgetedType::kBudgeted != resource->resourcePriv().budgetedType() ||
uniqueKey.isValid());
if (!uniqueKey.isValid()) {
++fCouldBeScratch;
@ -763,7 +764,7 @@ void GrResourceCache::validate() const {
if (uniqueKey.isValid()) {
++fContent;
SkASSERT(fUniqueHash->find(uniqueKey) == resource);
SkASSERT(SkBudgeted::kYes == resource->resourcePriv().isBudgeted() ||
SkASSERT(GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType() ||
resource->resourcePriv().refsWrappedObjects());
if (scratchKey.isValid()) {
@ -771,7 +772,7 @@ void GrResourceCache::validate() const {
}
}
if (SkBudgeted::kYes == resource->resourcePriv().isBudgeted()) {
if (GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType()) {
++fBudgetedCount;
fBudgetedBytes += resource->gpuMemorySize();
}

View File

@ -225,7 +225,7 @@ public:
if (resource->resourcePriv().refsWrappedObjects()) {
++fWrapped;
}
if (SkBudgeted::kNo == resource->resourcePriv().isBudgeted()) {
if (GrBudgetedType::kBudgeted != resource->resourcePriv().budgetedType()) {
fUnbudgetedSize += resource->gpuMemorySize();
}
}

View File

@ -236,12 +236,12 @@ sk_sp<GrTexture> GrResourceProvider::refScratchTexture(const GrSurfaceDesc& desc
sk_sp<GrTexture> GrResourceProvider::wrapBackendTexture(const GrBackendTexture& tex,
GrWrapOwnership ownership,
GrIOType ioType,
bool purgeImmediately) {
GrWrapCacheable cacheable) {
ASSERT_SINGLE_OWNER
if (this->isAbandoned()) {
return nullptr;
}
return fGpu->wrapBackendTexture(tex, ownership, ioType, purgeImmediately);
return fGpu->wrapBackendTexture(tex, ownership, cacheable, ioType);
}
sk_sp<GrTexture> GrResourceProvider::wrapRenderableBackendTexture(const GrBackendTexture& tex,

View File

@ -109,7 +109,7 @@ public:
sk_sp<GrTexture> wrapBackendTexture(const GrBackendTexture& tex,
GrWrapOwnership /* = kBorrow_GrWrapOwnership*/,
GrIOType,
bool purgeImmediately = false);
GrWrapCacheable = GrWrapCacheable::kYes);
/**
* This makes the backend texture be renderable. If sampleCnt is > 1 and the underlying API

View File

@ -89,7 +89,9 @@ GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, GrSurfaceOrigin origin,
, fHeight(fTarget->height())
, fOrigin(origin)
, fFit(fit)
, fBudgeted(fTarget->resourcePriv().isBudgeted())
, fBudgeted(fTarget->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted
? SkBudgeted::kYes
: SkBudgeted::kNo)
, fUniqueID(fTarget->uniqueID()) // Note: converting from unique resource ID to a proxy ID!
, fNeedsClear(false)
, fGpuMemorySize(kInvalidGpuMemorySize)

View File

@ -653,8 +653,8 @@ static bool check_backend_texture(const GrBackendTexture& backendTex, const GrGL
}
sk_sp<GrTexture> GrGLGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
GrWrapOwnership ownership, GrIOType ioType,
bool purgeImmediately) {
GrWrapOwnership ownership, GrWrapCacheable cacheable,
GrIOType ioType) {
GrGLTexture::IDDesc idDesc;
if (!check_backend_texture(backendTex, this->glCaps(), &idDesc)) {
return nullptr;
@ -678,8 +678,8 @@ sk_sp<GrTexture> GrGLGpu::onWrapBackendTexture(const GrBackendTexture& backendTe
GrMipMapsStatus mipMapsStatus = backendTex.hasMipMaps() ? GrMipMapsStatus::kValid
: GrMipMapsStatus::kNotAllocated;
auto texture = GrGLTexture::MakeWrapped(this, surfDesc, mipMapsStatus, idDesc, ioType,
purgeImmediately);
auto texture =
GrGLTexture::MakeWrapped(this, surfDesc, mipMapsStatus, idDesc, cacheable, ioType);
// We don't know what parameters are already set on wrapped textures.
texture->textureParamsModified();
return std::move(texture);

View File

@ -189,8 +189,8 @@ private:
GrBuffer* onCreateBuffer(size_t size, GrBufferType intendedType, GrAccessPattern,
const void* data) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrIOType,
bool purgeImmediately) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrWrapCacheable,
GrIOType) override;
sk_sp<GrTexture> onWrapRenderableBackendTexture(const GrBackendTexture&,
int sampleCnt,
GrWrapOwnership) override;

View File

@ -53,13 +53,12 @@ GrGLTexture::GrGLTexture(GrGLGpu* gpu, SkBudgeted budgeted, const GrSurfaceDesc&
}
}
GrGLTexture::GrGLTexture(GrGLGpu* gpu, Wrapped, const GrSurfaceDesc& desc,
GrMipMapsStatus mipMapsStatus, const IDDesc& idDesc, GrIOType ioType,
bool purgeImmediately)
GrGLTexture::GrGLTexture(GrGLGpu* gpu, const GrSurfaceDesc& desc, GrMipMapsStatus mipMapsStatus,
const IDDesc& idDesc, GrWrapCacheable cacheable, GrIOType ioType)
: GrSurface(gpu, desc)
, INHERITED(gpu, desc, TextureTypeFromTarget(idDesc.fInfo.fTarget), mipMapsStatus) {
this->init(desc, idDesc);
this->registerWithCacheWrapped(purgeImmediately);
this->registerWithCacheWrapped(cacheable);
if (ioType == kRead_GrIOType) {
this->setReadOnly();
}
@ -117,9 +116,8 @@ GrBackendFormat GrGLTexture::backendFormat() const {
sk_sp<GrGLTexture> GrGLTexture::MakeWrapped(GrGLGpu* gpu, const GrSurfaceDesc& desc,
GrMipMapsStatus mipMapsStatus, const IDDesc& idDesc,
GrIOType ioType, bool purgeImmediately) {
return sk_sp<GrGLTexture>(
new GrGLTexture(gpu, kWrapped, desc, mipMapsStatus, idDesc, ioType, purgeImmediately));
GrWrapCacheable cacheable, GrIOType ioType) {
return sk_sp<GrGLTexture>(new GrGLTexture(gpu, desc, mipMapsStatus, idDesc, cacheable, ioType));
}
bool GrGLTexture::onStealBackendTexture(GrBackendTexture* backendTexture,

View File

@ -111,7 +111,7 @@ public:
void baseLevelWasBoundToFBO() { fBaseLevelHasBeenBoundToFBO = true; }
static sk_sp<GrGLTexture> MakeWrapped(GrGLGpu*, const GrSurfaceDesc&, GrMipMapsStatus,
const IDDesc&, GrIOType, bool purgeImmediately);
const IDDesc&, GrWrapCacheable, GrIOType);
void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const override;
@ -119,10 +119,9 @@ protected:
// Constructor for subclasses.
GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, const IDDesc&, GrMipMapsStatus);
enum Wrapped { kWrapped };
// Constructor for instances wrapping backend objects.
GrGLTexture(GrGLGpu*, Wrapped, const GrSurfaceDesc&, GrMipMapsStatus, const IDDesc&, GrIOType,
bool purgeImmediately);
GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, GrMipMapsStatus, const IDDesc&, GrWrapCacheable,
GrIOType);
void init(const GrSurfaceDesc&, const IDDesc&);

View File

@ -107,8 +107,8 @@ sk_sp<GrTexture> GrMockGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgete
}
sk_sp<GrTexture> GrMockGpu::onWrapBackendTexture(const GrBackendTexture& tex,
GrWrapOwnership ownership, GrIOType ioType,
bool purgeImmediately) {
GrWrapOwnership ownership,
GrWrapCacheable wrapType, GrIOType ioType) {
GrSurfaceDesc desc;
desc.fWidth = tex.width();
desc.fHeight = tex.height();
@ -120,8 +120,7 @@ sk_sp<GrTexture> GrMockGpu::onWrapBackendTexture(const GrBackendTexture& tex,
GrMipMapsStatus mipMapsStatus = tex.hasMipMaps() ? GrMipMapsStatus::kValid
: GrMipMapsStatus::kNotAllocated;
return sk_sp<GrTexture>(new GrMockTexture(this, GrMockTexture::kWrapped, desc, mipMapsStatus,
info, ioType, purgeImmediately));
return sk_sp<GrTexture>(new GrMockTexture(this, desc, mipMapsStatus, info, wrapType, ioType));
}
sk_sp<GrTexture> GrMockGpu::onWrapRenderableBackendTexture(const GrBackendTexture& tex,

View File

@ -59,8 +59,8 @@ private:
sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc&, SkBudgeted, const GrMipLevel[],
int mipLevelCount) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrIOType,
bool purgeImmediately) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrWrapCacheable,
GrIOType) override;
sk_sp<GrTexture> onWrapRenderableBackendTexture(const GrBackendTexture&,
int sampleCnt,

View File

@ -22,14 +22,13 @@ public:
this->registerWithCache(budgeted);
}
enum Wrapped { kWrapped };
GrMockTexture(GrMockGpu* gpu, Wrapped, const GrSurfaceDesc& desc, GrMipMapsStatus mipMapsStatus,
const GrMockTextureInfo& info, GrIOType ioType, bool purgeImmediately)
GrMockTexture(GrMockGpu* gpu, const GrSurfaceDesc& desc, GrMipMapsStatus mipMapsStatus,
const GrMockTextureInfo& info, GrWrapCacheable cacheable, GrIOType ioType)
: GrMockTexture(gpu, desc, mipMapsStatus, info) {
if (ioType == kRead_GrIOType) {
this->setReadOnly();
}
this->registerWithCacheWrapped(purgeImmediately);
this->registerWithCacheWrapped(cacheable);
}
~GrMockTexture() override {}

View File

@ -132,8 +132,8 @@ private:
sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
const GrMipLevel texels[], int mipLevelCount) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrIOType,
bool purgeImmediately) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrWrapCacheable,
GrIOType) override;
sk_sp<GrTexture> onWrapRenderableBackendTexture(const GrBackendTexture&,
int sampleCnt,

View File

@ -342,8 +342,8 @@ static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture>
}
sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
GrWrapOwnership ownership, GrIOType ioType,
bool purgeImmediately) {
GrWrapOwnership ownership,
GrWrapCacheable cacheable, GrIOType ioType) {
id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
if (!mtlTexture) {
return nullptr;
@ -352,7 +352,7 @@ sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendT
GrSurfaceDesc surfDesc;
init_surface_desc(&surfDesc, mtlTexture, false, backendTex.config());
return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture, ioType, purgeImmediately);
return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture, cacheable, ioType);
}
sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,

View File

@ -22,7 +22,7 @@ public:
GrMipMapsStatus);
static sk_sp<GrMtlTexture> MakeWrappedTexture(GrMtlGpu*, const GrSurfaceDesc&, id<MTLTexture>,
GrIOType, bool purgeImmediately);
GrWrapCacheable, GrIOType);
~GrMtlTexture() override;
@ -88,7 +88,7 @@ private:
GrMipMapsStatus);
GrMtlTexture(GrMtlGpu*, Wrapped, const GrSurfaceDesc&, id<MTLTexture>, GrMipMapsStatus,
GrIOType, bool purgeImmediately);
GrWrapCacheable, GrIOType);
id<MTLTexture> fTexture;
sk_sp<GrReleaseProcHelper> fReleaseHelper;

View File

@ -28,8 +28,8 @@ GrMtlTexture::GrMtlTexture(GrMtlGpu* gpu,
const GrSurfaceDesc& desc,
id<MTLTexture> texture,
GrMipMapsStatus mipMapsStatus,
GrIOType ioType,
bool purgeImmediately)
GrWrapCacheable cacheable,
GrIOType ioType)
: GrSurface(gpu, desc)
, INHERITED(gpu, desc, GrTextureType::k2D, mipMapsStatus)
, fTexture(texture) {
@ -37,7 +37,7 @@ GrMtlTexture::GrMtlTexture(GrMtlGpu* gpu,
if (ioType == kRead_GrIOType) {
this->setReadOnly();
}
this->registerWithCacheWrapped(purgeImmediately);
this->registerWithCacheWrapped(cacheable);
}
GrMtlTexture::GrMtlTexture(GrMtlGpu* gpu,
@ -67,8 +67,8 @@ sk_sp<GrMtlTexture> GrMtlTexture::CreateNewTexture(GrMtlGpu* gpu, SkBudgeted bud
sk_sp<GrMtlTexture> GrMtlTexture::MakeWrappedTexture(GrMtlGpu* gpu,
const GrSurfaceDesc& desc,
id<MTLTexture> texture,
GrIOType ioType,
bool purgeImmediately) {
GrWrapCacheable cacheable,
GrIOType ioType) {
if (desc.fSampleCnt > 1) {
SkASSERT(false); // Currently we don't support msaa
return nullptr;
@ -77,8 +77,8 @@ sk_sp<GrMtlTexture> GrMtlTexture::MakeWrappedTexture(GrMtlGpu* gpu,
SkASSERT(MTLTextureUsageShaderRead & texture.usage);
GrMipMapsStatus mipMapsStatus = texture.mipmapLevelCount > 1 ? GrMipMapsStatus::kValid
: GrMipMapsStatus::kNotAllocated;
return sk_sp<GrMtlTexture>(new GrMtlTexture(gpu, kWrapped, desc, texture, mipMapsStatus, ioType,
purgeImmediately));
return sk_sp<GrMtlTexture>(new GrMtlTexture(gpu, kWrapped, desc, texture, mipMapsStatus,
cacheable, ioType));
}
GrMtlTexture::~GrMtlTexture() {

View File

@ -1022,8 +1022,8 @@ static bool check_image_info(const GrVkCaps& caps,
}
sk_sp<GrTexture> GrVkGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
GrWrapOwnership ownership, GrIOType ioType,
bool purgeImmediately) {
GrWrapOwnership ownership, GrWrapCacheable cacheable,
GrIOType ioType) {
GrVkImageInfo imageInfo;
if (!backendTex.getVkImageInfo(&imageInfo)) {
return nullptr;
@ -1042,8 +1042,8 @@ sk_sp<GrTexture> GrVkGpu::onWrapBackendTexture(const GrBackendTexture& backendTe
sk_sp<GrVkImageLayout> layout = backendTex.getGrVkImageLayout();
SkASSERT(layout);
return GrVkTexture::MakeWrappedTexture(this, surfDesc, ownership, ioType, purgeImmediately,
imageInfo, std::move(layout));
return GrVkTexture::MakeWrappedTexture(this, surfDesc, ownership, cacheable, ioType, imageInfo,
std::move(layout));
}
sk_sp<GrTexture> GrVkGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,

View File

@ -184,8 +184,8 @@ private:
sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc&, SkBudgeted, const GrMipLevel[],
int mipLevelCount) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrIOType,
bool purgeImmediately) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrWrapOwnership, GrWrapCacheable,
GrIOType) override;
sk_sp<GrTexture> onWrapRenderableBackendTexture(const GrBackendTexture&,
int sampleCnt,
GrWrapOwnership) override;

View File

@ -36,16 +36,10 @@ GrVkTexture::GrVkTexture(GrVkGpu* gpu,
}
}
GrVkTexture::GrVkTexture(GrVkGpu* gpu,
Wrapped,
const GrSurfaceDesc& desc,
const GrVkImageInfo& info,
sk_sp<GrVkImageLayout> layout,
const GrVkImageView* view,
GrMipMapsStatus mipMapsStatus,
GrBackendObjectOwnership ownership,
GrIOType ioType,
bool purgeImmediately)
GrVkTexture::GrVkTexture(GrVkGpu* gpu, const GrSurfaceDesc& desc, const GrVkImageInfo& info,
sk_sp<GrVkImageLayout> layout, const GrVkImageView* view,
GrMipMapsStatus mipMapsStatus, GrBackendObjectOwnership ownership,
GrWrapCacheable cacheable, GrIOType ioType)
: GrSurface(gpu, desc)
, GrVkImage(info, std::move(layout), ownership)
, INHERITED(gpu, desc, GrTextureType::k2D, mipMapsStatus)
@ -54,7 +48,7 @@ GrVkTexture::GrVkTexture(GrVkGpu* gpu,
if (ioType == kRead_GrIOType) {
this->setReadOnly();
}
this->registerWithCacheWrapped(purgeImmediately);
this->registerWithCacheWrapped(cacheable);
}
// Because this class is virtually derived from GrSurface we must explicitly call its constructor.
@ -96,11 +90,9 @@ sk_sp<GrVkTexture> GrVkTexture::MakeNewTexture(GrVkGpu* gpu, SkBudgeted budgeted
imageView, mipMapsStatus));
}
sk_sp<GrVkTexture> GrVkTexture::MakeWrappedTexture(GrVkGpu* gpu,
const GrSurfaceDesc& desc,
sk_sp<GrVkTexture> GrVkTexture::MakeWrappedTexture(GrVkGpu* gpu, const GrSurfaceDesc& desc,
GrWrapOwnership wrapOwnership,
GrIOType ioType,
bool purgeImmediately,
GrWrapCacheable cacheable, GrIOType ioType,
const GrVkImageInfo& info,
sk_sp<GrVkImageLayout> layout) {
// Wrapped textures require both image and allocation (because they can be mapped)
@ -118,9 +110,8 @@ sk_sp<GrVkTexture> GrVkTexture::MakeWrappedTexture(GrVkGpu* gpu,
GrBackendObjectOwnership ownership = kBorrow_GrWrapOwnership == wrapOwnership
? GrBackendObjectOwnership::kBorrowed : GrBackendObjectOwnership::kOwned;
return sk_sp<GrVkTexture>(new GrVkTexture(gpu, kWrapped, desc, info, std::move(layout),
imageView, mipMapsStatus, ownership, ioType,
purgeImmediately));
return sk_sp<GrVkTexture>(new GrVkTexture(gpu, desc, info, std::move(layout), imageView,
mipMapsStatus, ownership, cacheable, ioType));
}
GrVkTexture::~GrVkTexture() {

View File

@ -25,8 +25,8 @@ public:
GrMipMapsStatus);
static sk_sp<GrVkTexture> MakeWrappedTexture(GrVkGpu*, const GrSurfaceDesc&, GrWrapOwnership,
GrIOType, bool purgeImmediately,
const GrVkImageInfo&, sk_sp<GrVkImageLayout>);
GrWrapCacheable, GrIOType, const GrVkImageInfo&,
sk_sp<GrVkImageLayout>);
~GrVkTexture() override;
@ -62,13 +62,12 @@ protected:
}
private:
enum Wrapped { kWrapped };
GrVkTexture(GrVkGpu*, SkBudgeted, const GrSurfaceDesc&, const GrVkImageInfo&,
sk_sp<GrVkImageLayout> layout, const GrVkImageView* imageView,
GrMipMapsStatus);
GrVkTexture(GrVkGpu*, Wrapped, const GrSurfaceDesc&, const GrVkImageInfo&,
sk_sp<GrVkImageLayout> layout, const GrVkImageView* imageView, GrMipMapsStatus,
GrBackendObjectOwnership, GrIOType ioType, bool purgeImmediately);
GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, const GrVkImageInfo&, sk_sp<GrVkImageLayout>,
const GrVkImageView*, GrMipMapsStatus, GrBackendObjectOwnership, GrWrapCacheable,
GrIOType);
void becamePurgeable() override;

View File

@ -459,7 +459,12 @@ DEF_GPUTEST(TextureIdleProcTest, reporter, options) {
if (isRT) {
desc.fFlags = kRenderTarget_GrSurfaceFlag;
}
SkBudgeted budgeted(texture->resourcePriv().isBudgeted());
SkBudgeted budgeted;
if (texture->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted) {
budgeted = SkBudgeted::kYes;
} else {
budgeted = SkBudgeted::kNo;
}
auto proxy = context->contextPriv().proxyProvider()->createLazyProxy(
singleUseLazyCB, backendFormat, desc,
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,

View File

@ -51,7 +51,7 @@ void testing_only_texture_test(skiatest::Reporter* reporter, GrContext* context,
GrWrapOwnership::kAdopt_GrWrapOwnership);
} else {
wrappedTex = gpu->wrapBackendTexture(backendTex, GrWrapOwnership::kAdopt_GrWrapOwnership,
kRead_GrIOType, false);
GrWrapCacheable::kYes, kRead_GrIOType);
}
REPORTER_ASSERT(reporter, wrappedTex);

View File

@ -466,8 +466,9 @@ DEF_GPUTEST(LazyProxyDeinstantiateTest, reporter, /* options */) {
return sk_sp<GrTexture>();
}
sk_sp<GrTexture> texture = rp->wrapBackendTexture(
backendTex, kBorrow_GrWrapOwnership, kRead_GrIOType);
sk_sp<GrTexture> texture =
rp->wrapBackendTexture(backendTex, kBorrow_GrWrapOwnership,
kRead_GrIOType, GrWrapCacheable::kYes);
if (!texture) {
return sk_sp<GrTexture>();
}

View File

@ -670,7 +670,8 @@ void test_unbudgeted_to_scratch(skiatest::Reporter* reporter);
// Since this resource is unbudgeted, it should not be reachable as scratch.
REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
REPORTER_ASSERT(reporter, SkBudgeted::kNo == resource->resourcePriv().isBudgeted());
REPORTER_ASSERT(reporter, GrBudgetedType::kUnbudgetedCacheable ==
resource->resourcePriv().budgetedType());
REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(key, TestResource::kDefaultSize, GrResourceCache::ScratchFlags::kNone));
REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
@ -689,7 +690,8 @@ void test_unbudgeted_to_scratch(skiatest::Reporter* reporter);
REPORTER_ASSERT(reporter, resource);
REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
REPORTER_ASSERT(reporter, resource->cacheAccess().isScratch());
REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
REPORTER_ASSERT(reporter,
GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType());
if (0 == i) {
// If made unbudgeted, it should return to original state: ref'ed and unbudgeted. Try
@ -705,7 +707,8 @@ void test_unbudgeted_to_scratch(skiatest::Reporter* reporter);
REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
REPORTER_ASSERT(reporter, !resource->resourcePriv().getScratchKey().isValid());
REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
REPORTER_ASSERT(reporter,
GrBudgetedType::kBudgeted == resource->resourcePriv().budgetedType());
// now when it is unrefed it should die since it has no key.
resource->unref();
@ -1451,7 +1454,7 @@ static void test_abandoned(skiatest::Reporter* reporter) {
resource->abandon();
resource->resourcePriv().getScratchKey();
resource->resourcePriv().isBudgeted();
resource->resourcePriv().budgetedType();
resource->resourcePriv().makeBudgeted();
resource->resourcePriv().makeUnbudgeted();
resource->resourcePriv().removeScratchKey();

View File

@ -128,7 +128,7 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkTraceMemoryDump_unownedGLTexture, report
idDesc.fOwnership = GrBackendObjectOwnership::kBorrowed;
auto texture = GrGLTexture::MakeWrapped(gpu, desc, GrMipMapsStatus::kNotAllocated, idDesc,
kRead_GrIOType, false);
GrWrapCacheable::kYes, kRead_GrIOType);
ValidateMemoryDumps(reporter, context, texture->gpuMemorySize(), false /* isOwned */);
}

View File

@ -42,8 +42,8 @@ void wrap_tex_test(skiatest::Reporter* reporter, GrContext* context) {
GrVkImageInfo imageInfo;
SkAssertResult(origBackendTex.getVkImageInfo(&imageInfo));
sk_sp<GrTexture> tex =
gpu->wrapBackendTexture(origBackendTex, kBorrow_GrWrapOwnership, kRead_GrIOType, false);
sk_sp<GrTexture> tex = gpu->wrapBackendTexture(origBackendTex, kBorrow_GrWrapOwnership,
GrWrapCacheable::kYes, kRead_GrIOType);
REPORTER_ASSERT(reporter, tex);
// image is null
@ -52,9 +52,11 @@ void wrap_tex_test(skiatest::Reporter* reporter, GrContext* context) {
backendCopy.fImage = VK_NULL_HANDLE;
GrBackendTexture backendTex = GrBackendTexture(kW, kH, backendCopy);
backendTex.setPixelConfig(kPixelConfig);
tex = gpu->wrapBackendTexture(backendTex, kBorrow_GrWrapOwnership, kRead_GrIOType, false);
tex = gpu->wrapBackendTexture(backendTex, kBorrow_GrWrapOwnership, GrWrapCacheable::kYes,
kRead_GrIOType);
REPORTER_ASSERT(reporter, !tex);
tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership, kRead_GrIOType, false);
tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership, GrWrapCacheable::kYes,
kRead_GrIOType);
REPORTER_ASSERT(reporter, !tex);
}
@ -64,9 +66,11 @@ void wrap_tex_test(skiatest::Reporter* reporter, GrContext* context) {
backendCopy.fAlloc = GrVkAlloc();
GrBackendTexture backendTex = GrBackendTexture(kW, kH, backendCopy);
backendTex.setPixelConfig(kPixelConfig);
tex = gpu->wrapBackendTexture(backendTex, kBorrow_GrWrapOwnership, kRead_GrIOType, false);
tex = gpu->wrapBackendTexture(backendTex, kBorrow_GrWrapOwnership, GrWrapCacheable::kYes,
kRead_GrIOType);
REPORTER_ASSERT(reporter, !tex);
tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership, kRead_GrIOType, false);
tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership, GrWrapCacheable::kYes,
kRead_GrIOType);
REPORTER_ASSERT(reporter, !tex);
}
@ -75,7 +79,8 @@ void wrap_tex_test(skiatest::Reporter* reporter, GrContext* context) {
GrVkImageInfo backendCopy = imageInfo;
GrBackendTexture backendTex = GrBackendTexture(kW, kH, backendCopy);
backendTex.setPixelConfig(kPixelConfig);
tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership, kRead_GrIOType, false);
tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership, GrWrapCacheable::kYes,
kRead_GrIOType);
REPORTER_ASSERT(reporter, tex);
}