bool TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) { if (mSize.width == 0 || mSize.height == 0) { return true; } nsIntRegion region; if (mTextureState != Valid) { IntRect bounds = IntRect(0, 0, mSize.width, mSize.height); region = nsIntRegion(bounds); } else { region = aRegion; } bool result = true; int oldCurrentImage = mCurrentImage; BeginBigImageIteration(); do { IntRect tileRect = GetSrcTileRect(); int xPos = tileRect.x; int yPos = tileRect.y; nsIntRegion tileRegion; tileRegion.And(region, tileRect); // intersect with tile if (tileRegion.IsEmpty()) continue; if (CanUploadSubTextures(mGL)) { tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space } else { // If sub-textures are unsupported, expand to tile boundaries tileRect.x = tileRect.y = 0; tileRegion = nsIntRegion(tileRect); } result &= mImages[mCurrentImage]-> DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos)); if (mCurrentImage == mImages.Length() - 1) { // We know we're done, but we still need to ensure that the callback // gets called (e.g. to update the uploaded region). NextTile(); break; } // Override a callback cancelling iteration if the texture wasn't valid. // We need to force the update in that situation, or we may end up // showing invalid/out-of-date texture data. } while (NextTile() || (mTextureState != Valid)); mCurrentImage = oldCurrentImage; mTextureFormat = mImages[0]->GetTextureFormat(); mTextureState = Valid; return result; }
void CreateBug1148350LayerTree() { const char* layerTreeSyntax = "c(t)"; // LayerID 0 1 nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0,0,200,200)), nsIntRegion(IntRect(0,0,200,200)), }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID); }
void CreateHitTesting1LayerTree() { const char* layerTreeSyntax = "c(tttt)"; // LayerID 0 1234 nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0,0,100,100)), nsIntRegion(IntRect(0,0,100,100)), nsIntRegion(IntRect(10,10,20,20)), nsIntRegion(IntRect(10,10,20,20)), nsIntRegion(IntRect(5,5,20,20)), }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); }
TEST_F(APZCSnappingTester, Bug1265510) { const char* layerTreeSyntax = "c(t)"; nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0, 0, 100, 100)), nsIntRegion(IntRect(0, 100, 100, 100)) }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 200)); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200)); SetScrollHandoff(layers[1], root); ScrollSnapInfo snap; snap.mScrollSnapTypeY = NS_STYLE_SCROLL_SNAP_TYPE_MANDATORY; snap.mScrollSnapIntervalY = Some(100 * AppUnitsPerCSSPixel()); ScrollMetadata metadata = root->GetScrollMetadata(0); metadata.SetSnapInfo(ScrollSnapInfo(snap)); root->SetScrollMetadata(metadata); UniquePtr<ScopedLayerTreeRegistration> registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); TestAsyncPanZoomController* outer = ApzcOf(layers[0]); TestAsyncPanZoomController* inner = ApzcOf(layers[1]); // Position the mouse near the bottom of the outer frame and scroll by 60px. // (6 lines of 10px each). APZC will actually scroll to y=100 because of the // mandatory snap coordinate there. TimeStamp now = mcc->Time(); SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), now); // Advance in 5ms increments until we've scrolled by 70px. At this point, the // closest snap point is y=100, and the inner frame should be under the mouse // cursor. while (outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y < 70) { mcc->AdvanceByMillis(5); outer->AdvanceAnimations(mcc->Time()); } // Now do another wheel in a new transaction. This should start scrolling the // inner frame; we verify that it does by checking the inner scroll position. TimeStamp newTransactionTime = now + TimeDuration::FromMilliseconds(gfxPrefs::MouseWheelTransactionTimeoutMs() + 100); SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), newTransactionTime); inner->AdvanceAnimationsUntilEnd(); EXPECT_LT(0.0f, inner->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y); // However, the outer frame should also continue to the snap point, otherwise // it is demonstrating incorrect behaviour by violating the mandatory snapping. outer->AdvanceAnimationsUntilEnd(); EXPECT_EQ(100.0f, outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y); }
void TiledLayerBufferComposite::Clear() { for (TileHost& tile : mRetainedTiles) { tile.ReadUnlock(); tile.ReadUnlockPrevious(); } mRetainedTiles.Clear(); mTiles.mFirst = TileIntPoint(); mTiles.mSize = TileIntSize(); mValidRegion = nsIntRegion(); mPaintedRegion = nsIntRegion(); mResolution = 1.0; }
void TiledContentHost::ProcessLowPrecisionUploadQueue() { if (!mPendingLowPrecisionUpload) { return; } mLowPrecisionRegionToUpload.And(mLowPrecisionRegionToUpload, mLowPrecisionMainMemoryTiledBuffer.GetValidRegion()); mLowPrecisionVideoMemoryTiledBuffer.SetResolution( mLowPrecisionMainMemoryTiledBuffer.GetResolution()); // It's assumed that the video memory tiled buffer has an up-to-date // frame resolution. As it's always updated first when zooming, this // should always be true. mLowPrecisionVideoMemoryTiledBuffer.Upload(&mLowPrecisionMainMemoryTiledBuffer, mLowPrecisionMainMemoryTiledBuffer.GetValidRegion(), mLowPrecisionRegionToUpload, mVideoMemoryTiledBuffer.GetFrameResolution()); nsIntRegion validRegion = mLowPrecisionVideoMemoryTiledBuffer.GetValidRegion(); mLowPrecisionMainMemoryTiledBuffer.ReadUnlock(); mLowPrecisionMainMemoryTiledBuffer = BasicTiledLayerBuffer(); mLowPrecisionRegionToUpload = nsIntRegion(); mPendingLowPrecisionUpload = false; }
void ThebesLayerD3D10::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode) { nsIntRect visibleRect = mVisibleRegion.GetBounds(); if (!mD2DSurface && !mDrawTarget) { return; } nsRefPtr<gfxASurface> destinationSurface; if (aMode == SURFACE_COMPONENT_ALPHA) { FillTexturesBlackWhite(aRegion, visibleRect.TopLeft()); if (!gfxPlatform::UseAzureContentDrawing()) { gfxASurface* surfaces[2] = { mD2DSurface.get(), mD2DSurfaceOnWhite.get() }; destinationSurface = new gfxTeeSurface(surfaces, ArrayLength(surfaces)); // Using this surface as a source will likely go horribly wrong, since // only the onBlack surface will really be used, so alpha information will // be incorrect. destinationSurface->SetAllowUseAsSource(false); } } else { destinationSurface = mD2DSurface; } nsRefPtr<gfxContext> context; if (mDrawTarget) { context = new gfxContext(mDrawTarget); } else { context = new gfxContext(destinationSurface); } nsIntRegionRectIterator iter(aRegion); context->Translate(gfxPoint(-visibleRect.x, -visibleRect.y)); context->NewPath(); const nsIntRect *iterRect; while ((iterRect = iter.Next())) { context->Rectangle(gfxRect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); if (mDrawTarget && aMode == SURFACE_SINGLE_CHANNEL_ALPHA) { mDrawTarget->ClearRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); } } context->Clip(); if (!mDrawTarget && aMode == SURFACE_SINGLE_CHANNEL_ALPHA) { context->SetOperator(gfxContext::OPERATOR_CLEAR); context->Paint(); context->SetOperator(gfxContext::OPERATOR_OVER); } if (mD2DSurface) { mD2DSurface->SetSubpixelAntialiasingEnabled(!(mContentFlags & CONTENT_COMPONENT_ALPHA)); } else if (mDrawTarget) { mDrawTarget->SetPermitSubpixelAA(!(mContentFlags & CONTENT_COMPONENT_ALPHA)); } LayerManagerD3D10::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); }
void TiledContentHost::ProcessUploadQueue(nsIntRegion* aNewValidRegion, TiledLayerProperties* aLayerProperties) { if (!mPendingUpload) return; // If we coalesce uploads while the layers' valid region is changing we will // end up trying to upload area outside of the valid region. (bug 756555) mRegionToUpload.And(mRegionToUpload, mMainMemoryTiledBuffer.GetValidRegion()); mVideoMemoryTiledBuffer.Upload(&mMainMemoryTiledBuffer, mMainMemoryTiledBuffer.GetValidRegion(), mRegionToUpload, aLayerProperties->mEffectiveResolution); *aNewValidRegion = mVideoMemoryTiledBuffer.GetValidRegion(); mMainMemoryTiledBuffer.ReadUnlock(); // Release all the tiles by replacing the tile buffer with an empty // tiled buffer. This will prevent us from doing a double unlock when // calling ~TiledThebesLayerComposite. // XXX: This wont be needed when we do progressive upload and lock // tile by tile. mMainMemoryTiledBuffer = BasicTiledLayerBuffer(); mRegionToUpload = nsIntRegion(); mPendingUpload = false; }
bool ClientTiledThebesLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData) { // Render the low precision buffer, if the visible region is larger than the // critical display port. if (!nsIntRegion(LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)).Contains(aVisibleRegion)) { nsIntRegion oldValidRegion = mContentClient->mLowPrecisionTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, aVisibleRegion); bool updatedBuffer = false; // If the frame resolution or format have changed, invalidate the buffer if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution || mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) { if (!mLowPrecisionValidRegion.IsEmpty()) { updatedBuffer = true; } oldValidRegion.SetEmpty(); mLowPrecisionValidRegion.SetEmpty(); mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution); aInvalidRegion = aVisibleRegion; } // Invalidate previously valid content that is no longer visible if (mPaintData.mLowPrecisionPaintCount == 1) { mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion); } mPaintData.mLowPrecisionPaintCount++; // Remove the valid high-precision region from the invalid low-precision // region. We don't want to spend time drawing things twice. aInvalidRegion.Sub(aInvalidRegion, mValidRegion); TILING_LOG("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, Stringify(aInvalidRegion).c_str()); TILING_LOG("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, Stringify(oldValidRegion).c_str()); if (!aInvalidRegion.IsEmpty()) { updatedBuffer = mContentClient->mLowPrecisionTiledBuffer.ProgressiveUpdate( mLowPrecisionValidRegion, aInvalidRegion, oldValidRegion, &mPaintData, aCallback, aCallbackData); } TILING_LOG("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, Stringify(mLowPrecisionValidRegion).c_str()); return updatedBuffer; } if (!mLowPrecisionValidRegion.IsEmpty()) { TILING_LOG("TILING %p: Clearing low-precision buffer\n", this); // Clear the low precision tiled buffer. mLowPrecisionValidRegion.SetEmpty(); mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState(); // Return true here so we send a Painted callback after clearing the valid // region of the low precision buffer. This allows the shadow buffer's valid // region to be updated and the associated resources to be freed. return true; } return false; }
void TiledContentHost::Composite(EffectChain& aEffectChain, float aOpacity, const gfx::Matrix4x4& aTransform, const gfx::Point& aOffset, const gfx::Filter& aFilter, const gfx::Rect& aClipRect, const nsIntRegion* aVisibleRegion /* = nullptr */, TiledLayerProperties* aLayerProperties /* = nullptr */) { MOZ_ASSERT(aLayerProperties, "aLayerProperties required for TiledContentHost"); // note that ProcessUploadQueue updates the valid region which is then used by // the RenderLayerBuffer calls below and then sent back to the layer. ProcessUploadQueue(&aLayerProperties->mValidRegion, aLayerProperties); ProcessLowPrecisionUploadQueue(); // Render valid tiles. nsIntRect visibleRect = aVisibleRegion->GetBounds(); RenderLayerBuffer(mLowPrecisionVideoMemoryTiledBuffer, mLowPrecisionVideoMemoryTiledBuffer.GetValidRegion(), aEffectChain, aOpacity, aOffset, aFilter, aClipRect, aLayerProperties->mValidRegion, visibleRect, aTransform); RenderLayerBuffer(mVideoMemoryTiledBuffer, aLayerProperties->mValidRegion, aEffectChain, aOpacity, aOffset, aFilter, aClipRect, nsIntRegion(), visibleRect, aTransform); }
bool TextureImageEGL::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom /* = nsIntPoint(0, 0) */) { nsIntRect bounds = aRegion.GetBounds(); nsIntRegion region; if (mTextureState != Valid) { bounds = nsIntRect(0, 0, mSize.width, mSize.height); region = nsIntRegion(bounds); } else { region = aRegion; } mTextureFormat = UploadSurfaceToTexture(mGLContext, aSurf, region, mTexture, mTextureState == Created, bounds.TopLeft() + aFrom, false); mTextureState = Valid; return true; }
gfx::DrawTarget* TiledTextureImage::BeginUpdate(nsIntRegion& aRegion) { NS_ASSERTION(!mInUpdate, "nested update"); mInUpdate = true; // Note, we don't call GetUpdateRegion here as if the updated region is // fully contained in a single tile, we get to avoid iterating through // the tiles again (and a little copying). if (mTextureState != Valid) { // if the texture hasn't been initialized yet, or something important // changed, we need to recreate our backing surface and force the // client to paint everything aRegion = IntRect(IntPoint(0, 0), mSize); } IntRect bounds = aRegion.GetBounds(); for (unsigned i = 0; i < mImages.Length(); i++) { int xPos = (i % mColumns) * mTileSize; int yPos = (i / mColumns) * mTileSize; nsIntRegion imageRegion = nsIntRegion(IntRect(IntPoint(xPos,yPos), mImages[i]->GetSize())); // a single Image can handle this update request if (imageRegion.Contains(aRegion)) { // adjust for tile offset aRegion.MoveBy(-xPos, -yPos); // forward the actual call RefPtr<gfx::DrawTarget> drawTarget = mImages[i]->BeginUpdate(aRegion); // caller expects container space aRegion.MoveBy(xPos, yPos); // we don't have a temp surface mUpdateDrawTarget = nullptr; // remember which image to EndUpdate mCurrentImage = i; return drawTarget.get(); } } // Get the real updated region, taking into account the capabilities of // each TextureImage tile GetUpdateRegion(aRegion); mUpdateRegion = aRegion; bounds = aRegion.GetBounds(); // update covers multiple Images - create a temp surface to paint in gfx::SurfaceFormat format = (GetContentType() == gfxContentType::COLOR) ? gfx::SurfaceFormat::B8G8R8X8: gfx::SurfaceFormat::B8G8R8A8; mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, bounds.Size(), format); return mUpdateDrawTarget;; }
void CreateObscuringLayerTree() { const char* layerTreeSyntax = "c(c(t)t)"; // LayerID 0 1 2 3 // 0 is the root. // 1 is a parent scrollable layer. // 2 is a child scrollable layer. // 3 is the Obscurer, who ruins everything. nsIntRegion layerVisibleRegions[] = { // x coordinates are uninteresting nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200] nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200] nsIntRegion(IntRect(0, 100, 200, 50)), // [100, 150] nsIntRegion(IntRect(0, 100, 200, 100)) // [100, 200] }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200)); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 200, 300)); SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 200, 100)); SetScrollHandoff(layers[2], layers[1]); SetScrollHandoff(layers[1], root); EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200))); root->SetEventRegions(regions); regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 300)); layers[1]->SetEventRegions(regions); regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100)); layers[2]->SetEventRegions(regions); registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); rootApzc = ApzcOf(root); }
void CreateBug1117712LayerTree() { const char* layerTreeSyntax = "c(c(t)t)"; // LayerID 0 1 2 3 // 0 is the root // 1 is a container layer whose sole purpose to make a non-empty ancestor // transform for 2, so that 2's screen-to-apzc and apzc-to-gecko // transforms are different from 3's. // 2 is a small layer that is the actual target // 3 is a big layer obscuring 2 with a dispatch-to-content region nsIntRegion layerVisibleRegions[] = { nsIntRegion(IntRect(0, 0, 100, 100)), nsIntRegion(IntRect(0, 0, 0, 0)), nsIntRegion(IntRect(0, 0, 10, 10)), nsIntRegion(IntRect(0, 0, 100, 100)), }; Matrix4x4 layerTransforms[] = { Matrix4x4(), Matrix4x4::Translation(50, 0, 0), Matrix4x4(), Matrix4x4(), }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, layerTransforms, lm, layers); SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 10, 10)); SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 100, 100)); EventRegions regions(nsIntRegion(IntRect(0, 0, 10, 10))); layers[2]->SetEventRegions(regions); regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100)); regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100)); layers[3]->SetEventRegions(regions); registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); }
void CreateEventRegionsLayerTree1() { const char* layerTreeSyntax = "c(tt)"; nsIntRegion layerVisibleRegions[] = { nsIntRegion(IntRect(0, 0, 200, 200)), // root nsIntRegion(IntRect(0, 0, 100, 200)), // left half nsIntRegion(IntRect(0, 100, 200, 100)), // bottom half }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1); SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2); SetScrollHandoff(layers[1], root); SetScrollHandoff(layers[2], root); // Set up the event regions over a 200x200 area. The root layer has the // whole 200x200 as the hit region; layers[1] has the left half and // layers[2] has the bottom half. The bottom-left 100x100 area is also // in the d-t-c region for both layers[1] and layers[2] (but layers[2] is // on top so it gets the events by default if the main thread doesn't // respond). EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200))); root->SetEventRegions(regions); regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 100, 100, 100)); regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 200)); layers[1]->SetEventRegions(regions); regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100)); layers[2]->SetEventRegions(regions); registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); rootApzc = ApzcOf(root); }
virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback) { ColorLayer* color = static_cast<ColorLayer*>(mLayer.get()); if (mColor != color->GetColor()) { return NewTransformedBounds(); } return nsIntRegion(); }
void CreateEventRegionsLayerTree2() { const char* layerTreeSyntax = "c(t)"; nsIntRegion layerVisibleRegions[] = { nsIntRegion(IntRect(0, 0, 100, 500)), nsIntRegion(IntRect(0, 150, 100, 100)), }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID); // Set up the event regions so that the child thebes layer is positioned far // away from the scrolling container layer. EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100))); root->SetEventRegions(regions); regions.mHitRegion = nsIntRegion(IntRect(0, 150, 100, 100)); layers[1]->SetEventRegions(regions); registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); rootApzc = ApzcOf(root); }
void CreateBug1119497LayerTree() { const char* layerTreeSyntax = "c(tt)"; // LayerID 0 12 // 0 is the root and has an APZC // 1 is behind 2 and has an APZC // 2 entirely covers 1 and should take all the input events, but has no APZC // so hits to 2 should go to to the root APZC nsIntRegion layerVisibleRegions[] = { nsIntRegion(IntRect(0, 0, 100, 100)), nsIntRegion(IntRect(0, 0, 100, 100)), nsIntRegion(IntRect(0, 0, 100, 100)), }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1); registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); }
TEST_F(APZHitTestingTester, HitTestingRespectsScrollClip_Bug1257288) { // Create the layer tree. const char* layerTreeSyntax = "c(tt)"; // LayerID 0 12 nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0,0,200,200)), nsIntRegion(IntRect(0,0,200,200)), nsIntRegion(IntRect(0,0,200,100)) }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); // Add root scroll metadata to the first painted layer. SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200)); // Add root and subframe scroll metadata to the second painted layer. // Give the subframe metadata a scroll clip corresponding to the subframe's // composition bounds. // Importantly, give the layer a layer clip which leaks outside of the // subframe's composition bounds. ScrollMetadata rootMetadata = BuildScrollMetadata( FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200), ParentLayerRect(0,0,200,200)); ScrollMetadata subframeMetadata = BuildScrollMetadata( FrameMetrics::START_SCROLL_ID + 1, CSSRect(0,0,200,200), ParentLayerRect(0,0,200,100)); subframeMetadata.SetScrollClip(Some(LayerClip(ParentLayerIntRect(0,0,200,100)))); layers[2]->SetScrollMetadata({subframeMetadata, rootMetadata}); layers[2]->SetClipRect(Some(ParentLayerIntRect(0,0,200,200))); SetEventRegionsBasedOnBottommostMetrics(layers[2]); // Build the hit testing tree. ScopedLayerTreeRegistration registration(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); // Pan on a region that's inside layers[2]'s layer clip, but outside // its subframe metadata's scroll clip. Pan(manager, 120, 110); // Test that the subframe hasn't scrolled. EXPECT_EQ(CSSPoint(0,0), ApzcOf(layers[2], 0)->GetFrameMetrics().GetScrollOffset()); }
void CreateHitTesting2LayerTree() { const char* layerTreeSyntax = "c(tc(t))"; // LayerID 0 12 3 nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0,0,100,100)), nsIntRegion(IntRect(10,10,40,40)), nsIntRegion(IntRect(10,60,40,40)), nsIntRegion(IntRect(10,60,40,40)), }; Matrix4x4 transforms[] = { Matrix4x4(), Matrix4x4(), Matrix4x4::Scaling(2, 1, 1), Matrix4x4(), }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers); SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200)); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80)); SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80)); }
void SimpleClientTiledThebesLayer::RenderLayer() { LayerManager::DrawThebesLayerCallback callback = ClientManager()->GetThebesLayerCallback(); void *data = ClientManager()->GetThebesLayerCallbackData(); if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } // First time? Create a content client. if (!mContentClient) { mContentClient = new SimpleTiledContentClient(this, ClientManager()); mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } // If the format changed, nothing is valid if (mContentClient->mTiledBuffer.HasFormatChanged()) { mValidRegion = nsIntRegion(); } nsIntRegion invalidRegion = mVisibleRegion; invalidRegion.Sub(invalidRegion, mValidRegion); if (invalidRegion.IsEmpty()) { return; } // Only paint the mask layer on the first transaction. if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } // SimpleTiledContentClient doesn't support progressive updates or the low // precision buffer yet. MOZ_ASSERT(!gfxPrefs::UseProgressiveTilePainting() && !gfxPrefs::UseLowPrecisionBuffer()); mValidRegion = mVisibleRegion; NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer"); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(); }
void CreateComplexMultiLayerTree() { const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))"; // LayerID 0 12 3 45 6 7 89 nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0,0,300,400)), // root(0) nsIntRegion(IntRect(0,0,100,100)), // thebes(1) in top-left nsIntRegion(IntRect(50,50,200,300)), // container(2) centered in root(0) nsIntRegion(IntRect(50,50,200,300)), // thebes(3) fully occupying parent container(2) nsIntRegion(IntRect(0,200,100,100)), // thebes(4) in bottom-left nsIntRegion(IntRect(200,0,100,400)), // container(5) along the right 100px of root(0) nsIntRegion(IntRect(200,0,100,200)), // container(6) taking up the top half of parent container(5) nsIntRegion(IntRect(200,0,100,200)), // thebes(7) fully occupying parent container(6) nsIntRegion(IntRect(200,200,100,100)), // thebes(8) in bottom-right (below (6)) nsIntRegion(IntRect(200,300,100,100)), // thebes(9) in bottom-right (below (8)) }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID); SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID); SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1); SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 1); SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2); SetScrollableFrameMetrics(layers[8], FrameMetrics::START_SCROLL_ID + 1); SetScrollableFrameMetrics(layers[9], FrameMetrics::START_SCROLL_ID + 3); }
void TextureImageEGL::GetUpdateRegion(nsIntRegion& aForRegion) { if (mTextureState != Valid) { // if the texture hasn't been initialized yet, force the // client to paint everything aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); } // We can only draw a rectangle, not subregions due to // the way that our texture upload functions work. If // needed, we /could/ do multiple texture uploads if we have // non-overlapping rects, but that's a tradeoff. aForRegion = nsIntRegion(aForRegion.GetBounds()); }
void TextureImageDeprecatedTextureHostOGL::UpdateImpl(const SurfaceDescriptor& aImage, nsIntRegion* aRegion, nsIntPoint* aOffset) { if (!mGL) { NS_WARNING("trying to update TextureImageDeprecatedTextureHostOGL without a compositor?"); return; } AutoOpenSurface surf(OPEN_READ_ONLY, aImage); nsIntSize size = surf.Size(); TextureImage::ImageFormat format = surf.ImageFormat(); if (!mTexture || (mTexture->GetSize() != size && !aOffset) || mTexture->GetContentType() != surf.ContentType() || (mTexture->GetImageFormat() != format && mTexture->GetImageFormat() != gfxASurface::ImageFormatUnknown)) { mTexture = mGL->CreateTextureImage(size, surf.ContentType(), WrapMode(mGL, mFlags & TEXTURE_ALLOW_REPEAT), FlagsToGLFlags(mFlags), format); } // XXX this is always just ridiculously slow nsIntRegion updateRegion; if (!aRegion) { updateRegion = nsIntRegion(nsIntRect(0, 0, size.width, size.height)); } else { updateRegion = *aRegion; } nsIntPoint offset; if (aOffset) { offset = *aOffset; } mTexture->DirectUpdate(surf.Get(), updateRegion, offset); mFormat = mTexture->GetTextureFormat(); if (mTexture->InUpdate()) { mTexture->EndUpdate(); } }
already_AddRefed<gfxPattern> BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext, float aOpacity, Layer* aMaskLayer) { if (!mContainer) return nullptr; mContainer->SetImageFactory(mManager->IsCompositingCheap() ? nullptr : BasicManager()->GetImageFactory()); nsRefPtr<gfxASurface> surface; AutoLockImage autoLock(mContainer, getter_AddRefs(surface)); Image *image = autoLock.GetImage(); gfxIntSize size = mSize = autoLock.GetSize(); if (!surface || surface->CairoStatus()) { return nullptr; } nsRefPtr<gfxPattern> pat = new gfxPattern(surface); if (!pat) { return nullptr; } pat->SetFilter(mFilter); // The visible region can extend outside the image, so just draw // within the image bounds. if (aContext) { gfxContext::GraphicsOperator mixBlendMode = GetEffectiveMixBlendMode(); AutoSetOperator setOptimizedOperator(aContext, mixBlendMode != gfxContext::OPERATOR_OVER ? mixBlendMode : GetOperator()); PaintContext(pat, nsIntRegion(nsIntRect(0, 0, size.width, size.height)), aOpacity, aContext, aMaskLayer); GetContainer()->NotifyPaintedImage(image); } return pat.forget(); }
bool AutoLockShmemClient::Update(Image* aImage, uint32_t aContentFlags, gfxPattern* pat) { nsRefPtr<gfxASurface> surface = pat->GetSurface(); if (!aImage) { return false; } nsRefPtr<gfxPattern> pattern = pat ? pat : new gfxPattern(surface); gfxIntSize size = aImage->GetSize(); gfxASurface::gfxContentType contentType = gfxASurface::CONTENT_COLOR_ALPHA; bool isOpaque = (aContentFlags & Layer::CONTENT_OPAQUE); if (surface) { contentType = surface->GetContentType(); } if (contentType != gfxASurface::CONTENT_ALPHA && isOpaque) { contentType = gfxASurface::CONTENT_COLOR; } mTextureClient->EnsureAllocated(gfx::IntSize(size.width, size.height), contentType); OpenMode mode = mTextureClient->GetAccessMode() == TextureClient::ACCESS_READ_WRITE ? OPEN_READ_WRITE : OPEN_READ_ONLY; nsRefPtr<gfxASurface> tmpASurface = ShadowLayerForwarder::OpenDescriptor(mode, *mTextureClient->LockSurfaceDescriptor()); if (!tmpASurface) { return false; } nsRefPtr<gfxContext> tmpCtx = new gfxContext(tmpASurface.get()); tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE); PaintContext(pat, nsIntRegion(nsIntRect(0, 0, size.width, size.height)), 1.0, tmpCtx, nullptr); return true; }
void TiledContentHost::ProcessUploadQueue(nsIntRegion* aNewValidRegion, TiledLayerProperties* aLayerProperties) { if (!mPendingUpload) return; // If we coalesce uploads while the layers' valid region is changing we will // end up trying to upload area outside of the valid region. (bug 756555) mRegionToUpload.And(mRegionToUpload, mMainMemoryTiledBuffer.GetValidRegion()); mVideoMemoryTiledBuffer.Upload(&mMainMemoryTiledBuffer, mMainMemoryTiledBuffer.GetValidRegion(), mRegionToUpload, aLayerProperties->mEffectiveResolution); *aNewValidRegion = mVideoMemoryTiledBuffer.GetValidRegion(); // Release all the tiles by replacing the tile buffer with an empty // tiled buffer. mMainMemoryTiledBuffer = BasicTiledLayerBuffer(); mRegionToUpload = nsIntRegion(); mPendingUpload = false; }
void ClientTiledThebesLayer::RenderLayer() { LayerManager::DrawThebesLayerCallback callback = ClientManager()->GetThebesLayerCallback(); void *data = ClientManager()->GetThebesLayerCallbackData(); if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } if (!mContentClient) { mContentClient = new TiledContentClient(this, ClientManager()); mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } if (mContentClient->mTiledBuffer.HasFormatChanged()) { mValidRegion = nsIntRegion(); } TILING_PRLOG_OBJ(("TILING 0x%p: Initial visible region %s\n", this, tmpstr.get()), mVisibleRegion); TILING_PRLOG_OBJ(("TILING 0x%p: Initial valid region %s\n", this, tmpstr.get()), mValidRegion); nsIntRegion invalidRegion = mVisibleRegion; invalidRegion.Sub(invalidRegion, mValidRegion); if (invalidRegion.IsEmpty()) { EndPaint(true); return; } // Only paint the mask layer on the first transaction. if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition(); // Fast path for no progressive updates, no low-precision updates and no // critical display-port set, or no display-port set, or this is a fixed // position layer/contained in a fixed position layer const FrameMetrics& parentMetrics = GetParent()->GetFrameMetrics(); if ((!gfxPrefs::UseProgressiveTilePainting() && !gfxPrefs::UseLowPrecisionBuffer() && parentMetrics.mCriticalDisplayPort.IsEmpty()) || parentMetrics.mDisplayPort.IsEmpty() || isFixed) { mValidRegion = mVisibleRegion; NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer"); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); return; } // Calculate everything we need to perform the paint. BeginPaint(); if (mPaintData.mPaintFinished) { return; } TILING_PRLOG_OBJ(("TILING 0x%p: Valid region %s\n", this, tmpstr.get()), mValidRegion); TILING_PRLOG_OBJ(("TILING 0x%p: Visible region %s\n", this, tmpstr.get()), mVisibleRegion); // Make sure that tiles that fall outside of the visible region are // discarded on the first update. if (!ClientManager()->IsRepeatTransaction()) { mValidRegion.And(mValidRegion, mVisibleRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { // Make sure that tiles that fall outside of the critical displayport are // discarded on the first update. mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } } nsIntRegion lowPrecisionInvalidRegion; if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { if (gfxPrefs::UseLowPrecisionBuffer()) { // Calculate the invalid region for the low precision buffer lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion); // Remove the valid region from the low precision valid region (we don't // validate this part of the low precision buffer). lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); } // Clip the invalid region to the critical display-port invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) { EndPaint(true); return; } } TILING_PRLOG_OBJ(("TILING 0x%p: Invalid region %s\n", this, tmpstr.get()), invalidRegion); if (!invalidRegion.IsEmpty() && mPaintData.mLowPrecisionPaintCount == 0) { bool updatedBuffer = false; // Only draw progressively when the resolution is unchanged. if (gfxPrefs::UseProgressiveTilePainting() && !ClientManager()->HasShadowTarget() && mContentClient->mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) { // Store the old valid region, then clear it before painting. // We clip the old valid region to the visible region, as it only gets // used to decide stale content (currently valid and previously visible) nsIntRegion oldValidRegion = mContentClient->mTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, mVisibleRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { oldValidRegion.And(oldValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_PRLOG_OBJ(("TILING 0x%p: Progressive update with old valid region %s\n", this, tmpstr.get()), oldValidRegion); updatedBuffer = mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, invalidRegion, oldValidRegion, &mPaintData, callback, data); } else { updatedBuffer = true; mValidRegion = mVisibleRegion; if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_PRLOG_OBJ(("TILING 0x%p: Painting: valid region %s\n", this, tmpstr.get()), mValidRegion); TILING_PRLOG_OBJ(("TILING 0x%p: and invalid region %s\n", this, tmpstr.get()), invalidRegion); mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); } if (updatedBuffer) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); // If there are low precision updates, mark the paint as unfinished and // request a repeat transaction. if (!lowPrecisionInvalidRegion.IsEmpty() && mPaintData.mPaintFinished) { ClientManager()->SetRepeatTransaction(); mPaintData.mLowPrecisionPaintCount = 1; mPaintData.mPaintFinished = false; } // Return so that low precision updates aren't performed in the same // transaction as high-precision updates. EndPaint(false); return; } } TILING_PRLOG_OBJ(("TILING 0x%p: Low-precision valid region is %s\n", this, tmpstr.get()), mLowPrecisionValidRegion); TILING_PRLOG_OBJ(("TILING 0x%p: Low-precision invalid region is %s\n", this, tmpstr.get()), lowPrecisionInvalidRegion); // Render the low precision buffer, if there's area to invalidate and the // visible region is larger than the critical display port. bool updatedLowPrecision = false; if (!lowPrecisionInvalidRegion.IsEmpty() && !nsIntRegion(LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)).Contains(mVisibleRegion)) { nsIntRegion oldValidRegion = mContentClient->mLowPrecisionTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, mVisibleRegion); // If the frame resolution or format have changed, invalidate the buffer if (mContentClient->mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution || mContentClient->mLowPrecisionTiledBuffer.HasFormatChanged()) { if (!mLowPrecisionValidRegion.IsEmpty()) { updatedLowPrecision = true; } oldValidRegion.SetEmpty(); mLowPrecisionValidRegion.SetEmpty(); mContentClient->mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution); lowPrecisionInvalidRegion = mVisibleRegion; } // Invalidate previously valid content that is no longer visible if (mPaintData.mLowPrecisionPaintCount == 1) { mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, mVisibleRegion); } mPaintData.mLowPrecisionPaintCount++; // Remove the valid high-precision region from the invalid low-precision // region. We don't want to spend time drawing things twice. lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); if (!lowPrecisionInvalidRegion.IsEmpty()) { updatedLowPrecision = mContentClient->mLowPrecisionTiledBuffer .ProgressiveUpdate(mLowPrecisionValidRegion, lowPrecisionInvalidRegion, oldValidRegion, &mPaintData, callback, data); } } else if (!mLowPrecisionValidRegion.IsEmpty()) { // Clear the low precision tiled buffer updatedLowPrecision = true; mLowPrecisionValidRegion.SetEmpty(); mContentClient->mLowPrecisionTiledBuffer.ResetPaintedAndValidState(); } // We send a Painted callback if we clear the valid region of the low // precision buffer, so that the shadow buffer's valid region can be updated // and the associated resources can be freed. if (updatedLowPrecision) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); } EndPaint(false); }
void ClientTiledPaintedLayer::RenderLayer() { LayerManager::DrawPaintedLayerCallback callback = ClientManager()->GetPaintedLayerCallback(); void *data = ClientManager()->GetPaintedLayerCallbackData(); if (!callback) { ClientManager()->SetTransactionIncomplete(); return; } if (!mContentClient) { mContentClient = new TiledContentClient(this, ClientManager()); mContentClient->Connect(); ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); MOZ_ASSERT(mContentClient->GetForwarder()); } if (mContentClient->mTiledBuffer.HasFormatChanged()) { mValidRegion = nsIntRegion(); mContentClient->mTiledBuffer.ResetPaintedAndValidState(); } TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str()); TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str()); TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str()); nsIntRegion neededRegion = mVisibleRegion; #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile if (MayResample()) { // If we're resampling then bilinear filtering can read up to 1 pixel // outside of our texture coords. Make the visible region a single rect, // and pad it out by 1 pixel (restricted to tile boundaries) so that // we always have valid content or transparent pixels to sample from. nsIntRect bounds = neededRegion.GetBounds(); nsIntRect wholeTiles = bounds; wholeTiles.Inflate(nsIntSize( gfxPlatform::GetPlatform()->GetTileWidth(), gfxPlatform::GetPlatform()->GetTileHeight())); nsIntRect padded = bounds; padded.Inflate(1); padded.IntersectRect(padded, wholeTiles); neededRegion = padded; } #endif nsIntRegion invalidRegion; invalidRegion.Sub(neededRegion, mValidRegion); if (invalidRegion.IsEmpty()) { EndPaint(); return; } if (!ClientManager()->IsRepeatTransaction()) { // Only paint the mask layer on the first transaction. if (GetMaskLayer()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } // In some cases we can take a fast path and just be done with it. if (UseFastPath()) { TILING_LOG("TILING %p: Taking fast-path\n", this); mValidRegion = neededRegion; mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); return; } // For more complex cases we need to calculate a bunch of metrics before we // can do the paint. BeginPaint(); if (mPaintData.mPaintFinished) { return; } // Make sure that tiles that fall outside of the visible region or outside of the // critical displayport are discarded on the first update. Also make sure that we // only draw stuff inside the critical displayport on the first update. mValidRegion.And(mValidRegion, neededRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str()); TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); } else { if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { invalidRegion.And(invalidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str()); } nsIntRegion lowPrecisionInvalidRegion; if (gfxPrefs::UseLowPrecisionBuffer()) { // Calculate the invalid region for the low precision buffer. Make sure // to remove the valid high-precision area so we don't double-paint it. lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion); lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); } TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str()); bool updatedHighPrecision = RenderHighPrecision(invalidRegion, neededRegion, callback, data); if (updatedHighPrecision) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); if (!mPaintData.mPaintFinished) { // There is still more high-res stuff to paint, so we're not // done yet. A subsequent transaction will take care of this. ClientManager()->SetRepeatTransaction(); return; } } // If there is nothing to draw in low-precision, then we're done. if (lowPrecisionInvalidRegion.IsEmpty()) { EndPaint(); return; } if (updatedHighPrecision) { // If there are low precision updates, but we just did some high-precision // updates, then mark the paint as unfinished and request a repeat transaction. // This is so that we don't perform low-precision updates in the same transaction // as high-precision updates. TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this); ClientManager()->SetRepeatTransaction(); mPaintData.mLowPrecisionPaintCount = 1; mPaintData.mPaintFinished = false; return; } bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion, neededRegion, callback, data); if (updatedLowPrecision) { ClientManager()->Hold(this); mContentClient->UseTiledLayerBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER); if (!mPaintData.mPaintFinished) { // There is still more low-res stuff to paint, so we're not // done yet. A subsequent transaction will take care of this. ClientManager()->SetRepeatTransaction(); return; } } // If we get here, we've done all the high- and low-precision // paints we wanted to do, so we can finish the paint and chill. EndPaint(); }
SimpleTiledLayerTile SimpleTiledLayerBuffer::ValidateTile(SimpleTiledLayerTile aTile, const nsIntPoint& aTileOrigin, const nsIntRegion& aDirtyRegion) { PROFILER_LABEL("SimpleTiledLayerBuffer", "ValidateTile"); static gfx::IntSize kTileSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()); gfx::SurfaceFormat tileFormat = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType()); // if this is true, we're using a separate buffer to do our drawing first bool doBufferedDrawing = true; bool fullPaint = false; RefPtr<TextureClient> textureClient = mManager->GetSimpleTileTexturePool(tileFormat)->GetTextureClientWithAutoRecycle(); if (!textureClient) { NS_WARNING("TextureClient allocation failed"); return SimpleTiledLayerTile(); } if (!textureClient->Lock(OPEN_READ_WRITE)) { NS_WARNING("TextureClient lock failed"); return SimpleTiledLayerTile(); } if (!textureClient->CanExposeDrawTarget()) { doBufferedDrawing = false; } RefPtr<DrawTarget> drawTarget; unsigned char *bufferData = nullptr; // these are set/updated differently based on doBufferedDrawing nsIntRect drawBounds; nsIntRegion drawRegion; nsIntRegion invalidateRegion; RefPtr<DrawTarget> srcDT; uint8_t* srcData = nullptr; int32_t srcStride = 0; gfx::IntSize srcSize; gfx::SurfaceFormat srcFormat = gfx::SurfaceFormat::UNKNOWN; if (doBufferedDrawing) { // try to directly access the pixels of the TextureClient srcDT = textureClient->GetAsDrawTarget(); if (srcDT->LockBits(&srcData, &srcSize, &srcStride, &srcFormat)) { if (!aTile.mCachedBuffer) { aTile.mCachedBuffer = SharedBuffer::Create(srcStride * srcSize.height); fullPaint = true; } bufferData = (unsigned char*) aTile.mCachedBuffer->Data(); drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForData(bufferData, kTileSize, srcStride, tileFormat); if (fullPaint) { drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileSize().width, GetScaledTileSize().height); drawRegion = nsIntRegion(drawBounds); } else { drawBounds = aDirtyRegion.GetBounds(); drawRegion = nsIntRegion(drawBounds); if (GetContentType() == gfxContentType::COLOR_ALPHA) drawTarget->ClearRect(Rect(drawBounds.x - aTileOrigin.x, drawBounds.y - aTileOrigin.y, drawBounds.width, drawBounds.height)); } } else { // failed to obtain the client as an ImageSurface doBufferedDrawing = false; } } // this might get set above if we couldn't extract out a buffer if (!doBufferedDrawing) { drawTarget = textureClient->GetAsDrawTarget(); fullPaint = true; drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileSize().width, GetScaledTileSize().height); drawRegion = nsIntRegion(drawBounds); if (GetContentType() == gfxContentType::COLOR_ALPHA) drawTarget->ClearRect(Rect(0, 0, drawBounds.width, drawBounds.height)); } // do the drawing RefPtr<gfxContext> ctxt = new gfxContext(drawTarget); ctxt->Scale(mResolution, mResolution); ctxt->Translate(gfxPoint(-aTileOrigin.x, -aTileOrigin.y)); mCallback(mThebesLayer, ctxt, drawRegion, fullPaint ? DrawRegionClip::CLIP_NONE : DrawRegionClip::DRAW_SNAPPED, // XXX DRAW or DRAW_SNAPPED? invalidateRegion, mCallbackData); ctxt = nullptr; if (doBufferedDrawing) { memcpy(srcData, bufferData, srcSize.height * srcStride); bufferData = nullptr; srcDT->ReleaseBits(srcData); srcDT = nullptr; } drawTarget = nullptr; textureClient->Unlock(); if (!mCompositableClient->AddTextureClient(textureClient)) { NS_WARNING("Failed to add tile TextureClient [simple]"); return SimpleTiledLayerTile(); } // aTile.mCachedBuffer was set earlier aTile.mTileBuffer = textureClient; aTile.mManager = mManager; aTile.mLastUpdate = TimeStamp::Now(); return aTile; }