diff options
-rw-r--r-- | src/gpu/GrLayerCache.cpp | 44 | ||||
-rw-r--r-- | src/gpu/GrLayerCache.h | 56 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 138 | ||||
-rw-r--r-- | tests/GpuLayerCacheTest.cpp | 22 |
4 files changed, 184 insertions, 76 deletions
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp index 2c97387c7f..5fb76d7392 100644 --- a/src/gpu/GrLayerCache.cpp +++ b/src/gpu/GrLayerCache.cpp @@ -13,8 +13,8 @@ DECLARE_SKMESSAGEBUS_MESSAGE(GrPictureDeletedMessage); #ifdef SK_DEBUG void GrCachedLayer::validate(const GrTexture* backingTexture) const { - SkASSERT(SK_InvalidGenID != fKey.getPictureID()); - SkASSERT(-1 != fKey.getLayerID()); + SkASSERT(SK_InvalidGenID != fKey.pictureID()); + SkASSERT(fKey.start() > 0 && fKey.stop() > 0); if (NULL != fTexture) { @@ -117,24 +117,31 @@ void GrLayerCache::freeAll() { this->initAtlas(); } -GrCachedLayer* GrLayerCache::createLayer(const SkPicture* picture, int layerID) { - SkASSERT(picture->uniqueID() != SK_InvalidGenID && layerID >= 0); +GrCachedLayer* GrLayerCache::createLayer(const SkPicture* picture, + int start, int stop, + const SkMatrix& ctm) { + SkASSERT(picture->uniqueID() != SK_InvalidGenID && start > 0 && stop > 0); - GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (picture->uniqueID(), layerID)); + GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (picture->uniqueID(), start, stop, ctm)); fLayerHash.add(layer); return layer; } -GrCachedLayer* GrLayerCache::findLayer(const SkPicture* picture, int layerID) { - SkASSERT(picture->uniqueID() != SK_InvalidGenID && layerID >= 0); - return fLayerHash.find(GrCachedLayer::Key(picture->uniqueID(), layerID)); +GrCachedLayer* GrLayerCache::findLayer(const SkPicture* picture, + int start, int stop, + const SkMatrix& ctm) { + SkASSERT(picture->uniqueID() != SK_InvalidGenID && start > 0 && stop > 0); + return fLayerHash.find(GrCachedLayer::Key(picture->uniqueID(), start, stop, ctm)); } -GrCachedLayer* GrLayerCache::findLayerOrCreate(const SkPicture* picture, int layerID) { - SkASSERT(picture->uniqueID() != SK_InvalidGenID && layerID >= 0); - GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(picture->uniqueID(), layerID)); +GrCachedLayer* GrLayerCache::findLayerOrCreate(const SkPicture* picture, + int start, int stop, + const SkMatrix& ctm) { + SkASSERT(picture->uniqueID() != SK_InvalidGenID && start > 0 && stop > 0); + GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(picture->uniqueID(), + start, stop, ctm)); if (NULL == layer) { - layer = this->createLayer(picture, layerID); + layer = this->createLayer(picture, start, stop, ctm); } return layer; @@ -221,6 +228,19 @@ void GrLayerCache::unlock(GrCachedLayer* layer) { fPlotLocks[plotID]--; // At this point we could aggressively clear out un-locked plots but // by delaying we may be able to reuse some of the atlased layers later. +#if 0 + // This testing code aggressively removes the atlased layers. This + // can be used to separate the performance contribution of less + // render target pingponging from that due to the re-use of cached layers + GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID()); + SkASSERT(NULL != pictInfo); + + GrAtlas::RemovePlot(&pictInfo->fPlotUsage, layer->plot()); + + layer->setPlot(NULL); + layer->setTexture(NULL, GrIRect16::MakeEmpty()); +#endif + } else { fContext->unlockScratchTexture(layer->texture()); layer->setTexture(NULL, GrIRect16::MakeEmpty()); diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h index f4087d66f0..f027a26cbc 100644 --- a/src/gpu/GrLayerCache.h +++ b/src/gpu/GrLayerCache.h @@ -44,50 +44,70 @@ public: // get a ref to the GrTexture in which they reside. In both cases 'fRect' // contains the layer's extent in its texture. // Atlased layers also get a pointer to the plot in which they reside. -// For non-atlased layers the lock field just corresponds to locking in -// the resource cache. For atlased layers it implements an additional level +// For non-atlased layers, the lock field just corresponds to locking in +// the resource cache. For atlased layers, it implements an additional level // of locking to allow atlased layers to be reused multiple times. struct GrCachedLayer { public: // For SkTDynamicHash struct Key { - Key(uint32_t pictureID, int layerID) : fPictureID(pictureID) , fLayerID(layerID) {} + Key(uint32_t pictureID, int start, int stop, const SkMatrix& ctm) + : fPictureID(pictureID) + , fStart(start) + , fStop(stop) + , 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) + 2 * sizeof(int) + + 9 * sizeof(SkScalar) + sizeof(uint32_t)); + } bool operator==(const Key& other) const { - return fPictureID == other.fPictureID && fLayerID == other.fLayerID; + return fPictureID == other.fPictureID && + fStart == other.fStart && + fStop == other.fStop && + fCTM.cheapEqualTo(other.fCTM); } - uint32_t getPictureID() const { return fPictureID; } - int getLayerID() const { return fLayerID; } + uint32_t pictureID() const { return fPictureID; } + int start() const { return fStart; } + int stop() const { return fStop; } + const SkMatrix& ctm() const { return fCTM; } private: // ID of the picture of which this layer is a part const uint32_t fPictureID; - // fLayerID is the index of this layer in the picture (one of 0 .. #layers). - const int fLayerID; + // The range of commands in the picture this layer represents + const int fStart; + const int fStop; + // The CTM applied to this layer in the picture + SkMatrix fCTM; }; static const Key& GetKey(const GrCachedLayer& layer) { return layer.fKey; } static uint32_t Hash(const Key& key) { - return SkChecksum::Mix((key.getPictureID() << 16) | key.getLayerID()); + return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key)); } // GrCachedLayer proper - GrCachedLayer(uint32_t pictureID, int layerID) - : fKey(pictureID, layerID) + GrCachedLayer(uint32_t pictureID, int start, int stop, const SkMatrix& ctm) + : fKey(pictureID, start, stop, ctm) , fTexture(NULL) , fRect(GrIRect16::MakeEmpty()) , fPlot(NULL) , fLocked(false) { - SkASSERT(SK_InvalidGenID != pictureID && layerID >= 0); + SkASSERT(SK_InvalidGenID != pictureID && start >= 0 && stop >= 0); } ~GrCachedLayer() { SkSafeUnref(fTexture); } - uint32_t pictureID() const { return fKey.getPictureID(); } - int layerID() const { return fKey.getLayerID(); } + uint32_t pictureID() const { return fKey.pictureID(); } + int start() const { return fKey.start(); } + int stop() const { return fKey.stop(); } + const SkMatrix& ctm() const { return fKey.ctm(); } void setTexture(GrTexture* texture, const GrIRect16& rect) { SkRefCnt_SafeAssign(fTexture, texture); @@ -151,8 +171,10 @@ public: // elements by the GrContext void freeAll(); - GrCachedLayer* findLayer(const SkPicture* picture, int layerID); - GrCachedLayer* findLayerOrCreate(const SkPicture* picture, int layerID); + GrCachedLayer* findLayer(const SkPicture* picture, int start, int stop, const SkMatrix& ctm); + GrCachedLayer* findLayerOrCreate(const SkPicture* picture, + int start, int stop, + const SkMatrix& ctm); // Inform the cache that layer's cached image is now required. Return true // if it was found in the ResourceCache and doesn't need to be regenerated. @@ -206,7 +228,7 @@ private: int fPlotLocks[kNumPlotsX * kNumPlotsY]; void initAtlas(); - GrCachedLayer* createLayer(const SkPicture* picture, int layerID); + GrCachedLayer* createLayer(const SkPicture* picture, int start, int stop, const SkMatrix& ctm); // Remove all the layers (and unlock any resources) associated with 'pictureID' void purge(uint32_t pictureID); diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 2853805a5b..b1e23f9474 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1871,7 +1871,7 @@ static void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* re result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); } -bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* picture) { +bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture* picture) { fContext->getLayerCache()->processDeletedPictures(); SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey(); @@ -1893,7 +1893,7 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi } SkRect clipBounds; - if (!canvas->getClipBounds(&clipBounds)) { + if (!mainCanvas->getClipBounds(&clipBounds)) { return true; } SkIRect query; @@ -1970,13 +1970,19 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi SkPictureReplacementPlayback::PlaybackReplacements replacements; + SkTDArray<GrCachedLayer*> atlased, nonAtlased; + atlased.setReserve(gpuData->numSaveLayers()); + // Generate the layer and/or ensure it is locked for (int i = 0; i < gpuData->numSaveLayers(); ++i) { if (pullForward[i]) { - GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, i); - const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i); + GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, + info.fSaveLayerOpID, + info.fRestoreOpID, + info.fCTM); + SkPictureReplacementPlayback::PlaybackReplacements::ReplacementInfo* layerInfo = replacements.push(); layerInfo->fStart = info.fSaveLayerOpID; @@ -2009,56 +2015,110 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* pi layer->rect().width(), layer->rect().height()); - if (needsRendering) { - SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( - layer->texture()->asRenderTarget(), - SkSurface::kStandard_TextRenderMode, - SkSurface::kDontClear_RenderTargetFlag)); - - SkCanvas* canvas = surface->getCanvas(); - - // Add a rect clip to make sure the rendering doesn't - // extend beyond the boundaries of the atlased sub-rect - SkRect bound = SkRect::Make(layerInfo->fSrcRect); - canvas->clipRect(bound); - if (layer->isAtlased()) { - // Since 'clear' doesn't respect the clip we need to draw a rect - // TODO: ensure none of the atlased layers contain a clear call! - SkPaint paint; - paint.setColor(SK_ColorTRANSPARENT); - paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode))->unref(); - canvas->drawRect(bound, paint); + *atlased.append() = layer; } else { - canvas->clear(SK_ColorTRANSPARENT); + *nonAtlased.append() = layer; } + } + } + } - // info.fCTM maps the layer's top/left to the origin. - // If this layer is atlased the top/left corner needs - // to be offset to some arbitrary location in the backing - // texture. - canvas->translate(bound.fLeft, bound.fTop); - canvas->concat(info.fCTM); + // Render the atlased layers that require it + if (atlased.count() > 0) { + // All the atlased layers are rendered into the same GrTexture + SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( + atlased[0]->texture()->asRenderTarget(), + SkSurface::kStandard_TextRenderMode, + SkSurface::kDontClear_RenderTargetFlag)); - SkPictureRangePlayback rangePlayback(picture, - info.fSaveLayerOpID, - info.fRestoreOpID); - rangePlayback.draw(canvas, NULL); + SkCanvas* atlasCanvas = surface->getCanvas(); - canvas->flush(); - } + SkPaint paint; + paint.setColor(SK_ColorTRANSPARENT); + paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode))->unref(); + + for (int i = 0; i < atlased.count(); ++i) { + GrCachedLayer* layer = atlased[i]; + + atlasCanvas->save(); + + // Add a rect clip to make sure the rendering doesn't + // extend beyond the boundaries of the atlased sub-rect + SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft), + SkIntToScalar(layer->rect().fTop), + SkIntToScalar(layer->rect().width()), + SkIntToScalar(layer->rect().height())); + atlasCanvas->clipRect(bound); + + // Since 'clear' doesn't respect the clip we need to draw a rect + // TODO: ensure none of the atlased layers contain a clear call! + atlasCanvas->drawRect(bound, paint); + + // info.fCTM maps the layer's top/left to the origin. + // Since this layer is atlased, the top/left corner needs + // to be offset to the correct location in the backing texture. + atlasCanvas->translate(bound.fLeft, bound.fTop); + atlasCanvas->concat(layer->ctm()); + + SkPictureRangePlayback rangePlayback(picture, + layer->start(), + layer->stop()); + rangePlayback.draw(atlasCanvas, NULL); + + atlasCanvas->restore(); } + + atlasCanvas->flush(); } - // Playback using new layers + // Render the non-atlased layers that require it + for (int i = 0; i < nonAtlased.count(); ++i) { + GrCachedLayer* layer = nonAtlased[i]; + + // Each non-atlased layer has its own GrTexture + SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( + layer->texture()->asRenderTarget(), + SkSurface::kStandard_TextRenderMode, + SkSurface::kDontClear_RenderTargetFlag)); + + SkCanvas* layerCanvas = surface->getCanvas(); + + // Add a rect clip to make sure the rendering doesn't + // extend beyond the boundaries of the atlased sub-rect + SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft), + SkIntToScalar(layer->rect().fTop), + SkIntToScalar(layer->rect().width()), + SkIntToScalar(layer->rect().height())); + + layerCanvas->clipRect(bound); // TODO: still useful? + + layerCanvas->clear(SK_ColorTRANSPARENT); + + layerCanvas->concat(layer->ctm()); + + SkPictureRangePlayback rangePlayback(picture, + layer->start(), + layer->stop()); + rangePlayback.draw(layerCanvas, NULL); + + layerCanvas->flush(); + } + + // Render the entire picture using new layers SkPictureReplacementPlayback playback(picture, &replacements, ops.get()); - playback.draw(canvas, NULL); + playback.draw(mainCanvas, NULL); // unlock the layers for (int i = 0; i < gpuData->numSaveLayers(); ++i) { - GrCachedLayer* layer = fContext->getLayerCache()->findLayer(picture, i); + const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i); + + GrCachedLayer* layer = fContext->getLayerCache()->findLayer(picture, + info.fSaveLayerOpID, + info.fRestoreOpID, + info.fCTM); fContext->getLayerCache()->unlock(layer); } diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp index 8ca7b2bdfb..789854cf1f 100644 --- a/tests/GpuLayerCacheTest.cpp +++ b/tests/GpuLayerCacheTest.cpp @@ -31,15 +31,19 @@ static void create_layers(skiatest::Reporter* reporter, int idOffset) { for (int i = 0; i < numToAdd; ++i) { - GrCachedLayer* layer = cache->findLayerOrCreate(&picture, idOffset+i); + GrCachedLayer* layer = cache->findLayerOrCreate(&picture, + idOffset+i+1, idOffset+i+2, + SkMatrix::I()); REPORTER_ASSERT(reporter, NULL != layer); - GrCachedLayer* temp = cache->findLayer(&picture, idOffset+i); + GrCachedLayer* temp = cache->findLayer(&picture, idOffset+i+1, idOffset+i+2, SkMatrix::I()); REPORTER_ASSERT(reporter, temp == layer); REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1); REPORTER_ASSERT(reporter, picture.uniqueID() == layer->pictureID()); - REPORTER_ASSERT(reporter, layer->layerID() == idOffset + i); + REPORTER_ASSERT(reporter, layer->start() == idOffset + i + 1); + REPORTER_ASSERT(reporter, layer->stop() == idOffset + i + 2); + REPORTER_ASSERT(reporter, layer->ctm() == SkMatrix::I()); REPORTER_ASSERT(reporter, NULL == layer->texture()); REPORTER_ASSERT(reporter, !layer->isAtlased()); } @@ -95,7 +99,7 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { create_layers(reporter, &cache, *picture, kInitialNumLayers, 0); for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture, i); + GrCachedLayer* layer = cache.findLayer(picture, i+1, i+2, SkMatrix::I()); REPORTER_ASSERT(reporter, NULL != layer); lock_layer(reporter, &cache, layer); @@ -112,14 +116,14 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { // Unlock the textures for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture, i); + GrCachedLayer* layer = cache.findLayer(picture, i+1, i+2, SkMatrix::I()); REPORTER_ASSERT(reporter, NULL != layer); cache.unlock(layer); } for (int i = 0; i < kInitialNumLayers; ++i) { - GrCachedLayer* layer = cache.findLayer(picture, i); + GrCachedLayer* layer = cache.findLayer(picture, i+1, i+2, SkMatrix::I()); REPORTER_ASSERT(reporter, NULL != layer); REPORTER_ASSERT(reporter, !layer->locked()); @@ -138,7 +142,9 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { // Add an additional layer. Since all the layers are unlocked this // will force out the first atlased layer create_layers(reporter, &cache, *picture, 1, kInitialNumLayers); - GrCachedLayer* layer = cache.findLayer(picture, kInitialNumLayers); + GrCachedLayer* layer = cache.findLayer(picture, + kInitialNumLayers+1, kInitialNumLayers+2, + SkMatrix::I()); REPORTER_ASSERT(reporter, NULL != layer); lock_layer(reporter, &cache, layer); @@ -146,7 +152,7 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { } for (int i = 0; i < kInitialNumLayers+1; ++i) { - GrCachedLayer* layer = cache.findLayer(picture, i); + GrCachedLayer* layer = cache.findLayer(picture, i+1, i+2, 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, NULL != layer); |