void LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion) { nsIntRegion localOpaque; Matrix transform2d; bool isTranslation = false; // 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()) { isTranslation = true; localOpaque = aOpaqueRegion; localOpaque.MoveBy(-transform2d._31, -transform2d._32); } } // Subtract any areas that we know to be opaque from our // visible region. LayerComposite *composite = aLayer->AsLayerComposite(); if (!localOpaque.IsEmpty()) { nsIntRegion visible = composite->GetShadowVisibleRegion(); visible.Sub(visible, localOpaque); composite->SetShadowVisibleRegion(visible); } // Compute occlusions for our descendants (in front-to-back order) and allow them to // contribute to localOpaque. for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { ApplyOcclusionCulling(child, localOpaque); } // If we have a simple transform, then we can add our opaque area into // aOpaqueRegion. if (isTranslation && !aLayer->HasMaskLayers() && aLayer->GetLocalOpacity() == 1.0f) { if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) { localOpaque.Or(localOpaque, composite->GetFullyRenderedRegion()); } localOpaque.MoveBy(transform2d._31, transform2d._32); const Maybe<ParentLayerIntRect>& clip = aLayer->GetEffectiveClipRect(); if (clip) { localOpaque.And(localOpaque, ParentLayerIntRect::ToUntyped(*clip)); } aOpaqueRegion.Or(aOpaqueRegion, localOpaque); } }
void LayerManagerComposite::EndTransaction(const TimeStamp& aTimeStamp, EndTransactionFlags aFlags) { NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?"); NS_ASSERTION(!(aFlags & END_NO_COMPOSITE), "Shouldn't get END_NO_COMPOSITE here"); mInTransaction = false; if (!mIsCompositorReady) { return; } mIsCompositorReady = false; #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); #endif if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } // Set composition timestamp here because we need it in // ComputeEffectiveTransforms (so the correct video frame size is picked) and // also to compute invalid regions properly. mCompositor->SetCompositionTime(aTimeStamp); if (mRoot && mClonedLayerTreeProperties) { MOZ_ASSERT(!mTarget); nsIntRegion invalid = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged); mClonedLayerTreeProperties = nullptr; mInvalidRegion.Or(mInvalidRegion, invalid); } else if (!mTarget) { mInvalidRegion.Or(mInvalidRegion, mRenderBounds); } if (mInvalidRegion.IsEmpty() && !mTarget) { // Composition requested, but nothing has changed. Don't do any work. return; } if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { MOZ_ASSERT(!aTimeStamp.IsNull()); // 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()); nsIntRegion opaque; ApplyOcclusionCulling(mRoot, opaque); Render(); #ifdef MOZ_WIDGET_ANDROID RenderToPresentationSurface(); #endif mGeometryChanged = false; } else { // Modified layer tree mGeometryChanged = true; } mCompositor->ClearTargetContext(); mTarget = nullptr; #ifdef MOZ_LAYERS_HAVE_LOG Log(); MOZ_LAYERS_LOG(("]----- EndTransaction")); #endif }
void LayerManagerComposite::EndTransaction(DrawPaintedLayerCallback aCallback, void* aCallbackData, EndTransactionFlags aFlags) { NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?"); NS_ASSERTION(!aCallback && !aCallbackData, "Not expecting callbacks here"); mInTransaction = false; if (!mIsCompositorReady) { return; } mIsCompositorReady = false; #ifdef MOZ_LAYERS_HAVE_LOG MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); #endif if (mDestroyed) { NS_WARNING("Call on destroyed layer manager"); return; } if (mRoot && mClonedLayerTreeProperties) { MOZ_ASSERT(!mTarget); nsIntRegion invalid = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged); mClonedLayerTreeProperties = nullptr; mInvalidRegion.Or(mInvalidRegion, invalid); } else if (!mTarget) { mInvalidRegion.Or(mInvalidRegion, mRenderBounds); } if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) { if (aFlags & END_NO_COMPOSITE) { // Apply pending tree updates before recomputing effective // properties. mRoot->ApplyPendingUpdatesToSubtree(); } // 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()); nsIntRegion opaque; ApplyOcclusionCulling(mRoot, opaque); Render(); #ifdef MOZ_WIDGET_ANDROID RenderToPresentationSurface(); #endif mGeometryChanged = false; } else { // Modified layer tree mGeometryChanged = true; } mCompositor->ClearTargetContext(); mTarget = nullptr; #ifdef MOZ_LAYERS_HAVE_LOG Log(); MOZ_LAYERS_LOG(("]----- EndTransaction")); #endif }
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 nsRefPtr<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(); #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; ApplyOcclusionCulling(mRoot, opaque); 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(RenderTargetPixel::FromUntyped(clipRect)); RootLayer()->RenderLayer(clipRect); mCompositor->EndFrame(); mCompositor->SetDispAcquireFence(mRoot); // Call after EndFrame() #ifdef MOZ_WIDGET_GONK nsRefPtr<Composer2D> composer2D; composer2D = mCompositor->GetWidget()->GetComposer2D(); if (composer2D) { composer2D->Render(mirrorScreenWidget); } #endif }
void LayerManagerComposite::UpdateAndRender() { nsIntRegion invalid; if (mClonedLayerTreeProperties) { // 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(); } // Update cached layer tree information. mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot()); if (invalid.IsEmpty() && !mWindowOverlayChanged) { // Composition requested, but nothing has changed. Don't do any work. 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(mRenderBounds); // 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()); nsIntRegion opaque; ApplyOcclusionCulling(mRoot, opaque); Render(invalid); #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) RenderToPresentationSurface(); #endif mGeometryChanged = false; mWindowOverlayChanged = false; }