void LayerManagerComposite::UpdateAndRender() { nsIntRegion invalid; bool didEffectiveTransforms = false; nsIntRegion opaque; LayerIntRegion visible; PostProcessLayers(mRoot, opaque, visible, Nothing()); if (mClonedLayerTreeProperties) { // Effective transforms are needed by ComputeDifferences(). mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4()); didEffectiveTransforms = true; // We need to compute layer tree differences even if we're not going to // immediately use the resulting damage area, since ComputeDifferences // is also responsible for invalidates intermediate surfaces in // ContainerLayers. nsIntRegion changed = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged); if (mTarget) { // Since we're composing to an external target, we're not going to use // the damage region from layers changes - we want to composite // everything in the target bounds. Instead we accumulate the layers // damage region for the next window composite. mInvalidRegion.Or(mInvalidRegion, changed); } else { invalid = Move(changed); } } if (mTarget) { invalid.Or(invalid, mTargetBounds); } else { // If we didn't have a previous layer tree, invalidate the entire render // area. if (!mClonedLayerTreeProperties) { invalid.Or(invalid, mRenderBounds); } // Add any additional invalid rects from the window manager or previous // damage computed during ComposeToTarget(). invalid.Or(invalid, mInvalidRegion); mInvalidRegion.SetEmpty(); } if (invalid.IsEmpty() && !mWindowOverlayChanged) { // Composition requested, but nothing has changed. Don't do any work. mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot()); return; } // We don't want our debug overlay to cause more frames to happen // so we will invalidate after we've decided if something changed. InvalidateDebugOverlay(invalid, mRenderBounds); if (!didEffectiveTransforms) { // The results of our drawing always go directly into a pixel buffer, // so we don't need to pass any global transform here. mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4()); } Render(invalid, opaque); #if defined(MOZ_WIDGET_ANDROID) RenderToPresentationSurface(); #endif mGeometryChanged = false; mWindowOverlayChanged = false; // Update cached layer tree information. mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot()); }
void LayerManagerComposite::PostProcessLayers(Layer* aLayer, nsIntRegion& aOpaqueRegion, LayerIntRegion& aVisibleRegion, const Maybe<ParentLayerIntRect>& aClipFromAncestors) { if (aLayer->Extend3DContext()) { // For layers participating 3D rendering context, their visible // region should be empty (invisible), so we pass through them // without doing anything. // Direct children of the establisher may have a clip, becaue the // item containing it; ex. of nsHTMLScrollFrame, may give it one. Maybe<ParentLayerIntRect> layerClip = aLayer->AsHostLayer()->GetShadowClipRect(); Maybe<ParentLayerIntRect> ancestorClipForChildren = IntersectMaybeRects(layerClip, aClipFromAncestors); MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(), "Only direct children of the establisher could have a clip"); for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { PostProcessLayers(child, aOpaqueRegion, aVisibleRegion, ancestorClipForChildren); } return; } nsIntRegion localOpaque; // Treat layers on the path to the root of the 3D rendering context as // a giant layer if it is a leaf. Matrix4x4 transform = GetAccTransformIn3DContext(aLayer); Matrix transform2d; Maybe<IntPoint> integerTranslation; // If aLayer has a simple transform (only an integer translation) then we // can easily convert aOpaqueRegion into pre-transform coordinates and include // that region. if (transform.Is2D(&transform2d)) { if (transform2d.IsIntegerTranslation()) { integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation())); localOpaque = aOpaqueRegion; localOpaque.MoveBy(-*integerTranslation); } } // Compute a clip that's the combination of our layer clip with the clip // from our ancestors. LayerComposite* composite = static_cast<LayerComposite*>(aLayer->AsHostLayer()); Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect(); MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(), "The layer with a clip should not participate " "a 3D rendering context"); Maybe<ParentLayerIntRect> outsideClip = IntersectMaybeRects(layerClip, aClipFromAncestors); // Convert the combined clip into our pre-transform coordinate space, so // that it can later be intersected with our visible region. // If our transform is a perspective, there's no meaningful insideClip rect // we can compute (it would need to be a cone). Maybe<LayerIntRect> insideClip; if (outsideClip && !transform.HasPerspectiveComponent()) { Matrix4x4 inverse = transform; if (inverse.Invert()) { Maybe<LayerRect> insideClipFloat = UntransformBy(ViewAs<ParentLayerToLayerMatrix4x4>(inverse), ParentLayerRect(*outsideClip), LayerRect::MaxIntRect()); if (insideClipFloat) { insideClipFloat->RoundOut(); LayerIntRect insideClipInt; if (insideClipFloat->ToIntRect(&insideClipInt)) { insideClip = Some(insideClipInt); } } } } Maybe<ParentLayerIntRect> ancestorClipForChildren; if (insideClip) { ancestorClipForChildren = Some(ViewAs<ParentLayerPixel>(*insideClip, PixelCastJustification::MovingDownToChildren)); } // Save the value of localOpaque, which currently stores the region obscured // by siblings (and uncles and such), before our descendants contribute to it. nsIntRegion obscured = localOpaque; // Recurse on our descendants, in front-to-back order. In this process: // - Occlusions are computed for them, and they contribute to localOpaque. // - They recalculate their visible regions, taking ancestorClipForChildren // into account, and accumulate them into descendantsVisibleRegion. LayerIntRegion descendantsVisibleRegion; bool hasPreserve3DChild = false; for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { PostProcessLayers(child, localOpaque, descendantsVisibleRegion, ancestorClipForChildren); if (child->Extend3DContext()) { hasPreserve3DChild = true; } } // Recalculate our visible region. LayerIntRegion visible = composite->GetShadowVisibleRegion(); // If we have descendants, throw away the visible region stored on this // layer, and use the region accumulated by our descendants instead. if (aLayer->GetFirstChild() && !hasPreserve3DChild) { visible = descendantsVisibleRegion; } // Subtract any areas that we know to be opaque. if (!obscured.IsEmpty()) { visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured)); } // Clip the visible region using the combined clip. if (insideClip) { visible.AndWith(*insideClip); } composite->SetShadowVisibleRegion(visible); // Transform the newly calculated visible region into our parent's space, // apply our clip to it (if any), and accumulate it into |aVisibleRegion| // for the caller to use. ParentLayerIntRegion visibleParentSpace = TransformBy( ViewAs<LayerToParentLayerMatrix4x4>(transform), visible); if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) { visibleParentSpace.AndWith(*clipRect); } aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace, PixelCastJustification::MovingDownToChildren)); // If we have a simple transform, then we can add our opaque area into // aOpaqueRegion. if (integerTranslation && !aLayer->HasMaskLayers() && aLayer->IsOpaqueForVisibility()) { if (aLayer->IsOpaque()) { localOpaque.OrWith(composite->GetFullyRenderedRegion()); } localOpaque.MoveBy(*integerTranslation); if (layerClip) { localOpaque.AndWith(layerClip->ToUnknownRect()); } aOpaqueRegion.OrWith(localOpaque); } }
void LayerManagerComposite::RenderToPresentationSurface() { #ifdef MOZ_WIDGET_ANDROID if (!AndroidBridge::Bridge()) { return; } void* window = AndroidBridge::Bridge()->GetPresentationWindow(); if (!window) { return; } EGLSurface surface = AndroidBridge::Bridge()->GetPresentationSurface(); if (!surface) { //create surface; surface = GLContextProviderEGL::CreateEGLSurface(window); if (!surface) { return; } AndroidBridge::Bridge()->SetPresentationSurface(surface); } CompositorOGL* compositor = static_cast<CompositorOGL*>(mCompositor.get()); GLContext* gl = compositor->gl(); GLContextEGL* egl = GLContextEGL::Cast(gl); if (!egl) { return; } const IntSize windowSize = AndroidBridge::Bridge()->GetNativeWindowSize(window); #elif defined(MOZ_WIDGET_GONK) CompositorOGL* compositor = static_cast<CompositorOGL*>(mCompositor.get()); nsScreenGonk* screen = static_cast<nsWindow*>(mCompositor->GetWidget())->GetScreen(); if (!screen->IsPrimaryScreen()) { // Only primary screen support mirroring return; } nsWindow* mirrorScreenWidget = screen->GetMirroringWidget(); if (!mirrorScreenWidget) { // No mirroring return; } nsScreenGonk* mirrorScreen = mirrorScreenWidget->GetScreen(); if (!mirrorScreen->GetTopWindows().IsEmpty()) { return; } EGLSurface surface = mirrorScreen->GetEGLSurface(); if (surface == LOCAL_EGL_NO_SURFACE) { // Create GLContext RefPtr<GLContext> gl = gl::GLContextProvider::CreateForWindow(mirrorScreenWidget); mirrorScreenWidget->SetNativeData(NS_NATIVE_OPENGL_CONTEXT, reinterpret_cast<uintptr_t>(gl.get())); surface = mirrorScreen->GetEGLSurface(); if (surface == LOCAL_EGL_NO_SURFACE) { // Failed to create EGLSurface return; } } GLContext* gl = compositor->gl(); GLContextEGL* egl = GLContextEGL::Cast(gl); const IntSize windowSize = mirrorScreen->GetNaturalBounds().Size().ToUnknownSize(); #endif if ((windowSize.width <= 0) || (windowSize.height <= 0)) { return; } ScreenRotation rotation = compositor->GetScreenRotation(); const int actualWidth = windowSize.width; const int actualHeight = windowSize.height; const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize(); const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height); int pageWidth = originalSize.width; int pageHeight = originalSize.height; if (rotation == ROTATION_90 || rotation == ROTATION_270) { pageWidth = originalSize.height; pageHeight = originalSize.width; } float scale = 1.0; if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) { const float scaleWidth = (float)actualWidth / (float)pageWidth; const float scaleHeight = (float)actualHeight / (float)pageHeight; scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight; } const gfx::IntSize actualSize(actualWidth, actualHeight); ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize); const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0); ScopedContextSurfaceOverride overrideSurface(egl, surface); Matrix viewMatrix = ComputeTransformForRotation(originalRect, rotation); viewMatrix.Invert(); // unrotate viewMatrix.PostScale(scale, scale); viewMatrix.PostTranslate(offset.x, offset.y); Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix); mRoot->ComputeEffectiveTransforms(matrix); nsIntRegion opaque; LayerIntRegion visible; PostProcessLayers(mRoot, opaque, visible); nsIntRegion invalid; Rect bounds(0.0f, 0.0f, scale * pageWidth, (float)actualHeight); Rect rect, actualBounds; mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds); // The Java side of Fennec sets a scissor rect that accounts for // chrome such as the URL bar. Override that so that the entire frame buffer // is cleared. ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight); egl->fClearColor(0.0, 0.0, 0.0, 0.0); egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT); const IntRect clipRect = IntRect(0, 0, actualWidth, actualHeight); RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect)); RootLayer()->RenderLayer(clipRect); mCompositor->EndFrame(); #ifdef MOZ_WIDGET_GONK mCompositor->SetDispAcquireFence(mRoot, mirrorScreenWidget); // Call after EndFrame() RefPtr<Composer2D> composer2D; composer2D = mCompositor->GetWidget()->GetComposer2D(); if (composer2D) { composer2D->Render(mirrorScreenWidget); } #endif }
void LayerManagerComposite::RenderToPresentationSurface() { #ifdef MOZ_WIDGET_ANDROID nsIWidget* const widget = mCompositor->GetWidget()->RealWidget(); auto window = static_cast<ANativeWindow*>( widget->GetNativeData(NS_PRESENTATION_WINDOW)); if (!window) { return; } EGLSurface surface = widget->GetNativeData(NS_PRESENTATION_SURFACE); if (!surface) { //create surface; surface = GLContextProviderEGL::CreateEGLSurface(window); if (!surface) { return; } widget->SetNativeData(NS_PRESENTATION_SURFACE, reinterpret_cast<uintptr_t>(surface)); } CompositorOGL* compositor = mCompositor->AsCompositorOGL(); GLContext* gl = compositor->gl(); GLContextEGL* egl = GLContextEGL::Cast(gl); if (!egl) { return; } const IntSize windowSize(ANativeWindow_getWidth(window), ANativeWindow_getHeight(window)); #endif if ((windowSize.width <= 0) || (windowSize.height <= 0)) { return; } ScreenRotation rotation = compositor->GetScreenRotation(); const int actualWidth = windowSize.width; const int actualHeight = windowSize.height; const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize(); const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height); int pageWidth = originalSize.width; int pageHeight = originalSize.height; if (rotation == ROTATION_90 || rotation == ROTATION_270) { pageWidth = originalSize.height; pageHeight = originalSize.width; } float scale = 1.0; if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) { const float scaleWidth = (float)actualWidth / (float)pageWidth; const float scaleHeight = (float)actualHeight / (float)pageHeight; scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight; } const gfx::IntSize actualSize(actualWidth, actualHeight); ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize); const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0); ScopedContextSurfaceOverride overrideSurface(egl, surface); Matrix viewMatrix = ComputeTransformForRotation(originalRect, rotation); viewMatrix.Invert(); // unrotate viewMatrix.PostScale(scale, scale); viewMatrix.PostTranslate(offset.x, offset.y); Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix); mRoot->ComputeEffectiveTransforms(matrix); nsIntRegion opaque; LayerIntRegion visible; PostProcessLayers(mRoot, opaque, visible, Nothing()); nsIntRegion invalid; IntRect bounds = IntRect::Truncate(0, 0, scale * pageWidth, actualHeight); IntRect rect, actualBounds; MOZ_ASSERT(mRoot->GetOpacity() == 1); mCompositor->BeginFrame(invalid, nullptr, bounds, nsIntRegion(), &rect, &actualBounds); // The Java side of Fennec sets a scissor rect that accounts for // chrome such as the URL bar. Override that so that the entire frame buffer // is cleared. ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight); egl->fClearColor(0.0, 0.0, 0.0, 0.0); egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT); const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight); RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect)); RootLayer()->RenderLayer(clipRect); mCompositor->EndFrame(); }
void LayerManagerComposite::PostProcessLayers(Layer* aLayer, nsIntRegion& aOpaqueRegion, LayerIntRegion& aVisibleRegion) { nsIntRegion localOpaque; Matrix transform2d; Maybe<nsIntPoint> integerTranslation; // If aLayer has a simple transform (only an integer translation) then we // can easily convert aOpaqueRegion into pre-transform coordinates and include // that region. if (aLayer->GetLocalTransform().Is2D(&transform2d)) { if (transform2d.IsIntegerTranslation()) { integerTranslation = Some(TruncatedToInt(transform2d.GetTranslation())); localOpaque = aOpaqueRegion; localOpaque.MoveBy(-*integerTranslation); } } // Save the value of localOpaque, which currently stores the region obscured // by siblings (and uncles and such), before our descendants contribute to it. nsIntRegion obscured = localOpaque; // Recurse on our descendants, in front-to-back order. In this process: // - Occlusions are computed for them, and they contribute to localOpaque. // - They recalculate their visible regions, and accumulate them into // descendantsVisibleRegion. LayerIntRegion descendantsVisibleRegion; for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { PostProcessLayers(child, localOpaque, descendantsVisibleRegion); } // Recalculate our visible region. LayerComposite* composite = aLayer->AsLayerComposite(); LayerIntRegion visible = composite->GetShadowVisibleRegion(); // If we have descendants, throw away the visible region stored on this // layer, and use the region accumulated by our descendants instead. if (aLayer->GetFirstChild()) { visible = descendantsVisibleRegion; } // Subtract any areas that we know to be opaque. if (!obscured.IsEmpty()) { visible.SubOut(LayerIntRegion::FromUnknownRegion(obscured)); } composite->SetShadowVisibleRegion(visible); // Transform the newly calculated visible region into our parent's space, // apply our clip to it (if any), and accumulate it into |aVisibleRegion| // for the caller to use. ParentLayerIntRegion visibleParentSpace = TransformTo<ParentLayerPixel>( aLayer->GetLocalTransform(), visible); if (const Maybe<ParentLayerIntRect>& clipRect = composite->GetShadowClipRect()) { visibleParentSpace.AndWith(*clipRect); } aVisibleRegion.OrWith(ViewAs<LayerPixel>(visibleParentSpace, PixelCastJustification::MovingDownToChildren)); // If we have a simple transform, then we can add our opaque area into // aOpaqueRegion. if (integerTranslation && !aLayer->HasMaskLayers() && aLayer->IsOpaqueForVisibility()) { if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) { localOpaque.OrWith(composite->GetFullyRenderedRegion()); } localOpaque.MoveBy(*integerTranslation); const Maybe<ParentLayerIntRect>& clip = aLayer->GetEffectiveClipRect(); if (clip) { localOpaque.AndWith(clip->ToUnknownRect()); } aOpaqueRegion.OrWith(localOpaque); } }