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; }
bool ClientTiledPaintedLayer::RenderHighPrecision(const nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { // If we have started drawing low-precision already, then we // shouldn't do anything there. if (mPaintData.mLowPrecisionPaintCount != 0) { return false; } // Only draw progressively when there is something to paint and the // resolution is unchanged if (!aInvalidRegion.IsEmpty() && UseProgressiveDraw() && mContentClient->GetTiledBuffer()->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->GetTiledBuffer()->GetValidRegion(); oldValidRegion.And(oldValidRegion, aVisibleRegion); if (mPaintData.mCriticalDisplayPort) { oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect()); } TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str()); nsIntRegion drawnRegion; bool updatedBuffer = mContentClient->GetTiledBuffer()->ProgressiveUpdate(GetValidRegion(), aInvalidRegion, oldValidRegion, drawnRegion, &mPaintData, aCallback, aCallbackData); AddToValidRegion(drawnRegion); return updatedBuffer; } // Otherwise do a non-progressive paint. We must do this even when // the region to paint is empty as the valid region may have shrunk. nsIntRegion validRegion = aVisibleRegion; if (mPaintData.mCriticalDisplayPort) { validRegion.AndWith(mPaintData.mCriticalDisplayPort->ToUnknownRect()); } SetValidRegion(validRegion); TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str()); TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(GetValidRegion()).c_str()); mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution); mContentClient->GetTiledBuffer()->PaintThebes(GetValidRegion(), aInvalidRegion, aInvalidRegion, aCallback, aCallbackData); mPaintData.mPaintFinished = true; return true; }
bool ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { // If we have no high-precision stuff to draw, or we have started drawing low-precision // already, then we shouldn't do anything there. if (aInvalidRegion.IsEmpty() || mPaintData.mLowPrecisionPaintCount != 0) { return false; } // Only draw progressively when the resolution is unchanged, and we're not // in a reftest scenario (that's what the HasShadowManager() check is for). if (gfxPlatform::GetPlatform()->UseProgressivePaint() && !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, aVisibleRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { oldValidRegion.And(oldValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str()); return mContentClient->mTiledBuffer.ProgressiveUpdate(mValidRegion, aInvalidRegion, oldValidRegion, &mPaintData, aCallback, aCallbackData); } // Otherwise do a non-progressive paint mValidRegion = aVisibleRegion; if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, LayerIntRect::ToUntyped(mPaintData.mCriticalDisplayPort)); } TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str()); TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str()); mContentClient->mTiledBuffer.SetFrameResolution(mPaintData.mResolution); mContentClient->mTiledBuffer.PaintThebes(mValidRegion, aInvalidRegion, aCallback, aCallbackData); return true; }
bool nsViewManager::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) { if (!aWidget || !mContext) return false; NS_ASSERTION(IsPaintingAllowed(), "shouldn't be receiving paint events while painting is disallowed!"); // Get the view pointer here since NS_WILL_PAINT might have // destroyed it during CallWillPaintOnObservers (bug 378273). nsView* view = nsView::GetViewFor(aWidget); if (view && !aRegion.IsEmpty()) { Refresh(view, aRegion); } return true; }
bool ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { // If we have no high-precision stuff to draw, or we have started drawing low-precision // already, then we shouldn't do anything there. if (aInvalidRegion.IsEmpty() || mPaintData.mLowPrecisionPaintCount != 0) { return false; } // Only draw progressively when the resolution is unchanged if (UseProgressiveDraw() && mContentClient->GetTiledBuffer()->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->GetTiledBuffer()->GetValidRegion(); oldValidRegion.And(oldValidRegion, aVisibleRegion); if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort.ToUnknownRect()); } TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str()); return mContentClient->GetTiledBuffer()->ProgressiveUpdate(mValidRegion, aInvalidRegion, oldValidRegion, &mPaintData, aCallback, aCallbackData); } // Otherwise do a non-progressive paint mValidRegion = aVisibleRegion; if (!mPaintData.mCriticalDisplayPort.IsEmpty()) { mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort.ToUnknownRect()); } TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str()); TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str()); mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution); mContentClient->GetTiledBuffer()->PaintThebes(mValidRegion, aInvalidRegion, aInvalidRegion, aCallback, aCallbackData); mPaintData.mPaintFinished = true; return true; }
nsIntRegion nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame, const nsPoint& aToReferenceFrame, const nsIntRegion& aInvalidRegion) { if (aInvalidRegion.IsEmpty()) { return nsIntRect(); } // Don't bother calling GetEffectProperties; the filter property should // already have been set up during reflow/ComputeFrameEffectsRect nsIFrame* firstFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame); if (!prop || !prop->IsInObserverLists()) { return aInvalidRegion; } int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); if (!prop || !prop->ReferencesValidResources()) { // The frame is either not there or not currently available, // perhaps because we're in the middle of tearing stuff down. // Be conservative, return our visual overflow rect relative // to the reference frame. nsRect overflow = aFrame->GetVisualOverflowRect() + aToReferenceFrame; return overflow.ToOutsidePixels(appUnitsPerDevPixel); } // Convert aInvalidRegion into bounding box frame space in app units: nsPoint toBoundingBox = aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame); // The initial rect was relative to the reference frame, so we need to // remove that offset to get a rect relative to the current frame. toBoundingBox -= aToReferenceFrame; nsRegion preEffectsRegion = aInvalidRegion.ToAppUnits(appUnitsPerDevPixel).MovedBy(toBoundingBox); // Adjust the dirty area for effects, and shift it back to being relative to // the reference frame. nsRegion result = nsFilterInstance::GetPostFilterDirtyArea(firstFrame, preEffectsRegion).MovedBy(-toBoundingBox); // Return the result, in pixels relative to the reference frame. return result.ToOutsidePixels(appUnitsPerDevPixel); }
static void SubtractTransformedRegion(nsIntRegion& aRegion, const nsIntRegion& aRegionToSubtract, const Matrix4x4& aTransform) { if (aRegionToSubtract.IsEmpty()) { return; } // For each rect in the region, find out its bounds in screen space and // subtract it from the screen region. nsIntRegionRectIterator it(aRegionToSubtract); while (const IntRect* rect = it.Next()) { Rect incompleteRect = aTransform.TransformBounds(ToRect(*rect)); aRegion.Sub(aRegion, IntRect(incompleteRect.x, incompleteRect.y, incompleteRect.width, incompleteRect.height)); } }
bool nsViewManager::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion, uint32_t aFlags) { if (!aWidget || !mContext) return false; NS_ASSERTION(IsPaintingAllowed(), "shouldn't be receiving paint events while painting is disallowed!"); if (!(aFlags & nsIWidgetListener::SENT_WILL_PAINT) && !IsRefreshDriverPaintingEnabled()) { WillPaintWindow(aWidget, (aFlags & nsIWidgetListener::WILL_SEND_DID_PAINT)); } // Get the view pointer here since NS_WILL_PAINT might have // destroyed it during CallWillPaintOnObservers (bug 378273). nsView* view = nsView::GetViewFor(aWidget); if (view && !aRegion.IsEmpty()) { Refresh(view, aRegion, (aFlags & nsIWidgetListener::WILL_SEND_DID_PAINT)); } return true; }
/* static */ void LayerManagerComposite::ComputeRenderIntegrityInternal(Layer* aLayer, nsIntRegion& aScreenRegion, nsIntRegion& aLowPrecisionScreenRegion, const Matrix4x4& aTransform) { if (aLayer->GetOpacity() <= 0.f || (aScreenRegion.IsEmpty() && aLowPrecisionScreenRegion.IsEmpty())) { return; } // If the layer's a container, recurse into all of its children ContainerLayer* container = aLayer->AsContainerLayer(); if (container) { // Accumulate the transform of intermediate surfaces Matrix4x4 transform = aTransform; if (container->UseIntermediateSurface()) { transform = aLayer->GetEffectiveTransform(); transform = aTransform * transform; } for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) { ComputeRenderIntegrityInternal(child, aScreenRegion, aLowPrecisionScreenRegion, transform); } return; } // Only painted layers can be incomplete PaintedLayer* paintedLayer = aLayer->AsPaintedLayer(); if (!paintedLayer) { return; } // See if there's any incomplete rendering nsIntRegion incompleteRegion = aLayer->GetEffectiveVisibleRegion(); incompleteRegion.Sub(incompleteRegion, paintedLayer->GetValidRegion()); if (!incompleteRegion.IsEmpty()) { // Calculate the transform to get between screen and layer space Matrix4x4 transformToScreen = aLayer->GetEffectiveTransform(); transformToScreen = aTransform * transformToScreen; SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen); // See if there's any incomplete low-precision rendering TiledContentHost* composer = nullptr; LayerComposite* shadow = aLayer->AsLayerComposite(); if (shadow) { composer = shadow->GetCompositableHost()->AsTiledContentHost(); if (composer) { incompleteRegion.Sub(incompleteRegion, composer->GetValidLowPrecisionRegion()); if (!incompleteRegion.IsEmpty()) { SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen); } } } // If we can't get a valid low precision region, assume it's the same as // the high precision region. if (!composer) { SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen); } } }
bool HwcComposer2D::PrepareLayerList(Layer* aLayer, const nsIntRect& aClip, const Matrix& aParentTransform, bool aFindSidebandStreams) { // NB: we fall off this path whenever there are container layers // that require intermediate surfaces. That means all the // GetEffective*() coordinates are relative to the framebuffer. bool fillColor = false; const nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion(); if (visibleRegion.IsEmpty()) { return true; } uint8_t opacity = std::min(0xFF, (int)(aLayer->GetEffectiveOpacity() * 256.0)); if (opacity == 0) { LOGD("%s Layer has zero opacity; skipping", aLayer->Name()); return true; } if (!mHal->SupportTransparency() && opacity < 0xFF && !aFindSidebandStreams) { LOGD("%s Layer has planar semitransparency which is unsupported by hwcomposer", aLayer->Name()); return false; } if (aLayer->GetMaskLayer() && !aFindSidebandStreams) { LOGD("%s Layer has MaskLayer which is unsupported by hwcomposer", aLayer->Name()); return false; } nsIntRect clip; nsIntRect layerClip = aLayer->GetLocalClipRect().valueOr(ParentLayerIntRect()).ToUnknownRect(); nsIntRect* layerClipPtr = aLayer->GetLocalClipRect() ? &layerClip : nullptr; if (!HwcUtils::CalculateClipRect(aParentTransform, layerClipPtr, aClip, &clip)) { LOGD("%s Clip rect is empty. Skip layer", aLayer->Name()); return true; } // HWC supports only the following 2D transformations: // // Scaling via the sourceCrop and displayFrame in HwcLayer // Translation via the sourceCrop and displayFrame in HwcLayer // Rotation (in square angles only) via the HWC_TRANSFORM_ROT_* flags // Reflection (horizontal and vertical) via the HWC_TRANSFORM_FLIP_* flags // // A 2D transform with PreservesAxisAlignedRectangles() has all the attributes // above Matrix layerTransform; if (!aLayer->GetEffectiveTransform().Is2D(&layerTransform) || !layerTransform.PreservesAxisAlignedRectangles()) { LOGD("Layer EffectiveTransform has a 3D transform or a non-square angle rotation"); return false; } Matrix layerBufferTransform; if (!aLayer->GetEffectiveTransformForBuffer().Is2D(&layerBufferTransform) || !layerBufferTransform.PreservesAxisAlignedRectangles()) { LOGD("Layer EffectiveTransformForBuffer has a 3D transform or a non-square angle rotation"); return false; } if (ContainerLayer* container = aLayer->AsContainerLayer()) { if (container->UseIntermediateSurface() && !aFindSidebandStreams) { LOGD("Container layer needs intermediate surface"); return false; } AutoTArray<Layer*, 12> children; container->SortChildrenBy3DZOrder(children); for (uint32_t i = 0; i < children.Length(); i++) { if (!PrepareLayerList(children[i], clip, layerTransform, aFindSidebandStreams) && !aFindSidebandStreams) { return false; } } return true; } LayerRenderState state = aLayer->GetRenderState(); #if ANDROID_VERSION >= 21 if (!state.GetGrallocBuffer() && !state.GetSidebandStream().IsValid()) { #else if (!state.GetGrallocBuffer()) { #endif if (aLayer->AsColorLayer() && mColorFill) { fillColor = true; } else { LOGD("%s Layer doesn't have a gralloc buffer", aLayer->Name()); return false; } } nsIntRect visibleRect = visibleRegion.GetBounds(); nsIntRect bufferRect; if (fillColor) { bufferRect = nsIntRect(visibleRect); } else { nsIntRect layerRect; if (state.mHasOwnOffset) { bufferRect = nsIntRect(state.mOffset.x, state.mOffset.y, state.mSize.width, state.mSize.height); layerRect = bufferRect; } else { //Since the buffer doesn't have its own offset, assign the whole //surface size as its buffer bounds bufferRect = nsIntRect(0, 0, state.mSize.width, state.mSize.height); layerRect = bufferRect; if (aLayer->GetType() == Layer::TYPE_IMAGE) { ImageLayer* imageLayer = static_cast<ImageLayer*>(aLayer); if(imageLayer->GetScaleMode() != ScaleMode::SCALE_NONE) { layerRect = nsIntRect(0, 0, imageLayer->GetScaleToSize().width, imageLayer->GetScaleToSize().height); } } } // In some cases the visible rect assigned to the layer can be larger // than the layer's surface, e.g., an ImageLayer with a small Image // in it. visibleRect.IntersectRect(visibleRect, layerRect); } // Buffer rotation is not to be confused with the angled rotation done by a transform matrix // It's a fancy PaintedLayer feature used for scrolling if (state.BufferRotated()) { LOGD("%s Layer has a rotated buffer", aLayer->Name()); return false; } const bool needsYFlip = state.OriginBottomLeft() ? true : false; hwc_rect_t sourceCrop, displayFrame; if(!HwcUtils::PrepareLayerRects(visibleRect, layerTransform, layerBufferTransform, clip, bufferRect, needsYFlip, &(sourceCrop), &(displayFrame))) { return true; } // OK! We can compose this layer with hwc. int current = mList ? mList->numHwLayers : 0; // Do not compose any layer below full-screen Opaque layer // Note: It can be generalized to non-fullscreen Opaque layers. bool isOpaque = opacity == 0xFF && (state.mFlags & LayerRenderStateFlags::OPAQUE); // Currently we perform opacity calculation using the *bounds* of the layer. // We can only make this assumption if we're not dealing with a complex visible region. bool isSimpleVisibleRegion = visibleRegion.Contains(visibleRect); if (current && isOpaque && isSimpleVisibleRegion) { nsIntRect displayRect = nsIntRect(displayFrame.left, displayFrame.top, displayFrame.right - displayFrame.left, displayFrame.bottom - displayFrame.top); if (displayRect.Contains(mScreenRect)) { // In z-order, all previous layers are below // the current layer. We can ignore them now. mList->numHwLayers = current = 0; mHwcLayerMap.Clear(); } } if (!mList || current >= mMaxLayerCount) { if (!ReallocLayerList() || current >= mMaxLayerCount) { LOGE("PrepareLayerList failed! Could not increase the maximum layer count"); return false; } } HwcLayer& hwcLayer = mList->hwLayers[current]; hwcLayer.displayFrame = displayFrame; mHal->SetCrop(hwcLayer, sourceCrop); buffer_handle_t handle = nullptr; #if ANDROID_VERSION >= 21 if (state.GetSidebandStream().IsValid()) { handle = state.GetSidebandStream().GetRawNativeHandle(); } else if (state.GetGrallocBuffer()) { handle = state.GetGrallocBuffer()->getNativeBuffer()->handle; } #else if (state.GetGrallocBuffer()) { handle = state.GetGrallocBuffer()->getNativeBuffer()->handle; } #endif hwcLayer.handle = handle; hwcLayer.flags = 0; hwcLayer.hints = 0; hwcLayer.blending = isOpaque ? HWC_BLENDING_NONE : HWC_BLENDING_PREMULT; #if ANDROID_VERSION >= 17 hwcLayer.compositionType = HWC_FRAMEBUFFER; #if ANDROID_VERSION >= 21 if (state.GetSidebandStream().IsValid()) { hwcLayer.compositionType = HWC_SIDEBAND; } #endif hwcLayer.acquireFenceFd = -1; hwcLayer.releaseFenceFd = -1; #if ANDROID_VERSION >= 18 hwcLayer.planeAlpha = opacity; #endif #else hwcLayer.compositionType = HwcUtils::HWC_USE_COPYBIT; #endif if (!fillColor) { if (state.FormatRBSwapped()) { if (!mRBSwapSupport) { LOGD("No R/B swap support in H/W Composer"); return false; } hwcLayer.flags |= HwcUtils::HWC_FORMAT_RB_SWAP; } // Translation and scaling have been addressed in PrepareLayerRects(). // Given the above and that we checked for PreservesAxisAlignedRectangles() // the only possible transformations left to address are // square angle rotation and horizontal/vertical reflection. // // The rotation and reflection permutations total 16 but can be // reduced to 8 transformations after eliminating redundancies. // // All matrices represented here are in the form // // | xx xy | // | yx yy | // // And ignore scaling. // // Reflection is applied before rotation gfx::Matrix rotation = layerTransform; // Compute fuzzy zero like PreservesAxisAlignedRectangles() if (fabs(rotation._11) < 1e-6) { if (rotation._21 < 0) { if (rotation._12 > 0) { // 90 degree rotation // // | 0 -1 | // | 1 0 | // hwcLayer.transform = HWC_TRANSFORM_ROT_90; LOGD("Layer rotated 90 degrees"); } else { // Horizontal reflection then 90 degree rotation // // | 0 -1 | | -1 0 | = | 0 -1 | // | 1 0 | | 0 1 | | -1 0 | // // same as vertical reflection then 270 degree rotation // // | 0 1 | | 1 0 | = | 0 -1 | // | -1 0 | | 0 -1 | | -1 0 | // hwcLayer.transform = HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_FLIP_H; LOGD("Layer vertically reflected then rotated 270 degrees"); } } else { if (rotation._12 < 0) { // 270 degree rotation // // | 0 1 | // | -1 0 | // hwcLayer.transform = HWC_TRANSFORM_ROT_270; LOGD("Layer rotated 270 degrees"); } else { // Vertical reflection then 90 degree rotation // // | 0 1 | | -1 0 | = | 0 1 | // | -1 0 | | 0 1 | | 1 0 | // // Same as horizontal reflection then 270 degree rotation // // | 0 -1 | | 1 0 | = | 0 1 | // | 1 0 | | 0 -1 | | 1 0 | // hwcLayer.transform = HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_FLIP_V; LOGD("Layer horizontally reflected then rotated 270 degrees"); } } } else if (rotation._11 < 0) { if (rotation._22 > 0) { // Horizontal reflection // // | -1 0 | // | 0 1 | // hwcLayer.transform = HWC_TRANSFORM_FLIP_H; LOGD("Layer rotated 180 degrees"); } else { // 180 degree rotation // // | -1 0 | // | 0 -1 | // // Same as horizontal and vertical reflection // // | -1 0 | | 1 0 | = | -1 0 | // | 0 1 | | 0 -1 | | 0 -1 | // hwcLayer.transform = HWC_TRANSFORM_ROT_180; LOGD("Layer rotated 180 degrees"); } } else { if (rotation._22 < 0) { // Vertical reflection // // | 1 0 | // | 0 -1 | // hwcLayer.transform = HWC_TRANSFORM_FLIP_V; LOGD("Layer rotated 180 degrees"); } else { // No rotation or reflection // // | 1 0 | // | 0 1 | // hwcLayer.transform = 0; } } const bool needsYFlip = state.OriginBottomLeft() ? true : false; if (needsYFlip) { // Invert vertical reflection flag if it was already set hwcLayer.transform ^= HWC_TRANSFORM_FLIP_V; } hwc_region_t region; if (visibleRegion.GetNumRects() > 1) { mVisibleRegions.push_back(HwcUtils::RectVector()); HwcUtils::RectVector* visibleRects = &(mVisibleRegions.back()); bool isVisible = false; if(!HwcUtils::PrepareVisibleRegion(visibleRegion, layerTransform, layerBufferTransform, clip, bufferRect, visibleRects, isVisible)) { LOGD("A region of layer is too small to be rendered by HWC"); return false; } if (!isVisible) { // Layer is not visible, no need to render it return true; } region.numRects = visibleRects->size(); region.rects = &((*visibleRects)[0]); } else { region.numRects = 1; region.rects = &(hwcLayer.displayFrame); } hwcLayer.visibleRegionScreen = region; } else { hwcLayer.flags |= HwcUtils::HWC_COLOR_FILL; ColorLayer* colorLayer = aLayer->AsColorLayer(); if (colorLayer->GetColor().a < 1.0) { LOGD("Color layer has semitransparency which is unsupported"); return false; } hwcLayer.transform = colorLayer->GetColor().ToABGR(); } #if ANDROID_VERSION >= 21 if (aFindSidebandStreams && hwcLayer.compositionType == HWC_SIDEBAND) { mCachedSidebandLayers.AppendElement(hwcLayer); } #endif mHwcLayerMap.AppendElement(static_cast<LayerComposite*>(aLayer->ImplData())); mList->numHwLayers++; return true; } #if ANDROID_VERSION >= 17 bool HwcComposer2D::TryHwComposition(nsScreenGonk* aScreen) { DisplaySurface* dispSurface = aScreen->GetDisplaySurface(); if (!(dispSurface && dispSurface->lastHandle)) { LOGD("H/W Composition failed. DispSurface not initialized."); return false; } // Add FB layer int idx = mList->numHwLayers++; if (idx >= mMaxLayerCount) { if (!ReallocLayerList() || idx >= mMaxLayerCount) { LOGE("TryHwComposition failed! Could not add FB layer"); return false; } } Prepare(dispSurface->lastHandle, -1, aScreen); /* Possible composition paths, after hwc prepare: 1. GPU Composition 2. BLIT Composition 3. Full OVERLAY Composition 4. Partial OVERLAY Composition (GPU + OVERLAY) */ bool gpuComposite = false; bool blitComposite = false; bool overlayComposite = true; for (int j=0; j < idx; j++) { if (mList->hwLayers[j].compositionType == HWC_FRAMEBUFFER || mList->hwLayers[j].compositionType == HWC_BLIT) { // Full OVERLAY composition is not possible on this frame // It is either GPU / BLIT / partial OVERLAY composition. overlayComposite = false; break; } } if (!overlayComposite) { for (int k=0; k < idx; k++) { switch (mList->hwLayers[k].compositionType) { case HWC_FRAMEBUFFER: gpuComposite = true; break; case HWC_BLIT: blitComposite = true; break; #if ANDROID_VERSION >= 21 case HWC_SIDEBAND: #endif case HWC_OVERLAY: { // HWC will compose HWC_OVERLAY layers in partial // Overlay Composition, set layer composition flag // on mapped LayerComposite to skip GPU composition mHwcLayerMap[k]->SetLayerComposited(true); uint8_t opacity = std::min(0xFF, (int)(mHwcLayerMap[k]->GetLayer()->GetEffectiveOpacity() * 256.0)); if ((mList->hwLayers[k].hints & HWC_HINT_CLEAR_FB) && (opacity == 0xFF)) { // Clear visible rect on FB with transparent pixels. hwc_rect_t r = mList->hwLayers[k].displayFrame; mHwcLayerMap[k]->SetClearRect(nsIntRect(r.left, r.top, r.right - r.left, r.bottom - r.top)); } break; } default: break; } } if (gpuComposite) { // GPU or partial OVERLAY Composition return false; } else if (blitComposite) { // BLIT Composition, flip DispSurface target GetGonkDisplay()->UpdateDispSurface(aScreen->GetEGLDisplay(), aScreen->GetEGLSurface()); DisplaySurface* dispSurface = aScreen->GetDisplaySurface(); if (!dispSurface) { LOGE("H/W Composition failed. NULL DispSurface."); return false; } mList->hwLayers[idx].handle = dispSurface->lastHandle; mList->hwLayers[idx].acquireFenceFd = dispSurface->GetPrevDispAcquireFd(); } } // BLIT or full OVERLAY Composition return Commit(aScreen); }
bool ClientTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOldValidRegion, nsIntRegion& aRegionToPaint, BasicTiledLayerPaintData* aPaintData, bool aIsRepeated) { aRegionToPaint = aInvalidRegion; // If the composition bounds rect is empty, we can't make any sensible // decision about how to update coherently. In this case, just update // everything in one transaction. if (aPaintData->mCompositionBounds.IsEmpty()) { aPaintData->mPaintFinished = true; return false; } // If this is a low precision buffer, we force progressive updates. The // assumption is that the contents is less important, so visual coherency // is lower priority than speed. bool drawingLowPrecision = IsLowPrecision(); // Find out if we have any non-stale content to update. nsIntRegion staleRegion; staleRegion.And(aInvalidRegion, aOldValidRegion); // Find out the current view transform to determine which tiles to draw // first, and see if we should just abort this paint. Aborting is usually // caused by there being an incoming, more relevant paint. ParentLayerRect compositionBounds; CSSToParentLayerScale zoom; #if defined(MOZ_WIDGET_ANDROID) bool abortPaint = mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion), compositionBounds, zoom, !drawingLowPrecision); #else MOZ_ASSERT(mSharedFrameMetricsHelper); ContainerLayer* parent = mThebesLayer->AsLayer()->GetParent(); bool abortPaint = mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics( parent, !staleRegion.Contains(aInvalidRegion), drawingLowPrecision, compositionBounds, zoom); #endif if (abortPaint) { // We ignore if front-end wants to abort if this is the first, // non-low-precision paint, as in that situation, we're about to override // front-end's page/viewport metrics. if (!aPaintData->mFirstPaint || drawingLowPrecision) { PROFILER_LABEL("ContentClient", "Abort painting"); aRegionToPaint.SetEmpty(); return aIsRepeated; } } // Transform the screen coordinates into transformed layout device coordinates. LayoutDeviceRect transformedCompositionBounds = TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset, aPaintData->mResolution, aPaintData->mTransformParentLayerToLayout); // Paint tiles that have stale content or that intersected with the screen // at the time of issuing the draw command in a single transaction first. // This is to avoid rendering glitches on animated page content, and when // layers change size/shape. LayoutDeviceRect coherentUpdateRect = transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds); nsIntRect roundedCoherentUpdateRect = LayoutDeviceIntRect::ToUntyped(RoundedOut(coherentUpdateRect)); aRegionToPaint.And(aInvalidRegion, roundedCoherentUpdateRect); aRegionToPaint.Or(aRegionToPaint, staleRegion); bool drawingStale = !aRegionToPaint.IsEmpty(); if (!drawingStale) { aRegionToPaint = aInvalidRegion; } // Prioritise tiles that are currently visible on the screen. bool paintVisible = false; if (aRegionToPaint.Intersects(roundedCoherentUpdateRect)) { aRegionToPaint.And(aRegionToPaint, roundedCoherentUpdateRect); paintVisible = true; } // Paint area that's visible and overlaps previously valid content to avoid // visible glitches in animated elements, such as gifs. bool paintInSingleTransaction = paintVisible && (drawingStale || aPaintData->mFirstPaint); // The following code decides what order to draw tiles in, based on the // current scroll direction of the primary scrollable layer. NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!"); nsIntRect paintBounds = aRegionToPaint.GetBounds(); int startX, incX, startY, incY; int tileLength = GetScaledTileLength(); if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) { startX = RoundDownToTileEdge(paintBounds.x); incX = tileLength; } else { startX = RoundDownToTileEdge(paintBounds.XMost() - 1); incX = -tileLength; } if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) { startY = RoundDownToTileEdge(paintBounds.y); incY = tileLength; } else { startY = RoundDownToTileEdge(paintBounds.YMost() - 1); incY = -tileLength; } // Find a tile to draw. nsIntRect tileBounds(startX, startY, tileLength, tileLength); int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x; int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y; // This loop will always terminate, as there is at least one tile area // along the first/last row/column intersecting with regionToPaint, or its // bounds would have been smaller. while (true) { aRegionToPaint.And(aInvalidRegion, tileBounds); if (!aRegionToPaint.IsEmpty()) { break; } if (Abs(scrollDiffY) >= Abs(scrollDiffX)) { tileBounds.x += incX; } else { tileBounds.y += incY; } } if (!aRegionToPaint.Contains(aInvalidRegion)) { // The region needed to paint is larger then our progressive chunk size // therefore update what we want to paint and ask for a new paint transaction. // If we need to draw more than one tile to maintain coherency, make // sure it happens in the same transaction by requesting this work be // repeated immediately. // If this is unnecessary, the remaining work will be done tile-by-tile in // subsequent transactions. if (!drawingLowPrecision && paintInSingleTransaction) { return true; } mManager->SetRepeatTransaction(); return false; } // We're not repeating painting and we've not requested a repeat transaction, // so the paint is finished. If there's still a separate low precision // paint to do, it will get marked as unfinished later. aPaintData->mPaintFinished = true; return false; }