mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
15060c53d3
CLOSED TREE
917 lines
28 KiB
C++
917 lines
28 KiB
C++
/* -*- 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 "LayerManagerComposite.h"
|
|
#include <stddef.h> // for size_t
|
|
#include <stdint.h> // for uint16_t, uint32_t
|
|
#include "CanvasLayerComposite.h" // for CanvasLayerComposite
|
|
#include "ColorLayerComposite.h" // for ColorLayerComposite
|
|
#include "Composer2D.h" // for Composer2D
|
|
#include "CompositableHost.h" // for CompositableHost
|
|
#include "ContainerLayerComposite.h" // for ContainerLayerComposite, etc
|
|
#include "FPSCounter.h" // for FPSState, FPSCounter
|
|
#include "FrameMetrics.h" // for FrameMetrics
|
|
#include "GeckoProfiler.h" // for profiler_set_frame_number, etc
|
|
#include "ImageLayerComposite.h" // for ImageLayerComposite
|
|
#include "Layers.h" // for Layer, ContainerLayer, etc
|
|
#include "ThebesLayerComposite.h" // for ThebesLayerComposite
|
|
#include "TiledLayerBuffer.h" // for TiledLayerComposer
|
|
#include "Units.h" // for ScreenIntRect
|
|
#include "gfx2DGlue.h" // for ToMatrix4x4
|
|
#include "gfx3DMatrix.h" // for gfx3DMatrix
|
|
#include "gfxPrefs.h" // for gfxPrefs
|
|
#ifdef XP_MACOSX
|
|
#include "gfxPlatformMac.h"
|
|
#endif
|
|
#include "gfxRect.h" // for gfxRect
|
|
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
|
|
#include "mozilla/RefPtr.h" // for RefPtr, TemporaryRef
|
|
#include "mozilla/gfx/2D.h" // for DrawTarget
|
|
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
|
#include "mozilla/gfx/Point.h" // for IntSize, Point
|
|
#include "mozilla/gfx/Rect.h" // for Rect
|
|
#include "mozilla/gfx/Types.h" // for Color, SurfaceFormat
|
|
#include "mozilla/layers/Compositor.h" // for Compositor
|
|
#include "mozilla/layers/CompositorTypes.h"
|
|
#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
|
|
#include "mozilla/layers/LayersTypes.h" // for etc
|
|
#include "ipc/ShadowLayerUtils.h"
|
|
#include "mozilla/mozalloc.h" // for operator new, etc
|
|
#include "nsAutoPtr.h" // for nsRefPtr
|
|
#include "nsCOMPtr.h" // for already_AddRefed
|
|
#include "nsDebug.h" // for NS_WARNING, NS_RUNTIMEABORT, etc
|
|
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
|
|
#include "nsIWidget.h" // for nsIWidget
|
|
#include "nsPoint.h" // for nsIntPoint
|
|
#include "nsRect.h" // for nsIntRect
|
|
#include "nsRegion.h" // for nsIntRegion, etc
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
#include <android/log.h>
|
|
#endif
|
|
#include "GeckoProfiler.h"
|
|
#include "TextRenderer.h" // for TextRenderer
|
|
|
|
class gfxASurface;
|
|
class gfxContext;
|
|
struct nsIntSize;
|
|
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
class ImageLayer;
|
|
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::gl;
|
|
|
|
static LayerComposite*
|
|
ToLayerComposite(Layer* aLayer)
|
|
{
|
|
return static_cast<LayerComposite*>(aLayer->ImplData());
|
|
}
|
|
|
|
static void ClearSubtree(Layer* aLayer)
|
|
{
|
|
ToLayerComposite(aLayer)->CleanupResources();
|
|
for (Layer* child = aLayer->GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
ClearSubtree(child);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::ClearCachedResources(Layer* aSubtree)
|
|
{
|
|
MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
|
|
Layer* subtree = aSubtree ? aSubtree : mRoot.get();
|
|
if (!subtree) {
|
|
return;
|
|
}
|
|
|
|
ClearSubtree(subtree);
|
|
// FIXME [bjacob]
|
|
// XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here.
|
|
// Do we need that?
|
|
}
|
|
|
|
/**
|
|
* LayerManagerComposite
|
|
*/
|
|
LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
|
|
: mCompositor(aCompositor)
|
|
, mInTransaction(false)
|
|
, mIsCompositorReady(false)
|
|
, mDebugOverlayWantsNextFrame(false)
|
|
{
|
|
mTextRenderer = new TextRenderer(aCompositor);
|
|
MOZ_ASSERT(aCompositor);
|
|
}
|
|
|
|
LayerManagerComposite::~LayerManagerComposite()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
|
|
bool
|
|
LayerManagerComposite::Initialize()
|
|
{
|
|
bool result = mCompositor->Initialize();
|
|
return result;
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::Destroy()
|
|
{
|
|
if (!mDestroyed) {
|
|
mCompositor->GetWidget()->CleanupWindowEffects();
|
|
if (mRoot) {
|
|
RootLayer()->Destroy();
|
|
}
|
|
mRoot = nullptr;
|
|
|
|
mCompositor->Destroy();
|
|
|
|
mDestroyed = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::UpdateRenderBounds(const nsIntRect& aRect)
|
|
{
|
|
mRenderBounds = aRect;
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::BeginTransaction()
|
|
{
|
|
mInTransaction = true;
|
|
|
|
if (!mCompositor->Ready()) {
|
|
return;
|
|
}
|
|
|
|
mIsCompositorReady = true;
|
|
|
|
if (Compositor::GetBackend() == LayersBackend::LAYERS_BASIC) {
|
|
mClonedLayerTreeProperties = LayerProperties::CloneFrom(GetRoot());
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget)
|
|
{
|
|
mInTransaction = true;
|
|
|
|
if (!mCompositor->Ready()) {
|
|
return;
|
|
}
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
MOZ_LAYERS_LOG(("[----- BeginTransaction"));
|
|
Log();
|
|
#endif
|
|
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return;
|
|
}
|
|
|
|
mIsCompositorReady = true;
|
|
mCompositor->SetTargetContext(aTarget);
|
|
mTarget = aTarget;
|
|
}
|
|
|
|
bool
|
|
LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags)
|
|
{
|
|
NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
|
|
if (!mRoot) {
|
|
mInTransaction = false;
|
|
mIsCompositorReady = false;
|
|
return false;
|
|
}
|
|
|
|
EndTransaction(nullptr, nullptr);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::EndTransaction(DrawThebesLayerCallback 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) {
|
|
nsIntRegion invalid = mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr);
|
|
mClonedLayerTreeProperties = nullptr;
|
|
|
|
mInvalidRegion.Or(mInvalidRegion, invalid);
|
|
} else {
|
|
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());
|
|
|
|
Render();
|
|
}
|
|
|
|
mCompositor->SetTargetContext(nullptr);
|
|
mTarget = nullptr;
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
Log();
|
|
MOZ_LAYERS_LOG(("]----- EndTransaction"));
|
|
#endif
|
|
}
|
|
|
|
already_AddRefed<gfxASurface>
|
|
LayerManagerComposite::CreateOptimalMaskSurface(const IntSize &aSize)
|
|
{
|
|
NS_RUNTIMEABORT("Should only be called on the drawing side");
|
|
return nullptr;
|
|
}
|
|
|
|
already_AddRefed<ThebesLayer>
|
|
LayerManagerComposite::CreateThebesLayer()
|
|
{
|
|
NS_RUNTIMEABORT("Should only be called on the drawing side");
|
|
return nullptr;
|
|
}
|
|
|
|
already_AddRefed<ContainerLayer>
|
|
LayerManagerComposite::CreateContainerLayer()
|
|
{
|
|
NS_RUNTIMEABORT("Should only be called on the drawing side");
|
|
return nullptr;
|
|
}
|
|
|
|
already_AddRefed<ImageLayer>
|
|
LayerManagerComposite::CreateImageLayer()
|
|
{
|
|
NS_RUNTIMEABORT("Should only be called on the drawing side");
|
|
return nullptr;
|
|
}
|
|
|
|
already_AddRefed<ColorLayer>
|
|
LayerManagerComposite::CreateColorLayer()
|
|
{
|
|
NS_RUNTIMEABORT("Should only be called on the drawing side");
|
|
return nullptr;
|
|
}
|
|
|
|
already_AddRefed<CanvasLayer>
|
|
LayerManagerComposite::CreateCanvasLayer()
|
|
{
|
|
NS_RUNTIMEABORT("Should only be called on the drawing side");
|
|
return nullptr;
|
|
}
|
|
|
|
LayerComposite*
|
|
LayerManagerComposite::RootLayer() const
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
|
|
return ToLayerComposite(mRoot);
|
|
}
|
|
|
|
// Size of the builtin font.
|
|
static const float FontHeight = 7.f;
|
|
static const float FontWidth = 4.f;
|
|
static const float FontStride = 4.f;
|
|
|
|
// Scale the font when drawing it to the viewport for better readability.
|
|
static const float FontScaleX = 2.f;
|
|
static const float FontScaleY = 3.f;
|
|
|
|
static void DrawDigits(unsigned int aValue,
|
|
int aOffsetX, int aOffsetY,
|
|
Compositor* aCompositor,
|
|
EffectChain& aEffectChain)
|
|
{
|
|
if (aValue > 999) {
|
|
aValue = 999;
|
|
}
|
|
|
|
unsigned int divisor = 100;
|
|
float textureWidth = FontWidth * 10;
|
|
gfx::Float opacity = 1;
|
|
gfx::Matrix4x4 transform;
|
|
transform.Scale(FontScaleX, FontScaleY, 1);
|
|
|
|
for (size_t n = 0; n < 3; ++n) {
|
|
unsigned int digit = aValue % (divisor * 10) / divisor;
|
|
divisor /= 10;
|
|
|
|
RefPtr<TexturedEffect> texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
|
|
texturedEffect->mTextureCoords = Rect(float(digit * FontWidth) / textureWidth, 0, FontWidth / textureWidth, 1.0f);
|
|
|
|
Rect drawRect = Rect(aOffsetX + n * FontWidth, aOffsetY, FontWidth, FontHeight);
|
|
Rect clipRect = Rect(0, 0, 300, 100);
|
|
aCompositor->DrawQuad(drawRect, clipRect,
|
|
aEffectChain, opacity, transform);
|
|
}
|
|
}
|
|
|
|
void FPSState::DrawFPS(TimeStamp aNow,
|
|
unsigned int aFillRatio,
|
|
Compositor* aCompositor)
|
|
{
|
|
if (!mFPSTextureSource) {
|
|
const char *text =
|
|
" "
|
|
" XXX XX XXX XXX X X XXX XXX XXX XXX XXX"
|
|
" X X X X X X X X X X X X X X"
|
|
" X X X XXX XXX XXX XXX XXX X XXX XXX"
|
|
" X X X X X X X X X X X X X"
|
|
" XXX XXX XXX XXX X XXX XXX X XXX X"
|
|
" ";
|
|
|
|
// Convert the text encoding above to RGBA.
|
|
int w = FontWidth * 10;
|
|
int h = FontHeight;
|
|
uint32_t* buf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
|
|
for (int i = 0; i < h; i++) {
|
|
for (int j = 0; j < w; j++) {
|
|
uint32_t purple = 0xfff000ff;
|
|
uint32_t white = 0xffffffff;
|
|
buf[i * w + j] = (text[i * w + j] == ' ') ? purple : white;
|
|
}
|
|
}
|
|
|
|
int bytesPerPixel = 4;
|
|
RefPtr<DataSourceSurface> fpsSurface = Factory::CreateWrappingDataSourceSurface(
|
|
reinterpret_cast<uint8_t*>(buf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
|
|
mFPSTextureSource = aCompositor->CreateDataTextureSource();
|
|
mFPSTextureSource->Update(fpsSurface);
|
|
}
|
|
|
|
EffectChain effectChain;
|
|
effectChain.mPrimaryEffect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mFPSTextureSource, Filter::POINT);
|
|
|
|
unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
|
|
unsigned int txnFps = unsigned(mTransactionFps.GetFpsAt(aNow));
|
|
|
|
DrawDigits(fps, 0, 0, aCompositor, effectChain);
|
|
DrawDigits(txnFps, FontWidth * 4, 0, aCompositor, effectChain);
|
|
DrawDigits(aFillRatio, FontWidth * 8, 0, aCompositor, effectChain);
|
|
}
|
|
|
|
static uint16_t sFrameCount = 0;
|
|
void
|
|
LayerManagerComposite::RenderDebugOverlay(const Rect& aBounds)
|
|
{
|
|
if (gfxPrefs::LayersDrawFPS()) {
|
|
if (!mFPS) {
|
|
mFPS = new FPSState();
|
|
}
|
|
|
|
float fillRatio = mCompositor->GetFillRatio();
|
|
mFPS->DrawFPS(TimeStamp::Now(), unsigned(fillRatio), mCompositor);
|
|
} else {
|
|
mFPS = nullptr;
|
|
}
|
|
|
|
if (gfxPrefs::DrawFrameCounter()) {
|
|
profiler_set_frame_number(sFrameCount);
|
|
|
|
uint16_t frameNumber = sFrameCount;
|
|
const uint16_t bitWidth = 3;
|
|
float opacity = 1.0;
|
|
gfx::Rect clip(0,0, bitWidth*16, bitWidth);
|
|
for (size_t i = 0; i < 16; i++) {
|
|
|
|
gfx::Color bitColor;
|
|
if ((frameNumber >> i) & 0x1) {
|
|
bitColor = gfx::Color(0, 0, 0, 1.0);
|
|
} else {
|
|
bitColor = gfx::Color(1.0, 1.0, 1.0, 1.0);
|
|
}
|
|
EffectChain effects;
|
|
effects.mPrimaryEffect = new EffectSolidColor(bitColor);
|
|
mCompositor->DrawQuad(gfx::Rect(bitWidth*i, 0, bitWidth, bitWidth),
|
|
clip,
|
|
effects,
|
|
opacity,
|
|
gfx::Matrix4x4());
|
|
}
|
|
// We intentionally overflow at 2^16.
|
|
sFrameCount++;
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::Render()
|
|
{
|
|
PROFILER_LABEL("LayerManagerComposite", "Render");
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return;
|
|
}
|
|
|
|
if (gfxPrefs::LayersDump()) {
|
|
this->Dump();
|
|
}
|
|
|
|
/** Our more efficient but less powerful alter ego, if one is available. */
|
|
nsRefPtr<Composer2D> composer2D = mCompositor->GetWidget()->GetComposer2D();
|
|
|
|
if (!mTarget && composer2D && composer2D->TryRender(mRoot, mWorldMatrix)) {
|
|
if (mFPS) {
|
|
double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
|
|
if (gfxPrefs::LayersDrawFPS()) {
|
|
printf_stderr("HWComposer: FPS is %g\n", fps);
|
|
}
|
|
}
|
|
mCompositor->EndFrameForExternalComposition(mWorldMatrix);
|
|
return;
|
|
}
|
|
|
|
{
|
|
PROFILER_LABEL("LayerManagerComposite", "PreRender");
|
|
if (!mCompositor->GetWidget()->PreRender(this)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
nsIntRect clipRect;
|
|
Rect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height);
|
|
Rect actualBounds;
|
|
if (mRoot->GetClipRect()) {
|
|
clipRect = *mRoot->GetClipRect();
|
|
WorldTransformRect(clipRect);
|
|
Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
|
|
mCompositor->BeginFrame(mInvalidRegion, &rect, mWorldMatrix, bounds, nullptr, &actualBounds);
|
|
} else {
|
|
gfx::Rect rect;
|
|
mCompositor->BeginFrame(mInvalidRegion, nullptr, mWorldMatrix, bounds, &rect, &actualBounds);
|
|
clipRect = nsIntRect(rect.x, rect.y, rect.width, rect.height);
|
|
}
|
|
|
|
// Reset the invalid region now that we've begun compositing.
|
|
mInvalidRegion.SetEmpty();
|
|
|
|
if (actualBounds.IsEmpty()) {
|
|
mCompositor->GetWidget()->PostRender(this);
|
|
return;
|
|
}
|
|
|
|
// Allow widget to render a custom background.
|
|
mCompositor->GetWidget()->DrawWindowUnderlay(this, nsIntRect(actualBounds.x,
|
|
actualBounds.y,
|
|
actualBounds.width,
|
|
actualBounds.height));
|
|
|
|
// Render our layers.
|
|
RootLayer()->RenderLayer(clipRect);
|
|
|
|
// Allow widget to render a custom foreground.
|
|
mCompositor->GetWidget()->DrawWindowOverlay(this, nsIntRect(actualBounds.x,
|
|
actualBounds.y,
|
|
actualBounds.width,
|
|
actualBounds.height));
|
|
|
|
// Debugging
|
|
RenderDebugOverlay(actualBounds);
|
|
|
|
{
|
|
PROFILER_LABEL("LayerManagerComposite", "EndFrame");
|
|
mCompositor->EndFrame();
|
|
mCompositor->SetFBAcquireFence(mRoot);
|
|
}
|
|
|
|
mCompositor->GetWidget()->PostRender(this);
|
|
|
|
RecordFrame();
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::SetWorldTransform(const gfx::Matrix& aMatrix)
|
|
{
|
|
NS_ASSERTION(aMatrix.PreservesAxisAlignedRectangles(),
|
|
"SetWorldTransform only accepts matrices that satisfy PreservesAxisAlignedRectangles");
|
|
NS_ASSERTION(!aMatrix.HasNonIntegerScale(),
|
|
"SetWorldTransform only accepts matrices with integer scale");
|
|
|
|
mWorldMatrix = aMatrix;
|
|
}
|
|
|
|
gfx::Matrix&
|
|
LayerManagerComposite::GetWorldTransform(void)
|
|
{
|
|
return mWorldMatrix;
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::WorldTransformRect(nsIntRect& aRect)
|
|
{
|
|
gfx::Rect grect(aRect.x, aRect.y, aRect.width, aRect.height);
|
|
grect = mWorldMatrix.TransformBounds(grect);
|
|
aRect.SetRect(grect.X(), grect.Y(), grect.Width(), grect.Height());
|
|
}
|
|
|
|
static void
|
|
SubtractTransformedRegion(nsIntRegion& aRegion,
|
|
const nsIntRegion& aRegionToSubtract,
|
|
const gfx3DMatrix& 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 nsIntRect* rect = it.Next()) {
|
|
gfxRect incompleteRect = aTransform.TransformBounds(gfxRect(*rect));
|
|
aRegion.Sub(aRegion, nsIntRect(incompleteRect.x,
|
|
incompleteRect.y,
|
|
incompleteRect.width,
|
|
incompleteRect.height));
|
|
}
|
|
}
|
|
|
|
/* static */ void
|
|
LayerManagerComposite::ComputeRenderIntegrityInternal(Layer* aLayer,
|
|
nsIntRegion& aScreenRegion,
|
|
nsIntRegion& aLowPrecisionScreenRegion,
|
|
const gfx3DMatrix& 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
|
|
gfx3DMatrix transform = aTransform;
|
|
if (container->UseIntermediateSurface()) {
|
|
gfx::To3DMatrix(aLayer->GetEffectiveTransform(), transform);
|
|
transform.PreMultiply(aTransform);
|
|
}
|
|
for (Layer* child = aLayer->GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
ComputeRenderIntegrityInternal(child, aScreenRegion, aLowPrecisionScreenRegion, transform);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Only thebes layers can be incomplete
|
|
ThebesLayer* thebesLayer = aLayer->AsThebesLayer();
|
|
if (!thebesLayer) {
|
|
return;
|
|
}
|
|
|
|
// See if there's any incomplete rendering
|
|
nsIntRegion incompleteRegion = aLayer->GetEffectiveVisibleRegion();
|
|
incompleteRegion.Sub(incompleteRegion, thebesLayer->GetValidRegion());
|
|
|
|
if (!incompleteRegion.IsEmpty()) {
|
|
// Calculate the transform to get between screen and layer space
|
|
gfx3DMatrix transformToScreen;
|
|
To3DMatrix(aLayer->GetEffectiveTransform(), transformToScreen);
|
|
transformToScreen.PreMultiply(aTransform);
|
|
|
|
SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen);
|
|
|
|
// See if there's any incomplete low-precision rendering
|
|
TiledLayerComposer* composer = nullptr;
|
|
LayerComposite* shadow = aLayer->AsLayerComposite();
|
|
if (shadow) {
|
|
composer = shadow->GetTiledLayerComposer();
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MOZ_ANDROID_OMTC
|
|
static float
|
|
GetDisplayportCoverage(const CSSRect& aDisplayPort,
|
|
const gfx3DMatrix& aTransformToScreen,
|
|
const nsIntRect& aScreenRect)
|
|
{
|
|
gfxRect transformedDisplayport =
|
|
aTransformToScreen.TransformBounds(gfxRect(aDisplayPort.x,
|
|
aDisplayPort.y,
|
|
aDisplayPort.width,
|
|
aDisplayPort.height));
|
|
transformedDisplayport.RoundOut();
|
|
nsIntRect displayport = nsIntRect(transformedDisplayport.x,
|
|
transformedDisplayport.y,
|
|
transformedDisplayport.width,
|
|
transformedDisplayport.height);
|
|
if (!displayport.Contains(aScreenRect)) {
|
|
nsIntRegion coveredRegion;
|
|
coveredRegion.And(aScreenRect, displayport);
|
|
return coveredRegion.Area() / (float)(aScreenRect.width * aScreenRect.height);
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
#endif // MOZ_ANDROID_OMTC
|
|
|
|
float
|
|
LayerManagerComposite::ComputeRenderIntegrity()
|
|
{
|
|
// We only ever have incomplete rendering when progressive tiles are enabled.
|
|
Layer* root = GetRoot();
|
|
if (!gfxPrefs::UseProgressiveTilePainting() || !root) {
|
|
return 1.f;
|
|
}
|
|
|
|
const FrameMetrics& rootMetrics = root->AsContainerLayer()->GetFrameMetrics();
|
|
nsIntRect screenRect(rootMetrics.mCompositionBounds.x,
|
|
rootMetrics.mCompositionBounds.y,
|
|
rootMetrics.mCompositionBounds.width,
|
|
rootMetrics.mCompositionBounds.height);
|
|
|
|
float lowPrecisionMultiplier = 1.0f;
|
|
float highPrecisionMultiplier = 1.0f;
|
|
|
|
#ifdef MOZ_ANDROID_OMTC
|
|
// Use the transform on the primary scrollable layer and its FrameMetrics
|
|
// to find out how much of the viewport the current displayport covers
|
|
Layer* primaryScrollable = GetPrimaryScrollableLayer();
|
|
if (primaryScrollable) {
|
|
// This is derived from the code in
|
|
// AsyncCompositionManager::TransformScrollableLayer
|
|
const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
|
|
gfx3DMatrix transform;
|
|
gfx::To3DMatrix(primaryScrollable->GetEffectiveTransform(), transform);
|
|
transform.ScalePost(metrics.mResolution.scale, metrics.mResolution.scale, 1);
|
|
|
|
// Clip the screen rect to the document bounds
|
|
gfxRect documentBounds =
|
|
transform.TransformBounds(gfxRect(metrics.mScrollableRect.x - metrics.GetScrollOffset().x,
|
|
metrics.mScrollableRect.y - metrics.GetScrollOffset().y,
|
|
metrics.mScrollableRect.width,
|
|
metrics.mScrollableRect.height));
|
|
documentBounds.RoundOut();
|
|
screenRect = screenRect.Intersect(nsIntRect(documentBounds.x, documentBounds.y,
|
|
documentBounds.width, documentBounds.height));
|
|
|
|
// If the screen rect is empty, the user has scrolled entirely into
|
|
// over-scroll and so we can be considered to have full integrity.
|
|
if (screenRect.IsEmpty()) {
|
|
return 1.0f;
|
|
}
|
|
|
|
// Work out how much of the critical display-port covers the screen
|
|
bool hasLowPrecision = false;
|
|
if (!metrics.mCriticalDisplayPort.IsEmpty()) {
|
|
hasLowPrecision = true;
|
|
highPrecisionMultiplier =
|
|
GetDisplayportCoverage(metrics.mCriticalDisplayPort, transform, screenRect);
|
|
}
|
|
|
|
// Work out how much of the display-port covers the screen
|
|
if (!metrics.mDisplayPort.IsEmpty()) {
|
|
if (hasLowPrecision) {
|
|
lowPrecisionMultiplier =
|
|
GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect);
|
|
} else {
|
|
lowPrecisionMultiplier = highPrecisionMultiplier =
|
|
GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If none of the screen is covered, we have zero integrity.
|
|
if (highPrecisionMultiplier <= 0.0f && lowPrecisionMultiplier <= 0.0f) {
|
|
return 0.0f;
|
|
}
|
|
#endif // MOZ_ANDROID_OMTC
|
|
|
|
nsIntRegion screenRegion(screenRect);
|
|
nsIntRegion lowPrecisionScreenRegion(screenRect);
|
|
gfx3DMatrix transform;
|
|
ComputeRenderIntegrityInternal(root, screenRegion,
|
|
lowPrecisionScreenRegion, transform);
|
|
|
|
if (!screenRegion.IsEqual(screenRect)) {
|
|
// Calculate the area of the region. All rects in an nsRegion are
|
|
// non-overlapping.
|
|
float screenArea = screenRect.width * screenRect.height;
|
|
float highPrecisionIntegrity = screenRegion.Area() / screenArea;
|
|
float lowPrecisionIntegrity = 1.f;
|
|
if (!lowPrecisionScreenRegion.IsEqual(screenRect)) {
|
|
lowPrecisionIntegrity = lowPrecisionScreenRegion.Area() / screenArea;
|
|
}
|
|
|
|
return ((highPrecisionIntegrity * highPrecisionMultiplier) +
|
|
(lowPrecisionIntegrity * lowPrecisionMultiplier)) / 2;
|
|
}
|
|
|
|
return 1.f;
|
|
}
|
|
|
|
already_AddRefed<ThebesLayerComposite>
|
|
LayerManagerComposite::CreateThebesLayerComposite()
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ThebesLayerComposite>(new ThebesLayerComposite(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<ContainerLayerComposite>
|
|
LayerManagerComposite::CreateContainerLayerComposite()
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ContainerLayerComposite>(new ContainerLayerComposite(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<ImageLayerComposite>
|
|
LayerManagerComposite::CreateImageLayerComposite()
|
|
{
|
|
if (mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ImageLayerComposite>(new ImageLayerComposite(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<ColorLayerComposite>
|
|
LayerManagerComposite::CreateColorLayerComposite()
|
|
{
|
|
if (LayerManagerComposite::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<ColorLayerComposite>(new ColorLayerComposite(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<CanvasLayerComposite>
|
|
LayerManagerComposite::CreateCanvasLayerComposite()
|
|
{
|
|
if (LayerManagerComposite::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<CanvasLayerComposite>(new CanvasLayerComposite(this)).forget();
|
|
}
|
|
|
|
already_AddRefed<RefLayerComposite>
|
|
LayerManagerComposite::CreateRefLayerComposite()
|
|
{
|
|
if (LayerManagerComposite::mDestroyed) {
|
|
NS_WARNING("Call on destroyed layer manager");
|
|
return nullptr;
|
|
}
|
|
return nsRefPtr<RefLayerComposite>(new RefLayerComposite(this)).forget();
|
|
}
|
|
|
|
LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer,
|
|
EffectChain& aEffects,
|
|
bool aIs3D)
|
|
: mCompositable(nullptr)
|
|
{
|
|
if (!aMaskLayer) {
|
|
return;
|
|
}
|
|
|
|
mCompositable = ToLayerComposite(aMaskLayer)->GetCompositableHost();
|
|
if (!mCompositable) {
|
|
NS_WARNING("Mask layer with no compositable host");
|
|
return;
|
|
}
|
|
|
|
if (!mCompositable->AddMaskEffect(aEffects, aMaskLayer->GetEffectiveTransform(), aIs3D)) {
|
|
mCompositable = nullptr;
|
|
}
|
|
}
|
|
|
|
LayerManagerComposite::AutoAddMaskEffect::~AutoAddMaskEffect()
|
|
{
|
|
if (!mCompositable) {
|
|
return;
|
|
}
|
|
|
|
mCompositable->RemoveMaskEffect();
|
|
}
|
|
|
|
TemporaryRef<DrawTarget>
|
|
LayerManagerComposite::CreateDrawTarget(const IntSize &aSize,
|
|
SurfaceFormat aFormat)
|
|
{
|
|
#ifdef XP_MACOSX
|
|
// We don't want to accelerate if the surface is too small which indicates
|
|
// that it's likely used for an icon/static image. We also don't want to
|
|
// accelerate anything that is above the maximum texture size of weakest gpu.
|
|
// Safari uses 5000 area as the minimum for acceleration, we decided 64^2 is more logical.
|
|
bool useAcceleration = aSize.width <= 4096 && aSize.height <= 4096 &&
|
|
aSize.width > 64 && aSize.height > 64 &&
|
|
gfxPlatformMac::GetPlatform()->UseAcceleratedCanvas();
|
|
if (useAcceleration) {
|
|
return Factory::CreateDrawTarget(BackendType::COREGRAPHICS_ACCELERATED,
|
|
aSize, aFormat);
|
|
}
|
|
#endif
|
|
return LayerManager::CreateDrawTarget(aSize, aFormat);
|
|
}
|
|
|
|
LayerComposite::LayerComposite(LayerManagerComposite *aManager)
|
|
: mCompositeManager(aManager)
|
|
, mCompositor(aManager->GetCompositor())
|
|
, mShadowOpacity(1.0)
|
|
, mUseShadowClipRect(false)
|
|
, mShadowTransformSetByAnimation(false)
|
|
, mDestroyed(false)
|
|
, mLayerComposited(false)
|
|
{ }
|
|
|
|
LayerComposite::~LayerComposite()
|
|
{
|
|
}
|
|
|
|
void
|
|
LayerComposite::Destroy()
|
|
{
|
|
if (!mDestroyed) {
|
|
mDestroyed = true;
|
|
CleanupResources();
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayerManagerComposite::CanUseCanvasLayerForSize(const IntSize &aSize)
|
|
{
|
|
return mCompositor->CanUseCanvasLayerForSize(gfx::IntSize(aSize.width,
|
|
aSize.height));
|
|
}
|
|
|
|
void
|
|
LayerManagerComposite::NotifyShadowTreeTransaction()
|
|
{
|
|
if (mFPS) {
|
|
mFPS->NotifyShadowTreeTransaction();
|
|
}
|
|
}
|
|
|
|
#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
|
|
|
|
/*static*/ bool
|
|
LayerManagerComposite::SupportsDirectTexturing()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*static*/ void
|
|
LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
|
|
{
|
|
}
|
|
|
|
#endif // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
|
|
|
|
} /* layers */
|
|
} /* mozilla */
|