diff options
-rw-r--r-- | src/gpu/GrLayerCache.cpp | 17 | ||||
-rw-r--r-- | src/gpu/GrLayerCache.h | 38 | ||||
-rw-r--r-- | src/gpu/GrLayerHoister.cpp | 34 | ||||
-rw-r--r-- | src/gpu/GrPictureUtils.cpp | 21 | ||||
-rw-r--r-- | src/gpu/GrPictureUtils.h | 7 | ||||
-rw-r--r-- | tests/GpuLayerCacheTest.cpp | 20 | ||||
-rw-r--r-- | tests/PictureTest.cpp | 50 |
7 files changed, 101 insertions, 86 deletions
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp index 7c2a8bd61c..154c6f3621 100644 --- a/src/gpu/GrLayerCache.cpp +++ b/src/gpu/GrLayerCache.cpp @@ -129,30 +129,33 @@ void GrLayerCache::freeAll() { GrCachedLayer* GrLayerCache::createLayer(uint32_t pictureID, int start, int stop, + const SkIRect& bounds, const SkMatrix& ctm, const SkPaint* paint) { SkASSERT(pictureID != SK_InvalidGenID && start >= 0 && stop > 0); - GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (pictureID, start, stop, ctm, paint)); + GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (pictureID, start, stop, bounds, ctm, paint)); fLayerHash.add(layer); return layer; } GrCachedLayer* GrLayerCache::findLayer(uint32_t pictureID, int start, + const SkIRect& bounds, const SkMatrix& ctm) { SkASSERT(pictureID != SK_InvalidGenID && start > 0); - return fLayerHash.find(GrCachedLayer::Key(pictureID, start, ctm)); + return fLayerHash.find(GrCachedLayer::Key(pictureID, start, bounds, ctm)); } GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID, int start, int stop, + const SkIRect& bounds, const SkMatrix& ctm, const SkPaint* paint) { SkASSERT(pictureID != SK_InvalidGenID && start >= 0 && stop > 0); - GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(pictureID, start, ctm)); + GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(pictureID, start, bounds, ctm)); if (NULL == layer) { - layer = this->createLayer(pictureID, start, stop, ctm, paint); + layer = this->createLayer(pictureID, start, stop, bounds, ctm, paint); } return layer; @@ -455,11 +458,7 @@ void GrLayerCache::writeLayersToDisk(const SkString& dirName) { } SkString fileName(dirName); - fileName.append("\\"); - fileName.appendU32(layer->fKey.pictureID()); - fileName.append("-"); - fileName.appendU32(layer->fKey.start()); - fileName.append(".png"); + fileName.appendf("\\%d-%d.png", layer->fKey.pictureID(), layer->fKey.start()); layer->texture()->surfacePriv().savePixels(fileName.c_str()); } diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h index d8c5ad9bf5..aa0d325526 100644 --- a/src/gpu/GrLayerCache.h +++ b/src/gpu/GrLayerCache.h @@ -51,36 +51,41 @@ struct GrCachedLayer { public: // For SkTDynamicHash struct Key { - // TODO: the key needs to include the clip - Key(uint32_t pictureID, int start, const SkMatrix& ctm) + Key(uint32_t pictureID, int start, const SkIRect& bounds, const SkMatrix& ctm) : fPictureID(pictureID) - , fStart(start) { - fCTM[0] = ctm.getScaleX(); - fCTM[1] = ctm.getSkewX(); - fCTM[2] = ctm.getSkewY(); - fCTM[3] = ctm.getScaleY(); + , fStart(start) + , fBounds(bounds) + , fCTM(ctm) { + fCTM.getType(); // force initialization of type so hashes match + // Key needs to be tightly packed. GR_STATIC_ASSERT(sizeof(Key) == sizeof(uint32_t) + // picture ID sizeof(int) + // start index - 4 * sizeof(SkScalar)); // 2x2 from CTM + 4 * sizeof(uint32_t) + // bounds + 9 * sizeof(SkScalar) + sizeof(uint32_t)); // matrix } bool operator==(const Key& other) const { return fPictureID == other.fPictureID && fStart == other.fStart && - 0 == memcmp(fCTM, other.fCTM, sizeof(fCTM)); + fBounds == other.fBounds && + fCTM.cheapEqualTo(other.fCTM); } uint32_t pictureID() const { return fPictureID; } int start() const { return fStart; } + const SkIRect& bound() const { return fBounds; } + const SkMatrix& ctm() const { return fCTM; } private: // ID of the picture of which this layer is a part const uint32_t fPictureID; // The the index of the saveLayer command in the picture const int fStart; + // The bounds of the layer. The TL corner is its offset. + const SkIRect fBounds; // The 2x2 portion of the CTM applied to this layer in the picture - SkScalar fCTM[4]; + SkMatrix fCTM; }; static const Key& GetKey(const GrCachedLayer& layer) { return layer.fKey; } @@ -90,8 +95,9 @@ public: // GrCachedLayer proper GrCachedLayer(uint32_t pictureID, int start, int stop, - const SkMatrix& ctm, const SkPaint* paint) - : fKey(pictureID, start, ctm) + const SkIRect& bounds, const SkMatrix& ctm, + const SkPaint* paint) + : fKey(pictureID, start, bounds, ctm) , fStop(stop) , fPaint(paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL) , fTexture(NULL) @@ -109,6 +115,7 @@ public: uint32_t pictureID() const { return fKey.pictureID(); } int start() const { return fKey.start(); } + const SkIRect& bound() const { return fKey.bound(); } int stop() const { return fStop; } void setTexture(GrTexture* texture, const GrIRect16& rect) { @@ -193,9 +200,11 @@ public: // elements by the GrContext void freeAll(); - GrCachedLayer* findLayer(uint32_t pictureID, int start, const SkMatrix& ctm); + GrCachedLayer* findLayer(uint32_t pictureID, int start, + const SkIRect& bounds, const SkMatrix& ctm); GrCachedLayer* findLayerOrCreate(uint32_t pictureID, int start, int stop, + const SkIRect& bounds, const SkMatrix& ctm, const SkPaint* paint); @@ -265,7 +274,8 @@ private: void initAtlas(); GrCachedLayer* createLayer(uint32_t pictureID, int start, int stop, - const SkMatrix& ctm, const SkPaint* paint); + const SkIRect& bounds, const SkMatrix& ctm, + const SkPaint* paint); void purgeAll(); diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp index e9e3a3f710..929c12285d 100644 --- a/src/gpu/GrLayerHoister.cpp +++ b/src/gpu/GrLayerHoister.cpp @@ -7,10 +7,11 @@ #include "GrLayerCache.h" #include "GrLayerHoister.h" -#include "SkCanvas.h" -#include "SkRecordDraw.h" #include "GrRecordReplaceDraw.h" + +#include "SkCanvas.h" #include "SkGrPixelRef.h" +#include "SkRecordDraw.h" #include "SkSurface.h" // Create the layer information for the hoisted layer and secure the @@ -18,6 +19,7 @@ static void prepare_for_hoisting(GrLayerCache* layerCache, const SkPicture* topLevelPicture, const GrAccelData::SaveLayerInfo& info, + const SkIRect& layerRect, SkTDArray<GrHoistedLayer>* atlased, SkTDArray<GrHoistedLayer>* nonAtlased, SkTDArray<GrHoistedLayer>* recycled) { @@ -26,17 +28,22 @@ static void prepare_for_hoisting(GrLayerCache* layerCache, GrCachedLayer* layer = layerCache->findLayerOrCreate(pict->uniqueID(), info.fSaveLayerOpID, info.fRestoreOpID, + layerRect, info.fOriginXform, info.fPaint); GrTextureDesc desc; desc.fFlags = kRenderTarget_GrTextureFlagBit; - desc.fWidth = info.fSize.fWidth; - desc.fHeight = info.fSize.fHeight; + desc.fWidth = layerRect.width(); + desc.fHeight = layerRect.height(); desc.fConfig = kSkia8888_GrPixelConfig; // TODO: need to deal with sample count - bool needsRendering = layerCache->lock(layer, desc, info.fHasNestedLayers || info.fIsNested); + + bool disallowAtlasing = info.fHasNestedLayers || info.fIsNested || + (layer->paint() && layer->paint()->getImageFilter()); + + bool needsRendering = layerCache->lock(layer, desc, disallowAtlasing); if (NULL == layer->texture()) { // GPU resources could not be secured for the hoisting of this layer return; @@ -57,7 +64,7 @@ static void prepare_for_hoisting(GrLayerCache* layerCache, layerCache->addUse(layer); hl->fLayer = layer; hl->fPicture = pict; - hl->fOffset = info.fOffset; + hl->fOffset = SkIPoint::Make(layerRect.fLeft, layerRect.fTop); hl->fCTM = info.fOriginXform; } @@ -68,7 +75,6 @@ bool GrLayerHoister::FindLayersToHoist(GrContext* context, SkTDArray<GrHoistedLayer>* atlased, SkTDArray<GrHoistedLayer>* nonAtlased, SkTDArray<GrHoistedLayer>* recycled) { - GrLayerCache* layerCache = context->getLayerCache(); layerCache->processDeletedPictures(); @@ -102,15 +108,15 @@ bool GrLayerHoister::FindLayersToHoist(GrContext* context, const GrAccelData::SaveLayerInfo& info = topLevelGPUData->saveLayerInfo(i); - SkRect layerRect = SkRect::MakeXYWH(SkIntToScalar(info.fOffset.fX), - SkIntToScalar(info.fOffset.fY), - SkIntToScalar(info.fSize.fWidth), - SkIntToScalar(info.fSize.fHeight)); - - if (!SkRect::Intersects(query, layerRect)) { + SkRect layerRect = SkRect::Make(info.fBounds); + if (!layerRect.intersect(query)) { continue; } + + SkIRect ir; + layerRect.roundOut(&ir); + // TODO: ignore perspective projected layers here! // TODO: once this code is more stable unsuitable layers can // just be omitted during the optimization stage @@ -118,7 +124,7 @@ bool GrLayerHoister::FindLayersToHoist(GrContext* context, continue; } - prepare_for_hoisting(layerCache, topLevelPicture, info, atlased, nonAtlased, recycled); + prepare_for_hoisting(layerCache, topLevelPicture, info, ir, atlased, nonAtlased, recycled); anyHoisted = true; } diff --git a/src/gpu/GrPictureUtils.cpp b/src/gpu/GrPictureUtils.cpp index da238cef9f..a215a0e7a6 100644 --- a/src/gpu/GrPictureUtils.cpp +++ b/src/gpu/GrPictureUtils.cpp @@ -108,15 +108,14 @@ private: for (int i = 0; i < childData->numSaveLayers(); ++i) { const GrAccelData::SaveLayerInfo& src = childData->saveLayerInfo(i); - this->updateStackForSaveLayer(); - - // TODO: need to store an SkRect in GrAccelData::SaveLayerInfo? - SkRect srcRect = SkRect::MakeXYWH(SkIntToScalar(src.fOffset.fX), - SkIntToScalar(src.fOffset.fY), - SkIntToScalar(src.fSize.width()), - SkIntToScalar(src.fSize.height())); + SkRect srcRect = SkRect::Make(src.fBounds); SkIRect newClip(fCurrentClipBounds); - newClip.intersect(this->adjustAndMap(srcRect, dp.paint)); + + if (!newClip.intersect(this->adjustAndMap(srcRect, dp.paint))) { + continue; + } + + this->updateStackForSaveLayer(); GrAccelData::SaveLayerInfo& dst = fAccelData->addSaveLayerInfo(); @@ -124,8 +123,7 @@ private: // it belongs to a sub-picture. dst.fPicture = src.fPicture ? src.fPicture : static_cast<const SkPicture*>(dp.picture); dst.fPicture->ref(); - dst.fSize = SkISize::Make(newClip.width(), newClip.height()); - dst.fOffset = SkIPoint::Make(newClip.fLeft, newClip.fTop); + dst.fBounds = newClip; dst.fOriginXform = src.fOriginXform; dst.fOriginXform.postConcat(*fCTM); if (src.fPaint) { @@ -181,8 +179,7 @@ private: GrAccelData::SaveLayerInfo& slInfo = fAccelData->addSaveLayerInfo(); SkASSERT(NULL == slInfo.fPicture); // This layer is in the top-most picture - slInfo.fSize = SkISize::Make(si.fBounds.width(), si.fBounds.height()); - slInfo.fOffset = SkIPoint::Make(si.fBounds.fLeft, si.fBounds.fTop); + slInfo.fBounds = si.fBounds; slInfo.fOriginXform = *fCTM; if (si.fPaint) { slInfo.fPaint = SkNEW_ARGS(SkPaint, (*si.fPaint)); diff --git a/src/gpu/GrPictureUtils.h b/src/gpu/GrPictureUtils.h index 07c32ded1b..7bcf6322d0 100644 --- a/src/gpu/GrPictureUtils.h +++ b/src/gpu/GrPictureUtils.h @@ -26,14 +26,11 @@ public: // this pointer is NULL. If it is a nested picture then the pointer // is non-NULL and owns a ref on the picture. const SkPicture* fPicture; - // The size of the saveLayer - SkISize fSize; + // The device space bounds of this layer. + SkIRect fBounds; // The matrix state in which this layer's draws must occur. It does not // include the translation needed to map the layer's top-left point to the origin. SkMatrix fOriginXform; - // The offset that needs to be passed to drawBitmap to correctly - // position the pre-rendered layer. It is in device space. - SkIPoint fOffset; // The paint to use on restore. Can be NULL since it is optional. const SkPaint* fPaint; // The ID of this saveLayer in the picture. 0 is an invalid ID. diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp index f7b2d6ef3e..62f6ec7a8c 100644 --- a/tests/GpuLayerCacheTest.cpp +++ b/tests/GpuLayerCacheTest.cpp @@ -36,11 +36,12 @@ static void create_layers(skiatest::Reporter* reporter, for (int i = 0; i < numToAdd; ++i) { GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(), idOffset+i+1, idOffset+i+2, + SkIRect::MakeEmpty(), SkMatrix::I(), NULL); REPORTER_ASSERT(reporter, layer); - GrCachedLayer* temp = cache->findLayer(picture.uniqueID(), idOffset+i+1, - SkMatrix::I()); + GrCachedLayer* temp = cache->findLayer(picture.uniqueID(), idOffset + i + 1, + SkIRect::MakeEmpty(), SkMatrix::I()); REPORTER_ASSERT(reporter, temp == layer); REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1); @@ -108,7 +109,8 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, SkMatrix::I()); + GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, + SkIRect::MakeEmpty(), SkMatrix::I()); REPORTER_ASSERT(reporter, layer); lock_layer(reporter, &cache, layer); @@ -125,13 +127,15 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { // Unlock the textures for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, SkMatrix::I()); + GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, + SkIRect::MakeEmpty(), SkMatrix::I()); REPORTER_ASSERT(reporter, layer); cache.removeUse(layer); } for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, SkMatrix::I()); + GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, + SkIRect::MakeEmpty(), SkMatrix::I()); REPORTER_ASSERT(reporter, layer); // All the layers should be unlocked @@ -153,7 +157,8 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { // will force out the first atlased layer create_layers(reporter, &cache, *picture, 1, kInitialNumLayers); GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), - kInitialNumLayers+1, SkMatrix::I()); + kInitialNumLayers+1, + SkIRect::MakeEmpty(), SkMatrix::I()); REPORTER_ASSERT(reporter, layer); lock_layer(reporter, &cache, layer); @@ -161,7 +166,8 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { } for (int i = 0; i < kInitialNumLayers+1; ++i) { - GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, SkMatrix::I()); + GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i + 1, + SkIRect::MakeEmpty(), SkMatrix::I()); // 3 old layers plus the new one should be in the atlas. if (1 == i || 2 == i || 3 == i || 5 == i) { REPORTER_ASSERT(reporter, layer); diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp index b539d06403..d10d2b2220 100644 --- a/tests/PictureTest.cpp +++ b/tests/PictureTest.cpp @@ -981,69 +981,69 @@ static void test_gpu_picture_optimization(skiatest::Reporter* reporter, const GrAccelData::SaveLayerInfo& info7 = gpuData->saveLayerInfo(6); REPORTER_ASSERT(reporter, NULL == info0.fPicture); - REPORTER_ASSERT(reporter, kWidth == info0.fSize.fWidth && - kHeight == info0.fSize.fHeight); + REPORTER_ASSERT(reporter, kWidth == info0.fBounds.width() && + kHeight == info0.fBounds.height()); REPORTER_ASSERT(reporter, info0.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, 0 == info0.fOffset.fX && 0 == info0.fOffset.fY); + REPORTER_ASSERT(reporter, 0 == info0.fBounds.fLeft && 0 == info0.fBounds.fTop); REPORTER_ASSERT(reporter, NULL == info0.fPaint); REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers); REPORTER_ASSERT(reporter, NULL == info1.fPicture); - REPORTER_ASSERT(reporter, kWidth == info1.fSize.fWidth && - kHeight == info1.fSize.fHeight); + REPORTER_ASSERT(reporter, kWidth == info1.fBounds.width() && + kHeight == info1.fBounds.height()); REPORTER_ASSERT(reporter, info1.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, 0 == info1.fOffset.fX && 0 == info1.fOffset.fY); + REPORTER_ASSERT(reporter, 0 == info1.fBounds.fLeft && 0 == info1.fBounds.fTop); REPORTER_ASSERT(reporter, NULL == info1.fPaint); REPORTER_ASSERT(reporter, !info1.fIsNested && info1.fHasNestedLayers); // has a nested SL REPORTER_ASSERT(reporter, NULL == info2.fPicture); - REPORTER_ASSERT(reporter, kWidth / 2 == info2.fSize.fWidth && - kHeight/2 == info2.fSize.fHeight); // bound reduces size + REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.width() && + kHeight / 2 == info2.fBounds.height()); // bound reduces size REPORTER_ASSERT(reporter, !info2.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, kWidth/2 == info2.fOffset.fX && // translated - kHeight/2 == info2.fOffset.fY); + REPORTER_ASSERT(reporter, kWidth / 2 == info2.fBounds.fLeft && // translated + kHeight / 2 == info2.fBounds.fTop); REPORTER_ASSERT(reporter, NULL == info1.fPaint); REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested REPORTER_ASSERT(reporter, NULL == info3.fPicture); - REPORTER_ASSERT(reporter, kWidth == info3.fSize.fWidth && - kHeight == info3.fSize.fHeight); + REPORTER_ASSERT(reporter, kWidth == info3.fBounds.width() && + kHeight == info3.fBounds.height()); REPORTER_ASSERT(reporter, info3.fOriginXform.isIdentity()); - REPORTER_ASSERT(reporter, 0 == info3.fOffset.fX && 0 == info3.fOffset.fY); + REPORTER_ASSERT(reporter, 0 == info3.fBounds.fLeft && 0 == info3.fBounds.fTop); REPORTER_ASSERT(reporter, info3.fPaint); REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers); REPORTER_ASSERT(reporter, NULL == info4.fPicture); - REPORTER_ASSERT(reporter, kWidth == info4.fSize.fWidth && - kHeight == info4.fSize.fHeight); - REPORTER_ASSERT(reporter, 0 == info4.fOffset.fX && 0 == info4.fOffset.fY); + REPORTER_ASSERT(reporter, kWidth == info4.fBounds.width() && + kHeight == info4.fBounds.height()); + REPORTER_ASSERT(reporter, 0 == info4.fBounds.fLeft && 0 == info4.fBounds.fTop); REPORTER_ASSERT(reporter, info4.fOriginXform.isIdentity()); REPORTER_ASSERT(reporter, info4.fPaint); REPORTER_ASSERT(reporter, !info4.fIsNested && info4.fHasNestedLayers); // has a nested SL REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture - REPORTER_ASSERT(reporter, kWidth == info5.fSize.fWidth && - kHeight == info5.fSize.fHeight); - REPORTER_ASSERT(reporter, 0 == info5.fOffset.fX && 0 == info5.fOffset.fY); + REPORTER_ASSERT(reporter, kWidth == info5.fBounds.width() && + kHeight == info5.fBounds.height()); + REPORTER_ASSERT(reporter, 0 == info5.fBounds.fLeft && 0 == info5.fBounds.fTop); REPORTER_ASSERT(reporter, info5.fOriginXform.isIdentity()); REPORTER_ASSERT(reporter, NULL == info5.fPaint); REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested REPORTER_ASSERT(reporter, NULL == info6.fPicture); - REPORTER_ASSERT(reporter, kWidth == info6.fSize.fWidth && - kHeight == info6.fSize.fHeight); - REPORTER_ASSERT(reporter, 0 == info6.fOffset.fX && 0 == info6.fOffset.fY); + REPORTER_ASSERT(reporter, kWidth == info6.fBounds.width() && + kHeight == info6.fBounds.height()); + REPORTER_ASSERT(reporter, 0 == info6.fBounds.fLeft && 0 == info6.fBounds.fTop); REPORTER_ASSERT(reporter, info6.fOriginXform.isIdentity()); REPORTER_ASSERT(reporter, info6.fPaint); REPORTER_ASSERT(reporter, !info6.fIsNested && info6.fHasNestedLayers); // has a nested SL REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture - REPORTER_ASSERT(reporter, kWidth == info7.fSize.fWidth && - kHeight == info7.fSize.fHeight); - REPORTER_ASSERT(reporter, 0 == info7.fOffset.fX && 0 == info7.fOffset.fY); + REPORTER_ASSERT(reporter, kWidth == info7.fBounds.width() && + kHeight == info7.fBounds.height()); + REPORTER_ASSERT(reporter, 0 == info7.fBounds.fLeft && 0 == info7.fBounds.fTop); REPORTER_ASSERT(reporter, info7.fOriginXform.isIdentity()); REPORTER_ASSERT(reporter, NULL == info7.fPaint); REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested |