зеркало из https://github.com/mozilla/gecko-dev.git
860 строки
29 KiB
C++
Executable File
860 строки
29 KiB
C++
Executable File
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ContainerLayerComposite.h"
|
|
#include <algorithm> // for min
|
|
#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController
|
|
#include "FrameMetrics.h" // for FrameMetrics
|
|
#include "Units.h" // for LayerRect, LayerPixel, etc
|
|
#include "CompositableHost.h" // for CompositableHost
|
|
#include "gfxEnv.h" // for gfxEnv
|
|
#include "gfxPrefs.h" // for gfxPrefs
|
|
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
|
|
#include "mozilla/RefPtr.h" // for RefPtr
|
|
#include "mozilla/UniquePtr.h" // for UniquePtr
|
|
#include "mozilla/gfx/BaseRect.h" // for BaseRect
|
|
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
|
#include "mozilla/gfx/Point.h" // for Point, IntPoint
|
|
#include "mozilla/gfx/Rect.h" // for IntRect, Rect
|
|
#include "mozilla/layers/Compositor.h" // for Compositor, etc
|
|
#include "mozilla/layers/CompositorTypes.h" // for DiagnosticFlags::CONTAINER
|
|
#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
|
|
#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
|
|
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
|
|
#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
|
|
#include "mozilla/mozalloc.h" // for operator delete, etc
|
|
#include "mozilla/RefPtr.h" // for nsRefPtr
|
|
#include "nsDebug.h" // for NS_ASSERTION
|
|
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
|
|
#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
|
|
#include "nsRegion.h" // for nsIntRegion
|
|
#include "nsTArray.h" // for AutoTArray
|
|
#include <stack>
|
|
#include "TextRenderer.h" // for TextRenderer
|
|
#include <vector>
|
|
#include "GeckoProfiler.h" // for GeckoProfiler
|
|
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
#include "ProfilerMarkerPayload.h" // for LayerTranslationMarkerPayload
|
|
#endif
|
|
|
|
#define CULLING_LOG(...)
|
|
// #define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__)
|
|
|
|
#define DUMP(...) do { if (gfxEnv::DumpDebug()) { printf_stderr(__VA_ARGS__); } } while(0)
|
|
#define XYWH(k) (k).x, (k).y, (k).Width(), (k).Height()
|
|
#define XY(k) (k).x, (k).y
|
|
#define WH(k) (k).Width(), (k).Height()
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
using namespace gfx;
|
|
|
|
static void
|
|
DrawLayerInfo(const RenderTargetIntRect& aClipRect,
|
|
LayerManagerComposite* aManager,
|
|
Layer* aLayer)
|
|
{
|
|
if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
|
|
// XXX - should figure out a way to render this, but for now this
|
|
// is hard to do, since it will often get superimposed over the first
|
|
// child of the layer, which is bad.
|
|
return;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
aLayer->PrintInfo(ss, "");
|
|
|
|
LayerIntRegion visibleRegion = aLayer->GetVisibleRegion();
|
|
|
|
uint32_t maxWidth = std::min<uint32_t>(visibleRegion.GetBounds().Width(), 500);
|
|
|
|
IntPoint topLeft = visibleRegion.ToUnknownRegion().GetBounds().TopLeft();
|
|
aManager->GetTextRenderer()->RenderText(
|
|
aManager->GetCompositor(),
|
|
ss.str().c_str(),
|
|
topLeft,
|
|
aLayer->GetEffectiveTransform(), 16,
|
|
maxWidth);
|
|
}
|
|
|
|
static void
|
|
PrintUniformityInfo(Layer* aLayer)
|
|
{
|
|
#if defined(MOZ_GECKO_PROFILER)
|
|
if (!profiler_is_active()) {
|
|
return;
|
|
}
|
|
|
|
// Don't want to print a log for smaller layers
|
|
if (aLayer->GetLocalVisibleRegion().GetBounds().Width() < 300 ||
|
|
aLayer->GetLocalVisibleRegion().GetBounds().Height() < 300) {
|
|
return;
|
|
}
|
|
|
|
Matrix4x4 transform = aLayer->AsHostLayer()->GetShadowBaseTransform();
|
|
if (!transform.Is2D()) {
|
|
return;
|
|
}
|
|
|
|
Point translation = transform.As2D().GetTranslation();
|
|
profiler_add_marker(
|
|
"LayerTranslation",
|
|
MakeUnique<LayerTranslationMarkerPayload>(aLayer, translation,
|
|
TimeStamp::Now()));
|
|
#endif
|
|
}
|
|
|
|
static Maybe<gfx::Polygon>
|
|
SelectLayerGeometry(const Maybe<gfx::Polygon>& aParentGeometry,
|
|
const Maybe<gfx::Polygon>& aChildGeometry)
|
|
{
|
|
// Both the parent and the child layer were split.
|
|
if (aParentGeometry && aChildGeometry) {
|
|
return Some(aParentGeometry->ClipPolygon(*aChildGeometry));
|
|
}
|
|
|
|
// The parent layer was split.
|
|
if (aParentGeometry) {
|
|
return aParentGeometry;
|
|
}
|
|
|
|
// The child layer was split.
|
|
if(aChildGeometry) {
|
|
return aChildGeometry;
|
|
}
|
|
|
|
// No split.
|
|
return Nothing();
|
|
}
|
|
|
|
void
|
|
TransformLayerGeometry(Layer* aLayer, Maybe<gfx::Polygon>& aGeometry)
|
|
{
|
|
Layer* parent = aLayer;
|
|
gfx::Matrix4x4 transform;
|
|
|
|
// Collect all parent transforms.
|
|
while (parent != nullptr && !parent->Is3DContextLeaf()) {
|
|
transform = transform * parent->GetLocalTransform();
|
|
parent = parent->GetParent();
|
|
}
|
|
|
|
// Transform the geometry to the parent 3D context leaf coordinate space.
|
|
transform = transform.ProjectTo2D();
|
|
|
|
if (!transform.IsSingular()) {
|
|
aGeometry->TransformToScreenSpace(transform.Inverse());
|
|
} else {
|
|
// Discard the geometry since the result might not be correct.
|
|
aGeometry.reset();
|
|
}
|
|
}
|
|
|
|
|
|
template<class ContainerT>
|
|
static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
|
|
{
|
|
gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
|
|
return surfaceRect;
|
|
}
|
|
|
|
|
|
/* all of the per-layer prepared data we need to maintain */
|
|
struct PreparedLayer
|
|
{
|
|
PreparedLayer(Layer *aLayer,
|
|
RenderTargetIntRect aClipRect,
|
|
Maybe<gfx::Polygon>&& aGeometry)
|
|
: mLayer(aLayer), mClipRect(aClipRect), mGeometry(Move(aGeometry)) {}
|
|
|
|
RefPtr<Layer> mLayer;
|
|
RenderTargetIntRect mClipRect;
|
|
Maybe<Polygon> mGeometry;
|
|
};
|
|
|
|
/* all of the prepared data that we need in RenderLayer() */
|
|
struct PreparedData
|
|
{
|
|
RefPtr<CompositingRenderTarget> mTmpTarget;
|
|
AutoTArray<PreparedLayer, 12> mLayers;
|
|
bool mNeedsSurfaceCopy;
|
|
};
|
|
|
|
// ContainerPrepare is shared between RefLayer and ContainerLayer
|
|
template<class ContainerT> void
|
|
ContainerPrepare(ContainerT* aContainer,
|
|
LayerManagerComposite* aManager,
|
|
const RenderTargetIntRect& aClipRect)
|
|
{
|
|
// We can end up calling prepare multiple times if we duplicated
|
|
// layers due to preserve-3d plane splitting. The results
|
|
// should be identical, so we only need to do it once.
|
|
if (aContainer->mPrepared) {
|
|
return;
|
|
}
|
|
aContainer->mPrepared = MakeUnique<PreparedData>();
|
|
aContainer->mPrepared->mNeedsSurfaceCopy = false;
|
|
|
|
const ContainerLayerComposite::SortMode sortMode =
|
|
aManager->GetCompositor()->SupportsLayerGeometry()
|
|
? ContainerLayerComposite::SortMode::WITH_GEOMETRY
|
|
: ContainerLayerComposite::SortMode::WITHOUT_GEOMETRY;
|
|
|
|
nsTArray<LayerPolygon> polygons =
|
|
aContainer->SortChildrenBy3DZOrder(sortMode);
|
|
|
|
for (LayerPolygon& layer : polygons) {
|
|
LayerComposite* layerToRender =
|
|
static_cast<LayerComposite*>(layer.layer->ImplData());
|
|
|
|
RenderTargetIntRect clipRect =
|
|
layerToRender->GetLayer()->CalculateScissorRect(aClipRect);
|
|
|
|
if (layerToRender->GetLayer()->IsBackfaceHidden()) {
|
|
continue;
|
|
}
|
|
|
|
// We don't want to skip container layers because otherwise their mPrepared
|
|
// may be null which is not allowed.
|
|
if (!layerToRender->GetLayer()->AsContainerLayer()) {
|
|
if (!layerToRender->GetLayer()->IsVisible()) {
|
|
CULLING_LOG("Sublayer %p has no effective visible region\n", layerToRender->GetLayer());
|
|
continue;
|
|
}
|
|
|
|
if (clipRect.IsEmpty()) {
|
|
CULLING_LOG("Sublayer %p has an empty world clip rect\n", layerToRender->GetLayer());
|
|
continue;
|
|
}
|
|
}
|
|
|
|
CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
|
|
|
|
layerToRender->Prepare(clipRect);
|
|
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender->GetLayer(),
|
|
clipRect,
|
|
Move(layer.geometry)));
|
|
}
|
|
|
|
CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
|
|
|
|
/**
|
|
* Setup our temporary surface for rendering the contents of this container.
|
|
*/
|
|
|
|
gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
|
|
if (surfaceRect.IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
bool surfaceCopyNeeded;
|
|
// DefaultComputeSupportsComponentAlphaChildren can mutate aContainer so call it unconditionally
|
|
aContainer->DefaultComputeSupportsComponentAlphaChildren(&surfaceCopyNeeded);
|
|
if (aContainer->UseIntermediateSurface()) {
|
|
if (!surfaceCopyNeeded) {
|
|
RefPtr<CompositingRenderTarget> surface = nullptr;
|
|
|
|
RefPtr<CompositingRenderTarget>& lastSurf = aContainer->mLastIntermediateSurface;
|
|
if (lastSurf && !aContainer->mChildrenChanged && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
|
|
surface = lastSurf;
|
|
}
|
|
|
|
if (!surface) {
|
|
// If we don't need a copy we can render to the intermediate now to avoid
|
|
// unecessary render target switching. This brings a big perf boost on mobile gpus.
|
|
surface = CreateOrRecycleTarget(aContainer, aManager);
|
|
|
|
MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface rendering\n", aContainer);
|
|
RenderIntermediate(aContainer, aManager, aClipRect.ToUnknownRect(), surface);
|
|
aContainer->SetChildrenChanged(false);
|
|
}
|
|
|
|
aContainer->mPrepared->mTmpTarget = surface;
|
|
} else {
|
|
MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface copy\n", aContainer);
|
|
aContainer->mPrepared->mNeedsSurfaceCopy = true;
|
|
aContainer->mLastIntermediateSurface = nullptr;
|
|
}
|
|
} else {
|
|
aContainer->mLastIntermediateSurface = nullptr;
|
|
}
|
|
}
|
|
|
|
template<class ContainerT> void
|
|
RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
|
|
const RenderTargetIntRect& aClipRect, Layer* aLayer)
|
|
{
|
|
Compositor* compositor = aManager->GetCompositor();
|
|
|
|
if (aLayer->GetScrollMetadataCount() < 1) {
|
|
return;
|
|
}
|
|
|
|
AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
|
|
if (!controller) {
|
|
return;
|
|
}
|
|
|
|
ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing);
|
|
|
|
// Options
|
|
const int verticalPadding = 10;
|
|
const int horizontalPadding = 5;
|
|
gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
|
|
gfx::Color tileActiveColor(1, 1, 1, 0.4f);
|
|
gfx::Color tileBorderColor(0, 0, 0, 0.1f);
|
|
gfx::Color pageBorderColor(0, 0, 0);
|
|
gfx::Color criticalDisplayPortColor(1.f, 1.f, 0);
|
|
gfx::Color displayPortColor(0, 1.f, 0);
|
|
gfx::Color viewPortColor(0, 0, 1.f, 0.3f);
|
|
gfx::Color visibilityColor(1.f, 0, 0);
|
|
|
|
// Rects
|
|
const FrameMetrics& fm = aLayer->GetFrameMetrics(0);
|
|
ParentLayerRect compositionBounds = fm.GetCompositionBounds();
|
|
LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel();
|
|
LayerRect viewRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1);
|
|
LayerRect dp = (fm.GetDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel();
|
|
Maybe<LayerRect> cdp;
|
|
if (!fm.GetCriticalDisplayPort().IsEmpty()) {
|
|
cdp = Some((fm.GetCriticalDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel());
|
|
}
|
|
|
|
// Don't render trivial minimap. They can show up from textboxes and other tiny frames.
|
|
if (viewRect.width < 64 && viewRect.height < 64) {
|
|
return;
|
|
}
|
|
|
|
// Compute a scale with an appropriate aspect ratio
|
|
// We allocate up to 100px of width and the height of this layer.
|
|
float scaleFactor;
|
|
float scaleFactorX;
|
|
float scaleFactorY;
|
|
Rect dest = Rect(aClipRect.ToUnknownRect());
|
|
if (aLayer->GetLocalClipRect()) {
|
|
dest = Rect(aLayer->GetLocalClipRect().value().ToUnknownRect());
|
|
} else {
|
|
dest = aContainer->GetEffectiveTransform().Inverse().TransformBounds(dest);
|
|
}
|
|
dest = dest.Intersect(compositionBounds.ToUnknownRect());
|
|
scaleFactorX = std::min(100.f, dest.width - (2 * horizontalPadding)) / scrollRect.width;
|
|
scaleFactorY = (dest.height - (2 * verticalPadding)) / scrollRect.height;
|
|
scaleFactor = std::min(scaleFactorX, scaleFactorY);
|
|
if (scaleFactor <= 0) {
|
|
return;
|
|
}
|
|
|
|
Matrix4x4 transform = Matrix4x4::Scaling(scaleFactor, scaleFactor, 1);
|
|
transform.PostTranslate(horizontalPadding + dest.x, verticalPadding + dest.y, 0);
|
|
|
|
Rect transformedScrollRect = transform.TransformBounds(scrollRect.ToUnknownRect());
|
|
|
|
IntRect clipRect = RoundedOut(aContainer->GetEffectiveTransform().TransformBounds(transformedScrollRect));
|
|
|
|
// Render the scrollable area.
|
|
compositor->FillRect(transformedScrollRect, backgroundColor, clipRect, aContainer->GetEffectiveTransform());
|
|
compositor->SlowDrawRect(transformedScrollRect, pageBorderColor, clipRect, aContainer->GetEffectiveTransform());
|
|
|
|
// If enabled, render information about visibility.
|
|
if (gfxPrefs::APZMinimapVisibilityEnabled()) {
|
|
// Retrieve the APZC scrollable layer guid, which we'll use to get the
|
|
// appropriate visibility information from the layer manager.
|
|
AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
|
|
MOZ_ASSERT(controller);
|
|
|
|
ScrollableLayerGuid guid = controller->GetGuid();
|
|
|
|
// Get the approximately visible region.
|
|
static CSSIntRegion emptyRegion;
|
|
CSSIntRegion* visibleRegion = aManager->GetApproximatelyVisibleRegion(guid);
|
|
if (!visibleRegion) {
|
|
visibleRegion = &emptyRegion;
|
|
}
|
|
|
|
// Iterate through and draw the rects in the region.
|
|
for (CSSIntRegion::RectIterator iterator = visibleRegion->RectIter();
|
|
!iterator.Done();
|
|
iterator.Next())
|
|
{
|
|
CSSIntRect rect = iterator.Get();
|
|
LayerRect scaledRect = rect * fm.LayersPixelsPerCSSPixel();
|
|
Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
|
|
compositor->FillRect(r, visibilityColor, clipRect, aContainer->GetEffectiveTransform());
|
|
}
|
|
}
|
|
|
|
// Render the displayport.
|
|
Rect r = transform.TransformBounds(dp.ToUnknownRect());
|
|
compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform());
|
|
compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform());
|
|
|
|
// Render the critical displayport if there is one
|
|
if (cdp) {
|
|
r = transform.TransformBounds(cdp->ToUnknownRect());
|
|
compositor->SlowDrawRect(r, criticalDisplayPortColor, clipRect, aContainer->GetEffectiveTransform());
|
|
}
|
|
|
|
// Render the viewport.
|
|
r = transform.TransformBounds(viewRect.ToUnknownRect());
|
|
compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
|
|
}
|
|
|
|
template<class ContainerT> void
|
|
RenderLayers(ContainerT* aContainer, LayerManagerComposite* aManager,
|
|
const RenderTargetIntRect& aClipRect,
|
|
const Maybe<gfx::Polygon>& aGeometry)
|
|
{
|
|
Compositor* compositor = aManager->GetCompositor();
|
|
|
|
for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
|
|
PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
|
|
|
|
const gfx::IntRect clipRect = preparedData.mClipRect.ToUnknownRect();
|
|
LayerComposite* layerToRender = static_cast<LayerComposite*>(preparedData.mLayer->ImplData());
|
|
const Maybe<gfx::Polygon>& childGeometry = preparedData.mGeometry;
|
|
|
|
Layer* layer = layerToRender->GetLayer();
|
|
|
|
if (layerToRender->HasStaleCompositor()) {
|
|
continue;
|
|
}
|
|
|
|
if (gfxPrefs::LayersDrawFPS()) {
|
|
for (const auto& metadata : layer->GetAllScrollMetadata()) {
|
|
if (metadata.IsApzForceDisabled()) {
|
|
aManager->DisabledApzWarning();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (layerToRender->HasLayerBeenComposited()) {
|
|
// Composer2D will compose this layer so skip GPU composition
|
|
// this time. The flag will be reset for the next composition phase
|
|
// at the beginning of LayerManagerComposite::Rener().
|
|
gfx::IntRect clearRect = layerToRender->GetClearRect();
|
|
if (!clearRect.IsEmpty()) {
|
|
// Clear layer's visible rect on FrameBuffer with transparent pixels
|
|
gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.Width(), clearRect.Height());
|
|
compositor->ClearRect(fbRect);
|
|
layerToRender->SetClearRect(gfx::IntRect(0, 0, 0, 0));
|
|
}
|
|
} else {
|
|
// Since we force an intermediate surface for nested 3D contexts,
|
|
// aGeometry and childGeometry are both in the same coordinate space.
|
|
Maybe<gfx::Polygon> geometry =
|
|
SelectLayerGeometry(aGeometry, childGeometry);
|
|
|
|
// If we are dealing with a nested 3D context, we might need to transform
|
|
// the geometry back to the coordinate space of the current layer before
|
|
// rendering the layer.
|
|
ContainerLayer* container = layer->AsContainerLayer();
|
|
const bool isLeafLayer = !container || container->UseIntermediateSurface();
|
|
|
|
if (geometry && isLeafLayer) {
|
|
TransformLayerGeometry(layer, geometry);
|
|
}
|
|
|
|
layerToRender->RenderLayer(clipRect, geometry);
|
|
}
|
|
|
|
if (gfxPrefs::UniformityInfo()) {
|
|
PrintUniformityInfo(layer);
|
|
}
|
|
|
|
if (gfxPrefs::DrawLayerInfo()) {
|
|
DrawLayerInfo(preparedData.mClipRect, aManager, layer);
|
|
}
|
|
|
|
// Draw a border around scrollable layers.
|
|
// A layer can be scrolled by multiple scroll frames. Draw a border
|
|
// for each.
|
|
// Within the list of scroll frames for a layer, the layer border for a
|
|
// scroll frame lower down is affected by the async transforms on scroll
|
|
// frames higher up, so loop from the top down, and accumulate an async
|
|
// transform as we go along.
|
|
Matrix4x4 asyncTransform;
|
|
for (uint32_t i = layer->GetScrollMetadataCount(); i > 0; --i) {
|
|
if (layer->GetFrameMetrics(i - 1).IsScrollable()) {
|
|
// Since the composition bounds are in the parent layer's coordinates,
|
|
// use the parent's effective transform rather than the layer's own.
|
|
ParentLayerRect compositionBounds = layer->GetFrameMetrics(i - 1).GetCompositionBounds();
|
|
aManager->GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTAINER,
|
|
compositionBounds.ToUnknownRect(),
|
|
aClipRect.ToUnknownRect(),
|
|
asyncTransform * aContainer->GetEffectiveTransform());
|
|
if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) {
|
|
asyncTransform =
|
|
apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForCompositing).ToUnknownMatrix()
|
|
* asyncTransform;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gfxPrefs::APZMinimap()) {
|
|
RenderMinimap(aContainer, aManager, aClipRect, layer);
|
|
}
|
|
|
|
// invariant: our GL context should be current here, I don't think we can
|
|
// assert it though
|
|
}
|
|
}
|
|
|
|
template<class ContainerT> RefPtr<CompositingRenderTarget>
|
|
CreateOrRecycleTarget(ContainerT* aContainer,
|
|
LayerManagerComposite* aManager)
|
|
{
|
|
Compositor* compositor = aManager->GetCompositor();
|
|
SurfaceInitMode mode = INIT_MODE_CLEAR;
|
|
gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
|
|
if (aContainer->GetLocalVisibleRegion().GetNumRects() == 1 &&
|
|
(aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE))
|
|
{
|
|
mode = INIT_MODE_NONE;
|
|
}
|
|
|
|
RefPtr<CompositingRenderTarget>& lastSurf = aContainer->mLastIntermediateSurface;
|
|
if (lastSurf && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
|
|
if (mode == INIT_MODE_CLEAR) {
|
|
lastSurf->ClearOnBind();
|
|
}
|
|
|
|
return lastSurf;
|
|
} else {
|
|
lastSurf = compositor->CreateRenderTarget(surfaceRect, mode);
|
|
|
|
return lastSurf;
|
|
}
|
|
}
|
|
|
|
template<class ContainerT> RefPtr<CompositingRenderTarget>
|
|
CreateTemporaryTargetAndCopyFromBackground(ContainerT* aContainer,
|
|
LayerManagerComposite* aManager)
|
|
{
|
|
Compositor* compositor = aManager->GetCompositor();
|
|
gfx::IntRect visibleRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
|
|
RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
|
|
gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y,
|
|
visibleRect.Width(), visibleRect.Height());
|
|
|
|
gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.x, visibleRect.y);
|
|
|
|
gfx::Matrix4x4 transform = aContainer->GetEffectiveTransform();
|
|
DebugOnly<gfx::Matrix> transform2d;
|
|
MOZ_ASSERT(transform.Is2D(&transform2d) && !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation());
|
|
sourcePoint += gfx::IntPoint::Truncate(transform._41, transform._42);
|
|
|
|
sourcePoint -= compositor->GetCurrentRenderTarget()->GetOrigin();
|
|
|
|
return compositor->CreateRenderTargetFromSource(surfaceRect, previousTarget, sourcePoint);
|
|
}
|
|
|
|
template<class ContainerT> void
|
|
RenderIntermediate(ContainerT* aContainer,
|
|
LayerManagerComposite* aManager,
|
|
const gfx::IntRect& aClipRect,
|
|
RefPtr<CompositingRenderTarget> surface)
|
|
{
|
|
Compositor* compositor = aManager->GetCompositor();
|
|
RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
|
|
|
|
if (!surface) {
|
|
return;
|
|
}
|
|
|
|
compositor->SetRenderTarget(surface);
|
|
// pre-render all of the layers into our temporary
|
|
RenderLayers(aContainer, aManager,
|
|
RenderTargetIntRect::FromUnknownRect(aClipRect),
|
|
Nothing());
|
|
|
|
// Unbind the current surface and rebind the previous one.
|
|
compositor->SetRenderTarget(previousTarget);
|
|
}
|
|
|
|
template<class ContainerT> void
|
|
ContainerRender(ContainerT* aContainer,
|
|
LayerManagerComposite* aManager,
|
|
const gfx::IntRect& aClipRect,
|
|
const Maybe<gfx::Polygon>& aGeometry)
|
|
{
|
|
MOZ_ASSERT(aContainer->mPrepared);
|
|
|
|
if (aContainer->UseIntermediateSurface()) {
|
|
RefPtr<CompositingRenderTarget> surface;
|
|
|
|
if (aContainer->mPrepared->mNeedsSurfaceCopy) {
|
|
// we needed to copy the background so we waited until now to render the intermediate
|
|
surface = CreateTemporaryTargetAndCopyFromBackground(aContainer, aManager);
|
|
RenderIntermediate(aContainer, aManager,
|
|
aClipRect, surface);
|
|
} else {
|
|
surface = aContainer->mPrepared->mTmpTarget;
|
|
}
|
|
|
|
if (!surface) {
|
|
return;
|
|
}
|
|
|
|
gfx::Rect visibleRect(aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
|
|
|
|
RefPtr<Compositor> compositor = aManager->GetCompositor();
|
|
#ifdef MOZ_DUMP_PAINTING
|
|
if (gfxEnv::DumpCompositorTextures()) {
|
|
RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
|
|
if (surf) {
|
|
WriteSnapshotToDumpFile(aContainer, surf);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
RefPtr<ContainerT> container = aContainer;
|
|
RenderWithAllMasks(aContainer, compositor, aClipRect,
|
|
[&, surface, compositor, container](EffectChain& effectChain, const IntRect& clipRect) {
|
|
effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
|
|
|
|
compositor->DrawGeometry(visibleRect, clipRect, effectChain,
|
|
container->GetEffectiveOpacity(),
|
|
container->GetEffectiveTransform(), aGeometry);
|
|
});
|
|
|
|
} else {
|
|
RenderLayers(aContainer, aManager,
|
|
RenderTargetIntRect::FromUnknownRect(aClipRect),
|
|
aGeometry);
|
|
}
|
|
|
|
// If it is a scrollable container layer with no child layers, and one of the APZCs
|
|
// attached to it has a nonempty async transform, then that transform is not applied
|
|
// to any visible content. Display a warning box (conditioned on the FPS display being
|
|
// enabled).
|
|
if (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollableWithoutContent()) {
|
|
// Since aContainer doesn't have any children we can just iterate from the top metrics
|
|
// on it down to the bottom using GetFirstChild and not worry about walking onto another
|
|
// underlying layer.
|
|
for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) {
|
|
if (AsyncPanZoomController* apzc = i.GetApzc()) {
|
|
if (!apzc->GetAsyncTransformAppliedToContent()
|
|
&& !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForHitTesting)).IsIdentity()) {
|
|
aManager->UnusedApzTransformWarning();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ContainerLayerComposite::ContainerLayerComposite(LayerManagerComposite *aManager)
|
|
: ContainerLayer(aManager, nullptr)
|
|
, LayerComposite(aManager)
|
|
{
|
|
MOZ_COUNT_CTOR(ContainerLayerComposite);
|
|
mImplData = static_cast<LayerComposite*>(this);
|
|
}
|
|
|
|
ContainerLayerComposite::~ContainerLayerComposite()
|
|
{
|
|
MOZ_COUNT_DTOR(ContainerLayerComposite);
|
|
|
|
// We don't Destroy() on destruction here because this destructor
|
|
// can be called after remote content has crashed, and it may not be
|
|
// safe to free the IPC resources of our children. Those resources
|
|
// are automatically cleaned up by IPDL-generated code.
|
|
//
|
|
// In the common case of normal shutdown, either
|
|
// LayerManagerComposite::Destroy(), a parent
|
|
// *ContainerLayerComposite::Destroy(), or Disconnect() will trigger
|
|
// cleanup of our resources.
|
|
while (mFirstChild) {
|
|
RemoveChild(mFirstChild);
|
|
}
|
|
}
|
|
|
|
void
|
|
ContainerLayerComposite::Destroy()
|
|
{
|
|
if (!mDestroyed) {
|
|
while (mFirstChild) {
|
|
GetFirstChildComposite()->Destroy();
|
|
RemoveChild(mFirstChild);
|
|
}
|
|
mDestroyed = true;
|
|
}
|
|
}
|
|
|
|
LayerComposite*
|
|
ContainerLayerComposite::GetFirstChildComposite()
|
|
{
|
|
if (!mFirstChild) {
|
|
return nullptr;
|
|
}
|
|
return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
|
|
}
|
|
|
|
void
|
|
ContainerLayerComposite::Cleanup()
|
|
{
|
|
mPrepared = nullptr;
|
|
|
|
for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
|
|
static_cast<LayerComposite*>(l->AsHostLayer())->Cleanup();
|
|
}
|
|
}
|
|
|
|
void
|
|
ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
|
|
const Maybe<gfx::Polygon>& aGeometry)
|
|
{
|
|
ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
|
|
}
|
|
|
|
void
|
|
ContainerLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
|
|
{
|
|
ContainerPrepare(this, mCompositeManager, aClipRect);
|
|
}
|
|
|
|
void
|
|
ContainerLayerComposite::CleanupResources()
|
|
{
|
|
mLastIntermediateSurface = nullptr;
|
|
mPrepared = nullptr;
|
|
|
|
for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
|
|
static_cast<LayerComposite*>(l->AsHostLayer())->CleanupResources();
|
|
}
|
|
}
|
|
|
|
static LayerIntRect
|
|
TransformRect(const LayerIntRect& aRect, const Matrix4x4& aTransform)
|
|
{
|
|
if (aRect.IsEmpty()) {
|
|
return LayerIntRect();
|
|
}
|
|
|
|
Rect rect(aRect.x, aRect.y, aRect.width, aRect.height);
|
|
rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect());
|
|
rect.RoundOut();
|
|
|
|
IntRect intRect;
|
|
if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) {
|
|
return LayerIntRect();
|
|
}
|
|
|
|
return ViewAs<LayerPixel>(intRect);
|
|
}
|
|
|
|
static void
|
|
AddTransformedRegion(LayerIntRegion& aDest, const LayerIntRegion& aSource, const Matrix4x4& aTransform)
|
|
{
|
|
for (auto iter = aSource.RectIter(); !iter.Done(); iter.Next()) {
|
|
aDest.Or(aDest, TransformRect(iter.Get(), aTransform));
|
|
}
|
|
aDest.SimplifyOutward(20);
|
|
}
|
|
|
|
// Async animations can move child layers without updating our visible region.
|
|
// PostProcessLayers will recompute visible regions for layers with an intermediate
|
|
// surface, but otherwise we need to do it now.
|
|
void
|
|
ComputeVisibleRegionForChildren(ContainerLayer* aContainer, LayerIntRegion& aResult)
|
|
{
|
|
for (Layer* l = aContainer->GetFirstChild(); l; l = l->GetNextSibling()) {
|
|
if (l->Extend3DContext()) {
|
|
MOZ_ASSERT(l->AsContainerLayer());
|
|
ComputeVisibleRegionForChildren(l->AsContainerLayer(), aResult);
|
|
} else {
|
|
AddTransformedRegion(aResult,
|
|
l->GetLocalVisibleRegion(),
|
|
l->ComputeTransformToPreserve3DRoot());
|
|
}
|
|
}
|
|
}
|
|
|
|
const LayerIntRegion&
|
|
ContainerLayerComposite::GetShadowVisibleRegion()
|
|
{
|
|
if (!UseIntermediateSurface()) {
|
|
mShadowVisibleRegion.SetEmpty();
|
|
ComputeVisibleRegionForChildren(this, mShadowVisibleRegion);
|
|
}
|
|
|
|
return mShadowVisibleRegion;
|
|
}
|
|
|
|
const LayerIntRegion&
|
|
RefLayerComposite::GetShadowVisibleRegion()
|
|
{
|
|
if (!UseIntermediateSurface()) {
|
|
mShadowVisibleRegion.SetEmpty();
|
|
ComputeVisibleRegionForChildren(this, mShadowVisibleRegion);
|
|
}
|
|
|
|
return mShadowVisibleRegion;
|
|
}
|
|
|
|
RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager)
|
|
: RefLayer(aManager, nullptr)
|
|
, LayerComposite(aManager)
|
|
{
|
|
mImplData = static_cast<LayerComposite*>(this);
|
|
}
|
|
|
|
RefLayerComposite::~RefLayerComposite()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
void
|
|
RefLayerComposite::Destroy()
|
|
{
|
|
MOZ_ASSERT(!mFirstChild);
|
|
mDestroyed = true;
|
|
}
|
|
|
|
LayerComposite*
|
|
RefLayerComposite::GetFirstChildComposite()
|
|
{
|
|
if (!mFirstChild) {
|
|
return nullptr;
|
|
}
|
|
return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
|
|
}
|
|
|
|
void
|
|
RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
|
|
const Maybe<gfx::Polygon>& aGeometry)
|
|
{
|
|
ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
|
|
}
|
|
|
|
void
|
|
RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
|
|
{
|
|
ContainerPrepare(this, mCompositeManager, aClipRect);
|
|
}
|
|
|
|
void
|
|
RefLayerComposite::Cleanup()
|
|
{
|
|
mPrepared = nullptr;
|
|
|
|
for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
|
|
static_cast<LayerComposite*>(l->AsHostLayer())->Cleanup();
|
|
}
|
|
}
|
|
|
|
void
|
|
RefLayerComposite::CleanupResources()
|
|
{
|
|
mLastIntermediateSurface = nullptr;
|
|
mPrepared = nullptr;
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|