michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/layers/TiledContentClient.h" michael@0: #include // for ceil, ceilf, floor michael@0: #include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer michael@0: #include "GeckoProfiler.h" // for PROFILER_LABEL michael@0: #include "ClientLayerManager.h" // for ClientLayerManager michael@0: #include "CompositorChild.h" // for CompositorChild michael@0: #include "gfxContext.h" // for gfxContext, etc michael@0: #include "gfxPlatform.h" // for gfxPlatform michael@0: #include "gfxPrefs.h" // for gfxPrefs michael@0: #include "gfxRect.h" // for gfxRect michael@0: #include "mozilla/MathAlgorithms.h" // for Abs michael@0: #include "mozilla/gfx/Point.h" // for IntSize michael@0: #include "mozilla/gfx/Rect.h" // for Rect michael@0: #include "mozilla/layers/CompositableForwarder.h" michael@0: #include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder michael@0: #include "TextureClientPool.h" michael@0: #include "nsDebug.h" // for NS_ASSERTION michael@0: #include "nsISupportsImpl.h" // for gfxContext::AddRef, etc michael@0: #include "nsSize.h" // for nsIntSize michael@0: #include "gfxReusableSharedImageSurfaceWrapper.h" michael@0: #include "nsMathUtils.h" // for NS_roundf michael@0: #include "gfx2DGlue.h" michael@0: michael@0: // This is the minimum area that we deem reasonable to copy from the front buffer to the michael@0: // back buffer on tile updates. If the valid region is smaller than this, we just michael@0: // redraw it and save on the copy (and requisite surface-locking involved). michael@0: #define MINIMUM_TILE_COPY_AREA (1.f/16.f) michael@0: michael@0: #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY michael@0: #include "cairo.h" michael@0: #include michael@0: using mozilla::layers::Layer; michael@0: static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int width, int height) michael@0: { michael@0: gfxContext c(dt); michael@0: michael@0: // Draw border michael@0: c.NewPath(); michael@0: c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0)); michael@0: c.Rectangle(gfxRect(0, 0, width, height)); michael@0: c.Stroke(); michael@0: michael@0: // Build tile description michael@0: std::stringstream ss; michael@0: ss << x << ", " << y; michael@0: michael@0: // Draw text using cairo toy text API michael@0: cairo_t* cr = c.GetCairo(); michael@0: cairo_set_font_size(cr, 25); michael@0: cairo_text_extents_t extents; michael@0: cairo_text_extents(cr, ss.str().c_str(), &extents); michael@0: michael@0: int textWidth = extents.width + 6; michael@0: michael@0: c.NewPath(); michael@0: c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0)); michael@0: c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30))); michael@0: c.Fill(); michael@0: michael@0: c.NewPath(); michael@0: c.SetDeviceColor(gfxRGBA(1.0, 0.0, 0.0, 1.0)); michael@0: c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30))); michael@0: c.Stroke(); michael@0: michael@0: c.NewPath(); michael@0: cairo_move_to(cr, 4, 28); michael@0: cairo_show_text(cr, ss.str().c_str()); michael@0: michael@0: } michael@0: michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: michael@0: using namespace gfx; michael@0: michael@0: namespace layers { michael@0: michael@0: michael@0: TiledContentClient::TiledContentClient(ClientTiledThebesLayer* aThebesLayer, michael@0: ClientLayerManager* aManager) michael@0: : CompositableClient(aManager->AsShadowForwarder()) michael@0: { michael@0: MOZ_COUNT_CTOR(TiledContentClient); michael@0: michael@0: mTiledBuffer = ClientTiledLayerBuffer(aThebesLayer, this, aManager, michael@0: &mSharedFrameMetricsHelper); michael@0: mLowPrecisionTiledBuffer = ClientTiledLayerBuffer(aThebesLayer, this, aManager, michael@0: &mSharedFrameMetricsHelper); michael@0: michael@0: mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution()/1000.f); michael@0: } michael@0: michael@0: void michael@0: TiledContentClient::ClearCachedResources() michael@0: { michael@0: mTiledBuffer.DiscardBackBuffers(); michael@0: mLowPrecisionTiledBuffer.DiscardBackBuffers(); michael@0: } michael@0: michael@0: void michael@0: TiledContentClient::UseTiledLayerBuffer(TiledBufferType aType) michael@0: { michael@0: ClientTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER michael@0: ? &mLowPrecisionTiledBuffer michael@0: : &mTiledBuffer; michael@0: michael@0: // Take a ReadLock on behalf of the TiledContentHost. This michael@0: // reference will be adopted when the descriptor is opened in michael@0: // TiledLayerBufferComposite. michael@0: buffer->ReadLock(); michael@0: michael@0: mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles()); michael@0: buffer->ClearPaintedRegion(); michael@0: } michael@0: michael@0: SharedFrameMetricsHelper::SharedFrameMetricsHelper() michael@0: : mLastProgressiveUpdateWasLowPrecision(false) michael@0: , mProgressiveUpdateWasInDanger(false) michael@0: { michael@0: MOZ_COUNT_CTOR(SharedFrameMetricsHelper); michael@0: } michael@0: michael@0: SharedFrameMetricsHelper::~SharedFrameMetricsHelper() michael@0: { michael@0: MOZ_COUNT_DTOR(SharedFrameMetricsHelper); michael@0: } michael@0: michael@0: static inline bool michael@0: FuzzyEquals(float a, float b) { michael@0: return (fabsf(a - b) < 1e-6); michael@0: } michael@0: michael@0: bool michael@0: SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics( michael@0: ContainerLayer* aLayer, michael@0: bool aHasPendingNewThebesContent, michael@0: bool aLowPrecision, michael@0: ParentLayerRect& aCompositionBounds, michael@0: CSSToParentLayerScale& aZoom) michael@0: { michael@0: MOZ_ASSERT(aLayer); michael@0: michael@0: CompositorChild* compositor = CompositorChild::Get(); michael@0: michael@0: if (!compositor) { michael@0: FindFallbackContentFrameMetrics(aLayer, aCompositionBounds, aZoom); michael@0: return false; michael@0: } michael@0: michael@0: const FrameMetrics& contentMetrics = aLayer->GetFrameMetrics(); michael@0: FrameMetrics compositorMetrics; michael@0: michael@0: if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(), michael@0: compositorMetrics)) { michael@0: FindFallbackContentFrameMetrics(aLayer, aCompositionBounds, aZoom); michael@0: return false; michael@0: } michael@0: michael@0: aCompositionBounds = ParentLayerRect(compositorMetrics.mCompositionBounds); michael@0: aZoom = compositorMetrics.GetZoomToParent(); michael@0: michael@0: // Reset the checkerboard risk flag when switching to low precision michael@0: // rendering. michael@0: if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) { michael@0: // Skip low precision rendering until we're at risk of checkerboarding. michael@0: if (!mProgressiveUpdateWasInDanger) { michael@0: return true; michael@0: } michael@0: mProgressiveUpdateWasInDanger = false; michael@0: } michael@0: mLastProgressiveUpdateWasLowPrecision = aLowPrecision; michael@0: michael@0: // Always abort updates if the resolution has changed. There's no use michael@0: // in drawing at the incorrect resolution. michael@0: if (!FuzzyEquals(compositorMetrics.GetZoom().scale, contentMetrics.GetZoom().scale)) { michael@0: return true; michael@0: } michael@0: michael@0: // Never abort drawing if we can't be sure we've sent a more recent michael@0: // display-port. If we abort updating when we shouldn't, we can end up michael@0: // with blank regions on the screen and we open up the risk of entering michael@0: // an endless updating cycle. michael@0: if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 && michael@0: fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 && michael@0: fabsf(contentMetrics.mDisplayPort.x - compositorMetrics.mDisplayPort.x) <= 2 && michael@0: fabsf(contentMetrics.mDisplayPort.y - compositorMetrics.mDisplayPort.y) <= 2 && michael@0: fabsf(contentMetrics.mDisplayPort.width - compositorMetrics.mDisplayPort.width) <= 2 && michael@0: fabsf(contentMetrics.mDisplayPort.height - compositorMetrics.mDisplayPort.height)) { michael@0: return false; michael@0: } michael@0: michael@0: // When not a low precision pass and the page is in danger of checker boarding michael@0: // abort update. michael@0: if (!aLowPrecision && !mProgressiveUpdateWasInDanger) { michael@0: if (AboutToCheckerboard(contentMetrics, compositorMetrics)) { michael@0: mProgressiveUpdateWasInDanger = true; michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // Abort drawing stale low-precision content if there's a more recent michael@0: // display-port in the pipeline. michael@0: if (aLowPrecision && !aHasPendingNewThebesContent) { michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: SharedFrameMetricsHelper::FindFallbackContentFrameMetrics(ContainerLayer* aLayer, michael@0: ParentLayerRect& aCompositionBounds, michael@0: CSSToParentLayerScale& aZoom) { michael@0: if (!aLayer) { michael@0: return; michael@0: } michael@0: michael@0: ContainerLayer* layer = aLayer; michael@0: const FrameMetrics* contentMetrics = &(layer->GetFrameMetrics()); michael@0: michael@0: // Walk up the layer tree until a valid composition bounds is found michael@0: while (layer && contentMetrics->mCompositionBounds.IsEmpty()) { michael@0: layer = layer->GetParent(); michael@0: contentMetrics = layer ? &(layer->GetFrameMetrics()) : contentMetrics; michael@0: } michael@0: michael@0: MOZ_ASSERT(!contentMetrics->mCompositionBounds.IsEmpty()); michael@0: michael@0: aCompositionBounds = ParentLayerRect(contentMetrics->mCompositionBounds); michael@0: aZoom = contentMetrics->GetZoomToParent(); // TODO(botond): double-check this michael@0: return; michael@0: } michael@0: michael@0: bool michael@0: SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics, michael@0: const FrameMetrics& aCompositorMetrics) michael@0: { michael@0: return !aContentMetrics.mDisplayPort.Contains(aCompositorMetrics.CalculateCompositedRectInCssPixels() - aCompositorMetrics.GetScrollOffset()); michael@0: } michael@0: michael@0: ClientTiledLayerBuffer::ClientTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer, michael@0: CompositableClient* aCompositableClient, michael@0: ClientLayerManager* aManager, michael@0: SharedFrameMetricsHelper* aHelper) michael@0: : mThebesLayer(aThebesLayer) michael@0: , mCompositableClient(aCompositableClient) michael@0: , mManager(aManager) michael@0: , mLastPaintOpaque(false) michael@0: , mSharedFrameMetricsHelper(aHelper) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: ClientTiledLayerBuffer::HasFormatChanged() const michael@0: { michael@0: return mThebesLayer->CanUseOpaqueSurface() != mLastPaintOpaque; michael@0: } michael@0: michael@0: michael@0: gfxContentType michael@0: ClientTiledLayerBuffer::GetContentType() const michael@0: { michael@0: if (mThebesLayer->CanUseOpaqueSurface()) { michael@0: return gfxContentType::COLOR; michael@0: } else { michael@0: return gfxContentType::COLOR_ALPHA; michael@0: } michael@0: } michael@0: michael@0: gfxMemorySharedReadLock::gfxMemorySharedReadLock() michael@0: : mReadCount(1) michael@0: { michael@0: MOZ_COUNT_CTOR(gfxMemorySharedReadLock); michael@0: } michael@0: michael@0: gfxMemorySharedReadLock::~gfxMemorySharedReadLock() michael@0: { michael@0: MOZ_COUNT_DTOR(gfxMemorySharedReadLock); michael@0: } michael@0: michael@0: int32_t michael@0: gfxMemorySharedReadLock::ReadLock() michael@0: { michael@0: NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock); michael@0: michael@0: return PR_ATOMIC_INCREMENT(&mReadCount); michael@0: } michael@0: michael@0: int32_t michael@0: gfxMemorySharedReadLock::ReadUnlock() michael@0: { michael@0: int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount); michael@0: NS_ASSERTION(readCount >= 0, "ReadUnlock called without ReadLock."); michael@0: michael@0: return readCount; michael@0: } michael@0: michael@0: int32_t michael@0: gfxMemorySharedReadLock::GetReadCount() michael@0: { michael@0: NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock); michael@0: return mReadCount; michael@0: } michael@0: michael@0: gfxShmSharedReadLock::gfxShmSharedReadLock(ISurfaceAllocator* aAllocator) michael@0: : mAllocator(aAllocator) michael@0: , mAllocSuccess(false) michael@0: { michael@0: MOZ_COUNT_CTOR(gfxShmSharedReadLock); michael@0: MOZ_ASSERT(mAllocator); michael@0: if (mAllocator) { michael@0: #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3) michael@0: if (mAllocator->AllocShmemSection(MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) { michael@0: ShmReadLockInfo* info = GetShmReadLockInfoPtr(); michael@0: info->readCount = 1; michael@0: mAllocSuccess = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: gfxShmSharedReadLock::~gfxShmSharedReadLock() michael@0: { michael@0: MOZ_COUNT_DTOR(gfxShmSharedReadLock); michael@0: } michael@0: michael@0: int32_t michael@0: gfxShmSharedReadLock::ReadLock() { michael@0: NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock); michael@0: if (!mAllocSuccess) { michael@0: return 0; michael@0: } michael@0: ShmReadLockInfo* info = GetShmReadLockInfoPtr(); michael@0: return PR_ATOMIC_INCREMENT(&info->readCount); michael@0: } michael@0: michael@0: int32_t michael@0: gfxShmSharedReadLock::ReadUnlock() { michael@0: if (!mAllocSuccess) { michael@0: return 0; michael@0: } michael@0: ShmReadLockInfo* info = GetShmReadLockInfoPtr(); michael@0: int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount); michael@0: NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock."); michael@0: if (readCount <= 0) { michael@0: mAllocator->FreeShmemSection(mShmemSection); michael@0: } michael@0: return readCount; michael@0: } michael@0: michael@0: int32_t michael@0: gfxShmSharedReadLock::GetReadCount() { michael@0: NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock); michael@0: if (!mAllocSuccess) { michael@0: return 0; michael@0: } michael@0: ShmReadLockInfo* info = GetShmReadLockInfoPtr(); michael@0: return info->readCount; michael@0: } michael@0: michael@0: // Placeholder michael@0: TileClient::TileClient() michael@0: : mBackBuffer(nullptr) michael@0: , mFrontBuffer(nullptr) michael@0: , mBackLock(nullptr) michael@0: , mFrontLock(nullptr) michael@0: { michael@0: } michael@0: michael@0: TileClient::TileClient(const TileClient& o) michael@0: { michael@0: mBackBuffer = o.mBackBuffer; michael@0: mFrontBuffer = o.mFrontBuffer; michael@0: mBackLock = o.mBackLock; michael@0: mFrontLock = o.mFrontLock; michael@0: #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY michael@0: mLastUpdate = o.mLastUpdate; michael@0: #endif michael@0: mManager = o.mManager; michael@0: mInvalidFront = o.mInvalidFront; michael@0: mInvalidBack = o.mInvalidBack; michael@0: } michael@0: michael@0: TileClient& michael@0: TileClient::operator=(const TileClient& o) michael@0: { michael@0: if (this == &o) return *this; michael@0: mBackBuffer = o.mBackBuffer; michael@0: mFrontBuffer = o.mFrontBuffer; michael@0: mBackLock = o.mBackLock; michael@0: mFrontLock = o.mFrontLock; michael@0: #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY michael@0: mLastUpdate = o.mLastUpdate; michael@0: #endif michael@0: mManager = o.mManager; michael@0: mInvalidFront = o.mInvalidFront; michael@0: mInvalidBack = o.mInvalidBack; michael@0: return *this; michael@0: } michael@0: michael@0: michael@0: void michael@0: TileClient::Flip() michael@0: { michael@0: RefPtr frontBuffer = mFrontBuffer; michael@0: mFrontBuffer = mBackBuffer; michael@0: mBackBuffer = frontBuffer; michael@0: RefPtr frontLock = mFrontLock; michael@0: mFrontLock = mBackLock; michael@0: mBackLock = frontLock; michael@0: nsIntRegion invalidFront = mInvalidFront; michael@0: mInvalidFront = mInvalidBack; michael@0: mInvalidBack = invalidFront; michael@0: } michael@0: michael@0: void michael@0: TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion, michael@0: bool aCanRerasterizeValidRegion) michael@0: { michael@0: if (mBackBuffer && mFrontBuffer) { michael@0: gfx::IntSize tileSize = mFrontBuffer->GetSize(); michael@0: const nsIntRect tileRect = nsIntRect(0, 0, tileSize.width, tileSize.height); michael@0: michael@0: if (aDirtyRegion.Contains(tileRect)) { michael@0: // The dirty region means that we no longer need the front buffer, so michael@0: // discard it. michael@0: DiscardFrontBuffer(); michael@0: } else { michael@0: // Region that needs copying. michael@0: nsIntRegion regionToCopy = mInvalidBack; michael@0: michael@0: regionToCopy.Sub(regionToCopy, aDirtyRegion); michael@0: michael@0: if (regionToCopy.IsEmpty() || michael@0: (aCanRerasterizeValidRegion && michael@0: regionToCopy.Area() < tileSize.width * tileSize.height * MINIMUM_TILE_COPY_AREA)) { michael@0: // Just redraw it all. michael@0: return; michael@0: } michael@0: michael@0: if (!mFrontBuffer->Lock(OPEN_READ)) { michael@0: NS_WARNING("Failed to lock the tile's front buffer"); michael@0: return; michael@0: } michael@0: TextureClientAutoUnlock autoFront(mFrontBuffer); michael@0: michael@0: if (!mBackBuffer->Lock(OPEN_WRITE)) { michael@0: NS_WARNING("Failed to lock the tile's back buffer"); michael@0: return; michael@0: } michael@0: TextureClientAutoUnlock autoBack(mBackBuffer); michael@0: michael@0: // Copy the bounding rect of regionToCopy. As tiles are quite small, it michael@0: // is unlikely that we'd save much by copying each individual rect of the michael@0: // region, but we can reevaluate this if it becomes an issue. michael@0: const nsIntRect rectToCopy = regionToCopy.GetBounds(); michael@0: gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.width, rectToCopy.height); michael@0: gfx::IntPoint gfxRectToCopyTopLeft = gfxRectToCopy.TopLeft(); michael@0: mFrontBuffer->CopyToTextureClient(mBackBuffer, &gfxRectToCopy, &gfxRectToCopyTopLeft); michael@0: michael@0: mInvalidBack.SetEmpty(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: TileClient::DiscardFrontBuffer() michael@0: { michael@0: if (mFrontBuffer) { michael@0: MOZ_ASSERT(mFrontLock); michael@0: mManager->GetTexturePool(mFrontBuffer->GetFormat())->ReturnTextureClientDeferred(mFrontBuffer); michael@0: mFrontLock->ReadUnlock(); michael@0: mFrontBuffer = nullptr; michael@0: mFrontLock = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: TileClient::DiscardBackBuffer() michael@0: { michael@0: if (mBackBuffer) { michael@0: MOZ_ASSERT(mBackLock); michael@0: if (!mBackBuffer->ImplementsLocking() && mBackLock->GetReadCount() > 1) { michael@0: // Our current back-buffer is still locked by the compositor. This can occur michael@0: // when the client is producing faster than the compositor can consume. In michael@0: // this case we just want to drop it and not return it to the pool. michael@0: mManager->GetTexturePool(mBackBuffer->GetFormat())->ReportClientLost(); michael@0: } else { michael@0: mManager->GetTexturePool(mBackBuffer->GetFormat())->ReturnTextureClient(mBackBuffer); michael@0: } michael@0: mBackLock->ReadUnlock(); michael@0: mBackBuffer = nullptr; michael@0: mBackLock = nullptr; michael@0: } michael@0: } michael@0: michael@0: TextureClient* michael@0: TileClient::GetBackBuffer(const nsIntRegion& aDirtyRegion, TextureClientPool *aPool, bool *aCreatedTextureClient, bool aCanRerasterizeValidRegion) michael@0: { michael@0: // Try to re-use the front-buffer if possible michael@0: if (mFrontBuffer && michael@0: mFrontBuffer->HasInternalBuffer() && michael@0: mFrontLock->GetReadCount() == 1) { michael@0: // If we had a backbuffer we no longer care about it since we'll michael@0: // re-use the front buffer. michael@0: DiscardBackBuffer(); michael@0: Flip(); michael@0: return mBackBuffer; michael@0: } michael@0: michael@0: if (!mBackBuffer || michael@0: mBackLock->GetReadCount() > 1) { michael@0: if (mBackBuffer) { michael@0: // Our current back-buffer is still locked by the compositor. This can occur michael@0: // when the client is producing faster than the compositor can consume. In michael@0: // this case we just want to drop it and not return it to the pool. michael@0: aPool->ReportClientLost(); michael@0: } michael@0: mBackBuffer = aPool->GetTextureClient(); michael@0: // Create a lock for our newly created back-buffer. michael@0: if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) { michael@0: // If our compositor is in the same process, we can save some cycles by not michael@0: // using shared memory. michael@0: mBackLock = new gfxMemorySharedReadLock(); michael@0: } else { michael@0: mBackLock = new gfxShmSharedReadLock(mManager->AsShadowForwarder()); michael@0: } michael@0: michael@0: MOZ_ASSERT(mBackLock->IsValid()); michael@0: michael@0: *aCreatedTextureClient = true; michael@0: mInvalidBack = nsIntRect(0, 0, mBackBuffer->GetSize().width, mBackBuffer->GetSize().height); michael@0: } michael@0: michael@0: ValidateBackBufferFromFront(aDirtyRegion, aCanRerasterizeValidRegion); michael@0: michael@0: return mBackBuffer; michael@0: } michael@0: michael@0: TileDescriptor michael@0: TileClient::GetTileDescriptor() michael@0: { michael@0: if (IsPlaceholderTile()) { michael@0: return PlaceholderTileDescriptor(); michael@0: } michael@0: MOZ_ASSERT(mFrontLock); michael@0: if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) { michael@0: // AddRef here and Release when receiving on the host side to make sure the michael@0: // reference count doesn't go to zero before the host receives the message. michael@0: // see TiledLayerBufferComposite::TiledLayerBufferComposite michael@0: mFrontLock->AddRef(); michael@0: } michael@0: michael@0: if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) { michael@0: return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(), michael@0: TileLock(uintptr_t(mFrontLock.get()))); michael@0: } else { michael@0: gfxShmSharedReadLock *lock = static_cast(mFrontLock.get()); michael@0: return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(), michael@0: TileLock(lock->GetShmemSection())); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientTiledLayerBuffer::ReadUnlock() { michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: if (mRetainedTiles[i].IsPlaceholderTile()) continue; michael@0: mRetainedTiles[i].ReadUnlock(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientTiledLayerBuffer::ReadLock() { michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: if (mRetainedTiles[i].IsPlaceholderTile()) continue; michael@0: mRetainedTiles[i].ReadLock(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientTiledLayerBuffer::Release() michael@0: { michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: if (mRetainedTiles[i].IsPlaceholderTile()) continue; michael@0: mRetainedTiles[i].Release(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ClientTiledLayerBuffer::DiscardBackBuffers() michael@0: { michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: if (mRetainedTiles[i].IsPlaceholderTile()) continue; michael@0: mRetainedTiles[i].DiscardBackBuffer(); michael@0: } michael@0: } michael@0: michael@0: SurfaceDescriptorTiles michael@0: ClientTiledLayerBuffer::GetSurfaceDescriptorTiles() michael@0: { michael@0: InfallibleTArray tiles; michael@0: michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: TileDescriptor tileDesc; michael@0: if (mRetainedTiles.SafeElementAt(i, GetPlaceholderTile()) == GetPlaceholderTile()) { michael@0: tileDesc = PlaceholderTileDescriptor(); michael@0: } else { michael@0: tileDesc = mRetainedTiles[i].GetTileDescriptor(); michael@0: } michael@0: tiles.AppendElement(tileDesc); michael@0: } michael@0: return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion, michael@0: tiles, mRetainedWidth, mRetainedHeight, michael@0: mResolution, mFrameResolution.scale); michael@0: } michael@0: michael@0: void michael@0: ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, michael@0: const nsIntRegion& aPaintRegion, michael@0: LayerManager::DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData) michael@0: { michael@0: mCallback = aCallback; michael@0: mCallbackData = aCallbackData; michael@0: michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: long start = PR_IntervalNow(); michael@0: #endif michael@0: michael@0: // If this region is empty XMost() - 1 will give us a negative value. michael@0: NS_ASSERTION(!aPaintRegion.GetBounds().IsEmpty(), "Empty paint region\n"); michael@0: michael@0: bool useSinglePaintBuffer = UseSinglePaintBuffer(); michael@0: // XXX The single-tile case doesn't work at the moment, see bug 850396 michael@0: /* michael@0: if (useSinglePaintBuffer) { michael@0: // Check if the paint only spans a single tile. If that's michael@0: // the case there's no point in using a single paint buffer. michael@0: nsIntRect paintBounds = aPaintRegion.GetBounds(); michael@0: useSinglePaintBuffer = GetTileStart(paintBounds.x) != michael@0: GetTileStart(paintBounds.XMost() - 1) || michael@0: GetTileStart(paintBounds.y) != michael@0: GetTileStart(paintBounds.YMost() - 1); michael@0: } michael@0: */ michael@0: michael@0: if (useSinglePaintBuffer) { michael@0: nsRefPtr ctxt; michael@0: michael@0: const nsIntRect bounds = aPaintRegion.GetBounds(); michael@0: { michael@0: PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferAlloc"); michael@0: gfxImageFormat format = michael@0: gfxPlatform::GetPlatform()->OptimalFormatForContent( michael@0: GetContentType()); michael@0: michael@0: mSinglePaintDrawTarget = michael@0: gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( michael@0: gfx::IntSize(ceilf(bounds.width * mResolution), michael@0: ceilf(bounds.height * mResolution)), michael@0: gfx::ImageFormatToSurfaceFormat(format)); michael@0: michael@0: if (!mSinglePaintDrawTarget) { michael@0: return; michael@0: } michael@0: michael@0: ctxt = new gfxContext(mSinglePaintDrawTarget); michael@0: michael@0: mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y); michael@0: } michael@0: ctxt->NewPath(); michael@0: ctxt->Scale(mResolution, mResolution); michael@0: ctxt->Translate(gfxPoint(-bounds.x, -bounds.y)); michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: if (PR_IntervalNow() - start > 3) { michael@0: printf_stderr("Slow alloc %i\n", PR_IntervalNow() - start); michael@0: } michael@0: start = PR_IntervalNow(); michael@0: #endif michael@0: PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesSingleBufferDraw"); michael@0: michael@0: mCallback(mThebesLayer, ctxt, aPaintRegion, DrawRegionClip::CLIP_NONE, nsIntRegion(), mCallbackData); michael@0: } michael@0: michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: if (PR_IntervalNow() - start > 30) { michael@0: const nsIntRect bounds = aPaintRegion.GetBounds(); michael@0: printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height); michael@0: if (aPaintRegion.IsComplex()) { michael@0: printf_stderr("Complex region\n"); michael@0: nsIntRegionRectIterator it(aPaintRegion); michael@0: for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) { michael@0: printf_stderr(" rect %i, %i, %i, %i\n", rect->x, rect->y, rect->width, rect->height); michael@0: } michael@0: } michael@0: } michael@0: start = PR_IntervalNow(); michael@0: #endif michael@0: michael@0: PROFILER_LABEL("ClientTiledLayerBuffer", "PaintThebesUpdate"); michael@0: Update(aNewValidRegion, aPaintRegion); michael@0: michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: if (PR_IntervalNow() - start > 10) { michael@0: const nsIntRect bounds = aPaintRegion.GetBounds(); michael@0: printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height); michael@0: } michael@0: #endif michael@0: michael@0: mLastPaintOpaque = mThebesLayer->CanUseOpaqueSurface(); michael@0: mCallback = nullptr; michael@0: mCallbackData = nullptr; michael@0: mSinglePaintDrawTarget = nullptr; michael@0: } michael@0: michael@0: TileClient michael@0: ClientTiledLayerBuffer::ValidateTile(TileClient aTile, michael@0: const nsIntPoint& aTileOrigin, michael@0: const nsIntRegion& aDirtyRegion) michael@0: { michael@0: PROFILER_LABEL("ClientTiledLayerBuffer", "ValidateTile"); michael@0: michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: if (aDirtyRegion.IsComplex()) { michael@0: printf_stderr("Complex region\n"); michael@0: } michael@0: #endif michael@0: michael@0: if (aTile.IsPlaceholderTile()) { michael@0: aTile.SetLayerManager(mManager); michael@0: } michael@0: michael@0: // Discard our front and backbuffers if our contents changed. In this case michael@0: // the calling code will already have taken care of invalidating the entire michael@0: // layer. michael@0: if (HasFormatChanged()) { michael@0: aTile.DiscardBackBuffer(); michael@0: aTile.DiscardFrontBuffer(); michael@0: } michael@0: michael@0: bool createdTextureClient = false; michael@0: nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin); michael@0: offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution); michael@0: michael@0: bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget; michael@0: RefPtr backBuffer = michael@0: aTile.GetBackBuffer(offsetScaledDirtyRegion, michael@0: mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())), michael@0: &createdTextureClient, !usingSinglePaintBuffer); michael@0: michael@0: if (!backBuffer->Lock(OPEN_READ_WRITE)) { michael@0: NS_WARNING("Failed to lock tile TextureClient for updating."); michael@0: aTile.DiscardFrontBuffer(); michael@0: return aTile; michael@0: } michael@0: michael@0: // We must not keep a reference to the DrawTarget after it has been unlocked, michael@0: // make sure these are null'd before unlocking as destruction of the context michael@0: // may cause the target to be flushed. michael@0: RefPtr drawTarget = backBuffer->GetAsDrawTarget(); michael@0: drawTarget->SetTransform(Matrix()); michael@0: michael@0: RefPtr ctxt = new gfxContext(drawTarget); michael@0: michael@0: if (usingSinglePaintBuffer) { michael@0: // XXX Perhaps we should just copy the bounding rectangle here? michael@0: RefPtr source = mSinglePaintDrawTarget->Snapshot(); michael@0: nsIntRegionRectIterator it(aDirtyRegion); michael@0: for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) { michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: printf_stderr(" break into subdirtyRect %i, %i, %i, %i\n", michael@0: dirtyRect->x, dirtyRect->y, dirtyRect->width, dirtyRect->height); michael@0: #endif michael@0: gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x, michael@0: dirtyRect->y - aTileOrigin.y, michael@0: dirtyRect->width, michael@0: dirtyRect->height); michael@0: drawRect.Scale(mResolution); michael@0: michael@0: gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution), michael@0: NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution), michael@0: drawRect.width, michael@0: drawRect.height); michael@0: gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y)); michael@0: drawTarget->CopySurface(source, copyRect, copyTarget); michael@0: michael@0: // Mark the newly updated area as invalid in the front buffer michael@0: aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height)); michael@0: } michael@0: michael@0: // The new buffer is now validated, remove the dirty region from it. michael@0: aTile.mInvalidBack.Sub(nsIntRect(0, 0, GetTileSize().width, GetTileSize().height), michael@0: offsetScaledDirtyRegion); michael@0: } else { michael@0: // Area of the full tile... michael@0: nsIntRegion tileRegion = michael@0: nsIntRect(aTileOrigin.x, aTileOrigin.y, michael@0: GetScaledTileSize().width, GetScaledTileSize().height); michael@0: michael@0: // Intersect this area with the portion that's dirty. michael@0: tileRegion = tileRegion.Intersect(aDirtyRegion); michael@0: michael@0: // Add the resolution scale to store the dirty region. michael@0: nsIntPoint unscaledTileOrigin = nsIntPoint(aTileOrigin.x * mResolution, michael@0: aTileOrigin.y * mResolution); michael@0: nsIntRegion unscaledTileRegion(tileRegion); michael@0: unscaledTileRegion.ScaleRoundOut(mResolution, mResolution); michael@0: michael@0: // Move invalid areas into scaled layer space. michael@0: aTile.mInvalidFront.MoveBy(unscaledTileOrigin); michael@0: aTile.mInvalidBack.MoveBy(unscaledTileOrigin); michael@0: michael@0: // Add the area that's going to be redrawn to the invalid area of the michael@0: // front region. michael@0: aTile.mInvalidFront.Or(aTile.mInvalidFront, unscaledTileRegion); michael@0: michael@0: // Add invalid areas of the backbuffer to the area to redraw. michael@0: tileRegion.Or(tileRegion, aTile.mInvalidBack); michael@0: michael@0: // Move invalid areas back into tile space. michael@0: aTile.mInvalidFront.MoveBy(-unscaledTileOrigin); michael@0: michael@0: // This will be validated now. michael@0: aTile.mInvalidBack.SetEmpty(); michael@0: michael@0: nsIntRect bounds = tileRegion.GetBounds(); michael@0: bounds.MoveBy(-aTileOrigin); michael@0: michael@0: if (GetContentType() != gfxContentType::COLOR) { michael@0: drawTarget->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height)); michael@0: } michael@0: michael@0: ctxt->NewPath(); michael@0: ctxt->Clip(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)); michael@0: ctxt->Translate(gfxPoint(-unscaledTileOrigin.x, -unscaledTileOrigin.y)); michael@0: ctxt->Scale(mResolution, mResolution); michael@0: mCallback(mThebesLayer, ctxt, michael@0: tileRegion.GetBounds(), michael@0: DrawRegionClip::CLIP_NONE, michael@0: nsIntRegion(), mCallbackData); michael@0: michael@0: } michael@0: michael@0: #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY michael@0: DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution, michael@0: aTileOrigin.y * mResolution, GetTileLength(), GetTileLength()); michael@0: #endif michael@0: michael@0: ctxt = nullptr; michael@0: drawTarget = nullptr; michael@0: michael@0: backBuffer->Unlock(); michael@0: michael@0: aTile.Flip(); michael@0: michael@0: if (createdTextureClient) { michael@0: if (!mCompositableClient->AddTextureClient(backBuffer)) { michael@0: NS_WARNING("Failed to add tile TextureClient."); michael@0: aTile.DiscardFrontBuffer(); michael@0: aTile.DiscardBackBuffer(); michael@0: return aTile; michael@0: } michael@0: } michael@0: michael@0: // Note, we don't call UpdatedTexture. The Updated function is called manually michael@0: // by the TiledContentHost before composition. michael@0: michael@0: if (backBuffer->HasInternalBuffer()) { michael@0: // If our new buffer has an internal buffer, we don't want to keep another michael@0: // TextureClient around unnecessarily, so discard the back-buffer. michael@0: aTile.DiscardBackBuffer(); michael@0: } michael@0: michael@0: return aTile; michael@0: } michael@0: michael@0: static LayoutDeviceRect michael@0: TransformCompositionBounds(const ParentLayerRect& aCompositionBounds, michael@0: const CSSToParentLayerScale& aZoom, michael@0: const ParentLayerPoint& aScrollOffset, michael@0: const CSSToParentLayerScale& aResolution, michael@0: const gfx3DMatrix& aTransformParentLayerToLayoutDevice) michael@0: { michael@0: // Transform the current composition bounds into ParentLayer coordinates michael@0: // by compensating for the difference in resolution and subtracting the michael@0: // old composition bounds origin. michael@0: ParentLayerRect offsetViewportRect = (aCompositionBounds / aZoom) * aResolution; michael@0: offsetViewportRect.MoveBy(-aScrollOffset); michael@0: michael@0: gfxRect transformedViewport = michael@0: aTransformParentLayerToLayoutDevice.TransformBounds( michael@0: gfxRect(offsetViewportRect.x, offsetViewportRect.y, michael@0: offsetViewportRect.width, offsetViewportRect.height)); michael@0: michael@0: return LayoutDeviceRect(transformedViewport.x, michael@0: transformedViewport.y, michael@0: transformedViewport.width, michael@0: transformedViewport.height); michael@0: } michael@0: michael@0: bool michael@0: ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion, michael@0: const nsIntRegion& aOldValidRegion, michael@0: nsIntRegion& aRegionToPaint, michael@0: BasicTiledLayerPaintData* aPaintData, michael@0: bool aIsRepeated) michael@0: { michael@0: aRegionToPaint = aInvalidRegion; michael@0: michael@0: // If the composition bounds rect is empty, we can't make any sensible michael@0: // decision about how to update coherently. In this case, just update michael@0: // everything in one transaction. michael@0: if (aPaintData->mCompositionBounds.IsEmpty()) { michael@0: aPaintData->mPaintFinished = true; michael@0: return false; michael@0: } michael@0: michael@0: // If this is a low precision buffer, we force progressive updates. The michael@0: // assumption is that the contents is less important, so visual coherency michael@0: // is lower priority than speed. michael@0: bool drawingLowPrecision = IsLowPrecision(); michael@0: michael@0: // Find out if we have any non-stale content to update. michael@0: nsIntRegion staleRegion; michael@0: staleRegion.And(aInvalidRegion, aOldValidRegion); michael@0: michael@0: // Find out the current view transform to determine which tiles to draw michael@0: // first, and see if we should just abort this paint. Aborting is usually michael@0: // caused by there being an incoming, more relevant paint. michael@0: ParentLayerRect compositionBounds; michael@0: CSSToParentLayerScale zoom; michael@0: #if defined(MOZ_WIDGET_ANDROID) michael@0: bool abortPaint = mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion), michael@0: compositionBounds, zoom, michael@0: !drawingLowPrecision); michael@0: #else michael@0: MOZ_ASSERT(mSharedFrameMetricsHelper); michael@0: michael@0: ContainerLayer* parent = mThebesLayer->AsLayer()->GetParent(); michael@0: michael@0: bool abortPaint = michael@0: mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics( michael@0: parent, michael@0: !staleRegion.Contains(aInvalidRegion), michael@0: drawingLowPrecision, michael@0: compositionBounds, michael@0: zoom); michael@0: #endif michael@0: michael@0: if (abortPaint) { michael@0: // We ignore if front-end wants to abort if this is the first, michael@0: // non-low-precision paint, as in that situation, we're about to override michael@0: // front-end's page/viewport metrics. michael@0: if (!aPaintData->mFirstPaint || drawingLowPrecision) { michael@0: PROFILER_LABEL("ContentClient", "Abort painting"); michael@0: aRegionToPaint.SetEmpty(); michael@0: return aIsRepeated; michael@0: } michael@0: } michael@0: michael@0: // Transform the composition bounds, which is in the ParentLayer coordinates michael@0: // of the nearest ContainerLayer with a valid displayport to LayoutDevice michael@0: // coordinates relative to this layer. michael@0: LayoutDeviceRect transformedCompositionBounds = michael@0: TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset, michael@0: aPaintData->mResolution, aPaintData->mTransformParentLayerToLayoutDevice); michael@0: michael@0: // Paint tiles that have stale content or that intersected with the screen michael@0: // at the time of issuing the draw command in a single transaction first. michael@0: // This is to avoid rendering glitches on animated page content, and when michael@0: // layers change size/shape. michael@0: LayoutDeviceRect typedCoherentUpdateRect = michael@0: transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds); michael@0: michael@0: // Offset by the viewport origin, as the composition bounds are stored in michael@0: // Layer space and not LayoutDevice space. michael@0: typedCoherentUpdateRect.MoveBy(aPaintData->mViewport.TopLeft()); michael@0: michael@0: // Convert to untyped to intersect with the invalid region. michael@0: nsIntRect roundedCoherentUpdateRect = michael@0: LayoutDeviceIntRect::ToUntyped(RoundedOut(typedCoherentUpdateRect)); michael@0: michael@0: aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect); michael@0: aRegionToPaint.Or(aRegionToPaint, staleRegion); michael@0: bool drawingStale = !aRegionToPaint.IsEmpty(); michael@0: if (!drawingStale) { michael@0: aRegionToPaint = aInvalidRegion; michael@0: } michael@0: michael@0: // Prioritise tiles that are currently visible on the screen. michael@0: bool paintVisible = false; michael@0: if (aRegionToPaint.Intersects(roundedCoherentUpdateRect)) { michael@0: aRegionToPaint.And(aRegionToPaint, roundedCoherentUpdateRect); michael@0: paintVisible = true; michael@0: } michael@0: michael@0: // Paint area that's visible and overlaps previously valid content to avoid michael@0: // visible glitches in animated elements, such as gifs. michael@0: bool paintInSingleTransaction = paintVisible && (drawingStale || aPaintData->mFirstPaint); michael@0: michael@0: // The following code decides what order to draw tiles in, based on the michael@0: // current scroll direction of the primary scrollable layer. michael@0: NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!"); michael@0: nsIntRect paintBounds = aRegionToPaint.GetBounds(); michael@0: michael@0: int startX, incX, startY, incY; michael@0: gfx::IntSize scaledTileSize = GetScaledTileSize(); michael@0: if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) { michael@0: startX = RoundDownToTileEdge(paintBounds.x, scaledTileSize.width); michael@0: incX = scaledTileSize.width; michael@0: } else { michael@0: startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width); michael@0: incX = -scaledTileSize.width; michael@0: } michael@0: michael@0: if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) { michael@0: startY = RoundDownToTileEdge(paintBounds.y, scaledTileSize.height); michael@0: incY = scaledTileSize.height; michael@0: } else { michael@0: startY = RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height); michael@0: incY = -scaledTileSize.height; michael@0: } michael@0: michael@0: // Find a tile to draw. michael@0: nsIntRect tileBounds(startX, startY, scaledTileSize.width, scaledTileSize.height); michael@0: int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x; michael@0: int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y; michael@0: // This loop will always terminate, as there is at least one tile area michael@0: // along the first/last row/column intersecting with regionToPaint, or its michael@0: // bounds would have been smaller. michael@0: while (true) { michael@0: aRegionToPaint.And(aInvalidRegion, tileBounds); michael@0: if (!aRegionToPaint.IsEmpty()) { michael@0: break; michael@0: } michael@0: if (Abs(scrollDiffY) >= Abs(scrollDiffX)) { michael@0: tileBounds.x += incX; michael@0: } else { michael@0: tileBounds.y += incY; michael@0: } michael@0: } michael@0: michael@0: if (!aRegionToPaint.Contains(aInvalidRegion)) { michael@0: // The region needed to paint is larger then our progressive chunk size michael@0: // therefore update what we want to paint and ask for a new paint transaction. michael@0: michael@0: // If we need to draw more than one tile to maintain coherency, make michael@0: // sure it happens in the same transaction by requesting this work be michael@0: // repeated immediately. michael@0: // If this is unnecessary, the remaining work will be done tile-by-tile in michael@0: // subsequent transactions. michael@0: if (!drawingLowPrecision && paintInSingleTransaction) { michael@0: return true; michael@0: } michael@0: michael@0: mManager->SetRepeatTransaction(); michael@0: return false; michael@0: } michael@0: michael@0: // We're not repeating painting and we've not requested a repeat transaction, michael@0: // so the paint is finished. If there's still a separate low precision michael@0: // paint to do, it will get marked as unfinished later. michael@0: aPaintData->mPaintFinished = true; michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: ClientTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion, michael@0: nsIntRegion& aInvalidRegion, michael@0: const nsIntRegion& aOldValidRegion, michael@0: BasicTiledLayerPaintData* aPaintData, michael@0: LayerManager::DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData) michael@0: { michael@0: bool repeat = false; michael@0: bool isBufferChanged = false; michael@0: do { michael@0: // Compute the region that should be updated. Repeat as many times as michael@0: // is required. michael@0: nsIntRegion regionToPaint; michael@0: repeat = ComputeProgressiveUpdateRegion(aInvalidRegion, michael@0: aOldValidRegion, michael@0: regionToPaint, michael@0: aPaintData, michael@0: repeat); michael@0: michael@0: // There's no further work to be done. michael@0: if (regionToPaint.IsEmpty()) { michael@0: break; michael@0: } michael@0: michael@0: isBufferChanged = true; michael@0: michael@0: // Keep track of what we're about to refresh. michael@0: aValidRegion.Or(aValidRegion, regionToPaint); michael@0: michael@0: // aValidRegion may have been altered by InvalidateRegion, but we still michael@0: // want to display stale content until it gets progressively updated. michael@0: // Create a region that includes stale content. michael@0: nsIntRegion validOrStale; michael@0: validOrStale.Or(aValidRegion, aOldValidRegion); michael@0: michael@0: // Paint the computed region and subtract it from the invalid region. michael@0: PaintThebes(validOrStale, regionToPaint, aCallback, aCallbackData); michael@0: aInvalidRegion.Sub(aInvalidRegion, regionToPaint); michael@0: } while (repeat); michael@0: michael@0: // Return false if nothing has been drawn, or give what has been drawn michael@0: // to the shadow layer to upload. michael@0: return isBufferChanged; michael@0: } michael@0: michael@0: } michael@0: }